--- linux-2.4.20/Documentation/Configure.help	2002-11-29 00:53:08.000000000 +0100
+++ linux/Documentation/Configure.help	2003-07-31 16:25:18.000000000 +0200
@@ -10493,6 +10493,15 @@
 
   If unsure, say N here.
 
+Raw HDLC Ethernet device support
+CONFIG_HDLC_RAW_ETH
+  Say Y to this option if you want generic HDLC driver to support
+  raw HDLC Ethernet device emulation over WAN (Wide Area Network)
+  connections.
+  You will need it for Ethernet over HDLC bridges.
+
+  If unsure, say N here.
+
 Cisco HDLC support
 CONFIG_HDLC_CISCO
   Say Y to this option if you want generic HDLC driver to support
@@ -10507,13 +10516,6 @@
 
   If unsure, say N here.
 
-Frame-Relay bridging support
-CONFIG_HDLC_FR_BRIDGE
-  Say Y to this option if you want generic HDLC driver to support
-  bridging LAN frames over Frame-Relay links.
-
-  If unsure, say N here.
-
 Synchronous Point-to-Point Protocol (PPP) support
 CONFIG_HDLC_PPP
   Say Y to this option if you want generic HDLC driver to support
@@ -10556,6 +10558,55 @@
 
   If unsure, say N here.
 
+CONFIG_HDLC_DEBUG_PKT
+  This option is for developers only - do NOT use on production
+  systems.
+
+CONFIG_HDLC_DEBUG_HARD_HEADER
+  This option is for developers only - do NOT use on production
+  systems.
+
+CONFIG_HDLC_DEBUG_ECN
+  This option is for developers only - do NOT use on production
+  systems.
+
+CONFIG_HDLC_DEBUG_RINGS
+  If you answer Y here you will be able to get a diagnostic dump of
+  port's TX and RX packet rings, using "sethdlc hdlcX private"
+  command. It does not affect normal operations.
+
+  If unsure, say Y here.
+
+CONFIG_PC300TOO
+  This is alternative driver for PC300 RSV/X21 PCI cards made by
+  Cyclades, Inc. If you have such a card, say Y or M here and see
+  <http://hq.pm.waw.pl/pub/hdlc/>
+
+  If you want to compile the 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 pc300too.o.
+
+  If unsure, say N here.
+
+CONFIG_PC300TOO_CRYSTAL_CLOCK
+  This option changes the clock source used by the card circuitry.
+  Lower clock frequency means lower maximum transmission rate.
+
+  If unsure, say N here.
+
+CONFIG_TAHOE9XX
+  This driver is for Tahoe 931/932/971/972 cards
+  If you have such a card, say Y or M here and see
+  <http://www.tahoe.pl/>
+
+  If you want to compile the 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 tahoe9xx.o.
+
+  If unsure, say N here.
+
 Ethernet (10 or 100Mbit)
 CONFIG_NET_ETHERNET
   Ethernet (also called IEEE 802.3 or ISO 8802-2) is the most common
--- linux-2.4.20/Documentation/networking/generic-hdlc.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux/Documentation/networking/generic-hdlc.txt	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,129 @@
+Generic HDLC layer
+Krzysztof Halasa <khc@pm.waw.pl>
+January, 2003
+
+
+Generic HDLC layer currently supports:
+- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP).
+  Normal (routed) and Ethernet-bridged (Ethernet device emulation)
+  interfaces can share a single PVC.
+- raw HDLC - either IP (IPv4) interface or Ethernet device emulation.
+- Cisco HDLC,
+- PPP (uses syncppp.c),
+- X.25 (uses X.25 routines).
+
+There are hardware drivers for the following cards:
+- C101 by Moxa Technologies Co., Ltd.
+- RISCom/N2 by SDL Communications Inc.
+- and others, some not in the official kernel.
+
+Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
+with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
+
+
+Make sure the hdlc.o and the hardware driver are loaded. It should
+create a number of "hdlc" (hdlc0 etc) network devices, one for each
+WAN port. You'll need the "sethdlc" utility, get it from:
+	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
+
+* hdlc-eth - Ethernet device emulation using HDLC. Parity and encoding
+  as above.
+
+* 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
+
+Frame-Relay only:
+* create n | delete n - adds / deletes PVC interface with DLCI #n.
+  Newly created interface will be named pvc0, pvc1 etc.
+
+* create ether n | delete ether n - adds a device for Ethernet-bridged
+  frames. The device will be named pvceth0, pvceth1 etc.
+
+
+
+
+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 (in kernel logs):
+
+	sethdlc hdlc0 private
+
+The hardware driver has 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/
--- linux-2.4.20/drivers/net/wan/Config.in	2002-11-29 00:53:14.000000000 +0100
+++ linux/drivers/net/wan/Config.in	2003-07-14 17:04:24.000000000 +0200
@@ -66,8 +66,14 @@
 
    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 '    Raw HDLC Ethernet device support' CONFIG_HDLC_RAW_ETH
+      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
@@ -76,6 +82,15 @@
       fi
       dep_tristate '    SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC
       dep_tristate '    Moxa C101 support' CONFIG_C101 $CONFIG_HDLC
+      dep_tristate '    Cyclades PC300 RSV/X21 alternative support' CONFIG_PC300TOO $CONFIG_HDLC
+      if [ "$CONFIG_PC300TOO" != "n" ]; then
+	 bool '      Use 24.576 MHz crystal clock instead of 33 MHz PCI clock' CONFIG_PC300TOO_CRYSTAL_CLOCK
+      fi
+      dep_tristate '    Tahoe 9xx support' CONFIG_TAHOE9XX $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
--- linux-2.4.20/drivers/net/wan/Makefile	2002-08-03 02:39:44.000000000 +0200
+++ linux/drivers/net/wan/Makefile	2003-07-22 00:30:31.000000000 +0200
@@ -9,7 +9,7 @@
 
 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,14 @@
 cyclomx-objs = cycx_main.o $(cyclomx-y)  
 cyclomx-$(CONFIG_CYCLOMX_X25)	+= cycx_x25.o
 
+hdlc-y				:= hdlc_generic.o
+hdlc-$(CONFIG_HDLC_RAW)		+= hdlc_raw.o
+hdlc-$(CONFIG_HDLC_RAW_ETH)	+= hdlc_raw_eth.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
+hdlc-objs			:= $(hdlc-y)
 
 obj-$(CONFIG_HOSTESS_SV11)	+= z85230.o	syncppp.o	hostess_sv11.o
 obj-$(CONFIG_SEALEVEL_4021)	+= z85230.o	syncppp.o	sealevel.o
@@ -64,12 +72,19 @@
 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
+obj-$(CONFIG_PC300TOO)		+= pc300too.o
+obj-$(CONFIG_TAHOE9XX)		+= tahoe9xx.o
 
 include $(TOPDIR)/Rules.make
 
+hdlc.o: $(hdlc-objs)
+	$(LD) -r -o $@ $(hdlc-objs)
+
 wanpipe.o: $(wanpipe-objs)
 	$(LD) -r -o $@ $(wanpipe-objs)
 
--- linux-2.4.20/drivers/net/wan/c101.c	2001-09-14 01:04:43.000000000 +0200
+++ linux/drivers/net/wan/c101.c	2003-04-18 13:19:50.000000000 +0200
@@ -1,12 +1,11 @@
 /*
  * Moxa C101 synchronous serial card driver for Linux
  *
- * Copyright (C) 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000-2003 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  *
  * For information see http://hq.pm.waw.pl/hdlc/
  *
@@ -15,6 +14,7 @@
  *    Moxa C101 User's Manual
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -29,10 +29,8 @@
 
 #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.14";
 static const char* devname = "C101";
 
 #define C101_PAGE 0x1D00
@@ -42,6 +40,10 @@
 #define C101_MAPPED_RAM_SIZE 0x4000
 
 #define RAM_SIZE (256 * 1024)
+#define TX_RING_BUFFERS 10
+#define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) /		\
+			 (sizeof(pkt_desc) + HDLC_MAX_MRU) - TX_RING_BUFFERS)
+
 #define CLOCK_BASE 9830400	/* 9.8304 MHz */
 #define PAGE0_ALWAYS_MAPPED
 
@@ -51,32 +53,40 @@
 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 */
+	sync_serial_settings settings;
+	int rxpart;		/* partial frame received, next frame invalid*/
+	unsigned short encoding;
+	unsigned short parity;
+	u16 rx_ring_buffers;	/* number of buffers in a ring */
+	u16 tx_ring_buffers;
 	u16 buff_offset;	/* offset of first buffer of first channel */
+	u16 rxin;		/* rx ring buffer 'in' pointer */
+	u16 txin;		/* tx ring buffer 'in' and 'last' pointers */
+	u16 txlast;
 	u8 rxs, txs, tmc;	/* SCA registers */
 	u8 irq;			/* IRQ (3-15) */
-	u8 ring_buffers;	/* number of buffers in a ring */
 	u8 page;
 
-	u8 rxin;		/* rx ring buffer 'in' pointer */
-	u8 txin;		/* tx ring buffer 'in' and 'last' pointers */
-	u8 txlast;
-	u8 rxpart;		/* partial frame received, next frame invalid*/
-
 	struct card_s *next_card;
 }card_t;
 
 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))
 #define sca_inw(reg, card)	   readw((card)->win0base + C101_SCA + (reg))
-#define sca_outw(value, reg, card) writew(value, (card)->win0base + C101_SCA + (reg))
+
+/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */
+#define sca_outw(value, reg, card) do { \
+	writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \
+	writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg+1));\
+} while(0)
 
 #define port_to_card(port)	   (port)
 #define log_node(port)		   (0)
@@ -105,18 +115,13 @@
 #include "hd6457x.c"
 
 
-static int c101_set_clock(port_t *port, int value)
+static void 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) {
-	case CLOCK_EXT:
-		rxs |= CLK_LINE_RX; /* RXC input */
-		txs |= CLK_LINE_TX; /* TXC input */
-		break;
-
+	switch(port->settings.clock_type) {
 	case CLOCK_INT:
 		rxs |= CLK_BRG_RX; /* TX clock */
 		txs |= CLK_RXCLK_TX; /* BRG output */
@@ -132,90 +137,108 @@
 		txs |= CLK_RXCLK_TX; /* RX clock */
 		break;
 
-	default:
-		return -EINVAL;
+	default:	/* EXTernal clock */
+		rxs |= CLK_LINE_RX; /* RXC input */
+		txs |= CLK_LINE_TX; /* TXC input */
 	}
 
 	port->rxs = rxs;
 	port->txs = txs;
 	sca_out(rxs, msci + RXS, port);
 	sca_out(txs, msci + TXS, port);
-	port->clkmode = value;
-	return 0;
+	sca_set_port(port);
 }
 
 
-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);
+	c101_set_iface(port);
 	return 0;
 }
 
 
-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;
+	const size_t size = sizeof(sync_serial_settings);
+	sync_serial_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.sync;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
 
-	if(!capable(CAP_NET_ADMIN))
-		return -EPERM;
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+	if (cmd == SIOCDEVPRIVATE) {
+		sca_dump_rings(hdlc);
+		return 0;
+	}
+#endif
+	if (cmd != SIOCWANDEV)
+		return hdlc_ioctl(dev, ifr, cmd);
 
-	switch(cmd) {
-	case HDLCSCLOCK:
-		result = c101_set_clock(port, value);
-	case HDLCGCLOCK:
-		value = port->clkmode;
-		break;
+	switch(ifr->ifr_settings.type) {
+	case IF_GET_IFACE:
+		ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(line, &port->settings, size))
+			return -EFAULT;
+		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;
+
+		if (copy_from_user(&new_line, line, size))
+			return -EFAULT;
+
+		if (new_line.clock_type != CLOCK_EXT &&
+		    new_line.clock_type != CLOCK_TXFROMRX &&
+		    new_line.clock_type != CLOCK_INT &&
+		    new_line.clock_type != CLOCK_TXINT)
+		return -EINVAL;	/* No such clock setting */
 
-	case HDLCSLINE:
-		result = sca_set_loopback(port, value);
-	case HDLCGLINE:
-		value = port->line;
-		break;
+		if (new_line.loopback != 0 && new_line.loopback != 1)
+			return -EINVAL;
 
-#ifdef DEBUG_RINGS
-	case HDLCRUN:
-		sca_dump_rings(hdlc);
+		memcpy(&port->settings, &new_line, size); /* Update settings */
+		c101_set_iface(port);
 		return 0;
-#endif /* DEBUG_RINGS */
 
 	default:
-		return -EINVAL;
+		return hdlc_ioctl(dev, ifr, cmd);
 	}
-
-	ifr->ifr_ifru.ifru_ivalue = value;
-	return result;
 }
 
 
 
 static void c101_destroy_card(card_t *card)
 {
+	readb(card->win0base + C101_PAGE); /* Resets SCA? */
+
 	if (card->irq)
 		free_irq(card->irq, card);
 
@@ -229,8 +252,9 @@
 
 
 
-static int c101_run(unsigned long irq, unsigned long winbase)
+static int __init c101_run(unsigned long irq, unsigned long winbase)
 {
+	struct net_device *dev;
 	card_t *card;
 	int result;
 
@@ -271,9 +295,10 @@
 		return -EBUSY;
 	}
 
-	/* 2 rings required for 1 port */
-	card->ring_buffers = (RAM_SIZE -C101_WINDOW_SIZE) / (2 * HDLC_MAX_MRU);
-	printk(KERN_DEBUG "c101: using %u packets rings\n",card->ring_buffers);
+	card->tx_ring_buffers = TX_RING_BUFFERS;
+	card->rx_ring_buffers = RX_RING_BUFFERS;
+	printk(KERN_DEBUG "c101: using %u TX + %u RX packets rings\n",
+	       card->tx_ring_buffers, card->rx_ring_buffers);
 
 	card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */
 
@@ -284,15 +309,19 @@
 
 	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) {
@@ -334,7 +363,7 @@
 			c101_run(irq, ram);
 
 		if (*hw == '\x0')
-			return 0;
+			return first_card ? 0 : -ENOSYS;
 	}while(*hw++ == ':');
 
 	printk(KERN_ERR "c101: invalid hardware parameters\n");
@@ -371,6 +400,6 @@
 
 MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
 MODULE_DESCRIPTION("Moxa C101 serial port driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_PARM(hw, "s");		/* hw=irq,ram:irq,... */
 EXPORT_NO_SYMBOLS;
--- linux-2.4.20/drivers/net/wan/hd64570.h	2001-03-07 04:44:36.000000000 +0100
+++ linux/drivers/net/wan/hd64570.h	2003-04-18 13:19:50.000000000 +0200
@@ -152,7 +152,7 @@
 	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 @@
 #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
--- linux-2.4.20/drivers/net/wan/hd64572.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hd64572.h	2003-04-18 13:19:55.000000000 +0200
@@ -0,0 +1,240 @@
+#ifndef __HD64572_H
+#define __HD64572_H
+
+/* SCA-II HD64572 register definitions - all addresses for mode 0
+   and 2 (Intel MPU). For modes 2 and 3, XOR the address with 0x03.
+
+   Source: HD64572 SCA User's Manual
+*/
+
+#define	ILAR	0x00		/* Illegal Access */
+
+/* Wait Controller Registers */
+#define PABR0L	0x20		/* Physical Address Boundary 0 L */
+#define PABR0H	0x21		/* Physical Address Boundary 0 H */
+#define PABR1L	0x22		/* Physical Address Boundary 1 L */
+#define PABR1H	0x23		/* Physical Address Boundary 1 H */
+#define WCRL	0x24		/* Wait Control L */
+#define WCRM	0x25		/* Wait Control M */
+#define WCRH	0x26		/* Wait Control H */
+
+/* Interrupt Registers */
+#define IVR	0x60		/* Interrupt Vector */
+#define IMVR	0x64		/* Interrupt Modified Vector */
+#define ITCR	0x68		/* Interrupt Control */
+#define ISR0	0x6c		/* Interrupt Status 0 */
+#define ISR1	0x70		/* Interrupt Status 1 */
+#define IER0	0x74		/* Interrupt Enable 0 */
+#define IER1	0x78		/* Interrupt Enable 1 */
+
+/* MSCI Channel Registers */
+#define MSCI0_OFFSET 0x00
+#define MSCI1_OFFSET 0x80
+
+#define MD0	0x138		/* Mode reg 0 */
+#define MD1	0x139		/* Mode reg 1 */
+#define MD2	0x13A		/* Mode reg 2 */
+#define MD3	0x13B		/* Mode reg 3 */
+#define CTL	0x130		/* Control reg */
+#define RXS	0x13C		/* RX clock source */
+#define TXS	0x13D		/* TX clock source */
+#define EXS	0x13E		/* External clock input selection */
+#define TMCT	0x144		/* Time constant (Tx) */
+#define TMCR	0x145		/* Time constant (Rx) */
+#define CMD	0x128		/* Command reg */
+#define ST0	0x118		/* Status reg 0 */
+#define ST1	0x119		/* Status reg 1 */
+#define ST2	0x11A		/* Status reg 2 */
+#define ST3	0x11B		/* Status reg 3 */
+#define ST4	0x11C		/* Status reg 4 */
+#define FST	0x11D		/* frame Status reg  */
+#define IE0	0x120		/* Interrupt enable reg 0 */
+#define IE1	0x121		/* Interrupt enable reg 1 */
+#define IE2	0x122		/* Interrupt enable reg 2 */
+#define IE4	0x124		/* Interrupt enable reg 4 */
+#define FIE	0x125		/* Frame Interrupt enable reg  */
+#define SA0	0x140		/* Syn Address reg 0 */
+#define SA1	0x141		/* Syn Address reg 1 */
+#define IDL	0x142		/* Idle register */
+#define TRBL	0x100		/* TX/RX buffer reg L */ 
+#define TRBK	0x101		/* TX/RX buffer reg K */ 
+#define TRBJ	0x102		/* TX/RX buffer reg J */ 
+#define TRBH	0x103		/* TX/RX buffer reg H */ 
+#define TRC0	0x148		/* TX Ready control reg 0 */ 
+#define TRC1	0x149		/* TX Ready control reg 1 */ 
+#define RRC	0x14a		/* RX Ready control reg */ 
+#define CST0	0x108		/* Current Status 0 */ 
+#define CST1	0x109		/* Current Status 1 */ 
+#define CST2	0x10A		/* Current Status 2 */ 
+#define CST3	0x10B		/* Current Status 3 */ 
+#define GPO	0x131		/* General Purpose Output Pin Ctl Reg */
+#define TFS	0x14B		/* Tx Start Threshold Ctl Reg */
+#define TFN	0x143		/* Inter-transmit-frame Time Fill Ctl Reg */
+#define TBN	0x110		/* Tx Buffer Number Reg */
+#define RBN	0x111		/* Rx Buffer Number Reg */
+#define TNR0	0x150		/* Tx DMA Request Ctl Reg 0 */
+#define TNR1	0x151		/* Tx DMA Request Ctl Reg 1 */
+#define TCR	0x152		/* Tx DMA Critical Request Reg */
+#define RNR	0x154		/* Rx DMA Request Ctl Reg */
+#define RCR	0x156		/* Rx DMA Critical Request Reg */
+
+/* Timer Registers */
+#define TIMER0RX_OFFSET 0x00
+#define TIMER0TX_OFFSET 0x10
+#define TIMER1RX_OFFSET 0x20
+#define TIMER1TX_OFFSET 0x30
+
+#define TCNTL	0x200		/* Timer Upcounter L */
+#define TCNTH	0x201		/* Timer Upcounter H */
+#define TCONRL	0x204		/* Timer Constant L */
+#define TCONRH	0x205		/* Timer Constant H */
+#define TCSR	0x206		/* Timer Control/Status */
+#define TEPR	0x207		/* Timer Expand Prescale */
+
+/* DMA registers */
+#define PCR		0x40	/* DMA priority control reg */
+#define DRR		0x44	/* DMA reset reg */
+#define DMER		0x07	/* DMA Master Enable reg */
+#define BTCR		0x08	/* Burst Tx Ctl Reg */
+#define BOLR		0x0c	/* Back-off Length Reg */
+#define DSR_RX(node) (0x48 + node*2) /* DMA Status Reg (Rx) */
+#define DSR_TX(node) (0x49 + node*2) /* DMA Status Reg (Tx) */
+#define DIR_RX(node) (0x4c + node*2) /* DMA IRQ Enable Reg (Rx) */
+#define DIR_TX(node) (0x4d + node*2) /* DMA IRQ Enable Reg (Tx) */
+#define FCT_RX(node) (0x50 + node*2) /* Frame End IRQ Counter (Rx) */
+#define FCT_TX(node) (0x51 + node*2) /* Frame End IRQ Counter (Tx) */
+#define DMR_RX(node) (0x54 + node*2) /* DMA Mode Reg (Rx) */
+#define DMR_TX(node) (0x55 + node*2) /* DMA Mode Reg (Tx) */
+#define DCR_RX(node) (0x58 + node*2) /* DMA Command Reg (Rx) */
+#define DCR_TX(node) (0x59 + node*2) /* DMA Command Reg (Tx) */
+
+/* DMA Channel Registers */
+#define DMAC0RX_OFFSET 0x00
+#define DMAC0TX_OFFSET 0x20
+#define DMAC1RX_OFFSET 0x40
+#define DMAC1TX_OFFSET 0x60
+
+#define DARL	0x80		/* Dest Addr L (single-block, RX only) */
+#define DARH	0x81		/* Dest Addr H (single-block, RX only) */
+#define DARB	0x82		/* Dest Addr B (single-block, RX only) */
+#define DARBH	0x83		/* Dest Addr BH (single-block, RX only) */
+#define SARL	0x80		/* Source Addr L (single-block, TX only) */
+#define SARH	0x81		/* Source Addr H (single-block, TX only) */
+#define SARB	0x82		/* Source Addr B (single-block, TX only) */
+#define SARBH	0x83		/* Source Addr BH (single-block, TX only) */
+#define BARL	0x80		/* Buffer Addr L (chained-block) */
+#define BARH	0x81		/* Buffer Addr H (chained-block) */
+#define BARB	0x82		/* Buffer Addr B (chained-block) */
+#define BARBH	0x83		/* Buffer Addr BH (chained-block) */
+#define CDAL	0x84		/* Current Descriptor Addr L */
+#define CDAH	0x85		/* Current Descriptor Addr H */
+#define CDAB	0x86		/* Current Descriptor Addr B */
+#define CDABH	0x87		/* Current Descriptor Addr BH */
+#define EDAL	0x88		/* Error Descriptor Addr L */
+#define EDAH	0x89		/* Error Descriptor Addr H */
+#define EDAB	0x8a		/* Error Descriptor Addr B */
+#define EDABH	0x8b		/* Error Descriptor Addr BH */
+#define BFLL	0x90		/* RX Buffer Length L (only RX) */
+#define BFLH	0x91		/* RX Buffer Length H (only RX) */
+#define BCRL	0x8c		/* Byte Count L */
+#define BCRH	0x8d		/* Byte Count H */
+
+/* Block Descriptor Structure */
+typedef struct {
+	u32 cp;			/* pointer to next block descriptor */
+	u32 bp;			/* buffer pointer */
+	u16 len;		/* data length */
+	u8 stat;		/* status */
+	u8 unused;		/* pads to 4-byte boundary */
+}pkt_desc;
+
+
+/* Packet Descriptor Status bits */
+
+#define ST_TX_EOM     0x80	/* End of frame */
+#define ST_TX_UNDRRUN 0x08
+#define ST_TX_OWNRSHP 0x02
+#define ST_TX_EOT     0x01	/* End of transmition */
+
+#define ST_RX_EOM     0x80	/* End of frame */
+#define ST_RX_SHORT   0x40	/* Short frame */
+#define ST_RX_ABORT   0x20	/* Abort */
+#define ST_RX_RESBIT  0x10	/* Residual bit */
+#define ST_RX_OVERRUN 0x08	/* Overrun */
+#define ST_RX_CRC     0x04	/* CRC */
+#define ST_RX_OWNRSHP 0x02
+                   
+#define ST_ERROR_MASK 0x7C
+
+#define MD0_HDLC        0x80	/* Bit-sync HDLC mode */
+
+#define MD0_CRC_NONE	0x00
+#define MD0_CRC_16_0	0x04
+#define MD0_CRC_16	0x05
+#define MD0_CRC_ITU32	0x06
+#define MD0_CRC_ITU	0x07
+
+#define MD2_NRZ		0x00
+#define MD2_NRZI	0x20
+#define MD2_NRZI_IEEE	0x40
+#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
+#define CTL_NODTR	0x02
+#define CTL_IDLE	0x10
+#define CTL_UDRNC	0x20
+#define CTL_URSKP	0x40
+#define CTL_URCT	0x80
+
+#define ST1_UDRN      	0x80	/* MSCI TX underrun */
+
+#define IE0_TXINT     	0x00000080 /* TX INT MSCI interrupt enable */
+#define IE0_UDRN      	0x00008000 /* TX underrun MSCI interrupt enable */
+
+#define CLK_BRG_MASK	0x0F
+#define CLK_PIN_OUT	0x80
+#define CLK_LINE    	0x00	/* clock line input */
+#define CLK_BRG     	0x40	/* internal baud rate generator */
+#define CLK_TX_RXCLK	0x60	/* TX clock from RX clock */
+
+#define EXS_TES1	0x20
+#define EXS_RES1	0x02
+
+#define CMD_RESET	0x21
+#define CMD_TX_ENABLE	0x02
+#define CMD_RX_ENABLE	0x12
+
+#define DSR_DWE		0x01
+#define DSR_DE		0x02
+#define DSR_REF		0x04
+#define DSR_UDRF	0x04
+#define DSR_COA		0x08
+#define DSR_COF		0x10
+#define DSR_BOF		0x20
+#define DSR_EOM		0x40
+#define DSR_EOT		0x80
+
+#define DIR_REFE	0x04
+#define DIR_UDRFE	0x04
+#define DIR_COAE	0x08
+#define DIR_COFE	0x10
+#define DIR_BOFE	0x20
+#define DIR_EOME	0x40
+#define DIR_EOTE	0x80
+
+#define DMR_CNTE	0x02
+#define DMR_NF		0x04
+#define DMR_SEOME	0x08
+#define DMR_TMOD	0x10
+
+#define DMER_DME        0x80	/* DMA Master Enable */
+
+#define DCR_ABORT	0x01
+#define DCR_CLEAR_EOF	0x02
+
+#define PCR_COTE	0x80
+
+#endif /* (__HD64572_H) */
--- linux-2.4.20/drivers/net/wan/hd6457x.c	2001-03-07 04:44:36.000000000 +0100
+++ linux/drivers/net/wan/hd6457x.c	2003-07-31 16:31:43.000000000 +0200
@@ -1,16 +1,29 @@
 /*
  * Hitachi SCA HD64570 and HD64572 common driver for Linux
  *
- * Copyright (C) 1998-2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2003 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  *
  * Sources of information:
  *    Hitachi HD64570 SCA User's Manual
  *    Hitachi HD64572 SCA-II User's Manual
+ *
+ * We use the following SCA memory map:
+ *
+ * Packet buffer descriptor rings - starting from winbase or win0base:
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used)
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used)
+ *
+ * Packet data buffers - starting from winbase + buff_offset:
+ * rx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 RX buffers
+ * tx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 TX buffers
+ * rx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 RX buffers (if used)
+ * tx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 TX buffers (if used)
  */
 
 #include <linux/module.h>
@@ -42,14 +55,6 @@
 #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
-
 #define get_msci(port)	  (phy_node(port) ?   MSCI1_OFFSET :   MSCI0_OFFSET)
 #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
 #define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_OFFSET)
@@ -63,11 +68,19 @@
 #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 +89,8 @@
 	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 +99,17 @@
 	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;
+}
 
 
 
@@ -119,24 +127,35 @@
 
 
 
-static inline u8 next_desc(port_t *port, u8 desc)
+static inline u16 next_desc(port_t *port, u16 desc, int transmit)
+{
+	return (desc + 1) % (transmit ? port_to_card(port)->tx_ring_buffers
+			     : port_to_card(port)->rx_ring_buffers);
+}
+
+
+
+static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit)
 {
-	return (desc + 1) % port_to_card(port)->ring_buffers;
+	u16 rx_buffs = port_to_card(port)->rx_ring_buffers;
+	u16 tx_buffs = port_to_card(port)->tx_ring_buffers;
+
+	desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc.
+	return log_node(port) * (rx_buffs + tx_buffs) +
+		transmit * rx_buffs + desc;
 }
 
 
 
-static inline u16 desc_offset(port_t *port, u8 desc, u8 transmit)
+static inline u16 desc_offset(port_t *port, u16 desc, int transmit)
 {
 	/* Descriptor offset always fits in 16 bytes */
-	u8 buffs = port_to_card(port)->ring_buffers;
-	return ((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) *
-		sizeof(pkt_desc);
+	return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
 }
 
 
 
-static inline pkt_desc* desc_address(port_t *port, u8 desc, u8 transmit)
+static inline pkt_desc* desc_address(port_t *port, u16 desc, int transmit)
 {
 #ifdef PAGE0_ALWAYS_MAPPED
 	return (pkt_desc*)(win0base(port_to_card(port))
@@ -149,12 +168,10 @@
 
 
 
-static inline u32 buffer_offset(port_t *port, u8 desc, u8 transmit)
+static inline u32 buffer_offset(port_t *port, u16 desc, int transmit)
 {
-	u8 buffs = port_to_card(port)->ring_buffers;
 	return port_to_card(port)->buff_offset +
-		((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) *
-		(u32)HDLC_MAX_MRU;
+		desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
 }
 
 
@@ -162,8 +179,7 @@
 static void sca_init_sync_port(port_t *port)
 {
 	card_t *card = port_to_card(port);
-	u8 transmit, i;
-	u16 dmac, buffs = card->ring_buffers;
+	int transmit, i;
 
 	port->rxin = 0;
 	port->txin = 0;
@@ -174,6 +190,10 @@
 #endif
 
 	for (transmit = 0; transmit < 2; transmit++) {
+		u16 dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
+		u16 buffs = transmit ? card->tx_ring_buffers
+			: card->rx_ring_buffers;
+
 		for (i = 0; i < buffs; i++) {
 			pkt_desc* desc = desc_address(port, i, transmit);
 			u16 chain_off = desc_offset(port, i + 1, transmit);
@@ -185,7 +205,6 @@
 			writeb(0, &desc->stat);
 		}
 
-		dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
 		/* DMA disable - to halt state */
 		sca_out(0, transmit ? DSR_TX(phy_node(port)) :
 			DSR_RX(phy_node(port)), card);
@@ -236,7 +255,7 @@
 	card_t* card = port_to_card(port);
 	u8 stat = sca_in(msci + ST1, card); /* read MSCI ST1 status */
 
-	/* printk(KERN_DEBUG "MSCI INT: ST1=%02X ILAR=%02X\n",
+	/*printk(KERN_DEBUG "MSCI INT: ST1=%02X ILAR=%02X\n",
 	   stat, sca_in(ILAR, card)); */
 
 	/* Reset MSCI TX underrun status bit */
@@ -250,8 +269,7 @@
 
 
 
-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, u16 rxin)
 {
 	struct sk_buff *skb;
 	u16 len;
@@ -289,13 +307,17 @@
 	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->dev->last_rx = jiffies;
+	skb->protocol = hdlc_type_trans(skb, hdlc_to_dev(&port->hdlc));
+	netif_rx(skb);
 }
 
 
@@ -303,6 +325,7 @@
 /* Receive DMA interrupt service */
 static inline void sca_rx_intr(port_t *port)
 {
+	int	j=1000;
 	u16 dmac = get_dmac_rx(port);
 	card_t *card = port_to_card(port);
 	u8 stat = sca_in(DSR_RX(phy_node(port)), card); /* read DMA Status */
@@ -315,19 +338,15 @@
 	if (stat & DSR_BOF)
 		stats->rx_over_errors++; /* Dropped one or more frames */
 
-	while (1) {
+	while (j) {
 		u32 desc_off = desc_offset(port, port->rxin, 0);
 		pkt_desc *desc;
 		u32 cda = sca_ina(dmac + CDAL, card);
 
-		if (cda == desc_off)
+		j--;
+		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))
@@ -346,8 +365,9 @@
 
 		/* Set new error descriptor address */
 		sca_outa(desc_off, dmac + EDAL, card);
-		port->rxin = next_desc(port, port->rxin);
+		port->rxin = next_desc(port, port->rxin, 0);
 	}
+	if (!j) printk(KERN_INFO "sca_rx_intr: loop\n");
 
 	/* make sure RX DMA is enabled */
 	sca_out(DSR_DE, DSR_RX(phy_node(port)), card);
@@ -361,6 +381,7 @@
 	u16 dmac = get_dmac_tx(port);
 	card_t* card = port_to_card(port);
 	u8 stat;
+	int	j=1000;
 
 	spin_lock(&port->lock);
 
@@ -370,24 +391,22 @@
 	sca_out((stat & (DSR_EOT | DSR_EOM | DSR_BOF | DSR_COF)) | DSR_DWE,
 		DSR_TX(phy_node(port)), card);
 
-	while (1) {
-		u32 desc_off = desc_offset(port, port->txlast, 1);
+	while (j) {
 		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);
+		j--;
+		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;
+		port->txlast = next_desc(port, port->txlast, 1);
 	}
+	if (!j) printk(KERN_INFO "sca_tx_intr: loop\n");
 
 	netif_wake_queue(hdlc_to_dev(&port->hdlc));
 	spin_unlock(&port->lock);
@@ -398,8 +417,7 @@
 static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
 {
 	card_t *card = dev_id;
-	int boguscnt = INTR_WORK;
-	int i;
+	int i, j=1000;
 	u8 stat;
 
 #ifndef ALL_PAGES_ALWAYS_MAPPED
@@ -407,6 +425,11 @@
 #endif
 
 	while((stat = sca_intr_status(card)) != 0) {
+		j--;
+		if (!j) {
+			printk(KERN_INFO "sca_intr: loop\n");
+			break;
+		}
 		for (i = 0; i < 2; i++) {
 			port_t *port = get_port(card, i);
 			if (port) {
@@ -419,17 +442,9 @@
 				if (stat & SCA_INTR_DMAC_TX(i))
 					sca_tx_intr(port);
 			}
-
-			if (--boguscnt < 0) {
-				printk(KERN_ERR "%s: too much work at "
-				       "interrupt\n",
-				       hdlc_to_name(&port->hdlc));
-				goto exit;
-			}
 		}
 	}
 
- exit:
 #ifndef ALL_PAGES_ALWAYS_MAPPED
 	openwin(card, page);		/* Restore original page */
 #endif
@@ -437,47 +452,22 @@
 
 
 
-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);
+	u16 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 +477,11 @@
 		} 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,54 +499,91 @@
 	/* 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 */
+	u16 msci = get_msci(port);
+	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 */
 	/* TX with higher priority and possibly with shorter transfers */
+/* 07 10 14 */
 	sca_out(0x07, msci + RRC, card); /* +1=RXRDY/DMA activation condition*/
-	sca_out(0x10, msci + TRC0, card); /* = TXRDY/DMA activation condition*/
+	sca_out(0x12, msci + TRC0, card); /* = TXRDY/DMA activation condition*/
 	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 +600,52 @@
 	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));
+}
+
+
+
+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 DEBUG_RINGS
+#ifdef CONFIG_HDLC_DEBUG_RINGS
 static void sca_dump_rings(hdlc_device *hdlc)
 {
 	port_t *port = hdlc_to_port(hdlc);
@@ -592,14 +660,13 @@
 	openwin(card, 0);
 #endif
 
-	printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u "
-	       "%sactive",
+	printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
 	       sca_ina(get_dmac_rx(port) + CDAL, card),
 	       sca_ina(get_dmac_rx(port) + EDAL, card),
 	       sca_in(DSR_RX(phy_node(port)), card),
 	       port->rxin,
 	       sca_in(DSR_RX(phy_node(port)), card) & DSR_DE?"":"in");
-	for (cnt = 0; cnt<port_to_card(port)->ring_buffers; cnt++)
+	for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++)
 		printk(" %02X",
 		       readb(&(desc_address(port, cnt, 0)->stat)));
 
@@ -611,7 +678,7 @@
 	       port->txlast,
 	       sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in");
 
-	for (cnt = 0; cnt<port_to_card(port)->ring_buffers; cnt++)
+	for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++)
 		printk(" %02X",
 		       readb(&(desc_address(port, cnt, 1)->stat)));
 	printk("\n");
@@ -644,34 +711,14 @@
 	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 +732,7 @@
 	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 +740,7 @@
 		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
@@ -723,7 +770,7 @@
 	writeb(ST_TX_EOM, &desc->stat);
 	dev->trans_start = jiffies;
 
-	port->txin = next_desc(port, port->txin);
+	port->txin = next_desc(port, port->txin, 1);
 	sca_outa(desc_offset(port, port->txin, 1),
 		 get_dmac_tx(port) + EDAL, card);
 
@@ -740,7 +787,50 @@
 }
 
 
-static void sca_init(card_t *card, int wait_states)
+
+#ifdef NEED_DETECT_RAM
+static u32 __devinit sca_detect_ram(card_t *card, u8 *rambase, u32 ramsize)
+{
+	/* Round RAM size to 32 bits, fill from end to start */
+	u32 i = ramsize &= ~3;
+
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+	u32 size = winsize(card);
+
+	openwin(card, (i - 4) / size); /* select last window */
+#endif
+	do {
+		i -= 4;
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+		if ((i + 4) % size == 0)
+			openwin(card, i / size);
+		writel(i ^ 0x12345678, rambase + i % size);
+#else
+		writel(i ^ 0x12345678, rambase + i);
+#endif
+	}while (i > 0);
+
+	for (i = 0; i < ramsize ; i += 4) {
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+		if (i % size == 0)
+			openwin(card, i / size);
+
+		if (readl(rambase + i % size) != (i ^ 0x12345678))
+			break;
+#else
+		if (readl(rambase + i) != (i ^ 0x12345678))
+			break;
+#endif
+	}
+	for (i = 0; i < ramsize ; i += 4) writel(0, rambase + i);
+	
+	return i;
+}
+#endif /* NEED_DETECT_RAM */
+
+
+
+static void __devinit sca_init(card_t *card, int wait_states)
 {
 	sca_out(wait_states, WCRL, card); /* Wait Control */
 	sca_out(wait_states, WCRM, card);
--- linux-2.4.20/drivers/net/wan/hdlc.c	2001-09-14 01:04:43.000000000 +0200
+++ linux/drivers/net/wan/hdlc.c	1970-01-01 01:00:00.000000000 +0100
@@ -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);
--- linux-2.4.20/drivers/net/wan/hdlc_cisco.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hdlc_cisco.c	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,318 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Cisco HDLC support
+ *
+ * Copyright (C) 2000 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.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;
+	/* we will need do_div here if 1000 % HZ != 0 */
+	data->time = htonl(jiffies * (1000 / HZ));
+
+	skb_put(skb, sizeof(cisco_packet));
+	skb->priority = TC_PRIO_CONTROL;
+	skb->dev = hdlc_to_dev(hdlc);
+	skb->nh.raw = skb->data;
+
+	dev_queue_xmit(skb);
+}
+
+
+
+static unsigned short cisco_type_trans(struct sk_buff *skb,
+				       struct net_device *dev)
+{
+	hdlc_header *data = (hdlc_header*)skb->data;
+
+	if (skb->len < sizeof(hdlc_header))
+		return __constant_htons(ETH_P_HDLC);
+
+	if (data->address != CISCO_MULTICAST &&
+	    data->address != CISCO_UNICAST)
+		return __constant_htons(ETH_P_HDLC);
+
+	switch(data->protocol) {
+	case __constant_htons(ETH_P_IP):
+	case __constant_htons(ETH_P_IPX):
+	case __constant_htons(ETH_P_IPV6):
+		skb_pull(skb, sizeof(hdlc_header));
+		return data->protocol;
+	default:
+		return __constant_htons(ETH_P_HDLC);
+	}
+}
+
+
+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 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)
+{
+	cisco_proto *cisco_s = ifr->ifr_settings.ifs_ifsu.cisco;
+	const size_t size = sizeof(cisco_proto);
+	cisco_proto new_settings;
+	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.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_CISCO:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if(dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, cisco_s, size))
+			return -EFAULT;
+
+		if (new_settings.interval < 1 ||
+		    new_settings.timeout < 2)
+			return -EINVAL;
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+		memcpy(&hdlc->state.cisco.settings, &new_settings, size);
+
+		hdlc->open = cisco_open;
+		hdlc->stop = cisco_close;
+		hdlc->netif_rx = cisco_rx;
+		hdlc->type_trans = cisco_type_trans;
+		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;
+}
--- linux-2.4.20/drivers/net/wan/hdlc_fr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hdlc_fr.c	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,1063 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Frame Relay support
+ *
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+
+ Theory of PVC state in DCE mode:
+
+ (exist,new) -> 0,0 when "PVC create" or if "link unreliable"
+         0,x -> 1,1 if "link reliable" when sending FULL STATUS
+         1,1 -> 1,0 if received FULL STATUS ACK
+
+ (active)    -> 0 when "ifconfig PVC down" or "link unreliable" or "PVC create"
+             -> 1 when "PVC up" and (exist,new) = 1,0
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.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/random.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.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 (pvc->dlci == dlci)
+			return pvc;
+		if (pvc->dlci > dlci)
+			return NULL; /* the listed is sorted */
+		pvc = pvc->next;
+	}
+
+	return NULL;
+}
+
+
+__inline__ pvc_device* add_pvc(hdlc_device *hdlc, u16 dlci)
+{
+	pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc;
+
+	while(*pvc_p) {
+		if ((*pvc_p)->dlci == dlci)
+			return *pvc_p;
+		if ((*pvc_p)->dlci > dlci)
+			break;	/* the listed is sorted */
+		pvc_p = &(*pvc_p)->next;
+	}
+
+	pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC);
+	if (!pvc)
+		return NULL;
+
+	memset(pvc, 0, sizeof(pvc_device));
+	pvc->dlci = dlci;
+	pvc->master = hdlc;
+	pvc->next = *pvc_p;	/* Put it in the chain */
+	*pvc_p = pvc;
+	return pvc;
+}
+
+
+__inline__ int pvc_is_used(pvc_device *pvc)
+{
+	return pvc->main != NULL || pvc->ether != NULL;
+}
+
+
+__inline__ void delete_unused_pvcs(hdlc_device *hdlc)
+{
+	pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+
+	while(*pvc_p) {
+		if (!pvc_is_used(*pvc_p)) {
+			pvc_device *pvc = *pvc_p;
+			*pvc_p = pvc->next;
+			kfree(pvc);
+			continue;
+		}
+		pvc_p = &(*pvc_p)->next;
+	}
+}
+
+
+__inline__ struct net_device** get_dev_p(pvc_device *pvc, int type)
+{
+	if (type == ARPHRD_ETHER)
+		return &pvc->ether;
+	else
+		return &pvc->main;
+}
+
+
+__inline__ u16 status_to_dlci(u8 *status, int *active, int *new)
+{
+	*new = (status[2] & 0x08) ? 1 : 0;
+	*active = (status[2] & 0x02) ? 1 : 0;
+
+	return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
+}
+
+
+__inline__ void dlci_to_status(u16 dlci, u8 *status,
+			       int active, int new)
+{
+	status[0] = (dlci>>4) & 0x3F;
+	status[1] = ((dlci<<3) & 0x78) | 0x80;
+	status[2] = 0x80;
+
+	if (new)
+		status[2] |= 0x08;
+	else if (active)
+		status[2] |= 0x02;
+}
+
+
+
+static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
+{
+	u16 head_len;
+	struct sk_buff *skb = *skb_p;
+
+	switch(skb->protocol) {
+	case __constant_ntohs(ETH_P_IP):
+		head_len = 4;
+		skb_push(skb, head_len);
+		skb->data[3] = NLPID_IP;
+		break;
+
+	case __constant_ntohs(ETH_P_IPV6):
+		head_len = 4;
+		skb_push(skb, head_len);
+		skb->data[3] = NLPID_IPV6;
+		break;
+
+	case __constant_ntohs(LMI_PROTO):
+		head_len = 4;
+		skb_push(skb, head_len);
+		skb->data[3] = LMI_PROTO;
+		break;
+
+	case __constant_ntohs(ETH_P_802_3):
+		head_len = 10;
+		if (skb_headroom(skb) < head_len) {
+			struct sk_buff *skb2 = skb_realloc_headroom(skb,
+								    head_len);
+			if (!skb2)
+				return -ENOBUFS;
+			dev_kfree_skb(skb);
+			skb = *skb_p = skb2;
+		}
+		skb_push(skb, head_len);
+		skb->data[3] = FR_PAD;
+		skb->data[4] = NLPID_SNAP;
+		skb->data[5] = FR_PAD;
+		skb->data[6] = 0x80;
+		skb->data[7] = 0xC2;
+		skb->data[8] = 0x00;
+		skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */
+		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;
+		*(u16*)(skb->data + 8) = skb->protocol;
+	}
+
+	dlci_to_q922(skb->data, dlci);
+	skb->data[2] = FR_UI;
+	return 0;
+}
+
+
+
+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->open_count++ == 0) {
+		if (pvc->master->state.fr.settings.lmi == LMI_NONE)
+			pvc->state.active = 1;
+
+		pvc->master->state.fr.dce_changed = 1;
+	}
+	return 0;
+}
+
+
+
+static int pvc_close(struct net_device *dev)
+{
+	pvc_device *pvc = dev_to_pvc(dev);
+
+	if (--pvc->open_count == 0) {
+		if (pvc->master->state.fr.settings.lmi == LMI_NONE)
+			pvc->state.active = 0;
+
+		if (pvc->master->state.fr.settings.dce) {
+			pvc->master->state.fr.dce_changed = 1;
+			pvc->state.active = 0;
+		}
+	}
+	return 0;
+}
+
+
+
+int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	pvc_device *pvc = dev_to_pvc(dev);
+	fr_proto_pvc_info info;
+
+	if (ifr->ifr_settings.type == IF_GET_PROTO) {
+		if (dev->type == ARPHRD_ETHER)
+			ifr->ifr_settings.type = IF_PROTO_FR_ETH_PVC;
+		else
+			ifr->ifr_settings.type = IF_PROTO_FR_PVC;
+
+		if (ifr->ifr_settings.size < sizeof(info)) {
+			/* data size wanted */
+			ifr->ifr_settings.size = sizeof(info);
+			return -ENOBUFS;
+		}
+
+		info.dlci = pvc->dlci;
+		memcpy(info.master, hdlc_to_name(pvc->master), IFNAMSIZ);
+		if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info,
+				 &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+
+__inline__ struct net_device_stats *pvc_get_stats(struct net_device *dev)
+{
+	return (struct net_device_stats *)
+		((char *)dev + sizeof(struct net_device));
+}
+
+
+
+static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	pvc_device *pvc = dev_to_pvc(dev);
+	struct net_device_stats *stats = pvc_get_stats(dev);
+
+	if (pvc->state.active) {
+		if (dev->type == ARPHRD_ETHER) {
+			int pad = ETH_ZLEN - skb->len;
+			if (pad > 0) { /* Pad the frame with zeros */
+				int len = skb->len;
+				if (skb_tailroom(skb) < pad)
+					if (pskb_expand_head(skb, 0, pad,
+							     GFP_ATOMIC)) {
+						stats->tx_dropped++;
+						dev_kfree_skb(skb);
+						return 0;
+					}
+				skb_put(skb, pad);
+				memset(skb->data + len, 0, pad);
+			}
+			skb->protocol = __constant_htons(ETH_P_802_3);
+		}
+		if (!fr_hard_header(&skb, pvc->dlci)) {
+			stats->tx_bytes += skb->len;
+			stats->tx_packets++;
+			if (pvc->state.fecn) /* TX Congestion counter */
+				stats->tx_compressed++;
+			skb->dev = hdlc_to_dev(pvc->master);
+			dev_queue_xmit(skb);
+			return 0;
+		}
+	}
+
+	stats->tx_dropped++;
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+
+
+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: DLCI %d [%s%s%s]%s %s\n",
+	       hdlc_to_name(pvc->master),
+	       pvc->dlci,
+	       pvc->main ? pvc->main->name : "",
+	       pvc->main && pvc->ether ? " " : "",
+	       pvc->ether ? pvc->ether->name : "",
+	       pvc->state.new ? " new" : "",
+	       !pvc->state.exist ? "deleted" :
+	       pvc->state.active ? "active" : "inactive");
+}
+
+
+
+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.dce_pvc_count * (2 + stat_len);
+		if (len > HDLC_MAX_MRU) {
+			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);
+	skb->protocol = __constant_htons(LMI_PROTO);
+	fr_hard_header(&skb, LMI_DLCI);
+	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;
+
+			/* LMI start/restart */
+			if (hdlc->state.fr.reliable && !pvc->state.exist) {
+				pvc->state.exist = pvc->state.new = 1;
+				fr_log_dlci_active(pvc);
+			}
+
+			/* ifconfig PVC up */
+			if (pvc->open_count && !pvc->state.active &&
+			    pvc->state.exist && !pvc->state.new) {
+				pvc->state.active = 1;
+				fr_log_dlci_active(pvc);
+			}
+
+			dlci_to_status(pvc->dlci, 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);
+	skb->nh.raw = skb->data;
+
+	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.dce_changed = 1;
+		} else {
+			while (pvc) {	/* Deactivate all PVCs */
+				pvc->state.exist = 0;
+				pvc->state.active = pvc->state.new = 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, no_ram;
+	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;
+
+/* Tell DTE that new PVC is now active */
+					hdlc->state.fr.dce_changed = 1;
+				}
+				pvc = pvc->next;
+			}
+		}
+
+		if (hdlc->state.fr.dce_changed) {
+			reptype = LMI_FULLREP;
+			hdlc->state.fr.fullrep_sent = 1;
+			hdlc->state.fr.dce_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 = 1;
+		pvc = pvc->next;
+	}
+
+	no_ram = 0;
+	while (skb->len >= i + 2 + stat_len) {
+		u16 dlci;
+		unsigned int active, new;
+
+		if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT)
+				     ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
+			printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n",
+			       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(skb->data + i, &active, &new);
+
+		pvc = add_pvc(hdlc, dlci);
+
+		if (!pvc && !no_ram) {
+			printk(KERN_WARNING
+			       "%s: Memory squeeze on fr_lmi_recv()\n",
+			       hdlc_to_name(hdlc));
+			no_ram = 1;
+		}
+
+		if (pvc) {
+			pvc->state.exist = 1;
+			pvc->state.deleted = 0;
+			if (active != pvc->state.active ||
+			    new != pvc->state.new ||
+			    !pvc->state.exist) {
+				pvc->state.new = new;
+				pvc->state.active = active;
+				fr_log_dlci_active(pvc);
+			}
+		}
+
+		i += stat_len;
+	}
+
+	pvc = hdlc->state.fr.first_pvc;
+
+	while (pvc) {
+		if (pvc->state.deleted && pvc->state.exist) {
+			pvc->state.active = pvc->state.new = 0;
+			pvc->state.exist = 0;
+			fr_log_dlci_active(pvc);
+		}
+		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;
+	struct net_device *dev = NULL;
+
+	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
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (pvc->state.fecn != fh->fecn) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+		printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", hdlc_to_name(pvc),
+		       dlci, fh->fecn ? "N" : "FF");
+#endif
+		pvc->state.fecn ^= 1;
+	}
+
+	if (pvc->state.becn != fh->becn) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+		printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", hdlc_to_name(pvc),
+		       dlci, fh->becn ? "N" : "FF");
+#endif
+		pvc->state.becn ^= 1;
+	}
+
+
+	if (data[3] == NLPID_IP) {
+		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+		dev = pvc->main;
+		skb->protocol = htons(ETH_P_IP);
+
+	} else if (data[3] == NLPID_IPV6) {
+		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+		dev = pvc->main;
+		skb->protocol = htons(ETH_P_IPV6);
+
+	} else if (skb->len > 10 && data[3] == FR_PAD &&
+		   data[4] == NLPID_SNAP && data[5] == FR_PAD) {
+		u16 oui = ntohs(*(u16*)(data + 6));
+		u16 pid = ntohs(*(u16*)(data + 8));
+		skb_pull(skb, 10);
+
+		switch ((((u32)oui) << 16) | pid) {
+		case ETH_P_ARP: /* routed frame with SNAP */
+		case ETH_P_IPX:
+		case ETH_P_IP:	/* a long variant */
+		case ETH_P_IPV6:
+			dev = pvc->main;
+			skb->protocol = htons(pid);
+			break;
+
+		case 0x80C20007: /* bridged Ethernet frame */
+			if ((dev = pvc->ether) != NULL)
+				skb->protocol = eth_type_trans(skb, dev);
+			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;
+		}
+	} else {
+		printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "
+		       "length = %i\n", hdlc_to_name(hdlc), data[3], skb->len);
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
+	if (dev) {
+		struct net_device_stats *stats = pvc_get_stats(dev);
+		stats->rx_packets++; /* PVC traffic */
+		stats->rx_bytes += skb->len;
+		if (pvc->state.becn)
+			stats->rx_compressed++;
+		skb->dev = dev;
+		netif_rx(skb);
+	} else
+		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.dce_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) {		/* Shutdown all PVCs for this FRAD */
+		if (pvc->main)
+			dev_close(pvc->main);
+		if (pvc->ether)
+			dev_close(pvc->ether);
+		pvc->state.active = pvc->state.new = pvc->state.fecn =
+			pvc->state.becn = 0;
+		pvc->state.exist = 0;
+		pvc = pvc->next;
+	}
+}
+
+
+static int fr_add_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
+{
+	pvc_device *pvc = NULL;
+	struct net_device *dev;
+	int result, used;
+	char * prefix = "pvc%d";
+
+	if (type == ARPHRD_ETHER)
+		prefix = "pvceth%d";
+
+	if ((pvc = add_pvc(hdlc, dlci)) == NULL) {
+		printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
+		       hdlc_to_name(hdlc));
+		return -ENOBUFS;
+	}
+
+	if (*get_dev_p(pvc, type))
+		return -EEXIST;
+
+	used = pvc_is_used(pvc);
+
+	dev = kmalloc(sizeof(struct net_device) +
+		      sizeof(struct net_device_stats), GFP_KERNEL);
+	if (!dev) {
+		printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
+		       hdlc_to_name(hdlc));
+		delete_unused_pvcs(hdlc);
+		return -ENOBUFS;
+	}
+	memset(dev, 0, sizeof(struct net_device) +
+	       sizeof(struct net_device_stats));
+
+	if (type == ARPHRD_ETHER) {
+		ether_setup(dev);
+		memcpy(dev->dev_addr, "\x00\x01", 2);
+                get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
+	} else {
+		dev->type = ARPHRD_DLCI;
+		dev->flags = IFF_POINTOPOINT;
+		dev->hard_header_len = 10;
+		dev->addr_len = 2;
+		*(u16*)dev->dev_addr = htons(dlci);
+		dlci_to_q922(dev->broadcast, dlci);
+	}
+	dev->hard_start_xmit = pvc_xmit;
+	dev->get_stats = pvc_get_stats;
+	dev->open = pvc_open;
+	dev->stop = pvc_close;
+	dev->do_ioctl = pvc_ioctl;
+	dev->change_mtu = pvc_change_mtu;
+	dev->mtu = HDLC_MAX_MTU;
+	dev->tx_queue_len = 0;
+	dev->priv = pvc;
+
+	result = dev_alloc_name(dev, prefix);
+	if (result < 0) {
+		kfree(dev);
+		delete_unused_pvcs(hdlc);
+		return result;
+	}
+
+	if (register_netdevice(dev) != 0) {
+		kfree(dev);
+		delete_unused_pvcs(hdlc);
+		return -EIO;
+	}
+
+	*get_dev_p(pvc, type) = dev;
+	if (!used) {
+		hdlc->state.fr.dce_changed = 1;
+		hdlc->state.fr.dce_pvc_count++;
+	}
+	return 0;
+}
+
+
+
+static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
+{
+	pvc_device *pvc;
+	struct net_device *dev;
+
+	if ((pvc = find_pvc(hdlc, dlci)) == NULL)
+		return -ENOENT;
+
+	if ((dev = *get_dev_p(pvc, type)) == NULL)
+		return -ENOENT;
+
+	if (dev->flags & IFF_UP)
+		return -EBUSY;		/* PVC in use */
+
+	unregister_netdevice(dev);
+	kfree(dev);
+	*get_dev_p(pvc, type) = NULL;
+
+	if (!pvc_is_used(pvc)) {
+		hdlc->state.fr.dce_pvc_count--;
+		hdlc->state.fr.dce_changed = 1;
+	}
+	delete_unused_pvcs(hdlc);
+	return 0;
+}
+
+
+
+static void fr_destroy(hdlc_device *hdlc)
+{
+	pvc_device *pvc = hdlc->state.fr.first_pvc;
+	while(pvc) {
+		pvc_device *next = pvc->next;
+		if (pvc->main) {
+			unregister_netdevice(pvc->main);
+			kfree(pvc->main);
+		}
+		if (pvc->ether) {
+			unregister_netdevice(pvc->ether);
+			kfree(pvc->ether);
+		}
+		kfree(pvc);
+		pvc = next;
+	}
+
+	hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
+	hdlc->state.fr.dce_pvc_count = 0;
+	hdlc->state.fr.dce_changed = 1;
+}
+
+
+
+int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	fr_proto *fr_s = ifr->ifr_settings.ifs_ifsu.fr;
+	const size_t size = sizeof(fr_proto);
+	fr_proto new_settings;
+	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.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(fr_s, &hdlc->state.fr.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_FR:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if(dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, fr_s, size))
+			return -EFAULT;
+
+		if (new_settings.lmi == LMI_DEFAULT)
+			new_settings.lmi = LMI_ANSI;
+
+		if ((new_settings.lmi != LMI_NONE &&
+		     new_settings.lmi != LMI_ANSI &&
+		     new_settings.lmi != LMI_CCITT) ||
+		    new_settings.t391 < 1 ||
+		    new_settings.t392 < 2 ||
+		    new_settings.n391 < 1 ||
+		    new_settings.n392 < 1 ||
+		    new_settings.n393 < new_settings.n392 ||
+		    new_settings.n393 > 32 ||
+		    (new_settings.dce != 0 &&
+		     new_settings.dce != 1))
+			return -EINVAL;
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+		if (result)
+			return result;
+
+		if (hdlc->proto != IF_PROTO_FR) {
+			hdlc_proto_detach(hdlc);
+			hdlc->state.fr.first_pvc = NULL;
+			hdlc->state.fr.dce_pvc_count = 0;
+		}
+		memcpy(&hdlc->state.fr.settings, &new_settings, size);
+
+		hdlc->open = fr_open;
+		hdlc->stop = fr_close;
+		hdlc->netif_rx = fr_rx;
+		hdlc->type_trans = NULL;
+		hdlc->proto_detach = fr_destroy;
+		hdlc->proto = IF_PROTO_FR;
+		dev->hard_start_xmit = hdlc->xmit;
+		dev->hard_header = NULL;
+		dev->type = ARPHRD_FRAD;
+		dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+		dev->addr_len = 0;
+		return 0;
+
+	case IF_PROTO_FR_ADD_PVC:
+	case IF_PROTO_FR_DEL_PVC:
+	case IF_PROTO_FR_ADD_ETH_PVC:
+	case IF_PROTO_FR_DEL_ETH_PVC:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (copy_from_user(&pvc, ifr->ifr_settings.ifs_ifsu.fr_pvc,
+				   sizeof(fr_proto_pvc)))
+			return -EFAULT;
+
+		if (pvc.dlci <= 0 || pvc.dlci >= 1024)
+			return -EINVAL;	/* Only 10 bits, DLCI 0 reserved */
+
+		if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC ||
+		    ifr->ifr_settings.type == IF_PROTO_FR_DEL_ETH_PVC)
+			result = ARPHRD_ETHER; /* bridged Ethernet device */
+		else
+			result = ARPHRD_DLCI;
+
+		if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC ||
+		    ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC)
+			return fr_add_pvc(hdlc, pvc.dlci, result);
+		else
+			return fr_del_pvc(hdlc, pvc.dlci, result);
+	}
+
+	return -EINVAL;
+}
--- linux-2.4.20/drivers/net/wan/hdlc_generic.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hdlc_generic.c	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,206 @@
+/*
+ * Generic HDLC support routines for Linux
+ *
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * 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/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.14";
+
+
+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)
+{
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	if (hdlc->netif_rx)
+		hdlc->netif_rx(skb);
+	else {
+		hdlc->stats.rx_dropped++; /* Shouldn't happen */
+		dev_kfree_skb(skb);
+	}
+	return 0;
+}
+
+
+#ifndef CONFIG_HDLC_RAW
+#define hdlc_raw_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_RAW_ETH
+#define hdlc_raw_eth_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_PPP
+#define hdlc_ppp_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_CISCO
+#define hdlc_cisco_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_FR
+#define hdlc_fr_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_X25
+#define hdlc_x25_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+
+int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	unsigned int proto;
+
+	if (cmd != SIOCWANDEV)
+		return -EINVAL;
+
+	switch(ifr->ifr_settings.type) {
+	case IF_PROTO_HDLC:
+	case IF_PROTO_HDLC_ETH:
+	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) {
+	case IF_PROTO_HDLC:	return hdlc_raw_ioctl(hdlc, ifr);
+	case IF_PROTO_HDLC_ETH:	return hdlc_raw_eth_ioctl(hdlc, ifr);
+	case IF_PROTO_PPP:	return hdlc_ppp_ioctl(hdlc, ifr);
+	case IF_PROTO_CISCO:	return hdlc_cisco_ioctl(hdlc, ifr);
+	case IF_PROTO_FR:	return hdlc_fr_ioctl(hdlc, ifr);
+	case IF_PROTO_X25:	return hdlc_x25_ioctl(hdlc, ifr);
+	default:		return -EINVAL;
+	}
+}
+
+
+
+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->proto_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)
+{
+	rtnl_lock();
+	hdlc_proto_detach(hdlc);
+	unregister_netdevice(hdlc_to_dev(hdlc));
+	rtnl_unlock();
+	MOD_DEC_USE_COUNT;
+}
+
+
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("HDLC support module");
+MODULE_LICENSE("GPL v2");
+
+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);
--- linux-2.4.20/drivers/net/wan/hdlc_ppp.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hdlc_ppp.c	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,116 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Point-to-point protocol support
+ *
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.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 unsigned short ppp_type_trans(struct sk_buff *skb,
+				     struct net_device *dev)
+{
+	return __constant_htons(ETH_P_WAN_PPP);
+}
+
+
+
+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;
+		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;
+
+		/* no settable parameters */
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+
+		hdlc->open = ppp_open;
+		hdlc->stop = ppp_close;
+		hdlc->netif_rx = NULL;
+		hdlc->type_trans = ppp_type_trans;
+		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;
+}
--- linux-2.4.20/drivers/net/wan/hdlc_raw.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hdlc_raw.c	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * Generic HDLC support routines for Linux
+ * HDLC support
+ *
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.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 unsigned short raw_type_trans(struct sk_buff *skb,
+				     struct net_device *dev)
+{
+	return __constant_htons(ETH_P_IP);
+}
+
+
+
+int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	raw_hdlc_proto *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
+	const size_t size = sizeof(raw_hdlc_proto);
+	raw_hdlc_proto new_settings;
+	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.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_HDLC:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, raw_s, size))
+			return -EFAULT;
+
+		if (new_settings.encoding == ENCODING_DEFAULT)
+			new_settings.encoding = ENCODING_NRZ;
+
+		if (new_settings.parity == PARITY_DEFAULT)
+			new_settings.parity = PARITY_CRC16_PR1_CCITT;
+
+		result = hdlc->attach(hdlc, new_settings.encoding,
+				      new_settings.parity);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+		memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
+
+		hdlc->open = NULL;
+		hdlc->stop = NULL;
+		hdlc->netif_rx = NULL;
+		hdlc->type_trans = raw_type_trans;
+		hdlc->proto = IF_PROTO_HDLC;
+		dev->hard_start_xmit = hdlc->xmit;
+		dev->hard_header = NULL;
+		dev->type = ARPHRD_RAWHDLC;
+		dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+		dev->addr_len = 0;
+		return 0;
+	}
+
+	return -EINVAL;
+}
--- linux-2.4.20/drivers/net/wan/hdlc_raw_eth.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hdlc_raw_eth.c	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,110 @@
+/*
+ * Generic HDLC support routines for Linux
+ * HDLC Ethernet emulation support
+ *
+ * Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.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/random.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <linux/hdlc.h>
+
+
+static int eth_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	int pad = ETH_ZLEN - skb->len;
+	if (pad > 0) {		/* Pad the frame with zeros */
+		int len = skb->len;
+		if (skb_tailroom(skb) < pad)
+			if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) {
+				dev_to_hdlc(dev)->stats.tx_dropped++;
+				dev_kfree_skb(skb);
+				return 0;
+			}
+		skb_put(skb, pad);
+		memset(skb->data + len, 0, pad);
+	}
+	return dev_to_hdlc(dev)->xmit(skb, dev);
+}
+
+
+int hdlc_raw_eth_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	raw_hdlc_proto *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
+	const size_t size = sizeof(raw_hdlc_proto);
+	raw_hdlc_proto new_settings;
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	int result;
+	void *old_ch_mtu;
+	int old_qlen;
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_PROTO:
+		ifr->ifr_settings.type = IF_PROTO_HDLC_ETH;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_HDLC_ETH:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, raw_s, size))
+			return -EFAULT;
+
+		if (new_settings.encoding == ENCODING_DEFAULT)
+			new_settings.encoding = ENCODING_NRZ;
+
+		if (new_settings.parity == PARITY_DEFAULT)
+			new_settings.parity = PARITY_CRC16_PR1_CCITT;
+
+		result = hdlc->attach(hdlc, new_settings.encoding,
+				      new_settings.parity);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+		memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
+
+		hdlc->open = NULL;
+		hdlc->stop = NULL;
+		hdlc->netif_rx = NULL;
+		hdlc->type_trans = eth_type_trans;
+		hdlc->proto = IF_PROTO_HDLC_ETH;
+		dev->hard_start_xmit = eth_tx;
+		old_ch_mtu = dev->change_mtu;
+		old_qlen = dev->tx_queue_len;
+		ether_setup(dev);
+		dev->change_mtu = old_ch_mtu;
+		dev->tx_queue_len = old_qlen;
+		memcpy(dev->dev_addr, "\x00\x01", 2);
+                get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
+		return 0;
+	}
+
+	return -EINVAL;
+}
--- linux-2.4.20/drivers/net/wan/hdlc_x25.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/hdlc_x25.c	2003-04-18 13:19:50.000000000 +0200
@@ -0,0 +1,216 @@
+/*
+ * Generic HDLC support routines for Linux
+ * X.25 support
+ *
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.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;
+		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;
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+
+		hdlc->open = x25_open;
+		hdlc->stop = x25_close;
+		hdlc->netif_rx = x25_rx;
+		hdlc->type_trans = NULL;
+		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;
+}
--- linux-2.4.20/drivers/net/wan/n2.c	2001-09-14 01:04:43.000000000 +0200
+++ linux/drivers/net/wan/n2.c	2003-04-18 13:19:50.000000000 +0200
@@ -1,12 +1,11 @@
 /*
  * SDL Inc. RISCom/N2 synchronous serial card driver for Linux
  *
- * Copyright (C) 1998-2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2003 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  *
  * For information see http://hq.pm.waw.pl/hdlc/
  *
@@ -17,10 +16,10 @@
  *    SDL Inc. PPP/HDLC/CISCO driver
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
 #include <linux/in.h>
@@ -33,17 +32,22 @@
 #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.14";
 static const char* devname = "RISCom/N2";
 
 #define USE_WINDOWSIZE 16384
 #define USE_BUS16BITS 1
 #define CLOCK_BASE 9830400	/* 9.8304 MHz */
-
+#define MAX_PAGES      16	/* 16 RAM pages at max */
+#define MAX_RAM_SIZE 0x80000	/* 512 KB */
+#if MAX_RAM_SIZE > MAX_PAGES * USE_WINDOWSIZE
+#undef MAX_RAM_SIZE
+#define MAX_RAM_SIZE (MAX_PAGES * USE_WINDOWSIZE)
+#endif
 #define N2_IOPORTS 0x10
+#define NEED_DETECT_RAM
+#define MAX_TX_BUFFERS 10
 
 static char *hw = NULL;	/* pointer to hw=xxx command line string */
 
@@ -87,17 +91,17 @@
 	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;
+	int valid;		/* port enabled */
+	int rxpart;		/* partial frame received, next frame invalid*/
+	unsigned short encoding;
+	unsigned short parity;
+	u16 rxin;		/* rx ring buffer 'in' pointer */
+	u16 txin;		/* tx ring buffer 'in' and 'last' pointers */
+	u16 txlast;
 	u8 rxs, txs, tmc;	/* SCA registers */
-	u8 valid;		/* port enabled */
 	u8 phy_node;		/* physical port # - 0 or 1 */
 	u8 log_node;		/* logical port # */
-	u8 rxin;		/* rx ring buffer 'in' pointer */
-	u8 txin;		/* tx ring buffer 'in' and 'last' pointers */
-	u8 txlast;
-	u8 rxpart;		/* partial frame received, next frame invalid*/
 }port_t;
 
 
@@ -108,14 +112,18 @@
 	u32 ram_size;		/* number of bytes */
 	u16 io;			/* IO Base address */
 	u16 buff_offset;	/* offset of first buffer of first channel */
+	u16 rx_ring_buffers;	/* number of buffers in a ring */
+	u16 tx_ring_buffers;
 	u8 irq;			/* IRQ (3-15) */
-	u8 ring_buffers;	/* number of buffers in a ring */
 
 	port_t ports[2];
 	struct card_s *next_card;
 }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 +165,7 @@
 
 
 
-static int n2_set_clock(port_t *port, int value)
+static void n2_set_iface(port_t *port)
 {
 	card_t *card = port->card;
 	int io = card->io;
@@ -166,13 +174,7 @@
 	u8 rxs = port->rxs & CLK_BRG_MASK;
 	u8 txs = port->txs & CLK_BRG_MASK;
 
-	switch(value) {
-	case CLOCK_EXT:
-		mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0;
-		rxs |= CLK_LINE_RX; /* RXC input */
-		txs |= CLK_LINE_TX; /* TXC input */
-		break;
-
+	switch(port->settings.clock_type) {
 	case CLOCK_INT:
 		mcr |= port->phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0;
 		rxs |= CLK_BRG_RX; /* BRG output */
@@ -191,8 +193,10 @@
 		txs |= CLK_RXCLK_TX; /* RX clock */
 		break;
 
-	default:
-		return -EINVAL;
+	default:		/* Clock EXTernal */
+		mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0;
+		rxs |= CLK_LINE_RX; /* RXC input */
+		txs |= CLK_LINE_TX; /* TXC input */
 	}
 
 	outb(mcr, io + N2_MCR);
@@ -200,33 +204,38 @@
 	port->txs = txs;
 	sca_out(rxs, msci + RXS, card);
 	sca_out(txs, msci + TXS, card);
-	port->clkmode = value;
-	return 0;
+	sca_set_port(port);
 }
 
 
 
-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 */
 	outb(mcr, io + N2_MCR);
-  
+
 	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);
+	n2_set_iface(port);
 	return 0;
 }
 
 
 
-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,108 +243,63 @@
 	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;
+	const size_t size = sizeof(sync_serial_settings);
+	sync_serial_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.sync;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
 
-	if(!capable(CAP_NET_ADMIN))
-		return -EPERM;
-
-	switch(cmd) {
-	case HDLCSCLOCK:
-		result = n2_set_clock(port, value);
-	case HDLCGCLOCK:
-		value = port->clkmode;
-		break;
-
-	case HDLCSCLOCKRATE:
-		port->clkrate = value;
-		sca_set_clock(port);
-	case HDLCGCLOCKRATE:
-		value = port->clkrate;
-		break;
-
-	case HDLCSLINE:
-		result = sca_set_loopback(port, value);
-	case HDLCGLINE:
-		value = port->line;
-		break;
-
-#ifdef DEBUG_RINGS
-	case HDLCRUN:
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+	if (cmd == SIOCDEVPRIVATE) {
 		sca_dump_rings(hdlc);
 		return 0;
-#endif /* DEBUG_RINGS */
-
-	default:
-		return -EINVAL;
 	}
+#endif
+	if (cmd != SIOCWANDEV)
+		return hdlc_ioctl(dev, ifr, cmd);
 
-	ifr->ifr_ifru.ifru_ivalue = value;
-	return result;
-}
-
-
-
-static u8 n2_count_page(card_t *card)
-{
-	u8 page;
-	int i, bcount = USE_WINDOWSIZE, wcount = USE_WINDOWSIZE/2;
-	u16 *dp = (u16*)card->winbase;
-	u8 *bp = (u8*)card->winbase;
-	u8 psr = inb(card->io + N2_PSR) & PSR_WINBITS;
-
-
-	for (page = 0; page < 16; page++) {
-		outb(psr | page, card->io + N2_PSR); /* select a page */
-		writeb(page, dp);
-		if (readb(dp) != page)
-			break;	/* If can't read back, no good memory */
-
-		outb(psr, card->io + N2_PSR); /* goto page 0 */
-		if (readb(dp))
-			break;	/* If page 0 changed, then wrapped around */
-
-		outb(psr | page, card->io + N2_PSR); /* select page again */
-
-		/*  first do byte tests */
-		for (i = 0; i < bcount; i++)
-			writeb(i, bp + i);
-		for (i = 0; i < bcount; i++)
-			if (readb(bp + i) != (i & 0xff))
-				return 0;
+	switch(ifr->ifr_settings.type) {
+	case IF_GET_IFACE:
+		ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(line, &port->settings, size))
+			return -EFAULT;
+		return 0;
 
-		for (i = 0; i < bcount; i++)
-			writeb(~i, bp + i);
-		for (i = 0; i < bcount; i++)
-			if (readb(bp + i) != (~i & 0xff))
-				return 0;
+	case IF_IFACE_SYNC_SERIAL:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (copy_from_user(&new_line, line, size))
+			return -EFAULT;
+
+		if (new_line.clock_type != CLOCK_EXT &&
+		    new_line.clock_type != CLOCK_TXFROMRX &&
+		    new_line.clock_type != CLOCK_INT &&
+		    new_line.clock_type != CLOCK_TXINT)
+		return -EINVAL;	/* No such clock setting */
 
-		/* next do 16-bit tests */
-		for (i = 0; i < wcount; i++)
-			writew(0x55AA, dp + i);
-		for (i = 0; i < wcount; i++)
-			if (readw(dp + i) != 0x55AA)
-				return 0;
+		if (new_line.loopback != 0 && new_line.loopback != 1)
+			return -EINVAL;
 
-		for (i = 0; i < wcount; i++)
-			writew(0xAA55, dp + i);
-		for (i = 0; i < wcount; i++)
-			if (readw(dp + i) != 0xAA55)
-				return 0;
+		memcpy(&port->settings, &new_line, size); /* Update settings */
+		n2_set_iface(port);
+		return 0;
 
-		for (i = 0; i < wcount; i++)
-			writew(page, dp + i);
+	default:
+		return hdlc_ioctl(dev, ifr, cmd);
 	}
-
-	return page;
 }
 
 
@@ -363,11 +327,12 @@
 
 
 
-static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
-		  long valid0, long valid1)
+static int __init n2_run(unsigned long io, unsigned long irq,
+			 unsigned long winbase, long valid0, long valid1)
 {
 	card_t *card;
 	u8 cnt, pcr;
+	int i;
 
 	if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) {
 		printk(KERN_ERR "n2: invalid I/O port value\n");
@@ -378,7 +343,7 @@
 		printk(KERN_ERR "n2: invalid IRQ value\n");
 		return -ENODEV;
 	}
-    
+
 	if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) {
 		printk(KERN_ERR "n2: invalid RAM value\n");
 		return -ENODEV;
@@ -438,25 +403,27 @@
 	pcr = PCR_ENWIN | PCR_VPM | (USE_BUS16BITS ? PCR_BUS16 : 0);
 	outb(pcr, io + N2_PCR);
 
-	cnt = n2_count_page(card);
-	if (!cnt) {
-		printk(KERN_ERR "n2: memory test failed.\n");
-		n2_destroy_card(card);
-		return -EIO;
-	}
+	card->ram_size = sca_detect_ram(card, card->winbase, MAX_RAM_SIZE);
 
-	card->ram_size = cnt * USE_WINDOWSIZE;
+	/* number of TX + RX buffers for one port */
+	i = card->ram_size / ((valid0 + valid1) * (sizeof(pkt_desc) +
+						   HDLC_MAX_MRU));
 
-	/* 4 rings required for 2 ports, 2 rings for one port */
-	card->ring_buffers = card->ram_size /
-		((valid0 + valid1) * 2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
+	card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
+	card->rx_ring_buffers = i - card->tx_ring_buffers;
 
-	card->buff_offset = (valid0 + valid1) * 2 * (sizeof(pkt_desc))
-		* card->ring_buffers;
+	card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) *
+		(card->tx_ring_buffers + card->rx_ring_buffers);
 
 	printk(KERN_DEBUG "n2: RISCom/N2 %u KB RAM, IRQ%u, "
-	       "using %u packets rings\n", card->ram_size / 1024, card->irq,
-	       card->ring_buffers);
+	       "using %u TX + %u RX packets rings\n", card->ram_size / 1024,
+	       card->irq, card->tx_ring_buffers, card->rx_ring_buffers);
+
+	if (card->tx_ring_buffers < 1) {
+		printk(KERN_ERR "n2: RAM test failed\n");
+		n2_destroy_card(card);
+		return -EIO;
+	}
 
 	pcr |= PCR_RUNSCA;		/* run SCA */
 	outb(pcr, io + N2_PCR);
@@ -465,6 +432,7 @@
 	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 +444,16 @@
 			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 "
@@ -542,7 +512,7 @@
 				break;
 			hw++;
 		}
-      
+
 		if (!valid[0] && !valid[1])
 			break;	/* at least one port must be used */
 
@@ -550,7 +520,7 @@
 			n2_run(io, irq, ram, valid[0], valid[1]);
 
 		if (*hw == '\x0')
-			return 0;
+			return first_card ? 0 : -ENOSYS;
 	}while(*hw++ == ':');
 
 	printk(KERN_ERR "n2: invalid hardware parameters\n");
@@ -586,6 +556,6 @@
 
 MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
 MODULE_DESCRIPTION("RISCom/N2 serial port driver");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_PARM(hw, "s");		/* hw=io,irq,ram,ports:io,irq,... */
 EXPORT_NO_SYMBOLS;
--- linux-2.4.20/drivers/net/wan/pc300too.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/pc300too.c	2003-04-18 13:19:55.000000000 +0200
@@ -0,0 +1,524 @@
+/*
+ * Cyclades PC300 synchronous serial card driver for Linux
+ *
+ * Copyright (C) 2000-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * For information see http://hq.pm.waw.pl/hdlc/
+ *
+ * Sources of information:
+ *    Hitachi HD64572 SCA-II User's Manual
+ *    Cyclades PC300 Linux driver
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/hdlc.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+
+#include "hd64572.h"
+
+
+static const char* version = "Cyclades PC300 driver version: 1.14";
+static const char* devname = "PC300";
+
+#define	PC300_PLX_WIN	0x80    /* PLX control window size (128b) */
+#define	PC300_RAMSIZE	0x80000 /* RAM window size (512Kb) */
+#define	PC300_SCASIZE	0x400   /* SCA window size (1Kb) */
+#define ALL_PAGES_ALWAYS_MAPPED
+#define NEED_DETECT_RAM
+#define MAX_TX_BUFFERS 10
+
+#ifdef CONFIG_PC300TOO_CRYSTAL_CLOCK
+#define CLOCK_BASE 24576000
+#else
+static int pci_clock_freq = 33000000;
+#define CLOCK_BASE pci_clock_freq
+#endif
+
+
+/* Masks to access the init_ctrl PLX register */
+#define	PC300_CLKSEL_MASK		(0x00000004UL)
+#define	PC300_CHMEDIA_MASK(port)	(0x00000020UL << (port * 3))
+#define	PC300_CTYPE_MASK		(0x00000800UL)
+
+
+/* Control Constant Definitions */
+#define	PC300_RSV	0x01
+#define	PC300_X21	0x02
+#define	PC300_TE	0x03
+
+
+/*
+ *      PLX PCI9050-1 local configuration and shared runtime registers.
+ *	This structure can be used to access 9050 registers (memory mapped).
+ */
+typedef struct {
+	u32 loc_addr_range[4];	/* 00-0Ch : Local Address Ranges */
+	u32 loc_rom_range;	/* 10h : Local ROM Range */
+	u32 loc_addr_base[4];	/* 14-20h : Local Address Base Addrs */
+	u32 loc_rom_base;	/* 24h : Local ROM Base */
+	u32 loc_bus_descr[4];	/* 28-34h : Local Bus Descriptors */
+	u32 rom_bus_descr;	/* 38h : ROM Bus Descriptor */
+	u32 cs_base[4];		/* 3C-48h : Chip Select Base Addrs */
+	u32 intr_ctrl_stat;	/* 4Ch : Interrupt Control/Status */
+	u32 init_ctrl;		/* 50h : EEPROM ctrl, Init Ctrl, etc */
+}plx9050;
+
+
+
+typedef struct port_s {
+	hdlc_device hdlc;	/* HDLC device struct - must be first */
+	struct card_s *card;
+	spinlock_t lock;	/* TX lock */
+	sync_serial_settings settings;
+	int rxpart;		/* partial frame received, next frame invalid*/
+	unsigned short encoding;
+	unsigned short parity;
+	unsigned int iface;
+	u16 rxin;		/* rx ring buffer 'in' pointer */
+	u16 txin;		/* tx ring buffer 'in' and 'last' pointers */
+	u16 txlast;
+	u8 rxs, txs, tmc;	/* SCA registers */
+	u8 phy_node;		/* physical port # - 0 or 1 */
+}port_t;
+
+
+
+typedef struct card_s {
+	int type;		/* RSV, X21, etc. */
+	int n_ports;		/* 1 or 2 ports */
+	u32 ramphys;		/* dual-port memory base (physical) */
+	u8* rambase;		/* dual-port memory base (virtual) */
+	u32 ramsize;		/* dual-port memory size */
+	u32 scaphys;		/* dual-port memory base (physical) */
+	u8* scabase;		/* dual-port memory base (virtual) */
+	u32 plxphys;		/* PLX registers memory base (physical) */
+	plx9050* plxbase;	/* PLX registers memory base (virtual) */
+	u16 rx_ring_buffers;	/* number of buffers in a ring */
+	u16 tx_ring_buffers;
+	u16 buff_offset;	/* offset of first buffer of first channel */
+	u8 irq;			/* interrupt request level */
+
+	port_t ports[2];
+}card_t;
+
+
+#define sca_in(reg, card)	     readb(card->scabase + (reg))
+#define sca_out(value, reg, card)    writeb(value, card->scabase + (reg))
+#define sca_inw(reg, card)	     readw(card->scabase + (reg))
+#define sca_outw(value, reg, card)   writew(value, card->scabase + (reg))
+#define sca_inl(reg, card)	     readl(card->scabase + (reg))
+#define sca_outl(value, reg, card)   writel(value, card->scabase + (reg))
+
+#define port_to_card(port)	     (port->card)
+#define log_node(port)		     (port->phy_node)
+#define phy_node(port)		     (port->phy_node)
+#define winsize(card)		     (PC300_RAMSIZE)
+#define win0base(card)		     (card->rambase)
+#define winbase(card)      	     (card->rambase)
+#define get_port(card, port)	     ((port) < (card)->n_ports ? \
+					 (&(card)->ports[port]) : (NULL))
+
+#include "hd6457x.c"
+
+
+
+static void pc300_set_iface(port_t *port)
+{
+	card_t *card = port->card;
+	u32* init_ctrl = &card->plxbase->init_ctrl;
+	u16 msci = get_msci(port);
+	u8 rxs = port->rxs & CLK_BRG_MASK;
+	u8 txs = port->txs & CLK_BRG_MASK;
+
+	sca_out(EXS_TES1, (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) + EXS,
+		port_to_card(port));
+	switch(port->settings.clock_type) {
+	case CLOCK_INT:
+		rxs |= CLK_BRG; /* BRG output */
+		txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
+		break;
+
+	case CLOCK_TXINT:
+		rxs |= CLK_LINE; /* RXC input */
+		txs |= CLK_PIN_OUT | CLK_BRG; /* BRG output */
+		break;
+
+	case CLOCK_TXFROMRX:
+		rxs |= CLK_LINE; /* RXC input */
+		txs |= CLK_PIN_OUT | CLK_TX_RXCLK; /* RX clock */
+		break;
+
+	default:		/* EXTernal clock */
+		rxs |= CLK_LINE; /* RXC input */
+		txs |= CLK_PIN_OUT | CLK_LINE; /* TXC input */
+		break;
+	}
+
+	port->rxs = rxs;
+	port->txs = txs;
+	sca_out(rxs, msci + RXS, card);
+	sca_out(txs, msci + TXS, card);
+	sca_set_port(port);
+
+	if (port->card->type == PC300_RSV) {
+		if (port->iface == IF_IFACE_V35)
+			writel(readl(init_ctrl) |
+			       PC300_CHMEDIA_MASK(port->phy_node), init_ctrl);
+		else
+
+			writel(readl(init_ctrl) &
+			       ~PC300_CHMEDIA_MASK(port->phy_node), init_ctrl);
+	}
+}
+
+
+
+static int pc300_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;
+	sca_open(hdlc);
+	pc300_set_iface(port);
+	return 0;
+}
+
+
+
+static int pc300_close(struct net_device *dev)
+{
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	sca_close(hdlc);
+	hdlc_close(hdlc);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+
+static int pc300_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	const size_t size = sizeof(sync_serial_settings);
+	sync_serial_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.sync;
+	int new_type;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	port_t *port = hdlc_to_port(hdlc);
+
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+	if (cmd == SIOCDEVPRIVATE) {
+		sca_dump_rings(hdlc);
+		return 0;
+	}
+#endif
+	if (cmd != SIOCWANDEV)
+		return hdlc_ioctl(dev, ifr, cmd);
+
+	if (ifr->ifr_settings.type == IF_GET_IFACE) {
+		ifr->ifr_settings.type = port->iface;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(line, &port->settings, size))
+			return -EFAULT;
+		return 0;
+
+	}
+
+	if (port->card->type == PC300_X21 &&
+	    (ifr->ifr_settings.type == IF_IFACE_SYNC_SERIAL ||
+	     ifr->ifr_settings.type == IF_IFACE_X21))
+		new_type = IF_IFACE_X21;
+
+	else if (port->card->type == PC300_RSV &&
+		 (ifr->ifr_settings.type == IF_IFACE_SYNC_SERIAL ||
+		  ifr->ifr_settings.type == IF_IFACE_V35))
+		new_type = IF_IFACE_V35;
+
+	else if (port->card->type == PC300_RSV &&
+		 ifr->ifr_settings.type == IF_IFACE_V24)
+		new_type = IF_IFACE_V24;
+
+	else
+		return hdlc_ioctl(dev, ifr, cmd);
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	if (copy_from_user(&new_line, line, size))
+		return -EFAULT;
+
+	if (new_line.clock_type != CLOCK_EXT &&
+	    new_line.clock_type != CLOCK_TXFROMRX &&
+	    new_line.clock_type != CLOCK_INT &&
+	    new_line.clock_type != CLOCK_TXINT)
+		return -EINVAL;	/* No such clock setting */
+
+	if (new_line.loopback != 0 && new_line.loopback != 1)
+		return -EINVAL;
+
+	memcpy(&port->settings, &new_line, size); /* Update settings */
+	port->iface = new_type;
+	pc300_set_iface(port);
+	return 0;
+}
+
+
+
+static void pc300_pci_remove_one(struct pci_dev *pdev)
+{
+	int i;
+        card_t *card = pci_get_drvdata(pdev);
+
+        if (!card)
+                BUG();
+
+	for(i = 0; i < 2; i++)
+		if (card->ports[i].card)
+			unregister_hdlc_device(&card->ports[i].hdlc);
+
+	if (card->irq)
+		free_irq(card->irq, card);
+	iounmap(card->rambase);
+	iounmap(card->scabase);
+	iounmap(card->plxbase);
+        pci_release_regions(pdev);
+	kfree(card);
+        pci_set_drvdata(pdev, NULL);
+}
+
+
+
+static void __devinit plx_init(card_t *card)
+{
+	plx9050 *plx_ctl = (plx9050 *)card->plxbase;
+
+	/* Reset PLX */
+	writel(readl(&plx_ctl->init_ctrl) | 0x40000000, &plx_ctl->init_ctrl);
+	udelay(100L);
+	writel(readl(&plx_ctl->init_ctrl) & ~0x40000000, &plx_ctl->init_ctrl);
+
+	/* Reload Config. Registers from EEPROM */
+	writel(readl(&plx_ctl->init_ctrl) | 0x20000000, &plx_ctl->init_ctrl);
+	udelay(100L);
+	writel(readl(&plx_ctl->init_ctrl) & ~0x20000000, &plx_ctl->init_ctrl);
+}
+
+
+
+static int __devinit pc300_pci_init_one(struct pci_dev *pdev,
+					const struct pci_device_id *ent)
+{
+	card_t *card;
+	u8 rev_id;
+	int i;
+
+#ifndef MODULE
+        static int printed_version;
+        if (!printed_version++)
+                printk(KERN_INFO "%s\n", version);
+#endif
+
+	i = pci_enable_device(pdev);
+	if (i)
+		return i;
+
+        i = pci_request_regions(pdev, "PC300");
+	if (i)
+                return i;
+
+	card = kmalloc(sizeof(card_t), GFP_KERNEL);
+	if (card == NULL) {
+		printk(KERN_ERR "pc300: unable to allocate memory\n");
+		return -ENOBUFS;
+	}
+	memset(card, 0, sizeof(card_t));
+
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
+
+	card->plxphys = pci_resource_start(pdev,0) & PCI_BASE_ADDRESS_MEM_MASK;
+	card->plxbase = ioremap(card->plxphys, PC300_PLX_WIN);
+
+	card->scaphys = pci_resource_start(pdev,2) & PCI_BASE_ADDRESS_MEM_MASK;
+	card->scabase = ioremap(card->scaphys, PC300_SCASIZE);
+
+	card->ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK;
+	card->rambase = ioremap(card->ramphys, PC300_RAMSIZE);
+
+	card->ramsize = sca_detect_ram(card, card->rambase, PC300_RAMSIZE);
+
+	/* Enable interrupts on the PCI bridge */
+	plx_init(card);
+	writew(readw(&card->plxbase->intr_ctrl_stat) | 0x0040,
+	       &card->plxbase->intr_ctrl_stat);
+
+#ifdef CONFIG_PC300_PCI_CLOCK
+	/* Set board clock to PCI clock */
+	writel(readl(&card->plxbase->init_ctrl) | 0x00000004UL,
+	       &card->plxbase->init_ctrl);
+#else
+	/* Set board clock to internal oscillator clock */
+	writel(readl(&card->plxbase->init_ctrl) &~0x00000004UL,
+	       &card->plxbase->init_ctrl);
+#endif
+
+	if (readl(&card->plxbase->init_ctrl) & PC300_CTYPE_MASK)
+		card->type = PC300_X21;
+	else
+		card->type = PC300_RSV;
+
+	if (pdev->device == PCI_DEVICE_ID_PC300_RX_1 ||
+	    pdev->device == PCI_DEVICE_ID_PC300_TE_1)
+		card->n_ports = 1;
+	else
+		card->n_ports = 2;
+
+	pci_set_drvdata(pdev, card);
+
+	/* Allocate IRQ */
+	if(request_irq(pdev->irq, sca_intr, SA_SHIRQ, devname, card)) {
+		printk(KERN_WARNING "pc300: could not allocate IRQ%d.\n",
+		       pdev->irq);
+		pc300_pci_remove_one(pdev);
+		return -EBUSY;
+	}
+	card->irq = pdev->irq;
+
+	/* number of TX + RX buffers for one port */
+	i = card->ramsize / (card->n_ports * (sizeof(pkt_desc) +HDLC_MAX_MRU));
+	card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
+	card->rx_ring_buffers = i - card->tx_ring_buffers;
+
+	card->buff_offset = card->n_ports * sizeof(pkt_desc) *
+		(card->tx_ring_buffers + card->rx_ring_buffers);
+
+	printk(KERN_INFO "pc300: PC300/%s %u KB RAM at 0x%x, IRQ%u, "
+	       "using %u TX + %u RX packets rings\n",
+	       card->type == PC300_X21 ? "X21" : "RSV", card->ramsize / 1024,
+	       card->ramphys, card->irq,
+	       card->tx_ring_buffers, card->rx_ring_buffers);
+
+	if (card->tx_ring_buffers < 1) {
+		printk(KERN_ERR "pc300: RAM test failed\n");
+		pc300_pci_remove_one(pdev);
+		return -EIO;
+	}
+
+	sca_init(card, 0);
+
+	// COTE not set - allows better TX DMA settings
+	// sca_out(sca_in(PCR, card) | PCR_COTE, PCR, card);
+
+	sca_out(0x10, BTCR, card);
+
+	for(i = 0; i < card->n_ports; i++) {
+		port_t *port = &card->ports[i];
+		struct net_device *dev = hdlc_to_dev(&port->hdlc);
+		port->phy_node = i;
+
+		spin_lock_init(&port->lock);
+		dev->irq = card->irq;
+		dev->mem_start = card->ramphys;
+		dev->mem_end = card->ramphys + card->ramsize - 1;
+		dev->tx_queue_len = 50;
+		dev->do_ioctl = pc300_ioctl;
+		dev->open = pc300_open;
+		dev->stop = pc300_close;
+		port->hdlc.attach = sca_attach;
+		port->hdlc.xmit = sca_xmit;
+		port->settings.clock_type = CLOCK_EXT;
+		if (card->type == PC300_X21)
+			port->iface = IF_IFACE_X21;
+		else
+			port->iface = IF_IFACE_V35;
+
+		if(register_hdlc_device(&port->hdlc)) {
+			printk("pc300: unable to register hdlc device\n");
+			pc300_pci_remove_one(pdev);
+			return -ENOBUFS;
+		}
+		port->card = card;
+		sca_init_sync_port(port);	/* Set up SCA memory */
+
+		printk(KERN_INFO "%s: PC300 node %d\n",
+		       hdlc_to_name(&port->hdlc), port->phy_node);
+	}
+	return 0;
+}
+
+
+
+static struct pci_device_id pc300_pci_tbl[] __devinitdata = {
+        { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_RX_1, PCI_ANY_ID,
+	  PCI_ANY_ID, 0, 0, 0 },
+        { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_RX_2, PCI_ANY_ID,
+	  PCI_ANY_ID, 0, 0, 0 },
+        { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_TE_1, PCI_ANY_ID,
+	  PCI_ANY_ID, 0, 0, 0 },
+        { PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_PC300_TE_2, PCI_ANY_ID,
+	  PCI_ANY_ID, 0, 0, 0 },
+        { 0, }
+};
+
+
+static struct pci_driver pc300_pci_driver = {
+        name:           "PC300",
+        id_table:       pc300_pci_tbl,
+        probe:          pc300_pci_init_one,
+        remove:         pc300_pci_remove_one,
+};
+
+
+static int __init pc300_init_module(void)
+{
+#ifdef MODULE
+        printk(KERN_INFO "%s\n", version);
+#endif
+	if (pci_clock_freq < 1000000 || pci_clock_freq > 80000000) {
+		printk(KERN_ERR "pc300: Invalid PCI clock frequency\n");
+		return -EINVAL;
+	}
+	return pci_module_init(&pc300_pci_driver);
+}
+
+
+
+static void __exit pc300_cleanup_module(void)
+{
+	pci_unregister_driver(&pc300_pci_driver);
+}
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Cyclades PC300 serial port driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(pci, pc300_pci_tbl);
+#ifndef CONFIG_PC300TOO_CRYSTAL_CLOCK
+MODULE_PARM(pci_clock_freq, "i");
+MODULE_PARM_DESC(pci_clock_freq, "System PCI clock frequency in Hz");
+#endif
+EXPORT_NO_SYMBOLS;
+module_init(pc300_init_module);
+module_exit(pc300_cleanup_module);
--- linux-2.4.20/drivers/net/wan/tahoe9xx.c	1970-01-01 01:00:00.000000000 +0100
+++ linux/drivers/net/wan/tahoe9xx.c	2003-07-31 16:08:39.000000000 +0200
@@ -0,0 +1,799 @@
+/*
+ * Tahoe 9xx synchronous serial card driver for Linux
+ *
+ * Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2003 Piotr Kaczmarzyk <piotr@tahoe.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * For information see http://hq.pm.waw.pl/hdlc/
+ *
+ * Sources of information:
+ *    Hitachi HD64570 SCA User's Manual
+ *    PLX Technology Inc. PCI9052 Data Book
+ *    Dallas Semiconductor DS21554 Datasheet
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/hdlc.h>
+#include <linux/pci.h>
+#include <asm/delay.h>
+#include <asm/io.h>
+
+#include "hd64570.h"
+
+static const char* version = "Tahoe 9xx driver version: 1.14t";
+static const char* devname = "TAHOE9XX";
+
+#define	TAHOE9XX_PLX_SIZE	0x80	/* PLX control window size (128b) */
+#define	TAHOE9XX_SCA_SIZE	0x100	/* SCA window size (256b) */
+#define ALL_PAGES_ALWAYS_MAPPED
+#define NEED_DETECT_RAM
+#define MAX_TX_BUFFERS		10
+
+#define CLOCK_BASE 9216000
+
+#define G703_AMI		0
+#define G703_HDB3		1
+
+#define DEFAULT_LICR    0x80
+
+#define PCI_VENDOR_ID_TAHOE		0x8246
+#define PCI_DEVICE_ID_TAHOE931	0x0931
+#define PCI_DEVICE_ID_TAHOE932	0x0932
+#define PCI_DEVICE_ID_TAHOE971	0x0971
+#define PCI_DEVICE_ID_TAHOE972	0x0972
+
+/*
+ *      PLX PCI9052 local configuration and shared runtime registers.
+ *	This structure can be used to access 9052 registers (memory mapped).
+ */
+typedef struct {
+	u32 loc_addr_range[4];	/* 00-0Ch : Local Address Ranges */
+	u32 loc_rom_range;	/* 10h : Local ROM Range */
+	u32 loc_addr_base[4];	/* 14-20h : Local Address Base Addrs */
+	u32 loc_rom_base;	/* 24h : Local ROM Base */
+	u32 loc_bus_descr[4];	/* 28-34h : Local Bus Descriptors */
+	u32 rom_bus_descr;	/* 38h : ROM Bus Descriptor */
+	u32 cs_base[4];		/* 3C-48h : Chip Select Base Addrs */
+	u32 intr_ctrl_stat;	/* 4Ch : Interrupt Control/Status */
+	u32 init_ctrl;		/* 50h : EEPROM ctrl, Init Ctrl, etc */
+}plx9052;
+
+
+
+
+
+typedef struct ds21554_s {
+	u8	vcr1;		/* Counter:	Violation Error */
+	u8	vcr2;
+	u8	crccr1;		/* Counter:	CRC4 Error */
+	u8	crccr2;
+	u8	ebcr1;		/* Counter:	E-bit Error (FEBE) */
+	u8	ebcr2;
+	u8	sr1;		/* Status:	Status Register 1 */
+	u8	sr2;		/* Status:	Status Register 2 */
+	u8	rir;		/* Status:	Receive Information */
+	u8	reserved1[6];
+	u8	idr;		/* Misc:	Device Indentification */
+	u8	rcr1;		/* Control:	Receive Control 1 */
+	u8	rcr2;		/* Control:	Receive Control 2 */
+	u8	tcr1;		/* Control:	Transmit Control 1 */
+	u8	tcr2;		/* Control:	Transmit Control 2 */
+	u8	ccr1;		/* Control:	Common Control 1 */
+	u8	test1;
+	u8	imr1;		/* Interrupt Mask 1 */
+	u8	imr2;		/* Interrupt Mask 2 */
+	u8	licr;		/* Control: Line interface */
+	u8	test2;
+	u8	ccr2;		/* Control: Common Control 2 */
+	u8	ccr3;		/* Control: Common Control 3 */
+	u8	tsacr;		/* Control: Transmit Sa bit */
+	u8	ccr6;		/* Control: Common Control 6 */
+	u8	ssr;		/* Status: 	Synchronizer Status */
+	u8	rnaf;		/* Receive non-align frame */
+	u8	taf;		/* Transmit align frame */
+	u8	tnaf;		/* Transmit non-align frame */
+	u8	tcbr1;		/* Transmit channel blocking */
+	u8	tcbr2;
+	u8	tcbr3;
+	u8	tcbr4;
+	u8	tir1;		/* Transmit idle */
+	u8	tir2;
+	u8	tir3;
+	u8	tir4;
+	u8	tidr;		/* Transmit idle definition */
+	u8	rcbr1;		/* Receive channel blocking */
+	u8	rcbr2;
+	u8	rcbr3;
+	u8	rcbr4;
+	u8	raf;		/* Receive align frame */
+	u8	rs1;		/* Receive signalling */
+	u8	rs2;
+	u8	rs3;
+	u8	rs4;
+	u8	rs5;
+	u8	rs6;
+	u8	rs7;
+	u8	rs8;
+	u8	rs9;
+	u8	rs10;
+	u8	rs11;
+	u8	rs12;
+	u8	rs13;
+	u8	rs14;
+	u8	rs15;
+	u8	rs16;
+	u8	ts1;		/* Transmit signaling */
+	u8	ts2;
+	u8	ts3;
+	u8	ts4;
+	u8	ts5;
+	u8	ts6;
+	u8	ts7;
+	u8	ts8;
+	u8	ts9;
+	u8	ts10;
+	u8	ts11;
+	u8	ts12;
+	u8	ts13;
+	u8	ts14;
+	u8	ts15;
+	u8	ts16;
+	u8	tsiaf;		/* Transmit Si Bits Align Frame */
+	u8	tsinaf;		/* Transmit Si Bits Non-align Frame */
+	u8	tra;		/* Transmit Remote Alarm Bits */
+	u8	tsa4;		/* Transmit Sa Bits */
+	u8	tsa5;
+	u8	tsa6;
+	u8	tsa7;
+	u8	tsa8;
+	u8	rsiaf;		/* Receive Si Bits Align Frame */
+	u8	rsinaf;		/* Receive Si Bits Non-Align Frame */
+	u8	rra;		/* Receive Remote Alarm Bits */
+	u8	rsa4;		/* Receive Sa Bits */
+	u8	rsa5;
+	u8	rsa6;
+	u8	rsa7;
+	u8	rsa8;
+	u8	tc1;		/* Transmit channel */
+	u8	tc2;
+	u8	tc3;
+	u8	tc4;
+	u8	tc5;
+	u8	tc6;
+	u8	tc7;
+	u8	tc8;
+	u8	tc9;
+	u8	tc10;
+	u8	tc11;
+	u8	tc12;
+	u8	tc13;
+	u8	tc14;
+	u8	tc15;
+	u8	tc16;
+	u8	tc17;
+	u8	tc18;
+	u8	tc19;
+	u8	tc20;
+	u8	tc21;
+	u8	tc22;
+	u8	tc23;
+	u8	tc24;
+	u8	tc25;
+	u8	tc26;
+	u8	tc27;
+	u8	tc28;
+	u8	tc29;
+	u8	tc30;
+	u8	tc31;
+	u8	tc32;
+	u8	rc1;		/* Receive channel */
+	u8	rc2;
+	u8	rc3;
+	u8	rc4;
+	u8	rc5;
+	u8	rc6;
+	u8	rc7;
+	u8	rc8;
+	u8	rc9;
+	u8	rc10;
+	u8	rc11;
+	u8	rc12;
+	u8	rc13;
+	u8	rc14;
+	u8	rc15;
+	u8	rc16;
+	u8	rc17;
+	u8	rc18;
+	u8	rc19;
+	u8	rc20;
+	u8	rc21;
+	u8	rc22;
+	u8	rc23;
+	u8	rc24;
+	u8	rc25;
+	u8	rc26;
+	u8	rc27;
+	u8	rc28;
+	u8	rc29;
+	u8	rc30;
+	u8	rc31;
+	u8	rc32;
+	u8	tcc1;		/* Transmit channel control */
+	u8	tcc2;
+	u8	tcc3;
+	u8	tcc4;
+	u8	rcc1;		/* Receive channel control */
+	u8	rcc2;
+	u8	rcc3;
+	u8	rcc4;
+	u8	ccr4;		/* Control:	Common Control 4 */
+	u8	tds0m;		/* Transmit DS0 Monitor */
+	u8	ccr5;		/* Control:	Common Control 5 */
+	u8	rds0m;		/* Receive DS0 Monitor */
+	u8	test3;
+	u8	reserved2[3];
+	u8	hcr;		/* HDLC Control */
+	u8	hsr;		/* HDLC Status */
+	u8	himr;		/* HDLC Interrupt Mask */
+	u8	rhir;		/* Receive HDLC Information */
+	u8	rhfr;		/* Receive HDLC FIFO */
+	u8	ibo;		/* Interleave Bus Operation */
+	u8	thir;		/* Transmit HDLC Information */
+	u8	thfr;		/* Transmit HDLC FIFO */
+	u8	rdc1;		/* Receive HDLC DS0 Control 1 */
+	u8	rdc2;		/* Receive HDLC DS0 Control 2 */
+	u8	tdc1;		/* Transmit HDLC DS0 Control 1 */
+	u8	tdc2;		/* Transmit HDLC DS0 Control 2 */
+	u8	reserved3[4];
+} ds21554_t;
+
+typedef struct port_s {
+	hdlc_device hdlc;	/* HDLC device struct - must be first */
+	struct card_s *card;
+	spinlock_t lock;	/* TX lock */
+	te1_settings settings;
+	int rxpart;		/* partial frame received, next frame invalid*/
+	unsigned short encoding;
+	unsigned short parity;
+	u16 rxin;		/* rx ring buffer 'in' pointer */
+	u16 txin;		/* tx ring buffer 'in' and 'last' pointers */
+	u16 txlast;
+	u8 rxs, txs, tmc;	/* SCA registers */
+	u8 phy_node;		/* physical port # - 0 or 1 */
+	u32 dsphys;			/* DS21544 memory base (physical) */
+	ds21554_t* dsbase;	/* DS21544 memory base (virtual) */
+	u8	g703_on;		/* Enable/disable G.703 transceiver */
+	u8	g703_coding;	/* G.703 line coding */
+	u8	g703_idlecode;	/* G.703 idle timeslots contents */
+}port_t;
+
+typedef struct card_s {
+	u32 ramphys;		/* buffer memory base (physical) */
+	u8* rambase;		/* buffer memory base (virtual) */
+	u32 ramsize;		/* buffer memory size */
+	u32 scaphys;		/* SCA memory base (physical) */
+	u8* scabase;		/* SCA memory base (virtual) */
+	u32 plxphys;		/* PLX registers memory base (physical) */
+	plx9052* plxbase;	/* PLX registers memory base (virtual) */
+	u16 rx_ring_buffers;	/* number of buffers in a ring */
+	u16 tx_ring_buffers;
+	u16 buff_offset;	/* offset of first buffer of first channel */
+	u8 irq;				/* interrupt request level */
+	u8 no_ports;		/* number of ports */
+	char dev_name[10];	/* device name */
+	port_t ports[2];
+}card_t;
+
+#define sca_in(reg, card)	     readb(card->scabase + (reg))
+#define sca_out(value, reg, card)    writeb(value, card->scabase + (reg))
+#define sca_inw(reg, card)	     readw(card->scabase + (reg))
+#define sca_outw(value, reg, card)   writew(value, card->scabase + (reg))
+#define sca_inl(reg, card)	     readl(card->scabase + (reg))
+#define sca_outl(value, reg, card)   writel(value, card->scabase + (reg))
+
+#define port_to_card(port)	     (port->card)
+#define log_node(port)		     (port->phy_node)
+#define phy_node(port)		     (port->phy_node)
+#define winbase(card)      	     (card->rambase)
+#define get_port(card, port)	     (&card->ports[port])
+#define sca_flush(card)		     (sca_in(IER0, card));
+
+static inline void new_memcpy_toio(char *dest, char *src, int length)
+{
+	int len;
+	
+	do {
+		len = length > 128 ? 128 : length; /* 32 */
+		memcpy_toio(dest, src, len);
+		dest += len;
+		src += len;
+		length -= len;
+		readb(dest);
+	} while (len);
+}
+
+#undef memcpy_toio
+#define memcpy_toio new_memcpy_toio
+
+#include "hd6457x.c"
+
+void init_ds21554(port_t *port);
+
+static void t9xx_set_iface(port_t *port)
+{
+	card_t *card = port->card;
+	u8 msci = get_msci(port);
+	u8 rxs = port->rxs & CLK_BRG_MASK;
+	u8 txs = port->txs & CLK_BRG_MASK;
+	
+	if (port->dsbase) {
+		init_ds21554(port);
+	}
+	
+	rxs |= CLK_LINE_RX; /* RXC input */
+	txs |= CLK_LINE_TX; /* TXC input */
+
+	port->rxs = rxs;
+	port->txs = txs;
+	sca_out(rxs, msci + RXS, card);
+	sca_out(txs, msci + TXS, card);
+	sca_set_port(port);
+}
+
+
+
+static int t9xx_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;
+	sca_open(hdlc);
+	t9xx_set_iface(port);
+	sca_flush(port_to_card(port));
+	return 0;
+}
+
+
+
+static int t9xx_close(struct net_device *dev)
+{
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+
+	sca_close(hdlc);
+	sca_flush(port_to_card(dev_to_port(dev)));
+	hdlc_close(hdlc);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+
+
+static int t9xx_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	const size_t size = sizeof(te1_settings);
+	te1_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.te1;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	port_t *port = hdlc_to_port(hdlc);
+
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+	if (cmd == SIOCDEVPRIVATE) {
+		sca_dump_rings(hdlc);
+		return 0;
+	}
+#endif
+	if (cmd != SIOCWANDEV)
+		return hdlc_ioctl(dev, ifr, cmd);
+
+	switch(ifr->ifr_settings.type) {
+	case IF_GET_IFACE:
+		if (port->dsbase)
+			ifr->ifr_settings.type = IF_IFACE_E1;
+		else
+			ifr->ifr_settings.type = IF_IFACE_V35;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(line, &port->settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_IFACE_E1:
+	case IF_IFACE_V35:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (copy_from_user(&new_line, line, size))
+			return -EFAULT;
+
+		if (new_line.clock_type != CLOCK_EXT)
+			return -EINVAL;	/* No such clock setting */
+
+		if (new_line.loopback > 3)
+			return -EINVAL;
+
+		memcpy(&port->settings, &new_line, size); /* Update settings */
+		t9xx_set_iface(port);
+		sca_flush(port_to_card(port));
+		return 0;
+
+	default:
+		return hdlc_ioctl(dev, ifr, cmd);
+	}
+}
+
+
+
+static void t9xx_pci_remove_one(struct pci_dev *pdev)
+{
+	int i, ports;
+        card_t *card = pci_get_drvdata(pdev);
+
+	if ((pdev->subsystem_device == PCI_DEVICE_ID_TAHOE932) ||
+	    (pdev->subsystem_device == PCI_DEVICE_ID_TAHOE972))
+		ports = 2;
+	else
+		ports = 1;
+	for(i = 0; i < ports; i++)
+		if (card->ports[i].card)
+			unregister_hdlc_device(&card->ports[i].hdlc);
+
+	if (card->irq)
+		free_irq(card->irq, card);
+	iounmap(card->rambase);
+	iounmap(card->scabase);
+	iounmap(card->plxbase);
+        pci_release_regions(pdev);
+	kfree(card);
+}
+
+static void ds21554_update_licr(port_t *port)
+{
+	port->dsbase->licr = DEFAULT_LICR | (port->settings.egl << 4) | (port->g703_on == 0);
+}
+
+static void ds21554_update_registers(port_t *port)
+{
+	ds21554_t	*ds = port->dsbase;
+
+	if (port->settings.slot_map == 0xffffffff) {
+		/* Unframed */
+		ds->ccr1 = 0x08 | (port->g703_coding << 6) | (port->g703_coding << 2);
+		ds->tcr1 = 0x40;
+		ds->tcr2 = 0x00;
+		ds->ccr2 = 0x80; //84
+		ds->tir1 = 0; ds->tir2 = 0; ds->tir3 = 0; ds->tir4 = 0;
+		ds->tcc1 = 0; ds->tcc2 = 0; ds->tcc3 = 0; ds->tcc4 = 0;
+		ds->rcc1 = 0; ds->rcc2 = 0; ds->rcc3 = 0; ds->rcc4 = 0;
+		ds->rcbr1 = 0xff; ds->rcbr2 = 0xff; 
+		ds->rcbr3 = 0xff; ds->rcbr4 = 0xff; 
+		ds->tcbr1 = 0xff; ds->tcbr2 = 0xff; 
+		ds->tcbr3 = 0xff; ds->tcbr4 = 0xff; 
+		ds->tsacr = 0x00;
+		ds->tdc1 = 0x00;
+
+		ds->rcr2 = 0x04;
+		ds->ccr3 = 0x02;
+
+		ds->ccr6 = 0x03;	/* elastic buffers reset */
+		udelay(1000);
+		ds->ccr6 = 0x00;	/* elastic buffers reset */
+
+		ds->ccr5 = 0x60;	/* elastic buffers align */
+		udelay(1000);
+		ds->ccr5 = 0x00;
+	} else {
+		ds->ccr1 = 0x08 | (port->g703_coding << 6) | (port->g703_coding << 2)
+		                | (port->settings.crc4 << 4) | (port->settings.crc4);
+		ds->tcr1 = 0x00;
+		ds->ccr2 = 0x94;	/* Automatic alarm generation */
+
+		/* Receive channels */
+		ds->rcbr1 = port->settings.slot_map & 0xff;
+		ds->rcbr2 = (port->settings.slot_map >> 8) & 0xff;
+		ds->rcbr3 = (port->settings.slot_map >> 16) & 0xff;
+		ds->rcbr4 = (port->settings.slot_map >> 24) & 0xff;
+	
+		/* Transmit channels */
+		ds->tcbr1 = port->settings.slot_map & 0xff;
+		ds->tcbr2 = (port->settings.slot_map >> 8) & 0xff;
+		ds->tcbr3 = (port->settings.slot_map >> 16) & 0xff;
+		ds->tcbr4 = (port->settings.slot_map >> 24) & 0xff;
+
+		/* Transmit idle */
+		/* (w pozostalych szczelinach wysylamy kod idle) */
+		ds->tir1 = ~port->settings.slot_map & 0xfe;	// Slot 1 nigdy nie jest idle
+		ds->tir2 = (~port->settings.slot_map >> 8) & 0xff;
+		ds->tir3 = (~port->settings.slot_map >> 16) & 0xff;
+		ds->tir4 = (~port->settings.slot_map >> 24) & 0xff;
+		ds->rcr2 = 0x06;	/* RSYSCLK = 2048, rx elastic store enabled */
+		ds->ccr3 = 0x82;	/* TSYSCLK = 2048, tx elastic store enabled */
+
+		ds->ccr6 = 0x07;	/* elastic buffers reset */
+		udelay(1000);
+		ds->ccr6 = 0x04;	/* elastic buffers reset */
+
+		ds->ccr5 = 0x60;	/* elastic buffers align */
+		udelay(1000);
+		ds->ccr5 = 0x00;
+	}
+}
+
+void init_ds21554(port_t *port)
+{
+	ds21554_t	*ds = port->dsbase;
+	
+	ds->ccr2 = 0x04;
+	udelay(1000);
+	ds->ccr5 = 0x80;	/* Line Interface Reset */
+	udelay(1000);
+
+	ds->ccr5 = 0xe0;	/* Elastic Buffers Reset */
+	udelay(1000);
+	ds->ccr5 = 0x00;
+	ds->ccr6 = 0x04;	/* TCLK from RCLK */
+	ds->tcr2 = 0x00;
+	ds21554_update_licr(port);
+
+	/* Setup HDB3, CRC4, CAS/CCS, G.802 */
+	ds21554_update_registers(port);
+
+	ds->ccr2 = 0x94;	/* Automatic alarm generation */
+
+	ds->taf = 0x1b;
+	ds->tnaf = 0x40;
+
+	ds21554_update_registers(port);
+
+	ds->tidr = port->g703_idlecode;
+
+//	ds->ccr4 |= 0x40;
+}
+
+
+static int __devinit t9xx_pci_init_one(struct pci_dev *pdev,
+					 const struct pci_device_id *ent)
+{
+	card_t *card;
+	u8 rev_id, tahoe97x = 0;
+	u32 *p;
+	int i;
+
+#ifndef MODULE
+        static int printed_version;
+        if (!printed_version++)
+                printk(KERN_INFO "%s\n", version);
+#endif
+
+	i = pci_enable_device(pdev);
+	if (i)
+		return i;
+
+        i = pci_request_regions(pdev, "Tahoe9xx");
+	if (i)
+                return i;
+
+	card = kmalloc(sizeof(card_t), GFP_KERNEL);
+	if (card == NULL) {
+		printk(KERN_ERR "%s: unable to allocate memory\n", card->dev_name);
+		return -ENOBUFS;
+	}
+	memset(card, 0, sizeof(card_t));
+	pci_set_drvdata(pdev, card);
+
+	sprintf(card->dev_name, "Tahoe");
+	switch (pdev->subsystem_device) {
+		case PCI_DEVICE_ID_TAHOE931:
+			strcat(card->dev_name, "931");
+			card->no_ports = 1;
+			break;
+		case PCI_DEVICE_ID_TAHOE932:
+			strcat(card->dev_name, "932");
+			card->no_ports = 2;
+			break;
+		case PCI_DEVICE_ID_TAHOE971:
+			strcat(card->dev_name, "971");
+			tahoe97x = 1;
+			card->no_ports = 1;
+			break;
+		case PCI_DEVICE_ID_TAHOE972:
+			strcat(card->dev_name, "972");
+			tahoe97x = 1;
+			card->no_ports = 2;
+			break;
+		default:
+			strcat(card->dev_name, "9xx");
+			card->no_ports = 0;
+			break;
+	}
+
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
+	if (pci_resource_len(pdev, 0) != TAHOE9XX_PLX_SIZE ||
+	    pci_resource_len(pdev, 2) != TAHOE9XX_SCA_SIZE ||
+	    pci_resource_len(pdev, 3) < 16384) {
+		printk(KERN_ERR "%s: invalid card EEPROM parameters\n", card->dev_name);
+		kfree(card);
+		return -EFAULT;
+	}
+
+	card->plxphys = pci_resource_start(pdev,0) & PCI_BASE_ADDRESS_MEM_MASK;
+	card->plxbase = ioremap(card->plxphys, TAHOE9XX_PLX_SIZE);
+
+	card->scaphys = pci_resource_start(pdev,2) & PCI_BASE_ADDRESS_MEM_MASK;
+	card->scabase = ioremap(card->scaphys, TAHOE9XX_SCA_SIZE);
+
+	card->ramphys = pci_resource_start(pdev,3) & PCI_BASE_ADDRESS_MEM_MASK;
+	card->rambase = ioremap(card->ramphys, pci_resource_len(pdev,3));
+
+	if (tahoe97x) {
+		for (i=0; i < card->no_ports; i++) {
+			card->ports[i].dsphys = pci_resource_start(pdev,4+i) & PCI_BASE_ADDRESS_MEM_MASK;
+			card->ports[i].dsbase = (ds21554_t *)ioremap(card->ports[i].dsphys, pci_resource_len(pdev,4+i));
+		}
+	} else {
+		for (i=0; i < card->no_ports; i++)
+			card->ports[i].dsbase = (ds21554_t *)0;
+	}
+
+	/* Reset PLX */
+	p = &card->plxbase->init_ctrl;
+	writel(readl(p) | 0x40000000, p);
+	readl(p);		/* Flush the write - do not use sca_flush */
+	udelay(1);
+
+	writel(readl(p) & ~0x40000000, p);
+	readl(p);		/* Flush the write - do not use sca_flush */
+	udelay(1);
+
+	card->ramsize = sca_detect_ram(card, card->rambase,
+				       pci_resource_len(pdev,3));
+
+	/* number of TX + RX buffers for one port - this is dual port card */
+	i = card->ramsize / (2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
+	card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
+	card->rx_ring_buffers = i - card->tx_ring_buffers;
+
+	card->buff_offset = 2 * sizeof(pkt_desc) * (card->tx_ring_buffers +
+						    card->rx_ring_buffers);
+
+	printk(KERN_INFO "%s: %u KB RAM at 0x%x, IRQ%u, using %u TX +"
+	       " %u RX packets rings\n", card->dev_name, card->ramsize / 1024,
+	       card->ramphys, pdev->irq,
+	       card->tx_ring_buffers, card->rx_ring_buffers);
+	if (tahoe97x) {
+		for (i=0; i < card->no_ports; i++) {
+			/* Make sure DS21554 is there */
+			card->ports[i].dsbase->idr = 0x00;
+			card->ports[i].dsbase->tc1 = 0xaa;
+			card->ports[i].dsbase->rc1 = 0x55;
+			if (((card->ports[i].dsbase->idr & 0xf0) == 0xa0) && (card->ports[i].dsbase->tc1 == 0xaa) && (card->ports[i].dsbase->rc1 == 0x55)) {
+				printk(KERN_INFO "%s: DS21554 (port %d) detected at 0x%x\n", card->dev_name, i, card->ports[i].dsphys);
+				/* Clear registers */
+				memset(card->ports[i].dsbase, 0, 256);
+				/* Default settings */
+				card->ports[i].g703_on = 1;
+				card->ports[i].settings.egl = 0;
+				card->ports[i].g703_coding = G703_HDB3;
+				card->ports[i].g703_idlecode = 0x54;
+				card->ports[i].settings.crc4 = 1;
+				/* Unframed */
+				card->ports[i].settings.slot_map = 0xffffffff;
+				init_ds21554(&card->ports[i]);
+			} else
+				printk(KERN_INFO "%s: DS21554 (port %d) test failed!\n", card->dev_name, i);
+		}
+	}
+
+	if (card->tx_ring_buffers < 1) {
+		printk(KERN_ERR "%s: RAM test failed\n", card->dev_name);
+		t9xx_pci_remove_one(pdev);
+		return -EFAULT;
+	}
+
+	/* Enable interrupts on the PCI bridge */
+	p = &card->plxbase->intr_ctrl_stat;
+	writew(readw(p) | 0x0040, p);
+
+	/* Allocate IRQ */
+	if(request_irq(pdev->irq, sca_intr, SA_SHIRQ, devname, card)) {
+		printk(KERN_WARNING "%s: could not allocate IRQ%d.\n", card->dev_name,
+		       pdev->irq);
+		t9xx_pci_remove_one(pdev);
+		return -EBUSY;
+	}
+	card->irq = pdev->irq;
+
+	sca_init(card, 0);
+
+	for(i = 0; i < card->no_ports; i++) {
+		port_t *port = &card->ports[i];
+		struct net_device *dev = hdlc_to_dev(&port->hdlc);
+		port->phy_node = i;
+
+		spin_lock_init(&port->lock);
+		dev->irq = card->irq;
+		dev->mem_start = card->ramphys;
+		dev->mem_end = card->ramphys + card->ramsize - 1;
+		dev->tx_queue_len = 50;
+		dev->do_ioctl = t9xx_ioctl;
+		dev->open = t9xx_open;
+		dev->stop = t9xx_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_ERR "%s: unable to register hdlc "
+			       "device\n", card->dev_name);
+			t9xx_pci_remove_one(pdev);
+			return -ENOBUFS;
+		}
+		port->card = card;
+		sca_init_sync_port(port);	/* Set up SCA memory */
+
+		printk(KERN_INFO "%s: %s node %d\n",
+		       hdlc_to_name(&port->hdlc), card->dev_name, port->phy_node);
+	}
+
+	sca_flush(card);
+	return 0;
+}
+
+
+
+static struct pci_device_id t9xx_pci_tbl[] __devinitdata = {
+        { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, 
+          PCI_VENDOR_ID_TAHOE, PCI_ANY_ID,
+          0, 0, 0 },
+        { 0, }
+};
+
+static struct pci_driver t9xx_pci_driver = {
+        name:           "Tahoe9xx",
+        id_table:       t9xx_pci_tbl,
+        probe:          t9xx_pci_init_one,
+        remove:         t9xx_pci_remove_one,
+};
+
+static int __init t9xx_init_module(void)
+{
+#ifdef MODULE
+        printk(KERN_INFO "%s\n", version);
+#endif
+	return pci_module_init(&t9xx_pci_driver);
+}
+
+
+
+static void __exit t9xx_cleanup_module(void)
+{
+	pci_unregister_driver(&t9xx_pci_driver);
+}
+
+MODULE_AUTHOR("Piotr Kaczmarzyk <piotr@tahoe.pl>");
+MODULE_DESCRIPTION("Tahoe 9xx serial port driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(pci, t9xx_pci_tbl);
+EXPORT_NO_SYMBOLS;
+module_init(t9xx_init_module);
+module_exit(t9xx_cleanup_module);
--- linux-2.4.20/include/linux/hdlc/ioctl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux/include/linux/hdlc/ioctl.h	2003-07-31 14:46:58.000000000 +0200
@@ -0,0 +1,50 @@
+#ifndef __HDLC_IOCTL_H__
+#define __HDLC_IOCTL_H__
+
+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;
+	unsigned short crc4;
+	unsigned short egl;
+} te1_settings;                  /* T1, E1 */
+
+typedef struct {
+	unsigned short encoding;
+	unsigned short parity;
+} raw_hdlc_proto;
+
+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 dlci;
+	char master[IFNAMSIZ];	/* Name of master FRAD device */
+}fr_proto_pvc_info;		/* for returning PVC information only */
+
+typedef struct {
+    unsigned int interval;
+    unsigned int timeout;
+} cisco_proto;
+
+/* PPP doesn't need any info now - supply length = 0 to ioctl */
+
+#endif /* __HDLC_IOCTL_H__ */
--- linux-2.4.20/include/linux/hdlc.h	2001-07-26 22:51:45.000000000 +0200
+++ linux/include/linux/hdlc.h	2003-07-31 14:48:09.000000000 +0200
@@ -1,75 +1,59 @@
 /*
  * Generic HDLC support routines for Linux
  *
- * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999-2003 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  */
 
 #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 GENERIC_HDLC_VERSION 4	/* For synchronization with sethdlc utility */
 
+#define CLOCK_DEFAULT   0	/* Default 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 */
+
+
+#define ENCODING_DEFAULT	0 /* Default 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 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 */
+
+#define LMI_DEFAULT		0 /* Default 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 */
 
-#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/skbuff.h>
 #include <linux/netdevice.h>
 #include <net/syncppp.h>
+#include <linux/hdlc/ioctl.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 + 14 + 4) /* for ETH+VLAN over 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
@@ -112,6 +96,7 @@
 
 
 typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned ea1  : 1;
 	unsigned cr   : 1;
 	unsigned dlcih: 6;
@@ -121,6 +106,19 @@
 	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;
 
 
@@ -146,68 +144,107 @@
 
 
 typedef struct pvc_device_struct {
-	struct net_device netdev; /* PVC net device - must be first */
-	struct net_device_stats stats;
 	struct hdlc_device_struct *master;
-	struct pvc_device_struct *next;
-
-	u8 state;
-	u8 newstate;
+	struct net_device *main;
+	struct net_device *ether; /* bridged Ethernet interface */
+	struct pvc_device_struct *next;	/* Sorted in ascending DLCI order */
+	int dlci;
+	int open_count;
+
+	struct {
+		unsigned int new: 1;
+		unsigned int active: 1;
+		unsigned int exist: 1;
+		unsigned int deleted: 1;
+		unsigned int fecn: 1;
+		unsigned int becn: 1;
+	}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);
-	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;
+	/* 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 (*stop)(struct hdlc_device_struct *hdlc);
+	void (*proto_detach)(struct hdlc_device_struct *hdlc);
+	void (*netif_rx)(struct sk_buff *skb);
+	unsigned short (*type_trans)(struct sk_buff *skb,
+				     struct net_device *dev);
+	int proto;		/* IF_PROTO_HDLC/CISCO/FR/etc. */
+
+	union {
+		struct {
+			fr_proto settings;
+			pvc_device *first_pvc;
+			int dce_pvc_count;
+
+			struct timer_list timer;
+			int last_poll;
+			int reliable;
+			int dce_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 {
+			raw_hdlc_proto settings;
+		}raw_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_raw_eth_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)
@@ -222,15 +259,9 @@
 }
 
 
-static __inline__ struct net_device* pvc_to_dev(pvc_device *pvc)
-{
-	return &pvc->netdev;
-}
-
-
 static __inline__ pvc_device* dev_to_pvc(struct net_device *dev)
 {
-	return (pvc_device*)dev;
+	return (pvc_device*)dev->priv;
 }
 
 
@@ -240,97 +271,75 @@
 }
 
 
-static __inline__ const char *pvc_to_name(pvc_device *pvc)
+static __inline__ u16 q922_to_dlci(u8 *hdr)
 {
-	return pvc_to_dev(pvc)->name;
+	return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
 }
 
 
-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)
+static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci)
 {
-	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;
+	hdr[0] = (dlci >> 2) & 0xFC;
+	hdr[1] = ((dlci << 4) & 0xF0) | 0x01;
 }
 
 
 
-static __inline__ u16 netdev_dlci(struct net_device *dev)
+static __inline__ void debug_frame(const struct sk_buff *skb)
 {
-	return ntohs(*(u16*)dev->dev_addr);
-}
-
-
+	int i;
 
-static __inline__ u16 q922_to_dlci(u8 *hdr)
-{
-	return ((hdr[0] & 0xFC)<<2) | ((hdr[1] & 0xF0)>>4);
+	for (i=0; i<skb->len; i++) {
+		if (i == 100) {
+			printk("...\n");
+			return;
+		}
+		printk(" %02X", skb->data[i]);
+	}
+	printk("\n");
 }
 
 
 
-static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci)
+/* Must be called by hardware driver when HDLC device is being opened */
+static __inline__ int hdlc_open(hdlc_device *hdlc)
 {
-	hdr[0] = (dlci>>2) & 0xFC;
-	hdr[1] = ((dlci<<4) & 0xF0) | 0x01;
-}
+	if (hdlc->proto == -1)
+		return -ENOSYS;		/* no protocol attached */
 
+	if (hdlc->open)
+		return hdlc->open(hdlc);
+	return 0;
+}
 
 
-static __inline__ int mode_is(hdlc_device *hdlc, int mask)
+/* Must be called by hardware driver when HDLC device is being closed */
+static __inline__ void hdlc_close(hdlc_device *hdlc)
 {
-	return (hdlc->mode & mask) == mask;
+	if (hdlc->stop)
+		hdlc->stop(hdlc);
 }
 
 
-
-static __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
+/* May be used by hardware driver to gain control over HDLC device */
+static __inline__ void hdlc_proto_detach(hdlc_device *hdlc)
 {
-	pvc_device *pvc=hdlc->first_pvc;
-	
-	while (pvc) {
-		if (netdev_dlci(&pvc->netdev) == dlci)
-			return pvc;
-		pvc=pvc->next;
-	}
-
-	return NULL;
+	if (hdlc->proto_detach)
+		hdlc->proto_detach(hdlc);
+	hdlc->proto_detach = NULL;
 }
 
 
-
-static __inline__ void debug_frame(const struct sk_buff *skb)
+static __inline__ unsigned short hdlc_type_trans(struct sk_buff *skb,
+						 struct net_device *dev)
 {
-	int i;
-
-	for (i=0; i<skb->len; i++) {
-		if (i == 100) {
-			printk("...\n");
-			return;
-		}
-		printk(" %02X", skb->data[i]);
-	}
-	printk("\n");
+	hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+	if (hdlc->type_trans)
+		return hdlc->type_trans(skb, dev);
+	else
+		return __constant_htons(ETH_P_HDLC);
 }
 
-
 #endif /* __KERNEL */
 #endif /* __HDLC_H */
--- linux-2.4.20/include/linux/if.h	2001-11-22 20:47:07.000000000 +0100
+++ linux/include/linux/if.h	2003-07-31 14:47:49.000000000 +0200
@@ -22,6 +22,9 @@
 #include <linux/types.h>		/* for "__kernel_caddr_t" et al	*/
 #include <linux/socket.h>		/* for "struct sockaddr" et al	*/
 
+#define	IFNAMSIZ	16
+#include <linux/hdlc/ioctl.h>
+
 /* Standard interface flags (netdevice->flags). */
 #define	IFF_UP		0x1		/* interface is up		*/
 #define	IFF_BROADCAST	0x2		/* broadcast address valid	*/
@@ -48,6 +51,33 @@
 /* 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	/* can't 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				*/
+#define IF_PROTO_HDLC_ETH 0x2007	/* raw HDLC, Ethernet emulation	*/
+#define IF_PROTO_FR_ADD_ETH_PVC 0x2008	/*  Create FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_DEL_ETH_PVC 0x2009	/*  Delete FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_PVC	0x200A		/* for reading PVC status	*/
+#define IF_PROTO_FR_ETH_PVC 0x200B
+
+
 /*
  *	Device mapping structure. I'd just gone off and designed a 
  *	beautiful scheme using only loadable modules with arguments
@@ -69,6 +99,24 @@
 	/* 3 bytes spare */
 };
 
+struct if_settings
+{
+	unsigned int type;	/* Type of physical device or protocol */
+	unsigned int size;	/* Size of the data allocated by the caller */
+	union {
+		/* {atm/eth/dsl}_settings anyone ? */
+		raw_hdlc_proto		*raw_hdlc;
+		cisco_proto		*cisco;
+		fr_proto		*fr;
+		fr_proto_pvc		*fr_pvc;
+		fr_proto_pvc_info	*fr_pvc_info;
+
+		/* interface settings */
+		sync_serial_settings	*sync;
+		te1_settings		*te1;
+	} ifs_ifsu;
+};
+
 /*
  * Interface request structure used for socket
  * ioctl's.  All interface ioctl's must have parameter
@@ -79,7 +127,6 @@
 struct ifreq 
 {
 #define IFHWADDRLEN	6
-#define	IFNAMSIZ	16
 	union
 	{
 		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
@@ -98,6 +145,7 @@
 		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
 		char	ifru_newname[IFNAMSIZ];
 		char *	ifru_data;
+		struct	if_settings ifru_settings;
 	} ifr_ifru;
 };
 
@@ -117,6 +165,7 @@
 #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.
--- linux-2.4.20/include/linux/if_ether.h	2002-11-29 00:53:15.000000000 +0100
+++ linux/include/linux/if_ether.h	2003-04-18 13:19:50.000000000 +0200
@@ -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.
--- linux-2.4.20/include/linux/sockios.h	2001-11-07 23:39:36.000000000 +0100
+++ linux/include/linux/sockios.h	2003-04-18 13:19:50.000000000 +0200
@@ -81,6 +81,8 @@
 #define SIOCGMIIREG	0x8948		/* Read MII PHY register.	*/
 #define SIOCSMIIREG	0x8949		/* Write MII PHY register.	*/
 
+#define SIOCWANDEV	0x894A		/* get/set netdev parameters	*/
+
 /* ARP cache control calls. */
 		    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
 #define SIOCDARP	0x8953		/* delete ARP table entry	*/
--- linux-2.4.20/net/core/dev.c	2002-11-29 00:53:15.000000000 +0100
+++ linux/net/core/dev.c	2003-04-18 13:19:50.000000000 +0200
@@ -2155,7 +2155,8 @@
 			    cmd == SIOCETHTOOL ||
 			    cmd == SIOCGMIIPHY ||
 			    cmd == SIOCGMIIREG ||
-			    cmd == SIOCSMIIREG) {
+			    cmd == SIOCSMIIREG ||
+			    cmd == SIOCWANDEV) {
 				if (dev->do_ioctl) {
 					if (!netif_device_present(dev))
 						return -ENODEV;
@@ -2321,8 +2322,9 @@
 		 */	
 		 
 		default:
-			if (cmd >= SIOCDEVPRIVATE &&
-			    cmd <= SIOCDEVPRIVATE + 15) {
+			if (cmd == SIOCWANDEV ||
+			    (cmd >= SIOCDEVPRIVATE &&
+			     cmd <= SIOCDEVPRIVATE + 15)) {
 				dev_load(ifr.ifr_name);
 				dev_probe_lock();
 				rtnl_lock();
