patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / net / wan / hdlc_generic.c
index 2aa4ae0..6ed064c 100644 (file)
  *     * 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>
@@ -33,7 +38,7 @@
 #include <linux/hdlc.h>
 
 
-static const char* version = "HDLC support module revision 1.16";
+static const char* version = "HDLC support module revision 1.17";
 
 #undef DEBUG_LINK
 
@@ -69,51 +74,75 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
 
 
 
+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);
+#ifdef DEBUG_LINK
+       if (netif_carrier_ok(dev))
+               printk(KERN_ERR "hdlc_set_carrier_on(): already on\n");
+#endif
+       netif_carrier_on(dev);
+}
+
+
+
+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);
+
+#ifdef DEBUG_LINK
+       if (!netif_carrier_ok(dev))
+               printk(KERN_ERR "hdlc_set_carrier_off(): already off\n");
+#endif
+       netif_carrier_off(dev);
+}
+
+
+
 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);
-       }
+       if (hdlc->carrier)
+               __hdlc_set_carrier_on(dev);
+       else
+               __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
 
@@ -128,14 +157,8 @@ int hdlc_open(struct net_device *dev)
 
        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);
+       if (hdlc->carrier)
+               __hdlc_set_carrier_on(dev);
 
        hdlc->open = 1;
 
@@ -150,15 +173,15 @@ void hdlc_close(struct net_device *dev)
 {
        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);
 
@@ -185,7 +208,7 @@ void hdlc_close(struct net_device *dev)
 #endif
 
 #ifndef CONFIG_HDLC_FR
-#define hdlc_fr_ioctl(dev, ifr)        -ENOSYS
+#define hdlc_fr_ioctl(dev, ifr)                -ENOSYS
 #endif
 
 #ifndef CONFIG_HDLC_X25
@@ -257,25 +280,7 @@ struct net_device *alloc_hdlcdev(void *priv)
 
 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;
 
@@ -283,6 +288,9 @@ int register_hdlc_device(struct net_device *dev)
        if (result != 0)
                return -EIO;
 
+       if (netif_carrier_ok(dev))
+               netif_carrier_off(dev); /* no carrier until DCD goes up */
+
        return 0;
 }