[PATCH] Generic HDLC update
authorKrzysztof Halasa <khc@pm.waw.pl>
Thu, 21 Apr 2005 13:57:25 +0000 (15:57 +0200)
committerJeff Garzik <jgarzik@pobox.com>
Mon, 16 May 2005 02:24:12 +0000 (22:24 -0400)
The attached patch updates generic HDLC to version 1.18.
FR Cisco LMI production-tested. Please apply to Linux 2.6. Thanks.

Changes:
- doc updates
- added Cisco LMI support to Frame-Relay code
- cleaned hdlc_fr.c a bit, removed some orphaned #defines etc.
- fixed a problem with non-functional LMI in FR DCE mode.
- changed diagnostic messages to better conform to FR standards
- all protocols: information about carrier changes (DCD line) is now
  printed to kernel logs.

Signed-Off-By: Krzysztof Halasa <khc@pm.waw.pl>
Documentation/networking/generic-hdlc.txt
drivers/net/wan/hdlc_fr.c
drivers/net/wan/hdlc_generic.c
include/linux/hdlc.h

index 7d1dc6b..31bc8b7 100644 (file)
@@ -1,21 +1,21 @@
 Generic HDLC layer
 Krzysztof Halasa <khc@pm.waw.pl>
 Generic HDLC layer
 Krzysztof Halasa <khc@pm.waw.pl>
-January, 2003
 
 
 Generic HDLC layer currently supports:
 
 
 Generic HDLC layer currently supports:
-- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP).
-  Normal (routed) and Ethernet-bridged (Ethernet device emulation)
-  interfaces can share a single PVC.
-- raw HDLC - either IP (IPv4) interface or Ethernet device emulation.
-- Cisco HDLC,
-- PPP (uses syncppp.c),
-- X.25 (uses X.25 routines).
-
-There are hardware drivers for the following cards:
-- C101 by Moxa Technologies Co., Ltd.
-- RISCom/N2 by SDL Communications Inc.
-- and others, some not in the official kernel.
+1. Frame Relay (ANSI, CCITT, Cisco and no LMI).
+   - Normal (routed) and Ethernet-bridged (Ethernet device emulation)
+     interfaces can share a single PVC.
+   - ARP support (no InARP support in the kernel - there is an
+     experimental InARP user-space daemon available on:
+     http://www.kernel.org/pub/linux/utils/net/hdlc/).
+2. raw HDLC - either IP (IPv4) interface or Ethernet device emulation.
+3. Cisco HDLC.
+4. PPP (uses syncppp.c).
+5. X.25 (uses X.25 routines).
+
+Generic HDLC is a protocol driver only - it needs a low-level driver
+for your particular hardware.
 
 Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
 with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
 
 Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
 with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
@@ -24,7 +24,7 @@ with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
 Make sure the hdlc.o and the hardware driver are loaded. It should
 create a number of "hdlc" (hdlc0 etc) network devices, one for each
 WAN port. You'll need the "sethdlc" utility, get it from:
 Make sure the hdlc.o and the hardware driver are loaded. It should
 create a number of "hdlc" (hdlc0 etc) network devices, one for each
 WAN port. You'll need the "sethdlc" utility, get it from:
-       http://hq.pm.waw.pl/hdlc/
+       http://www.kernel.org/pub/linux/utils/net/hdlc/
 
 Compile sethdlc.c utility:
        gcc -O2 -Wall -o sethdlc sethdlc.c
 
 Compile sethdlc.c utility:
        gcc -O2 -Wall -o sethdlc sethdlc.c
@@ -52,12 +52,12 @@ Setting interface:
 * v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port
                                 if the card has software-selectable interfaces
   loopback - activate hardware loopback (for testing only)
 * v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port
                                 if the card has software-selectable interfaces
   loopback - activate hardware loopback (for testing only)
-* clock ext - external clock (uses DTE RX and TX clock)
-* clock int - internal clock (provides clock signal on DCE clock output)
-* clock txint - TX internal, RX external (provides TX clock on DCE output)
-* clock txfromrx - TX clock derived from RX clock (TX clock on DCE output)
-* rate - sets clock rate in bps (not required for external clock or
-                                 for txfromrx)
+* clock ext - both RX clock and TX clock external
+* clock int - both RX clock and TX clock internal
+* clock txint - RX clock external, TX clock internal
+* clock txfromrx - RX clock external, TX clock derived from RX clock
+* rate - sets clock rate in bps (for "int" or "txint" clock only)
+
 
 Setting protocol:
 
 
 Setting protocol:
 
@@ -79,7 +79,7 @@ Setting protocol:
 * x25 - sets X.25 mode
 
 * fr - Frame Relay mode
 * x25 - sets X.25 mode
 
 * fr - Frame Relay mode
-  lmi ansi / ccitt / none - LMI (link management) type
+  lmi ansi / ccitt / cisco / none - LMI (link management) type
   dce - Frame Relay DCE (network) side LMI instead of default DTE (user).
   It has nothing to do with clocks!
   t391 - link integrity verification polling timer (in seconds) - user
   dce - Frame Relay DCE (network) side LMI instead of default DTE (user).
   It has nothing to do with clocks!
   t391 - link integrity verification polling timer (in seconds) - user
@@ -119,13 +119,14 @@ or
 
 
 
 
 
 
-If you have a problem with N2 or C101 card, you can issue the "private"
-command to see port's packet descriptor rings (in kernel logs):
+If you have a problem with N2, C101 or PLX200SYN card, you can issue the
+"private" command to see port's packet descriptor rings (in kernel logs):
 
        sethdlc hdlc0 private
 
 
        sethdlc hdlc0 private
 
-The hardware driver has to be build with CONFIG_HDLC_DEBUG_RINGS.
+The hardware driver has to be build with #define DEBUG_RINGS.
 Attaching this info to bug reports would be helpful. Anyway, let me know
 if you have problems using this.
 
 Attaching this info to bug reports would be helpful. Anyway, let me know
 if you have problems using this.
 
-For patches and other info look at http://hq.pm.waw.pl/hdlc/
+For patches and other info look at:
+<http://www.kernel.org/pub/linux/utils/net/hdlc/>.
index 7f450b5..a5d6891 100644 (file)
@@ -2,7 +2,7 @@
  * Generic HDLC support routines for Linux
  * Frame Relay support
  *
  * Generic HDLC support routines for Linux
  * Frame Relay support
  *
- * 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
  *
  * 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
  active = open and "link reliable"
  exist = new = not used
 
  active = open and "link reliable"
  exist = new = not used
 
+ CCITT LMI: ITU-T Q.933 Annex A
+ ANSI LMI: ANSI T1.617 Annex D
+ CISCO LMI: the original, aka "Gang of Four" LMI
+
 */
 
 #include <linux/module.h>
 */
 
 #include <linux/module.h>
 #undef DEBUG_ECN
 #undef DEBUG_LINK
 
 #undef DEBUG_ECN
 #undef DEBUG_LINK
 
-#define MAXLEN_LMISTAT  20     /* max size of status enquiry frame */
-
-#define PVC_STATE_NEW   0x01
-#define PVC_STATE_ACTIVE 0x02
-#define PVC_STATE_FECN  0x08 /* FECN condition */
-#define PVC_STATE_BECN  0x10 /* BECN condition */
-
-
-#define FR_UI           0x03
-#define FR_PAD          0x00
-
-#define NLPID_IP        0xCC
-#define NLPID_IPV6      0x8E
-#define NLPID_SNAP      0x80
-#define NLPID_PAD       0x00
-#define NLPID_Q933      0x08
-
-
-#define LMI_DLCI                   0 /* LMI DLCI */
-#define LMI_PROTO               0x08
-#define LMI_CALLREF             0x00 /* Call Reference */
-#define LMI_ANSI_LOCKSHIFT      0x95 /* ANSI lockshift */
-#define LMI_REPTYPE                1 /* report type */
-#define LMI_CCITT_REPTYPE       0x51
-#define LMI_ALIVE                  3 /* keep alive */
-#define LMI_CCITT_ALIVE         0x53
-#define LMI_PVCSTAT                7 /* pvc status */
-#define LMI_CCITT_PVCSTAT       0x57
-#define LMI_FULLREP                0 /* full report  */
-#define LMI_INTEGRITY              1 /* link integrity report */
-#define LMI_SINGLE                 2 /* single pvc report */
+#define FR_UI                  0x03
+#define FR_PAD                 0x00
+
+#define NLPID_IP               0xCC
+#define NLPID_IPV6             0x8E
+#define NLPID_SNAP             0x80
+#define NLPID_PAD              0x00
+#define NLPID_CCITT_ANSI_LMI   0x08
+#define NLPID_CISCO_LMI                0x09
+
+
+#define LMI_CCITT_ANSI_DLCI       0 /* LMI DLCI */
+#define LMI_CISCO_DLCI         1023
+
+#define LMI_CALLREF            0x00 /* Call Reference */
+#define LMI_ANSI_LOCKSHIFT     0x95 /* ANSI locking shift */
+#define LMI_ANSI_CISCO_REPTYPE 0x01 /* report type */
+#define LMI_CCITT_REPTYPE      0x51
+#define LMI_ANSI_CISCO_ALIVE   0x03 /* keep alive */
+#define LMI_CCITT_ALIVE                0x53
+#define LMI_ANSI_CISCO_PVCSTAT 0x07 /* PVC status */
+#define LMI_CCITT_PVCSTAT      0x57
+
+#define LMI_FULLREP            0x00 /* full report  */
+#define LMI_INTEGRITY          0x01 /* link integrity report */
+#define LMI_SINGLE             0x02 /* single PVC report */
+
 #define LMI_STATUS_ENQUIRY      0x75
 #define LMI_STATUS              0x7D /* reply */
 
 #define LMI_REPT_LEN               1 /* report type element length */
 #define LMI_INTEG_LEN              2 /* link integrity element length */
 
 #define LMI_STATUS_ENQUIRY      0x75
 #define LMI_STATUS              0x7D /* reply */
 
 #define LMI_REPT_LEN               1 /* report type element length */
 #define LMI_INTEG_LEN              2 /* link integrity element length */
 
-#define LMI_LENGTH                13 /* standard LMI frame length */
-#define LMI_ANSI_LENGTH           14
+#define LMI_CCITT_CISCO_LENGTH   13 /* LMI frame lengths */
+#define LMI_ANSI_LENGTH                  14
 
 
 typedef struct {
 
 
 typedef struct {
@@ -223,51 +223,34 @@ static inline struct net_device** get_dev_p(pvc_device *pvc, int type)
 }
 
 
 }
 
 
-static inline u16 status_to_dlci(u8 *status, int *active, int *new)
-{
-       *new = (status[2] & 0x08) ? 1 : 0;
-       *active = (status[2] & 0x02) ? 1 : 0;
-
-       return ((status[0] & 0x3F) << 4) | ((status[1] & 0x78) >> 3);
-}
-
-
-static inline void dlci_to_status(u16 dlci, u8 *status, int active, int new)
-{
-       status[0] = (dlci >> 4) & 0x3F;
-       status[1] = ((dlci << 3) & 0x78) | 0x80;
-       status[2] = 0x80;
-
-       if (new)
-               status[2] |= 0x08;
-       else if (active)
-               status[2] |= 0x02;
-}
-
-
-
 static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
 {
        u16 head_len;
        struct sk_buff *skb = *skb_p;
 
        switch (skb->protocol) {
 static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
 {
        u16 head_len;
        struct sk_buff *skb = *skb_p;
 
        switch (skb->protocol) {
-       case __constant_ntohs(ETH_P_IP):
+       case __constant_ntohs(NLPID_CCITT_ANSI_LMI):
                head_len = 4;
                skb_push(skb, head_len);
                head_len = 4;
                skb_push(skb, head_len);
-               skb->data[3] = NLPID_IP;
+               skb->data[3] = NLPID_CCITT_ANSI_LMI;
                break;
 
                break;
 
-       case __constant_ntohs(ETH_P_IPV6):
+       case __constant_ntohs(NLPID_CISCO_LMI):
                head_len = 4;
                skb_push(skb, head_len);
                head_len = 4;
                skb_push(skb, head_len);
-               skb->data[3] = NLPID_IPV6;
+               skb->data[3] = NLPID_CISCO_LMI;
                break;
 
                break;
 
-       case __constant_ntohs(LMI_PROTO):
+       case __constant_ntohs(ETH_P_IP):
+               head_len = 4;
+               skb_push(skb, head_len);
+               skb->data[3] = NLPID_IP;
+               break;
+
+       case __constant_ntohs(ETH_P_IPV6):
                head_len = 4;
                skb_push(skb, head_len);
                head_len = 4;
                skb_push(skb, head_len);
-               skb->data[3] = LMI_PROTO;
+               skb->data[3] = NLPID_IPV6;
                break;
 
        case __constant_ntohs(ETH_P_802_3):
                break;
 
        case __constant_ntohs(ETH_P_802_3):
@@ -461,13 +444,14 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
        hdlc_device *hdlc = dev_to_hdlc(dev);
        struct sk_buff *skb;
        pvc_device *pvc = hdlc->state.fr.first_pvc;
        hdlc_device *hdlc = dev_to_hdlc(dev);
        struct sk_buff *skb;
        pvc_device *pvc = hdlc->state.fr.first_pvc;
-       int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH
-               : LMI_LENGTH;
-       int stat_len = 3;
+       int lmi = hdlc->state.fr.settings.lmi;
+       int dce = hdlc->state.fr.settings.dce;
+       int len = lmi == LMI_ANSI ? LMI_ANSI_LENGTH : LMI_CCITT_CISCO_LENGTH;
+       int stat_len = (lmi == LMI_CISCO) ? 6 : 3;
        u8 *data;
        int i = 0;
 
        u8 *data;
        int i = 0;
 
-       if (hdlc->state.fr.settings.dce && fullrep) {
+       if (dce && fullrep) {
                len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
                if (len > HDLC_MAX_MRU) {
                        printk(KERN_WARNING "%s: Too many PVCs while sending "
                len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
                if (len > HDLC_MAX_MRU) {
                        printk(KERN_WARNING "%s: Too many PVCs while sending "
@@ -484,29 +468,31 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
        }
        memset(skb->data, 0, len);
        skb_reserve(skb, 4);
        }
        memset(skb->data, 0, len);
        skb_reserve(skb, 4);
-       skb->protocol = __constant_htons(LMI_PROTO);
-       fr_hard_header(&skb, LMI_DLCI);
+       if (lmi == LMI_CISCO) {
+               skb->protocol = __constant_htons(NLPID_CISCO_LMI);
+               fr_hard_header(&skb, LMI_CISCO_DLCI);
+       } else {
+               skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI);
+               fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI);
+       }
        data = skb->tail;
        data[i++] = LMI_CALLREF;
        data = skb->tail;
        data[i++] = LMI_CALLREF;
-       data[i++] = hdlc->state.fr.settings.dce
-               ? LMI_STATUS : LMI_STATUS_ENQUIRY;
-       if (hdlc->state.fr.settings.lmi == LMI_ANSI)
+       data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY;
+       if (lmi == LMI_ANSI)
                data[i++] = LMI_ANSI_LOCKSHIFT;
                data[i++] = LMI_ANSI_LOCKSHIFT;
-       data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
-               ? LMI_CCITT_REPTYPE : LMI_REPTYPE;
+       data[i++] = lmi == LMI_CCITT ? LMI_CCITT_REPTYPE :
+               LMI_ANSI_CISCO_REPTYPE;
        data[i++] = LMI_REPT_LEN;
        data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
        data[i++] = LMI_REPT_LEN;
        data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
-
-       data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
-               ? LMI_CCITT_ALIVE : LMI_ALIVE;
+       data[i++] = lmi == LMI_CCITT ? LMI_CCITT_ALIVE : LMI_ANSI_CISCO_ALIVE;
        data[i++] = LMI_INTEG_LEN;
        data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
        data[i++] = hdlc->state.fr.rxseq;
 
        data[i++] = LMI_INTEG_LEN;
        data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
        data[i++] = hdlc->state.fr.rxseq;
 
-       if (hdlc->state.fr.settings.dce && fullrep) {
+       if (dce && fullrep) {
                while (pvc) {
                while (pvc) {
-                       data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
-                               ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT;
+                       data[i++] = lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT :
+                               LMI_ANSI_CISCO_PVCSTAT;
                        data[i++] = stat_len;
 
                        /* LMI start/restart */
                        data[i++] = stat_len;
 
                        /* LMI start/restart */
@@ -523,8 +509,20 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
                                fr_log_dlci_active(pvc);
                        }
 
                                fr_log_dlci_active(pvc);
                        }
 
-                       dlci_to_status(pvc->dlci, data + i,
-                                      pvc->state.active, pvc->state.new);
+                       if (lmi == LMI_CISCO) {
+                               data[i] = pvc->dlci >> 8;
+                               data[i + 1] = pvc->dlci & 0xFF;
+                       } else {
+                               data[i] = (pvc->dlci >> 4) & 0x3F;
+                               data[i + 1] = ((pvc->dlci << 3) & 0x78) | 0x80;
+                               data[i + 2] = 0x80;
+                       }
+
+                       if (pvc->state.new)
+                               data[i + 2] |= 0x08;
+                       else if (pvc->state.active)
+                               data[i + 2] |= 0x02;
+
                        i += stat_len;
                        pvc = pvc->next;
                }
                        i += stat_len;
                        pvc = pvc->next;
                }
@@ -569,6 +567,8 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
                        pvc_carrier(0, pvc);
                        pvc->state.exist = pvc->state.active = 0;
                        pvc->state.new = 0;
                        pvc_carrier(0, pvc);
                        pvc->state.exist = pvc->state.active = 0;
                        pvc->state.new = 0;
+                       if (!hdlc->state.fr.settings.dce)
+                               pvc->state.bandwidth = 0;
                        pvc = pvc->next;
                }
        }
                        pvc = pvc->next;
                }
        }
@@ -583,11 +583,12 @@ static void fr_timer(unsigned long arg)
        int i, cnt = 0, reliable;
        u32 list;
 
        int i, cnt = 0, reliable;
        u32 list;
 
-       if (hdlc->state.fr.settings.dce)
+       if (hdlc->state.fr.settings.dce) {
                reliable = hdlc->state.fr.request &&
                        time_before(jiffies, hdlc->state.fr.last_poll +
                                    hdlc->state.fr.settings.t392 * HZ);
                reliable = hdlc->state.fr.request &&
                        time_before(jiffies, hdlc->state.fr.last_poll +
                                    hdlc->state.fr.settings.t392 * HZ);
-       else {
+               hdlc->state.fr.request = 0;
+       } else {
                hdlc->state.fr.last_errors <<= 1; /* Shift the list */
                if (hdlc->state.fr.request) {
                        if (hdlc->state.fr.reliable)
                hdlc->state.fr.last_errors <<= 1; /* Shift the list */
                if (hdlc->state.fr.request) {
                        if (hdlc->state.fr.reliable)
@@ -634,65 +635,88 @@ static void fr_timer(unsigned long arg)
 static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
 {
        hdlc_device *hdlc = dev_to_hdlc(dev);
 static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
 {
        hdlc_device *hdlc = dev_to_hdlc(dev);
-       int stat_len;
        pvc_device *pvc;
        pvc_device *pvc;
-       int reptype = -1, error, no_ram;
        u8 rxseq, txseq;
        u8 rxseq, txseq;
-       int i;
+       int lmi = hdlc->state.fr.settings.lmi;
+       int dce = hdlc->state.fr.settings.dce;
+       int stat_len = (lmi == LMI_CISCO) ? 6 : 3, reptype, error, no_ram, i;
 
 
-       if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI)
-                       ? LMI_ANSI_LENGTH : LMI_LENGTH)) {
+       if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH :
+                       LMI_CCITT_CISCO_LENGTH)) {
                printk(KERN_INFO "%s: Short LMI frame\n", dev->name);
                return 1;
        }
 
                printk(KERN_INFO "%s: Short LMI frame\n", dev->name);
                return 1;
        }
 
-       if (skb->data[5] != (!hdlc->state.fr.settings.dce ?
-                            LMI_STATUS : LMI_STATUS_ENQUIRY)) {
-               printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n",
-                      dev->name, skb->data[2],
-                      hdlc->state.fr.settings.dce ? "enquiry" : "reply");
+       if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI :
+                            NLPID_CCITT_ANSI_LMI)) {
+               printk(KERN_INFO "%s: Received non-LMI frame with LMI"
+                      " DLCI\n", dev->name);
+               return 1;
+       }
+
+       if (skb->data[4] != LMI_CALLREF) {
+               printk(KERN_INFO "%s: Invalid LMI Call reference (0x%02X)\n",
+                      dev->name, skb->data[4]);
+               return 1;
+       }
+
+       if (skb->data[5] != (dce ? LMI_STATUS_ENQUIRY : LMI_STATUS)) {
+               printk(KERN_INFO "%s: Invalid LMI Message type (0x%02X)\n",
+                      dev->name, skb->data[5]);
                return 1;
        }
 
                return 1;
        }
 
-       i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6;
+       if (lmi == LMI_ANSI) {
+               if (skb->data[6] != LMI_ANSI_LOCKSHIFT) {
+                       printk(KERN_INFO "%s: Not ANSI locking shift in LMI"
+                              " message (0x%02X)\n", dev->name, skb->data[6]);
+                       return 1;
+               }
+               i = 7;
+       } else
+               i = 6;
 
 
-       if (skb->data[i] !=
-           ((hdlc->state.fr.settings.lmi == LMI_CCITT)
-            ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) {
-               printk(KERN_INFO "%s: Not a report type=%x\n",
+       if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_REPTYPE :
+                            LMI_ANSI_CISCO_REPTYPE)) {
+               printk(KERN_INFO "%s: Not an LMI Report type IE (0x%02X)\n",
                       dev->name, skb->data[i]);
                return 1;
        }
                       dev->name, skb->data[i]);
                return 1;
        }
-       i++;
 
 
-       i++;                            /* Skip length field */
+       if (skb->data[++i] != LMI_REPT_LEN) {
+               printk(KERN_INFO "%s: Invalid LMI Report type IE length"
+                      " (%u)\n", dev->name, skb->data[i]);
+               return 1;
+       }
 
 
-       reptype = skb->data[i++];
+       reptype = skb->data[++i];
+       if (reptype != LMI_INTEGRITY && reptype != LMI_FULLREP) {
+               printk(KERN_INFO "%s: Unsupported LMI Report type (0x%02X)\n",
+                      dev->name, reptype);
+               return 1;
+       }
 
 
-       if (skb->data[i]!=
-           ((hdlc->state.fr.settings.lmi == LMI_CCITT)
-            ? LMI_CCITT_ALIVE : LMI_ALIVE)) {
-               printk(KERN_INFO "%s: Unsupported status element=%x\n",
-                      dev->name, skb->data[i]);
+       if (skb->data[++i] != (lmi == LMI_CCITT ? LMI_CCITT_ALIVE :
+                              LMI_ANSI_CISCO_ALIVE)) {
+               printk(KERN_INFO "%s: Not an LMI Link integrity verification"
+                      " IE (0x%02X)\n", dev->name, skb->data[i]);
                return 1;
        }
                return 1;
        }
-       i++;
 
 
-       i++;                    /* Skip length field */
+       if (skb->data[++i] != LMI_INTEG_LEN) {
+               printk(KERN_INFO "%s: Invalid LMI Link integrity verification"
+                      " IE length (%u)\n", dev->name, skb->data[i]);
+               return 1;
+       }
+       i++;
 
        hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
        rxseq = skb->data[i++]; /* Should confirm our sequence */
 
        txseq = hdlc->state.fr.txseq;
 
 
        hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
        rxseq = skb->data[i++]; /* Should confirm our sequence */
 
        txseq = hdlc->state.fr.txseq;
 
-       if (hdlc->state.fr.settings.dce) {
-               if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) {
-                       printk(KERN_INFO "%s: Unsupported report type=%x\n",
-                              dev->name, reptype);
-                       return 1;
-               }
+       if (dce)
                hdlc->state.fr.last_poll = jiffies;
                hdlc->state.fr.last_poll = jiffies;
-       }
 
        error = 0;
        if (!hdlc->state.fr.reliable)
 
        error = 0;
        if (!hdlc->state.fr.reliable)
@@ -703,7 +727,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
                error = 1;
        }
 
                error = 1;
        }
 
-       if (hdlc->state.fr.settings.dce) {
+       if (dce) {
                if (hdlc->state.fr.fullrep_sent && !error) {
 /* Stop sending full report - the last one has been confirmed by DTE */
                        hdlc->state.fr.fullrep_sent = 0;
                if (hdlc->state.fr.fullrep_sent && !error) {
 /* Stop sending full report - the last one has been confirmed by DTE */
                        hdlc->state.fr.fullrep_sent = 0;
@@ -725,6 +749,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
                        hdlc->state.fr.dce_changed = 0;
                }
 
                        hdlc->state.fr.dce_changed = 0;
                }
 
+               hdlc->state.fr.request = 1; /* got request */
                fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0);
                return 0;
        }
                fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0);
                return 0;
        }
@@ -739,7 +764,6 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
        if (reptype != LMI_FULLREP)
                return 0;
 
        if (reptype != LMI_FULLREP)
                return 0;
 
-       stat_len = 3;
        pvc = hdlc->state.fr.first_pvc;
 
        while (pvc) {
        pvc = hdlc->state.fr.first_pvc;
 
        while (pvc) {
@@ -750,24 +774,35 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
        no_ram = 0;
        while (skb->len >= i + 2 + stat_len) {
                u16 dlci;
        no_ram = 0;
        while (skb->len >= i + 2 + stat_len) {
                u16 dlci;
+               u32 bw;
                unsigned int active, new;
 
                unsigned int active, new;
 
-               if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT)
-                                    ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
-                       printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n",
-                              dev->name, skb->data[i]);
+               if (skb->data[i] != (lmi == LMI_CCITT ? LMI_CCITT_PVCSTAT :
+                                      LMI_ANSI_CISCO_PVCSTAT)) {
+                       printk(KERN_INFO "%s: Not an LMI PVC status IE"
+                              " (0x%02X)\n", dev->name, skb->data[i]);
                        return 1;
                }
                        return 1;
                }
-               i++;
 
 
-               if (skb->data[i] != stat_len) {
-                       printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n",
-                              dev->name, skb->data[i]);
+               if (skb->data[++i] != stat_len) {
+                       printk(KERN_INFO "%s: Invalid LMI PVC status IE length"
+                              " (%u)\n", dev->name, skb->data[i]);
                        return 1;
                }
                i++;
 
                        return 1;
                }
                i++;
 
-               dlci = status_to_dlci(skb->data + i, &active, &new);
+               new = !! (skb->data[i + 2] & 0x08);
+               active = !! (skb->data[i + 2] & 0x02);
+               if (lmi == LMI_CISCO) {
+                       dlci = (skb->data[i] << 8) | skb->data[i + 1];
+                       bw = (skb->data[i + 3] << 16) |
+                               (skb->data[i + 4] << 8) |
+                               (skb->data[i + 5]);
+               } else {
+                       dlci = ((skb->data[i] & 0x3F) << 4) |
+                               ((skb->data[i + 1] & 0x78) >> 3);
+                       bw = 0;
+               }
 
                pvc = add_pvc(dev, dlci);
 
 
                pvc = add_pvc(dev, dlci);
 
@@ -783,9 +818,11 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
                        pvc->state.deleted = 0;
                        if (active != pvc->state.active ||
                            new != pvc->state.new ||
                        pvc->state.deleted = 0;
                        if (active != pvc->state.active ||
                            new != pvc->state.new ||
+                           bw != pvc->state.bandwidth ||
                            !pvc->state.exist) {
                                pvc->state.new = new;
                                pvc->state.active = active;
                            !pvc->state.exist) {
                                pvc->state.new = new;
                                pvc->state.active = active;
+                               pvc->state.bandwidth = bw;
                                pvc_carrier(active, pvc);
                                fr_log_dlci_active(pvc);
                        }
                                pvc_carrier(active, pvc);
                                fr_log_dlci_active(pvc);
                        }
@@ -801,6 +838,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
                        pvc_carrier(0, pvc);
                        pvc->state.active = pvc->state.new = 0;
                        pvc->state.exist = 0;
                        pvc_carrier(0, pvc);
                        pvc->state.active = pvc->state.new = 0;
                        pvc->state.exist = 0;
+                       pvc->state.bandwidth = 0;
                        fr_log_dlci_active(pvc);
                }
                pvc = pvc->next;
                        fr_log_dlci_active(pvc);
                }
                pvc = pvc->next;
@@ -829,22 +867,15 @@ static int fr_rx(struct sk_buff *skb)
 
        dlci = q922_to_dlci(skb->data);
 
 
        dlci = q922_to_dlci(skb->data);
 
-       if (dlci == LMI_DLCI) {
-               if (hdlc->state.fr.settings.lmi == LMI_NONE)
-                       goto rx_error; /* LMI packet with no LMI? */
-
-               if (data[3] == LMI_PROTO) {
-                       if (fr_lmi_recv(ndev, skb))
-                               goto rx_error;
-                       else {
-                               dev_kfree_skb_any(skb);
-                               return NET_RX_SUCCESS;
-                       }
-               }
-
-               printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
-                      ndev->name);
-               goto rx_error;
+       if ((dlci == LMI_CCITT_ANSI_DLCI &&
+            (hdlc->state.fr.settings.lmi == LMI_ANSI ||
+             hdlc->state.fr.settings.lmi == LMI_CCITT)) ||
+           (dlci == LMI_CISCO_DLCI &&
+            hdlc->state.fr.settings.lmi == LMI_CISCO)) {
+               if (fr_lmi_recv(ndev, skb))
+                       goto rx_error;
+               dev_kfree_skb_any(skb);
+               return NET_RX_SUCCESS;
        }
 
        pvc = find_pvc(hdlc, dlci);
        }
 
        pvc = find_pvc(hdlc, dlci);
@@ -1170,7 +1201,8 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
 
                if ((new_settings.lmi != LMI_NONE &&
                     new_settings.lmi != LMI_ANSI &&
 
                if ((new_settings.lmi != LMI_NONE &&
                     new_settings.lmi != LMI_ANSI &&
-                    new_settings.lmi != LMI_CCITT) ||
+                    new_settings.lmi != LMI_CCITT &&
+                    new_settings.lmi != LMI_CISCO) ||
                    new_settings.t391 < 1 ||
                    new_settings.t392 < 2 ||
                    new_settings.n391 < 1 ||
                    new_settings.t391 < 1 ||
                    new_settings.t392 < 2 ||
                    new_settings.n391 < 1 ||
index 6ed064c..a63f6a2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Generic HDLC support routines for Linux
  *
 /*
  * 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
  *
  * 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
@@ -38,7 +38,7 @@
 #include <linux/hdlc.h>
 
 
 #include <linux/hdlc.h>
 
 
-static const char* version = "HDLC support module revision 1.17";
+static const char* version = "HDLC support module revision 1.18";
 
 #undef DEBUG_LINK
 
 
 #undef DEBUG_LINK
 
@@ -126,10 +126,13 @@ void hdlc_set_carrier(int on, struct net_device *dev)
        if (!hdlc->open)
                goto carrier_exit;
 
        if (!hdlc->open)
                goto carrier_exit;
 
-       if (hdlc->carrier)
+       if (hdlc->carrier) {
+               printk(KERN_INFO "%s: Carrier detected\n", dev->name);
                __hdlc_set_carrier_on(dev);
                __hdlc_set_carrier_on(dev);
-       else
+       } else {
+               printk(KERN_INFO "%s: Carrier lost\n", dev->name);
                __hdlc_set_carrier_off(dev);
                __hdlc_set_carrier_off(dev);
+       }
 
 carrier_exit:
        spin_unlock_irqrestore(&hdlc->state_lock, flags);
 
 carrier_exit:
        spin_unlock_irqrestore(&hdlc->state_lock, flags);
@@ -157,8 +160,11 @@ int hdlc_open(struct net_device *dev)
 
        spin_lock_irq(&hdlc->state_lock);
 
 
        spin_lock_irq(&hdlc->state_lock);
 
-       if (hdlc->carrier)
+       if (hdlc->carrier) {
+               printk(KERN_INFO "%s: Carrier detected\n", dev->name);
                __hdlc_set_carrier_on(dev);
                __hdlc_set_carrier_on(dev);
+       } else
+               printk(KERN_INFO "%s: No carrier\n", dev->name);
 
        hdlc->open = 1;
 
 
        hdlc->open = 1;
 
index 503194e..ed2927e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Generic HDLC support routines for Linux
  *
 /*
  * 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
  *
  * 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
@@ -41,6 +41,7 @@
 #define LMI_NONE               1 /* No LMI, all PVCs are static */
 #define LMI_ANSI               2 /* ANSI Annex D */
 #define LMI_CCITT              3 /* ITU-T Annex A */
 #define LMI_NONE               1 /* No LMI, all PVCs are static */
 #define LMI_ANSI               2 /* ANSI Annex D */
 #define LMI_CCITT              3 /* ITU-T Annex A */
+#define LMI_CISCO              4 /* The "original" LMI, aka Gang of Four */
 
 #define HDLC_MAX_MTU 1500      /* Ethernet 1500 bytes */
 #define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */
 
 #define HDLC_MAX_MTU 1500      /* Ethernet 1500 bytes */
 #define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */
@@ -89,6 +90,7 @@ typedef struct pvc_device_struct {
                unsigned int deleted: 1;
                unsigned int fecn: 1;
                unsigned int becn: 1;
                unsigned int deleted: 1;
                unsigned int fecn: 1;
                unsigned int becn: 1;
+               unsigned int bandwidth; /* Cisco LMI reporting only */
        }state;
 }pvc_device;
 
        }state;
 }pvc_device;