]> git.hungrycats.org Git - linux/commitdiff
WAN drivers update 1/5:
authorFrançois Romieu <romieu@cogenit.fr>
Wed, 6 Mar 2002 21:37:33 +0000 (16:37 -0500)
committerJeff Garzik <jgarzik@mandrakesoft.com>
Wed, 6 Mar 2002 21:37:33 +0000 (16:37 -0500)
Add new HDLC interface, split up huge hdlc.c driver into
multiple files based on hardware type.  Convert WAN drivers
to new interface.

23 files changed:
Documentation/networking/generic-hdlc.txt [new file with mode: 0644]
drivers/net/wan/Config.help
drivers/net/wan/Config.in
drivers/net/wan/Makefile
drivers/net/wan/c101.c
drivers/net/wan/dscc4.c
drivers/net/wan/farsync.c
drivers/net/wan/farsync.h
drivers/net/wan/hd64570.h
drivers/net/wan/hd6457x.c
drivers/net/wan/hdlc.c [deleted file]
drivers/net/wan/hdlc_cisco.c [new file with mode: 0644]
drivers/net/wan/hdlc_fr.c [new file with mode: 0644]
drivers/net/wan/hdlc_generic.c [new file with mode: 0644]
drivers/net/wan/hdlc_ppp.c [new file with mode: 0644]
drivers/net/wan/hdlc_raw.c [new file with mode: 0644]
drivers/net/wan/hdlc_x25.c [new file with mode: 0644]
drivers/net/wan/n2.c
include/linux/hdlc.h
include/linux/if.h
include/linux/if_ether.h
include/linux/sockios.h
net/core/dev.c

diff --git a/Documentation/networking/generic-hdlc.txt b/Documentation/networking/generic-hdlc.txt
new file mode 100644 (file)
index 0000000..37631c9
--- /dev/null
@@ -0,0 +1,115 @@
+Generic HDLC layer for Linux kernel 2.4/2.5
+Krzysztof Halasa <khc@pm.waw.pl>
+May, 2001
+
+
+Generic HDLC layer currently supports:
+- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP),
+- raw HDLC (IPv4 only),
+- 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.
+
+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/
+
+Compile sethdlc.c utility:
+       gcc -O2 -Wall -o sethdlc sethdlc.c
+Make sure you're using a correct version of sethdlc for your kernel.
+
+Use sethdlc to set physical interface, clock rate, HDLC mode used,
+and add any required PVCs if using Frame Relay.
+Usually you want something like:
+
+       sethdlc hdlc0 clock int rate 128000
+       sethdlc hdlc0 cisco interval 10 timeout 25
+or
+       sethdlc hdlc0 rs232 clock ext
+       sethdlc fr lmi ansi
+       sethdlc create 99
+
+In Frame Relay mode, ifconfig master hdlc device up (without assigning
+any IP address to it) before using pvc devices.
+
+
+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)
+* 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)
+
+Setting protocol:
+
+* hdlc - sets raw HDLC (IP-only) mode
+  nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code
+  no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
+  crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity
+
+* cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported)
+  interval - time in seconds between keepalive packets
+  timeout - time in seconds after last received keepalive packet before
+            we assume the link is down
+
+* ppp - sets synchronous PPP mode
+
+* x25 - sets X.25 mode
+
+* fr - Frame Relay mode
+  lmi ansi / ccitt / 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
+  t392 - polling verification timer (in seconds) - network
+  n391 - full status polling counter - user
+  n392 - error threshold - both user and network
+  n393 - monitored events count - both user and network
+
+* create | delete n - FR only - adds / deletes PVC interface with DLCI #n.
+
+
+
+
+Board-specific issues
+---------------------
+
+n2.o and c101.o need parameters to work (note double quotes):
+
+       insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"'
+example:
+       insmod n2 hw='"0x300,10,0xD0000,01"'
+
+or
+       insmod c101 hw='"irq,ram[:irq,...]"
+example:
+       insmod c101 hw='"9,0xdc000"'
+
+If built into the kernel, these drivers need kernel (command line) parameters:
+       n2=io,irq,ram,ports:...
+or
+       c101=irq,ram:...
+
+
+
+If you have a problem with N2 or C101 card, you can issue the "private"
+command to see port's packet descriptor rings:
+
+       sethdlc hdlc0 private
+
+The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS.
+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/
index d9ecd0636727697f1acc098b4bfef4f2cb871973..eebc3287fa86319d490e1388f9633070137e08fe 100644 (file)
@@ -41,14 +41,15 @@ CONFIG_FARSYNC
   This driver supports the FarSync T-Series X.21 (and V.35/V.24) cards
   from FarSite Communications Ltd.
   Synchronous communication is supported on all ports at speeds up to
-  8Mb/s (128K on V.24) using synchronous PPP or Cisco HDLC.
+  8Mb/s (128K on V.24) using synchronous PPP, Cisco HDLC, raw HDLC,
+  Frame Relay or X.25/LAPB.
 
   If you want to compile this driver as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want)
   say M here and read <file:Documentation/modules.txt>.
   The module will be called farsync.o and if you want the module to be
   automatically loaded when the interface is referenced then you
-  should add "alias syncX farsync" to /etc/modules.conf for each
+  should add "alias hdlcX farsync" to /etc/modules.conf for each
   interface, where X is 0, 1, 2, ...
 
 CONFIG_DLCI
index 0a910e1611d9d5a4448f24c46c1f855504a0e97f..9f0e4c3c5c1c91967aab3848c389dea69bd06891 100644 (file)
@@ -39,9 +39,6 @@ if [ "$CONFIG_WAN" = "y" ]; then
 
    dep_tristate '  Etinc PCISYNC serial board support (EXPERIMENTAL)' CONFIG_DSCC4 m
 
-# FarSite Communications' cards
-
-   tristate '  FarSync T-Series X.21 (and V.35/V.24) cards' CONFIG_FARSYNC
 
 #
 # Lan Media's board. Currently 1000, 1200, 5200, 5245
@@ -55,8 +52,13 @@ if [ "$CONFIG_WAN" = "y" ]; then
 
    tristate '  SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP
 
-   tristate '  Generic HDLC driver' CONFIG_HDLC
+# Generic HDLC
+
+   tristate '  Generic HDLC layer' CONFIG_HDLC
    if [ "$CONFIG_HDLC" != "n" ]; then
+      bool '    Raw HDLC support' CONFIG_HDLC_RAW
+      bool '    Cisco HDLC support' CONFIG_HDLC_CISCO
+      bool '    Frame Relay support' CONFIG_HDLC_FR
       bool '    Synchronous Point-to-Point Protocol (PPP) support' CONFIG_HDLC_PPP
       if [ "$CONFIG_LAPB" = "m" -a "$CONFIG_HDLC" = "m" -o "$CONFIG_LAPB" = "y" ]; then
         bool '    X.25 protocol support' CONFIG_HDLC_X25
@@ -65,6 +67,11 @@ if [ "$CONFIG_WAN" = "y" ]; then
       fi
       dep_tristate '    SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC
       dep_tristate '    Moxa C101 support' CONFIG_C101 $CONFIG_HDLC
+      dep_tristate '    FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC
+      bool '    Debug received/transmitted packets' CONFIG_HDLC_DEBUG_PKT
+      bool '    Debug hard_header routines' CONFIG_HDLC_DEBUG_HARD_HEADER
+      bool '    Debug FECN/BECN conditions' CONFIG_HDLC_DEBUG_ECN
+      bool '    Debug RX/TX packet rings' CONFIG_HDLC_DEBUG_RINGS
    fi
 
    tristate '  Frame relay DLCI support' CONFIG_DLCI
index 988990ef0848a1c203f2653ef593a53dfd4eaca0..04e5538586bcdbd6ae8f89501308d3b739264d71 100644 (file)
@@ -9,7 +9,7 @@ SUB_DIRS     :=
 
 O_TARGET := wan.o
 
-export-objs =  z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o
+export-objs =  z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o
 list-multi =   wanpipe.o cyclomx.o
 
 wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y)
@@ -22,6 +22,11 @@ wanpipe-$(CONFIG_WANPIPE_MULTPPP) += wanpipe_multppp.o
 cyclomx-objs = cycx_main.o $(cyclomx-y)  
 cyclomx-$(CONFIG_CYCLOMX_X25)  += cycx_x25.o
 
+hdlc-$(CONFIG_HDLC_RAW)                += hdlc_raw.o
+hdlc-$(CONFIG_HDLC_CISCO)      += hdlc_cisco.o
+hdlc-$(CONFIG_HDLC_FR)         += hdlc_fr.o
+hdlc-$(CONFIG_HDLC_PPP)                += hdlc_ppp.o
+hdlc-$(CONFIG_HDLC_X25)                += hdlc_x25.o
 
 obj-$(CONFIG_HOSTESS_SV11)     += z85230.o     syncppp.o       hostess_sv11.o
 obj-$(CONFIG_SEALEVEL_4021)    += z85230.o     syncppp.o       sealevel.o
@@ -56,12 +61,17 @@ obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o
 obj-$(CONFIG_LAPBETHER)                += lapbether.o
 obj-$(CONFIG_SBNI)             += sbni.o
 obj-$(CONFIG_HDLC)             += hdlc.o
-obj-$(CONFIG_HDLC_PPP)         += syncppp.o
+ifeq ($(CONFIG_HDLC_PPP),y)
+  obj-$(CONFIG_HDLC)           += syncppp.o
+endif
 obj-$(CONFIG_N2)               += n2.o
 obj-$(CONFIG_C101)             += c101.o
 
 include $(TOPDIR)/Rules.make
 
+hdlc.o: hdlc_generic.o $(hdlc-y)
+       $(LD) -r -o $@ hdlc_generic.o $(hdlc-y)
+
 wanpipe.o: $(wanpipe-objs)
        $(LD) -r -o $@ $(wanpipe-objs)
 
index e8f413ef9489379d35f24dc1b7e69dd4c63bb82c..4fe768a46521347e7bb1ce2b2122b85e34b4465f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Moxa C101 synchronous serial card driver for Linux
  *
- * Copyright (C) 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000-2001 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by
@@ -15,6 +15,7 @@
  *    Moxa C101 User's Manual
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 
 #include "hd64570.h"
 
-#define DEBUG_RINGS
-/* #define DEBUG_PKT */
 
-static const char* version = "Moxa C101 driver revision: 1.02 for Linux 2.4";
+static const char* version = "Moxa C101 driver version: 1.09";
 static const char* devname = "C101";
 
 #define C101_PAGE 0x1D00
@@ -51,12 +50,12 @@ static char *hw;            /* pointer to hw=xxx command line string */
 typedef struct card_s {
        hdlc_device hdlc;       /* HDLC device struct - must be first */
        spinlock_t lock;        /* TX lock */
-       int clkmode;            /* clock mode */
-       int clkrate;            /* clock speed */
-       int line;               /* loopback only */
        u8 *win0base;           /* ISA window base address */
        u32 phy_winbase;        /* ISA physical base address */
        u16 buff_offset;        /* offset of first buffer of first channel */
+       sync_serial_settings settings;
+       unsigned short encoding;
+       unsigned short parity;
        u8 rxs, txs, tmc;       /* SCA registers */
        u8 irq;                 /* IRQ (3-15) */
        u8 ring_buffers;        /* number of buffers in a ring */
@@ -72,6 +71,9 @@ typedef struct card_s {
 
 typedef card_t port_t;
 
+static card_t *first_card;
+static card_t **new_card = &first_card;
+
 
 #define sca_in(reg, card)         readb((card)->win0base + C101_SCA + (reg))
 #define sca_out(value, reg, card)  writeb(value, (card)->win0base + C101_SCA + (reg))
@@ -105,13 +107,13 @@ static inline void openwin(card_t *card, u8 page)
 #include "hd6457x.c"
 
 
-static int c101_set_clock(port_t *port, int value)
+static int c101_set_iface(port_t *port)
 {
        u8 msci = get_msci(port);
        u8 rxs = port->rxs & CLK_BRG_MASK;
        u8 txs = port->txs & CLK_BRG_MASK;
 
-       switch(value) {
+       switch(port->settings.clock_type) {
        case CLOCK_EXT:
                rxs |= CLK_LINE_RX; /* RXC input */
                txs |= CLK_LINE_TX; /* TXC input */
@@ -140,76 +142,85 @@ static int c101_set_clock(port_t *port, int value)
        port->txs = txs;
        sca_out(rxs, msci + RXS, port);
        sca_out(txs, msci + TXS, port);
-       port->clkmode = value;
+       sca_set_port(port);
        return 0;
 }
 
 
-static int c101_open(hdlc_device *hdlc)
+static int c101_open(struct net_device *dev)
 {
+       hdlc_device *hdlc = dev_to_hdlc(dev);
        port_t *port = hdlc_to_port(hdlc);
+       int result = hdlc_open(hdlc);
+       if (result)
+               return result;
 
        MOD_INC_USE_COUNT;
        writeb(1, port->win0base + C101_DTR);
        sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */
        sca_open(hdlc);
-       c101_set_clock(port, port->clkmode);
-       return 0;
+       return c101_set_iface(port);
 }
 
 
-static void c101_close(hdlc_device *hdlc)
+static int c101_close(struct net_device *dev)
 {
+       hdlc_device *hdlc = dev_to_hdlc(dev);
        port_t *port = hdlc_to_port(hdlc);
 
        sca_close(hdlc);
        writeb(0, port->win0base + C101_DTR);
        sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port);
+       hdlc_close(hdlc);
        MOD_DEC_USE_COUNT;
+       return 0;
 }
 
 
-static int c101_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       int value = ifr->ifr_ifru.ifru_ivalue;
-       int result = 0;
+       hdlc_device *hdlc = dev_to_hdlc(dev);
        port_t *port = hdlc_to_port(hdlc);
+       const size_t size = sizeof(sync_serial_settings);
 
-       if(!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       switch(cmd) {
-       case HDLCSCLOCK:
-               result = c101_set_clock(port, value);
-       case HDLCGCLOCK:
-               value = port->clkmode;
-               break;
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+       if (cmd == SIOCDEVPRIVATE) {
+               sca_dump_rings(hdlc);
+               return 0;
+       }
+#endif
+       if (cmd != SIOCDEVICE)
+               return hdlc_ioctl(dev, ifr, cmd);
+
+       switch(ifr->ifr_settings.type) {
+       case IF_GET_IFACE:
+               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+               if (ifr->ifr_settings.data_length == 0)
+                       return 0; /* return interface type only */
+               if (ifr->ifr_settings.data_length < size)
+                       return -ENOMEM; /* buffer too small */
+               if (copy_to_user(ifr->ifr_settings.data,
+                                &port->settings, size))
+                       return -EFAULT;
+               ifr->ifr_settings.data_length = size;
+               return 0;
 
-       case HDLCSCLOCKRATE:
-               port->clkrate = value;
-               sca_set_clock(port);
-       case HDLCGCLOCKRATE:
-               value = port->clkrate;
-               break;
+       case IF_IFACE_SYNC_SERIAL:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
 
-       case HDLCSLINE:
-               result = sca_set_loopback(port, value);
-       case HDLCGLINE:
-               value = port->line;
-               break;
+               if (ifr->ifr_settings.data_length != size)
+                       return -ENOMEM; /* incorrect data length */
 
-#ifdef DEBUG_RINGS
-       case HDLCRUN:
-               sca_dump_rings(hdlc);
-               return 0;
-#endif /* DEBUG_RINGS */
+               if (copy_from_user(&port->settings,
+                                  ifr->ifr_settings.data, size))
+                       return -EFAULT;
+               /* FIXME - put sanity checks here */
+               return c101_set_iface(port);
 
        default:
-               return -EINVAL;
+               return hdlc_ioctl(dev, ifr, cmd);
        }
-
-       ifr->ifr_ifru.ifru_ivalue = value;
-       return result;
 }
 
 
@@ -231,6 +242,7 @@ static void c101_destroy_card(card_t *card)
 
 static int c101_run(unsigned long irq, unsigned long winbase)
 {
+       struct net_device *dev;
        card_t *card;
        int result;
 
@@ -284,15 +296,19 @@ static int c101_run(unsigned long irq, unsigned long winbase)
 
        sca_init(card, 0);
 
+       dev = hdlc_to_dev(&card->hdlc);
+
        spin_lock_init(&card->lock);
-       hdlc_to_dev(&card->hdlc)->irq = irq;
-       hdlc_to_dev(&card->hdlc)->mem_start = winbase;
-       hdlc_to_dev(&card->hdlc)->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1;
-       hdlc_to_dev(&card->hdlc)->tx_queue_len = 50;
-       card->hdlc.ioctl = c101_ioctl;
-       card->hdlc.open = c101_open;
-       card->hdlc.close = c101_close;
+       dev->irq = irq;
+       dev->mem_start = winbase;
+       dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1;
+       dev->tx_queue_len = 50;
+       dev->do_ioctl = c101_ioctl;
+       dev->open = c101_open;
+       dev->stop = c101_close;
+       card->hdlc.attach = sca_attach;
        card->hdlc.xmit = sca_xmit;
+       card->settings.clock_type = CLOCK_EXT;
 
        result = register_hdlc_device(&card->hdlc);
        if (result) {
@@ -319,7 +335,7 @@ static int __init c101_init(void)
                return -ENOSYS; /* no parameters specified, abort */
        }
 
-       printk(KERN_INFO "%s\n", version);
+       printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
 
        do {
                unsigned long irq, ram;
index cc48edb4c8096b59d602a40fcacc185117ee2635..0d55b479ec00870763d4988261d37ce8bffa6670 100644 (file)
@@ -72,7 +72,7 @@
  * the documentation/chipset releases. An on-line errata would be welcome.
  *
  * TODO:
- * - syncppp oopses. X25 untested.
+ * - test X25.
  * - use polling at high irq/s,
  * - performance analysis,
  * - endianness.
@@ -91,6 +91,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/list.h>
 #include <linux/ioport.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
 #include <linux/hdlc.h>
 
 /* Version */
-static const char version[] = "$Id: dscc4.c,v 1.157 2002/01/28 01:54:19 romieu Exp $\n";
+static const char version[] = "$Id: dscc4.c,v 1.158 2002/01/30 00:40:37 romieu Exp $\n";
 static int debug;
 static int quartz;
 
@@ -168,9 +169,9 @@ struct RxFD {
 #define BRR_DIVIDER_MAX 64*0x00008000
 #define dev_per_card   4
 
-#define SOURCE_ID(flags) ((flags >> 28 ) & 0x03)
-#define TO_SIZE(state) ((state >> 16) & 0x1fff)
-#define TO_STATE(len) cpu_to_le32((len & TxSizeMax) << 16)
+#define SOURCE_ID(flags) (((flags) >> 28 ) & 0x03)
+#define TO_SIZE(state) (((state) >> 16) & 0x1fff)
+#define TO_STATE(len) cpu_to_le32(((len) & TxSizeMax) << 16)
 #define RX_MAX(len) ((((len) >> 5) + 1)<< 5)
 #define SCC_REG_START(id) SCC_START+(id)*SCC_OFFSET
 
@@ -343,6 +344,11 @@ static inline int dscc4_set_quartz(struct dscc4_dev_priv *, int);
 static int dscc4_tx_poll(struct dscc4_dev_priv *, struct net_device *);
 #endif
 
+static inline struct dscc4_dev_priv *dscc4_priv(struct net_device *dev)
+{
+       return list_entry(dev, struct dscc4_dev_priv, hdlc.netdev);
+}
+
 static inline void dscc4_patch_register(u32 ioaddr, u32 mask, u32 value)
 {
        u32 state;
@@ -479,52 +485,49 @@ static int dscc4_do_action(struct net_device *dev, char *msg)
 
 static inline int dscc4_xpr_ack(struct dscc4_dev_priv *dpriv)
 {
-       int cur, ret = 0;
-       s16 i;
+       int cur = dpriv->iqtx_current%IRQ_RING_SIZE;
+       s16 i = 0;
 
-       cur = dpriv->iqtx_current%IRQ_RING_SIZE;
-       for (i = 0; i >= 0; i++) {
+       do {
                if (!(dpriv->flags & (NeedIDR | NeedIDT)) ||
                    (dpriv->iqtx[cur] & Xpr))
                        break;
                smp_rmb();
-       }
-       if (i < 0) {
-               printk(KERN_ERR "%s: %s timeout\n", "dscc4", "XPR");
-               ret = -1;
-       }
-       return ret;
+       } while (i++ >= 0);
+
+       return i;
 }
 
 static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, int cur,
        struct RxFD *rx_fd, struct net_device *dev)
 {
+       struct net_device_stats *stats = &dev_to_hdlc(dev)->stats;
        struct pci_dev *pdev = dpriv->pci_priv->pdev;
        struct sk_buff *skb;
        int pkt_len;
 
        skb = dpriv->rx_skbuff[cur];
-       pkt_len = TO_SIZE(rx_fd->state2) - 1;
-       pci_dma_sync_single(pdev, rx_fd->data, pkt_len + 1, PCI_DMA_FROMDEVICE);
-       if((skb->data[pkt_len] & FrameOk) == FrameOk) {
+       pkt_len = TO_SIZE(rx_fd->state2);
+       pci_dma_sync_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE);
+       if((skb->data[--pkt_len] & FrameOk) == FrameOk) {
                pci_unmap_single(pdev, rx_fd->data, skb->len, PCI_DMA_FROMDEVICE);
-               dev_to_hdlc(dev)->stats.rx_packets++;
-               dev_to_hdlc(dev)->stats.rx_bytes += pkt_len;
+               stats->rx_packets++;
+               stats->rx_bytes += pkt_len;
                skb->tail += pkt_len;
                skb->len = pkt_len;
-                       if (netif_running(hdlc_to_dev(&dpriv->hdlc)))
+                       if (netif_running(dev))
                        skb->protocol = htons(ETH_P_HDLC);
                netif_rx(skb);
                try_get_rx_skb(dpriv, cur, dev);
        } else {
                if(skb->data[pkt_len] & FrameRdo)
-                       dev_to_hdlc(dev)->stats.rx_fifo_errors++;
+                       stats->rx_fifo_errors++;
                else if(!(skb->data[pkt_len] | ~FrameCrc))
-                       dev_to_hdlc(dev)->stats.rx_crc_errors++;
+                       stats->rx_crc_errors++;
                else if(!(skb->data[pkt_len] | ~(FrameVfr | FrameRab)))
-                       dev_to_hdlc(dev)->stats.rx_length_errors++;
+                       stats->rx_length_errors++;
                else
-                       dev_to_hdlc(dev)->stats.rx_errors++;
+                       stats->rx_errors++;
        }
        rx_fd->state1 |= Hold;
        rx_fd->state2 = 0x00000000;
@@ -612,7 +615,7 @@ static int __init dscc4_init_one (struct pci_dev *pdev,
         * SCC 0-3 private rx/tx irq structures 
         * IQRX/TXi needs to be set soon. Learned it the hard way...
         */
-       for(i = 0; i < dev_per_card; i++) {
+       for (i = 0; i < dev_per_card; i++) {
                dpriv = priv->root + i;
                dpriv->iqtx = (u32 *) pci_alloc_consistent(pdev,
                        IRQ_RING_SIZE*sizeof(u32), &dpriv->iqtx_dma);
@@ -620,7 +623,7 @@ static int __init dscc4_init_one (struct pci_dev *pdev,
                        goto err_out_free_iqtx;
                writel(dpriv->iqtx_dma, ioaddr + IQTX0 + i*4);
        }
-       for(i = 0; i < dev_per_card; i++) {
+       for (i = 0; i < dev_per_card; i++) {
                dpriv = priv->root + i;
                dpriv->iqrx = (u32 *) pci_alloc_consistent(pdev,
                        IRQ_RING_SIZE*sizeof(u32), &dpriv->iqrx_dma);
@@ -740,7 +743,6 @@ static int dscc4_found1(struct pci_dev *pdev, unsigned long ioaddr)
                dpriv->dev_id = i;
                dpriv->pci_priv = ppriv;
                spin_lock_init(&dpriv->lock);
-               d->priv = dpriv;
 
                hdlc->xmit = dscc4_start_xmit;
                hdlc->attach = dscc4_hdlc_attach;
@@ -778,7 +780,7 @@ static void dscc4_timer(unsigned long data)
        struct dscc4_dev_priv *dpriv;
        struct dscc4_pci_priv *ppriv;
 
-       dpriv = dev->priv;
+       dpriv = dscc4_priv(dev);
        if (netif_queue_stopped(dev) && 
           ((jiffies - dev->trans_start) > TX_TIMEOUT)) {
                ppriv = dpriv->pci_priv;
@@ -844,7 +846,7 @@ static int dscc4_loopback_check(struct dscc4_dev_priv *dpriv)
 
 static int dscc4_open(struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        hdlc_device *hdlc = &dpriv->hdlc;
        struct dscc4_pci_priv *ppriv;
        u32 ioaddr;
@@ -893,8 +895,10 @@ static int dscc4_open(struct net_device *dev)
         * WARNING, a really missing XPR usually means a hardware 
         * reset is needed. Suggestions anyone ?
         */
-       if (dscc4_xpr_ack(dpriv))
+       if (dscc4_xpr_ack(dpriv) < 0) {
+               printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR");
                goto err_free_ring;
+       }
        
        netif_start_queue(dev);
 
@@ -925,7 +929,7 @@ static int dscc4_tx_poll(struct dscc4_dev_priv *dpriv, struct net_device *dev)
 
 static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        struct dscc4_pci_priv *ppriv;
        struct TxFD *tx_fd;
        int cur, next;
@@ -935,7 +939,6 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev)
        next = dpriv->tx_current%TX_RING_SIZE;
        dpriv->tx_skbuff[next] = skb;
        tx_fd = dpriv->tx_fd + next;
-       printk(KERN_DEBUG "%s: %d sent\n", dev->name, skb->len);
        tx_fd->state = FrameEnd | Hold | TO_STATE(skb->len);
        tx_fd->data = pci_map_single(ppriv->pdev, skb->data, skb->len,
                                     PCI_DMA_TODEVICE);
@@ -972,7 +975,7 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
 static int dscc4_close(struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        u32 ioaddr = dev->base_addr;
        int dev_id;
        hdlc_device *hdlc = dev_to_hdlc(dev);
@@ -1011,7 +1014,7 @@ static inline int dscc4_check_clock_ability(int port)
 
 static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state)
 {
-       struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        u32 brr;
 
        *state &= ~Ccr0ClockMask;
@@ -1062,7 +1065,7 @@ static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state)
 
 static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       struct dscc4_dev_priv *dpriv = dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        struct if_settings *if_s = &ifr->ifr_settings;
        const size_t size = sizeof(dpriv->settings);
        int ret = 0;
@@ -1133,7 +1136,7 @@ static int dscc4_match(struct thingie *p, int value)
 
 static int dscc4_clock_setting(struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        sync_serial_settings *settings = &dpriv->settings;
        u32 bps, state;
        u32 ioaddr;
@@ -1160,7 +1163,7 @@ static int dscc4_clock_setting(struct net_device *dev)
 
 static int dscc4_encoding_setting(struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        struct thingie encoding[] = {
                { ENCODING_NRZ,         0x00000000 },
                { ENCODING_NRZI,        0x00200000 },
@@ -1184,7 +1187,7 @@ static int dscc4_encoding_setting(struct net_device *dev)
 
 static int dscc4_loopback_setting(struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        sync_serial_settings *settings = &dpriv->settings;
        u32 ioaddr, state;
 
@@ -1203,7 +1206,7 @@ static int dscc4_loopback_setting(struct net_device *dev)
 
 static int dscc4_crc_setting(struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        struct thingie crc[] = {
                { PARITY_CRC16_PR0_CCITT,       0x00000010 },
                { PARITY_CRC16_PR1_CCITT,       0x00000000 },
@@ -1516,7 +1519,6 @@ try:
                }
        } else { /* ! SccEvt */
 #ifdef DEBUG_PARANOIA
-               int i;
                static struct {
                        u32 mask;
                        const char *irq_name;
@@ -1528,21 +1530,21 @@ try:
                        { 0x00000008, "PLLA"},
                        { 0x00000004, "CDSC"},
                        { 0, NULL}
-               };
+               }, *evt;
 #endif /* DEBUG_PARANOIA */
                state &= 0x00ffffff;
 #ifdef DEBUG_PARANOIA
-               for (i = 0; evts[i].irq_name; i++) {
-                       if (state & evts[i].mask) {
+               for (evt = evts; evt->irq_name; evt++) {
+                       if (state & evt->mask) {
                                printk(KERN_DEBUG "%s: %s\n", dev->name, 
-                                       evts[i].irq_name);
-                               if (!(state &= ~evts[i].mask))
+                                       evt->irq_name);
+                               if (!(state &= ~evt->mask))
                                        goto try;
                        }
                }
 #endif /* DEBUG_PARANOIA */
                /*
-                * Receive Data Overflow (FIXME: untested)
+                * Receive Data Overflow (FIXME: fscked)
                 */
                if (state & Rdo) {
                        u32 ioaddr, scc_offset, scc_addr;
@@ -1633,7 +1635,7 @@ try:
 
 static int dscc4_init_ring(struct net_device *dev)
 {
-       struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
        struct TxFD *tx_fd;
        struct RxFD *rx_fd;
        int i;
@@ -1654,7 +1656,7 @@ static int dscc4_init_ring(struct net_device *dev)
        dpriv->tx_dirty = 0;
 
        /* the dma core of the dscc4 will be locked on the first desc */
-       for(i = 0; i < TX_RING_SIZE; ) {
+       for (i = 0; i < TX_RING_SIZE; ) {
                reset_TxFD(tx_fd);
                /* FIXME: NULL should be ok - to be tried */
                tx_fd->data = dpriv->tx_fd_dma;
@@ -1689,7 +1691,7 @@ static int dscc4_init_ring(struct net_device *dev)
                                     skb->len, PCI_DMA_TODEVICE);
        dpriv->tx_skbuff[0] = skb;
 }
-       for (i = 0; i < RX_RING_SIZE;) {
+       for (i = 0; i < RX_RING_SIZE; ) {
                /* size set by the host. Multiple of 4 bytes please */
                rx_fd->state1 = HiDesc; /* Hi, no Hold */
                rx_fd->state2 = 0x00000000;
@@ -1753,7 +1755,8 @@ static void __exit dscc4_remove_one(struct pci_dev *pdev)
 static int dscc4_hdlc_attach(hdlc_device *hdlc, unsigned short encoding, 
        unsigned short parity)
 {
-       struct dscc4_dev_priv *dpriv = hdlc_to_dev(hdlc)->priv;
+       struct net_device *dev = hdlc_to_dev(hdlc);
+       struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
 
        if (encoding != ENCODING_NRZ &&
            encoding != ENCODING_NRZI &&
@@ -1782,7 +1785,7 @@ static struct pci_device_id dscc4_pci_tbl[] __devinitdata = {
 MODULE_DEVICE_TABLE(pci, dscc4_pci_tbl);
 
 static struct pci_driver dscc4_driver = {
-       name:           "dscc4",
+       name:           DRV_NAME,
        id_table:       dscc4_pci_tbl,
        probe:          dscc4_init_one,
        remove:         dscc4_remove_one,
index 64258f647ce47a280296585abb7b3c1bba84d365..79241627cf3c5a0595e623d1078f70c5762f51d4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *      FarSync X21 driver for Linux (2.4.x kernel version)
+ *      FarSync X21 driver for Linux (generic HDLC version)
  *
  *      Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards
  *
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
-#include <linux/netdevice.h>
 #include <linux/init.h>
-#include <linux/if_arp.h>
 #include <asm/uaccess.h>
-#include <net/syncppp.h>
+#include <linux/if.h>
+#include <linux/hdlc.h>
 
 #include "farsync.h"
 
@@ -32,6 +31,7 @@
  */
 MODULE_AUTHOR("R.J.Dunlop <bob.dunlop@farsite.co.uk>");
 MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd.");
+MODULE_LICENSE("GPL");
 
 EXPORT_NO_SYMBOLS;
 
@@ -331,26 +331,15 @@ struct buf_window {
 /*      Per port (line or channel) information
  */
 struct fst_port_info {
-        void                   *if_ptr; /* Some drivers describe this as a
-                                         * general purpose pointer. However if
-                                         * using syncPPP it has a very specific
-                                         * purpose: it must be the first item in
-                                         * the structure pointed to by dev->priv
-                                         * and must in turn point to the
-                                         * associated ppp_device structure.
-                                         */
+        hdlc_device             hdlc;   /* HDLC device struct - must be first */
         struct fst_card_info   *card;   /* Card we're associated with */
         int                     index;  /* Port index on the card */
-        int                     proto;  /* Protocol we are running */
         int                     hwif;   /* Line hardware (lineInterface copy) */
         int                     run;    /* Port is running */
         int                     rxpos;  /* Next Rx buffer to use */
         int                     txpos;  /* Next Tx buffer to use */
         int                     txipos; /* Next Tx buffer to check for free */
         int                     txcnt;  /* Count of Tx buffers in use */
-        struct net_device      *dev;    /* Kernel network device entry */
-        struct net_device_stats stats;  /* Standard statistics */
-        struct ppp_device       pppdev; /* Link to syncPPP */
 };
 
 /*      Per card information
@@ -370,6 +359,11 @@ struct fst_card_info {
         struct fst_port_info ports[ FST_MAX_PORTS ];
 };
 
+/* Convert an HDLC device pointer into a port info pointer and similar */
+#define hdlc_to_port(H) ((struct fst_port_info *)(H))
+#define dev_to_port(D)  hdlc_to_port(dev_to_hdlc(D))
+#define port_to_dev(P)  hdlc_to_dev(&(P)->hdlc)
+
 
 /*
  *      Shared memory window access macros
@@ -632,25 +626,18 @@ fst_intr_ctlchg ( struct fst_card_info *card, struct fst_port_info *port )
 
         if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD ))
         {
-                if ( ! netif_carrier_ok ( port->dev ))
+                if ( ! netif_carrier_ok ( port_to_dev ( port )))
                 {
                         dbg ( DBG_INTR,"DCD active\n");
-
-                        /* Poke sPPP to renegotiate */
-                        if ( port->proto == FST_HDLC || port->proto == FST_PPP )
-                        {
-                                sppp_reopen ( port->dev );
-                        }
-
-                        netif_carrier_on ( port->dev );
+                        netif_carrier_on ( port_to_dev ( port ));
                 }
         }
         else
         {
-                if ( netif_carrier_ok ( port->dev ))
+                if ( netif_carrier_ok ( port_to_dev ( port )))
                 {
                         dbg ( DBG_INTR,"DCD lost\n");
-                        netif_carrier_off ( port->dev );
+                        netif_carrier_off ( port_to_dev ( port ));
                 }
         }
 }
@@ -693,24 +680,24 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port )
                                         len );
         if ( dmabits != ( RX_STP | RX_ENP ) || len > LEN_RX_BUFFER - 2 )
         {
-                port->stats.rx_errors++;
+                port->hdlc.stats.rx_errors++;
 
                 /* Update error stats and discard buffer */
                 if ( dmabits & RX_OFLO )
                 {
-                        port->stats.rx_fifo_errors++;
+                        port->hdlc.stats.rx_fifo_errors++;
                 }
                 if ( dmabits & RX_CRC )
                 {
-                        port->stats.rx_crc_errors++;
+                        port->hdlc.stats.rx_crc_errors++;
                 }
                 if ( dmabits & RX_FRAM )
                 {
-                        port->stats.rx_frame_errors++;
+                        port->hdlc.stats.rx_frame_errors++;
                 }
                 if ( dmabits == ( RX_STP | RX_ENP ))
                 {
-                        port->stats.rx_length_errors++;
+                        port->hdlc.stats.rx_length_errors++;
                 }
 
                 /* Discard buffer descriptors until we see the end of packet
@@ -747,7 +734,7 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port )
         {
                 dbg ( DBG_RX,"intr_rx: can't allocate buffer\n");
 
-                port->stats.rx_dropped++;
+                port->hdlc.stats.rx_dropped++;
 
                 /* Return descriptor to card */
                 FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN );
@@ -771,29 +758,16 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port )
                 port->rxpos = rxp;
 
         /* Update stats */
-        port->stats.rx_packets++;
-        port->stats.rx_bytes += len;
+        port->hdlc.stats.rx_packets++;
+        port->hdlc.stats.rx_bytes += len;
 
         /* Push upstream */
-        if ( port->proto == FST_HDLC || port->proto == FST_PPP )
-        {
-                /* Mark for further processing by sPPP module */
-                skb->protocol = htons ( ETH_P_WAN_PPP );
-        }
-        else
-        {
-                /* DEC customer specific protocol (since nothing defined for
-                 * marking raw data), at least one other driver uses this value
-                 * for this purpose.
-                 */
-                skb->protocol = htons ( ETH_P_CUST );
-                skb->pkt_type = PACKET_HOST;
-        }
         skb->mac.raw = skb->data;
-        skb->dev = port->dev;
+        skb->dev = hdlc_to_dev ( &port->hdlc );
+        skb->protocol = htons ( ETH_P_HDLC );
         netif_rx ( skb );
 
-        port->dev->last_rx = jiffies;
+        port_to_dev ( port )->last_rx = jiffies;
 }
 
 
@@ -844,7 +818,7 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs )
                 case CTLB_CHG:
                 case CTLC_CHG:
                 case CTLD_CHG:
-                        if ( port->run && port->dev != NULL )
+                        if ( port->run )
                                 fst_intr_ctlchg ( card, port );
                         break;
 
@@ -863,9 +837,8 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs )
                          * always load up the entire packet for DMA.
                          */
                         dbg ( DBG_TX,"Tx underflow port %d\n", event & 0x03 );
-
-                        port->stats.tx_errors++;
-                        port->stats.tx_fifo_errors++;
+                        port->hdlc.stats.tx_errors++;
+                        port->hdlc.stats.tx_fifo_errors++;
                         break;
 
                 case INIT_CPLT:
@@ -890,7 +863,7 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs )
 
         for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ )
         {
-                if ( port->dev == NULL || ! port->run )
+                if ( ! port->run )
                         continue;
 
                 /* Check for rx completions */
@@ -907,7 +880,7 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs )
                         --port->txcnt;
                         if ( ++port->txipos >= NUM_TX_BUFFER )
                                 port->txipos = 0;
-                        netif_wake_queue ( port->dev );
+                        netif_wake_queue ( port_to_dev ( port ));
                 }
         }
 
@@ -968,146 +941,29 @@ check_started_ok ( struct fst_card_info *card )
 }
 
 
-static int
-fst_change_mtu ( struct net_device *dev, int new_mtu )
-{
-        if ( new_mtu < 128 || new_mtu > FST_MAX_MTU )
-                return -EINVAL;
-        dev->mtu = new_mtu;
-        return 0;
-}
-
-
-/* Sooner or later you can't avoid a forward declaration */
-static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd );
-
-static int
-switch_proto ( struct fst_port_info *port, int new_proto )
-{
-        int err;
-        int orig_mtu;
-        struct net_device *dev;
-
-        dev = port->dev;
-
-        /* Turn off sPPP module ? */
-        if (( new_proto != FST_HDLC && new_proto != FST_PPP )
-                && ( port->proto == FST_HDLC || port->proto == FST_PPP ))
-        {
-                sppp_close ( port->pppdev.dev );
-                sppp_detach ( port->pppdev.dev );
-
-                /* Reset some fields overwritten by sPPP */
-                dev->hard_header     = NULL;
-                dev->rebuild_header  = NULL;
-                dev->tx_queue_len    = FST_TX_QUEUE_LEN;
-                dev->type            = ARPHRD_MYTYPE;
-                dev->addr_len        = 0;
-                dev->hard_header_len = 0;
-                dev->do_ioctl        = fst_ioctl;
-                dev->change_mtu      = fst_change_mtu;
-                dev->flags           = IFF_POINTOPOINT|IFF_NOARP;
-
-                return 0;
-        }
-
-        /* Turn on sPPP ? */
-        if (( new_proto == FST_HDLC || new_proto == FST_PPP )
-                && ( port->proto != FST_HDLC && port->proto != FST_PPP ))
-        {
-                orig_mtu = dev->mtu;
-
-                /* Attach to sync PPP module */
-                port->pppdev.dev = dev;
-                sppp_attach ( &port->pppdev );
-
-                if ( orig_mtu < dev->mtu )
-                        dev->change_mtu ( dev, orig_mtu );
-
-                /* Claw back the ioctl routine. We promise to be good and call
-                 * the sync PPP routines once we've eliminated our functions.
-                 */
-                dev->do_ioctl = fst_ioctl;
-
-                /* Set the mode */
-                if ( new_proto == FST_HDLC )
-                {
-                        err = sppp_do_ioctl ( port->pppdev.dev, NULL,
-                                                                SPPPIOCCISCO );
-                }
-                else
-                {
-                        err = sppp_do_ioctl ( port->pppdev.dev, NULL,
-                                                                SPPPIOCPPP );
-                }
-
-                /* Open the device */
-                if ( err == 0 )
-                {
-                        (void) sppp_open ( port->dev );
-                }
-
-                return err;
-        }
-
-        /* Switch sPPP mode to that desired */
-        err = 0;
-        if ( new_proto == FST_HDLC && port->pppdev.dev != NULL )
-        {
-                err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCCISCO );
-        }
-        else if ( new_proto == FST_PPP && port->pppdev.dev != NULL )
-        {
-                err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCPPP );
-        }
-
-        /* Anything else is switching from one raw mode to another which is
-         * basically a NOP
-         */
-
-        return err;
-}
-
-
 static int
 set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port,
                 struct fstioc_info *info )
 {
         int err;
 
-        /* Set things according to the user set valid flags */
+        /* Set things according to the user set valid flags.
+         * Several of the old options have been invalidated/replaced by the
+         * generic HDLC package.
+         */
         err = 0;
         if ( info->valid & FSTVAL_PROTO )
-        {
-                if ( port->proto != info->proto )
-                {
-                        err = switch_proto ( port, info->proto );
-                        if ( err == 0 )
-                                port->proto = info->proto;
-                }
-        }
+                err = -EINVAL;
         if ( info->valid & FSTVAL_CABLE )
-        {
-                FST_WRB ( card, portConfig[port->index].lineInterface,
-                                        info->lineInterface );
-                port->hwif = info->lineInterface;
-        }
+                err = -EINVAL;
         if ( info->valid & FSTVAL_SPEED )
-        {
-                FST_WRL ( card, portConfig[port->index].lineSpeed,
-                                        info->lineSpeed );
-                FST_WRB ( card, portConfig[port->index].internalClock,
-                                        info->internalClock );
-        }
+                err = -EINVAL;
+
         if ( info->valid & FSTVAL_MODE )
-        {
                 FST_WRW ( card, cardMode, info->cardMode );
-        }
 #if FST_DEBUG
         if ( info->valid & FSTVAL_DEBUG )
-        {
                 fst_debug_mask = info->debug;
-        }
 #endif
 
         return err;
@@ -1125,7 +981,7 @@ gather_conf_info ( struct fst_card_info *card, struct fst_port_info *port,
         info->nports = card->nports;
         info->type   = card->type;
         info->state  = card->state;
-        info->proto  = port->proto;
+        info->proto  = FST_GEN_HDLC;
         info->index  = i;
 #if FST_DEBUG
         info->debug  = fst_debug_mask;
@@ -1153,6 +1009,119 @@ gather_conf_info ( struct fst_card_info *card, struct fst_port_info *port,
 }
 
 
+static int
+fst_set_iface ( struct fst_card_info *card, struct fst_port_info *port,
+                struct ifreq *ifr )
+{
+        sync_serial_settings sync;
+        int i;
+
+        if ( ifr->ifr_settings.data_length != sizeof ( sync ))
+        {
+                return -ENOMEM;
+        }
+
+        if ( copy_from_user ( &sync, ifr->ifr_settings.data, sizeof ( sync )))
+        {
+                return -EFAULT;
+        }
+
+        if ( sync.loopback )
+                return -EINVAL;
+
+        i = port->index;
+
+        switch ( ifr->ifr_settings.type )
+        {
+        case IF_IFACE_V35:
+                FST_WRW ( card, portConfig[i].lineInterface, V35 );
+                port->hwif = V35;
+                break;
+
+        case IF_IFACE_V24:
+                FST_WRW ( card, portConfig[i].lineInterface, V24 );
+                port->hwif = V24;
+                break;
+
+        case IF_IFACE_X21:
+                FST_WRW ( card, portConfig[i].lineInterface, X21 );
+                port->hwif = X21;
+                break;
+
+        case IF_IFACE_SYNC_SERIAL:
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        switch ( sync.clock_type )
+        {
+        case CLOCK_EXT:
+                FST_WRB ( card, portConfig[i].internalClock, EXTCLK );
+                break;
+
+        case CLOCK_INT:
+                FST_WRB ( card, portConfig[i].internalClock, INTCLK );
+                break;
+
+        default:
+                return -EINVAL;
+        }
+        FST_WRL ( card, portConfig[i].lineSpeed, sync.clock_rate );
+        return 0;
+}
+
+static int
+fst_get_iface ( struct fst_card_info *card, struct fst_port_info *port,
+                struct ifreq *ifr )
+{
+        sync_serial_settings sync;
+        int i;
+
+        /* First check what line type is set, we'll default to reporting X.21
+         * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be
+         * changed
+         */
+        switch ( port->hwif )
+        {
+        case V35:
+                ifr->ifr_settings.type = IF_IFACE_V35;
+                break;
+        case V24:
+                ifr->ifr_settings.type = IF_IFACE_V24;
+                break;
+        case X21:
+        default:
+                ifr->ifr_settings.type = IF_IFACE_X21;
+                break;
+        }
+        if ( ifr->ifr_settings.data_length == 0 )
+        {
+                return 0;       /* only type requested */
+        }
+
+        if ( ifr->ifr_settings.data_length < sizeof ( sync ))
+        {
+                return -ENOMEM;
+        }
+
+        i = port->index;
+        sync.clock_rate = FST_RDL ( card, portConfig[i].lineSpeed );
+        /* Lucky card and linux use same encoding here */
+        sync.clock_type = FST_RDB ( card, portConfig[i].internalClock );
+        sync.loopback = 0;
+
+        if ( copy_to_user ( ifr->ifr_settings.data, &sync, sizeof ( sync )))
+        {
+                return -EFAULT;
+        }
+
+        ifr->ifr_settings.data_length = sizeof ( sync );
+        return 0;
+}
+
+
 static int
 fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd )
 {
@@ -1164,7 +1133,7 @@ fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd )
 
         dbg ( DBG_IOCTL,"ioctl: %x, %p\n", cmd, ifr->ifr_data );
 
-        port = dev->priv;
+        port = dev_to_port ( dev );
         card = port->card;
 
         if ( !capable ( CAP_NET_ADMIN ))
@@ -1201,7 +1170,7 @@ fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd )
                  * when going over the top
                  */
                 if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE
-                                || wrthdr.size + wrthdr.offset > FST_MEMSIZE )
+                               || wrthdr.size + wrthdr.offset > FST_MEMSIZE )
                 {
                         return -ENXIO;
                 }
@@ -1261,6 +1230,9 @@ fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd )
 
         case FSTSETCONF:
 
+                /* Most of the setting have been moved to the generic ioctls
+                 * this just covers debug and board ident mode now
+                 */
                 if ( copy_from_user ( &info,  ifr->ifr_data, sizeof ( info )))
                 {
                         return -EFAULT;
@@ -1268,12 +1240,25 @@ fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd )
 
                 return set_conf_from_info ( card, port, &info );
 
+        case SIOCDEVICE:
+                switch ( ifr->ifr_settings.type )
+                {
+                case IF_GET_IFACE:
+                        return fst_get_iface ( card, port, ifr );
+
+                case IF_IFACE_SYNC_SERIAL:
+                case IF_IFACE_V35:
+                case IF_IFACE_V24:
+                case IF_IFACE_X21:
+                        return fst_set_iface ( card, port, ifr );
+
+                default:
+                        return hdlc_ioctl ( dev, ifr, cmd );
+                }
+
         default:
-                /* Not one of ours. Pass it through to sPPP package */
-                if ( port->proto == FST_HDLC || port->proto == FST_PPP )
-                        return sppp_do_ioctl ( dev, ifr, cmd );
-                else
-                        return -EINVAL;
+                /* Not one of ours. Pass through to HDLC package */
+                return hdlc_ioctl ( dev, ifr, cmd );
         }
 }
 
@@ -1306,9 +1291,9 @@ fst_openport ( struct fst_port_info *port )
                 signals = FST_RDL ( port->card, v24DebouncedSts[port->index]);
                 if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE
                                                       : IPSTS_DCD ))
-                        netif_carrier_on ( port->dev );
+                        netif_carrier_on ( port_to_dev ( port ));
                 else
-                        netif_carrier_off ( port->dev );
+                        netif_carrier_off ( port_to_dev ( port ));
         }
 }
 
@@ -1335,64 +1320,15 @@ fst_closeport ( struct fst_port_info *port )
 static int
 fst_open ( struct net_device *dev )
 {
-        struct fst_card_info *card;
-        struct fst_port_info *port;
-        int orig_mtu;
         int err;
 
-        MOD_INC_USE_COUNT;
-
-        port = dev->priv;
-        card = port->card;
-
-        switch ( port->proto )
-        {
-        case FST_HDLC:
-        case FST_PPP:
-
-                orig_mtu = dev->mtu;
-
-                /* Attach to sync PPP module */
-                port->pppdev.dev = dev;
-                sppp_attach ( &port->pppdev );
-
-                if ( orig_mtu < dev->mtu )
-                        dev->change_mtu ( dev, orig_mtu );
-
-                /* Claw back the ioctl routine. We promise to be good and call
-                 * the sync PPP routines once we've eliminated our functions.
-                 */
-                dev->do_ioctl = fst_ioctl;
-
-                err = sppp_do_ioctl ( dev, NULL, port->proto == FST_HDLC
-                                                ? SPPPIOCCISCO : SPPPIOCPPP );
-                if ( err )
-               {
-                       sppp_detach ( dev );
-                       MOD_DEC_USE_COUNT;
-                       return err;
-               }
-
-                err = sppp_open ( dev );
-                if ( err )
-               {
-                       sppp_detach ( dev );
-                       MOD_DEC_USE_COUNT;
-                       return err;
-               }
-                break;
-
-        case FST_MONITOR:
-        case FST_RAW:
-                break;
-
-        default:
-                dbg ( DBG_OPEN,"open: Unknown proto %d\n", port->proto );
-                break;
-        }
+        err = hdlc_open ( dev_to_hdlc ( dev ));
+        if ( err )
+                return err;
 
-        fst_openport ( port );
+        MOD_INC_USE_COUNT;
 
+        fst_openport ( dev_to_port ( dev ));
         netif_wake_queue ( dev );
         return 0;
 }
@@ -1400,34 +1336,22 @@ fst_open ( struct net_device *dev )
 static int
 fst_close ( struct net_device *dev )
 {
-        struct fst_port_info *port;
-
-        port = dev->priv;
-
         netif_stop_queue ( dev );
-        switch ( port->proto )
-        {
-        case FST_HDLC:
-        case FST_PPP:
-                sppp_close ( dev );
-                sppp_detach ( dev );
-                break;
-
-        case FST_MONITOR:
-        case FST_RAW:
-                break;
-
-        default:
-                dbg ( DBG_OPEN,"close: Unknown proto %d\n", port->proto );
-                break;
-        }
-
-        fst_closeport ( port );
-
+        fst_closeport ( dev_to_port ( dev ));
+        hdlc_close ( dev_to_hdlc  ( dev ));
         MOD_DEC_USE_COUNT;
         return 0;
 }
 
+static int
+fst_attach ( hdlc_device *hdlc, unsigned short encoding, unsigned short parity )
+{
+        /* Setting currently fixed in FarSync card so we check and forget */
+        if ( encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT )
+                return -EINVAL;
+        return 0;
+}
+
 
 static void
 fst_tx_timeout ( struct net_device *dev )
@@ -1436,10 +1360,10 @@ fst_tx_timeout ( struct net_device *dev )
 
         dbg ( DBG_INTR | DBG_TX,"tx_timeout\n");
 
-        port = dev->priv;
+        port = dev_to_port ( dev );
 
-        port->stats.tx_errors++;
-        port->stats.tx_aborted_errors++;
+        port->hdlc.stats.tx_errors++;
+        port->hdlc.stats.tx_aborted_errors++;
 
         if ( port->txcnt > 0 )
                 fst_issue_cmd ( port, ABORTTX );
@@ -1459,22 +1383,15 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev )
         int pi;
         int txp;
 
-        port = dev->priv;
+        port = dev_to_port ( dev );
         card = port->card;
 
-        /* Drop packet if in monitor (rx only) mode */
-        if ( port->proto == FST_MONITOR )
-        {
-                dev_kfree_skb ( skb );
-                return 0;
-        }
-
         /* Drop packet with error if we don't have carrier */
         if ( ! netif_carrier_ok ( dev ))
         {
                 dev_kfree_skb ( skb );
-                port->stats.tx_errors++;
-                port->stats.tx_carrier_errors++;
+                port->hdlc.stats.tx_errors++;
+                port->hdlc.stats.tx_carrier_errors++;
                 return 0;
         }
 
@@ -1484,7 +1401,7 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev )
                 dbg ( DBG_TX,"Packet too large %d vs %d\n", skb->len,
                                                 LEN_TX_BUFFER );
                 dev_kfree_skb ( skb );
-                port->stats.tx_errors++;
+                port->hdlc.stats.tx_errors++;
                 return 0;
         }
 
@@ -1498,7 +1415,7 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev )
                 spin_unlock_irqrestore ( &card->card_lock, flags );
                 dbg ( DBG_TX,"Out of Tx buffers\n");
                 dev_kfree_skb ( skb );
-                port->stats.tx_errors++;
+                port->hdlc.stats.tx_errors++;
                 return 0;
         }
         if ( ++port->txpos >= NUM_TX_BUFFER )
@@ -1518,8 +1435,8 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev )
         FST_WRW ( card, txDescrRing[pi][txp].bcnt, cnv_bcnt ( skb->len ));
         FST_WRB ( card, txDescrRing[pi][txp].bits, DMA_OWN | TX_STP | TX_ENP );
 
-        port->stats.tx_packets++;
-        port->stats.tx_bytes += skb->len;
+        port->hdlc.stats.tx_packets++;
+        port->hdlc.stats.tx_bytes += skb->len;
 
         dev_kfree_skb ( skb );
 
@@ -1528,18 +1445,6 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev )
 }
 
 
-static struct net_device_stats *
-fst_get_stats ( struct net_device *dev )
-{
-        struct fst_port_info *port;
-
-        if (( port = dev->priv ) != NULL )
-                return &port->stats;
-        else
-                return NULL;
-}
-
-
 /*
  *      Card setup having checked hardware resources.
  *      Should be pretty bizarre if we get an error here (kernel memory
@@ -1566,34 +1471,13 @@ fst_init_card ( struct fst_card_info *card )
          */
         for ( i = 0 ; i < card->nports ; i++ )
         {
-                card->ports[i].if_ptr = &card->ports[i].pppdev;
                 card->ports[i].card   = card;
                 card->ports[i].index  = i;
-                card->ports[i].proto  = FST_HDLC;
                 card->ports[i].run    = 0;
 
-                dev = kmalloc ( sizeof ( struct net_device ), GFP_KERNEL );
-                if ( dev == NULL )
-                {
-                        printk_err ("Cannot allocate net_device for port %d\n",
-                                        i );
-                        /* No point going any further */
-                        card->nports = i;
-                        break;
-                }
-                memset ( dev, 0, sizeof ( struct net_device ));
-                card->ports[i].dev = dev;
+                dev = hdlc_to_dev ( &card->ports[i].hdlc );
 
-                if ( dev_alloc_name ( dev, FST_NDEV_NAME "%d") < 0 )
-                {
-                        printk_err ("Cannot allocate i/f name for port %d\n",
-                                        i );
-                        kfree ( dev );
-                        card->nports = i;
-                        break;
-                }
-
-                /* Fill in remainder of the net device info */
+                /* Fill in the net device info */
                                 /* Since this is a PCI setup this is purely
                                  * informational. Give them the buffer addresses
                                  * and basic card I/O.
@@ -1609,26 +1493,19 @@ fst_init_card ( struct fst_card_info *card )
                 dev->base_addr   = card->pci_conf;
                 dev->irq         = card->irq;
 
-                dev->get_stats          = fst_get_stats;
-                dev->mtu                = FST_DEF_MTU;
-                dev->change_mtu         = fst_change_mtu;
-                dev->priv               = &card->ports[i];
-                dev->tx_queue_len       = FST_TX_QUEUE_LEN;
-                dev->type               = ARPHRD_MYTYPE;
-                dev->addr_len           = 0;
-                dev->open               = fst_open;
-                dev->stop               = fst_close;
-                dev->hard_start_xmit    = fst_start_xmit;
-                dev->do_ioctl           = fst_ioctl;
-                dev->watchdog_timeo     = FST_TX_TIMEOUT;
-                dev->tx_timeout         = fst_tx_timeout;
-                dev->flags              = IFF_POINTOPOINT|IFF_NOARP;
-
-                if (( err = register_netdev ( dev )) < 0 )
+                dev->tx_queue_len          = FST_TX_QUEUE_LEN;
+                dev->open                  = fst_open;
+                dev->stop                  = fst_close;
+                dev->do_ioctl              = fst_ioctl;
+                dev->watchdog_timeo        = FST_TX_TIMEOUT;
+                dev->tx_timeout            = fst_tx_timeout;
+                card->ports[i].hdlc.attach = fst_attach;
+                card->ports[i].hdlc.xmit   = fst_start_xmit;
+
+                if (( err = register_hdlc_device ( &card->ports[i].hdlc )) < 0 )
                 {
-                        printk_err ("Cannot register %s (errno %d)\n",
-                                        dev->name, -err );
-                        kfree ( dev );
+                        printk_err ("Cannot register HDLC device for port %d"
+                                    " (errno %d)\n", i, -err );
                         card->nports = i;
                         break;
                 }
@@ -1637,8 +1514,8 @@ fst_init_card ( struct fst_card_info *card )
         spin_lock_init ( &card->card_lock );
 
         printk ( KERN_INFO "%s-%s: %s IRQ%d, %d ports\n",
-                        card->ports[0].dev->name,
-                        card->ports[card->nports-1].dev->name,
+                        hdlc_to_dev(&card->ports[0].hdlc)->name,
+                        hdlc_to_dev(&card->ports[card->nports-1].hdlc)->name,
                         type_strings[card->type], card->irq, card->nports );
 }
 
@@ -1789,8 +1666,7 @@ fst_remove_one ( struct pci_dev *pdev )
 
         for ( i = 0 ; i < card->nports ; i++ )
         {
-                unregister_netdev ( card->ports[i].dev );
-                kfree ( card->ports[i].dev );
+                unregister_hdlc_device ( &card->ports[i].hdlc );
         }
 
         fst_disable_intr ( card );
@@ -1830,4 +1706,3 @@ fst_cleanup_module(void)
 module_init ( fst_init );
 module_exit ( fst_cleanup_module );
 
-MODULE_LICENSE("GPL");
index bbc08a84ca074f1a01d8aa0b191fc39740a90e0e..f9e32e2f79b4aef38e4d39a7edbc8ab4215cb545 100644 (file)
  *      A short common prefix is useful for routines within the driver to avoid
  *      conflict with other similar drivers and I chosen to use "fst_" for this
  *      purpose (FarSite T-series).
- *
- *      Finally the device driver needs a short network interface name. Since
- *      "hdlc" is already in use I've chosen the even less informative "sync"
- *      for the present.
  */
 #define FST_NAME                "fst"           /* In debug/info etc */
-#define FST_NDEV_NAME           "sync"          /* For net interface */
 #define FST_DEV_NAME            "farsync"       /* For misc interfaces */
 
 
index 9d22122dad11606a9c4a58f983d27994caa8ba6c..cd3fcf86d6cfb83c6f88fbb49629efbbf798b6b5 100644 (file)
@@ -152,7 +152,7 @@ typedef struct {
        u32 bp;                 /* Buffer Pointer (24 bits) */
        u16 len;                /* Data Length */
        u8 stat;                /* Status */
-       u8 unused2;
+       u8 unused;              /* pads to 2-byte boundary */
 }__attribute__ ((packed)) pkt_desc;
 
 
@@ -202,7 +202,11 @@ typedef struct {
 #define MD0_CRC_ITU_0 0x06
 #define MD0_CRC_ITU   0x07
 
-#define MD2_NRZI      0x20     /* NRZI mode */
+#define MD2_NRZ              0x00
+#define MD2_NRZI      0x20
+#define MD2_MANCHESTER 0x80
+#define MD2_FM_MARK   0xA0
+#define MD2_FM_SPACE  0xC0
 #define MD2_LOOPBACK  0x03      /* Local data Loopback */
 
 #define CTL_NORTS     0x01
index ff976438932df2bfb03d9a6c41b6fafb305a348c..7f8677c62a04e0a382be35937df42a975c34b149 100644 (file)
 #error Either hd64570.h or hd64572.h must be included
 #endif
 
-
-static card_t *first_card;
-static card_t **new_card = &first_card;
-
-
-/* Maximum events to handle at each interrupt - should I increase it? */
-#define INTR_WORK 4
+static char sca_version[]="1.09";
 
 #define get_msci(port)   (phy_node(port) ?   MSCI1_OFFSET :   MSCI0_OFFSET)
 #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
@@ -63,11 +57,19 @@ static card_t **new_card = &first_card;
 #define sca_ina(reg, card)             sca_inw(reg, card)
 #define writea(value, ptr)             writew(value, ptr)
 
+#else /* HD64572 */
+#define sca_outa(value, reg, card)     sca_outl(value, reg, card)
+#define sca_ina(reg, card)             sca_inl(reg, card)
+#define writea(value, ptr)             writel(value, ptr)
+#endif
+
 static inline int sca_intr_status(card_t *card)
 {
+       u8 result = 0;
+
+#ifdef __HD64570_H /* HD64570 */
        u8 isr0 = sca_in(ISR0, card);
        u8 isr1 = sca_in(ISR1, card);
-       u8 result = 0;
 
        if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0);
        if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0);
@@ -76,19 +78,8 @@ static inline int sca_intr_status(card_t *card)
        if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0);
        if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1);
 
-       return result;
-}
-
 #else /* HD64572 */
-#define sca_outa(value, reg, card)     sca_outl(value, reg, card)
-#define sca_ina(reg, card)             sca_inl(reg, card)
-#define writea(value, ptr)             writel(value, ptr)
-
-
-static inline int sca_intr_status(card_t *card)
-{
        u32 isr0 = sca_inl(ISR0, card);
-       u8 result = 0;
 
        if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0);
        if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0);
@@ -97,11 +88,17 @@ static inline int sca_intr_status(card_t *card)
        if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0);
        if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1);
 
-       return result;
-}
-
 #endif /* HD64570 vs HD64572 */
 
+       if (!(result & SCA_INTR_DMAC_TX(0)))
+               if (sca_in(DSR_TX(0), card) & DSR_EOM)
+                       result |= SCA_INTR_DMAC_TX(0);
+       if (!(result & SCA_INTR_DMAC_TX(1)))
+               if (sca_in(DSR_TX(1), card) & DSR_EOM)
+                       result |= SCA_INTR_DMAC_TX(1);
+
+       return result;
+}
 
 
 
@@ -250,8 +247,7 @@ static inline void sca_msci_intr(port_t *port)
 
 
 
-static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc,
-                             u8 rxin)
+static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin)
 {
        struct sk_buff *skb;
        u16 len;
@@ -289,13 +285,16 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc,
        openwin(card, 0);
 #endif
        skb_put(skb, len);
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
        printk(KERN_DEBUG "%s RX(%i):", hdlc_to_name(&port->hdlc), skb->len);
        debug_frame(skb);
 #endif
        port->hdlc.stats.rx_packets++;
        port->hdlc.stats.rx_bytes += skb->len;
-       hdlc_netif_rx(&port->hdlc, skb);
+       skb->mac.raw = skb->data;
+       skb->dev = hdlc_to_dev(&port->hdlc);
+       skb->protocol = htons(ETH_P_HDLC);
+       netif_rx(skb);
 }
 
 
@@ -320,14 +319,9 @@ static inline void sca_rx_intr(port_t *port)
                pkt_desc *desc;
                u32 cda = sca_ina(dmac + CDAL, card);
 
-               if (cda == desc_off)
+               if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
                        break;  /* No frame received */
 
-#ifdef __HD64572_H
-               if (cda == desc_off + 8)
-                       break;  /* SCA-II updates CDA in 2 steps */
-#endif
-
                desc = desc_address(port, port->rxin, 0);
                stat = readb(&desc->stat);
                if (!(stat & ST_RX_EOM))
@@ -371,20 +365,17 @@ static inline void sca_tx_intr(port_t *port)
                DSR_TX(phy_node(port)), card);
 
        while (1) {
-               u32 desc_off = desc_offset(port, port->txlast, 1);
                pkt_desc *desc;
-               u16 len;
 
-               if (sca_ina(dmac + CDAL, card) == desc_off)
+               u32 desc_off = desc_offset(port, port->txlast, 1);
+               u32 cda = sca_ina(dmac + CDAL, card);
+               if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
                        break;  /* Transmitter is/will_be sending this frame */
 
                desc = desc_address(port, port->txlast, 1);
-               len = readw(&desc->len);
-
                port->hdlc.stats.tx_packets++;
-               port->hdlc.stats.tx_bytes += len;
+               port->hdlc.stats.tx_bytes += readw(&desc->len);
                writeb(0, &desc->stat); /* Free descriptor */
-
                port->txlast = (port->txlast + 1) %
                        port_to_card(port)->ring_buffers;
        }
@@ -398,7 +389,8 @@ static inline void sca_tx_intr(port_t *port)
 static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
 {
        card_t *card = dev_id;
-       int boguscnt = INTR_WORK;
+/* Maximum events to handle at each interrupt - should I increase it? */
+       int boguscnt = 4;
        int i;
        u8 stat;
 
@@ -421,9 +413,11 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
                        }
 
                        if (--boguscnt < 0) {
+#if 0
                                printk(KERN_ERR "%s: too much work at "
                                       "interrupt\n",
                                       hdlc_to_name(&port->hdlc));
+#endif
                                goto exit;
                        }
                }
@@ -437,47 +431,22 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
 
 
 
-static inline int sca_set_loopback(port_t *port, int line)
+static void sca_set_port(port_t *port)
 {
        card_t* card = port_to_card(port);
        u8 msci = get_msci(port);
        u8 md2 = sca_in(msci + MD2, card);
-
-       switch(line) {
-       case LINE_DEFAULT:
-               md2 &= ~MD2_LOOPBACK;
-               port->line &= ~LINE_LOOPBACK;
-               break;
-
-       case LINE_LOOPBACK:
-               md2 |= MD2_LOOPBACK;
-               port->line |= LINE_LOOPBACK;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       sca_out(md2, msci + MD2, card);
-       return 0;
-}
-
-
-
-static void sca_set_clock(port_t *port)
-{
-       card_t *card = port_to_card(port);
-       u8 msci = get_msci(port);
        unsigned int tmc, br = 10, brv = 1024;
 
-       if (port->clkrate > 0) {
+
+       if (port->settings.clock_rate > 0) {
                /* Try lower br for better accuracy*/
                do {
                        br--;
                        brv >>= 1; /* brv = 2^9 = 512 max in specs */
 
                        /* Baud Rate = CLOCK_BASE / TMC / 2^BR */
-                       tmc = CLOCK_BASE / (brv * port->clkrate);
+                       tmc = CLOCK_BASE / (brv * port->settings.clock_rate);
                }while(br > 1 && tmc <= 128);
 
                if (tmc < 1) {
@@ -487,11 +456,11 @@ static void sca_set_clock(port_t *port)
                } else if (tmc > 255)
                        tmc = 256; /* tmc=0 means 256 - low baud rates */
 
-               port->clkrate = CLOCK_BASE / (brv * tmc);
+               port->settings.clock_rate = CLOCK_BASE / (brv * tmc);
        } else {
                br = 9; /* Minimum clock rate */
                tmc = 256;      /* 8bit = 0 */
-               port->clkrate = CLOCK_BASE / (256 * 512);
+               port->settings.clock_rate = CLOCK_BASE / (256 * 512);
        }
 
        port->rxs = (port->rxs & ~CLK_BRG_MASK) | br;
@@ -509,27 +478,59 @@ static void sca_set_clock(port_t *port)
        /* Set BRG bits */
        sca_out(port->rxs, msci + RXS, card);
        sca_out(port->txs, msci + TXS, card);
+
+       if (port->settings.loopback)
+               md2 |= MD2_LOOPBACK;
+       else
+               md2 &= ~MD2_LOOPBACK;
+
+       sca_out(md2, msci + MD2, card);
+
 }
 
 
 
-static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi)
+static void sca_open(hdlc_device *hdlc)
 {
+       port_t *port = hdlc_to_port(hdlc);
        card_t* card = port_to_card(port);
        u8 msci = get_msci(port);
-       u8 md2 = (nrzi ? MD2_NRZI : 0) |
-               ((port->line & LINE_LOOPBACK) ? MD2_LOOPBACK : 0);
-       u8 ctl = (idle ? CTL_IDLE : 0);
-#ifdef __HD64572_H
-       ctl |= CTL_URCT | CTL_URSKP; /* Skip the rest of underrun frame */
-#endif
+       u8 md0, md2;
+
+       switch(port->encoding) {
+       case ENCODING_NRZ:      md2 = MD2_NRZ;          break;
+       case ENCODING_NRZI:     md2 = MD2_NRZI;         break;
+       case ENCODING_FM_MARK:  md2 = MD2_FM_MARK;      break;
+       case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE;     break;
+       default:                md2 = MD2_MANCHESTER;
+       }
+
+       if (port->settings.loopback)
+               md2 |= MD2_LOOPBACK;
+
+       switch(port->parity) {
+       case PARITY_CRC16_PR0:       md0 = MD0_HDLC | MD0_CRC_16_0;  break;
+       case PARITY_CRC16_PR1:       md0 = MD0_HDLC | MD0_CRC_16;    break;
+#ifdef __HD64570_H
+       case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
+#else
+       case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break;
+#endif 
+       case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU;   break;
+       default:                     md0 = MD0_HDLC | MD0_CRC_NONE;
+       }
 
        sca_out(CMD_RESET, msci + CMD, card);
-       sca_out(MD0_HDLC | crc, msci + MD0, card);
+       sca_out(md0, msci + MD0, card);
        sca_out(0x00, msci + MD1, card); /* no address field check */
        sca_out(md2, msci + MD2, card);
        sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */
-       sca_out(ctl, msci + CTL, card);
+#ifdef __HD64570_H
+       sca_out(CTL_IDLE, msci + CTL, card);
+#else
+       /* Skip the rest of underrun frame */
+       sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card);
+#endif
 
 #ifdef __HD64570_H
        /* Allow at least 8 bytes before requesting RX DMA operation */
@@ -539,24 +540,28 @@ static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi)
        sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */
 #else
        sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */
-       /* Setting than to larger value may cause Illegal Access */
-       sca_out(0x20, msci + TNR0, card); /* =TX DMA activation condition */
-       sca_out(0x30, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
-       sca_out(0x04, msci + TCR, card); /* =Critical TX DMA activ condition */
+       sca_out(0x3C, msci + TFS, card); /* +1 = TX start */
+       sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */
+       sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */
+       sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
 #endif
 
+/* We're using the following interrupts:
+   - TXINT (DMAC completed all transmisions, underflow or CTS change)
+   - all DMA interrupts
+*/
 
 #ifdef __HD64570_H
        /* MSCI TX INT IRQ enable */
        sca_out(IE0_TXINT, msci + IE0, card);
-       sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun IRQ */
+       sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun -> TXINT */
        sca_out(sca_in(IER0, card) | (phy_node(port) ? 0x80 : 0x08),
                IER0, card);
        /* DMA IRQ enable */
        sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F),
                IER1, card);
 #else
-       /* MSCI TX INT and underrrun IRQ enable */
+       /* MSCI TX INT IRQ enable */
        sca_outl(IE0_TXINT | IE0_UDRN, msci + IE0, card);
        /* DMA & MSCI IRQ enable */
        sca_outl(sca_in(IER0, card) |
@@ -573,11 +578,52 @@ static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi)
        sca_out(port->txs, msci + TXS, card);
        sca_out(CMD_TX_ENABLE, msci + CMD, card);
        sca_out(CMD_RX_ENABLE, msci + CMD, card);
+
+       netif_start_queue(hdlc_to_dev(hdlc));
+}
+
+
+
+static void sca_close(hdlc_device *hdlc)
+{
+       port_t *port = hdlc_to_port(hdlc);
+
+       /* reset channel */
+       netif_stop_queue(hdlc_to_dev(hdlc));
+       sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
 }
 
 
 
-#ifdef DEBUG_RINGS
+static int sca_attach(hdlc_device *hdlc, unsigned short encoding,
+                     unsigned short parity)
+{
+       if (encoding != ENCODING_NRZ &&
+           encoding != ENCODING_NRZI &&
+           encoding != ENCODING_FM_MARK &&
+           encoding != ENCODING_FM_SPACE &&
+           encoding != ENCODING_MANCHESTER)
+               return -EINVAL;
+
+       if (parity != PARITY_NONE &&
+           parity != PARITY_CRC16_PR0 &&
+           parity != PARITY_CRC16_PR1 &&
+#ifdef __HD64570_H
+           parity != PARITY_CRC16_PR0_CCITT &&
+#else
+           parity != PARITY_CRC32_PR1_CCITT &&
+#endif 
+           parity != PARITY_CRC16_PR1_CCITT)
+               return -EINVAL;
+
+       hdlc_to_port(hdlc)->encoding = encoding;
+       hdlc_to_port(hdlc)->parity = parity;
+       return 0;
+}
+
+
+
+#ifdef CONFIG_HDLC_DEBUG_RINGS
 static void sca_dump_rings(hdlc_device *hdlc)
 {
        port_t *port = hdlc_to_port(hdlc);
@@ -644,34 +690,14 @@ static void sca_dump_rings(hdlc_device *hdlc)
        openwin(card, page); /* Restore original page */
 #endif
 }
-#endif /* DEBUG_RINGS */
-
-
-
-static void sca_open(hdlc_device *hdlc)
-{
-       port_t *port = hdlc_to_port(hdlc);
-
-       sca_set_hdlc_mode(port, 1, MD0_CRC_ITU, 0);
-       netif_start_queue(hdlc_to_dev(hdlc));
-}
-
-
-static void sca_close(hdlc_device *hdlc)
-{
-       port_t *port = hdlc_to_port(hdlc);
-
-       /* reset channel */
-       netif_stop_queue(hdlc_to_dev(hdlc));
-       sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
-}
+#endif /* CONFIG_HDLC_DEBUG_RINGS */
 
 
 
-static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb)
+static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
 {
+       hdlc_device *hdlc = dev_to_hdlc(dev);
        port_t *port = hdlc_to_port(hdlc);
-       struct net_device *dev = hdlc_to_dev(hdlc);
        card_t *card = port_to_card(port);
        pkt_desc *desc;
        u32 buff, len;
@@ -685,7 +711,7 @@ static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb)
        desc = desc_address(port, port->txin + 1, 1);
        if (readb(&desc->stat)) { /* allow 1 packet gap */
                /* should never happen - previous xmit should stop queue */
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
                printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
 #endif
                netif_stop_queue(dev);
@@ -693,7 +719,7 @@ static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb)
                return 1;       /* request packet to be queued */
        }
 
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
        printk(KERN_DEBUG "%s TX(%i):", hdlc_to_name(hdlc), skb->len);
        debug_frame(skb);
 #endif
diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c
deleted file mode 100644 (file)
index 34da9c7..0000000
+++ /dev/null
@@ -1,1453 +0,0 @@
-/*
- * Generic HDLC support routines for Linux
- *
- * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Current status:
- *    - this is work in progress
- *    - not heavily tested on SMP
- *    - currently supported:
- *     * raw IP-in-HDLC
- *     * Cisco HDLC
- *     * Frame Relay with ANSI or CCITT LMI (both user and network side)
- *     * PPP (using syncppp.c)
- *     * X.25
- *
- * Use sethdlc utility to set line parameters, protocol and PVCs
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/pkt_sched.h>
-#include <linux/inetdevice.h>
-#include <linux/lapb.h>
-#include <linux/rtnetlink.h>
-#include <linux/hdlc.h>
-
-/* #define DEBUG_PKT */
-/* #define DEBUG_HARD_HEADER */
-/* #define DEBUG_FECN */
-/* #define DEBUG_BECN */
-
-static const char* version = "HDLC support module revision 1.02 for Linux 2.4";
-
-
-#define CISCO_MULTICAST                0x8F    /* Cisco multicast address */
-#define CISCO_UNICAST          0x0F    /* Cisco unicast address */
-#define CISCO_KEEPALIVE                0x8035  /* Cisco keepalive protocol */
-#define CISCO_SYS_INFO         0x2000  /* Cisco interface/system info */
-#define CISCO_ADDR_REQ         0       /* Cisco address request */
-#define CISCO_ADDR_REPLY       1       /* Cisco address reply */
-#define CISCO_KEEPALIVE_REQ    2       /* Cisco keepalive request */
-
-static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-
-/********************************************************
- *
- * Cisco HDLC support
- *
- *******************************************************/
-
-static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
-                            u16 type, void *daddr, void *saddr,
-                            unsigned int len)
-{
-       hdlc_header *data;
-#ifdef DEBUG_HARD_HEADER
-       printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
-#endif
-
-       skb_push(skb, sizeof(hdlc_header));
-       data = (hdlc_header*)skb->data;
-       if (type == CISCO_KEEPALIVE)
-               data->address = CISCO_MULTICAST;
-       else
-               data->address = CISCO_UNICAST;
-       data->control = 0;
-       data->protocol = htons(type);
-
-       return sizeof(hdlc_header);
-}
-
-
-
-static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
-                                u32 par1, u32 par2)
-{
-       struct sk_buff *skb;
-       cisco_packet *data;
-
-       skb = dev_alloc_skb(sizeof(hdlc_header)+sizeof(cisco_packet));
-       if (!skb) {
-               printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n",
-                              hdlc_to_name(hdlc));
-               return;
-       }
-       skb_reserve(skb, 4);
-       cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE,
-                         NULL, NULL, 0);
-       data = (cisco_packet*)skb->tail;
-
-       data->type = htonl(type);
-       data->par1 = htonl(par1);
-       data->par2 = htonl(par2);
-       data->rel = 0xFFFF;
-       data->time = htonl(jiffies * 1000 / HZ);
-
-       skb_put(skb, sizeof(cisco_packet));
-       skb->priority = TC_PRIO_CONTROL;
-       skb->dev = hdlc_to_dev(hdlc);
-
-       dev_queue_xmit(skb);
-}
-
-
-
-static void cisco_netif(hdlc_device *hdlc, struct sk_buff *skb)
-{
-       hdlc_header *data = (hdlc_header*)skb->data;
-       cisco_packet *cisco_data;
-       struct in_device *in_dev;
-       u32 addr, mask;
-
-       if (skb->len<sizeof(hdlc_header))
-               goto rx_error;
-
-       if (data->address != CISCO_MULTICAST &&
-           data->address != CISCO_UNICAST)
-               goto rx_error;
-
-       skb_pull(skb, sizeof(hdlc_header));
-
-       switch(ntohs(data->protocol)) {
-       case ETH_P_IP:
-       case ETH_P_IPX:
-       case ETH_P_IPV6:
-               skb->protocol = data->protocol;
-               skb->dev = hdlc_to_dev(hdlc);
-               netif_rx(skb);
-               return;
-
-       case CISCO_SYS_INFO:
-               /* Packet is not needed, drop it. */
-               dev_kfree_skb_any(skb);
-               return;
-
-       case CISCO_KEEPALIVE:
-               if (skb->len != CISCO_PACKET_LEN &&
-                   skb->len != CISCO_BIG_PACKET_LEN) {
-                       printk(KERN_INFO "%s: Invalid length of Cisco "
-                              "control packet (%d bytes)\n",
-                              hdlc_to_name(hdlc), skb->len);
-                       goto rx_error;
-               }
-
-               cisco_data = (cisco_packet*)skb->data;
-
-               switch(ntohl (cisco_data->type)) {
-               case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
-                       in_dev = hdlc_to_dev(hdlc)->ip_ptr;
-                       addr = 0;
-                       mask = ~0; /* is the mask correct? */
-
-                       if (in_dev != NULL) {
-                               struct in_ifaddr **ifap = &in_dev->ifa_list;
-
-                               while (*ifap != NULL) {
-                                       if (strcmp(hdlc_to_name(hdlc),
-                                                  (*ifap)->ifa_label) == 0) {
-                                               addr = (*ifap)->ifa_local;
-                                               mask = (*ifap)->ifa_mask;
-                                               break;
-                                       }
-                                       ifap = &(*ifap)->ifa_next;
-                               }
-
-                               cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY,
-                                                    addr, mask);
-                       }
-                       dev_kfree_skb_any(skb);
-                       return;
-
-               case CISCO_ADDR_REPLY:
-                       printk(KERN_INFO "%s: Unexpected Cisco IP address "
-                              "reply\n", hdlc_to_name(hdlc));
-                       goto rx_error;
-
-               case CISCO_KEEPALIVE_REQ:
-                       hdlc->lmi.rxseq = ntohl(cisco_data->par1);
-                       if (ntohl(cisco_data->par2) == hdlc->lmi.txseq) {
-                               hdlc->lmi.last_poll = jiffies;
-                               if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) {
-                                       u32 sec, min, hrs, days;
-                                       sec = ntohl(cisco_data->time) / 1000;
-                                       min = sec / 60; sec -= min * 60;
-                                       hrs = min / 60; min -= hrs * 60;
-                                       days = hrs / 24; hrs -= days * 24;
-                                       printk(KERN_INFO "%s: Link up (peer "
-                                              "uptime %ud%uh%um%us)\n",
-                                              hdlc_to_name(hdlc), days, hrs,
-                                              min, sec);
-                               }
-                               hdlc->lmi.state |= LINK_STATE_RELIABLE;
-                       }
-
-                       dev_kfree_skb_any(skb);
-                       return;
-               } /* switch(keepalive type) */
-       } /* switch(protocol) */
-
-       printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc),
-              data->protocol);
-       dev_kfree_skb_any(skb);
-       return;
-
- rx_error:
-       hdlc->stats.rx_errors++; /* Mark error */
-       dev_kfree_skb_any(skb);
-}
-
-
-
-static void cisco_timer(unsigned long arg)
-{
-       hdlc_device *hdlc = (hdlc_device*)arg;
-
-       if ((hdlc->lmi.state & LINK_STATE_RELIABLE) &&
-           (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) {
-               hdlc->lmi.state &= ~LINK_STATE_RELIABLE;
-               printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc));
-       }
-
-       cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq,
-                            hdlc->lmi.rxseq);
-       hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ;
-
-       hdlc->timer.function = cisco_timer;
-       hdlc->timer.data = arg;
-       add_timer(&hdlc->timer);
-}
-
-
-
-/******************************************************************
- *
- *     generic Frame Relay routines
- *
- *****************************************************************/
-
-
-static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
-                         u16 type, void *daddr, void *saddr, unsigned int len)
-{
-       u16 head_len;
-
-       if (!daddr)
-               daddr = dev->broadcast;
-
-#ifdef DEBUG_HARD_HEADER
-       printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);
-#endif
-
-       switch(type) {
-       case ETH_P_IP:
-               head_len = 4;
-               skb_push(skb, head_len);
-               skb->data[3] = NLPID_IP;
-               break;
-
-       case ETH_P_IPV6:
-               head_len = 4;
-               skb_push(skb, head_len);
-               skb->data[3] = NLPID_IPV6;
-               break;
-
-       case LMI_PROTO:
-               head_len = 4;
-               skb_push(skb, head_len);
-               skb->data[3] = LMI_PROTO;
-               break;
-
-       default:
-               head_len = 10;
-               skb_push(skb, head_len);
-               skb->data[3] = FR_PAD;
-               skb->data[4] = NLPID_SNAP;
-               skb->data[5] = FR_PAD;
-               skb->data[6] = FR_PAD;
-               skb->data[7] = FR_PAD;
-               skb->data[8] = type>>8;
-               skb->data[9] = (u8)type;
-       }
-
-       memcpy(skb->data, daddr, 2);
-       skb->data[2] = FR_UI;
-
-       return head_len;
-}
-
-
-
-static inline void fr_log_dlci_active(pvc_device *pvc)
-{
-       printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc),
-              pvc->state & PVC_STATE_ACTIVE ? "" : "in",
-              pvc->state & PVC_STATE_NEW ? " new" : "");
-}
-
-
-
-static inline u8 fr_lmi_nextseq(u8 x)
-{
-       x++;
-       return x ? x : 1;
-}
-
-
-
-static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
-{
-       struct sk_buff *skb;
-       pvc_device *pvc = hdlc->first_pvc;
-       int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH;
-       int stat_len = 3;
-       u8 *data;
-       int i = 0;
-
-       if (mode_is(hdlc, MODE_DCE) && fullrep) {
-               len += hdlc->pvc_count * (2 + stat_len);
-               if (len > HDLC_MAX_MTU) {
-                       printk(KERN_WARNING "%s: Too many PVCs while sending "
-                              "LMI full report\n", hdlc_to_name(hdlc));
-                       return;
-               }
-       }
-
-       skb = dev_alloc_skb(len);
-       if (!skb) {
-               printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
-                              hdlc_to_name(hdlc));
-               return;
-       }
-       memset(skb->data, 0, len);
-       skb_reserve(skb, 4);
-       fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0);
-       data = skb->tail;
-       data[i++] = LMI_CALLREF;
-       data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY;
-       if (mode_is(hdlc, MODE_FR_ANSI))
-               data[i++] = LMI_ANSI_LOCKSHIFT;
-       data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE :
-               LMI_REPTYPE;
-       data[i++] = LMI_REPT_LEN;
-       data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
-
-       data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE;
-       data[i++] = LMI_INTEG_LEN;
-       data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq);
-       data[i++] = hdlc->lmi.rxseq;
-
-       if (mode_is(hdlc, MODE_DCE) && fullrep) {
-               while (pvc) {
-                       data[i++] = mode_is(hdlc, MODE_FR_CCITT) ?
-                               LMI_CCITT_PVCSTAT:LMI_PVCSTAT;
-                       data[i++] = stat_len;
-
-                       if ((hdlc->lmi.state & LINK_STATE_RELIABLE) &&
-                           (pvc->netdev.flags & IFF_UP) &&
-                           !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) {
-                               pvc->state |= PVC_STATE_NEW;
-                               fr_log_dlci_active(pvc);
-                       }
-
-                       dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
-                                      data+i, pvc->state);
-                       i += stat_len;
-                       pvc = pvc->next;
-               }
-       }
-
-       skb_put(skb, i);
-       skb->priority = TC_PRIO_CONTROL;
-       skb->dev = hdlc_to_dev(hdlc);
-
-       dev_queue_xmit(skb);
-}
-
-
-
-static void fr_timer(unsigned long arg)
-{
-       hdlc_device *hdlc = (hdlc_device*)arg;
-       int i, cnt = 0, reliable;
-       u32 list;
-
-       if (mode_is(hdlc, MODE_DCE))
-               reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ);
-       else {
-               hdlc->lmi.last_errors <<= 1; /* Shift the list */
-               if (hdlc->lmi.state & LINK_STATE_REQUEST) {
-                       printk(KERN_INFO "%s: No LMI status reply received\n",
-                              hdlc_to_name(hdlc));
-                       hdlc->lmi.last_errors |= 1;
-               }
-
-               for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393;
-                    i++, list >>= 1)
-                       cnt += (list & 1);      /* errors count */
-
-               reliable = (cnt < hdlc->lmi.N392);
-       }
-
-       if ((hdlc->lmi.state & LINK_STATE_RELIABLE) !=
-           (reliable ? LINK_STATE_RELIABLE : 0)) {
-               pvc_device *pvc = hdlc->first_pvc;
-
-               while (pvc) {/* Deactivate all PVCs */
-                       pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE);
-                       pvc = pvc->next;
-               }
-
-               hdlc->lmi.state ^= LINK_STATE_RELIABLE;
-               printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc),
-                      reliable ? "" : "un");
-
-               if (reliable) {
-                       hdlc->lmi.N391cnt = 0; /* Request full status */
-                       hdlc->lmi.state |= LINK_STATE_CHANGED;
-               }
-       }
-
-       if (mode_is(hdlc, MODE_DCE))
-               hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ;
-       else {
-               if (hdlc->lmi.N391cnt)
-                       hdlc->lmi.N391cnt--;
-
-               fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0);
-
-               hdlc->lmi.state |= LINK_STATE_REQUEST;
-               hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ;
-       }
-
-       hdlc->timer.function = fr_timer;
-       hdlc->timer.data = arg;
-       add_timer(&hdlc->timer);
-}
-
-
-
-static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
-{
-       int stat_len;
-       pvc_device *pvc;
-       int reptype = -1, error;
-       u8 rxseq, txseq;
-       int i;
-
-       if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ?
-                       LMI_ANSI_LENGTH : LMI_LENGTH)) {
-               printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc));
-               return 1;
-       }
-
-       if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ?
-                            LMI_STATUS : LMI_STATUS_ENQUIRY)) {
-               printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n",
-                      hdlc_to_name(hdlc), skb->data[2],
-                      mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply");
-               return 1;
-       }
-
-       i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6;
-
-       if (skb->data[i] !=
-           (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) {
-               printk(KERN_INFO "%s: Not a report type=%x\n",
-                      hdlc_to_name(hdlc), skb->data[i]);
-               return 1;
-       }
-       i++;
-
-       i++;                            /* Skip length field */
-
-       reptype = skb->data[i++];
-
-       if (skb->data[i]!=
-           (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) {
-               printk(KERN_INFO "%s: Unsupported status element=%x\n",
-                      hdlc_to_name(hdlc), skb->data[i]);
-               return 1;
-       }
-       i++;
-
-       i++;                    /* Skip length field */
-
-       hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */
-       rxseq = skb->data[i++]; /* Should confirm our sequence */
-
-       txseq = hdlc->lmi.txseq;
-
-       if (mode_is(hdlc, MODE_DCE)) {
-               if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) {
-                       printk(KERN_INFO "%s: Unsupported report type=%x\n",
-                              hdlc_to_name(hdlc), reptype);
-                       return 1;
-               }
-       }
-
-       error = 0;
-       if (!(hdlc->lmi.state & LINK_STATE_RELIABLE))
-               error = 1;
-
-       if (rxseq == 0 || rxseq != txseq) {
-               hdlc->lmi.N391cnt = 0; /* Ask for full report next time */
-               error = 1;
-       }
-
-       if (mode_is(hdlc, MODE_DCE)) {
-               if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) {
-/* Stop sending full report - the last one has been confirmed by DTE */
-                       hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT;
-                       pvc = hdlc->first_pvc;
-                       while (pvc) {
-                               if (pvc->state & PVC_STATE_NEW) {
-                                       pvc->state &= ~PVC_STATE_NEW;
-                                       pvc->state |= PVC_STATE_ACTIVE;
-                                       fr_log_dlci_active(pvc);
-
-/* Tell DTE that new PVC is now active */
-                                       hdlc->lmi.state |= LINK_STATE_CHANGED;
-                               }
-                               pvc = pvc->next;
-                       }
-               }
-
-               if (hdlc->lmi.state & LINK_STATE_CHANGED) {
-                       reptype = LMI_FULLREP;
-                       hdlc->lmi.state |= LINK_STATE_FULLREP_SENT;
-                       hdlc->lmi.state &= ~LINK_STATE_CHANGED;
-               }
-
-               fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
-               return 0;
-       }
-
-       /* DTE */
-
-       if (reptype != LMI_FULLREP || error)
-               return 0;
-
-       stat_len = 3;
-       pvc = hdlc->first_pvc;
-
-       while (pvc) {
-               pvc->newstate = 0;
-               pvc = pvc->next;
-       }
-
-       while (skb->len >= i + 2 + stat_len) {
-               u16 dlci;
-               u8 state = 0;
-
-               if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ?
-                                    LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
-                       printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n",
-                              hdlc_to_name(hdlc), skb->data[i]);
-                       return 1;
-               }
-               i++;
-
-               if (skb->data[i] != stat_len) {
-                       printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n",
-                              hdlc_to_name(hdlc), skb->data[i]);
-                       return 1;
-               }
-               i++;
-
-               dlci = status_to_dlci(hdlc, skb->data+i, &state);
-               pvc = find_pvc(hdlc, dlci);
-
-               if (pvc)
-                       pvc->newstate = state;
-               else if (state == PVC_STATE_NEW)
-                       printk(KERN_INFO "%s: new PVC available, DLCI=%u\n",
-                              hdlc_to_name(hdlc), dlci);
-
-               i += stat_len;
-       }
-
-       pvc = hdlc->first_pvc;
-
-       while (pvc) {
-               if (pvc->newstate == PVC_STATE_NEW)
-                       pvc->newstate = PVC_STATE_ACTIVE;
-
-               pvc->newstate |= (pvc->state &
-                                 ~(PVC_STATE_NEW|PVC_STATE_ACTIVE));
-               if (pvc->state != pvc->newstate) {
-                       pvc->state = pvc->newstate;
-                       fr_log_dlci_active(pvc);
-               }
-               pvc = pvc->next;
-       }
-
-       /* Next full report after N391 polls */
-       hdlc->lmi.N391cnt = hdlc->lmi.N391;
-
-       return 0;
-}
-
-
-
-static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb)
-{
-       fr_hdr *fh = (fr_hdr*)skb->data;
-       u8 *data = skb->data;
-       u16 dlci;
-       pvc_device *pvc;
-
-       if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
-               goto rx_error;
-
-       dlci = q922_to_dlci(skb->data);
-
-       if (dlci == LMI_DLCI) {
-               if (data[3] == LMI_PROTO) {
-                       if (fr_lmi_recv(hdlc, skb))
-                               goto rx_error;
-                       else {
-                               /* No request pending */
-                               hdlc->lmi.state &= ~LINK_STATE_REQUEST;
-                               hdlc->lmi.last_poll = jiffies;
-                               dev_kfree_skb_any(skb);
-                               return;
-                       }
-               }
-
-               printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
-                      hdlc_to_name(hdlc));
-               goto rx_error;
-       }
-
-       pvc = find_pvc(hdlc, dlci);
-       if (!pvc) {
-#ifdef DEBUG_PKT
-               printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
-                      hdlc_to_name(hdlc), dlci);
-#endif
-               goto rx_error;
-       }
-
-       if ((pvc->netdev.flags & IFF_UP) == 0) {
-#ifdef DEBUG_PKT
-               printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n",
-                      hdlc_to_name(hdlc), dlci);
-#endif
-               goto rx_error;
-       }
-
-       pvc->stats.rx_packets++; /* PVC traffic */
-       pvc->stats.rx_bytes += skb->len;
-
-       if ((pvc->state & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) {
-#ifdef DEBUG_FECN
-               printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
-                      fh->fecn ? "N" : "FF");
-#endif
-               pvc->state ^= PVC_STATE_FECN;
-       }
-
-       if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) {
-#ifdef DEBUG_FECN
-               printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
-                      fh->becn ? "N" : "FF");
-#endif
-               pvc->state ^= PVC_STATE_BECN;
-       }
-
-       if (pvc->state & PVC_STATE_BECN)
-               pvc->stats.rx_compressed++;
-
-       if (data[3] == NLPID_IP) {
-               skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
-               skb->protocol = htons(ETH_P_IP);
-               skb->dev = &pvc->netdev;
-               netif_rx(skb);
-               return;
-       }
-
-
-       if (data[3] == NLPID_IPV6) {
-               skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
-               skb->protocol = htons(ETH_P_IPV6);
-               skb->dev = &pvc->netdev;
-               netif_rx(skb);
-               return;
-       }
-
-       if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD &&
-           data[6] == FR_PAD && data[7] == FR_PAD &&
-           ((data[8]<<8) | data[9]) == ETH_P_ARP) {
-               skb_pull(skb, 10);
-               skb->protocol = htons(ETH_P_ARP);
-               skb->dev = &pvc->netdev;
-               netif_rx(skb);
-               return;
-       }
-
-       printk(KERN_INFO "%s: Unusupported protocol %x\n",
-              hdlc_to_name(hdlc), data[3]);
-       dev_kfree_skb_any(skb);
-       return;
-
- rx_error:
-       hdlc->stats.rx_errors++; /* Mark error */
-       dev_kfree_skb_any(skb);
-}
-
-
-
-static void fr_cisco_open(hdlc_device *hdlc)
-{
-       hdlc->lmi.state = LINK_STATE_CHANGED;
-       hdlc->lmi.txseq = hdlc->lmi.rxseq = 0;
-       hdlc->lmi.last_errors = 0xFFFFFFFF;
-       hdlc->lmi.N391cnt = 0;
-
-       init_timer(&hdlc->timer);
-       hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */
-       hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer;
-       hdlc->timer.data = (unsigned long)hdlc;
-       add_timer(&hdlc->timer);
-}
-
-
-
-static void fr_cisco_close(hdlc_device *hdlc)
-{
-       pvc_device *pvc = hdlc->first_pvc;
-
-       del_timer_sync(&hdlc->timer);
-
-       while(pvc) {            /* NULL in Cisco mode */
-               dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */
-               pvc = pvc->next;
-       }
-}
-
-
-
-/******************************************************************
- *
- *     generic HDLC routines
- *
- *****************************************************************/
-
-
-
-static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
-{
-       if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
-               return -EINVAL;
-       dev->mtu = new_mtu;
-       return 0;
-}
-
-
-
-/********************************************************
- *
- * PVC device routines
- *
- *******************************************************/
-
-static int pvc_open(struct net_device *dev)
-{
-       pvc_device *pvc = dev_to_pvc(dev);
-       int result = 0;
-
-       if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
-               return -EIO;  /* Master must be UP in order to activate PVC */
-
-       memset(&(pvc->stats), 0, sizeof(struct net_device_stats));
-       pvc->state = 0;
-
-       if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc)
-               result = pvc->master->open_pvc(pvc);
-       if (result)
-               return result;
-
-       pvc->master->lmi.state |= LINK_STATE_CHANGED;
-       return 0;
-}
-
-
-
-static int pvc_close(struct net_device *dev)
-{
-       pvc_device *pvc = dev_to_pvc(dev);
-       pvc->state = 0;
-
-       if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc)
-               pvc->master->close_pvc(pvc);
-
-       pvc->master->lmi.state |= LINK_STATE_CHANGED;
-       return 0;
-}
-
-
-
-static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-       pvc_device *pvc = dev_to_pvc(dev);
-
-       if (pvc->state & PVC_STATE_ACTIVE) {
-               skb->dev = hdlc_to_dev(pvc->master);
-               pvc->stats.tx_bytes += skb->len;
-               pvc->stats.tx_packets++;
-               if (pvc->state & PVC_STATE_FECN)
-                       pvc->stats.tx_compressed++; /* TX Congestion counter */
-               dev_queue_xmit(skb);
-       } else {
-               pvc->stats.tx_dropped++;
-               dev_kfree_skb(skb);
-       }
-
-       return 0;
-}
-
-
-
-static struct net_device_stats *pvc_get_stats(struct net_device *dev)
-{
-       pvc_device *pvc = dev_to_pvc(dev);
-       return &pvc->stats;
-}
-
-
-
-static int pvc_change_mtu(struct net_device *dev, int new_mtu)
-{
-       if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
-               return -EINVAL;
-       dev->mtu = new_mtu;
-       return 0;
-}
-
-
-
-static void destroy_pvc_list(hdlc_device *hdlc)
-{
-       pvc_device *pvc = hdlc->first_pvc;
-       while(pvc) {
-               pvc_device *next = pvc->next;
-               unregister_netdevice(&pvc->netdev);
-               kfree(pvc);
-               pvc = next;
-       }
-
-       hdlc->first_pvc = NULL; /* All PVCs destroyed */
-       hdlc->pvc_count = 0;
-       hdlc->lmi.state |= LINK_STATE_CHANGED;
-}
-
-
-
-/********************************************************
- *
- * X.25 protocol support routines
- *
- *******************************************************/
-
-#ifdef CONFIG_HDLC_X25
-/* These functions are callbacks called by LAPB layer */
-
-void x25_connect_disconnect(void *token, int reason, int code)
-{
-       hdlc_device *hdlc = token;
-       struct sk_buff *skb;
-       unsigned char *ptr;
-
-       if ((skb = dev_alloc_skb(1)) == NULL) {
-               printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc));
-               return;
-       }
-
-       ptr = skb_put(skb, 1);
-       *ptr = code;
-
-       skb->dev = hdlc_to_dev(hdlc);
-       skb->protocol = htons(ETH_P_X25);
-       skb->mac.raw = skb->data;
-       skb->pkt_type = PACKET_HOST;
-
-       netif_rx(skb);
-}
-
-void x25_connected(void *token, int reason)
-{
-       x25_connect_disconnect(token, reason, 1);
-}
-
-void x25_disconnected(void *token, int reason)
-{
-       x25_connect_disconnect(token, reason, 2);
-}
-
-
-int x25_data_indication(void *token, struct sk_buff *skb)
-{
-       hdlc_device *hdlc = token;
-       unsigned char *ptr;
-
-       ptr = skb_push(skb, 1);
-       *ptr = 0;
-
-       skb->dev = hdlc_to_dev(hdlc);
-       skb->protocol = htons(ETH_P_X25);
-       skb->mac.raw = skb->data;
-       skb->pkt_type = PACKET_HOST;
-
-       return netif_rx(skb);
-}
-
-
-void x25_data_transmit(void *token, struct sk_buff *skb)
-{
-       hdlc_device *hdlc = token;
-       hdlc->xmit(hdlc, skb);  /* Ignore return value :-( */
-}
-#endif /* CONFIG_HDLC_X25 */
-
-
-/********************************************************
- *
- * HDLC device routines
- *
- *******************************************************/
-
-static int hdlc_open(struct net_device *dev)
-{
-       hdlc_device *hdlc = dev_to_hdlc(dev);
-       int result;
-
-       if (hdlc->mode == MODE_NONE)
-               return -ENOSYS;
-
-       memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-
-       if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
-           mode_is(hdlc, MODE_CISCO | MODE_SOFT))
-               fr_cisco_open(hdlc);
-#ifdef CONFIG_HDLC_PPP
-       else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
-               sppp_attach(&hdlc->pppdev);
-               /* sppp_attach nukes them. We don't need syncppp's ioctl */
-               dev->do_ioctl = hdlc_ioctl;
-               hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO;
-               dev->type = ARPHRD_PPP;
-               result = sppp_open(dev);
-               if (result) {
-                       sppp_detach(dev);
-                       return result;
-               }
-       }
-#endif
-#ifdef CONFIG_HDLC_X25
-       else if (mode_is(hdlc, MODE_X25)) {
-               struct lapb_register_struct cb;
-
-               cb.connect_confirmation = x25_connected;
-               cb.connect_indication = x25_connected;
-               cb.disconnect_confirmation = x25_disconnected;
-               cb.disconnect_indication = x25_disconnected;
-               cb.data_indication = x25_data_indication;
-               cb.data_transmit = x25_data_transmit;
-
-               result = lapb_register(hdlc, &cb);
-               if (result != LAPB_OK)
-                       return result;
-       }
-#endif
-       result = hdlc->open(hdlc);
-       if (result) {
-               if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
-                   mode_is(hdlc, MODE_CISCO | MODE_SOFT))
-                       fr_cisco_close(hdlc);
-#ifdef CONFIG_HDLC_PPP
-               else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
-                       sppp_close(dev);
-                       sppp_detach(dev);
-                       dev->rebuild_header = NULL;
-                       dev->change_mtu = hdlc_change_mtu;
-                       dev->mtu = HDLC_MAX_MTU;
-                       dev->hard_header_len = 16;
-               }
-#endif
-#ifdef CONFIG_HDLC_X25
-               else if (mode_is(hdlc, MODE_X25))
-                       lapb_unregister(hdlc);
-#endif
-       }
-
-       return result;
-}
-
-
-
-static int hdlc_close(struct net_device *dev)
-{
-       hdlc_device *hdlc = dev_to_hdlc(dev);
-
-       hdlc->close(hdlc);
-
-       if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
-           mode_is(hdlc, MODE_CISCO | MODE_SOFT))
-               fr_cisco_close(hdlc);
-#ifdef CONFIG_HDLC_PPP
-       else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
-               sppp_close(dev);
-               sppp_detach(dev);
-               dev->rebuild_header = NULL;
-               dev->change_mtu = hdlc_change_mtu;
-               dev->mtu = HDLC_MAX_MTU;
-               dev->hard_header_len = 16;
-       }
-#endif
-#ifdef CONFIG_HDLC_X25
-       else if (mode_is(hdlc, MODE_X25))
-               lapb_unregister(hdlc);
-#endif
-       return 0;
-}
-
-
-
-static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-       hdlc_device *hdlc = dev_to_hdlc(dev);
-
-#ifdef CONFIG_HDLC_X25
-       if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) {
-               int result;
-
-
-               /* X.25 to LAPB */
-               switch (skb->data[0]) {
-               case 0:         /* Data to be transmitted */
-                       skb_pull(skb, 1);
-                       if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK)
-                               dev_kfree_skb(skb);
-                       return 0;
-
-               case 1:
-                       if ((result = lapb_connect_request(hdlc))!= LAPB_OK) {
-                               if (result == LAPB_CONNECTED) {
-                               /* Send connect confirm. msg to level 3 */
-                                       x25_connected(hdlc, 0);
-                               } else {
-                                       printk(KERN_ERR "%s: LAPB connect "
-                                              "request failed, error code = "
-                                              "%i\n", hdlc_to_name(hdlc),
-                                              result);
-                               }
-                       }
-                       break;
-
-               case 2:
-                       if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) {
-                               if (result == LAPB_NOTCONNECTED) {
-                               /* Send disconnect confirm. msg to level 3 */
-                                       x25_disconnected(hdlc, 0);
-                               } else {
-                                       printk(KERN_ERR "%s: LAPB disconnect "
-                                              "request failed, error code = "
-                                              "%i\n", hdlc_to_name(hdlc),
-                                              result);
-                               }
-                       }
-                       break;
-
-               default:
-                       /* to be defined */
-                       break;
-               }
-
-               dev_kfree_skb(skb);
-               return 0;
-       } /* MODE_X25 */
-#endif /* CONFIG_HDLC_X25 */
-
-       return hdlc->xmit(hdlc, skb);
-}
-
-
-
-void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb)
-{
-/* skb contains raw HDLC frame, in both hard- and software modes */
-       skb->mac.raw = skb->data;
-
-       switch(hdlc->mode & MODE_MASK) {
-       case MODE_HDLC:
-               skb->protocol = htons(ETH_P_IP);
-               skb->dev = hdlc_to_dev(hdlc);
-               netif_rx(skb);
-               return;
-
-       case MODE_FR:
-               fr_netif(hdlc, skb);
-               return;
-
-       case MODE_CISCO:
-               cisco_netif(hdlc, skb);
-               return;
-
-#ifdef CONFIG_HDLC_PPP
-       case MODE_PPP:
-#if 0
-               sppp_input(hdlc_to_dev(hdlc), skb);
-#else
-               skb->protocol = htons(ETH_P_WAN_PPP);
-               skb->dev = hdlc_to_dev(hdlc);
-               netif_rx(skb);
-#endif
-               return;
-#endif
-#ifdef CONFIG_HDLC_X25
-       case MODE_X25:
-               skb->dev = hdlc_to_dev(hdlc);
-               if (lapb_data_received(hdlc, skb) == LAPB_OK)
-                       return;
-               break;
-#endif
-       }
-
-       hdlc->stats.rx_errors++;
-       dev_kfree_skb_any(skb);
-}
-
-
-
-static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
-{
-       return &dev_to_hdlc(dev)->stats;
-}
-
-
-
-static int hdlc_set_mode(hdlc_device *hdlc, int mode)
-{
-       int result = -1;        /* Default to soft modes */
-       struct net_device *dev = hdlc_to_dev(hdlc);
-
-       if(!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       if(dev->flags & IFF_UP)
-               return -EBUSY;
-
-       dev->addr_len = 0;
-       dev->hard_header = NULL;
-       hdlc->mode = MODE_NONE;
-
-       if (!(mode & MODE_SOFT))
-               switch(mode & MODE_MASK) {
-               case MODE_HDLC:
-                       result = hdlc->set_mode ?
-                               hdlc->set_mode(hdlc, MODE_HDLC) : 0;
-                       break;
-
-               case MODE_CISCO: /* By card */
-#ifdef CONFIG_HDLC_PPP
-               case MODE_PPP:
-#endif
-#ifdef CONFIG_HDLC_X25
-               case MODE_X25:
-#endif
-               case MODE_FR:
-                       result = hdlc->set_mode ?
-                               hdlc->set_mode(hdlc, mode) : -ENOSYS;
-                       break;
-
-               default:
-                       return -EINVAL;
-               }
-
-       if (result) {
-               mode |= MODE_SOFT; /* Try "host software" protocol */
-
-               switch(mode & MODE_MASK) {
-               case MODE_CISCO:
-                       dev->hard_header = cisco_hard_header;
-                       break;
-
-#ifdef CONFIG_HDLC_PPP
-               case MODE_PPP:
-                       break;
-#endif
-#ifdef CONFIG_HDLC_X25
-               case MODE_X25:
-                       break;
-#endif
-
-               case MODE_FR:
-                       dev->hard_header = fr_hard_header;
-                       dev->addr_len = 2;
-                       *(u16*)dev->dev_addr = htons(LMI_DLCI);
-                       dlci_to_q922(dev->broadcast, LMI_DLCI);
-                       break;
-
-               default:
-                       return -EINVAL;
-               }
-
-               result = hdlc->set_mode ?
-                       hdlc->set_mode(hdlc, MODE_HDLC) : 0;
-       }
-
-       if (result)
-               return result;
-
-       hdlc->mode = mode;
-       switch(mode & MODE_MASK) {
-#ifdef CONFIG_HDLC_PPP
-       case MODE_PPP:   dev->type = ARPHRD_PPP;   break;
-#endif
-#ifdef CONFIG_HDLC_X25
-       case MODE_X25:   dev->type = ARPHRD_X25;   break;
-#endif
-       case MODE_FR:    dev->type = ARPHRD_FRAD;  break;
-       case MODE_CISCO: dev->type = ARPHRD_CISCO; break;
-       default:         dev->type = ARPHRD_RAWHDLC;
-       }
-
-       memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-       destroy_pvc_list(hdlc);
-       return 0;
-}
-
-
-
-static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci)
-{
-       pvc_device **pvc_p = &hdlc->first_pvc;
-       pvc_device *pvc;
-       int result, create = 1; /* Create or delete PVC */
-
-       if(!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       if(dlci<0) {
-               dlci = -dlci;
-               create = 0;
-       }
-
-       if(dlci <= 0 || dlci >= 1024)
-               return -EINVAL; /* Only 10 bits for DLCI, DLCI=0 is reserved */
-
-       if(!mode_is(hdlc, MODE_FR))
-               return -EINVAL; /* Only meaningfull on FR */
-
-       while(*pvc_p) {
-               if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
-                       break;
-               pvc_p = &(*pvc_p)->next;
-       }
-
-       if (create) {           /* Create PVC */
-               if (*pvc_p != NULL)
-                       return -EEXIST;
-
-               pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL);
-               if (!pvc) {
-                       printk(KERN_WARNING "%s: Memory squeeze on "
-                              "hdlc_fr_pvc()\n", hdlc_to_name(hdlc));
-                       return -ENOBUFS;
-               }
-               memset(pvc, 0, sizeof(pvc_device));
-
-               pvc->netdev.hard_start_xmit = pvc_xmit;
-               pvc->netdev.get_stats = pvc_get_stats;
-               pvc->netdev.open = pvc_open;
-               pvc->netdev.stop = pvc_close;
-               pvc->netdev.change_mtu = pvc_change_mtu;
-               pvc->netdev.mtu = HDLC_MAX_MTU;
-
-               pvc->netdev.type = ARPHRD_DLCI;
-               pvc->netdev.hard_header_len = 16;
-               pvc->netdev.hard_header = fr_hard_header;
-               pvc->netdev.tx_queue_len = 0;
-               pvc->netdev.flags = IFF_POINTOPOINT;
-
-               pvc->master = hdlc;
-               *(u16*)pvc->netdev.dev_addr = htons(dlci);
-               dlci_to_q922(pvc->netdev.broadcast, dlci);
-               pvc->netdev.addr_len = 2;
-               pvc->netdev.irq = hdlc_to_dev(hdlc)->irq;
-
-               result = dev_alloc_name(&pvc->netdev, "pvc%d");
-               if (result < 0) {
-                       kfree(pvc);
-                       *pvc_p = NULL;
-                       return result;
-               }
-
-               if (register_netdevice(&pvc->netdev) != 0) {
-                       kfree(pvc);
-                       *pvc_p = NULL;
-                       return -EIO;
-               }
-
-               if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) {
-                       result = hdlc->create_pvc(pvc);
-                       if (result) {
-                               unregister_netdevice(&pvc->netdev);
-                               kfree(pvc);
-                               *pvc_p = NULL;
-                               return result;
-                       }
-               }
-
-               hdlc->lmi.state |= LINK_STATE_CHANGED;
-               hdlc->pvc_count++;
-               return 0;
-       }
-
-       if (*pvc_p == NULL)             /* Delete PVC */
-               return -ENOENT;
-
-       pvc = *pvc_p;
-
-       if (pvc->netdev.flags & IFF_UP)
-               return -EBUSY;          /* PVC in use */
-
-       if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc)
-               hdlc->destroy_pvc(pvc);
-
-       hdlc->lmi.state |= LINK_STATE_CHANGED;
-       hdlc->pvc_count--;
-       *pvc_p = pvc->next;
-       unregister_netdevice(&pvc->netdev);
-       kfree(pvc);
-       return 0;
-}
-
-
-
-static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-       hdlc_device *hdlc = dev_to_hdlc(dev);
-
-       switch(cmd) {
-       case HDLCGMODE:
-               ifr->ifr_ifru.ifru_ivalue = hdlc->mode;
-               return 0;
-
-       case HDLCSMODE:
-               return hdlc_set_mode(hdlc, ifr->ifr_ifru.ifru_ivalue);
-
-       case HDLCPVC:
-               return hdlc_fr_pvc(hdlc, ifr->ifr_ifru.ifru_ivalue);
-
-       default:
-               if (hdlc->ioctl != NULL)
-                       return hdlc->ioctl(hdlc, ifr, cmd);
-       }
-
-       return -EINVAL;
-}
-
-
-
-static int hdlc_init(struct net_device *dev)
-{
-       hdlc_device *hdlc = dev_to_hdlc(dev);
-
-       memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-
-       dev->get_stats = hdlc_get_stats;
-       dev->open = hdlc_open;
-       dev->stop = hdlc_close;
-       dev->hard_start_xmit = hdlc_xmit;
-       dev->do_ioctl = hdlc_ioctl;
-       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;
-
-       return 0;
-}
-
-
-
-int register_hdlc_device(hdlc_device *hdlc)
-{
-       int result;
-       struct net_device *dev = hdlc_to_dev(hdlc);
-
-       dev->init = hdlc_init;
-       dev->priv = &hdlc->syncppp_ptr;
-       hdlc->syncppp_ptr = &hdlc->pppdev;
-       hdlc->pppdev.dev = dev;
-       hdlc->mode = MODE_NONE;
-       hdlc->lmi.T391 = 10;    /* polling verification timer */
-       hdlc->lmi.T392 = 15;    /* link integrity verification polling timer */
-       hdlc->lmi.N391 = 6;     /* full status polling counter */
-       hdlc->lmi.N392 = 3;     /* error threshold */
-       hdlc->lmi.N393 = 4;     /* monitored events count */
-
-       result = dev_alloc_name(dev, "hdlc%d");
-       if (result<0)
-               return result;
-
-       result = register_netdev(dev);
-       if (result != 0)
-               return -EIO;
-
-       MOD_INC_USE_COUNT;
-       return 0;
-}
-
-
-
-void unregister_hdlc_device(hdlc_device *hdlc)
-{
-       destroy_pvc_list(hdlc);
-       unregister_netdev(hdlc_to_dev(hdlc));
-       MOD_DEC_USE_COUNT;
-}
-
-MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
-MODULE_DESCRIPTION("HDLC support module");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(hdlc_netif_rx);
-EXPORT_SYMBOL(register_hdlc_device);
-EXPORT_SYMBOL(unregister_hdlc_device);
-
-static int __init hdlc_module_init(void)
-{
-       printk(KERN_INFO "%s\n", version);
-       return 0;
-}
-
-
-module_init(hdlc_module_init);
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
new file mode 100644 (file)
index 0000000..143ef3d
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Cisco HDLC support
+ *
+ * Copyright (C) 2000 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+#define CISCO_MULTICAST                0x8F    /* Cisco multicast address */
+#define CISCO_UNICAST          0x0F    /* Cisco unicast address */
+#define CISCO_KEEPALIVE                0x8035  /* Cisco keepalive protocol */
+#define CISCO_SYS_INFO         0x2000  /* Cisco interface/system info */
+#define CISCO_ADDR_REQ         0       /* Cisco address request */
+#define CISCO_ADDR_REPLY       1       /* Cisco address reply */
+#define CISCO_KEEPALIVE_REQ    2       /* Cisco keepalive request */
+
+
+static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
+                            u16 type, void *daddr, void *saddr,
+                            unsigned int len)
+{
+       hdlc_header *data;
+#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER
+       printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
+#endif
+
+       skb_push(skb, sizeof(hdlc_header));
+       data = (hdlc_header*)skb->data;
+       if (type == CISCO_KEEPALIVE)
+               data->address = CISCO_MULTICAST;
+       else
+               data->address = CISCO_UNICAST;
+       data->control = 0;
+       data->protocol = htons(type);
+
+       return sizeof(hdlc_header);
+}
+
+
+
+static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
+                                u32 par1, u32 par2)
+{
+       struct sk_buff *skb;
+       cisco_packet *data;
+
+       skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet));
+       if (!skb) {
+               printk(KERN_WARNING
+                      "%s: Memory squeeze on cisco_keepalive_send()\n",
+                      hdlc_to_name(hdlc));
+               return;
+       }
+       skb_reserve(skb, 4);
+       cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE,
+                         NULL, NULL, 0);
+       data = (cisco_packet*)skb->tail;
+
+       data->type = htonl(type);
+       data->par1 = htonl(par1);
+       data->par2 = htonl(par2);
+       data->rel = 0xFFFF;
+       data->time = htonl(jiffies * 1000 / HZ);
+
+       skb_put(skb, sizeof(cisco_packet));
+       skb->priority = TC_PRIO_CONTROL;
+       skb->dev = hdlc_to_dev(hdlc);
+
+       dev_queue_xmit(skb);
+}
+
+
+
+static void cisco_rx(struct sk_buff *skb)
+{
+       hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+       hdlc_header *data = (hdlc_header*)skb->data;
+       cisco_packet *cisco_data;
+       struct in_device *in_dev;
+       u32 addr, mask;
+
+       if (skb->len < sizeof(hdlc_header))
+               goto rx_error;
+
+       if (data->address != CISCO_MULTICAST &&
+           data->address != CISCO_UNICAST)
+               goto rx_error;
+
+       skb_pull(skb, sizeof(hdlc_header));
+
+       switch(ntohs(data->protocol)) {
+       case ETH_P_IP:
+       case ETH_P_IPX:
+       case ETH_P_IPV6:
+               skb->protocol = data->protocol;
+               skb->dev = hdlc_to_dev(hdlc);
+               netif_rx(skb);
+               return;
+
+       case CISCO_SYS_INFO:
+               /* Packet is not needed, drop it. */
+               dev_kfree_skb_any(skb);
+               return;
+
+       case CISCO_KEEPALIVE:
+               if (skb->len != CISCO_PACKET_LEN &&
+                   skb->len != CISCO_BIG_PACKET_LEN) {
+                       printk(KERN_INFO "%s: Invalid length of Cisco "
+                              "control packet (%d bytes)\n",
+                              hdlc_to_name(hdlc), skb->len);
+                       goto rx_error;
+               }
+
+               cisco_data = (cisco_packet*)skb->data;
+
+               switch(ntohl (cisco_data->type)) {
+               case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
+                       in_dev = hdlc_to_dev(hdlc)->ip_ptr;
+                       addr = 0;
+                       mask = ~0; /* is the mask correct? */
+
+                       if (in_dev != NULL) {
+                               struct in_ifaddr **ifap = &in_dev->ifa_list;
+
+                               while (*ifap != NULL) {
+                                       if (strcmp(hdlc_to_name(hdlc),
+                                                  (*ifap)->ifa_label) == 0) {
+                                               addr = (*ifap)->ifa_local;
+                                               mask = (*ifap)->ifa_mask;
+                                               break;
+                                       }
+                                       ifap = &(*ifap)->ifa_next;
+                               }
+
+                               cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY,
+                                                    addr, mask);
+                       }
+                       dev_kfree_skb_any(skb);
+                       return;
+
+               case CISCO_ADDR_REPLY:
+                       printk(KERN_INFO "%s: Unexpected Cisco IP address "
+                              "reply\n", hdlc_to_name(hdlc));
+                       goto rx_error;
+
+               case CISCO_KEEPALIVE_REQ:
+                       hdlc->state.cisco.rxseq = ntohl(cisco_data->par1);
+                       if (ntohl(cisco_data->par2) == hdlc->state.cisco.txseq) {
+                               hdlc->state.cisco.last_poll = jiffies;
+                               if (!hdlc->state.cisco.up) {
+                                       u32 sec, min, hrs, days;
+                                       sec = ntohl(cisco_data->time) / 1000;
+                                       min = sec / 60; sec -= min * 60;
+                                       hrs = min / 60; min -= hrs * 60;
+                                       days = hrs / 24; hrs -= days * 24;
+                                       printk(KERN_INFO "%s: Link up (peer "
+                                              "uptime %ud%uh%um%us)\n",
+                                              hdlc_to_name(hdlc), days, hrs,
+                                              min, sec);
+                               }
+                               hdlc->state.cisco.up = 1;
+                       }
+
+                       dev_kfree_skb_any(skb);
+                       return;
+               } /* switch(keepalive type) */
+       } /* switch(protocol) */
+
+       printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc),
+              data->protocol);
+       dev_kfree_skb_any(skb);
+       return;
+
+ rx_error:
+       hdlc->stats.rx_errors++; /* Mark error */
+       dev_kfree_skb_any(skb);
+}
+
+
+
+static void cisco_timer(unsigned long arg)
+{
+       hdlc_device *hdlc = (hdlc_device*)arg;
+
+       if (hdlc->state.cisco.up &&
+           jiffies - hdlc->state.cisco.last_poll >=
+           hdlc->state.cisco.settings.timeout * HZ) {
+               hdlc->state.cisco.up = 0;
+               printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc));
+       }
+
+       cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ,
+                            ++hdlc->state.cisco.txseq,
+                            hdlc->state.cisco.rxseq);
+       hdlc->state.cisco.timer.expires = jiffies +
+               hdlc->state.cisco.settings.interval * HZ;
+       hdlc->state.cisco.timer.function = cisco_timer;
+       hdlc->state.cisco.timer.data = arg;
+       add_timer(&hdlc->state.cisco.timer);
+}
+
+
+
+static int cisco_open(hdlc_device *hdlc)
+{
+       hdlc->state.cisco.last_poll = 0;
+       hdlc->state.cisco.up = 0;
+       hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0;
+
+       init_timer(&hdlc->state.cisco.timer);
+       hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/
+       hdlc->state.cisco.timer.function = cisco_timer;
+       hdlc->state.cisco.timer.data = (unsigned long)hdlc;
+       add_timer(&hdlc->state.cisco.timer);
+       return 0;
+}
+
+
+
+static void cisco_close(hdlc_device *hdlc)
+{
+       del_timer_sync(&hdlc->state.cisco.timer);
+}
+
+
+
+int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+       const size_t size = sizeof(cisco_proto);
+       struct net_device *dev = hdlc_to_dev(hdlc);
+       int result;
+
+       switch (ifr->ifr_settings.type) {
+       case IF_GET_PROTO:
+               ifr->ifr_settings.type = IF_PROTO_CISCO;
+               if (ifr->ifr_settings.data_length == 0)
+                       return 0; /* return protocol only */
+               if (ifr->ifr_settings.data_length < size)
+                       return -ENOMEM; /* buffer too small */
+               if (copy_to_user(ifr->ifr_settings.data,
+                                &hdlc->state.cisco.settings, size))
+                       return -EFAULT;
+               ifr->ifr_settings.data_length = size;
+               return 0;
+
+       case IF_PROTO_CISCO:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               if(dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               if (ifr->ifr_settings.data_length != size)
+                       return -ENOMEM; /* incorrect data length */
+                       
+               if (copy_from_user(&hdlc->state.cisco.settings,
+                                  ifr->ifr_settings.data, size))
+                       return -EFAULT;
+
+               /* FIXME - put sanity checks here */
+               hdlc_detach(hdlc);
+
+               result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+               if (result) {
+                       hdlc->proto = -1;
+                       return result;
+               }
+
+               hdlc->open = cisco_open;
+               hdlc->stop = cisco_close;
+               hdlc->netif_rx = cisco_rx;
+               hdlc->proto = IF_PROTO_CISCO;
+               dev->hard_start_xmit = hdlc->xmit;
+               dev->hard_header = cisco_hard_header;
+               dev->type = ARPHRD_CISCO;
+               dev->addr_len = 0;
+               return 0;
+       }
+
+       return -EINVAL;
+}
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
new file mode 100644 (file)
index 0000000..89f2aa8
--- /dev/null
@@ -0,0 +1,851 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Frame Relay support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+__inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
+{
+       pvc_device *pvc=hdlc->state.fr.first_pvc;
+       
+       while (pvc) {
+               if (netdev_dlci(&pvc->netdev) == dlci)
+                       return pvc;
+               pvc = pvc->next;
+       }
+
+       return NULL;
+}
+
+
+
+__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status,
+                             int *active, int *new)
+{
+       *new = (status[2] & 0x08);
+       *active = (!*new && (status[2] & 0x02));
+
+       return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
+}
+
+
+__inline__ void dlci_to_status(hdlc_device *hdlc, 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, struct net_device *dev,
+                         u16 type, void *daddr, void *saddr, unsigned int len)
+{
+       u16 head_len;
+
+       if (!daddr)
+               daddr = dev->broadcast;
+
+#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER
+       printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);
+#endif
+
+       switch(type) {
+       case ETH_P_IP:
+               head_len = 4;
+               skb_push(skb, head_len);
+               skb->data[3] = NLPID_IP;
+               break;
+
+       case ETH_P_IPV6:
+               head_len = 4;
+               skb_push(skb, head_len);
+               skb->data[3] = NLPID_IPV6;
+               break;
+
+       case LMI_PROTO:
+               head_len = 4;
+               skb_push(skb, head_len);
+               skb->data[3] = LMI_PROTO;
+               break;
+
+       default:
+               head_len = 10;
+               skb_push(skb, head_len);
+               skb->data[3] = FR_PAD;
+               skb->data[4] = NLPID_SNAP;
+               skb->data[5] = FR_PAD;
+               skb->data[6] = FR_PAD;
+               skb->data[7] = FR_PAD;
+               skb->data[8] = type>>8;
+               skb->data[9] = (u8)type;
+       }
+
+       memcpy(skb->data, daddr, 2);
+       skb->data[2] = FR_UI;
+
+       return head_len;
+}
+
+
+
+static int pvc_open(struct net_device *dev)
+{
+       pvc_device *pvc = dev_to_pvc(dev);
+
+       if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
+               return -EIO;  /* Master must be UP in order to activate PVC */
+
+       if (pvc->master->state.fr.settings.lmi != LMI_NONE)
+               pvc->state.active = 0;
+       else
+               pvc->state.active = 1;
+
+       pvc->state.new = 0;
+       pvc->master->state.fr.changed = 1;
+       return 0;
+}
+
+
+
+static int pvc_close(struct net_device *dev)
+{
+       pvc_device *pvc = dev_to_pvc(dev);
+       pvc->state.active = pvc->state.new = 0;
+       pvc->master->state.fr.changed = 1;
+       return 0;
+}
+
+
+
+static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       pvc_device *pvc = dev_to_pvc(dev);
+
+       if (pvc->state.active) {
+               skb->dev = hdlc_to_dev(pvc->master);
+               pvc->stats.tx_bytes += skb->len;
+               pvc->stats.tx_packets++;
+               if (pvc->state.fecn)
+                       pvc->stats.tx_compressed++; /* TX Congestion counter */
+               dev_queue_xmit(skb);
+       } else {
+               pvc->stats.tx_dropped++;
+               dev_kfree_skb(skb);
+       }
+
+       return 0;
+}
+
+
+
+static struct net_device_stats *pvc_get_stats(struct net_device *dev)
+{
+       pvc_device *pvc = dev_to_pvc(dev);
+       return &pvc->stats;
+}
+
+
+
+static int pvc_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
+               return -EINVAL;
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+
+
+static inline void fr_log_dlci_active(pvc_device *pvc)
+{
+       printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc),
+              pvc->state.active ? "" : "in",
+              pvc->state.new ? " new" : "");
+}
+
+
+
+static inline u8 fr_lmi_nextseq(u8 x)
+{
+       x++;
+       return x ? x : 1;
+}
+
+
+
+static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
+{
+       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;
+       u8 *data;
+       int i = 0;
+
+       if (hdlc->state.fr.settings.dce && fullrep) {
+               len += hdlc->state.fr.pvc_count * (2 + stat_len);
+               if (len > HDLC_MAX_MTU) {
+                       printk(KERN_WARNING "%s: Too many PVCs while sending "
+                              "LMI full report\n", hdlc_to_name(hdlc));
+                       return;
+               }
+       }
+
+       skb = dev_alloc_skb(len);
+       if (!skb) {
+               printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
+                              hdlc_to_name(hdlc));
+               return;
+       }
+       memset(skb->data, 0, len);
+       skb_reserve(skb, 4);
+       fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0);
+       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++] = LMI_ANSI_LOCKSHIFT;
+       data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+               ? LMI_CCITT_REPTYPE : LMI_REPTYPE;
+       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_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) {
+               while (pvc) {
+                       data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+                               ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT;
+                       data[i++] = stat_len;
+
+                       if (hdlc->state.fr.reliable &&
+                           (pvc->netdev.flags & IFF_UP) &&
+                           !pvc->state.active &&
+                           !pvc->state.new) {
+                               pvc->state.new = 1;
+                               fr_log_dlci_active(pvc);
+                       }
+
+                       dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
+                                      data + i,
+                                      pvc->state.active, pvc->state.new);
+                       i += stat_len;
+                       pvc = pvc->next;
+               }
+       }
+
+       skb_put(skb, i);
+       skb->priority = TC_PRIO_CONTROL;
+       skb->dev = hdlc_to_dev(hdlc);
+
+       dev_queue_xmit(skb);
+}
+
+
+
+static void fr_timer(unsigned long arg)
+{
+       hdlc_device *hdlc = (hdlc_device*)arg;
+       int i, cnt = 0, reliable;
+       u32 list;
+
+       if (hdlc->state.fr.settings.dce)
+               reliable = (jiffies - hdlc->state.fr.last_poll <
+                           hdlc->state.fr.settings.t392 * HZ);
+       else {
+               hdlc->state.fr.last_errors <<= 1; /* Shift the list */
+               if (hdlc->state.fr.request) {
+                       if (hdlc->state.fr.reliable)
+                               printk(KERN_INFO "%s: No LMI status reply "
+                                      "received\n", hdlc_to_name(hdlc));
+                       hdlc->state.fr.last_errors |= 1;
+               }
+
+               list = hdlc->state.fr.last_errors;
+               for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1)
+                       cnt += (list & 1);      /* errors count */
+
+               reliable = (cnt < hdlc->state.fr.settings.n392);
+       }
+
+       if (hdlc->state.fr.reliable != reliable) {
+               pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+               hdlc->state.fr.reliable = reliable;
+               printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc),
+                      reliable ? "" : "un");
+
+               if (reliable) {
+                       hdlc->state.fr.n391cnt = 0; /* Request full status */
+                       hdlc->state.fr.changed = 1;
+               } else {
+                       while (pvc) {   /* Deactivate all PVCs */
+                               pvc->state.new = pvc->state.active = 0;
+                               pvc = pvc->next;
+                       }
+               }
+       }
+
+       if (hdlc->state.fr.settings.dce)
+               hdlc->state.fr.timer.expires = jiffies +
+                       hdlc->state.fr.settings.t392 * HZ;
+       else {
+               if (hdlc->state.fr.n391cnt)
+                       hdlc->state.fr.n391cnt--;
+
+               fr_lmi_send(hdlc, hdlc->state.fr.n391cnt == 0);
+
+               hdlc->state.fr.request = 1;
+               hdlc->state.fr.timer.expires = jiffies +
+                       hdlc->state.fr.settings.t391 * HZ;
+       }
+
+       hdlc->state.fr.timer.function = fr_timer;
+       hdlc->state.fr.timer.data = arg;
+       add_timer(&hdlc->state.fr.timer);
+}
+
+
+
+static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
+{
+       int stat_len;
+       pvc_device *pvc;
+       int reptype = -1, error;
+       u8 rxseq, txseq;
+       int i;
+
+       if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI)
+                       ? LMI_ANSI_LENGTH : LMI_LENGTH)) {
+               printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc));
+               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",
+                      hdlc_to_name(hdlc), skb->data[2],
+                      hdlc->state.fr.settings.dce ? "enquiry" : "reply");
+               return 1;
+       }
+
+       i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 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",
+                      hdlc_to_name(hdlc), skb->data[i]);
+               return 1;
+       }
+       i++;
+
+       i++;                            /* Skip length field */
+
+       reptype = skb->data[i++];
+
+       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",
+                      hdlc_to_name(hdlc), skb->data[i]);
+               return 1;
+       }
+       i++;
+
+       i++;                    /* Skip length field */
+
+       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",
+                              hdlc_to_name(hdlc), reptype);
+                       return 1;
+               }
+       }
+
+       error = 0;
+       if (!hdlc->state.fr.reliable)
+               error = 1;
+
+       if (rxseq == 0 || rxseq != txseq) {
+               hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */
+               error = 1;
+       }
+
+       if (hdlc->state.fr.settings.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;
+                       pvc = hdlc->state.fr.first_pvc;
+                       while (pvc) {
+                               if (pvc->state.new) {
+                                       pvc->state.new = 0;
+                                       pvc->state.active = 1;
+                                       fr_log_dlci_active(pvc);
+
+/* Tell DTE that new PVC is now active */
+                                       hdlc->state.fr.changed = 1;
+                               }
+                               pvc = pvc->next;
+                       }
+               }
+
+               if (hdlc->state.fr.changed) {
+                       reptype = LMI_FULLREP;
+                       hdlc->state.fr.fullrep_sent = 1;
+                       hdlc->state.fr.changed = 0;
+               }
+
+               fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
+               return 0;
+       }
+
+       /* DTE */
+
+       if (reptype != LMI_FULLREP || error)
+               return 0;
+
+       stat_len = 3;
+       pvc = hdlc->state.fr.first_pvc;
+
+       while (pvc) {
+               pvc->state.deleted = pvc->state.active; /* mark active PVCs */
+               pvc = pvc->next;
+       }
+
+       while (skb->len >= i + 2 + stat_len) {
+               u16 dlci;
+               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",
+                              hdlc_to_name(hdlc), skb->data[i]);
+                       return 1;
+               }
+               i++;
+
+               if (skb->data[i] != stat_len) {
+                       printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n",
+                              hdlc_to_name(hdlc), skb->data[i]);
+                       return 1;
+               }
+               i++;
+
+               dlci = status_to_dlci(hdlc, skb->data + i, &active, &new);
+               pvc = find_pvc(hdlc, dlci);
+
+               active |= new;
+               if (pvc) {
+                       if (active && !pvc->state.active &&
+                           (pvc->netdev.flags & IFF_UP)) {
+                               pvc->state.active = active;
+                               fr_log_dlci_active(pvc);
+                       }
+                       pvc->state.deleted = 0;
+               }
+               else if (new)
+                       printk(KERN_INFO "%s: new PVC available, DLCI=%u\n",
+                              hdlc_to_name(hdlc), dlci);
+
+               i += stat_len;
+       }
+
+       pvc = hdlc->state.fr.first_pvc;
+
+       while (pvc) {
+               if (pvc->state.deleted) {
+                       pvc->state.active = pvc->state.new = 0;
+                       fr_log_dlci_active(pvc);
+                       pvc->state.deleted = 0;
+               }
+               pvc = pvc->next;
+       }
+
+       /* Next full report after N391 polls */
+       hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391;
+
+       return 0;
+}
+
+
+
+static void fr_rx(struct sk_buff *skb)
+{
+       hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+       fr_hdr *fh = (fr_hdr*)skb->data;
+       u8 *data = skb->data;
+       u16 dlci;
+       pvc_device *pvc;
+
+       if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
+               goto rx_error;
+
+       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(hdlc, skb))
+                               goto rx_error;
+                       else {
+                               /* No request pending */
+                               hdlc->state.fr.request = 0;
+                               hdlc->state.fr.last_poll = jiffies;
+                               dev_kfree_skb_any(skb);
+                               return;
+                       }
+               }
+
+               printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
+                      hdlc_to_name(hdlc));
+               goto rx_error;
+       }
+
+       pvc = find_pvc(hdlc, dlci);
+       if (!pvc) {
+#ifdef CONFIG_HDLC_DEBUG_PKT
+               printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
+                      hdlc_to_name(hdlc), dlci);
+#endif
+               goto rx_error;
+       }
+
+       if ((pvc->netdev.flags & IFF_UP) == 0) {
+#ifdef CONFIG_HDLC_DEBUG_PKT
+               printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n",
+                      hdlc_to_name(hdlc), dlci);
+#endif
+               goto rx_error;
+       }
+
+       pvc->stats.rx_packets++; /* PVC traffic */
+       pvc->stats.rx_bytes += skb->len;
+
+       if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+               printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
+                      fh->fecn ? "N" : "FF");
+#endif
+               pvc->state.fecn ^= 1;
+       }
+
+       if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+               printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
+                      fh->becn ? "N" : "FF");
+#endif
+               pvc->state.becn ^= 1;
+       }
+
+       if (pvc->state.becn)
+               pvc->stats.rx_compressed++;
+
+       skb->dev = &pvc->netdev;
+
+       if (data[3] == NLPID_IP) {
+               skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+               skb->protocol = htons(ETH_P_IP);
+               netif_rx(skb);
+               return;
+       }
+
+
+       if (data[3] == NLPID_IPV6) {
+               skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+               skb->protocol = htons(ETH_P_IPV6);
+               netif_rx(skb);
+               return;
+       }
+
+       if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) {
+               u16 oui = ntohl(*(u16*)(data + 6));
+               u16 pid = ntohl(*(u16*)(data + 8));
+               skb_pull(skb, 10);
+
+               switch ((((u32)oui) << 16) | pid) {
+               case ETH_P_ARP: /* routed frame with SNAP */
+               case ETH_P_IPX:
+                       skb->protocol = htons(pid);
+                       break;
+
+               default:
+                       printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
+                              "PID=%x\n", hdlc_to_name(hdlc), oui, pid);
+                       dev_kfree_skb_any(skb);
+                       return;
+               }
+
+               netif_rx(skb);
+               return;
+       }
+
+       printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n",
+              hdlc_to_name(hdlc), data[3]);
+       dev_kfree_skb_any(skb);
+       return;
+
+ rx_error:
+       hdlc->stats.rx_errors++; /* Mark error */
+       dev_kfree_skb_any(skb);
+}
+
+
+
+static int fr_open(hdlc_device *hdlc)
+{
+       if (hdlc->state.fr.settings.lmi != LMI_NONE) {
+               hdlc->state.fr.last_poll = 0;
+               hdlc->state.fr.reliable = 0;
+               hdlc->state.fr.changed = 1;
+               hdlc->state.fr.request = 0;
+               hdlc->state.fr.fullrep_sent = 0;
+               hdlc->state.fr.last_errors = 0xFFFFFFFF;
+               hdlc->state.fr.n391cnt = 0;
+               hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0;
+
+               init_timer(&hdlc->state.fr.timer);
+               /* First poll after 1 s */
+               hdlc->state.fr.timer.expires = jiffies + HZ;
+               hdlc->state.fr.timer.function = fr_timer;
+               hdlc->state.fr.timer.data = (unsigned long)hdlc;
+               add_timer(&hdlc->state.fr.timer);
+       } else
+               hdlc->state.fr.reliable = 1;
+
+       return 0;
+}
+
+
+
+static void fr_close(hdlc_device *hdlc)
+{
+       pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+       if (hdlc->state.fr.settings.lmi != LMI_NONE)
+               del_timer_sync(&hdlc->state.fr.timer);
+
+       while(pvc) {
+               dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */
+               pvc = pvc->next;
+       }
+}
+
+
+
+static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create)
+{
+       pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+       pvc_device *pvc;
+       int result;
+
+       if(dlci <= 0 || dlci >= 1024)
+               return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */
+
+       while(*pvc_p) {
+               if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
+                       break;
+               pvc_p = &(*pvc_p)->next;
+       }
+
+       if (create) {           /* Create PVC */
+               if (*pvc_p != NULL)
+                       return -EEXIST;
+
+               pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL);
+               if (!pvc) {
+                       printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
+                              hdlc_to_name(hdlc));
+                       return -ENOBUFS;
+               }
+               memset(pvc, 0, sizeof(pvc_device));
+
+               pvc->netdev.hard_start_xmit = pvc_xmit;
+               pvc->netdev.get_stats = pvc_get_stats;
+               pvc->netdev.open = pvc_open;
+               pvc->netdev.stop = pvc_close;
+               pvc->netdev.change_mtu = pvc_change_mtu;
+               pvc->netdev.mtu = HDLC_MAX_MTU;
+
+               pvc->netdev.type = ARPHRD_DLCI;
+               pvc->netdev.hard_header_len = 16;
+               pvc->netdev.hard_header = fr_hard_header;
+               pvc->netdev.tx_queue_len = 0;
+               pvc->netdev.flags = IFF_POINTOPOINT;
+
+               pvc->master = hdlc;
+               *(u16*)pvc->netdev.dev_addr = htons(dlci);
+               dlci_to_q922(pvc->netdev.broadcast, dlci);
+               pvc->netdev.addr_len = 2;
+
+               result = dev_alloc_name(&pvc->netdev, "pvc%d");
+               if (result < 0) {
+                       kfree(pvc);
+                       *pvc_p = NULL;
+                       return result;
+               }
+
+               if (register_netdevice(&pvc->netdev) != 0) {
+                       kfree(pvc);
+                       *pvc_p = NULL;
+                       return -EIO;
+               }
+
+               hdlc->state.fr.changed = 1;
+               hdlc->state.fr.pvc_count++;
+               return 0;
+       }
+
+       if (*pvc_p == NULL)             /* Delete PVC */
+               return -ENOENT;
+
+       pvc = *pvc_p;
+
+       if (pvc->netdev.flags & IFF_UP)
+               return -EBUSY;          /* PVC in use */
+
+       hdlc->state.fr.changed = 1;
+       hdlc->state.fr.pvc_count--;
+       *pvc_p = pvc->next;
+       unregister_netdevice(&pvc->netdev);
+       kfree(pvc);
+       return 0;
+}
+
+
+
+static void fr_destroy(hdlc_device *hdlc)
+{
+       pvc_device *pvc = hdlc->state.fr.first_pvc;
+       while(pvc) {
+               pvc_device *next = pvc->next;
+               unregister_netdevice(&pvc->netdev);
+               kfree(pvc);
+               pvc = next;
+       }
+
+       hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
+       hdlc->state.fr.pvc_count = 0;
+       hdlc->state.fr.changed = 1;
+}
+
+
+
+int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+       const size_t size = sizeof(fr_proto);
+       struct net_device *dev = hdlc_to_dev(hdlc);
+       fr_proto_pvc pvc;
+       int result;
+
+       switch (ifr->ifr_settings.type) {
+       case IF_GET_PROTO:
+               ifr->ifr_settings.type = IF_PROTO_FR;
+               if (ifr->ifr_settings.data_length == 0)
+                       return 0; /* return protocol only */
+               if (ifr->ifr_settings.data_length < size)
+                       return -ENOMEM; /* buffer too small */
+               if (copy_to_user(ifr->ifr_settings.data,
+                                &hdlc->state.fr.settings, size))
+                       return -EFAULT;
+               ifr->ifr_settings.data_length = size;
+               return 0;
+
+       case IF_PROTO_FR:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               if(dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               if (ifr->ifr_settings.data_length != size)
+                       return -ENOMEM; /* incorrect data length */
+
+               if (copy_from_user(&hdlc->state.fr.settings,
+                                  ifr->ifr_settings.data, size))
+                       return -EFAULT;
+
+               /* FIXME - put sanity checks here */
+               if (hdlc->proto != IF_PROTO_FR) {
+                       hdlc_detach(hdlc);
+                       hdlc->state.fr.first_pvc = NULL;
+                       hdlc->state.fr.pvc_count = 0;
+               }
+
+               result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+               if (result) {
+                       hdlc->proto = -1;
+                       return result;
+               }
+
+               hdlc->open = fr_open;
+               hdlc->stop = fr_close;
+               hdlc->netif_rx = fr_rx;
+               hdlc->detach = fr_destroy;
+               hdlc->proto = IF_PROTO_FR;
+               dev->hard_start_xmit = hdlc->xmit;
+               dev->hard_header = fr_hard_header;
+               dev->type = ARPHRD_FRAD;
+               dev->addr_len = 2;
+               *(u16*)dev->dev_addr = htons(LMI_DLCI);
+               dlci_to_q922(dev->broadcast, LMI_DLCI);
+               return 0;
+
+       case IF_PROTO_FR_ADD_PVC:
+       case IF_PROTO_FR_DEL_PVC:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               if (copy_from_user(&pvc, ifr->ifr_settings.data,
+                                  sizeof(fr_proto_pvc)))
+                       return -EFAULT;
+
+               return fr_pvc(hdlc, pvc.dlci,
+                             ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC);
+       }
+
+       return -EINVAL;
+}
diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc_generic.c
new file mode 100644 (file)
index 0000000..f1454c6
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Generic HDLC support routines for Linux
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Current status:
+ *    - this is work in progress
+ *    - not heavily tested on SMP
+ *    - 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
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static const char* version = "HDLC support module revision 1.08";
+
+
+static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
+               return -EINVAL;
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+
+
+static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
+{
+       return &dev_to_hdlc(dev)->stats;
+}
+
+
+
+static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
+                   struct packet_type *p)
+{
+       dev_to_hdlc(dev)->netif_rx(skb);
+       return 0;
+}
+
+
+
+int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       hdlc_device *hdlc = dev_to_hdlc(dev);
+       unsigned int proto;
+
+       if (cmd != SIOCDEVICE)
+               return -EINVAL;
+
+       switch(ifr->ifr_settings.type) {
+       case IF_PROTO_HDLC:
+       case IF_PROTO_PPP:
+       case IF_PROTO_CISCO:
+       case IF_PROTO_FR:
+       case IF_PROTO_X25:
+               proto = ifr->ifr_settings.type;
+               break;
+
+       default:
+               proto = hdlc->proto;
+       }
+
+       switch(proto) {
+#ifdef CONFIG_HDLC_RAW
+       case IF_PROTO_HDLC:     return hdlc_raw_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_PPP
+       case IF_PROTO_PPP:      return hdlc_ppp_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_CISCO
+       case IF_PROTO_CISCO:    return hdlc_cisco_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_FR
+       case IF_PROTO_FR:       return hdlc_fr_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_X25
+       case IF_PROTO_X25:      return hdlc_x25_ioctl(hdlc, ifr);
+#endif
+       default:                return -ENOSYS;
+       }
+}
+
+
+
+int register_hdlc_device(hdlc_device *hdlc)
+{
+       int result;
+       struct net_device *dev = hdlc_to_dev(hdlc);
+
+       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 = -1;
+       hdlc->detach = NULL;
+
+       result = dev_alloc_name(dev, "hdlc%d");
+       if (result<0)
+               return result;
+
+       result = register_netdev(dev);
+       if (result != 0)
+               return -EIO;
+
+       MOD_INC_USE_COUNT;
+       return 0;
+}
+
+
+
+void unregister_hdlc_device(hdlc_device *hdlc)
+{
+       hdlc_detach(hdlc);
+
+       unregister_netdev(hdlc_to_dev(hdlc));
+       MOD_DEC_USE_COUNT;
+}
+
+
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("HDLC support module");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(hdlc_ioctl);
+EXPORT_SYMBOL(register_hdlc_device);
+EXPORT_SYMBOL(unregister_hdlc_device);
+
+struct packet_type hdlc_packet_type=
+{
+        __constant_htons(ETH_P_HDLC),
+        NULL,
+        hdlc_rcv,
+        NULL,
+        NULL
+};
+
+
+static int __init hdlc_module_init(void)
+{
+       printk(KERN_INFO "%s\n", version);
+        dev_add_pack(&hdlc_packet_type);
+       return 0;
+}
+
+
+
+static void __exit hdlc_module_exit(void)
+{
+       dev_remove_pack(&hdlc_packet_type);
+}
+
+
+module_init(hdlc_module_init);
+module_exit(hdlc_module_exit);
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
new file mode 100644 (file)
index 0000000..0f95449
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Point-to-point protocol support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static int ppp_open(hdlc_device *hdlc)
+{
+       struct net_device *dev = hdlc_to_dev(hdlc);
+       void *old_ioctl;
+       int result;
+
+       dev->priv = &hdlc->state.ppp.syncppp_ptr;
+       hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev;
+       hdlc->state.ppp.pppdev.dev = dev;
+
+       old_ioctl = dev->do_ioctl;
+       hdlc->state.ppp.old_change_mtu = dev->change_mtu;
+       sppp_attach(&hdlc->state.ppp.pppdev);
+       /* sppp_attach nukes them. We don't need syncppp's ioctl */
+       dev->do_ioctl = old_ioctl;
+       hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO;
+       dev->type = ARPHRD_PPP;
+       result = sppp_open(dev);
+       if (result) {
+               sppp_detach(dev);
+               return result;
+       }
+
+       return 0;
+}
+
+
+
+static void ppp_close(hdlc_device *hdlc)
+{
+       struct net_device *dev = hdlc_to_dev(hdlc);
+
+       sppp_close(dev);
+       sppp_detach(dev);
+       dev->rebuild_header = NULL;
+       dev->change_mtu = hdlc->state.ppp.old_change_mtu;
+       dev->mtu = HDLC_MAX_MTU;
+       dev->hard_header_len = 16;
+}
+
+
+
+static void ppp_rx(struct sk_buff *skb)
+{
+       skb->protocol = htons(ETH_P_WAN_PPP);
+       netif_rx(skb);
+}
+
+
+
+int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+       struct net_device *dev = hdlc_to_dev(hdlc);
+       int result;
+
+       switch (ifr->ifr_settings.type) {
+       case IF_GET_PROTO:
+               ifr->ifr_settings.type = IF_PROTO_PPP;
+               ifr->ifr_settings.data_length = 0;
+               return 0; /* return protocol only, no settable parameters */
+
+       case IF_PROTO_PPP:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               if(dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               if (ifr->ifr_settings.data_length != 0)
+                       return -EINVAL; /* no settable parameters */
+
+               hdlc_detach(hdlc);
+
+               result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+               if (result) {
+                       hdlc->proto = -1;
+                       return result;
+               }
+
+               hdlc->open = ppp_open;
+               hdlc->stop = ppp_close;
+               hdlc->netif_rx = ppp_rx;
+               hdlc->proto = IF_PROTO_PPP;
+               dev->hard_start_xmit = hdlc->xmit;
+               dev->hard_header = NULL;
+               dev->type = ARPHRD_PPP;
+               dev->addr_len = 0;
+               return 0;
+       }
+
+       return -EINVAL;
+}
diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c
new file mode 100644 (file)
index 0000000..f61e546
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Generic HDLC support routines for Linux
+ * HDLC support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static void raw_rx(struct sk_buff *skb)
+{
+       skb->protocol = htons(ETH_P_IP);
+       netif_rx(skb);
+}
+
+
+
+int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+       const size_t size = sizeof(hdlc_proto);
+       struct net_device *dev = hdlc_to_dev(hdlc);
+       int result;
+
+       switch (ifr->ifr_settings.type) {
+       case IF_GET_PROTO:
+               ifr->ifr_settings.type = IF_PROTO_HDLC;
+               if (ifr->ifr_settings.data_length == 0)
+                       return 0; /* return protocol only */
+               if (ifr->ifr_settings.data_length < size)
+                       return -ENOMEM; /* buffer too small */
+               if (copy_to_user(ifr->ifr_settings.data,
+                                &hdlc->state.hdlc.settings, size))
+                       return -EFAULT;
+               ifr->ifr_settings.data_length = size;
+               return 0;
+
+       case IF_PROTO_HDLC:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               if(dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               if (ifr->ifr_settings.data_length != size)
+                       return -ENOMEM; /* incorrect data length */
+
+               if (copy_from_user(&hdlc->state.hdlc.settings,
+                                  ifr->ifr_settings.data, size))
+                       return -EFAULT;
+
+               /* FIXME - put sanity checks here */
+               hdlc_detach(hdlc);
+
+               result=hdlc->attach(hdlc, hdlc->state.hdlc.settings.encoding,
+                                   hdlc->state.hdlc.settings.parity);
+               if (result) {
+                       hdlc->proto = -1;
+                       return result;
+               }
+
+               hdlc->open = NULL;
+               hdlc->stop = NULL;
+               hdlc->netif_rx = raw_rx;
+               hdlc->proto = IF_PROTO_HDLC;
+               dev->hard_start_xmit = hdlc->xmit;
+               dev->hard_header = NULL;
+               dev->type = ARPHRD_RAWHDLC;
+               dev->addr_len = 0;
+               return 0;
+       }
+
+       return -EINVAL;
+}
diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c
new file mode 100644 (file)
index 0000000..b0bbce5
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Generic HDLC support routines for Linux
+ * X.25 support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+/* These functions are callbacks called by LAPB layer */
+
+static void x25_connect_disconnect(void *token, int reason, int code)
+{
+       hdlc_device *hdlc = token;
+       struct sk_buff *skb;
+       unsigned char *ptr;
+
+       if ((skb = dev_alloc_skb(1)) == NULL) {
+               printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc));
+               return;
+       }
+
+       ptr = skb_put(skb, 1);
+       *ptr = code;
+
+       skb->dev = hdlc_to_dev(hdlc);
+       skb->protocol = htons(ETH_P_X25);
+       skb->mac.raw = skb->data;
+       skb->pkt_type = PACKET_HOST;
+
+       netif_rx(skb);
+}
+
+
+
+static void x25_connected(void *token, int reason)
+{
+       x25_connect_disconnect(token, reason, 1);
+}
+
+
+
+static void x25_disconnected(void *token, int reason)
+{
+       x25_connect_disconnect(token, reason, 2);
+}
+
+
+
+static int x25_data_indication(void *token, struct sk_buff *skb)
+{
+       hdlc_device *hdlc = token;
+       unsigned char *ptr;
+
+       ptr = skb_push(skb, 1);
+       *ptr = 0;
+
+       skb->dev = hdlc_to_dev(hdlc);
+       skb->protocol = htons(ETH_P_X25);
+       skb->mac.raw = skb->data;
+       skb->pkt_type = PACKET_HOST;
+
+       return netif_rx(skb);
+}
+
+
+
+static void x25_data_transmit(void *token, struct sk_buff *skb)
+{
+       hdlc_device *hdlc = token;
+       hdlc->xmit(skb, hdlc_to_dev(hdlc)); /* Ignore return value :-( */
+}
+
+
+
+static int x25_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       hdlc_device *hdlc = dev_to_hdlc(dev);
+       int result;
+
+
+       /* X.25 to LAPB */
+       switch (skb->data[0]) {
+       case 0:         /* Data to be transmitted */
+               skb_pull(skb, 1);
+               if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK)
+                       dev_kfree_skb(skb);
+               return 0;
+
+       case 1:
+               if ((result = lapb_connect_request(hdlc))!= LAPB_OK) {
+                       if (result == LAPB_CONNECTED)
+                               /* Send connect confirm. msg to level 3 */
+                               x25_connected(hdlc, 0);
+                       else
+                               printk(KERN_ERR "%s: LAPB connect request "
+                                      "failed, error code = %i\n",
+                                      hdlc_to_name(hdlc), result);
+               }
+               break;
+
+       case 2:
+               if ((result = lapb_disconnect_request(hdlc)) != LAPB_OK) {
+                       if (result == LAPB_NOTCONNECTED)
+                               /* Send disconnect confirm. msg to level 3 */
+                               x25_disconnected(hdlc, 0);
+                       else
+                               printk(KERN_ERR "%s: LAPB disconnect request "
+                                      "failed, error code = %i\n",
+                                      hdlc_to_name(hdlc), result);
+               }
+               break;
+
+       default:                /* to be defined */
+               break;
+       }
+
+       dev_kfree_skb(skb);
+       return 0;
+}
+
+
+
+static int x25_open(hdlc_device *hdlc)
+{
+       struct lapb_register_struct cb;
+       int result;
+
+       cb.connect_confirmation = x25_connected;
+       cb.connect_indication = x25_connected;
+       cb.disconnect_confirmation = x25_disconnected;
+       cb.disconnect_indication = x25_disconnected;
+       cb.data_indication = x25_data_indication;
+       cb.data_transmit = x25_data_transmit;
+
+       result = lapb_register(hdlc, &cb);
+       if (result != LAPB_OK)
+               return result;
+       return 0;
+}
+
+
+
+static void x25_close(hdlc_device *hdlc)
+{
+       lapb_unregister(hdlc);
+}
+
+
+
+static void x25_rx(struct sk_buff *skb)
+{
+       hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+
+       if (lapb_data_received(hdlc, skb) == LAPB_OK)
+               return;
+       hdlc->stats.rx_errors++;
+       dev_kfree_skb_any(skb);
+}
+
+
+
+int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+       struct net_device *dev = hdlc_to_dev(hdlc);
+       int result;
+
+       switch (ifr->ifr_settings.type) {
+       case IF_GET_PROTO:
+               ifr->ifr_settings.type = IF_PROTO_X25;
+               ifr->ifr_settings.data_length = 0;
+               return 0; /* return protocol only, no settable parameters */
+
+       case IF_PROTO_X25:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
+
+               if(dev->flags & IFF_UP)
+                       return -EBUSY;
+
+               if (ifr->ifr_settings.data_length != 0)
+                       return -EINVAL; /* no settable parameters */
+
+               hdlc_detach(hdlc);
+
+               result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+               if (result) {
+                       hdlc->proto = -1;
+                       return result;
+               }
+
+               hdlc->open = x25_open;
+               hdlc->stop = x25_close;
+               hdlc->netif_rx = x25_rx;
+               hdlc->proto = IF_PROTO_X25;
+               dev->hard_start_xmit = x25_xmit;
+               dev->hard_header = NULL;
+               dev->type = ARPHRD_X25;
+               dev->addr_len = 0;
+               return 0;
+       }
+
+       return -EINVAL;
+}
index 4c0ae1d3a7fb27c6c870c9685e5c5662e6cbe65a..d5d0d1c2e87292a3e85406b554e21ae168544858 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * SDL Inc. RISCom/N2 synchronous serial card driver for Linux
  *
- * Copyright (C) 1998-2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2001 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
  *    SDL Inc. PPP/HDLC/CISCO driver
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <asm/io.h>
 #include "hd64570.h"
 
-#define DEBUG_RINGS
-/* #define DEBUG_PKT */
 
-static const char* version = "SDL RISCom/N2 driver revision: 1.02 for Linux 2.4";
+static const char* version = "SDL RISCom/N2 driver version: 1.09";
 static const char* devname = "RISCom/N2";
 
 #define USE_WINDOWSIZE 16384
@@ -87,9 +86,9 @@ typedef struct port_s {
        hdlc_device hdlc;       /* HDLC device struct - must be first */
        struct card_s *card;
        spinlock_t lock;        /* TX lock */
-       int clkmode;            /* clock mode */
-       int clkrate;            /* clock rate */
-       int line;               /* loopback only */
+       sync_serial_settings settings;
+       unsigned short encoding;
+       unsigned short parity;
        u8 rxs, txs, tmc;       /* SCA registers */
        u8 valid;               /* port enabled */
        u8 phy_node;            /* physical port # - 0 or 1 */
@@ -116,6 +115,9 @@ typedef struct card_s {
 }card_t;
 
 
+static card_t *first_card;
+static card_t **new_card = &first_card;
+
 
 #define sca_reg(reg, card) (0x8000 | (card)->io | \
                            ((reg) & 0x0F) | (((reg) & 0xF0) << 6))
@@ -157,7 +159,7 @@ static __inline__ void close_windows(card_t *card)
 
 
 
-static int n2_set_clock(port_t *port, int value)
+static int n2_set_iface(port_t *port)
 {
        card_t *card = port->card;
        int io = card->io;
@@ -166,7 +168,7 @@ static int n2_set_clock(port_t *port, int value)
        u8 rxs = port->rxs & CLK_BRG_MASK;
        u8 txs = port->txs & CLK_BRG_MASK;
 
-       switch(value) {
+       switch(port->settings.clock_type) {
        case CLOCK_EXT:
                mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0;
                rxs |= CLK_LINE_RX; /* RXC input */
@@ -200,17 +202,22 @@ static int n2_set_clock(port_t *port, int value)
        port->txs = txs;
        sca_out(rxs, msci + RXS, card);
        sca_out(txs, msci + TXS, card);
-       port->clkmode = value;
+       sca_set_port(port);
        return 0;
 }
 
 
 
-static int n2_open(hdlc_device *hdlc)
+static int n2_open(struct net_device *dev)
 {
+       hdlc_device *hdlc = dev_to_hdlc(dev);
        port_t *port = hdlc_to_port(hdlc);
        int io = port->card->io;
-       u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0);
+       u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0);
+
+       int result = hdlc_open(hdlc);
+       if (result)
+               return result;
 
        MOD_INC_USE_COUNT;
        mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
@@ -219,14 +226,14 @@ static int n2_open(hdlc_device *hdlc)
        outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */
        outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */
        sca_open(hdlc);
-       n2_set_clock(port, port->clkmode);
-       return 0;
+       return n2_set_iface(port);
 }
 
 
 
-static void n2_close(hdlc_device *hdlc)
+static int n2_close(struct net_device *dev)
 {
+       hdlc_device *hdlc = dev_to_hdlc(dev);
        port_t *port = hdlc_to_port(hdlc);
        int io = port->card->io;
        u8 mcr = inb(io+N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0);
@@ -234,52 +241,57 @@ static void n2_close(hdlc_device *hdlc)
        sca_close(hdlc);
        mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
        outb(mcr, io + N2_MCR);
+       hdlc_close(hdlc);
        MOD_DEC_USE_COUNT;
+       return 0;
 }
 
 
 
-static int n2_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       int value = ifr->ifr_ifru.ifru_ivalue;
-       int result = 0;
+       hdlc_device *hdlc = dev_to_hdlc(dev);
        port_t *port = hdlc_to_port(hdlc);
+       const size_t size = sizeof(sync_serial_settings);
 
-       if(!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       switch(cmd) {
-       case HDLCSCLOCK:
-               result = n2_set_clock(port, value);
-       case HDLCGCLOCK:
-               value = port->clkmode;
-               break;
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+       if (cmd == SIOCDEVPRIVATE) {
+               sca_dump_rings(hdlc);
+               return 0;
+       }
+#endif
+       if (cmd != SIOCDEVICE)
+               return hdlc_ioctl(dev, ifr, cmd);
+
+       switch(ifr->ifr_settings.type) {
+       case IF_GET_IFACE:
+               ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+               if (ifr->ifr_settings.data_length == 0)
+                       return 0; /* return interface type only */
+               if (ifr->ifr_settings.data_length < size)
+                       return -ENOMEM; /* buffer too small */
+               if (copy_to_user(ifr->ifr_settings.data,
+                                &port->settings, size))
+                       return -EFAULT;
+               ifr->ifr_settings.data_length = size;
+               return 0;
 
-       case HDLCSCLOCKRATE:
-               port->clkrate = value;
-               sca_set_clock(port);
-       case HDLCGCLOCKRATE:
-               value = port->clkrate;
-               break;
+       case IF_IFACE_SYNC_SERIAL:
+               if(!capable(CAP_NET_ADMIN))
+                       return -EPERM;
 
-       case HDLCSLINE:
-               result = sca_set_loopback(port, value);
-       case HDLCGLINE:
-               value = port->line;
-               break;
+               if (ifr->ifr_settings.data_length != size)
+                       return -ENOMEM; /* incorrect data length */
 
-#ifdef DEBUG_RINGS
-       case HDLCRUN:
-               sca_dump_rings(hdlc);
-               return 0;
-#endif /* DEBUG_RINGS */
+               if (copy_from_user(&port->settings,
+                                  ifr->ifr_settings.data, size))
+                       return -EFAULT;
+               /* FIXME - put sanity checks here */
+               return n2_set_iface(port);
 
        default:
-               return -EINVAL;
+               return hdlc_ioctl(dev, ifr, cmd);
        }
-
-       ifr->ifr_ifru.ifru_ivalue = value;
-       return result;
 }
 
 
@@ -465,6 +477,7 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
        sca_init(card, 0);
        for (cnt = 0; cnt < 2; cnt++) {
                port_t *port = &card->ports[cnt];
+               struct net_device *dev = hdlc_to_dev(&port->hdlc);
 
                if ((cnt == 0 && !valid0) || (cnt == 1 && !valid1))
                        continue;
@@ -476,14 +489,16 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
                        port->log_node = 1;
 
                spin_lock_init(&port->lock);
-               hdlc_to_dev(&port->hdlc)->irq = irq;
-               hdlc_to_dev(&port->hdlc)->mem_start = winbase;
-               hdlc_to_dev(&port->hdlc)->mem_end = winbase + USE_WINDOWSIZE-1;
-               hdlc_to_dev(&port->hdlc)->tx_queue_len = 50;
-               port->hdlc.ioctl = n2_ioctl;
-               port->hdlc.open = n2_open;
-               port->hdlc.close = n2_close;
+               dev->irq = irq;
+               dev->mem_start = winbase;
+               dev->mem_end = winbase + USE_WINDOWSIZE-1;
+               dev->tx_queue_len = 50;
+               dev->do_ioctl = n2_ioctl;
+               dev->open = n2_open;
+               dev->stop = n2_close;
+               port->hdlc.attach = sca_attach;
                port->hdlc.xmit = sca_xmit;
+               port->settings.clock_type = CLOCK_EXT;
 
                if (register_hdlc_device(&port->hdlc)) {
                        printk(KERN_WARNING "n2: unable to register hdlc "
@@ -515,7 +530,7 @@ static int __init n2_init(void)
                return -ENOSYS; /* no parameters specified, abort */
        }
 
-       printk(KERN_INFO "%s\n", version);
+       printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
 
        do {
                unsigned long io, irq, ram;
index 3ac12961d8023c2e1434656f06f854319c9f39e7..eea06e00db222911f77fa03b89d4eda5769ab9c2 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Generic HDLC support routines for Linux
  *
- * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999-2002 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by
 #ifndef __HDLC_H
 #define __HDLC_H
 
-/* Ioctls - to be changed */
-#define HDLCGSLOTMAP   (0x89F4) /* E1/T1 slot bitmap */
-#define HDLCGCLOCK     (0x89F5) /* clock sources */
-#define HDLCGCLOCKRATE (0x89F6) /* clock rate */
-#define HDLCGMODE      (0x89F7) /* internal to hdlc.c - protocol used */
-#define HDLCGLINE      (0x89F8) /* physical interface */
-#define HDLCSSLOTMAP   (0x89F9)
-#define HDLCSCLOCK     (0x89FA)
-#define HDLCSCLOCKRATE (0x89FB)
-#define HDLCSMODE      (0x89FC) /* internal to hdlc.c - select protocol */
-#define HDLCPVC                (0x89FD) /* internal to hdlc.c - create/delete PVC */
-#define HDLCSLINE      (0x89FE)
-#define HDLCRUN                (0x89FF) /* Download firmware and run board */
-
-/* Modes */
-#define MODE_NONE      0x00000000 /* Not initialized */
-#define MODE_DCE       0x00000080 /* DCE */
-#define MODE_HDLC      0x00000100 /* Raw HDLC frames */
-#define MODE_CISCO     0x00000200
-#define MODE_PPP       0x00000400
-#define MODE_FR                0x00000800 /* Any LMI */
-#define MODE_FR_ANSI   0x00000801
-#define MODE_FR_CCITT  0x00000802
-#define MODE_X25       0x00001000
-#define MODE_MASK      0x0000FF00
-#define MODE_SOFT      0x80000000 /* Driver modes, using hardware HDLC */
-
-/* Lines */
-#define LINE_DEFAULT   0x00000000
-#define LINE_V35       0x00000001
-#define LINE_RS232     0x00000002
-#define LINE_X21       0x00000003
-#define LINE_T1                0x00000004
-#define LINE_E1                0x00000005
-#define LINE_MASK      0x000000FF
-#define LINE_LOOPBACK  0x80000000 /* On-card loopback */
-
-#define CLOCK_EXT      0       /* External TX and RX clock - DTE */
-#define CLOCK_INT      1       /* Internal TX and RX clock - DCE */
-#define CLOCK_TXINT    2       /* Internal TX and external RX clock */
-#define CLOCK_TXFROMRX 3       /* TX clock derived from external RX clock */
+#define CLOCK_DEFAULT   0      /* Default (current) setting */
+#define CLOCK_EXT      1       /* External TX and RX clock - DTE */
+#define CLOCK_INT      2       /* Internal TX and RX clock - DCE */
+#define CLOCK_TXINT    3       /* Internal TX and external RX clock */
+#define CLOCK_TXFROMRX 4       /* TX clock derived from external RX clock */
 
+typedef struct {
+       unsigned int clock_rate; /* bits per second */
+       unsigned int clock_type; /* internal, external, TX-internal etc. */
+       unsigned short loopback;
+}sync_serial_settings;         /* V.35, V.24, X.21 */
+
+typedef struct {
+       unsigned int clock_rate; /* bits per second */
+       unsigned int clock_type; /* internal, external, TX-internal etc. */
+       unsigned short loopback;
+       unsigned int slot_map;
+}te1_settings;                 /* T1, E1 */
+
+
+
+#define ENCODING_DEFAULT       0 /* Default (current) setting */
+#define ENCODING_NRZ           1
+#define ENCODING_NRZI          2
+#define ENCODING_FM_MARK       3
+#define ENCODING_FM_SPACE      4
+#define ENCODING_MANCHESTER    5
+
+
+#define PARITY_DEFAULT         0 /* Default (current) setting */
+#define PARITY_NONE            1 /* No parity */
+#define PARITY_CRC16_PR0       2 /* CRC16, initial value 0x0000 */
+#define PARITY_CRC16_PR1       3 /* CRC16, initial value 0xFFFF */
+#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */
+#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */
+#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */
+#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */
+
+typedef struct {
+       unsigned short encoding;
+       unsigned short parity;
+}hdlc_proto;
+
+
+#define LMI_DEFAULT            0 /* Default (current) setting */
+#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 */
+
+typedef struct {
+       unsigned int t391;
+       unsigned int t392;
+       unsigned int n391;
+       unsigned int n392;
+       unsigned int n393;
+       unsigned short lmi;
+       unsigned short dce;     /* 1 for DCE (network side) operation */
+}fr_proto;
+
+typedef struct {
+       unsigned int dlci;
+}fr_proto_pvc;                 /* for creating/deleting FR PVCs */
+
+
+typedef struct {
+       unsigned int interval;
+       unsigned int timeout;
+}cisco_proto;
+
+
+/* PPP doesn't need any info now - supply length = 0 to ioctl */
 
-#define HDLC_MAX_MTU 1500      /* Ethernet 1500 bytes */
-#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
 
 #ifdef __KERNEL__
 
 #include <linux/netdevice.h>
 #include <net/syncppp.h>
 
-#define MAXLEN_LMISTAT  20     /* max size of status enquiry frame */
+#define HDLC_MAX_MTU 1500      /* Ethernet 1500 bytes */
+#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
 
-#define LINK_STATE_RELIABLE 0x01
-#define LINK_STATE_REQUEST  0x02 /* full stat sent (DCE) / req pending (DTE) */
-#define LINK_STATE_CHANGED  0x04 /* change in PVCs state, send full report */
-#define LINK_STATE_FULLREP_SENT 0x08 /* full report sent */
+#define MAXLEN_LMISTAT  20     /* max size of status enquiry frame */
 
 #define PVC_STATE_NEW       0x01
 #define PVC_STATE_ACTIVE    0x02
 
 
 typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
        unsigned ea1  : 1;
        unsigned cr   : 1;
        unsigned dlcih: 6;
@@ -121,6 +147,19 @@ typedef struct {
        unsigned becn : 1;
        unsigned fecn : 1;
        unsigned dlcil: 4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+       unsigned dlcih: 6;
+       unsigned cr   : 1;
+       unsigned ea1  : 1;
+  
+       unsigned dlcil: 4;
+       unsigned fecn : 1;
+       unsigned becn : 1;
+       unsigned de   : 1;
+       unsigned ea2  : 1;
+#else
+#error  "Please fix <asm/byteorder.h>"
+#endif
 }__attribute__ ((packed)) fr_hdr;
 
 
@@ -151,63 +190,96 @@ typedef struct pvc_device_struct {
        struct hdlc_device_struct *master;
        struct pvc_device_struct *next;
 
-       u8 state;
-       u8 newstate;
+       struct {
+               int active;
+               int new;
+               int deleted;
+               int fecn;
+               int becn;
+       }state;
 }pvc_device;
 
 
 
-typedef struct {
-       u32 last_errors;        /* last errors bit list */
-       int last_poll;          /* ! */
-       u8 T391;                /* ! link integrity verification polling timer */
-       u8 T392;                /* ! polling verification timer */
-       u8 N391;                /* full status polling counter */
-       u8 N392;                /* error threshold */
-       u8 N393;                /* monitored events count */
-       u8 N391cnt;
-
-       u8 state;               /* ! */
-       u32 txseq;              /* ! TX sequence number - Cisco uses 4 bytes */
-       u32 rxseq;              /* ! RX sequence number */
-}fr_lmi;                       /* ! means used in Cisco HDLC as well */
-
-
 typedef struct hdlc_device_struct {
-       /* to be initialized by hardware driver: */
+       /* To be initialized by hardware driver */
        struct net_device netdev; /* master net device - must be first */
        struct net_device_stats stats;
 
-       struct ppp_device pppdev;
-       struct ppp_device *syncppp_ptr;
+       /* used by HDLC layer to take control over HDLC device from hw driver*/
+       int (*attach)(struct hdlc_device_struct *hdlc,
+                     unsigned short encoding, unsigned short parity);
+
+       /* hardware driver must handle this instead of dev->hard_start_xmit */
+       int (*xmit)(struct sk_buff *skb, struct net_device *dev);
+
 
-       /* set_mode may be NULL if HDLC-only board */
-       int (*set_mode)(struct hdlc_device_struct *hdlc, int mode);
+       /* Things below are for HDLC layer internal use only */
+       int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
        int (*open)(struct hdlc_device_struct *hdlc);
-       void (*close)(struct hdlc_device_struct *hdlc);
-       int (*xmit)(struct hdlc_device_struct *hdlc, struct sk_buff *skb);
-       int (*ioctl)(struct hdlc_device_struct *hdlc, struct ifreq *ifr,
-                    int cmd);
-  
-       /* Only in "hardware" FR modes etc. - may be NULL */
-       int (*create_pvc)(pvc_device *pvc);
-       void (*destroy_pvc)(pvc_device *pvc);
-       int (*open_pvc)(pvc_device *pvc);
-       void (*close_pvc)(pvc_device *pvc);
-
-       /* for hdlc.c internal use only */
-       pvc_device *first_pvc;
-       u16 pvc_count;
-       int mode;
-
-       struct timer_list timer;
-       fr_lmi lmi;
+       void (*stop)(struct hdlc_device_struct *hdlc);
+       void (*detach)(struct hdlc_device_struct *hdlc);
+       void (*netif_rx)(struct sk_buff *skb);
+       int proto;              /* IF_PROTO_HDLC/CISCO/FR/etc. */
+
+       union {
+               struct {
+                       fr_proto settings;
+                       pvc_device *first_pvc;
+                       int pvc_count;
+
+                       struct timer_list timer;
+                       int last_poll;
+                       int reliable;
+                       int changed;
+                       int request;
+                       int fullrep_sent;
+                       u32 last_errors; /* last errors bit list */
+                       u8 n391cnt;
+                       u8 txseq; /* TX sequence number */
+                       u8 rxseq; /* RX sequence number */
+               }fr;
+
+               struct {
+                       cisco_proto settings;
+
+                       struct timer_list timer;
+                       int last_poll;
+                       int up;
+                       u32 txseq; /* TX sequence number */
+                       u32 rxseq; /* RX sequence number */
+               }cisco;
+
+               struct {
+                       hdlc_proto settings;
+               }hdlc;
+
+               struct {
+                       struct ppp_device pppdev;
+                       struct ppp_device *syncppp_ptr;
+                       int (*old_change_mtu)(struct net_device *dev,
+                                             int new_mtu);
+               }ppp;
+       }state;
 }hdlc_device;
 
 
+
+int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+
+
+/* Exported from hdlc.o */
+
+/* Called by hardware driver when a user requests HDLC service */
+int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+/* Must be used by hardware driver on module startup/exit */
 int register_hdlc_device(hdlc_device *hdlc);
 void unregister_hdlc_device(hdlc_device *hdlc);
-void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb);
 
 
 static __inline__ struct net_device* hdlc_to_dev(hdlc_device *hdlc)
@@ -246,33 +318,6 @@ static __inline__ const char *pvc_to_name(pvc_device *pvc)
 }
 
 
-static __inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, u8 *state)
-{
-       *state &= ~(PVC_STATE_ACTIVE | PVC_STATE_NEW);
-       if (status[2] & 0x08)
-               *state |= PVC_STATE_NEW;
-       else if (status[2] & 0x02)
-               *state |= PVC_STATE_ACTIVE;
-
-       return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
-}
-
-
-static __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
-                                     u8 state)
-{
-       status[0] = (dlci>>4) & 0x3F;
-       status[1] = ((dlci<<3) & 0x78) | 0x80;
-       status[2] = 0x80;
-
-       if (state & PVC_STATE_NEW)
-               status[2] |= 0x08;
-       else if (state & PVC_STATE_ACTIVE)
-               status[2] |= 0x02;
-}
-
-
-
 static __inline__ u16 netdev_dlci(struct net_device *dev)
 {
        return ntohs(*(u16*)dev->dev_addr);
@@ -282,37 +327,15 @@ static __inline__ u16 netdev_dlci(struct net_device *dev)
 
 static __inline__ u16 q922_to_dlci(u8 *hdr)
 {
-       return ((hdr[0] & 0xFC)<<2) | ((hdr[1] & 0xF0)>>4);
+       return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
 }
 
 
 
 static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci)
 {
-       hdr[0] = (dlci>>2) & 0xFC;
-       hdr[1] = ((dlci<<4) & 0xF0) | 0x01;
-}
-
-
-
-static __inline__ int mode_is(hdlc_device *hdlc, int mask)
-{
-       return (hdlc->mode & mask) == mask;
-}
-
-
-
-static __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
-{
-       pvc_device *pvc=hdlc->first_pvc;
-       
-       while (pvc) {
-               if (netdev_dlci(&pvc->netdev) == dlci)
-                       return pvc;
-               pvc=pvc->next;
-       }
-
-       return NULL;
+       hdr[0] = (dlci >> 2) & 0xFC;
+       hdr[1] = ((dlci << 4) & 0xF0) | 0x01;
 }
 
 
@@ -332,5 +355,35 @@ static __inline__ void debug_frame(const struct sk_buff *skb)
 }
 
 
+
+/* Must be called by hardware driver when HDLC device is being opened */
+static __inline__ int hdlc_open(hdlc_device *hdlc)
+{
+       if (hdlc->proto == -1)
+               return -ENOSYS;         /* no protocol attached */
+
+       if (hdlc->open)
+               return hdlc->open(hdlc);
+       return 0;
+}
+
+
+/* Must be called by hardware driver when HDLC device is being closed */
+static __inline__ void hdlc_close(hdlc_device *hdlc)
+{
+       if (hdlc->stop)
+               hdlc->stop(hdlc);
+}
+
+
+/* May be used by hardware driver to gain control over HDLC device */
+static __inline__ void hdlc_detach(hdlc_device *hdlc)
+{
+       if (hdlc->detach)
+               hdlc->detach(hdlc);
+       hdlc->detach = NULL;
+}
+
+
 #endif /* __KERNEL */
 #endif /* __HDLC_H */
index 9a38b2e2ab335656b734f5cd512bd4eb2426bef2..2bba24a313a43a82b99ba6c0e25ad8d236152e17 100644 (file)
 /* Private (from user) interface flags (netdevice->priv_flags). */
 #define IFF_802_1Q_VLAN 0x1             /* 802.1Q VLAN device.          */
 
+
+#define IF_GET_IFACE   0x0001          /* for querying only */
+#define IF_GET_PROTO   0x0002
+
+/* For definitions see hdlc.h */
+#define IF_IFACE_V35   0x1000          /* V.35 serial interface        */
+#define IF_IFACE_V24   0x1001          /* V.24 serial interface        */
+#define IF_IFACE_X21   0x1002          /* X.21 serial interface        */
+#define IF_IFACE_T1    0x1003          /* T1 telco serial interface    */
+#define IF_IFACE_E1    0x1004          /* E1 telco serial interface    */
+#define IF_IFACE_SYNC_SERIAL 0x1005    /* cant'b be set by software    */
+
+/* For definitions see hdlc.h */
+#define IF_PROTO_HDLC  0x2000          /* raw HDLC protocol            */
+#define IF_PROTO_PPP   0x2001          /* PPP protocol                 */
+#define IF_PROTO_CISCO 0x2002          /* Cisco HDLC protocol          */
+#define IF_PROTO_FR    0x2003          /* Frame Relay protocol         */
+#define IF_PROTO_FR_ADD_PVC 0x2004     /*    Create FR PVC             */
+#define IF_PROTO_FR_DEL_PVC 0x2005     /*    Delete FR PVC             */
+#define IF_PROTO_X25   0x2006          /* X.25                         */
+
+
+
 /*
  *     Device mapping structure. I'd just gone off and designed a 
  *     beautiful scheme using only loadable modules with arguments
@@ -69,6 +92,14 @@ struct ifmap
        /* 3 bytes spare */
 };
 
+struct if_settings
+{
+       unsigned int type;      /* Type of physical device or protocol */
+       unsigned int data_length; /* device/protocol data length */
+       void * data;            /* pointer to data, ignored if length = 0 */
+};
+
+
 /*
  * Interface request structure used for socket
  * ioctl's.  All interface ioctl's must have parameter
@@ -98,6 +129,7 @@ struct ifreq
                char    ifru_slave[IFNAMSIZ];   /* Just fits the size */
                char    ifru_newname[IFNAMSIZ];
                char *  ifru_data;
+               struct  if_settings ifru_settings;
        } ifr_ifru;
 };
 
@@ -117,6 +149,7 @@ struct ifreq
 #define ifr_bandwidth  ifr_ifru.ifru_ivalue    /* link bandwidth       */
 #define ifr_qlen       ifr_ifru.ifru_ivalue    /* Queue length         */
 #define ifr_newname    ifr_ifru.ifru_newname   /* New name             */
+#define ifr_settings   ifr_ifru.ifru_settings  /* Device/proto settings*/
 
 /*
  * Structure used in SIOCGIFCONF request.
index b64559d713f98f1a2264c803ba0e4b119f3c18ec..dbeb3728ef2491303325792ce8565273e3bac969 100644 (file)
@@ -85,6 +85,7 @@
 #define ETH_P_CONTROL  0x0016          /* Card specific control frames */
 #define ETH_P_IRDA     0x0017          /* Linux-IrDA                   */
 #define ETH_P_ECONET   0x0018          /* Acorn Econet                 */
+#define ETH_P_HDLC     0x0019          /* HDLC frames                  */
 
 /*
  *     This is an Ethernet frame header.
index 0c5c4e1f803a92b15f69e56f95eb83785dabc820..cc9e0d7186d0bbef612b44012a56738ab9cf8566 100644 (file)
@@ -81,6 +81,8 @@
 #define SIOCGMIIREG    0x8948          /* Read MII PHY register.       */
 #define SIOCSMIIREG    0x8949          /* Write MII PHY register.      */
 
+#define SIOCDEVICE     0x894A          /* get/set netdev parameters    */
+
 /* ARP cache control calls. */
                    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
 #define SIOCDARP       0x8953          /* delete ARP table entry       */
index 3afa0c27a4a1b10057b3938ef78c52cb0e72a827..8cfc4d14aad782910de7b740f46aed72e5e8c2ba 100644 (file)
@@ -2110,7 +2110,8 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd)
                            cmd == SIOCETHTOOL ||
                            cmd == SIOCGMIIPHY ||
                            cmd == SIOCGMIIREG ||
-                           cmd == SIOCSMIIREG) {
+                           cmd == SIOCSMIIREG ||
+                           cmd == SIOCDEVICE) {
                                if (dev->do_ioctl) {
                                        if (!netif_device_present(dev))
                                                return -ENODEV;
@@ -2276,8 +2277,9 @@ int dev_ioctl(unsigned int cmd, void *arg)
                 */     
                 
                default:
-                       if (cmd >= SIOCDEVPRIVATE &&
-                           cmd <= SIOCDEVPRIVATE + 15) {
+                       if (cmd == SIOCDEVICE ||
+                           (cmd >= SIOCDEVPRIVATE &&
+                            cmd <= SIOCDEVPRIVATE + 15)) {
                                dev_load(ifr.ifr_name);
                                dev_probe_lock();
                                rtnl_lock();