/*
* Generic HDLC support routines for Linux
*
- * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
* * 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.
*/
#include <linux/config.h>
#include <linux/hdlc.h>
-static const char* version = "HDLC support module revision 1.16";
+static const char* version = "HDLC support module revision 1.18";
#undef DEBUG_LINK
static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *p)
+ struct packet_type *p, struct net_device *orig_dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
if (hdlc->proto.netif_rx)
+static void __hdlc_set_carrier_on(struct net_device *dev)
+{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+ if (hdlc->proto.start)
+ return hdlc->proto.start(dev);
+#if 0
+#ifdef DEBUG_LINK
+ if (netif_carrier_ok(dev))
+ printk(KERN_ERR "hdlc_set_carrier_on(): already on\n");
+#endif
+ netif_carrier_on(dev);
+#endif
+}
+
+
+
+static void __hdlc_set_carrier_off(struct net_device *dev)
+{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+ if (hdlc->proto.stop)
+ return hdlc->proto.stop(dev);
+
+#if 0
+#ifdef DEBUG_LINK
+ if (!netif_carrier_ok(dev))
+ printk(KERN_ERR "hdlc_set_carrier_off(): already off\n");
+#endif
+ netif_carrier_off(dev);
+#endif
+}
+
+
+
void hdlc_set_carrier(int on, struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
+ unsigned long flags;
on = on ? 1 : 0;
#ifdef DEBUG_LINK
printk(KERN_DEBUG "hdlc_set_carrier %i\n", on);
#endif
- spin_lock_irq(&hdlc->state_lock);
+ spin_lock_irqsave(&hdlc->state_lock, flags);
if (hdlc->carrier == on)
goto carrier_exit; /* no change in DCD line level */
- printk(KERN_INFO "%s: carrier %s\n", dev->name,
- on ? "ON" : "off");
+#ifdef DEBUG_LINK
+ printk(KERN_INFO "%s: carrier %s\n", dev->name, on ? "ON" : "off");
+#endif
hdlc->carrier = on;
if (!hdlc->open)
goto carrier_exit;
if (hdlc->carrier) {
- if (hdlc->proto.start)
- hdlc->proto.start(dev);
- else if (!netif_carrier_ok(dev))
- netif_carrier_on(dev);
-
- } else { /* no carrier */
- if (hdlc->proto.stop)
- hdlc->proto.stop(dev);
- else if (netif_carrier_ok(dev))
- netif_carrier_off(dev);
+ printk(KERN_INFO "%s: Carrier detected\n", dev->name);
+ __hdlc_set_carrier_on(dev);
+ } else {
+ printk(KERN_INFO "%s: Carrier lost\n", dev->name);
+ __hdlc_set_carrier_off(dev);
}
- carrier_exit:
- spin_unlock_irq(&hdlc->state_lock);
+carrier_exit:
+ spin_unlock_irqrestore(&hdlc->state_lock, flags);
}
+
/* 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 "hdlc_open carrier %i open %i\n",
+ printk(KERN_DEBUG "hdlc_open() carrier %i open %i\n",
hdlc->carrier, hdlc->open);
#endif
spin_lock_irq(&hdlc->state_lock);
if (hdlc->carrier) {
- if (hdlc->proto.start)
- hdlc->proto.start(dev);
- else if (!netif_carrier_ok(dev))
- netif_carrier_on(dev);
-
- } else if (netif_carrier_ok(dev))
- netif_carrier_off(dev);
+ printk(KERN_INFO "%s: Carrier detected\n", dev->name);
+ __hdlc_set_carrier_on(dev);
+ } else
+ printk(KERN_INFO "%s: No carrier\n", dev->name);
hdlc->open = 1;
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
- printk(KERN_DEBUG "hdlc_close carrier %i open %i\n",
+ printk(KERN_DEBUG "hdlc_close() carrier %i open %i\n",
hdlc->carrier, hdlc->open);
#endif
spin_lock_irq(&hdlc->state_lock);
hdlc->open = 0;
- if (hdlc->carrier && hdlc->proto.stop)
- hdlc->proto.stop(dev);
+ if (hdlc->carrier)
+ __hdlc_set_carrier_off(dev);
spin_unlock_irq(&hdlc->state_lock);
#endif
#ifndef CONFIG_HDLC_FR
-#define hdlc_fr_ioctl(dev, ifr) -ENOSYS
+#define hdlc_fr_ioctl(dev, ifr) -ENOSYS
#endif
#ifndef CONFIG_HDLC_X25
int register_hdlc_device(struct net_device *dev)
{
- int result;
- hdlc_device *hdlc = dev_to_hdlc(dev);
-
- dev->get_stats = hdlc_get_stats;
- dev->change_mtu = hdlc_change_mtu;
- dev->mtu = HDLC_MAX_MTU;
-
- dev->type = ARPHRD_RAWHDLC;
- dev->hard_header_len = 16;
-
- dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-
- hdlc->proto.id = -1;
- hdlc->proto.detach = NULL;
- hdlc->carrier = 1;
- hdlc->open = 0;
- spin_lock_init(&hdlc->state_lock);
-
- result = dev_alloc_name(dev, "hdlc%d");
+ int result = dev_alloc_name(dev, "hdlc%d");
if (result < 0)
return result;
if (result != 0)
return -EIO;
+#if 0
+ if (netif_carrier_ok(dev))
+ netif_carrier_off(dev); /* no carrier until DCD goes up */
+#endif
+
return 0;
}