Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  hdlc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Generic HDLC support routines for Linux
 *
 * Copyright (C) 1999 - 2008 Krzysztof Halasa <khc@pm.waw.pl>
 *
 * Currently supported:
 * * raw IP-in-HDLC
 * * Cisco HDLC
 * * Frame Relay with ANSI or CCITT LMI (both user and network side)
 * * PPP
 * * X.25
 *
 * Use sethdlc utility to set line parameters, protocol and PVCs
 *
 * How does it work:
 * - proto->open(), close(), start(), stop() calls are serialized.
 *   The order is: open, [ start, stop ... ] close ...
 * - proto->start() and stop() are called with spin_lock_irq held.
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/errno.h>
#include <linux/hdlc.h>
#include <linux/if_arp.h>
#include <linux/inetdevice.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/pkt_sched.h>
#include <linux/poll.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <net/net_namespace.h>

static const char *version = "HDLC support module revision 1.22";

#undef DEBUG_LINK

static struct hdlc_proto *first_proto;

static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
      struct packet_type *p, struct net_device *orig_dev)
{
 struct hdlc_device *hdlc;

 /* First make sure "dev" is an HDLC device */
 if (!(dev->priv_flags & IFF_WAN_HDLC)) {
  kfree_skb(skb);
  return NET_RX_SUCCESS;
 }

 hdlc = dev_to_hdlc(dev);

 if (!net_eq(dev_net(dev), &init_net)) {
  kfree_skb(skb);
  return 0;
 }

 BUG_ON(!hdlc->proto->netif_rx);
 return hdlc->proto->netif_rx(skb);
}

netdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
 hdlc_device *hdlc = dev_to_hdlc(dev);

 if (hdlc->proto->xmit)
  return hdlc->proto->xmit(skb, dev);

 return hdlc->xmit(skb, dev); /* call hardware driver directly */
}
EXPORT_SYMBOL(hdlc_start_xmit);

static inline void hdlc_proto_start(struct net_device *dev)
{
 hdlc_device *hdlc = dev_to_hdlc(dev);

 if (hdlc->proto->start)
  hdlc->proto->start(dev);
}

static inline void hdlc_proto_stop(struct net_device *dev)
{
 hdlc_device *hdlc = dev_to_hdlc(dev);

 if (hdlc->proto->stop)
  hdlc->proto->stop(dev);
}

static int hdlc_device_event(struct notifier_block *thisunsigned long event,
        void *ptr)
{
 struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 hdlc_device *hdlc;
 unsigned long flags;
 int on;

 if (!net_eq(dev_net(dev), &init_net))
  return NOTIFY_DONE;

 if (!(dev->priv_flags & IFF_WAN_HDLC))
  return NOTIFY_DONE; /* not an HDLC device */

 if (event != NETDEV_CHANGE)
  return NOTIFY_DONE; /* Only interested in carrier changes */

 on = netif_carrier_ok(dev);

#ifdef DEBUG_LINK
 printk(KERN_DEBUG "%s: hdlc_device_event NETDEV_CHANGE, carrier %i\n",
        dev->name, on);
#endif

 hdlc = dev_to_hdlc(dev);
 spin_lock_irqsave(&hdlc->state_lock, flags);

 if (hdlc->carrier == on)
  goto carrier_exit; /* no change in DCD line level */

 hdlc->carrier = on;

 if (!hdlc->open)
  goto carrier_exit;

 if (hdlc->carrier) {
  netdev_info(dev, "Carrier detected\n");
  hdlc_proto_start(dev);
 } else {
  netdev_info(dev, "Carrier lost\n");
  hdlc_proto_stop(dev);
 }

carrier_exit:
 spin_unlock_irqrestore(&hdlc->state_lock, flags);
 return NOTIFY_DONE;
}

/* Must be called by hardware driver when HDLC device is being opened */
int hdlc_open(struct net_device *dev)
{
 hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
 printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name,
        hdlc->carrier, hdlc->open);
#endif

 if (!hdlc->proto)
  return -ENOSYS; /* no protocol attached */

 if (hdlc->proto->open) {
  int result = hdlc->proto->open(dev);

  if (result)
   return result;
 }

 spin_lock_irq(&hdlc->state_lock);

 if (hdlc->carrier) {
  netdev_info(dev, "Carrier detected\n");
  hdlc_proto_start(dev);
 } else {
  netdev_info(dev, "No carrier\n");
 }

 hdlc->open = 1;

 spin_unlock_irq(&hdlc->state_lock);
 return 0;
}
EXPORT_SYMBOL(hdlc_open);

/* Must be called by hardware driver when HDLC device is being closed */
void hdlc_close(struct net_device *dev)
{
 hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
 printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name,
        hdlc->carrier, hdlc->open);
#endif

 spin_lock_irq(&hdlc->state_lock);

 hdlc->open = 0;
 if (hdlc->carrier)
  hdlc_proto_stop(dev);

 spin_unlock_irq(&hdlc->state_lock);

 if (hdlc->proto->close)
  hdlc->proto->close(dev);
}
EXPORT_SYMBOL(hdlc_close);

int hdlc_ioctl(struct net_device *dev, struct if_settings *ifs)
{
 struct hdlc_proto *proto = first_proto;
 int result;

 if (dev_to_hdlc(dev)->proto) {
  result = dev_to_hdlc(dev)->proto->ioctl(dev, ifs);
  if (result != -EINVAL)
   return result;
 }

 /* Not handled by currently attached protocol (if any) */

 while (proto) {
  result = proto->ioctl(dev, ifs);
  if (result != -EINVAL)
   return result;
  proto = proto->next;
 }
 return -EINVAL;
}
EXPORT_SYMBOL(hdlc_ioctl);

static const struct header_ops hdlc_null_ops;

static void hdlc_setup_dev(struct net_device *dev)
{
 /* Re-init all variables changed by HDLC protocol drivers,
 * including ether_setup() called from hdlc_raw_eth.c.
 */

 dev->flags   = IFF_POINTOPOINT | IFF_NOARP;
 dev->priv_flags   = IFF_WAN_HDLC;
 dev->mtu   = HDLC_MAX_MTU;
 dev->min_mtu   = 68;
 dev->max_mtu   = HDLC_MAX_MTU;
 dev->type   = ARPHRD_RAWHDLC;
 dev->hard_header_len  = 0;
 dev->needed_headroom  = 0;
 dev->addr_len   = 0;
 dev->header_ops   = &hdlc_null_ops;
}

static void hdlc_setup(struct net_device *dev)
{
 hdlc_device *hdlc = dev_to_hdlc(dev);

 hdlc_setup_dev(dev);
 hdlc->carrier = 1;
 hdlc->open = 0;
 spin_lock_init(&hdlc->state_lock);
}

struct net_device *alloc_hdlcdev(void *priv)
{
 struct net_device *dev;

 dev = alloc_netdev(sizeof(struct hdlc_device), "hdlc%d",
      NET_NAME_UNKNOWN, hdlc_setup);
 if (dev)
  dev_to_hdlc(dev)->priv = priv;
 return dev;
}
EXPORT_SYMBOL(alloc_hdlcdev);

void unregister_hdlc_device(struct net_device *dev)
{
 rtnl_lock();
 detach_hdlc_protocol(dev);
 unregister_netdevice(dev);
 rtnl_unlock();
}
EXPORT_SYMBOL(unregister_hdlc_device);

int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
    size_t size)
{
 int err;

 err = detach_hdlc_protocol(dev);
 if (err)
  return err;

 if (!try_module_get(proto->module))
  return -ENOSYS;

 if (size) {
  dev_to_hdlc(dev)->state = kmalloc(size, GFP_KERNEL);
  if (!dev_to_hdlc(dev)->state) {
   module_put(proto->module);
   return -ENOBUFS;
  }
 }
 dev_to_hdlc(dev)->proto = proto;

 return 0;
}
EXPORT_SYMBOL(attach_hdlc_protocol);

int detach_hdlc_protocol(struct net_device *dev)
{
 hdlc_device *hdlc = dev_to_hdlc(dev);
 int err;

 if (hdlc->proto) {
  err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev);
  err = notifier_to_errno(err);
  if (err) {
   netdev_err(dev, "Refused to change device type\n");
   return err;
  }

  if (hdlc->proto->detach)
   hdlc->proto->detach(dev);
  module_put(hdlc->proto->module);
  hdlc->proto = NULL;
 }
 kfree(hdlc->state);
 hdlc->state = NULL;
 hdlc_setup_dev(dev);

 return 0;
}
EXPORT_SYMBOL(detach_hdlc_protocol);

void register_hdlc_protocol(struct hdlc_proto *proto)
{
 rtnl_lock();
 proto->next = first_proto;
 first_proto = proto;
 rtnl_unlock();
}
EXPORT_SYMBOL(register_hdlc_protocol);

void unregister_hdlc_protocol(struct hdlc_proto *proto)
{
 struct hdlc_proto **p;

 rtnl_lock();
 p = &first_proto;
 while (*p != proto) {
  BUG_ON(!*p);
  p = &((*p)->next);
 }
 *p = proto->next;
 rtnl_unlock();
}
EXPORT_SYMBOL(unregister_hdlc_protocol);

MODULE_AUTHOR("Krzysztof Halasa ");
MODULE_DESCRIPTION("HDLC support module");
MODULE_LICENSE("GPL v2");

static struct packet_type hdlc_packet_type __read_mostly = {
 .type = cpu_to_be16(ETH_P_HDLC),
 .func = hdlc_rcv,
};

static struct notifier_block hdlc_notifier = {
 .notifier_call = hdlc_device_event,
};

static int __init hdlc_module_init(void)
{
 int result;

 pr_info("%s\n", version);
 result = register_netdevice_notifier(&hdlc_notifier);
 if (result)
  return result;
 dev_add_pack(&hdlc_packet_type);
 return 0;
}

static void __exit hdlc_module_exit(void)
{
 dev_remove_pack(&hdlc_packet_type);
 unregister_netdevice_notifier(&hdlc_notifier);
}

module_init(hdlc_module_init);
module_exit(hdlc_module_exit);

Messung V0.5
C=93 H=88 G=90

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge