## Automatically generated incremental diff
## From:   linux-2.0.31-pre4
## To:     linux-2.0.31-pre5
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.0.31-pre4/arch/i386/defconfig linux-2.0.31-pre5/arch/i386/defconfig
--- linux-2.0.31-pre4/arch/i386/defconfig	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/arch/i386/defconfig	2003-08-15 15:03:58.000000000 -0700
@@ -124,7 +124,6 @@
 # ISDN subsystem
 #
 # CONFIG_ISDN is not set
-CONFIG_HISAX_EURO=y
 
 #
 # CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
@@ -161,6 +160,7 @@
 # CONFIG_STALDRV is not set
 # CONFIG_RISCOM8 is not set
 # CONFIG_PRINTER is not set
+# CONFIG_SPECIALIX is not set
 # CONFIG_MOUSE is not set
 # CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
diff -urN linux-2.0.31-pre4/arch/i386/mm/init.c linux-2.0.31-pre5/arch/i386/mm/init.c
--- linux-2.0.31-pre4/arch/i386/mm/init.c	1996-11-14 05:20:09.000000000 -0800
+++ linux-2.0.31-pre5/arch/i386/mm/init.c	2003-08-15 15:03:58.000000000 -0700
@@ -35,6 +35,8 @@
 #endif
 #endif
 
+const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n";
+
 extern void die_if_kernel(char *,struct pt_regs *,long);
 extern void show_net_buffers(void);
 
diff -urN linux-2.0.31-pre4/CREDITS linux-2.0.31-pre5/CREDITS
--- linux-2.0.31-pre4/CREDITS	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/CREDITS	2003-08-15 15:03:58.000000000 -0700
@@ -240,7 +240,7 @@
 
 N: Juan Jose Ciarlante
 E: jjciarla@raiz.uncu.edu.ar
-E: juanjo@irriga.uncu.edu.ar
+E: irriga@impsat1.com.ar
 D: Network driver alias support
 D: IP masq hashing and app modules
 S: Las Cuevas 2385 - Bo Guemes
@@ -862,8 +862,9 @@
 
 N: Martin Mares
 E: mj@k332.feld.cvut.cz
+W: http://atrey.karlin.mff.cuni.cz/~mj/
 D: BIOS video mode handling code
-D: Miscellaneous kernel fixes
+D: Miscellaneous kernel fixes and hacks
 D: MOXA C-218 serial board driver
 D: BOOTP support
 S: Kankovskeho 1241
@@ -1124,6 +1125,12 @@
 S: 79539 Loerrach
 S: Germany
 
+N: Joerg Reuter
+E: jreuter@poboxes.com
+E: dl1bke@db0pra.ampr.org (amateur radio)
+W: http://www.rat.de/jr
+D: Z8530 SCC driver and DAMA Slave for AX.25
+
 N: William E. Roadcap
 E: roadcapw@titus.org
 W: http://www.cfw.com/~roadcapw
@@ -1178,14 +1185,14 @@
 S: Finland
 
 N: Eric Schenk
-E: schenk@cs.toronto.edu
+E: Eric.Schenk@dna.lth.se
 D: Random kernel debugging.
 D: SYSV Semaphore code rewrite.
 D: Network layer debugging.
 D: Dial on demand facility (diald).
-S: 7 Borden Street
-S: Toronto, Ontario
-S: Canada M5S 2M8
+S: Dag Hammerskjolds v. 3E
+S: S-226 64 LUND
+S: Sweden
 
 N: Peter De Schrijver
 E: stud11@cc4.kuleuven.ac.be
@@ -1524,8 +1531,9 @@
 S: Finland
 
 N: Roger E. Wolff
-E: wolff@dutecai.et.tudelft.nl
+E: R.E.Wolff@BitWizard.nl
 D: Written kmalloc/kfree
+D: Written Specialix IO8+ driver
 S: Oosterstraat 23
 S: 2611 TT Delft
 S: The Netherlands
diff -urN linux-2.0.31-pre4/Documentation/Changes linux-2.0.31-pre5/Documentation/Changes
--- linux-2.0.31-pre4/Documentation/Changes	1996-11-12 22:23:13.000000000 -0800
+++ linux-2.0.31-pre5/Documentation/Changes	2003-08-15 15:03:58.000000000 -0700
@@ -295,7 +295,7 @@
 currently at release 2.5.  Some may find, especially when using the
 loop or xiafs file system, NFS, or automounting, that they need to
 upgrade to the latest release of mount, available from
-ftp://ftp.win.tue.nl/pub/linux/util/mount-2.5p.tar.gz.
+ftp://ftp.win.tue.nl/pub/linux/util/mount-2.6g.tar.gz.
 
 Console
 =======
@@ -679,12 +679,14 @@
 SysVinit utilities
 ==================
 
-ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.64.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/system/daemons/init/sysvinit-2.64.tar.gz
+or for the very latest:
+ftp://ftp.debian.org/debian/unstable/source/base/sysvinit_2.71-1.tar.gz
 
 Util-linux
 ==========
 
-ftp://sunsite.unc.edu/pub/Linux/system/Misc/util-linux-2.5.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/system/misc/util-linux-2.6.tar.gz
 
 Mtools
 ======
diff -urN linux-2.0.31-pre4/Documentation/memory-tuning.txt linux-2.0.31-pre5/Documentation/memory-tuning.txt
--- linux-2.0.31-pre4/Documentation/memory-tuning.txt	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/Documentation/memory-tuning.txt	2003-08-15 15:03:58.000000000 -0700
@@ -0,0 +1,48 @@
+There are several files in /proc/sys/vm you can use to tune the
+memory system with.
+
+You inspect them with 'cat', and set them with 'echo'. For example,
+/proc/sys/vm/freepages:
+
+'# cat /proc/sys/vm/freepages' may yield:
+64      96      128
+
+These three numbers are: min_free_pages, free_pages_low and 
+free_pages_high.
+
+You can adjust these with a command such as:
+
+# echo 128 256 512 > /proc/sys/vm/freepages
+
+Free memory never goes down below min_free_pages except for atomic
+allocation.  Background swapping is started if the number of free
+pages falls below free_pages_high, and intensive swapping is started
+below free_pages_low.  A "page" is 4 kB.
+
+The values selected as boot defaults are the following:  For a
+machine with n>=8 Megabytes of memory, set min_free_pages = n*2,
+free_pages_low = n*3 and free_pages_high = n*4.  Machines with less
+than 8 Megabytes or less as if they had 8 Megabytes.
+
+If "out of memory" errors sometimes occur, or if your machine does lots
+of networking, increasing min_free_pages to 64 or more may be a good
+idea.
+
+free_pages_low should probably be about double of min_free_pages.
+
+After a period of inactivity, the difference between free_pages_high and
+free_pages low is immediately available for any program you want to
+start up, without any need to swap out anything else.  If your memory
+is large enough (e.g. > 16 Meg), keeping 2 or 3 megabytes of memory
+ready for this purpose is probably a good idea.
+
+I've found that
+
+# echo 128 256 1024 > /proc/sys/vm/freepages
+
+gives good performance for a 32 Meg system used as a small server and
+personal workstation.
+
+The other three files in /proc/sys/vm are undocumented, as yet.
+
+Thomas Koenig, ig25@rz.uni-karlsruhe.de
diff -urN linux-2.0.31-pre4/Documentation/networking/alias.txt linux-2.0.31-pre5/Documentation/networking/alias.txt
--- linux-2.0.31-pre4/Documentation/networking/alias.txt	1996-01-03 10:36:23.000000000 -0800
+++ linux-2.0.31-pre5/Documentation/networking/alias.txt	2003-08-15 15:03:58.000000000 -0700
@@ -1,9 +1,11 @@
-NET_ALIAS device aliasing v0.4x
+NET_ALIAS device aliasing v0.5x
 ===============================
   The main step taken in versions 0.40+ is the implementation of a
   device aliasing mechanism that creates *actual* devices.
   This development includes NET_ALIAS (generic aliasing) plus IP_ALIAS
   (specific IP) support.
+  From version 0.50, dynamic configuration of max alias per device and
+  tx/rx stats for aliases added.
 
 Features
 --------
@@ -13,6 +15,10 @@
 o  hashed alias address lookup
 o  net_alias_type objs registration/unreg., module-ables.
 o  /proc/net/aliases & /proc/net/alias_types entries
+o  /proc/sys/net/core/net_alias_max entry (affects hash table size
+   also)
+o  tx/rx stats   
+
 
 o IP alias implementation: static or runtime module.
 
@@ -23,10 +29,14 @@
      # cat /proc/net/alias*
 
   For IP aliasing you must have IP_ALIAS support included by
-  static linking ('y' to 2nd question above), or runtime module
-  insertion ('m' to 2nd q. above):
-     # insmod /usr/src/linux/modules/ip_alias.o (1.3.xx)
-     # insmod /usr/src/ip_alias/ip_alias.o      (1.2.xx) see above.
+  static linking ('y' to CONFIG_IP_ALIAS? question), or runtime module
+  insertion ('m'):
+     # insmod /usr/src/linux/modules/ip_alias.o (2.0.xx) or
+     # modprobe ip_alias.o
+     
+     Also, dynamic loading is supported (kerneld).
+       You should have the following line in /etc/conf.modules:
+       alias net_alias-2 ip_alias
 
 o Alias creation.
   Alias creation is done by 'magic' iface naming: eg. to create a
@@ -49,11 +59,11 @@
   alias device is closed before deletion, so all network stuff that
   points to it (routes, arp entries, ...) will be released.
   		   		   
-Alias (re-)configuring
+o Alias (re-)configuring
   Aliases *are* devices, so you configure and refer to them as usual (ifconfig,
   route, etc).
   
-o Procfs entries
+o PROCfs entries
   2 entries are added to help fetching alias runtime configuration:
   a) /proc/net/alias_types
      Will show you alias_types registered (ie. address families that
@@ -70,7 +80,50 @@
      # cat /proc/net/aliases
      device           family address                                
      eth0:0           2      200.1.1.1
-
+     
+o PROCfs dynamic configuration
+  You can now change the max aliases per device limit via
+  /proc/sys/net/core/net_alias_max entry
+     # cat /proc/sys/net/core/net_alias_max
+     256
+     # echo 1000 > /proc/sys/net/core/net_alias_max
+     # cat /proc/sys/net/core/net_alias_max
+     1000
+     # _
+          
+  With this funcionality you can disable net_alias creation from now on
+     # echo 0 > /proc/sys/net/core/net_alias_max
+
+  The new aliasing limit is considered (grabbed) when creating the
+  FIRST alias for the main device.
+  Eg:
+     # echo 10 > /proc/sys/net/core/net_alias_max
+     # ifconfig eth0:0 xx.xx.xx.xx  (first alias creation for eth0,
+                                     eth0 will 'remember' max==10)
+     # echo 1000 > /proc/sys/net/core/net_alias_max
+     # ifconfig eth0:999 xx.xx.xx.xx
+     SIOCIFSADDR: No such device
+  Of course these semantics can be changed, please let me know.
+  
+  Configuration changes get logged as usual (klogd -> /var/log/messages)
+  
+o Alias devices rx/tx stats
+  Fake rx/tx stats are accounted:
+  - TX
+    When the packet is ``switched'' from logical alias device to
+    physical device, tx counter gets incr.
+  - RX
+    When an incoming packet's address equals alias device's addr it
+    gets ``switched'' from physical to logical device, rx counter gets
+    incr.
+    
+  Please NOTE that for ``same'' network alias devices you usually have
+  one net-route through physical device (eg. eth0), so output pkts
+  will NOT pass down via alias device (so, no tx++ will occur).
+  
+  Also NOTE that currently ifconfig does not handle the ``:'' of alias devices
+  names, a little patch (attached) solves the problem.
+  
 Relationship with main device
 -----------------------------
   - On main device closing, all aliases will be closed and freed.
@@ -78,12 +131,12 @@
     main device (aliases get 'stacked' after main_dev), eg:
       lo->eth0->eth0:0->eth0:2->eth1->0
     If eth0 is unregistered, all it aliases will also be:
-      lo->eth1->0     
+      lo->eth1->0
 
 Contact
 -------
-Please finger or e-mail me:
-   Juan Jose Ciarlante <jjciarla@raiz.uncu.edu.ar>
+Please e-mail me:
+   Juan Jose Ciarlante <irriga@impsat1.com.ar> or <jjciarla@raiz.uncu.edu.ar>
    
 		   		
 ; local variables:
diff -urN linux-2.0.31-pre4/Documentation/networking/ipx.txt linux-2.0.31-pre5/Documentation/networking/ipx.txt
--- linux-2.0.31-pre4/Documentation/networking/ipx.txt	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/Documentation/networking/ipx.txt	2003-08-15 15:03:58.000000000 -0700
@@ -0,0 +1,19 @@
+The IPX support in the Linux kernel has two modes of operation:
+With and without the full internal IPX network. For all normal
+operations, you do not need the full internal IPX network.
+
+The full internal IPX network enables you to allocate sockets on
+different virtual nodes of the internal network. This is done by
+evaluating the field sipx_node of the socket address given to the bind
+call. So applications should always initialize the node field to 0
+when binding a socket on the primary network. In this case the socket
+is assigned the default node that has been given to the kernel when
+the internal network was created.
+By enabling the full internal IPX network the cross-forwarding of
+packets targeted at 'special' sockets to sockets listening on the
+primary network is disabled. This might break existing applications,
+especially RIP/SAP daemons. A RIP/SAP daemon that works well with
+the full internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs.
+
+If you want the full internal network, please uncomment the correspondig
+#define in line 19 of include/net/ipx.h
diff -urN linux-2.0.31-pre4/Documentation/networking/so_bindtodevice.txt linux-2.0.31-pre5/Documentation/networking/so_bindtodevice.txt
--- linux-2.0.31-pre4/Documentation/networking/so_bindtodevice.txt	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/Documentation/networking/so_bindtodevice.txt	2003-08-15 15:03:58.000000000 -0700
@@ -0,0 +1,69 @@
+SO_BINDTODEVICE socket option for Linux 2.0.30+
+by Elliot Poger (elliot@poger.com)
+of Stanford's MosquitoNet project (http://mosquitonet.stanford.edu)
+
+Using the SO_BINDTODEVICE socket option allows your user-level Berkeley
+sockets code to explicitly select which network interface is used for
+both input and output on a per-socket basis.  I originally wrote it to 
+allow the Internet Software Consortium DHCP server 
+(http://www.fugue.com/dhcp/) to run on Linux machines with multiple 
+interfaces.  It has been tested with UDP and TCP sockets.
+
+Usage is as follows:
+
+
+        int skfd;
+        struct ifreq interface;
+
+        skfd = socket(AF_INET, SOCK_DGRAM, 0);
+        strncpy(interface.ifr_ifrn.ifrn_name, "eth1", IFNAMSIZ);
+        if (setsockopt(skfd, SOL_SOCKET, SO_BINDTODEVICE,
+                       (char *)&interface, sizeof(interface)) < 0) {
+                perror("sendpacket: setting SO_BINDTODEVICE");
+                exit(1);
+        }
+
+
+Once the BINDTODEVICE socket option has been set for a socket, as above,
+any data sent over this socket is guaranteed to go out of the "eth1" 
+interface, and any data received through the socket is guaranteed to 
+have arrived on eth1.  If you want to send and receive over multiple
+interfaces, keeping them separate, you can open several sockets and bind
+each one to a different interface with SO_BINDTODEVICE.  (You _can_ call
+BINDTODEVICE more than once for a socket to change the interface it's 
+bound to, but results may be unpredictable because of caching effects
+in the kernel...)
+
+Note that the routing table is still consulted when packets are transmitted.
+Basically, routing proceeds as usual, except that any routes which go
+through a network interface other than the one specified in the BINDTODEVICE
+call are ignored.  If you attempt to send a packet to a certain IP address 
+through an interface which provides no route to that IP address, you'll get
+a "network unreachable" error.  Here is an example of a routing table which
+will allow you to send packets to any IP address through either eth0 or
+eth1:
+
+Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
+171.64.69.0     0.0.0.0         255.255.255.192 U     0      0       37 eth0
+171.64.69.192   0.0.0.0         255.255.255.192 U     0      0      677 eth1
+127.0.0.0       0.0.0.0         255.0.0.0       U     0      0        4 lo
+0.0.0.0         171.64.69.1     0.0.0.0         UG    0      0       45 eth0
+0.0.0.0         171.64.69.193   0.0.0.0         UG    1      0        5 eth1
+
+Note that there are actually TWO default routes.  The routing table is
+searched from top to bottom, so every time you send out a packet, the first
+(uppermost) matching route which the kernel routing function finds which
+matches the destination IP address is used.  In this case, packets sent to
+the IP address 152.2.128.159 will normally be sent through eth0 and gateway
+171.64.69.1; if the socket is bound to the eth1 device, the packets will be
+sent through eth1 and gateway 171.64.69.193; if the socket is bound to some
+other device, you will get a "network unreachable" error. 
+
+By the way, you can add multiple default routes and set the order of
+preference as follows:
+
+route add default gateway 171.64.69.1
+route add default gateway 171.64.69.193 metric 1
+
+Routes with a higher "metric" are put lower in the table and thus have a 
+lower preference.
diff -urN linux-2.0.31-pre4/Documentation/networking/z8530drv.txt linux-2.0.31-pre5/Documentation/networking/z8530drv.txt
--- linux-2.0.31-pre4/Documentation/networking/z8530drv.txt	1996-04-11 23:49:29.000000000 -0700
+++ linux-2.0.31-pre5/Documentation/networking/z8530drv.txt	2003-08-15 15:03:58.000000000 -0700
@@ -1,18 +1,8 @@
 This is a subset of the documentation. To use this driver you MUST have the
-full package from:
-
-Internet:
-=========
-
-ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.0.dl1bke.real.tar.gz
-
-[
-  if you can't find it there, try:
-  .../tcpip/linux/z8530drv-2.0.dl1bke.tar.gz
-
-]
-
-and various mirrors (i.e. nic.switch.ch)
+full package "z8530drv-2.4c.dl1bke.tar.gz" from either ftp.pspt.fi, 
+sunsite.unc.edu or db0bm.automation.fh-aachen.de. Do not try to use the
+utilities from z8530drv-utils-3.0 as they will not work with the 2.4 series
+of the driver!
 
 ---------------------------------------------------------------------------
 
@@ -21,7 +11,7 @@
 
    ********************************************************************
 
-        (c) 1993,1995 by Joerg Reuter DL1BKE
+        (c) 1993,1997 by Joerg Reuter DL1BKE
 
         portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -787,5 +777,5 @@
 in the Linux standard distribution and their support.
 
 Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@lykos.tng.oche.de
+		WWW     : http://www.rat.de/jr
+		Internet: jreuter@poboxes.com
diff -urN linux-2.0.31-pre4/Documentation/specialix.txt linux-2.0.31-pre5/Documentation/specialix.txt
--- linux-2.0.31-pre4/Documentation/specialix.txt	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/Documentation/specialix.txt	2003-08-15 15:03:58.000000000 -0700
@@ -0,0 +1,334 @@
+
+      specialix.txt  -- specialix IO8+ multiport serial driver readme.
+
+
+
+      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
+
+      Specialix pays for the development and support of this driver.
+      Please DO contact io8-linux@specialix.co.uk if you require
+      support.
+
+      This driver was developed in the BitWizard linux device
+      driver service. If you require a linux device driver for your
+      product, please contact devices@BitWizard.nl for a quote.
+
+      This code is firmly based on the riscom/8 serial driver,
+      written by Dmitry Gorodchanin. The specialix IO8+ card
+      programming information was obtained from the CL-CD1865 Data
+      Book, and Specialix document number 6200059: IO8+ Hardware
+      Functional Specification.
+
+      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.
+
+      This program is distributed in the hope that it will be
+      useful, but WITHOUT ANY WARRANTY; without even the implied
+      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+      PURPOSE.  See the GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public
+      License along with this program; if not, write to the Free
+      Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+      USA.
+
+
+Intro
+=====
+
+ 
+This file contains some random information, that I like to have online
+instead of in a manual that can get lost. Ever misplace your Linux
+kernel sources?  And the manual of one of the boards in your computer?
+
+
+Adresses and interrupts
+=======================
+
+Addres dip switch settings:
+The dip switch sets bits 2-9 of the IO address. 
+
+       9 8 7 6 5 4 3 2 
+     +-----------------+
+   0 | X   X X X X X X |
+     |                 |    =   IoBase = 0x100 
+   1 |   X             |
+     +-----------------+          ------ RS232 connectors ---->
+         
+         |    |    |
+       edge connector
+         |    |    |
+         V    V    V
+
+Base address 0x100 caused a conflict in one of my computers once.  I
+haven't the foggiest why. My Specialix card is now at 0x180.  My
+other computer runs just fine with the Specialix card at 0x100....
+The card occupies 4 addresses, but actually only two are really used.
+
+The driver now still autoprobes at 0x100, 0x180, 0x250 and 0x260.  If
+that causes trouble for you, please report that. I'll remove
+autoprobing then.
+
+The driver will tell the card what IRQ to use, so you don't have to
+change any jumpers to change the IRQ. Just use a command line
+argument (irq=xx) to the insmod program to set the interrupt.
+
+If your specialix cards are not at the default locations, you can use
+the kernel command line argument "specialix=io0,irq0,io1,irq1...".
+Here "io0" is the io address for the first card, and "irq0" is the
+irq line that the first card should use. And so on. 
+
+Examples. 
+
+You use the driver as a module and have three cards at 0x100, 0x250
+and 0x180. And some way or another you want them detected in that
+order. Moreover irq 12 is taken (e.g. by your PS/2 mouse).
+
+  insmod specialix.o iobase=0x100,0x250,0x180 irq=9,11,15
+
+The same three cards, but now in the kernel would require you to
+add 
+
+   specialix=0x100,9,0x250,11,0x180,15
+
+to the command line. This would become 
+
+   append="specialix=0x100,9,0x250,11,0x180,15" 
+
+in your /etc/lilo.conf file if you use lilo. 
+
+
+Baud rates
+==========
+
+The rev 1.2 and below boards use a CL-CD1864. These chips can only 
+do 64kbit. The rev 1.3 and newer boards use a CL-CD1865. These chips
+are officially capable of 115k2.
+
+The Specialix card uses a 25MHz crystal (in times two mode, which in
+fact is a divided by two mode). This is not enough to reach the rated
+115k2 on all ports at the same time. With this clock rate you can only
+do 37% of this rate. This means that at 115k2 on all ports you are
+going to loose characters (The chip cannot handle that many incoming
+bits at this clock rate.) (Yes, you read that correctly: there is a
+limit to the number of -=bits=- per second that the chip can handle.)
+
+If you near the "limit" you will first start to see a graceful
+degradation in that the chip cannot keep the transmitter busy at all
+times. However with a central clock this slow, you can also get it to
+miss incoming characters.
+
+The specialix card cannot reliably do 115k2. If you use it, you have
+to do "extensive testing" (*) to verify if it actually works.
+
+When "mgetty" communicates with my modem at 115k2 it reports:
+got: +++[0d]ATQ0V1H0[0d][0d][8a]O[cb][0d][8a]
+                            ^^^^ ^^^^ ^^^^ 
+
+The three characters that have the "^^^" under them have suffered a
+bit error in the highest bit. In conclusion: I've tested it, and found
+that it simply DOESN"T work for me. I also suspect that this is also
+caused by the baud rate being just a little bit out of tune. 
+
+
+(*) Cirrus logic CD1864 databook, page 40.
+
+
+Cables for the Specialix IO8+
+=============================
+
+The pinout of the connectors on the IO8+ is:
+
+     pin    short    direction    long name
+            name
+    Pin 1   DCD      input        Data Carrier Detect
+    Pin 2   RXD      input        Receive
+    Pin 3   DTR/RTS  output       Data Terminal Ready/Ready To Send
+    Pin 4   GND      -            Ground
+    Pin 5   TXD      output       Transmit
+    Pin 6   CTS      input        Clear To Send
+        
+    
+             -- 6  5  4  3  2  1 --
+             |                    |
+             |                    |
+             |                    |
+             |                    |
+             +-----          -----+
+                  |__________|
+                      clip
+    
+    Front view of an RJ12 connector. Cable moves "into" the paper.
+    (the plug is ready to plug into your mouth this way...)
+
+    
+    NULL cable. I don't know who is going to use these except for
+    testing purposes, but I tested the cards with this cable. (It 
+    took quite a while to figure out, so I'm not going to delete
+    it. So there! :-)
+    
+    
+    This end goes               This end needs
+    straight into the           some twists in
+    RJ12 plug.                  the wiring.
+       IO8+ RJ12                   IO8+ RJ12
+        1  DCD       white          -
+        -             -             1 DCD
+        2  RXD       black          5 TXD
+        3  DTR/RTS   red            6 CTS
+        4  GND       green          4 GND
+        5  TXD       yellow         2 RXD
+        6  CTS       blue           3 DTR/RTS
+    
+
+    Same NULL cable, but now sorted on the second column.
+ 
+        1  DCD       white          -
+        -             -             1 DCD
+        5  TXD       yellow         2 RXD
+        6  CTS       blue           3 DTR/RTS
+        4  GND       green          4 GND
+        2  RXD       black          5 TXD
+        3  DTR/RTS   red            6 CTS
+    
+    
+    
+    This is a modem cable usable for hardware handshaking:
+        RJ12                        DB25           DB9
+        1   DCD      white          8 DCD          1 DCD
+        2   RXD      black          3 RXD          2 RXD
+        3   DTR/RTS  red            4 RTS          7 RTS
+        4   GND      green          7 GND          5 GND
+        5   TXD      yellow         2 TXD          3 TXD
+        6   CTS      blue           5 CTS          8 CTS
+                            +----   6 DSR          6 DSR
+                            +----  20 DTR          4 DTR
+
+    This is a modem cable usable for software handshaking:
+    It allows you to reset the modem using the DTR ioctls.
+    I (REW) have never tested this, "but xxxxxxxxxxxxx
+    says that it works." If you test this, please
+    tell me and I'll fill in your name on the xxx's.
+
+        RJ12                        DB25           DB9
+        1   DCD      white          8 DCD          1 DCD
+        2   RXD      black          3 RXD          2 RXD
+        3   DTR/RTS  red           20 DTR          4 DTR
+        4   GND      green          7 GND          5 GND
+        5   TXD      yellow         2 TXD          3 TXD
+        6   CTS      blue           5 CTS          8 CTS
+                            +----   6 DSR          6 DSR
+                            +----   4 RTS          7 RTS
+
+   I bought a 6 wire flat cable. It was colored as indicated.
+   Check that yours is the same before you trust me on this.
+   
+ 
+Hardware handshaking issues.
+============================
+
+The driver can be compiled in two different ways. The default
+("Specialix DTR/RTS pin is RTS" is off) the pin behaves as DTR when
+hardware handshaking is off. It behaves as the RTS hardware
+handshaking signal when hardware handshaking is selected.
+
+When you use this, you have to use the appropriate cable. The
+cable will either be compatible with hardware handshaking or with
+software handshaking. So switching on the fly is not really an
+option.
+
+I actually prefer to use the "Specialix DTR/RTS pin is RTS" option.
+This makes the DTR/RTS pin always an RTS pin, and ioctls to
+change DTR are always ignored. I have a cable that is configured
+for this. 
+
+
+Ports and devices
+=================
+
+Port 0 is the one furthest from the ISA connector.
+
+Devices:
+
+You should make the devices as follows:
+
+bash
+cd /dev
+for i in  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 \
+         16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+do
+  echo -n "$i "
+  mknod /dev/ttyW$i c 75 $i
+  mknod /dev/cuw$i c 76 $i
+done
+echo ""
+
+
+You cannot have more than 4 boards in one computer. The card only
+supports 4 different interrupts. If you really want this, contact me
+about this and I'll give you a few tips (requires soldering iron)....
+
+
+------------------------------------------------------------------------
+
+
+  Fixed bugs and restrictions:
+       - During intialization, interrupts are blindly turned on.
+            Having a shadow variable would cause an extra memory
+            access on every IO instruction. 
+       - The interrupt (on the card) should be disabled when we
+         don't allocate the Linux end of the interrupt. This allows 
+         a different driver/card to use it while all ports are not in
+         use..... (a la standard serial port)
+       == An extra _off variant of the sx_in and sx_out macros are
+          now available. They don't set the interrupt enable bit.
+          These are used during initialization. Normal operation uses
+          the old variant which enables the interrupt line.
+       - RTS/DTR issue needs to be implemented according to 
+         specialix' spec.
+            I kind of like the "determinism" of the current 
+            implementation. Compile time flag?
+       == Ok. Compile time flag! Default is how Specialix likes it.
+       == Now a config time flag! Gets saved in your config file. Neat!
+       - Can you set the IO address from the lilo command line?
+            If you need this, bug me about it, I'll make it. 
+       == Hah! No bugging needed. Fixed! :-)
+       - Cirrus logic hasn't gotten back to me yet why the CD1865 can
+            and the CD1864 can't do 115k2. I suspect that this is
+            because the CD1864 is not rated for 33MHz operation.
+            Therefore the CD1864 versions of the card can't do 115k2 on 
+            all ports just like the CD1865 versions. The driver does
+            not block 115k2 on CD1864 cards. 
+        == I called the Cirrus Logic representative here in Holland.
+           The CD1864 databook is identical to the CD1865 databook, 
+           except for an extra warning at the end. Similar Bit errors
+           have been observed in testing at 115k2 on both an 1865 and
+           a 1864 chip. I see no reason why I would prohibit 115k2 on
+           1864 chips and not do it on 1865 chips. Actually there is
+           reason to prohibit it on BOTH chips. I print a warning.
+           If you use 115k2, you're on your own. 
+       - A spiky CD may send spurious HUPs. Also in CLOCAL???
+         -- A fix for this turned out to be counter productive. 
+            Different fix? Current behaviour is acceptable?
+         -- Maybe the current implementation is correct. If anybody
+            gets bitten by this, please report, and it will get fixed.
+
+         -- Testing revealed that when in CLOCAL, the problem doesn't
+            occur. As warned for in the CD1865 manual, the chip may
+            send modem intr's on a spike. We could filter those out,
+            but that would be a cludge anyway (You'd still risk getting
+            a spurious HUP when two spikes occur.).....
+ 
+
+
+  Bugs & restrictions:
+       - This is a difficult card to autoprobe.
+            You have to WRITE to the address register to even 
+            read-probe a CD186x register. Disable autodetection?
+         -- Specialix: any suggestions?
+       - Arbitrary baud rates are not implemented yet. 
+            If you need this, bug me about it. 
+
+
diff -urN linux-2.0.31-pre4/drivers/block/loop.c linux-2.0.31-pre5/drivers/block/loop.c
--- linux-2.0.31-pre4/drivers/block/loop.c	1996-07-15 03:47:39.000000000 -0700
+++ linux-2.0.31-pre5/drivers/block/loop.c	2003-08-15 15:03:58.000000000 -0700
@@ -12,6 +12,8 @@
  * Modularized and updated for 1.1.16 kernel - Mitch Dsouza 28th May 1994
  *
  * Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996
+ *
+ * Fixed do_loop_request() re-entrancy - <Vincent.Renardias@waw.com> Mar 20, 1997
  */
 
 #include <linux/module.h>
@@ -181,12 +183,15 @@
 	char	*dest_addr;
 	struct loop_device *lo;
 	struct buffer_head *bh;
+	struct request *current_request;
 
 repeat:
 	INIT_REQUEST;
-	if (MINOR(CURRENT->rq_dev) >= MAX_LOOP)
+	current_request=CURRENT;
+	CURRENT=current_request->next;
+	if (MINOR(current_request->rq_dev) >= MAX_LOOP)
 		goto error_out;
-	lo = &loop_dev[MINOR(CURRENT->rq_dev)];
+	lo = &loop_dev[MINOR(current_request->rq_dev)];
 	if (!lo->lo_inode || !lo->transfer)
 		goto error_out;
 
@@ -197,14 +202,14 @@
 	      blksize = BLOCK_SIZE;
 	}
 
-	dest_addr = CURRENT->buffer;
+	dest_addr = current_request->buffer;
 	
 	if (blksize < 512) {
-		block = CURRENT->sector * (512/blksize);
+		block = current_request->sector * (512/blksize);
 		offset = 0;
 	} else {
-		block = CURRENT->sector / (blksize >> 9);
-		offset = (CURRENT->sector % (blksize >> 9)) << 9;
+		block = current_request->sector / (blksize >> 9);
+		offset = (current_request->sector % (blksize >> 9)) << 9;
 	}
 	block += lo->lo_offset / blksize;
 	offset += lo->lo_offset % blksize;
@@ -212,13 +217,13 @@
 		block++;
 		offset -= blksize;
 	}
-	len = CURRENT->current_nr_sectors << 9;
+	len = current_request->current_nr_sectors << 9;
 
-	if (CURRENT->cmd == WRITE) {
+	if (current_request->cmd == WRITE) {
 		if (lo->lo_flags & LO_FLAGS_READ_ONLY)
 			goto error_out;
-	} else if (CURRENT->cmd != READ) {
-		printk("unknown loop device command (%d)?!?", CURRENT->cmd);
+	} else if (current_request->cmd != READ) {
+		printk("unknown loop device command (%d)?!?", current_request->cmd);
 		goto error_out;
 	}
 	while (len > 0) {
@@ -237,7 +242,7 @@
 			       block, blksize);
 			goto error_out;
 		}
-		if (!buffer_uptodate(bh) && ((CURRENT->cmd == READ) ||
+		if (!buffer_uptodate(bh) && ((current_request->cmd == READ) ||
 					(offset || (len < blksize)))) {
 			ll_rw_block(READ, 1, &bh);
 			wait_on_buffer(bh);
@@ -250,13 +255,13 @@
 		if (size > len)
 			size = len;
 			   
-		if ((lo->transfer)(lo, CURRENT->cmd, bh->b_data + offset,
+		if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset,
 				   dest_addr, size)) {
 			printk("loop: transfer error block %d\n", block);
 			brelse(bh);
 			goto error_out;
 		}
-		if (CURRENT->cmd == WRITE) {
+		if (current_request->cmd == WRITE) {
 			mark_buffer_uptodate(bh, 1);
 			mark_buffer_dirty(bh, 1);
 		}
@@ -266,9 +271,13 @@
 		offset = 0;
 		block++;
 	}
+	current_request->next=CURRENT;
+	CURRENT=current_request;
 	end_request(1);
 	goto repeat;
 error_out:
+    current_request->next=CURRENT;
+	CURRENT=current_request;
 	end_request(0);
 	goto repeat;
 }
diff -urN linux-2.0.31-pre4/drivers/char/cd1865.h linux-2.0.31-pre5/drivers/char/cd1865.h
--- linux-2.0.31-pre4/drivers/char/cd1865.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/cd1865.h	2003-08-15 15:03:58.000000000 -0700
@@ -0,0 +1,263 @@
+/*
+ *      linux/drivers/char/cd1865.h -- Definitions relating to the CD1865
+ *                          for the Specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ */
+
+/*
+ * Definitions for Driving CD180/CD1864/CD1865 based eightport serial cards.
+ */
+
+
+/* Values of choice for Interrupt ACKs */
+/* These values are "obligatory" if you use the register based
+ * interrupt acknowledgements. See page 99-101 of V2.0 of the CD1865
+ * databook */
+#define SX_ACK_MINT     0x75    /* goes to PILR1                           */
+#define SX_ACK_TINT     0x76    /* goes to PILR2                           */
+#define SX_ACK_RINT     0x77    /* goes to PILR3                           */
+
+/* Chip ID (is used when chips ar daisy chained.) */
+#define SX_ID           0x10
+
+/* Definitions for Cirrus Logic CL-CD186x 8-port async mux chip */
+ 
+#define CD186x_NCH       8       /* Total number of channels                */
+#define CD186x_TPC       16      /* Ticks per character                     */
+#define CD186x_NFIFO	 8	 /* TX FIFO size                            */
+
+
+/* Global registers */
+
+#define CD186x_GIVR      0x40    /* Global Interrupt Vector Register        */
+#define CD186x_GICR      0x41    /* Global Interrupting Channel Register    */
+#define CD186x_PILR1     0x61    /* Priority Interrupt Level Register 1     */
+#define CD186x_PILR2     0x62    /* Priority Interrupt Level Register 2     */
+#define CD186x_PILR3     0x63    /* Priority Interrupt Level Register 3     */
+#define CD186x_CAR       0x64    /* Channel Access Register                 */
+#define CD186x_SRSR      0x65    /* Channel Access Register                 */
+#define CD186x_GFRCR     0x6b    /* Global Firmware Revision Code Register  */
+#define CD186x_PPRH      0x70    /* Prescaler Period Register High          */
+#define CD186x_PPRL      0x71    /* Prescaler Period Register Low           */
+#define CD186x_RDR       0x78    /* Receiver Data Register                  */
+#define CD186x_RCSR      0x7a    /* Receiver Character Status Register      */
+#define CD186x_TDR       0x7b    /* Transmit Data Register                  */
+#define CD186x_EOIR      0x7f    /* End of Interrupt Register               */
+#define CD186x_MRAR      0x75    /* Modem Request Acknowlege register       */
+#define CD186x_TRAR      0x76    /* Transmit Request Acknowlege register    */
+#define CD186x_RRAR      0x77    /* Recieve Request Acknowlege register     */
+#define CD186x_SRCR      0x66    /* Service Request Configuration register  */
+
+/* Channel Registers */
+
+#define CD186x_CCR       0x01    /* Channel Command Register                */
+#define CD186x_IER       0x02    /* Interrupt Enable Register               */
+#define CD186x_COR1      0x03    /* Channel Option Register 1               */
+#define CD186x_COR2      0x04    /* Channel Option Register 2               */
+#define CD186x_COR3      0x05    /* Channel Option Register 3               */
+#define CD186x_CCSR      0x06    /* Channel Control Status Register         */
+#define CD186x_RDCR      0x07    /* Receive Data Count Register             */
+#define CD186x_SCHR1     0x09    /* Special Character Register 1            */
+#define CD186x_SCHR2     0x0a    /* Special Character Register 2            */
+#define CD186x_SCHR3     0x0b    /* Special Character Register 3            */
+#define CD186x_SCHR4     0x0c    /* Special Character Register 4            */
+#define CD186x_MCOR1     0x10    /* Modem Change Option 1 Register          */
+#define CD186x_MCOR2     0x11    /* Modem Change Option 2 Register          */
+#define CD186x_MCR       0x12    /* Modem Change Register                   */
+#define CD186x_RTPR      0x18    /* Receive Timeout Period Register         */
+#define CD186x_MSVR      0x28    /* Modem Signal Value Register             */
+#define CD186x_MSVRTS    0x29    /* Modem Signal Value Register             */
+#define CD186x_MSVDTR    0x2a    /* Modem Signal Value Register             */
+#define CD186x_RBPRH     0x31    /* Receive Baud Rate Period Register High  */
+#define CD186x_RBPRL     0x32    /* Receive Baud Rate Period Register Low   */
+#define CD186x_TBPRH     0x39    /* Transmit Baud Rate Period Register High */
+#define CD186x_TBPRL     0x3a    /* Transmit Baud Rate Period Register Low  */
+
+
+/* Global Interrupt Vector Register (R/W) */
+
+#define GIVR_ITMASK     0x07     /* Interrupt type mask                     */
+#define  GIVR_IT_MODEM   0x01    /* Modem Signal Change Interrupt           */
+#define  GIVR_IT_TX      0x02    /* Transmit Data Interrupt                 */
+#define  GIVR_IT_RCV     0x03    /* Receive Good Data Interrupt             */
+#define  GIVR_IT_REXC    0x07    /* Receive Exception Interrupt             */
+
+
+/* Global Interrupt Channel Register (R/W) */
+ 
+#define GICR_CHAN       0x1c    /* Channel Number Mask                     */
+#define GICR_CHAN_OFF   2       /* Channel Number shift                    */
+
+
+/* Channel Address Register (R/W) */
+
+#define CAR_CHAN        0x07    /* Channel Number Mask                     */
+#define CAR_A7          0x08    /* A7 Address Extension (unused)           */
+
+
+/* Receive Character Status Register (R/O) */
+
+#define RCSR_TOUT       0x80    /* Rx Timeout                              */
+#define RCSR_SCDET      0x70    /* Special Character Detected Mask         */
+#define  RCSR_NO_SC      0x00   /* No Special Characters Detected          */
+#define  RCSR_SC_1       0x10   /* Special Char 1 (or 1 & 3) Detected      */
+#define  RCSR_SC_2       0x20   /* Special Char 2 (or 2 & 4) Detected      */
+#define  RCSR_SC_3       0x30   /* Special Char 3 Detected                 */
+#define  RCSR_SC_4       0x40   /* Special Char 4 Detected                 */
+#define RCSR_BREAK      0x08    /* Break has been detected                 */
+#define RCSR_PE         0x04    /* Parity Error                            */
+#define RCSR_FE         0x02    /* Frame Error                             */
+#define RCSR_OE         0x01    /* Overrun Error                           */
+
+
+/* Channel Command Register (R/W) (commands in groups can be OR-ed) */
+
+#define CCR_HARDRESET   0x81    /* Reset the chip                          */
+
+#define CCR_SOFTRESET   0x80    /* Soft Channel Reset                      */
+
+#define CCR_CORCHG1     0x42    /* Channel Option Register 1 Changed       */
+#define CCR_CORCHG2     0x44    /* Channel Option Register 2 Changed       */
+#define CCR_CORCHG3     0x48    /* Channel Option Register 3 Changed       */
+
+#define CCR_SSCH1       0x21    /* Send Special Character 1                */
+
+#define CCR_SSCH2       0x22    /* Send Special Character 2                */
+
+#define CCR_SSCH3       0x23    /* Send Special Character 3                */
+
+#define CCR_SSCH4       0x24    /* Send Special Character 4                */
+
+#define CCR_TXEN        0x18    /* Enable Transmitter                      */
+#define CCR_RXEN        0x12    /* Enable Receiver                         */
+
+#define CCR_TXDIS       0x14    /* Disable Transmitter                     */
+#define CCR_RXDIS       0x11    /* Disable Receiver                        */
+
+
+/* Interrupt Enable Register (R/W) */
+
+#define IER_DSR         0x80    /* Enable interrupt on DSR change          */
+#define IER_CD          0x40    /* Enable interrupt on CD change           */
+#define IER_CTS         0x20    /* Enable interrupt on CTS change          */
+#define IER_RXD         0x10    /* Enable interrupt on Receive Data        */
+#define IER_RXSC        0x08    /* Enable interrupt on Receive Spec. Char  */
+#define IER_TXRDY       0x04    /* Enable interrupt on TX FIFO empty       */
+#define IER_TXEMPTY     0x02    /* Enable interrupt on TX completely empty */
+#define IER_RET         0x01    /* Enable interrupt on RX Exc. Timeout     */
+
+
+/* Channel Option Register 1 (R/W) */
+
+#define COR1_ODDP       0x80    /* Odd Parity                              */
+#define COR1_PARMODE    0x60    /* Parity Mode mask                        */
+#define  COR1_NOPAR      0x00   /* No Parity                               */
+#define  COR1_FORCEPAR   0x20   /* Force Parity                            */
+#define  COR1_NORMPAR    0x40   /* Normal Parity                           */
+#define COR1_IGNORE     0x10    /* Ignore Parity on RX                     */
+#define COR1_STOPBITS   0x0c    /* Number of Stop Bits                     */
+#define  COR1_1SB        0x00   /* 1 Stop Bit                              */
+#define  COR1_15SB       0x04   /* 1.5 Stop Bits                           */
+#define  COR1_2SB        0x08   /* 2 Stop Bits                             */
+#define COR1_CHARLEN    0x03    /* Character Length                        */
+#define  COR1_5BITS      0x00   /* 5 bits                                  */
+#define  COR1_6BITS      0x01   /* 6 bits                                  */
+#define  COR1_7BITS      0x02   /* 7 bits                                  */
+#define  COR1_8BITS      0x03   /* 8 bits                                  */
+
+
+/* Channel Option Register 2 (R/W) */
+
+#define COR2_IXM        0x80    /* Implied XON mode                        */
+#define COR2_TXIBE      0x40    /* Enable In-Band (XON/XOFF) Flow Control  */
+#define COR2_ETC        0x20    /* Embedded Tx Commands Enable             */
+#define COR2_LLM        0x10    /* Local Loopback Mode                     */
+#define COR2_RLM        0x08    /* Remote Loopback Mode                    */
+#define COR2_RTSAO      0x04    /* RTS Automatic Output Enable             */
+#define COR2_CTSAE      0x02    /* CTS Automatic Enable                    */
+#define COR2_DSRAE      0x01    /* DSR Automatic Enable                    */
+
+
+/* Channel Option Register 3 (R/W) */
+
+#define COR3_XONCH      0x80    /* XON is a pair of characters (1 & 3)     */
+#define COR3_XOFFCH     0x40    /* XOFF is a pair of characters (2 & 4)    */
+#define COR3_FCT        0x20    /* Flow-Control Transparency Mode          */
+#define COR3_SCDE       0x10    /* Special Character Detection Enable      */
+#define COR3_RXTH       0x0f    /* RX FIFO Threshold value (1-8)           */
+
+
+/* Channel Control Status Register (R/O) */
+
+#define CCSR_RXEN       0x80    /* Receiver Enabled                        */
+#define CCSR_RXFLOFF    0x40    /* Receive Flow Off (XOFF was sent)        */
+#define CCSR_RXFLON     0x20    /* Receive Flow On (XON was sent)          */
+#define CCSR_TXEN       0x08    /* Transmitter Enabled                     */
+#define CCSR_TXFLOFF    0x04    /* Transmit Flow Off (got XOFF)            */
+#define CCSR_TXFLON     0x02    /* Transmit Flow On (got XON)              */
+
+
+/* Modem Change Option Register 1 (R/W) */
+
+#define MCOR1_DSRZD     0x80    /* Detect 0->1 transition of DSR           */
+#define MCOR1_CDZD      0x40    /* Detect 0->1 transition of CD            */
+#define MCOR1_CTSZD     0x20    /* Detect 0->1 transition of CTS           */
+#define MCOR1_DTRTH     0x0f    /* Auto DTR flow control Threshold (1-8)   */
+#define  MCOR1_NODTRFC   0x0     /* Automatic DTR flow control disabled     */
+
+
+/* Modem Change Option Register 2 (R/W) */
+
+#define MCOR2_DSROD     0x80    /* Detect 1->0 transition of DSR           */
+#define MCOR2_CDOD      0x40    /* Detect 1->0 transition of CD            */
+#define MCOR2_CTSOD     0x20    /* Detect 1->0 transition of CTS           */
+
+/* Modem Change Register (R/W) */
+
+#define MCR_DSRCHG      0x80    /* DSR Changed                             */
+#define MCR_CDCHG       0x40    /* CD Changed                              */
+#define MCR_CTSCHG      0x20    /* CTS Changed                             */
+
+
+/* Modem Signal Value Register (R/W) */
+
+#define MSVR_DSR        0x80    /* Current state of DSR input              */
+#define MSVR_CD         0x40    /* Current state of CD input               */
+#define MSVR_CTS        0x20    /* Current state of CTS input              */
+#define MSVR_DTR        0x02    /* Current state of DTR output             */
+#define MSVR_RTS        0x01    /* Current state of RTS output             */
+
+
+/* Escape characters */
+
+#define CD186x_C_ESC     0x00    /* Escape character                        */
+#define CD186x_C_SBRK    0x81    /* Start sending BREAK                     */
+#define CD186x_C_DELAY   0x82    /* Delay output                            */
+#define CD186x_C_EBRK    0x83    /* Stop sending BREAK                      */
+
+#define SRSR_RREQint     0x10    /* This chip wants "rec" serviced          */
+#define SRSR_TREQint     0x04    /* This chip wants "transmit" serviced     */
+#define SRSR_MREQint     0x01    /* This chip wants "mdm change" serviced   */
+
+
+
+#define SRCR_PKGTYPE    0x80
+#define SRCR_REGACKEN   0x40
+#define SRCR_DAISYEN    0x20
+#define SRCR_GLOBPRI    0x10
+#define SRCR_UNFAIR     0x08
+#define SRCR_AUTOPRI    0x02
+#define SRCR_PRISEL     0x01
+
+
diff -urN linux-2.0.31-pre4/drivers/char/Config.in linux-2.0.31-pre5/drivers/char/Config.in
--- linux-2.0.31-pre4/drivers/char/Config.in	1997-04-08 08:47:45.000000000 -0700
+++ linux-2.0.31-pre5/drivers/char/Config.in	2003-08-15 15:03:58.000000000 -0700
@@ -14,6 +14,10 @@
 fi
 tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
 tristate 'Parallel printer support' CONFIG_PRINTER
+tristate 'Specialix IO8+ card support' CONFIG_SPECIALIX
+if [ "$CONFIG_SPECIALIX" = "y" -o  "$CONFIG_SPECIALIX" = "m" ]; then
+  bool 'Specialix DTR/RTS pin is RTS' CONFIG_SPECIALIX_RTSCTS
+fi 
 
 
 bool 'Mouse Support (not serial mice)' CONFIG_MOUSE
diff -urN linux-2.0.31-pre4/drivers/char/console.c linux-2.0.31-pre5/drivers/char/console.c
--- linux-2.0.31-pre4/drivers/char/console.c	1996-11-07 01:25:21.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/console.c	2003-08-15 15:03:58.000000000 -0700
@@ -2320,3 +2320,22 @@
 {
 	return set_get_font (arg,0,video_mode_512ch);
 }
+
+/*
+ * Report the current status of the vc. This is exported to modules (ARub)
+ */
+
+int con_get_info(int *mode, int *shift, int *col, int *row,
+			struct tty_struct **tty)
+{
+	extern int shift_state;
+	extern struct tty_driver console_driver;
+	struct tty_struct **console_table=console_driver.table;
+
+	if (mode) *mode = vt_cons[fg_console]->vc_mode;
+	if (shift) *shift = shift_state;
+	if (col) *col = video_num_columns;
+	if (row) *row = video_num_lines;
+	if (tty) *tty = console_table[fg_console];
+	return fg_console;
+}
diff -urN linux-2.0.31-pre4/drivers/char/Makefile linux-2.0.31-pre5/drivers/char/Makefile
--- linux-2.0.31-pre4/drivers/char/Makefile	1996-10-31 04:31:41.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/Makefile	2003-08-15 15:03:58.000000000 -0700
@@ -78,6 +78,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SPECIALIX),y)
+L_OBJS += specialix.o
+else
+  ifeq ($(CONFIG_SPECIALIX),m)
+  M_OBJS += specialix.o
+  endif
+endif
+
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
 M = y
 L_OBJS += atixlmouse.o
diff -urN linux-2.0.31-pre4/drivers/char/misc.c linux-2.0.31-pre5/drivers/char/misc.c
--- linux-2.0.31-pre4/drivers/char/misc.c	1996-11-06 04:39:42.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/misc.c	2003-08-15 15:03:58.000000000 -0700
@@ -57,7 +57,7 @@
 /*
  * Assigned numbers, used for dynamic minors
  */
-#define DYNAMIC_MINORS 64 /* like dynamic majors */
+#define DYNAMIC_MINORS 64 /* like dynamic majors used to do */
 static unsigned char misc_minors[DYNAMIC_MINORS / 8];
 
 #ifndef MODULE
@@ -70,6 +70,8 @@
 extern void wdt_init(void);
 extern void pcwatchdog_init(void);
 extern int rtc_init(void);
+extern int con_get_info(int *mode, int *shift, int *col, int *row,
+			struct tty_struct **tty);
 
 #ifdef CONFIG_PROC_FS
 static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused)
@@ -181,6 +183,7 @@
 #ifndef MODULE
 	X(set_selection),   /* used by the kmouse module, can only */
 	X(paste_selection), /* be exported if misc.c is in linked in */
+	X(con_get_info),
 #endif
 #include <linux/symtab_end.h>
 };
diff -urN linux-2.0.31-pre4/drivers/char/README.scc linux-2.0.31-pre5/drivers/char/README.scc
--- linux-2.0.31-pre4/drivers/char/README.scc	1995-12-18 21:09:00.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/README.scc	2003-08-15 15:03:58.000000000 -0700
@@ -1,24 +1,14 @@
-
-You will find subset of the documentation in
+You will find a subset of the documentation in
 
                  linux/Documentation/networking/z8530drv.txt
 
+To use this driver you MUST have the full package "z8530drv-2.4c.dl1bke.tar.gz"
+from either ftp.pspt.fi, sunsite.unc.edu or db0bm.automation.fh-aachen.de. 
+The package includes the utilities necessary to initialize and control the driver.
 
-To use this driver you MUST have the full package from
-
-ftp.ucsd.edu:/hamradio/packet/tcpip/incoming/z8530drv-2.01.dl1bke.real.tar.gz
-
-[
-  if you can't find it there, try:
-  .../tcpip/linux/z8530drv-2.01.dl1bke.tar.gz
-
-]
-
-and various mirrors (i.e. nic.switch.ch)
-
-The package includes the utilities necessary to initialize and
-control the driver.
+Do not try to use the utilities from z8530drv-utils-3.0 as they will not work
+with the 2.4 series of the driver!
 
 Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@lykos.tng.oche.de
+		WWW     : http://www.rat.de/jr
+		Internet: jreuter@poboxes.com
diff -urN linux-2.0.31-pre4/drivers/char/scc.c linux-2.0.31-pre5/drivers/char/scc.c
--- linux-2.0.31-pre4/drivers/char/scc.c	1996-04-11 23:49:35.000000000 -0700
+++ linux-2.0.31-pre5/drivers/char/scc.c	2003-08-15 15:03:58.000000000 -0700
@@ -1,6 +1,6 @@
-#define RCS_ID "$Id: scc.c,v 1.41 1995/12/17 22:36:40 jreuter Exp jreuter $"
+#define RCS_ID "$Id: scc.c,v 1.67 1997/04/10 15:24:41 jreuter Exp jreuter $"
 
-#define BANNER "Z8530 SCC driver version 2.01.dl1bke (alpha) by DL1BKE\n"
+#define BANNER "Z8530 SCC driver version 2.4c.dl1bke (experimental) by DL1BKE\n"
 
 /*
 
@@ -11,7 +11,7 @@
 
    ********************************************************************
 
-	Copyright (c) 1993, 1995 Joerg Reuter DL1BKE
+	Copyright (c) 1993, 1997 Joerg Reuter DL1BKE
 
 	portions (c) 1993 Guido ten Dolle PE1NNZ
 
@@ -48,16 +48,6 @@
    
    ******************************************************************** 
 
-
-   ...If you find any portions of the code that are copyrighted to you,
-   and you don't want to see in here, please send me a private (!) 
-   message to my internet site. I will change it as soon as possible. 
-   Please don't flame to the tcp-group or anywhere else. Thanks!
-
-   Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
-		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
-		Internet: jreuter@lykos.tng.oche.de
-		
 		
    Incomplete history of z8530drv:
    -------------------------------
@@ -74,89 +64,105 @@
    950131	- changed copyright notice to GPL without limitations.
    
      .
-     .
+     .	<snip!>
      .
       
-   950922	- using kernel timer chain
-   
-   951002	- splitting timer routine, adding support for throttle/
-   		  unthrottle calls, removed typo in kiss decoder,
-   		  corrected termios settings.
-   		  
-   951011	- at last found one (the?) problem which caused the 
-   		  driver to mess up buffers on heavy load pe1ayx was
-   		  complaining about. Quite simple to reproduce:
-   		  set a 9k6 port into audio loopback, add a route
-   		  to 44.99.99.99 on that route and do a 
-   		  ping -f 44.99.99.99
+   951114	- rewrote memory management, took first steps to allow
+		  compilation as a module. Well, it looks like a whole
+		  new driver now.
+
+   960413	- Heiko Eissfeld fixed missing verify_area() calls
+
+   960507	- the driver may be used as a network driver now.
+ 		  (for kernel AX.25). 
+
+   960512	- added scc_net_ioctl(), restructured ioctl routines
+   		  added /proc filesystem support
    		  
-   951013	- it still does not survive a flood ping...
+   960517	- fixed fullduplex mode 2 operation and waittime bug
    
-   951107	- axattach w/o "-s" option (or setting an invalid 
-   		  baudrate) does not produce "division by zero" faults 
-   		  anymore.
+   960518	- added fullduplex mode 3 to allow direct hardware
+   		  access via protocol layer. Removed kiss.not_slip,
+   		  I do not think someone really needed it.
    		  
-   951114	- rewrote memory management, took first steps to allow
-		  compilation as a module. Well, it looks like a whole
-		  new driver now. BTW: It  d o e s  survive the flood
-		  ping at last...
-		  
-   951116	- scc_config.h is gone -- the driver will be initialized
-   		  through ioctl-commands. Use sccinit.c for this purpose.
+   960607	- switching off receiver while transmission in halfduplex
+   		  mode with external MODEM clock.
    		  
-   951117	- Well --- what should I say: You can compile it as a
-   		  module now. And I solved the problem with slip.c...
+   960715	- rewrote interrupt service routine for "polling" mode,
+   		  fixed bug in grouping algorithm.
    		  
-   951120	- most ioctl() routines may be called w/o suser()
-   		  permissions, check if you've set the permissions of
-   		  /dev/scc* right! NOT 0666, you know it's evil ;-)
+   960719	- New transmit timer routines (let's see what it breaks),
+   		  some improvements regarding DCD and SYNC interrupts,
+   		  clean-up of printk().
    		  
-   951217	- found silly bug in init_channel(), some bugfixes
-   		  for the "standalone" module
+   960725	- Fixed Maxkeyup problems. Will generate HDLC abort and
+   		  recover now.
 
+   960808	- Maxkeyup will set dev->tbusy until all remaining frames
+   		  are sent.
+   		  
+   970115	- Fixed return values in scc_net_tx(), added missing
+   		  scc_enqueue_buffer()
+   		  
+   970410	- Fixed problem with new *_timer() code in sched.h for
+   		  kernel 2.0.30.
 
    Thanks to:
    ----------
    
-   PE1CHL Rob	- for a lot of good ideas from his SCC driver for DOS
-   PE1NNZ Guido - for his port of the original driver to Linux
-   KA9Q   Phil  - from whom we stole the mbuf-structure
-   PA3AYX Hans  - for some useful changes
-   DL8MBT Flori - for support
-   DG0FT  Rene  - for the BayCom USCC support
-   PA3AOU Harry - for ESCC testing, information supply and support
-   
+   PE1CHL Rob	  - for a lot of good ideas from his SCC driver for DOS
+   PE1NNZ Guido   - for his port of the original driver to Linux
+   KA9Q   Phil    - from whom we stole the mbuf-structure
+   PA3AYX Hans    - for some useful changes
+   DL8MBT Flori   - for support
+   DG0FT  Rene    - for the BayCom USCC support
+   PA3AOU Harry   - for ESCC testing, information supply and support
+   Heiko Eissfeld - verify_area() clean-up
+   DL3KHB Klaus   - fullduplex mode 2 bug-hunter
+   PA3GCU Richard - for support and finding various bugs.
+
    PE1KOX Rob, DG1RTF Thomas, ON5QK Roland, G4XYW Andy, Linus,
-   EI9GL Paul,
-   
+   GW4PTS Alan, EI9GL Paul, G0DZB Peter, G8IMB Martin
+
    and all who sent me bug reports and ideas... 
    
    
    NB -- if you find errors, change something, please let me know
       	 first before you distribute it... And please don't touch
    	 the version number. Just replace my callsign in
-   	 "v2.01.dl1bke" with your own. Just to avoid confusion...
-   	 
+   	 "v2.4c.dl1bke" with your own. Just to avoid confusion...
+
    If you want to add your modification to the linux distribution
    please (!) contact me first.
-   	  
-   Jörg Reuter DL1BKE
-  
+   
+   New versions of the driver will be announced on the linux-hams
+   mailing list on vger.rutgers.edu. To subscribe send an e-mail
+   to majordomo@vger.rutgers.edu with the following line in
+   the body of the mail:
+   
+	   subscribe linux-hams
+	   
+   The content of the "Subject" field will be ignored.
+
+   vy 73,
+   Joerg Reuter	ampr-net: dl1bke@db0pra.ampr.org
+		AX-25   : DL1BKE @ DB0ACH.#NRW.DEU.EU
+		Internet: jreuter@poboxes.com  
 */
 
 /* ----------------------------------------------------------------------- */
 
-#define DEBUG_BUFFERS	/* keep defined unless it is really stable... */
-
-#undef  SCC_DELAY 	/* perhaps a 486DX2 is a *bit* too fast */
-#undef  SCC_LDELAY 5	/* slow it even a bit more down */
+#undef  SCC_DELAY 	/* perhaps your ISA bus is a *bit* too fast? */
+#undef  SCC_LDELAY 1	/* slow it even a bit more down */
 #undef  DONT_CHECK	/* don't look if the SCCs you specified are available */
 
 #define MAXSCC          4       /* number of max. supported chips */
 #define RXBUFFERS       8       /* default number of RX buffers */
 #define TXBUFFERS       8       /* default number of TX buffers */
 #define BUFSIZE         384     /* must not exceed 4096-sizeof(mbuf) */
-#define TPS             25      /* scc_tx_timer():  Ticks Per Second */
+#undef  DISABLE_ALL_INTS	/* use cli()/sti() in ISR instead of */
+				/* enable_irq()/disable_irq()        */
+#undef	SCC_DEBUG
 
 #define DEFAULT_CLOCK	4915200 /* default pclock if nothing is specified */
 
@@ -177,12 +183,26 @@
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
 #include <linux/string.h>
+#include <linux/in.h>
 #include <linux/fcntl.h>
 #include <linux/ptrace.h>
 #include <linux/malloc.h>
 #include <linux/delay.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+
+#ifdef CONFIG_SCC_STANDALONE
+#include "scc.h"
+#else
 #include <linux/scc.h>
+#endif
 
+#include <net/ax25.h>
+#include <asm/irq.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -193,6 +213,7 @@
 #include <ctype.h>
 #include <time.h>
 #include <linux/kernel.h>
+#include <linux/proc_fs.h>
 
 #ifdef MODULE
 int init_module(void);
@@ -203,6 +224,15 @@
 #define Z8530_MAJOR 34
 #endif
 
+#ifndef PROC_NET_Z8530
+#define PROC_NET_Z8530 PROC_NET_LAST+10		/* sorry... */
+#endif
+
+#if !defined(CONFIG_SCC_DEV) && !defined(CONFIG_SCC_TTY)
+#define CONFIG_SCC_DEV
+#define CONFIG_SCC_TTY
+#endif
+
 int scc_init(void);
 
 static struct mbuf * scc_enqueue_buffer(struct mbuf **queue, struct mbuf * buffer);
@@ -211,6 +241,7 @@
 static void free_buffer_pool(struct scc_channel *scc);
 static struct mbuf * scc_get_buffer(struct scc_channel *scc, char type);
 
+#ifdef CONFIG_SCC_TTY
 int scc_open(struct tty_struct *tty, struct file *filp);
 static void scc_close(struct tty_struct *tty, struct file *filp);
 int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count);
@@ -219,81 +250,112 @@
 static int scc_write_room(struct tty_struct *tty);
 static int scc_chars_in_buffer(struct tty_struct *tty);
 static void scc_flush_buffer(struct tty_struct *tty);
-static int scc_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
+static int scc_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg);
 static void scc_set_termios(struct tty_struct *tty, struct termios *old_termios);
 static void scc_set_ldisc(struct tty_struct *tty);
 static void scc_throttle(struct tty_struct *tty);
 static void scc_unthrottle(struct tty_struct *tty);
 static void scc_start(struct tty_struct *tty);
 static void scc_stop(struct tty_struct *tty);
+static void kiss_encode(struct scc_channel *scc);
+static void scc_rx_timer(unsigned long);
+static void scc_change_speed(struct scc_channel *scc);
+#endif
 
-static void z8530_init(void);
+static void t_dwait(unsigned long);
+static void t_txdelay(unsigned long);
+static void t_tail(unsigned long);
+static void t_busy(unsigned long);
+static void t_maxkeyup(unsigned long);
+static void t_idle(unsigned long);
+static void scc_tx_done(struct scc_channel *);
+static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long);
+static void scc_start_maxkeyup(struct scc_channel *);
+static void scc_start_defer(struct scc_channel *);
 
-static void scc_change_speed(struct scc_channel *scc);
-static void kiss_encode(struct scc_channel *scc);
+static int scc_ioctl(struct scc_channel *scc, unsigned int cmd, void * arg);
+
+static void z8530_init(void);
 
 static void init_channel(struct scc_channel *scc);
 static void scc_key_trx (struct scc_channel *scc, char tx);
-static void scc_txint(register struct scc_channel *scc);
-static void scc_exint(register struct scc_channel *scc);
-static void scc_rxint(register struct scc_channel *scc);
-static void scc_spint(register struct scc_channel *scc);
 static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
-static void scc_tx_timer(unsigned long);
-static void scc_rx_timer(unsigned long);
 static void scc_init_timer(struct scc_channel *scc);
 
-/* from serial.c */
+#ifdef CONFIG_SCC_DEV
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name);
+static void scc_net_rx(struct scc_channel *scc, struct mbuf *bp);
+#endif
+
+static unsigned char *SCC_DriverName = "scc";
 
+#ifdef CONFIG_SCC_TTY
 static int baud_table[] = {
 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
 	9600, 19200, 38400, 57600, 115200, 0 };
 
+struct semaphore scc_sem = MUTEX;
+static struct termios scc_std_termios;
+unsigned char scc_wbuf[BUFSIZE];
 
 struct tty_driver scc_driver;
-static int scc_refcount;
 static struct tty_struct *scc_table[2*MAXSCC];
 static struct termios scc_termios[2 * MAXSCC];
 static struct termios scc_termios_locked[2 * MAXSCC];
+static int scc_refcount;
+#endif
+
 
-struct irqflags { unsigned char used : 1; } Ivec[16];
+static struct irqflags { unsigned char used : 1; } Ivec[16];
 	
-struct scc_channel SCC_Info[2 * MAXSCC];         /* information per channel */
-io_port SCC_ctrl[2 * MAXSCC];			 /* Control ports */
+static struct scc_channel SCC_Info[2 * MAXSCC];	/* information per channel */
 
-unsigned char Random = 0;		/* random number for p-persist */
-unsigned char Driver_Initialized = 0;
-int Nchips = 0;
-io_port Vector_Latch = 0;
+static struct scc_ctrl {
+	io_port chan_A;
+	io_port chan_B;
+	int irq;
+} SCC_ctrl[MAXSCC+1];
 
-struct semaphore scc_sem = MUTEX;
-unsigned char scc_wbuf[BUFSIZE];
+static unsigned char Driver_Initialized = 0;
+static int Nchips = 0;
+static io_port Vector_Latch = 0;
 
-static struct termios scc_std_termios;
+#define SCC_DEVNAME (scc->stat.is_netdev? scc->dev->name:kdevname(scc->tty->device))
 
 /* ******************************************************************** */
 /* *			Port Access Functions			      * */
 /* ******************************************************************** */
 
-static inline unsigned char
-InReg(register io_port port, register unsigned char reg)
+/* These provide interrupt save 2-step access to the Z8530 registers */
+
+static __inline__ unsigned char
+InReg(io_port port, unsigned char reg)
 {
+	unsigned long flags;
+	unsigned char r;
+	
+	save_flags(flags);
+	cli();
 #ifdef SCC_LDELAY
-	register unsigned char r;
 	Outb(port, reg);
 	udelay(SCC_LDELAY);
 	r=Inb(port);
 	udelay(SCC_LDELAY);
-	return r;
 #else
 	Outb(port, reg);
-	return Inb(port);
+	r=Inb(port);
 #endif
+	restore_flags(flags);
+	return r;
 }
 
-static inline void
-OutReg(register io_port port, register unsigned char reg, register unsigned char val)
+static __inline__ void
+OutReg(io_port port, unsigned char reg, unsigned char val)
 {
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
 #ifdef SCC_LDELAY
 	Outb(port, reg); udelay(SCC_LDELAY);
 	Outb(port, val); udelay(SCC_LDELAY);
@@ -301,26 +363,40 @@
 	Outb(port, reg);
 	Outb(port, val);
 #endif
+	restore_flags(flags);
 }
 
-static inline void
-wr(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+wr(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
 }
 
-static inline void
-or(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+or(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
 }
 
-static inline void
-cl(register struct scc_channel *scc, register unsigned char reg, register unsigned char val)
+static __inline__ void
+cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
 {
 	OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
 }
 
+#ifdef DISABLE_ALL_INTS
+static __inline__ void scc_cli(int irq)
+{ cli(); }
+static __inline__ void scc_sti(int irq)
+{ sti(); }
+#else
+static __inline__ void scc_cli(int irq)
+{ disable_irq(irq); }
+static __inline__ void scc_sti(int irq)
+{ enable_irq(irq); }
+#endif
+
+
 /* ******************************************************************** */
 /* * 			Memory Buffer Management			*/
 /* ******************************************************************** */
@@ -331,6 +407,9 @@
  * fast. It has the disadvantage to mess things up double if something
  * is wrong.
  *
+ * We could have used the socket buffer routines from skbuff.h instead,
+ * but our own routines keep the driver portable...
+ *
  * Every scc channel has its own buffer pool now with an adjustable
  * number of buffers for transmit and receive buffers. The buffer
  * size remains the same for each AX.25 frame, but is (as a semi-
@@ -354,23 +433,6 @@
 	
 	save_flags(flags); cli();		/* do not disturb! */
 
-#ifdef DEBUG_BUFFERS			
-	if (queue == NULLBUFP)			/* called with illegal parameters, notify the user */
-
-	{
-		printk("z8530drv: Pointer to queue anchor is NULL pointer [enq]\n");
-		restore_flags(flags);
-		return NULLBUF;
-	}
-	
-	if (buffer == NULLBUF)
-	{
-		printk("z8530drv: can't enqueue a NULL pointer\n");
-		restore_flags(flags);
-		return NULLBUF;
-	}
-#endif
-
 	anchor = *queue;			/* the anchor is the "start" of the chain */
 
 	if (anchor == NULLBUF)			/* found an empty list */
@@ -380,22 +442,9 @@
 	} else	
 	if (anchor->prev == NULLBUF)		/* list has one member only */
 	{
-#ifdef DEBUG_BUFFERS
-		if (anchor->prev != NULLBUF)	/* oops?! */
-			printk("weird --- anchor->prev is NULL but not anchor->next [enq]\n");
-#endif			
-		anchor->next = anchor->prev = buffer;
-		buffer->next = buffer->prev = anchor;
-	} else
-#ifdef DEBUG_BUFFERS	
-	if (anchor->next == NULLBUF)		/* this has to be an error. Okay, make the best out of it */
-	{
-		printk("z8530drv: weird --- anchor->next is NULL but not anchor->prev [enq]\n");
 		anchor->next = anchor->prev = buffer;
 		buffer->next = buffer->prev = anchor;
-	} else
-#endif
-	{					/* every other case */
+	} else {				/* every other case */
 		buffer->prev = anchor->prev;	/* self explaining, isn't it? */
 		buffer->next = anchor;
 		anchor->prev->next = buffer;
@@ -418,28 +467,12 @@
 	
 	save_flags(flags); cli();
 
-#ifdef DEBUG_BUFFERS			
-	if (queue == NULLBUFP)			/* called with illegal parameter */
-
-	{
-		printk("z8530drv: Pointer to queue anchor is NULL pointer [deq]\n");
-		restore_flags(flags);
-		return NULLBUF;
-	}
-#endif
-
 	buffer = *queue;			/* head of the chain */
 	
 	if (buffer != NULLBUF)			/* not an empty list? */
 	{
 		if (buffer->prev != NULLBUF)	/* not last buffer? */
 		{
-#ifdef DEBUG_BUFFERS
-			if (buffer->next == NULLBUF)
-			{			/* what?! */
-				printk("z8530drv: weird --- buffer->next is NULL but not buffer->prev [deq]\n");
-			} else
-#endif
 			if (buffer->prev->next == buffer->prev->prev)
 			{			/* only one buffer remaining... */
 				buffer->next->prev = NULLBUF;
@@ -449,10 +482,7 @@
 				buffer->prev->next = buffer->next;
 			}
 		} 
-#ifdef DEBUG_BUFFERS
-		else if (buffer->next != NULLBUF)
-			printk("z8530drv: weird --- buffer->prev is NULL but not buffer->next [deq]\n");
-#endif
+
 		*queue = buffer->next;		/* new head of chain */
 		
 		buffer->next = NULLBUF;		/* for security only... */
@@ -476,6 +506,12 @@
 	struct mbuf * bptr;
 	int  buflen;
 	
+	if (scc->mempool)
+	{
+		printk(KERN_DEBUG "z8530drv: alloc_buffer_pool() had buffers already allocated\n");
+		return;
+	}
+	
 	buflen = sizeof(struct mbuf) + scc->stat.bufsize;
 	
 	if (scc->stat.bufsize <  336) scc->stat.bufsize = 336;
@@ -487,7 +523,6 @@
 	
 	if (scc->stat.rxbuffers < 4)  scc->stat.rxbuffers = 4;
 	if (scc->stat.txbuffers < 4)  scc->stat.txbuffers = 4;
-	
 
 	
 	/* allocate receive buffers for this channel */
@@ -502,7 +537,7 @@
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: %s: can't allocate memory for rx buffer pool", kdevname(scc->tty->device));
+			printk(KERN_WARNING "z8530drv: %s: can't allocate memory for rx buffer pool", SCC_DEVNAME);
 			break;
 		}
 		
@@ -527,7 +562,7 @@
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: %s: can't allocate memory for tx buffer pool", kdevname(scc->tty->device));
+			printk(KERN_WARNING "z8530drv: %s: can't allocate memory for tx buffer pool", SCC_DEVNAME);
 			break;
 		}
 		
@@ -537,6 +572,8 @@
 		
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
 	}
+	
+	scc->mempool = 1;
 }
 
 
@@ -545,102 +582,89 @@
 static void 
 free_buffer_pool(struct scc_channel *scc)
 {
-	struct mbuf * bptr;
 	unsigned long flags;
-	int cnt;
+	int rx_cnt, tx_cnt;
+	
+	if (!scc->mempool)
+		return;
 	
 	/* this one is a bit tricky and probably dangerous. */
+	rx_cnt = tx_cnt = 0;
 		
 	save_flags(flags); cli();
 	
 	/* esp. to free the buffers currently in use by ISR */
 	
-	bptr = scc->rx_bp;
-	if (bptr != NULLBUF)
+	if (scc->rx_bp != NULLBUF)
 	{
+		kfree(scc->rx_bp);
 		scc->rx_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+		rx_cnt++;
 	}
 
-	bptr = scc->tx_bp;	
-	if (bptr != NULLBUF)
+	if (scc->tx_bp != NULLBUF)
 	{
+		kfree(scc->tx_bp);
 		scc->tx_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+		tx_cnt++;
 	}
 	
-	bptr = scc->kiss_decode_bp;
-	if (bptr != NULLBUF)
+	if (scc->kiss_decode_bp != NULLBUF)
 	{
+		kfree(scc->kiss_decode_bp);
 		scc->kiss_decode_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+		tx_cnt++;
 	}
-	
-	bptr = scc->kiss_encode_bp;
-	if (bptr != NULLBUF)
+
+#ifdef CONFIG_SCC_TTY
+	if (scc->kiss_encode_bp != NULLBUF)
 	{
+		kfree(scc->kiss_encode_bp);
 		scc->kiss_encode_bp = NULLBUF;
-		scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+		rx_cnt++;
 	}
+#endif
 	
 	restore_flags(flags);
 	
 	
 	while (scc->rx_queue != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->rx_queue);
-		scc_enqueue_buffer(&scc->rx_buffer_pool, bptr);
+		kfree(scc_dequeue_buffer(&scc->rx_queue));
+		rx_cnt++;
 	}
+	scc->stat.rx_queued = 0;
 	
 	while (scc->tx_queue != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->tx_queue);
-		scc_enqueue_buffer(&scc->tx_buffer_pool, bptr);
+		kfree(scc_dequeue_buffer(&scc->tx_queue));
+		tx_cnt++;
 	}
-	
-	/* you want to know why we move every buffer back to the
-	   buffer pool? Well, good question... ;-) 
-	 */
-	
-	cnt = 0;
-	
-	/* Probably because we just want a central position in the
-	   code were we actually call free()?
-	 */
+	scc->stat.tx_queued = 0;
 	
 	while (scc->rx_buffer_pool != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->rx_buffer_pool);
-		
-		if (bptr != NULLBUF)
-		{
-			cnt++;
-			kfree(bptr);
-		}
+		kfree(scc_dequeue_buffer(&scc->rx_buffer_pool));
+		rx_cnt++;
 	}
-
-	if (cnt < scc->stat.rxbuffers)		/* hmm... hmm... :-( */
-		printk("z8530drv: oops, deallocated only %d of %d rx buffers\n", cnt, scc->stat.rxbuffers);
-	if (cnt > scc->stat.rxbuffers)		/* WHAT?!! */
-		printk("z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", cnt, scc->stat.rxbuffers);
-		
-	cnt = 0;
 	
 	while (scc->tx_buffer_pool != NULLBUF)
 	{
-		bptr = scc_dequeue_buffer(&scc->tx_buffer_pool);
-		
-		if (bptr != NULLBUF)
-		{
-			cnt++;
-			kfree(bptr);
-		}
+		kfree(scc_dequeue_buffer(&scc->tx_buffer_pool));
+		tx_cnt++;
 	}
-
-	if (cnt < scc->stat.txbuffers)
-		printk("z8530drv: oops, deallocated only %d of %d tx buffers\n", cnt, scc->stat.txbuffers);
-	if (cnt > scc->stat.txbuffers)
-		printk("z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", cnt, scc->stat.txbuffers);
+	
+	if (rx_cnt < scc->stat.rxbuffers)	/* hmm... hmm... :-( */
+		printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d rx buffers\n", rx_cnt, scc->stat.rxbuffers);
+	if (rx_cnt > scc->stat.rxbuffers)	/* WHAT?!! */
+		printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d rx buffers. Very strange.\n", rx_cnt, scc->stat.rxbuffers);
+		
+	if (tx_cnt < scc->stat.txbuffers)
+		printk(KERN_ERR "z8530drv: oops, deallocated only %d of %d tx buffers\n", tx_cnt, scc->stat.txbuffers);
+	if (tx_cnt > scc->stat.txbuffers)
+		printk(KERN_ERR "z8530drv: oops, deallocated %d instead of %d tx buffers. Very strange.\n", tx_cnt, scc->stat.txbuffers);
+		
+	scc->mempool = 0;
 }
 		
 		
@@ -666,28 +690,33 @@
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: scc_get_buffer(%s): tx buffer pool empty\n", kdevname(scc->tty->device));
-			
+#if 0
+			printk(KERN_DEBUG "z8530drv: tx buffer pool for %s empty\n", SCC_DEVNAME);
+#endif
 			/* use the oldest from the queue instead */
 			
 			bptr = scc_dequeue_buffer(&scc->tx_queue);
+			scc->stat.tx_queued--;
+			scc->stat.nospace++;
 			
 			/* this should never, ever happen... */
 			
 			if (bptr == NULLBUF)
-				printk("z8530drv: scc_get_buffer(): panic - even no buffer found in tx queue\n");
+				printk(KERN_ERR "z8530drv: panic - all tx buffers for %s gone\n", SCC_DEVNAME);
 		}
 	} else {
 		bptr = scc_dequeue_buffer(&scc->rx_buffer_pool);
 		
 		if (bptr == NULLBUF)
 		{
-			printk("z8530drv: scc_get_buffer(%s): rx buffer pool empty\n", kdevname(scc->tty->device));
+			printk(KERN_DEBUG "z8530drv: rx buffer pool for %s empty\n", SCC_DEVNAME);
 			
 			bptr = scc_dequeue_buffer(&scc->rx_queue);
+			scc->stat.rx_queued--;
+			scc->stat.nospace++;
 			
 			if (bptr == NULLBUF)
-				printk("z8530drv: scc_get_buffer(): panic - even no buffer found in rx queue\n");
+				printk(KERN_ERR "z8530drv: panic - all rx buffers for %s gone\n", SCC_DEVNAME);
 		}	
 	}
 	
@@ -705,153 +734,117 @@
 /* *			Interrupt Service Routines		      * */
 /* ******************************************************************** */
 
-/* ----> interrupt service routine for the 8530 <---- */
 
-/* it's recommended to keep this function "inline" ;-) */
-
-static inline void
-scc_isr_dispatch(register struct scc_channel *scc, register int vector)
-{
-	switch (vector & VECTOR_MASK)
-	{
-		case TXINT: scc_txint(scc); break;
-		case EXINT: scc_exint(scc); break;
-		case RXINT: scc_rxint(scc); break;
-		case SPINT: scc_spint(scc); break;
-		default	  : printk("scc_isr(): unknown interrupt status (addr %4.4x, state %2.2x)\n",scc->ctrl,vector);
-	}
-}
 
+/* ----> subroutines for the interrupt handlers <---- */
 
 
+#ifdef CONFIG_SCC_TTY
+/* kick rx_timer (try to send received frame or a status message ASAP) */
 
-
-/* If the card has a latch for the interrupt vector (like the PA0HZP card)
-   use it to get the number of the chip that generated the int.
-   If not: poll all defined chips.
+/* of course we could define a "bottom half" routine to do the job,
+   but since its structures are saved in an array instead of a linked
+   list we would get in trouble if it clashes with another driver.
+   IMHO we are fast enough with a timer routine called on the next 
+   timer-INT... Your opinions?
  */
 
-static void
-scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+static __inline__ void
+kick_rx_timer(struct scc_channel *scc)
 {
-	register unsigned char vector;	
-	register struct scc_channel *scc;
-	register io_port q;
-	register io_port *p;
-	register int k;
-
+	del_timer(&(scc->rx_t));
+	scc->rx_t.expires = jiffies + 1;
+	scc->rx_t.function = scc_rx_timer;
+	scc->rx_t.data = (unsigned long) scc;
+	add_timer(&scc->rx_t);
+}
+#endif
 
-	cli();
+static __inline__ void
+scc_notify(struct scc_channel *scc, int event)
+{
+	struct mbuf *bp;
 	
-	if (Vector_Latch)
-	{
-	    	while(1)			   /* forever...? */
-    		{ 
-			Outb(Vector_Latch, 0);      /* Generate INTACK */
-        
-			/* Read the vector */
-			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; 
-						/* ...not forever! */
-          
-			/* Extract channel number and status from vector. */
-			/* Isolate channel number */
-			/* Call handler */
-	        
-			if (vector & 0x01) break; 
-        	 
-		        scc=&SCC_Info[(((vector>>1)&0x7c)^0x04) >> 2];
-        
-			if (!scc->tty) break;
-
-			scc_isr_dispatch(scc, vector);
-			
-			Outb(scc->ctrl,0x38);              /* Reset Highest IUS" opcode to WR0 */
-		}  
-	    	
-		sti();	
+	if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
 		return;
-	}
-	
 	
-	/* Find the SCC generating the interrupt by polling all attached SCCs
-	 * reading RR3A (the interrupt pending register)
-	 */
+	bp = scc_get_buffer(scc, BT_RECEIVE);
 
-	k = 0;
-	p = SCC_ctrl;
-	
-	while (k++ < Nchips)
-	{
-		if (!(q=*p++)) break;
-		
-		Outb(q,3);
+	if (bp == NULLBUF)
+		return;
 		
-		if (Inb(q))
-		{
-			if (!(q=*p++)) break;
-			
-			Outb(q,2);
-			vector=Inb(q);                  /* Read the vector */
-        
-		        /* Extract channel number and status from vector. */
-		        /* Isolate channel number */
-		        /* Call handler */
-        
+	bp->data[0] = PARAM_HWEVENT;
+	bp->data[1] = event;
+	bp->cnt += 2;
 
-			if (vector & 1) break; 
-        
-		        scc = &SCC_Info[(((vector >> 1) & 0x7c) ^ 0x04) >> 2];
-        
-		        if (!scc->tty) break;
-
-			/* Isolate status info from vector, call handler */
-			
-			scc_isr_dispatch(scc, vector);
-
-			k = 0;
-			p = SCC_ctrl;
+	if (scc->stat.is_netdev)
+	{
+#ifdef CONFIG_SCC_DEV
+		scc_net_rx(scc, bp);
+		scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+#endif
+	} else {
+#ifdef CONFIG_SCC_TTY
+		scc_enqueue_buffer(&scc->rx_queue, bp);
+		scc->stat.rx_queued++;
+		kick_rx_timer(scc);
+#endif
+	}
+}
 
-	     	} else p++;
-	}    
+static __inline__ void
+flush_rx_FIFO(struct scc_channel *scc)
+{
+	int k;
 	
-	sti();
+	for (k=0; k<3; k++)
+		Inb(scc->data);
+		
+	if(scc->rx_bp != NULLBUF)	/* did we receive something? */
+	{
+		scc->stat.rxerrs++;  /* then count it as an error */
+		scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp);
+		
+		scc->rx_bp = NULLBUF;
+	}
 }
 
 
-
 /* ----> four different interrupt handlers for Tx, Rx, changing of	*/
 /*       DCD/CTS and Rx/Tx errors					*/
 
-
 /* Transmitter interrupt handler */
-static void
-scc_txint(register struct scc_channel *scc)
+static __inline__ void
+scc_txint(struct scc_channel *scc)
 {
-	register struct mbuf *bp;
+	struct mbuf *bp;
 
 	scc->stat.txints++;
 
 	bp = scc->tx_bp;
 	
+	/* send first octet */
+	
 	if (bp == NULLBUF)
 	{
-		do 
+		do
 		{
 			if (bp != NULLBUF)
+			{
 				scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+				scc->stat.tx_queued--;
+			}
 
 			bp = scc_dequeue_buffer(&scc->tx_queue);
 			
 			if (bp == NULLBUF)
 			{
-				scc->stat.tx_state = TXS_BUSY;
-				scc->t_tail = scc->kiss.tailtime;
-				
+				scc_tx_done(scc);
 				Outb(scc->ctrl, RES_Tx_P);	/* clear int */
 				return;
 			}
 			
-			if ( scc->kiss.not_slip && (bp->cnt > 0) )
+			if (bp->cnt > 0)
 			{
 				bp->rw_ptr++;
 				bp->cnt--;
@@ -859,57 +852,47 @@
 			
 		} while (bp->cnt < 1);
 		
-		
-		Outb(scc->ctrl, RES_Tx_CRC);	/* reset CRC generator */
+		scc->tx_bp = bp;
+		scc->stat.tx_state = TXS_ACTIVE;
+
+		OutReg(scc->ctrl, R0, RES_Tx_CRC);
+						/* reset CRC generator */
 		or(scc,R10,ABUNDER);		/* re-install underrun protection */
-		Outb(scc->data,*bp->rw_ptr);	/* send byte */
+		Outb(scc->data,*bp->rw_ptr++);	/* send byte */
+
 		if (!scc->enhanced)		/* reset EOM latch */
-			Outb(scc->ctrl, RES_EOM_L);
-		
-		scc->tx_bp = bp;
-		scc->stat.tx_state = TXS_ACTIVE; /* next byte... */
-	} else
+			Outb(scc->ctrl,RES_EOM_L);
+
+		bp->cnt--;
+		return;
+	}
+	
+	/* End Of Frame... */
+	
 	if (bp->cnt <= 0)
 	{
-		if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
-		
 		Outb(scc->ctrl, RES_Tx_P);	/* reset pending int */
 		cl(scc, R10, ABUNDER);		/* send CRC */
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+		scc->stat.tx_queued--;
 		scc->tx_bp = NULLBUF;
 		scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
 		return;
-	} else {
-		Outb(scc->data,*bp->rw_ptr);		
-	}
+	} 
 	
+	/* send octet */
+	
+	Outb(scc->data,*bp->rw_ptr);		
 	bp->rw_ptr++;			/* increment pointer */
 	bp->cnt--;                      /* decrease byte count */
 }
 
-static inline void
-flush_FIFO(register struct scc_channel *scc)
+
+/* External/Status interrupt handler */
+static __inline__ void
+scc_exint(struct scc_channel *scc)
 {
-	register int k;
-	
-	for (k=0; k<3; k++)
-		Inb(scc->data);
-		
-	if(scc->rx_bp != NULLBUF)	/* did we receive something? */
-	{
-		scc->stat.rxerrs++;  /* then count it as an error */
-		scc_enqueue_buffer(&scc->rx_buffer_pool, scc->rx_bp);
-		
-		scc->rx_bp = NULLBUF;
-	}
-}
-
-
-/* External/Status interrupt handler */
-static void
-scc_exint(register struct scc_channel *scc)
-{
-	register unsigned char status,changes,chg_and_stat;
+	unsigned char status,changes,chg_and_stat;
 
 	scc->stat.exints++;
 
@@ -920,9 +903,9 @@
 	/* ABORT: generated whenever DCD drops while receiving */
 
 	if (chg_and_stat & BRK_ABRT)		/* Received an ABORT */
-		flush_FIFO(scc);
-			
-		
+		flush_rx_FIFO(scc);
+
+
 	/* DCD: on = start to receive packet, off = ABORT condition */
 	/* (a successfully received packet generates a special condition int) */
 	
@@ -936,69 +919,67 @@
 			or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
 		} else {                        /* DCD is now OFF */
 			cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
-			flush_FIFO(scc);
+			flush_rx_FIFO(scc);
 		}
+		
+		if (!scc->kiss.softdcd)
+			scc_notify(scc, (status & DCD)? HWEV_DCD_ON:HWEV_DCD_OFF);
+	}
+	
+	/* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+	
+	if (changes & SYNC_HUNT)
+	{
+		if (scc->kiss.softdcd)
+			scc_notify(scc, (status & SYNC_HUNT)? HWEV_DCD_OFF:HWEV_DCD_ON);
+		else
+			cl(scc,R15,SYNCIE);	/* oops, we were too lazy to disable this? */
 	}
 
 #ifdef notdef
-	/* CTS: use external TxDelay (what's that good for?!) */
-	
+	/* CTS: use external TxDelay (what's that good for?!)
+	 * Anyway: If we _could_ use it (BayCom USCC uses CTS for
+	 * own purposes) we _should_ use the "autoenable" feature
+	 * of the Z8530 and not this interrupt...
+	 */
+	 
 	if (chg_and_stat & CTS)			/* CTS is now ON */
-	{			
-		if (!Running(t_txdel) && scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */
-			scc->t_txdel = 0;	/* kick it! */		
-		
+	{
+		if (scc->kiss.txdelay == 0)	/* zero TXDELAY = wait for CTS */
+			scc_start_tx_timer(scc, t_txdelay, 0);
 	}
 #endif
 	
-	if ( (status & TxEOM) && (scc->stat.tx_state == TXS_ACTIVE) )
+	if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
 	{
 		scc->stat.tx_under++;	  /* oops, an underrun! count 'em */
-		Outb(scc->ctrl, RES_Tx_P);
 		Outb(scc->ctrl, RES_EXT_INT);	/* reset ext/status interrupts */
-		scc->t_maxk = 1;
-		
+
 		if (scc->tx_bp != NULLBUF)
 		{
 			scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp);
+			scc->stat.tx_queued--;
 			scc->tx_bp = NULLBUF;
 		}
 		
-		if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
 		or(scc,R10,ABUNDER);
+		scc_start_tx_timer(scc, t_txdelay, 1);	/* restart transmission */
 	}
 		
-	if (status & ZCOUNT)		   /* Oops? */
-	{
-		scc->stat.tx_under = 9999;  /* errr... yes. */
-		Outb(scc->ctrl, RES_Tx_P); /* just to be sure */
-		scc->t_maxk = 1;
-		
-		if (scc->tx_bp != NULLBUF)
-		{
-			scc_enqueue_buffer(&scc->tx_buffer_pool, scc->tx_bp);
-			scc->tx_bp = NULLBUF;
-		}
-
-		if (--scc->stat.tx_queued < 0) scc->stat.tx_queued = 0;
-		scc->kiss.tx_inhibit = 1;	/* don't try it again! */
-	}
-		
-	
 	scc->status = status;
 	Outb(scc->ctrl,RES_EXT_INT);
 }
 
 
 /* Receiver interrupt handler */
-static void
-scc_rxint(register struct scc_channel *scc)
+static __inline__ void
+scc_rxint(struct scc_channel *scc)
 {
-	register struct mbuf *bp;
+	struct mbuf *bp;
 
 	scc->stat.rxints++;
 
-	if( Running(t_maxk) && !(scc->kiss.fulldup))
+	if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
 	{
 		Inb(scc->data);		/* discard char */
 		or(scc,R3,ENT_HM);	/* enter hunt mode for next flag */
@@ -1012,20 +993,22 @@
 		bp = scc_get_buffer(scc, BT_RECEIVE);
 		if (bp == NULLBUF)
 		{
-			printk("scc_rxint(): panic --- cannot get a buffer\n");
 			Inb(scc->data);
 			or(scc, R3, ENT_HM);
-			scc->stat.nospace++;
 			return;
 		}
 		
 		scc->rx_bp = bp;
+		
+		*bp->rw_ptr=0;		/* KISS data */
+		bp->rw_ptr++;
+		bp->cnt++;
 	}
 	
 	if (bp->cnt > scc->stat.bufsize)
 	{
 #ifdef notdef
-		printk("scc_rxint(): oops, received huge frame...\n");
+		printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
 #endif
 		scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
 		scc->rx_bp = NULLBUF;
@@ -1040,33 +1023,13 @@
 	bp->cnt++;
 }
 
-/* kick rx_timer (try to send received frame or part of it ASAP) */
-
-/* of course we could define a "bottom half" routine to do the job,
-   but since its structures are saved in an array instead of a linked
-   list we would get in trouble if it clashes with another driver or
-   when we try to modularize the driver. IMHO we are fast enough
-   with a timer routine called on the next timer-INT... Your opinions?
- */
-
-static inline void
-kick_rx_timer(register struct scc_channel *scc)
-{
-	if (scc->rx_t.next)
-		del_timer(&(scc->rx_t));
-		
-	scc->rx_t.expires = jiffies + 1;
-	scc->rx_t.function = scc_rx_timer;
-	scc->rx_t.data = (unsigned long) scc;
-	add_timer(&scc->rx_t);
-}
 
 /* Receive Special Condition interrupt handler */
-static void
-scc_spint(register struct scc_channel *scc)
+static __inline__ void
+scc_spint(struct scc_channel *scc)
 {
-	register unsigned char status;
-	register struct mbuf *bp;
+	unsigned char status;
+	struct mbuf *bp;
 
 	scc->stat.spints++;
 
@@ -1083,7 +1046,7 @@
 		if (bp) scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
 		scc->rx_bp = NULLBUF;
 	}
-	
+
 	if(status & END_FR && bp != NULLBUF)	/* end of frame */
 	{
 		/* CRC okay, frame ends on 8 bit boundary and received something ? */
@@ -1092,23 +1055,141 @@
 		{
 			/* ignore last received byte (first of the CRC bytes) */
 			bp->cnt--;
-			
-			scc_enqueue_buffer(&scc->rx_queue, bp);
+
+			if (scc->stat.is_netdev)
+			{
+#ifdef CONFIG_SCC_DEV
+				scc_net_rx(scc, bp);
+				scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
+#endif
+			} else {
+#ifdef CONFIG_SCC_TTY
+				scc_enqueue_buffer(&scc->rx_queue, bp);
+				scc->stat.rx_queued++;
+				kick_rx_timer(scc);
+#endif
+			}
+
 			scc->rx_bp = NULLBUF;
 			scc->stat.rxframes++;
-			scc->stat.rx_queued++;
-			kick_rx_timer(scc);
 		} else {				/* a bad frame */
 			scc_enqueue_buffer(&scc->rx_buffer_pool, bp);
 			scc->rx_bp = NULLBUF;
 			scc->stat.rxerrs++;
 		}
+	} 
+
+	Outb(scc->ctrl,ERR_RES);
+}
+
+
+/* ----> interrupt service routine for the Z8530 <---- */
+
+static void
+scc_isr_dispatch(struct scc_channel *scc, int vector)
+{
+	switch (vector & VECTOR_MASK)
+	{
+		case TXINT: scc_txint(scc); break;
+		case EXINT: scc_exint(scc); break;
+		case RXINT: scc_rxint(scc); break;
+		case SPINT: scc_spint(scc); break;
 	}
+}
+
+/* If the card has a latch for the interrupt vector (like the PA0HZP card)
+   use it to get the number of the chip that generated the int.
+   If not: poll all defined chips.
+ */
+
+#define SCC_IRQTIMEOUT 30000
+
+static void
+scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned char vector;	
+	struct scc_channel *scc;
+	struct scc_ctrl *ctrl;
+	int k;
 	
-	Outb(scc->ctrl,ERR_RES);
+	scc_cli(irq);
+
+	if (Vector_Latch)
+	{
+	    	for(k=0; k < SCC_IRQTIMEOUT; k++)
+    		{
+			Outb(Vector_Latch, 0);      /* Generate INTACK */
+        
+			/* Read the vector */
+			if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; 
+			if (vector & 0x01) break;
+        	 
+		        scc=&SCC_Info[vector >> 3 ^ 0x01];
+			if (!scc->tty && !scc->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+
+			OutReg(scc->ctrl,R0,RES_H_IUS);              /* Reset Highest IUS */
+		}  
+		scc_sti(irq);
+
+		if (k == SCC_IRQTIMEOUT)
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
+
+		return;
+	}
+
+	/* Find the SCC generating the interrupt by polling all attached SCCs
+	 * reading RR3A (the interrupt pending register)
+	 */
+
+	ctrl = SCC_ctrl;
+	while (ctrl->chan_A)
+	{
+		if (ctrl->irq != irq)
+		{
+			ctrl++;
+			continue;
+		}
+
+		scc = NULL;
+		for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
+		{
+			vector=InReg(ctrl->chan_B,R2);	/* Read the vector */
+			if (vector & 0x01) break; 
+
+			scc = &SCC_Info[vector >> 3 ^ 0x01];
+		        if (!scc->tty && !scc->dev) break;
+
+			scc_isr_dispatch(scc, vector);
+		}
+
+		if (k == SCC_IRQTIMEOUT)
+		{
+			printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
+			break;
+		}
+
+		/* This looks wierd and it is. At least the BayCom USCC doesn't
+		 * use the Interrupt Daisy Chain, thus we'll have to start
+		 * all over again to be sure not to miss an interrupt from 
+		 * (any of) the other chip(s)...
+		 * Honestly, the situation *is* braindamaged...
+		 */
+
+		if (scc != NULL)
+		{
+			OutReg(scc->ctrl,R0,RES_H_IUS);
+			ctrl = SCC_ctrl; 
+		} else
+			ctrl++;
+	}
+	
+	scc_sti(irq);
 }
 
 
+
 /* ******************************************************************** */
 /* *			Init Channel					*/
 /* ******************************************************************** */
@@ -1116,30 +1197,31 @@
 
 /* ----> set SCC channel speed <---- */
 
-static inline void set_brg(register struct scc_channel *scc, unsigned int tc)
+static __inline__ void 
+set_brg(struct scc_channel *scc, unsigned int tc)
 {
-	unsigned long flags;
-
- 	save_flags(flags); cli();      	/* 2-step register accesses... */
-
 	cl(scc,R14,BRENABL);		/* disable baudrate generator */
 	wr(scc,R12,tc & 255);		/* brg rate LOW */
 	wr(scc,R13,tc >> 8);   		/* brg rate HIGH */
 	or(scc,R14,BRENABL);		/* enable baudrate generator */
-
-	restore_flags(flags);
 }
 
-static inline void set_speed(register struct scc_channel *scc)
+static __inline__ void 
+set_speed(struct scc_channel *scc)
 {
+	disable_irq(scc->irq);
+
 	if (scc->modem.speed > 0)	/* paranoia... */
 		set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+
+	enable_irq(scc->irq);
 }
 
 
 /* ----> initialize a SCC channel <---- */
 
-static inline void init_brg(register struct scc_channel *scc)
+static __inline__ void 
+init_brg(struct scc_channel *scc)
 {
 	wr(scc, R14, BRSRC);				/* BRG source = PCLK */
 	OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]);	/* DPLL source = BRG */
@@ -1192,14 +1274,17 @@
  */
  
 static void
-init_channel(register struct scc_channel *scc)
+init_channel(struct scc_channel *scc)
 {
-	unsigned long flags;
+	disable_irq(scc->irq);
 	
-	if (scc->rx_t.next) del_timer(&(scc->rx_t));
-	if (scc->tx_t.next) del_timer(&(scc->tx_t));
+#ifdef CONFIG_SCC_TTY
+	del_timer(&scc->rx_t);
+#endif
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+
 
-	save_flags(flags); cli();
 
 	wr(scc,R4,X1CLK|SDLC);		/* *1 clock, SDLC mode */
 	wr(scc,R1,0);			/* no W/REQ operation */
@@ -1273,9 +1358,12 @@
 		or(scc,R3,ENT_HM|RxENABLE);	/* enable the receiver, hunt mode */
 	}
 	
-	/* enable CTS (not for Baycom), ABORT & DCD interrupts */
-	wr(scc,R15,((scc->brand & BAYCOM) ? 0 : CTSIE)|BRKIE|DCDIE|TxUIE);
-	
+	/* enable ABORT, DCD & SYNC/HUNT interrupts */
+
+	wr(scc,R15, BRKIE|TxUIE|DCDIE);
+	if (scc->kiss.softdcd)
+		or(scc,R15, SYNCIE);
+
 	Outb(scc->ctrl,RES_EXT_INT);	/* reset ext/status interrupts */
 	Outb(scc->ctrl,RES_EXT_INT);	/* must be done twice */
 
@@ -1287,7 +1375,7 @@
 	
 	scc_init_timer(scc);
 			
-	restore_flags(flags);
+	enable_irq(scc->irq);
 }
 
 
@@ -1305,21 +1393,29 @@
 scc_key_trx(struct scc_channel *scc, char tx)
 {
 	unsigned int time_const;
-
-	if (scc->modem.speed < baud_table[1]) 
-		scc->modem.speed = 1200;
 		
 	if (scc->brand & PRIMUS)
 		Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
-	
+
+	if (scc->modem.speed < 300) 
+		scc->modem.speed = 1200;
+
 	time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
-	
+
+	disable_irq(scc->irq);
+
+	if (tx)
+	{
+		or(scc, R1, TxINT_ENAB);	/* t_maxkeyup may have reset these */
+		or(scc, R15, TxUIE);
+	}
+
 	if (scc->modem.clocksrc == CLK_DPLL)
-	{				/* simplex operation */
+	{				/* force simplex operation */
 		if (tx)
 		{
-			cl(scc,R3,RxENABLE|ENT_HM);	/* then switch off receiver */
-			
+			cl(scc, R3, RxENABLE|ENT_HM);	/* switch off receiver */
+			cl(scc, R15, DCDIE);		/* No DCD changes, please */
 			set_brg(scc, time_const);	/* reprogram baudrate generator */
 
 			/* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
@@ -1335,21 +1431,140 @@
 			wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
 			
 			or(scc,R3,RxENABLE|ENT_HM);
+			or(scc,R15, DCDIE);
 		}
 	} else {
 		if (tx)
+		{
+			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			{
+				cl(scc, R3, RxENABLE);
+				cl(scc, R15, DCDIE);
+			}
+				
+				
 			or(scc,R5,RTS|TxENAB);		/* enable tx */
-		else
+		} else {
 			cl(scc,R5,RTS|TxENAB);		/* disable tx */
+			
+			if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			{
+				or(scc, R3, RxENABLE|ENT_HM);
+				or(scc, R15, DCDIE);
+			}
+		}
+	}
+
+	enable_irq(scc->irq);
+}
+
+
+/* ----> SCC timer interrupt handler and friends. <---- */
+
+static void 
+scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_t);
+
+	if (when == 0)
+	{
+		handler((unsigned long) scc);
+	} else 
+	if (when != TIMER_OFF)
+	{
+		scc->tx_t.data = (unsigned long) scc;
+		scc->tx_t.function = handler;
+		scc->tx_t.expires = jiffies + (when*HZ)/100;
+		add_timer(&scc->tx_t);
+	}
+	
+	restore_flags(flags);	
+}
+
+static void
+scc_start_defer(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	
+	if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
+	{
+		scc->tx_wdog.data = (unsigned long) scc;
+		scc->tx_wdog.function = t_busy;
+		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
+		add_timer(&scc->tx_wdog);
+	}
+	restore_flags(flags);
+}
+
+static void
+scc_start_maxkeyup(struct scc_channel *scc)
+{
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&scc->tx_wdog);
+	
+	if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
+	{
+		scc->tx_wdog.data = (unsigned long) scc;
+		scc->tx_wdog.function = t_maxkeyup;
+		scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
+		add_timer(&scc->tx_wdog);
 	}
+	
+	restore_flags(flags);
 }
 
+/* 
+ * This is called from scc_txint() when there are no more frames to send.
+ * Not exactly a timer function, but it is a close friend of the family...
+ */
+
+static void
+scc_tx_done(struct scc_channel *scc)
+{
+	/* 
+	 * trx remains keyed in fulldup mode 2 until t_idle expires.
+	 */
+				 
+	switch (scc->kiss.fulldup)
+	{
+		case KISS_DUPLEX_LINK:
+			scc->stat.tx_state = TXS_IDLE2;
+			if (scc->kiss.idletime != TIMER_OFF)
+			scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100);
+			break;
+		case KISS_DUPLEX_OPTIMA:
+			scc_notify(scc, HWEV_ALL_SENT);
+			break;
+		default:
+			scc->stat.tx_state = TXS_BUSY;
+			scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+	}
+
+#ifdef CONFIG_SCC_DEV
+	if (scc->stat.is_netdev)
+ 		scc->dev->tbusy = 0;
+#endif
+}
 
-/* ----> SCC timer interrupt handler and friends. Will be called every 1/TPS s <---- */
 
 static unsigned char Rand = 17;
 
-static inline int is_grouped(register struct scc_channel *scc)
+static __inline__ int 
+is_grouped(struct scc_channel *scc)
 {
 	int k;
 	struct scc_channel *scc2;
@@ -1362,8 +1577,8 @@
 		scc2 = &SCC_Info[k];
 		grp2 = scc2->kiss.group;
 		
-		if (scc2 == scc || !(scc2->tty && grp2)) 
-			return 0;
+		if (scc2 == scc || !(scc2->tty && scc2->dev && grp2))
+			continue;
 		
 		if ((grp1 & 0x3f) == (grp2 & 0x3f))
 		{
@@ -1376,205 +1591,244 @@
 	}
 	return 0;
 }
-		
 
-static inline void dw_slot_timeout(register struct scc_channel *scc)
+/* DWAIT and SLOTTIME expired
+ *
+ * fulldup == 0:  DCD is active or Rand > P-persistence: start t_busy timer
+ *                else key trx and start txdelay
+ * fulldup == 1:  key trx and start txdelay
+ * fulldup == 2:  mintime expired, reset status or key trx and start txdelay
+ */
+
+static void 
+t_dwait(unsigned long channel)
 {
-	scc->t_dwait = TIMER_STOPPED;
-	scc->t_slot = TIMER_STOPPED;
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	
-	if (!scc->kiss.fulldup)
+	if (scc->stat.tx_state == TXS_WAIT)
+	{
+		if (scc->tx_queue == NULL)
+		{
+			scc->stat.tx_state = TXS_IDLE;
+			return;
+		}
+
+		scc->stat.tx_state = TXS_BUSY;
+	}
+
+	if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
 	{
 		Rand = Rand * 17 + 31;
 		
 		if ( (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD))  || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
 		{
-			if (scc->t_mbusy == TIMER_STOPPED)
-				scc->t_mbusy = TPS * scc->kiss.maxdefer;
-				
-			scc->t_slot = scc->kiss.slottime;
+			scc_start_defer(scc);
+			scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
 			return ;
-			
 		}
 	}
-	
+
 	if ( !(scc->wreg[R5] & RTS) )
 	{
-		scc->t_txdel = scc->kiss.txdelay;
 		scc_key_trx(scc, TX_ON);
+		scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
 	} else {
-		scc->t_txdel = 0;
+		scc_start_tx_timer(scc, t_txdelay, 0);
 	}
 }
 
 
+/* TXDELAY expired
+ *
+ * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
+ */
 
-
-static inline void txdel_timeout(register struct scc_channel *scc)
+static void 
+t_txdelay(unsigned long channel)
 {
-	scc->t_txdel = TIMER_STOPPED;
-	
-	scc->t_maxk = TPS * scc->kiss.maxkeyup;
-	
+	struct scc_channel *scc = (struct scc_channel *) channel;
+
+	scc_start_maxkeyup(scc);
+
 	if (scc->tx_bp == NULLBUF)
+	{
+		disable_irq(scc->irq);
 		scc_txint(scc);	
+		enable_irq(scc->irq);
+	}
 }
 	
 
+/* TAILTIME expired
+ *
+ * switch off transmitter. If we were stopped by Maxkeyup restart
+ * transmission after 'mintime' seconds
+ */
 
-static inline void tail_timeout(register struct scc_channel *scc)
+static void 
+t_tail(unsigned long channel)
 {
-	scc->t_tail = TIMER_STOPPED;
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	
-	/* when fulldup is 0 or 1, switch off the transmitter.
-	 * when frames are still queued (because of transmit time limit),
-	 * restart the procedure to get the channel after MINTIME.
-	 * when fulldup is 2, the transmitter remains keyed and we
-	 * continue sending after waiting for waittime. IDLETIME is an 
-	 * idle timeout in this case.
-	 */ 
-	 
-	 if (scc->kiss.fulldup < 2)
-	 {
-	 	if (scc->tx_bp)		/* we had a timeout? */
-	 	{
-	 		scc->stat.tx_state = TXS_BUSY;
-	 		scc->t_dwait = TPS * scc->kiss.mintime; /* try again */
-	 	}
-	 	
-	 	scc->stat.tx_state = TXS_IDLE;
-		scc->t_maxk = TIMER_STOPPED;
-	 	scc_key_trx(scc, TX_OFF);
-	 	return;
-	 }
-	 
-	 if (scc->tx_bp)			/* maxkeyup expired */ /* ?! */
-	 {
-	 	scc->stat.tx_state = TXS_BUSY;
-	 	scc->t_txdel = TPS * scc->kiss.waittime;
-	 }
-	 else
-	 
-	 	scc->t_idle = TPS * scc->kiss.idletime;
+ 	del_timer(&scc->tx_wdog);
+ 	scc_key_trx(scc, TX_OFF);
+
+ 	if (scc->stat.tx_state == TXS_TIMEOUT)		/* we had a timeout? */
+ 	{
+ 		scc->stat.tx_state = TXS_WAIT;
+ 
+  		if (scc->tx_bp != NULLBUF)
+	 		scc_enqueue_buffer(&scc->tx_queue, scc->tx_bp);
+
+ 		scc->tx_bp = NULLBUF;
+
+ 		if (scc->kiss.mintime != TIMER_OFF)	/* try it again */
+ 			scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+ 		else
+ 			scc_start_tx_timer(scc, t_dwait, 0);
+ 		return;
+ 	}
+ 	
+ 	scc->stat.tx_state = TXS_IDLE;
+ 
+#ifdef CONFIG_SCC_DEV
+ 	if (scc->stat.is_netdev)
+		scc->dev->tbusy = 0;
+#endif
 }
 
 
-static inline void busy_timeout(register struct scc_channel *scc)
+/* BUSY timeout
+ *
+ * throw away send buffers if DCD remains active too long.
+ */
+
+static void 
+t_busy(unsigned long channel)
 {
-#ifdef	THROW_AWAY_AFTER_BUSY_TIMEOUT
-	register struct mbuf *bp;		/* not tested */
+	struct scc_channel *scc = (struct scc_channel *) channel;
+	struct mbuf *bp;
+
+	del_timer(&scc->tx_t);
 
-	while (bp = scc_dequeue_buffer(&scc->tx_queue))
+	while ((bp = scc_dequeue_buffer(&scc->tx_queue)) != NULLBUF)
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+
+	scc->stat.tx_queued = 0;
+	scc->stat.txerrs++;
 		
 	scc->tx_queue = NULLBUF;
 	scc->stat.tx_state = TXS_IDLE;
-	
-#else
-	scc->t_txdel = scc->kiss.txdelay;	/* brute force ... */
+
+#ifdef CONFIG_SCC_DEV
+ 	if (scc->stat.is_netdev)
+ 		scc->dev->tbusy = 0;
 #endif
-	scc->t_mbusy = TIMER_STOPPED;
-	
 }
 
+/* MAXKEYUP timeout
+ *
+ * this is our watchdog.
+ */
 
-static inline void maxk_idle_timeout(register struct scc_channel *scc)
-{
-	scc->t_maxk = TIMER_STOPPED;
-	scc->t_idle = TIMER_STOPPED;
-	
-	scc->stat.tx_state = TXS_BUSY;
-	scc->t_tail = scc->kiss.tailtime;
-}
-
-static void
-scc_tx_timer(unsigned long channel)
+static void 
+t_maxkeyup(unsigned long channel)
 {
-	register struct scc_channel *scc;
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	unsigned long flags;
-		
 
-	scc = (struct scc_channel *) channel;
-		
-	if (scc->tty && scc->init)
-	{		
-		save_flags(flags); cli();
-		
-		/* KISS-TNC emulation */
-		
-		if (Expired(t_dwait)) dw_slot_timeout(scc)	; else
-		if (Expired(t_slot))  dw_slot_timeout(scc)	; else
-		if (Expired(t_txdel)) txdel_timeout(scc)   	; else
-		if (Expired(t_tail))  tail_timeout(scc)	   	;
-			
-		/* watchdogs */
-		
-		if (Expired(t_mbusy)) busy_timeout(scc);
-		if (Expired(t_maxk))  maxk_idle_timeout(scc);
-		if (Expired(t_idle))  maxk_idle_timeout(scc);
-			
-		restore_flags(flags);
+	save_flags(flags);
+	cli();
+
+	if (scc->stat.is_netdev)
+	{
+#ifdef CONFIG_SCC_DEV
+		/* 
+		 * let things settle down before we start to
+		 * accept new data.
+		 */
+		scc->dev->tbusy = 1;
+		scc->dev->trans_start = jiffies;
+#endif
 	}
+
+	del_timer(&scc->tx_t);
+
+	cl(scc, R1, TxINT_ENAB);	/* force an ABORT, but don't */
+	cl(scc, R15, TxUIE);		/* count it. */
+	OutReg(scc->ctrl, R0, RES_Tx_P);
+
+	restore_flags(flags);
+
+	scc->stat.txerrs++;
+	scc->stat.tx_state = TXS_TIMEOUT;
+	scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+}
+
+/* IDLE timeout
+ *
+ * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
+ * of inactivity. We will not restart transmission before 'mintime'
+ * expires.
+ */
+
+static void 
+t_idle(unsigned long channel)
+{
+	struct scc_channel *scc = (struct scc_channel *) channel;
 	
-	scc->tx_t.expires = jiffies + HZ/TPS;
-	add_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+	scc_key_trx(scc, TX_OFF);
+
+	if (scc->kiss.mintime != TIMER_OFF)
+		scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
+	scc->stat.tx_state = TXS_WAIT;
 }
-			
 
+
+#ifdef CONFIG_SCC_TTY
 static void
 scc_rx_timer(unsigned long channel)
 {
-	register struct scc_channel *scc;
+	struct scc_channel *scc;
 	
 	scc = (struct scc_channel *) channel;
 	
 	if (scc->rx_queue && scc->throttled)
 	{
-		scc->rx_t.expires = jiffies + HZ/TPS;
+		scc->rx_t.expires = jiffies + HZ/25;	/* 40 msec */
 		add_timer(&scc->rx_t);
 		return;
 	}
 	
 	kiss_encode(scc);
-	
+
 	if (scc->rx_queue && !scc->throttled)
 	{
 
-		printk("z8530drv: warning: %s should be throttled\n", 
-		       kdevname(scc->tty->device));
-			       
-		scc->rx_t.expires = jiffies + HZ/TPS;
+		printk(KERN_DEBUG "z8530drv: warning, %s should be throttled\n", 
+		       SCC_DEVNAME);
+		       
+		scc->rx_t.expires = jiffies + HZ/25;	/* 40 msec */
 		add_timer(&scc->rx_t);
 	}
 }
+#endif
 
 static void
 scc_init_timer(struct scc_channel *scc)
 {
 	unsigned long flags;
 	
-	save_flags(flags); cli();
-	
-	Stop_Timer(t_dwait);
-	Stop_Timer(t_slot);
-	Stop_Timer(t_txdel);
-	Stop_Timer(t_tail);
-	Stop_Timer(t_mbusy);
-	Stop_Timer(t_maxk);
-	Stop_Timer(t_idle);
+	save_flags(flags); 
+	cli();
 	
 	scc->stat.tx_state = TXS_IDLE;
 	
-	if (scc->tx_t.next) 
-		del_timer(&scc->tx_t);
-	
-	scc->tx_t.data = (unsigned long) scc;
-	scc->tx_t.function = scc_tx_timer;
-	scc->tx_t.expires = jiffies + HZ/TPS;
-	add_timer(&scc->tx_t);
-	
+#ifdef CONFIG_SCC_TTY	
 	scc->rx_t.data = (unsigned long) scc;
 	scc->rx_t.function = scc_rx_timer;
+#endif
 	
 	restore_flags(flags);
 }
@@ -1586,54 +1840,111 @@
 
 
 /*
- * this will set the "kiss" parameters through kiss itself
+ * this will set the "kiss" parameters through KISS commands or ioctl()
  */
- 
-static void
-kiss_set_param(struct scc_channel *scc,char cmd, unsigned int val)
+
+#define CAST(x) (unsigned long)(x)
+
+static unsigned int
+kiss_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
 {
+	int dcd;
+
+	switch (cmd)
+	{
+		case PARAM_TXDELAY:	scc->kiss.txdelay=arg;		break;
+		case PARAM_PERSIST:	scc->kiss.persist=arg;		break;
+		case PARAM_SLOTTIME:	scc->kiss.slottime=arg;		break;
+		case PARAM_TXTAIL:	scc->kiss.tailtime=arg;		break;
+		case PARAM_FULLDUP:	scc->kiss.fulldup=arg;		break;
+		case PARAM_DTR:		break; /* does someone need this? */
+		case PARAM_GROUP:	scc->kiss.group=arg;		break;
+		case PARAM_IDLE:	scc->kiss.idletime=arg;		break;
+		case PARAM_MIN:		scc->kiss.mintime=arg;		break;
+		case PARAM_MAXKEY:	scc->kiss.maxkeyup=arg;		break;
+		case PARAM_WAIT:	scc->kiss.waittime=arg;		break;
+		case PARAM_MAXDEFER:	scc->kiss.maxdefer=arg;		break;
+		case PARAM_TX:		scc->kiss.tx_inhibit=arg;	break;
+
+		case PARAM_SOFTDCD:	
+			scc->kiss.softdcd=arg;
+			if (arg)
+				or(scc, R15, SYNCIE);
+			else
+				cl(scc, R15, SYNCIE);
+			break;
+				
+		case PARAM_SPEED:
+			if (arg < 256)
+				scc->modem.speed=arg*100;
+			else
+				scc->modem.speed=arg;
+
+			if (scc->stat.tx_state == 0)	/* only switch baudrate on rx... ;-) */
+				set_speed(scc);
+			break;
+			
+		case PARAM_RTS:	
+			if ( !(scc->wreg[R5] & RTS) )
+			{
+				if (arg != TX_OFF)
+					scc_key_trx(scc, TX_ON);
+					scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+			} else {
+				if (arg == TX_OFF)
+				{
+					scc->stat.tx_state = TXS_BUSY;
+					scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
+				}
+			}
+			break;
+			
+		case PARAM_HWEVENT:
+			dcd = (scc->kiss.softdcd? !(scc->status & SYNC_HUNT):(scc->status & DCD));
+			scc_notify(scc, dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
+			break;
+
+		default:		return -EINVAL;
+	}
+	
+	return 0;
+}
 
-#define  VAL val=val*TPS/100
-#define SVAL val? val:TIMER_STOPPED
 
-	switch(cmd){
-	case PARAM_TXDELAY:
-		scc->kiss.txdelay = VAL; break;
-	case PARAM_PERSIST:
-		scc->kiss.persist = val; break;
-	case PARAM_SLOTTIME:
-		scc->kiss.slottime = VAL; break;
-	case PARAM_TXTAIL:
-		scc->kiss.tailtime = VAL; break;
-	case PARAM_FULLDUP:
-		scc->kiss.fulldup = val; break;
-	case PARAM_WAIT:
-		scc->kiss.waittime = VAL; break;
-	case PARAM_MAXKEY:
-		scc->kiss.maxkeyup = SVAL; break;
-	case PARAM_MIN:
-		scc->kiss.mintime = SVAL; break;
-	case PARAM_IDLE:
-		scc->kiss.idletime = val; break;
-	case PARAM_MAXDEFER:
-		scc->kiss.maxdefer = SVAL; break;
-	case PARAM_GROUP:
-		scc->kiss.group = val;  break;
-	case PARAM_TX:
-		scc->kiss.tx_inhibit = val;
-	case PARAM_SOFTDCD:
-		scc->kiss.softdcd = val;
+ 
+static unsigned long
+kiss_get_param(struct scc_channel *scc, unsigned int cmd)
+{
+	switch (cmd)
+	{
+		case PARAM_TXDELAY:	return CAST(scc->kiss.txdelay);
+		case PARAM_PERSIST:	return CAST(scc->kiss.persist);
+		case PARAM_SLOTTIME:	return CAST(scc->kiss.slottime);
+		case PARAM_TXTAIL:	return CAST(scc->kiss.tailtime);
+		case PARAM_FULLDUP:	return CAST(scc->kiss.fulldup);
+		case PARAM_SOFTDCD:	return CAST(scc->kiss.softdcd);
+		case PARAM_DTR:		return CAST((scc->wreg[R5] & DTR)? 1:0);
+		case PARAM_RTS:		return CAST((scc->wreg[R5] & RTS)? 1:0);
+		case PARAM_SPEED:	return CAST(scc->modem.speed);
+		case PARAM_GROUP:	return CAST(scc->kiss.group);
+		case PARAM_IDLE:	return CAST(scc->kiss.idletime);
+		case PARAM_MIN:		return CAST(scc->kiss.mintime);
+		case PARAM_MAXKEY:	return CAST(scc->kiss.maxkeyup);
+		case PARAM_WAIT:	return CAST(scc->kiss.waittime);
+		case PARAM_MAXDEFER:	return CAST(scc->kiss.maxdefer);
+		case PARAM_TX:		return CAST(scc->kiss.tx_inhibit);
+		default:		return NO_SUCH_PARAM;
 	}
-	return;
 
-#undef  VAL
-#undef SVAL
 }
 
+#undef CAST
+#undef SVAL
 
 /* interpret frame: strip CRC and decode KISS */
 
-static void kiss_interpret_frame(struct scc_channel * scc)
+static void 
+kiss_interpret_frame(struct scc_channel * scc)
 {
 	unsigned char kisscmd;
 	unsigned long flags;
@@ -1642,14 +1953,6 @@
 	bp = scc->kiss_decode_bp;
 	bp->rw_ptr = bp->data;
 	
-#ifdef DEBUG_BUFFERS
-	if (bp == NULLBUF)
-	{
-		printk("kiss_interpret_frame(): weird --- nothing to do.\n");
-		return;
-	}
-#endif
-	
 	if (bp->cnt < 2)
 	{
 		scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
@@ -1657,15 +1960,8 @@
 		return;
 	}
 	
-	
-	
-	if (scc->kiss.not_slip)
-	{
-		kisscmd = *bp->rw_ptr;
-		bp->rw_ptr++;
-	} else {
-		kisscmd = 0;
-	}
+	kisscmd = *bp->rw_ptr;
+	bp->rw_ptr++;
 
 	if (kisscmd & 0xa0)
 	{
@@ -1679,10 +1975,8 @@
 		}
 	}
 	
-	
 	kisscmd &= 0x1f;
-	
-		
+			
 	if (kisscmd)
 	{
 		kiss_set_param(scc, kisscmd, *bp->rw_ptr);
@@ -1692,32 +1986,47 @@
 	}
 
 	scc_enqueue_buffer(&scc->tx_queue, bp);	/* enqueue frame */
+	scc->kiss_decode_bp = NULLBUF;
 	
 	scc->stat.txframes++;
 	scc->stat.tx_queued++;
-	scc->kiss_decode_bp = NULLBUF;
 
-	save_flags(flags); cli();
+	save_flags(flags); 
+	cli();
+
+	/*
+	 * start transmission if the trx state is idle or
+	 * t_idle hasn't expired yet. Use dwait/persistance/slottime
+	 * algorithm for normal halfduplex operation.
+	 */
 
-	if(scc->stat.tx_state == TXS_IDLE)
-	{      				/* when transmitter is idle */
+	if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2)
+	{
 		scc->stat.tx_state = TXS_BUSY;
-		scc->t_dwait = (scc->kiss.fulldup? 0:scc->kiss.waittime);
+		if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+			scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
+		else
+			scc_start_tx_timer(scc, t_dwait, 0);
 	}
-	
+
 	restore_flags(flags);
 }
 
-static inline void kiss_store_byte(struct scc_channel *scc, unsigned char ch)
+#ifdef CONFIG_SCC_TTY
+static inline void 
+kiss_store_byte(struct scc_channel *scc, unsigned char ch)
 {
-	register struct mbuf *bp = scc->kiss_decode_bp;
+	struct mbuf *bp = scc->kiss_decode_bp;
+	static int k = 0;
 	
 	if (bp != NULLBUF)
 	{
 		if (bp->cnt > scc->stat.bufsize)
-			printk("kiss_decode(): frame too long\n");
-		else
 		{
+			if (!k++)
+				printk(KERN_NOTICE "z8530drv: KISS frame too long\n");
+		} else {
+			k = 0;
 			*bp->rw_ptr = ch;
 			bp->rw_ptr++;
 			bp->cnt++;
@@ -1725,7 +2034,8 @@
 	}
 }
 
-static inline int kiss_decode(struct scc_channel *scc, unsigned char ch)
+static inline int 
+kiss_decode(struct scc_channel *scc, unsigned char ch)
 {
 	switch (scc->stat.tx_kiss_state) 
 	{
@@ -1779,7 +2089,7 @@
 /* ----> Encode received data and write it to the flip-buffer  <---- */
 
 static void
-kiss_encode(register struct scc_channel *scc)
+kiss_encode(struct scc_channel *scc)
 {
 	struct mbuf *bp;
 	struct tty_struct * tty = scc->tty;
@@ -1825,10 +2135,6 @@
 		if (scc->stat.rx_kiss_state == KISS_IDLE)
 		{
 			tty_insert_flip_char(tty, FEND, 0);
-			
-			if (scc->kiss.not_slip)
-				tty_insert_flip_char(tty, 0, 0);
-					
 			scc->stat.rx_kiss_state = KISS_RXFRAME;
 		}
 				
@@ -1852,6 +2158,7 @@
 		
  	queue_task(&tty->flip.tqueue, &tq_timer); /* kick it... */
 }
+#endif /* CONFIG_SCC_TTY */
 
 
 /* ******************************************************************* */
@@ -1868,7 +2175,7 @@
 	char *flag;
 
 
-	printk("Init Z8530 driver: %u channels, IRQ", Nchips*2);
+	printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
 	
 	flag=" ";
 	for (k = 0; k < 16; k++)
@@ -1885,9 +2192,7 @@
 	{
 		scc=&SCC_Info[2*chip];
 		if (!scc->ctrl) continue;
-			
-		save_flags(flags); cli();	/* because of 2-step accesses */
-		
+
 		/* Special SCC cards */
 
 		if(scc->brand & EAGLE)			/* this is an EAGLE card */
@@ -1901,12 +2206,15 @@
 
 		/* some general init we can do now */
 		
+		save_flags(flags);
+		cli();
+		
 		Outb(scc->ctrl, 0);
 		OutReg(scc->ctrl,R9,FHWRES);		/* force hardware reset */
 		udelay(100);				/* give it 'a bit' more time than required */
 		wr(scc, R2, chip*16);			/* interrupt vector */
 		wr(scc, R9, VIS);			/* vector includes status */
-
+		
         	restore_flags(flags);
         }
 
@@ -1920,18 +2228,17 @@
 /* ******************************************************************** */
 
 
-
 /* scc_paranoia_check(): warn user if something went wrong		*/
 
-static inline int scc_paranoia_check(struct scc_channel *scc, kdev_t device,
-				     const char *routine)
+static __inline__ int 
+scc_paranoia_check(struct scc_channel *scc, kdev_t device, const char *routine)
 {
 #ifdef SCC_PARANOIA_CHECK
 
 static const char *badmagic = 
-	"Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; 
+	KERN_ALERT "Warning: bad magic number for Z8530 SCC struct (%s) in %s\n"; 
 static const char *badinfo =  
-	"Warning: Z8530 not found for (%s) in %s\n";
+	KERN_CRIT "Warning: Z8530 not found for (%s) in %s (forgot to run sccinit?)\n";
        
 	if (!scc->init) 
 	{
@@ -1949,10 +2256,11 @@
 	return 0;
 }
 
-
+#ifdef CONFIG_SCC_TTY
 /* ----> this one is called whenever you open the device <---- */
 
-int scc_open(struct tty_struct *tty, struct file * filp)
+int 
+scc_open(struct tty_struct *tty, struct file * filp)
 {
 	struct scc_channel *scc;
 	int chan;
@@ -1976,28 +2284,30 @@
  	
 	if (scc->magic != SCC_MAGIC)
 	{
-		printk("ERROR: scc_open(): bad magic number for device (%s)",
+		printk(KERN_ALERT "z8530drv: scc_open() found bad magic number for device (%s)",
 		       kdevname(tty->device));
 		return -ENODEV;
-	}		
-	
+	}
+
 	MOD_INC_USE_COUNT;
-	
-	if(scc->tty != NULL)
+
+	if(scc->tty != NULL || scc->stat.is_netdev)
 	{
 		scc->tty_opened++;
 		return 0;
 	}
+	
+	scc->tty = tty;
 
-  	scc->tty = tty;
+ 	if(!scc->init) 
+ 		return 0;
+ 
 	alloc_buffer_pool(scc);
-	
- 	if(!scc->init) return 0;
  	
 	scc->throttled = 0;
 
-	scc->stat.tx_kiss_state = KISS_IDLE;	/* don't change this... */
-	scc->stat.rx_kiss_state = KISS_IDLE;	/* ...or this */
+	scc->stat.tx_kiss_state = KISS_IDLE;
+	scc->stat.rx_kiss_state = KISS_IDLE;
  
 	init_channel(scc);
 	return 0;
@@ -2014,10 +2324,11 @@
 
         if (!scc || (scc->magic != SCC_MAGIC))
                 return;
+
                 
         MOD_DEC_USE_COUNT;
 	
-	if(scc->tty_opened)
+	if(scc->tty_opened || scc->stat.is_netdev)
 	{
 		scc->tty_opened--;
 		return;
@@ -2028,21 +2339,26 @@
 	if (!Driver_Initialized)
 		return;
 	
-	save_flags(flags); cli();
+	save_flags(flags);
+	cli();
 	
 	Outb(scc->ctrl,0);		/* Make sure pointer is written */
 	wr(scc,R1,0);			/* disable interrupts */
 	wr(scc,R3,0);
 	
 	scc->tty = NULL;
-	
+
 	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
 	del_timer(&scc->rx_t);
 
-	free_buffer_pool(scc);
-	
 	restore_flags(flags);
 	
+	if (!scc->init)
+		return;
+
+	free_buffer_pool(scc);
+	
 	scc->throttled = 0;
 	tty->stopped = 0;
 }
@@ -2068,135 +2384,144 @@
 	if (scc->stat.tx_state == 0)	/* only switch baudrate on rx... ;-) */
 		set_speed(scc);
 }
-
+#endif /* CONFIG_SCC_TTY */
 
 /* ----> ioctl-routine of the driver <---- */
 
-/* perform ioctl on SCC (sdlc) channel
- * this is used for AX.25 mode, and will set the "kiss" parameters
- */
+
+/* sub routines to read/set KISS parameters */
  
-/* TIOCMGET 	- get modem status	arg: (unsigned long *) arg
- * TIOCMBIS 	- set PTT		arg: ---
- * TIOCMBIC 	- reset PTT		arg: ---
- * TIOCMBIC 	- set PTT		arg: ---
- * TIOCSCCINI	- initialize driver	arg: ---
- * TIOCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
- * TIOCGKISS	- get level 1 parameter	arg: (struct ioctl_command *) arg
- * TIOCSKISS	- set level 1 parameter arg: (struct ioctl_command *) arg
- * TIOCSCCSTAT  - get driver status	arg: (struct scc_stat *) arg
+/* generic ioctl() functions for both TTY or network device mode:
+ *
+ * TIOCSCCCFG		- configure driver	arg: (struct scc_hw_config *) arg
+ * TIOCSCCINI		- initialize driver	arg: ---
+ * TIOCSCCCHANINI	- initialize channel	arg: (struct scc_modem *) arg
+ * TIOCSCCGKISS		- get level 1 parameter	arg: (struct scc_kiss_cmd *) arg
+ * TIOCSCCSKISS		- set level 1 parameter arg: (struct scc_kiss_cmd *) arg
+ * TIOCSCCSTAT		- get driver status	arg: (struct scc_stat *) arg
  */
  
-
 static int
-scc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+scc_ioctl(struct scc_channel *scc, unsigned int cmd, void *arg)
 {
-	struct scc_channel * scc = tty->driver_data;
-	unsigned long flags, r;
-	unsigned int result;
-	unsigned int value;
-	struct ioctl_command kiss_cmd;
+	unsigned long flags;
+	struct scc_kiss_cmd kiss_cmd;
 	struct scc_mem_config memcfg;
 	struct scc_hw_config hwcfg;
 	int error, chan;
-
-        if (scc->magic != SCC_MAGIC) 
-        {
-		printk("ERROR: scc_ioctl(): bad magic number for device %s",
-			kdevname(tty->device));
-			
-                return -ENODEV;
-        }
-	                                                 
-	r = NO_SUCH_PARAM;
+	unsigned char device_name[10];
 	
 	if (!Driver_Initialized)
 	{
 		if (cmd == TIOCSCCCFG)
 		{
 			int found = 1;
-			
+
 			if (!suser()) return -EPERM;
 			if (!arg) return -EFAULT;
-			
+
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_hw_config));
+			if (error) return error;
+
 			if (Nchips >= MAXSCC) 
 				return -EINVAL;
-			
-			memcpy_fromfs(&hwcfg, (void *) arg, sizeof(hwcfg));
-			
+
+			memcpy_fromfs(&hwcfg, arg, sizeof(hwcfg));
+
 			if (hwcfg.irq == 2) hwcfg.irq = 9;
-			
+
 			if (!Ivec[hwcfg.irq].used && hwcfg.irq)
 			{
 				if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
-					printk("z8530drv: Warning --- could not get IRQ %d\n", hwcfg.irq);
+					printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
 				else
 					Ivec[hwcfg.irq].used = 1;
 			}
-			
+
 			if (hwcfg.vector_latch) 
 				Vector_Latch = hwcfg.vector_latch;
-				
+
 			if (hwcfg.clock == 0)
 				hwcfg.clock = DEFAULT_CLOCK;
 
 #ifndef DONT_CHECK
-			save_flags(flags); cli();
-			
+			disable_irq(hwcfg.irq);
+
 			check_region(scc->ctrl, 1);
 			Outb(hwcfg.ctrl_a, 0);
 			udelay(5);
 			OutReg(hwcfg.ctrl_a,R13,0x55);		/* is this chip really there? */
 			udelay(5);
-			
-			if (InReg(hwcfg.ctrl_a,R13) != 0x55 )
+
+			if (InReg(hwcfg.ctrl_a,R13) != 0x55)
 				found = 0;
-				
-			restore_flags(flags);
+
+			enable_irq(hwcfg.irq);
 #endif
 
 			if (found)
 			{
 				SCC_Info[2*Nchips  ].ctrl = hwcfg.ctrl_a;
 				SCC_Info[2*Nchips  ].data = hwcfg.data_a;
+				SCC_Info[2*Nchips  ].irq  = hwcfg.irq;
 				SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
 				SCC_Info[2*Nchips+1].data = hwcfg.data_b;
+				SCC_Info[2*Nchips+1].irq  = hwcfg.irq;
 			
-				SCC_ctrl[2*Nchips  ] = hwcfg.ctrl_a;
-				SCC_ctrl[2*Nchips+1] = hwcfg.ctrl_b;
+				SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
+				SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
+				SCC_ctrl[Nchips].irq    = hwcfg.irq;
 			}
-		
+
+
 			for (chan = 0; chan < 2; chan++)
 			{
+				sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
+
 				SCC_Info[2*Nchips+chan].special = hwcfg.special;
 				SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
 				SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
 				SCC_Info[2*Nchips+chan].option = hwcfg.option;
 				SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
-			
+
 #ifdef DONT_CHECK
-				printk("%s%i: data port = 0x%3.3x  control port = 0x%3.3x\n",
-					scc_driver.name, 2*Nchips+chan, 
+				printk(KERN_INFO "%s: data port = 0x%3.3x  control port = 0x%3.3x\n",
+					device_name, 
 					SCC_Info[2*Nchips+chan].data, 
 					SCC_Info[2*Nchips+chan].ctrl);
 
 #else
-				printk("%s%i: data port = 0x%3.3x  control port = 0x%3.3x -- %s\n",
-					scc_driver.name, 2*Nchips+chan, 
+				printk(KERN_INFO "%s: data port = 0x%3.3x  control port = 0x%3.3x -- %s\n",
+					device_name,
 					chan? hwcfg.data_b : hwcfg.data_a, 
 					chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
 					found? "found" : "missing");
 #endif
-				
+
 				if (found)
 				{
 					request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
 					request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
+#ifdef CONFIG_SCC_DEV
+					if (Nchips+chan != 0)
+						scc_net_setup(&SCC_Info[2*Nchips+chan], device_name);
+#endif
 				}
 			}
 			
 			if (found) Nchips++;
 			
+#ifdef SCC_DEBUG
+			printk(KERN_INFO "Available modes:"
+#ifdef CONFIG_SCC_DEV
+			" dev"
+#endif
+#ifdef CONFIG_SCC_TTY
+			" tty"
+#endif
+			"\n");
+#endif
+			
 			return 0;
 		}
 		
@@ -2209,9 +2534,6 @@
 				return -EINVAL;
 			
 			z8530_init();
-			
-			scc->tty=tty;
-			alloc_buffer_pool(scc);
 			return 0;
 		}
 		
@@ -2220,263 +2542,268 @@
 	
 	if (!scc->init)
 	{
-		if (cmd == TIOCCHANINI)
+		if (cmd == TIOCSCCCHANINI)
 		{
-			if (!arg)
-				return -EFAULT;
-				
-			if (!suser())
-				return -EPERM;
-			
-			memcpy_fromfs(&scc->modem, (void *) arg, sizeof(struct scc_modem));
+			if (!suser()) return -EPERM;
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_modem));
+			if (error) return error;
+
+			scc->stat.rxbuffers = RXBUFFERS;
+			scc->stat.txbuffers = TXBUFFERS;
+			scc->stat.bufsize   = BUFSIZE;
+
+			memcpy_fromfs(&scc->modem, arg, sizeof(struct scc_modem));
 			
 			/* default KISS Params */
 		
 			if (scc->modem.speed < 4800)
 			{
-				scc->kiss.txdelay = 36*TPS/100;    /* 360 ms */
-				scc->kiss.persist = 42;            /* 25% persistence */			/* was 25 */
-				scc->kiss.slottime = 16*TPS/100;   /* 160 ms */
-				scc->kiss.tailtime = 4;            /* minimal reasonable value */
-				scc->kiss.fulldup = 0;             /* CSMA */
-				scc->kiss.waittime = 50*TPS/100;   /* 500 ms */
-				scc->kiss.maxkeyup = 10;           /* 10 s */
-				scc->kiss.mintime = 3;             /* 3 s */
-				scc->kiss.idletime = 30;           /* 30 s */
-				scc->kiss.maxdefer = 120;	   /* 2 min */
-				scc->kiss.not_slip = 1;		   /* KISS mode */
-				scc->kiss.softdcd = 0;		   /* hardware dcd */
+				scc->kiss.txdelay = 36;		/* 360 ms */
+				scc->kiss.persist = 42;		/* 25% persistence */			/* was 25 */
+				scc->kiss.slottime = 16;	/* 160 ms */
+				scc->kiss.tailtime = 4;		/* minimal reasonable value */
+				scc->kiss.fulldup = 0;		/* CSMA */
+				scc->kiss.waittime = 50;	/* 500 ms */
+				scc->kiss.maxkeyup = 10;	/* 10 s */
+				scc->kiss.mintime = 3;		/* 3 s */
+				scc->kiss.idletime = 30;	/* 30 s */
+				scc->kiss.maxdefer = 120;	/* 2 min */
+				scc->kiss.softdcd = 0;		/* hardware dcd */
 			} else {
-				scc->kiss.txdelay = 10*TPS/100;    /* 100 ms */
-				scc->kiss.persist = 64;            /* 25% persistence */			/* was 25 */
-				scc->kiss.slottime = 8*TPS/100;    /* 160 ms */
-				scc->kiss.tailtime = 1;            /* minimal reasonable value */
-				scc->kiss.fulldup = 0;             /* CSMA */
-				scc->kiss.waittime = 50*TPS/100;   /* 500 ms */
-				scc->kiss.maxkeyup = 7;            /* 7 s */
-				scc->kiss.mintime = 3;             /* 3 s */
-				scc->kiss.idletime = 30;           /* 30 s */
-				scc->kiss.maxdefer = 120;	   /* 2 min */
-				scc->kiss.not_slip = 1;		   /* KISS mode */
-				scc->kiss.softdcd = 0;		   /* hardware dcd */
+				scc->kiss.txdelay = 10;		/* 100 ms */
+				scc->kiss.persist = 64;		/* 25% persistence */			/* was 25 */
+				scc->kiss.slottime = 8;		/* 160 ms */
+				scc->kiss.tailtime = 1;		/* minimal reasonable value */
+				scc->kiss.fulldup = 0;		/* CSMA */
+				scc->kiss.waittime = 50;	/* 500 ms */
+				scc->kiss.maxkeyup = 7;		/* 7 s */
+				scc->kiss.mintime = 3;		/* 3 s */
+				scc->kiss.idletime = 30;	/* 30 s */
+				scc->kiss.maxdefer = 120;	/* 2 min */
+				scc->kiss.softdcd = 0;		/* hardware dcd */
 			}
 			
-			scc->init = 1;			
+			scc->init = 1;
 			
 			return 0;
 		}
 		
 		return -EINVAL;
 	}
-
-	switch(cmd){
-	case TCSBRK:
-		return 0;
-	case TIOCMGET:
-		error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
-		if (error)
-			return error;
-
-		save_flags(flags); cli();
-		
-		result =  ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0)
-			| ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0)
-			| ((InReg(scc->ctrl,R0) & DCD)  ? TIOCM_CAR : 0)
-			| ((InReg(scc->ctrl,R0) & CTS)  ? TIOCM_CTS : 0);
+	
+	switch(cmd)
+	{
+		case TIOCSCCSMEM:
+		case TIOCCHANMEM_OLD:
+			if (!suser()) return -EPERM;
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
+	
+			memcpy_fromfs(&memcfg, arg, sizeof(struct scc_mem_config));
+
+			save_flags(flags); 
+			cli();
+
+			free_buffer_pool(scc);
+			scc->stat.rxbuffers = memcfg.rxbuffers;
+			scc->stat.txbuffers = memcfg.txbuffers;
+			scc->stat.bufsize   = memcfg.bufsize;
+			if (scc->tty || (scc->dev && (scc->dev->flags & IFF_UP)))
+				alloc_buffer_pool(scc);
 		
-		restore_flags(flags);
+			restore_flags(flags);
+			return 0;
+		
+		case TIOCSCCGSTAT:
+		case TIOCSCCSTAT_OLD:
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
 			
-		put_user(result,(unsigned int *) arg);
-		return 0;
-	case TIOCMBIS:
-	case TIOCMBIC:
-	case TIOCMSET:
-		switch (cmd) {
-		case TIOCMBIS:
-			scc->wreg[R5] |= DTR;
-			scc->wreg[R5] |= RTS;
+			memcpy_tofs(arg, &scc->stat, sizeof(struct scc_stat));
+			return 0;
+		
+		case TIOCSCCGKISS:
+		case TIOCGKISS_OLD:
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_WRITE, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
+		
+			memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd));
+			kiss_cmd.param = kiss_get_param(scc, kiss_cmd.command);
+			memcpy_tofs(arg, &kiss_cmd, sizeof(struct scc_kiss_cmd));
+			return 0;
 			break;
-		case TIOCMBIC:
-			scc->wreg[R5] &= ~DTR;
-			scc->wreg[R5] &= ~RTS;
+		
+		case TIOCSCCSKISS:
+		case TIOCSKISS_OLD:
+			if (!suser()) return -EPERM;
+			if (!arg) return -EINVAL;
+			error = verify_area(VERIFY_READ, arg, sizeof(struct scc_mem_config));
+			if (error) return error;
+
+			memcpy_fromfs(&kiss_cmd, arg, sizeof(struct scc_kiss_cmd));
+			return kiss_set_param(scc, kiss_cmd.command, kiss_cmd.param);
 			break;
-		case TIOCMSET:
-			value = get_user((unsigned int *) arg);
-			
-			if(value & TIOCM_DTR)
-				scc->wreg[R5] |= DTR;
-			else
-				scc->wreg[R5] &= ~DTR;
-			if(value & TIOCM_RTS)
-				scc->wreg[R5] |= RTS;
-			else
-				scc->wreg[R5] &= ~RTS;
-			break;
-		}
-		
-		save_flags(flags); cli();
+
+		default:
+			return -ENOIOCTLCMD;
 		
-		if(scc->stat.tx_state == TXS_IDLE && !Running(t_idle))
-			maxk_idle_timeout(scc);
+	}
+	
+	return 0;
+}
+
+#ifdef CONFIG_SCC_TTY
+/* addtional ioctl() for TTY driver mode:
+ *
+ * TIOCMGET 	- get modem status	arg: (unsigned long *) arg
+ * TIOCMBIS 	- set PTT		arg: ---
+ * TIOCMBIC 	- reset PTT		arg: ---
+ * TIOCMBIC 	- set PTT		arg: ---
+ */
+
+static int
+scc_tty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct scc_channel * scc = tty->driver_data;
+	unsigned long flags;
+	unsigned int result;
+	unsigned int value;
+	int error;
+
+        if (scc->magic != SCC_MAGIC) 
+        {
+		printk(KERN_ALERT "z8530drv: scc_ioctl() found bad magic number for device %s",
+			SCC_DEVNAME);
 			
-		restore_flags(flags);
-		
-		return 0;
-		
-	case TCGETS:
-		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios));
-		if (error)
-			return error;
-		if (!arg) 
-			return -EFAULT;
+                return -ENODEV;
+        }
+	                                                 
+	if (!Driver_Initialized && cmd == TIOCSCCINI)
+		scc->tty=tty;
+
+	switch(cmd)
+	{
+		case TIOCSCCCFG:
+		case TIOCSCCINI:
+		case TIOCSCCCHANINI:
+		case TIOCSCCSMEM:
+		case TIOCSCCSKISS:
+		case TIOCSCCGKISS:
+
+		case TIOCSCCCFG_OLD:
+		case TIOCSCCINI_OLD:
+		case TIOCCHANINI_OLD:
+		case TIOCCHANMEM_OLD:
+		case TIOCSKISS_OLD:
+		case TIOCGKISS_OLD:
+		case TIOCSCCSTAT_OLD:
+			return scc_ioctl(scc, cmd, (void *) arg);
+
+		case TCSBRK:
+			return 0;
 			
-		memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios));
-		return 0;
-		
-	case TCSETS:
-	case TCSETSF:		/* should flush first, but... */
-	case TCSETSW:		/* should wait 'till flush, but... */
-		if (!arg)
-			return -EFAULT;
-		
-		memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios));
-		scc_change_speed(scc);
-		return 0;
+		case TIOCMGET:
+			error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(unsigned int *));
+			if (error)
+				return error;
+
+			save_flags(flags);
+			cli();
+		
+			result =  ((scc->wreg[R5] & RTS) ? TIOCM_RTS : 0)
+				| ((scc->wreg[R5] & DTR) ? TIOCM_DTR : 0)
+				| ((InReg(scc->ctrl,R0) & DCD)  ? TIOCM_CAR : 0)
+				| ((InReg(scc->ctrl,R0) & CTS)  ? TIOCM_CTS : 0);
 		
-	case TIOCCHANMEM:
-		if (!arg)
-			return -EFAULT;
-			
-		memcpy_fromfs(&memcfg, (void *) arg, sizeof(struct scc_mem_config));
-		
-		save_flags(flags); cli();
-		
-		free_buffer_pool(scc);
-		scc->stat.rxbuffers = memcfg.rxbuffers;
-		scc->stat.txbuffers = memcfg.txbuffers;
-		scc->stat.bufsize   = memcfg.bufsize;
-		alloc_buffer_pool(scc);
-		
-		restore_flags(flags);
-		return 0;
-		
-		
-	case TIOCSCCSTAT:
-		error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct scc_stat));
-		if (error)
-			return error;
-		
-		if (!arg)
-			return -EFAULT;
+			restore_flags(flags);
 			
-		memcpy_tofs((void *) arg, &scc->stat, sizeof(struct scc_stat));
-		return 0;
-		
-#define TICKS (100/TPS)
-#define CAST(x) (unsigned long)(x)
-#define  Val	kiss_cmd.param
-#define  VAL	kiss_cmd.param*TPS/100
-#define SVAL	kiss_cmd.param? kiss_cmd.param:TIMER_STOPPED
-
-	case TIOCGKISS:
-		error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(struct ioctl_command));
-		if (error)
-			return error;
-			
-		if (!arg)
-			return -EFAULT;
-			
-		memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
-
-		switch (kiss_cmd.command)
-		{
-			case PARAM_TXDELAY:	r = CAST(scc->kiss.txdelay*TICKS);	break;
-			case PARAM_PERSIST:	r = CAST(scc->kiss.persist); 		break;
-			case PARAM_SLOTTIME:	r = CAST(scc->kiss.slottime*TICKS);	break;
-			case PARAM_TXTAIL:	r = CAST(scc->kiss.tailtime*TICKS);	break;
-			case PARAM_FULLDUP:	r = CAST(scc->kiss.fulldup); 		break;
-			case PARAM_SOFTDCD:	r = CAST(scc->kiss.softdcd);		break;
-			case PARAM_DTR:		r = CAST((scc->wreg[R5] & DTR)? 1:0); break;
-			case PARAM_RTS:		r = CAST((scc->wreg[R5] & RTS)? 1:0); break;
-			case PARAM_SPEED:	r = CAST(scc->modem.speed);	break;
-			case PARAM_GROUP:	r = CAST(scc->kiss.group); 		break;
-			case PARAM_IDLE:	r = CAST(scc->kiss.idletime);		break;
-			case PARAM_MIN:		r = CAST(scc->kiss.mintime);		break;
-			case PARAM_MAXKEY:	r = CAST(scc->kiss.maxkeyup);		break;
-			case PARAM_WAIT:	r = CAST(scc->kiss.waittime);		break;
-			case PARAM_MAXDEFER:	r = CAST(scc->kiss.maxdefer);		break;
-			case PARAM_TX:		r = CAST(scc->kiss.tx_inhibit);	break;
-			case PARAM_SLIP:	r = CAST(!scc->kiss.not_slip);		break;
-			default:		r = NO_SUCH_PARAM;
-		}
-		
-		kiss_cmd.param = r;
+			put_user(result,(unsigned int *) arg);
+			return 0;
+			
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			switch (cmd) 
+			{
+				case TIOCMBIS:
+					scc->wreg[R5] |= DTR;
+					scc->wreg[R5] |= RTS;
+					break;
+				case TIOCMBIC:
+					scc->wreg[R5] &= ~DTR;
+					scc->wreg[R5] &= ~RTS;
+					break;
+				case TIOCMSET:
+					error = verify_area(VERIFY_READ, (void *) arg,sizeof(unsigned int *));
+					if (error)
+						return error;
+					value = get_user((unsigned int *) arg);
+			
+					if(value & TIOCM_DTR)
+						scc->wreg[R5] |= DTR;
+					else
+						scc->wreg[R5] &= ~DTR;
+					if(value & TIOCM_RTS)
+						scc->wreg[R5] |= RTS;
+					else
+						scc->wreg[R5] &= ~RTS;
+					break;
+			}
+			
+			OutReg(scc->ctrl, R5, scc->wreg[R5]);
+			return 0;
 		
-		memcpy_tofs((void *) arg, &kiss_cmd, sizeof(struct ioctl_command));
-		return 0;
-		break;
+		case TCGETS:
+			error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct termios));
+			if (error)
+				return error;
+			if (!arg) 
+				return -EFAULT;
+			
+			memcpy_tofs((void *) arg, scc->tty->termios, sizeof(struct termios));
+			return 0;
 		
-	case TIOCSKISS:
-		if (!arg)
-			return -EFAULT;
-
-		memcpy_fromfs(&kiss_cmd, (void *) arg, sizeof(struct ioctl_command));
-		
-		switch (kiss_cmd.command)
-		{
-			case PARAM_TXDELAY:	scc->kiss.txdelay=VAL;		break;
-			case PARAM_PERSIST:	scc->kiss.persist=Val;		break;
-			case PARAM_SLOTTIME:	scc->kiss.slottime=VAL;		break;
-			case PARAM_TXTAIL:	scc->kiss.tailtime=VAL;		break;
-			case PARAM_FULLDUP:	scc->kiss.fulldup=Val;		break;
-			case PARAM_SOFTDCD:	scc->kiss.softdcd=Val;		break;
-			case PARAM_DTR:		break; /* does someone need this? */
-			case PARAM_RTS:		break; /* or this? */
-			case PARAM_SPEED:	scc->modem.speed=Val;		break;
-			case PARAM_GROUP:	scc->kiss.group=Val;		break;
-			case PARAM_IDLE:	scc->kiss.idletime=Val;		break;
-			case PARAM_MIN:		scc->kiss.mintime=SVAL;		break;
-			case PARAM_MAXKEY:	scc->kiss.maxkeyup=SVAL;	break;
-			case PARAM_WAIT:	scc->kiss.waittime=Val;		break;
-			case PARAM_MAXDEFER:	scc->kiss.maxdefer=SVAL;	break;
-			case PARAM_TX:		scc->kiss.tx_inhibit=Val;	break;
-			case PARAM_SLIP:	scc->kiss.not_slip=!Val;	break;
-			default:		return -ENOIOCTLCMD;
-		}
+		case TCSETS:
+		case TCSETSF:		/* should flush first, but... */
+		case TCSETSW:		/* should wait 'till flush, but... */
+			if (!arg)
+				return -EFAULT;
 		
-		return 0;
-		break;
-#undef TICKS
-#undef CAST
-#undef VAL
-#undef SVAL
-#undef Val
+			error = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios));
+			if (error)
+				return error;
+			memcpy_fromfs(scc->tty->termios, (void *) arg, sizeof(struct termios));
+			scc_change_speed(scc);
+			return 0;
 		
-	default:
-		return -ENOIOCTLCMD;
-    }
+	}
+	
+	return -ENOIOCTLCMD;
 }
 
 
 /* ----> tx routine: decode KISS data and scc_enqueue it <---- */
 
 /* send raw frame to SCC. used for AX.25 */
-int scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+int 
+scc_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
 {
 	struct scc_channel * scc = tty->driver_data;
 	unsigned char *p;
-	unsigned long flags;
 	int cnt, cnt2;
 	
-	if (!tty) return count;
+	if (!tty) 
+		return -EINVAL;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_write"))
-		return 0;
+		return -EINVAL;
 
 	if (scc->kiss.tx_inhibit) return count;
-	
-	save_flags(flags); cli();
-	
+
 	cnt2 = count;
 	
 	while (cnt2)
@@ -2493,8 +2820,8 @@
 		else
 			memcpy(scc_wbuf, buf, cnt);
 		
-		/* Strange thing. The timeout of the slip driver is */
-		/* very small, thus we'll wake him up now. 	    */
+		/* The timeout of the slip driver is very small, */
+		/* thus we'll wake him up now. 	    		 */
 
 		if (cnt2 == 0)
 		{
@@ -2510,34 +2837,28 @@
 		
 		while(cnt--) 
 		  if (kiss_decode(scc, *p++))
-		  {
-		  	scc->stat.nospace++;
-		  	restore_flags(flags);
-		  	return 0;
-		  }		  	
+		  	return -ENOMEM;
 	} /* while cnt2 */
 
-	restore_flags(flags);
-		
 	return count;
 }
 				
 
 /* put a single char into the buffer */
 
-static void scc_put_char(struct tty_struct * tty, unsigned char ch)
+static void 
+scc_put_char(struct tty_struct * tty, unsigned char ch)
 {
 	struct scc_channel *scc = tty->driver_data;
-	unsigned char ch2;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_put_char"))
 		return;
-		
-	ch2 = ch;
-	scc_write(tty, 0, &ch2, 1);	/* that's all */
+
+	kiss_decode(scc, ch);
 }
 
-static void scc_flush_chars(struct tty_struct * tty)
+static void 
+scc_flush_chars(struct tty_struct * tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2547,7 +2868,8 @@
 }
 
 
-static int scc_write_room(struct tty_struct *tty)
+static int 
+scc_write_room(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2557,7 +2879,8 @@
 	return BUFSIZE;
 }
 
-static int scc_chars_in_buffer(struct tty_struct *tty)
+static int 
+scc_chars_in_buffer(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2567,7 +2890,8 @@
 		return 0;
 }
 
-static void scc_flush_buffer(struct tty_struct *tty)
+static void 
+scc_flush_buffer(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 
@@ -2579,41 +2903,43 @@
 		(tty->ldisc.write_wakeup)(tty);
 }
 
-static void scc_throttle(struct tty_struct *tty)
+static void 
+scc_throttle(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_throttle"))
 		return;
 
-#ifdef DEBUG		
-	printk("scc: scc_throttle() called for device %d\n", MINOR(tty->device));
+#ifdef SCC_DEBUG		
+	printk(KERN_DEBUG "z8530drv: scc_throttle() called for device %d\n", MINOR(tty->device));
 #endif
 	scc->throttled = 1;
 	
 	del_timer(&(scc->rx_t));
-	scc->rx_t.expires = jiffies + HZ/TPS;
+	scc->rx_t.expires = jiffies + HZ/25;
 	add_timer(&scc->rx_t);
 }
 
-static void scc_unthrottle(struct tty_struct *tty)
+static void 
+scc_unthrottle(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
 	if (scc_paranoia_check(scc, tty->device, "scc_unthrottle"))
 		return;
 		
-#ifdef DEBUG
-	printk("scc: scc_unthrottle() called for device %d\n", MINOR(tty->device));
+#ifdef SCC_DEBUG
+	printk(KERN_DEBUG "z8350drv: scc_unthrottle() called for device %d\n", MINOR(tty->device));
 #endif		
 	scc->throttled = 0;
 	del_timer(&(scc->rx_t));
-	scc_tx_timer(scc->rx_t.data);
 }
 
 /* experimental, the easiest way to stop output is a fake scc_throttle */
 
-static void scc_start(struct tty_struct *tty)
+static void 
+scc_start(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2623,7 +2949,8 @@
 	scc_unthrottle(tty);
 }
 
-static void scc_stop(struct tty_struct *tty)
+static void 
+scc_stop(struct tty_struct *tty)
 {
 	struct scc_channel *scc = tty->driver_data;
 	
@@ -2657,20 +2984,521 @@
 		
 	scc_change_speed(scc);
 }
+#endif /* CONFIG_SCC_TTY */
+
+#ifdef CONFIG_SCC_DEV
+/* ******************************************************************** */
+/* *			    Network driver routines		      * */
+/* ******************************************************************** */
+
+static unsigned char ax25_bcast[AX25_ADDR_LEN] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static unsigned char ax25_nocall[AX25_ADDR_LEN] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+static int 
+scc_net_init(struct device *dev)
+{
+	return 0;	/* dummy */
+}
+
+/* ----> open network device <---- */
+
+static int 
+scc_net_open(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -ENODEV;
+
+	if (scc->tty != NULL)
+		return -EBUSY;
+
+ 	if (!scc->init)
+		return -EINVAL;
+
+	scc->stat.is_netdev = 1;
+
+	MOD_INC_USE_COUNT;
+
+	alloc_buffer_pool(scc);
+
+	scc->stat.tx_kiss_state = KISS_IDLE;	/* unused */
+	scc->stat.rx_kiss_state = KISS_IDLE;	/* unused */
+ 
+	init_channel(scc);
+
+	dev->tbusy = 0;
+	dev->start = 1;
+
+	return 0;
+}
+
+/* ----> close network device <---- */
+
+static int 
+scc_net_close(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned long flags;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -ENODEV;
+
+	if (!scc->stat.is_netdev)
+		return -ENXIO;
+
+	MOD_DEC_USE_COUNT;
+
+	scc->stat.is_netdev = 0;
+
+	save_flags(flags); 
+	cli();
+	
+	Outb(scc->ctrl,0);		/* Make sure pointer is written */
+	wr(scc,R1,0);			/* disable interrupts */
+	wr(scc,R3,0);
+
+	del_timer(&scc->tx_t);
+	del_timer(&scc->tx_wdog);
+#ifdef CONFIG_SCC_TTY
+	del_timer(&scc->rx_t);
+#endif
+	restore_flags(flags);
+
+	free_buffer_pool(scc);
+
+	dev->tbusy = 1;
+	dev->start = 0;
+
+	return 0;
+}
+
+/* ----> receive frame, called from scc_rxint() <---- */
+
+static void 
+scc_net_rx(struct scc_channel *scc, struct mbuf *bp)
+{
+	struct sk_buff *skb;
+
+	if ( bp == NULL || bp->cnt == 0 || 
+	    scc == NULL || scc->magic != SCC_MAGIC)
+		return;
+		
+	skb = dev_alloc_skb(bp->cnt+1);
+
+	if (skb == NULL)
+	{
+		scc->dev_stat.rx_dropped++;
+		return;
+	}
+
+	scc->dev_stat.rx_packets++;
+
+	skb->dev = scc->dev;
+	skb->protocol = htons(ETH_P_AX25);
+	
+	memcpy(skb_put(skb, bp->cnt), bp->data, bp->cnt);
 
+	skb->mac.raw = skb->data;
+	netif_rx(skb);
 
+	return;
+}
+
+/* ----> transmit frame <---- */
+
+static int 
+scc_net_tx(struct sk_buff *skb, struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	struct mbuf *bp;
+	long ticks;
+	
+	/* 
+	 * We have our own queues, dev->tbusy is set only by t_maxkeyup
+	 * to avoid running the driver into maxkeup again and again.
+	 *
+	 * Hope I havn't outsmart me _again_ ;-)
+	 */
+
+	if (dev->tbusy)
+	{
+		ticks = (signed) (jiffies - dev->trans_start);
+
+		if (ticks < scc->kiss.maxdefer*HZ || scc == NULL)
+			return 1;
+
+		/* 
+		 * Arrgh... Seems to be a _very_ busy channel.
+		 * throw away transmission queue. 
+		 */
+
+		del_timer(&scc->tx_wdog);
+		t_busy((unsigned long) scc);
+
+                dev->tbusy=0;
+                dev->trans_start = jiffies;
+        }
+
+	if (skb == NULL)
+	{
+		dev_tint(dev);
+		return 0;
+	}
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+	{
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	scc->dev_stat.tx_packets++;
+	bp = scc_get_buffer(scc, BT_TRANSMIT);
+
+	if (bp == NULLBUF || skb->len > scc->stat.bufsize || skb->len == 0)
+	{
+		scc->dev_stat.tx_dropped++;
+		if (bp) scc_enqueue_buffer(&scc->tx_buffer_pool, bp);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	memcpy(bp->data, skb->data, skb->len);
+	bp->cnt = skb->len;
+
+	dev_kfree_skb(skb, FREE_WRITE);
+
+	scc->kiss_decode_bp = bp;
+	kiss_interpret_frame(scc);
+	dev->trans_start = jiffies;
+	return 0;
+}
+
+/* ----> set interface callsign <---- */
+
+static int 
+scc_net_set_mac_address(struct device *dev, void *addr)
+{
+	struct sockaddr *sa = (struct sockaddr *) addr;
+	memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+	return 0;
+}
+
+/* ----> rebuild header <---- */
+
+static int 
+scc_net_rebuild_header(void *buff, struct device *dev, unsigned long raddr, struct sk_buff *skb)
+{
+    return ax25_rebuild_header(buff, dev, raddr, skb);
+}
+
+/* ----> "hard" header <---- */
+
+static int 
+scc_net_header(struct sk_buff *skb, struct device *dev, unsigned short type, 
+	       void *daddr, void *saddr, unsigned len)
+{
+    return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+}
+
+/* ----> get statistics <---- */
+
+static struct enet_statistics *
+scc_net_get_stats(struct device *dev)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return NULL;
+		
+	scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
+	scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
+	scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
+	scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
+
+	return &scc->dev_stat;
+}
+
+/* ----> ioctl for network devices <---- */
+
+static int 
+scc_net_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
+{
+	struct scc_channel *scc = (struct scc_channel *) dev->priv;
+	unsigned int ncmd = 0;
+	int res;
+
+	if (scc == NULL || scc->magic != SCC_MAGIC)
+		return -EINVAL;
+
+	if (!Driver_Initialized && cmd == SIOCSCCINI)
+	{
+		scc->dev = dev;
+		scc->stat.is_netdev = 1;
+	}
+
+	switch (cmd)
+	{
+		case SIOCSCCRESERVED:	return -EINVAL;      /* unused */
+		case SIOCSCCCFG:	ncmd = TIOCSCCCFG;	break;
+		case SIOCSCCINI:	ncmd = TIOCSCCINI;	break;
+		case SIOCSCCCHANINI:	ncmd = TIOCSCCCHANINI;	break;
+		case SIOCSCCSMEM:	ncmd = TIOCSCCSMEM;	break;
+		case SIOCSCCGKISS:	ncmd = TIOCSCCGKISS;	break;
+		case SIOCSCCSKISS:	ncmd = TIOCSCCSKISS;	break;
+		case SIOCSCCGSTAT:	ncmd = TIOCSCCGSTAT;	break;
+		default:		return -EINVAL;
+	}
+
+	res = scc_ioctl(scc, ncmd, (void *) ifr->ifr_data);
+
+#ifdef CONFIG_SCC_TTY
+	if (!(dev->flags & IFF_UP))
+		scc->stat.is_netdev = 0;
+#endif
+
+	return res;
+	
+}
+
+
+/* ----> initialize interface <---- */
+
+static int 
+scc_net_setup(struct scc_channel *scc, unsigned char *name)
+{
+	int k;
+	unsigned char *buf;
+	struct device *dev;
+
+	if (dev_get(name) != NULL)
+	{
+		printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
+		return -EEXIST;
+	}
+
+	if ((scc->dev = (struct device *) kmalloc(sizeof(struct device), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+#ifndef CONFIG_SCC_TTY
+	scc->stat.is_netdev = 1;
+#endif
+	dev = scc->dev;
+	memset(dev, 0, sizeof(struct device));
+
+	buf = (unsigned char *) kmalloc(10, GFP_KERNEL);
+	strcpy(buf, name);
+
+	dev->priv = (void *) scc;
+	dev->name = buf;
+	dev->init = scc_net_init;
+
+	if (register_netdev(dev) != 0)
+	{
+		kfree(dev);
+		return -EIO;
+	}
+
+	for (k=0; k < DEV_NUMBUFFS; k++)
+		skb_queue_head_init(&dev->buffs[k]);
+
+	dev->open            = scc_net_open;
+	dev->stop	     = scc_net_close;
+
+	dev->hard_start_xmit = scc_net_tx;
+	dev->hard_header     = scc_net_header;
+	dev->rebuild_header  = scc_net_rebuild_header;
+	dev->set_mac_address = scc_net_set_mac_address;
+	dev->get_stats       = scc_net_get_stats;
+	dev->do_ioctl        = scc_net_ioctl;
+
+	memcpy(dev->broadcast, ax25_bcast,  AX25_ADDR_LEN);
+	memcpy(dev->dev_addr,  ax25_nocall, AX25_ADDR_LEN);
+ 
+	dev->flags      = 0;
+	dev->family     = AF_INET;
+	dev->pa_addr    = 0;
+	dev->pa_brdaddr = 0;
+	dev->pa_mask    = 0;
+	dev->pa_alen    = 4;
+
+	dev->type = ARPHRD_AX25;
+	dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+	dev->mtu = AX25_DEF_PACLEN;
+	dev->addr_len = AX25_ADDR_LEN;
+
+	return 0;
+}
+#endif /* CONFIG_SCC_DEV */
+
+/* ******************************************************************** */
+/* *		dump statistics to /proc/net/z8530drv		      * */
+/* ******************************************************************** */
+
+
+static int 
+scc_net_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct scc_channel *scc;
+	char *txstate, *clksrc, *brand;
+	struct scc_kiss *kiss;
+	struct scc_stat *stat;
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+	int k;
+
+	if (!Driver_Initialized)
+	{
+		len += sprintf(buffer, "Driver not initialized.\n");
+		goto done;
+	}
+
+	if (!Nchips)
+	{
+		len += sprintf(buffer, "Z8530 chips not found\n");
+		goto done;
+	}
+
+	for (k = 0; k < Nchips*2; k++)
+	{
+		scc = &SCC_Info[k];
+		stat = &scc->stat;
+		kiss = &scc->kiss;
+
+		if (!scc->init)
+			continue;
+
+		switch(stat->tx_state)
+		{
+			case TXS_IDLE:		txstate = "idle";	break;
+			case TXS_BUSY:		txstate = "busy";	break;
+			case TXS_ACTIVE:	txstate = "active";	break;
+			case TXS_NEWFRAME:	txstate = "new";	break;
+			case TXS_IDLE2:		txstate = "keyed";	break;
+			case TXS_WAIT:		txstate = "wait";	break;
+			case TXS_TIMEOUT:	txstate = "timeout";	break;
+			default:		txstate = "???";
+		}
+		switch(scc->modem.clocksrc)
+		{
+			case CLK_DPLL:		clksrc = "dpll";	break;
+			case CLK_EXTERNAL:	clksrc = "ext";		break;
+			case CLK_DIVIDER:	clksrc = "div";		break;
+			default:		clksrc = "???";
+		}
+		switch(scc->brand)
+		{
+			case PA0HZP:	brand="pa0hzp";	break;
+			case EAGLE:	brand="eagle";	break;
+			case PC100:	brand="pc1100";	break;
+			case PRIMUS:	brand="primus";	break;
+			case DRSI:	brand="drsi";	break;
+			case BAYCOM:	brand="baycom";	break;
+			default:	brand="???";
+		}
+	
+		len += sprintf(buffer+len, "Device     : %s%d\n", SCC_DriverName, k);
+		len += sprintf(buffer+len, "Hardware   : %3.3x %3.3x %d %lu %s %s %3.3x %3.3x %d\n", 
+				scc->data, scc->ctrl, scc->irq, scc->clock, brand,
+				scc->enhanced? "enh":"norm", Vector_Latch, scc->special, 
+				scc->option);
+		len += sprintf(buffer+len, "Received   : %lu %lu %d\n", 
+				stat->rxframes, stat->rxerrs, stat->rx_over);
+		len += sprintf(buffer+len, "Transmitted: %lu %lu %d %s\n", 
+				stat->txframes, stat->txerrs, stat->tx_under, txstate);
+		len += sprintf(buffer+len, "Interrupts : %lu %lu %lu %lu\n",
+				stat->rxints, stat->txints, stat->exints, stat->spints);
+		len += sprintf(buffer+len, "Buffers    : %d %d/%d %d/%d %d\n", 
+				stat->bufsize, stat->rx_queued, stat->rxbuffers,
+				stat->tx_queued, stat->txbuffers, stat->nospace);
+		len += sprintf(buffer+len, "MODEM      : %lu %s %s %s\n", 
+				scc->modem.speed, scc->modem.nrz? "nrz":"nrzi",
+				clksrc, kiss->softdcd? "soft":"hard");
+		len += sprintf(buffer+len, "Mode       : %s\n",
+				stat->is_netdev? "dev":"tty");
+#define K(x) kiss->x
+		len += sprintf(buffer+len, "KISS params: %d %d %d %d %d %d %d %d %d %d %d %d\n",
+				K(txdelay), K(persist), K(slottime), K(tailtime),
+				K(fulldup), K(waittime), K(mintime), K(maxkeyup),
+				K(idletime), K(maxdefer), K(tx_inhibit), K(group));
+#undef K
+#ifdef SCC_DEBUG
+		{
+			int reg;
+
+			len += sprintf(buffer+len, "Z8530 wregs: ");
+			for (reg = 0; reg < 16; reg++)
+				len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
+			len += sprintf(buffer+len, "\n");
+			
+			len += sprintf(buffer+len, "Z8530 rregs: %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
+			for (reg = 3; reg < 8; reg++)
+				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+			len += sprintf(buffer+len, "XX ");
+			for (reg = 9; reg < 16; reg++)
+				len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+			len += sprintf(buffer+len, "\n");
+		}
+#endif
+		len += sprintf(buffer+len, "\n");
+
+                pos = begin + len;
+
+                if (pos < offset) {
+                        len   = 0;
+                        begin = pos;
+                }
+
+                if (pos > offset + length)
+                        break;
+	}
+
+done:
+
+        *start = buffer + (offset - begin);
+        len   -= (offset - begin);
+
+        if (len > length) len = length;
+
+        return len;
+}
+
+#ifdef CONFIG_INET
+struct proc_dir_entry scc_proc_dir_entry = 
+{ 
+  PROC_NET_Z8530, 8, "z8530drv", S_IFREG | S_IRUGO, 1, 0, 0, 0, 
+  &proc_net_inode_operations, scc_net_get_info 
+};
+
+#define scc_net_procfs_init()   proc_net_register(&scc_proc_dir_entry);
+#define scc_net_procfs_remove() proc_net_unregister(PROC_NET_Z8530);
+#else
+#define scc_net_procfs_init()
+#define scc_net_procfs_remove()
+#endif
+
+  
 /* ******************************************************************** */
 /* * 			Init SCC driver 			      * */
 /* ******************************************************************** */
 
-int  scc_init (void)
+int scc_init (void)
 {
 	int chip, chan, k;
+#ifdef CONFIG_SCC_DEV
+	char devname[10];
+#endif
 	
+	printk(KERN_INFO BANNER);
+
+#ifdef CONFIG_SCC_TTY
 	memset(&scc_std_termios, 0, sizeof(struct termios));
         memset(&scc_driver, 0, sizeof(struct tty_driver));
         scc_driver.magic = TTY_DRIVER_MAGIC;
-        scc_driver.name = "scc";
+        scc_driver.name = SCC_DriverName;
         scc_driver.major = Z8530_MAJOR;
         scc_driver.minor_start = 0;
         scc_driver.num = MAXSCC*2;
@@ -2699,17 +3527,19 @@
 	scc_driver.throttle = scc_throttle;
 	scc_driver.unthrottle = scc_unthrottle;
         
-        scc_driver.ioctl = scc_ioctl;
+        scc_driver.ioctl = scc_tty_ioctl;
         scc_driver.set_termios = scc_set_termios;
         scc_driver.set_ldisc = scc_set_ldisc;
 
-	printk(BANNER);
         
         if (tty_register_driver(&scc_driver))
         {
-		printk("Failed to register Z8530 SCC driver\n");
+		printk(KERN_ERR "Failed to register Z8530 SCC driver\n");
 		return -EIO;
 	}
+#endif
+	
+	memset(&SCC_ctrl, 0, sizeof(SCC_ctrl));
 	
 	/* pre-init channel information */
 	
@@ -2719,16 +3549,19 @@
 		memset((char *) &SCC_Info[2*chip+1], 0, sizeof(struct scc_channel));
 		
 		for (chan = 0; chan < 2; chan++)
-		{
 			SCC_Info[2*chip+chan].magic    = SCC_MAGIC;
-			SCC_Info[2*chip+chan].stat.rxbuffers = RXBUFFERS;
-			SCC_Info[2*chip+chan].stat.txbuffers = TXBUFFERS;
-			SCC_Info[2*chip+chan].stat.bufsize   = BUFSIZE;
-		}
 	}
-	
+
 	for (k = 0; k < 16; k++) Ivec[k].used = 0;
 
+#ifdef CONFIG_SCC_DEV
+	sprintf(devname,"%s0", SCC_DriverName);
+	
+	if (scc_net_setup(SCC_Info, devname))
+		printk(KERN_WARNING "z8530drv: cannot setup network device\n");
+#endif
+	scc_net_procfs_init();
+
 	return 0;
 }
 
@@ -2743,9 +3576,9 @@
 	int result = 0;
 	
 	result = scc_init();
-	
+
 	if (result == 0)
-		printk("Copyright 1993,1995 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
+		printk(KERN_INFO "Copyright 1993,1996 Joerg Reuter DL1BKE (jreuter@lykos.tng.oche.de)\n");
 		
 	return result;
 }
@@ -2754,19 +3587,26 @@
 {
 	long flags;
 	io_port ctrl;
-	int k, errno;
+	int k;
 	struct scc_channel *scc;
 	
-	save_flags(flags); cli();
-	if ( (errno = tty_unregister_driver(&scc_driver)) )
+	save_flags(flags); 
+	cli();
+
+#ifdef CONFIG_SCC_TTY
+	if ( (k = tty_unregister_driver(&scc_driver)) )
 	{
-		printk("Failed to unregister Z8530 SCC driver (%d)", -errno);
+		printk(KERN_ERR "z8530drv: failed to unregister tty driver: (%d)\n", -k);
 		restore_flags(flags);
 		return;
 	}
-	
+#endif
+
+	if (Nchips == 0)
+		unregister_netdev(SCC_Info[0].dev);
+
 	for (k = 0; k < Nchips; k++)
-		if ( (ctrl = SCC_ctrl[k*2]) )
+		if ( (ctrl = SCC_ctrl[k].chan_A) )
 		{
 			Outb(ctrl, 0);
 			OutReg(ctrl,R9,FHWRES);	/* force hardware reset */
@@ -2780,6 +3620,11 @@
 		{
 			release_region(scc->ctrl, 1);
 			release_region(scc->data, 1);
+			if (scc->dev)
+			{
+				unregister_netdev(scc->dev);
+				kfree(scc->dev);
+			}
 		}
 	}
 	
@@ -2787,5 +3632,7 @@
 		if (Ivec[k].used) free_irq(k, NULL);
 		
 	restore_flags(flags);
+
+	scc_net_procfs_remove();
 }
 #endif
diff -urN linux-2.0.31-pre4/drivers/char/specialix.c linux-2.0.31-pre5/drivers/char/specialix.c
--- linux-2.0.31-pre4/drivers/char/specialix.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/specialix.c	2003-08-15 15:03:58.000000000 -0700
@@ -0,0 +1,2330 @@
+/*
+ *      specialix.c  -- specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997  Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support. But please read the documentation (specialix.txt)
+ *      first.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      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.
+ *
+ *      This program is distributed in the hope that it will be
+ *      useful, but WITHOUT ANY WARRANTY; without even the implied
+ *      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ *      PURPOSE.  See the GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public
+ *      License along with this program; if not, write to the Free
+ *      Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ *      USA.
+ *
+ * Revision history:
+ *
+ * Revision 1.0:  April 1st 1997.
+ *                Initial release for alpha testing.
+ * Revision 1.1:  April 14th 1997. 
+ *                Incorporated Richard Hudsons suggestions, 
+ *                removed some debugging printk's.
+ * Revision 1.2:  April 15th 1997.
+ *                Ported to 2.1.x kernels.
+ * Revision 1.3:  April 17th 1997 
+ *                Backported to 2.0. (Compatibility macros). 
+ * Revision 1.4:  April 18th 1997
+ *                Fixed DTR/RTS bug that caused the card to indicate 
+ *                "don't send data" to a modem after the password prompt.  
+ *                Fixed bug for premature (fake) interrupts.
+ * Revision 1.5:  April 19th 1997
+ *                fixed a minor typo in the header file, cleanup a little. 
+ *                performance warnings are now MAXed at once per minute.
+ * Revision 1.6:  May 23 1997
+ *                Changed the specialix=... format to include interrupt.
+ * Revision 1.7:  May 27 1997
+ *                Made many more debug printk's a compile time option.
+ * 
+ */
+
+#define VERSION "1.7"
+
+
+/*
+ * There is a bunch of documentation about the card, jumpers, config
+ * settings, restrictions, cables, device names and numbers in
+ * ../../Documentation/specialix.txt 
+ */
+
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/serial.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/delay.h>
+#include <linux/tqueue.h>
+#include <linux/version.h>
+
+
+/* ************************************************************** */
+/* * This section can be removed when 2.0 becomes outdated....  * */
+/* ************************************************************** */
+
+#if LINUX_VERSION_CODE < 131328    /* Less than 2.1.0 */
+#define TWO_ZERO
+#else
+#if LINUX_VERSION_CODE < 131368   /* less than 2.1.40 */
+/* This has not been extensively tested yet. Sorry. */
+#warning "You're on your own between 2.1.0 and 2.1.40.... "
+#warning "Please use 2.1.40 or higher."
+#endif
+#endif
+
+
+#ifdef TWO_ZERO
+#define Get_user(a,b)         a = get_user(b)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
+#define queue_task            queue_task_irq_off
+#else
+#define Get_user(a,b)         get_user(a,b)
+#endif
+
+/* ************************************************************** */
+/* *                End of compatibility section..              * */
+/* ************************************************************** */
+
+
+#ifndef TWO_ZERO
+#include <asm/uaccess.h>
+#endif
+
+#include "specialix_io8.h"
+#include "cd1865.h"
+
+
+
+/* Configurable options: */
+
+/* Am I paranoid or not ? ;-) */
+#define SPECIALIX_PARANOIA_CHECK
+
+/* Do I trust the IRQ from the card? (enabeling it doesn't seem to help)
+   When the IRQ routine leaves the chip in a state that is keeps on
+   requiring attention, the timer doesn't help either. */
+#undef SPECIALIX_TIMER
+
+/* 
+ * The following defines are mostly for testing purposes. But if you need
+ * some nice reporting in your syslog, you can define them also.
+ */
+#undef SX_REPORT_FIFO
+#undef SX_REPORT_OVERRUN
+
+
+
+#ifdef CONFIG_SPECIALIX_RTSCTS
+#define SX_CRTSCTS(bla) 1
+#else
+#define SX_CRTSCTS(tty) C_CRTSCTS(tty)
+#endif
+
+
+/* Used to be outb (0xff, 0x80); */
+#define short_pause() udelay (1)
+
+
+#define SPECIALIX_LEGAL_FLAGS \
+	(ASYNC_HUP_NOTIFY   | ASYNC_SAK          | ASYNC_SPLIT_TERMIOS   | \
+	 ASYNC_SPD_HI       | ASYNC_SPEED_VHI    | ASYNC_SESSION_LOCKOUT | \
+	 ASYNC_PGRP_LOCKOUT | ASYNC_CALLOUT_NOHUP)
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+DECLARE_TASK_QUEUE(tq_specialix);
+
+
+
+#define SPECIALIX_TYPE_NORMAL	1
+#define SPECIALIX_TYPE_CALLOUT	2
+
+static struct specialix_board * IRQ_to_board[16] = { NULL, } ;
+static struct tty_driver specialix_driver, specialix_callout_driver;
+static int    specialix_refcount = 0;
+static struct tty_struct * specialix_table[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios[SX_NBOARD * SX_NPORT] = { NULL, };
+static struct termios * specialix_termios_locked[SX_NBOARD * SX_NPORT] = { NULL, };
+static unsigned char * tmp_buf = NULL;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static unsigned long baud_table[] =  {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 0, 
+};
+
+static struct specialix_board sx_board[SX_NBOARD] =  {
+	{ 0, SX_IOBASE1,  9, },
+	{ 0, SX_IOBASE2, 11, },
+	{ 0, SX_IOBASE3, 12, },
+	{ 0, SX_IOBASE4, 15, },
+};
+
+static struct specialix_port sx_port[SX_NBOARD * SX_NPORT] =  {
+	{ 0, },
+};
+		
+
+#ifdef SPECIALIX_TIMER
+static struct timer_list missed_irq_timer;
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs);
+#endif
+
+
+
+static inline int sx_paranoia_check(struct specialix_port const * port,
+				    kdev_t device, const char *routine)
+{
+#ifdef SPECIALIX_PARANOIA_CHECK
+	static const char *badmagic =
+		KERN_ERR "sx: Warning: bad specialix port magic number for device %s in %s\n";
+	static const char *badinfo =
+		KERN_ERR "sx: Warning: null specialix port for device %s in %s\n";
+ 
+	if (!port) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (port->magic != SPECIALIX_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+
+/*
+ * 
+ *  Service functions for specialix IO8+ driver.
+ * 
+ */
+
+/* Get board number from pointer */
+extern inline int board_No (struct specialix_board * bp)
+{
+	return bp - sx_board;
+}
+
+
+/* Get port number from pointer */
+extern inline int port_No (struct specialix_port const * port)
+{
+	return SX_PORT(port - sx_port); 
+}
+
+
+/* Get pointer to board from pointer to port */
+extern inline struct specialix_board * port_Board(struct specialix_port const * port)
+{
+	return &sx_board[SX_BOARD(port - sx_port)];
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in(struct specialix_board  * bp, unsigned short reg)
+{
+	bp->reg = reg | 0x80;
+	outb (reg | 0x80, bp->base + SX_ADDR_REG);
+	return inb  (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out(struct specialix_board  * bp, unsigned short reg,
+			  unsigned char val)
+{
+	bp->reg = reg | 0x80;
+	outb (reg | 0x80, bp->base + SX_ADDR_REG);
+	outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Input Byte from CL CD186x register */
+extern inline unsigned char sx_in_off(struct specialix_board  * bp, unsigned short reg)
+{
+	bp->reg = reg;
+	outb (reg, bp->base + SX_ADDR_REG);
+	return inb  (bp->base + SX_DATA_REG);
+}
+
+
+/* Output Byte to CL CD186x register */
+extern inline void sx_out_off(struct specialix_board  * bp, unsigned short reg,
+			  unsigned char val)
+{
+	bp->reg = reg;
+	outb (reg, bp->base + SX_ADDR_REG);
+	outb (val, bp->base + SX_DATA_REG);
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR(struct specialix_board  * bp)
+{
+	unsigned long delay;
+
+	for (delay = SX_CCR_TIMEOUT; delay; delay--) 
+		if (!sx_in(bp, CD186x_CCR))
+			return;
+	
+	printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/* Wait for Channel Command Register ready */
+extern inline void sx_wait_CCR_off(struct specialix_board  * bp)
+{
+	unsigned long delay;
+
+	for (delay = SX_CCR_TIMEOUT; delay; delay--) 
+		if (!sx_in_off(bp, CD186x_CCR))
+			return;
+	
+	printk(KERN_ERR "sx%d: Timeout waiting for CCR.\n", board_No(bp));
+}
+
+
+/*
+ *  specialix IO8+ IO range functions.
+ */
+
+extern inline int sx_check_io_range(struct specialix_board * bp)
+{
+	return check_region (bp->base, SX_IO_SPACE);
+}
+
+
+extern inline void sx_request_io_range(struct specialix_board * bp)
+{
+	request_region(bp->base, SX_IO_SPACE, "specialix IO8+" );
+}
+
+
+extern inline void sx_release_io_range(struct specialix_board * bp)
+{
+	release_region(bp->base, SX_IO_SPACE);
+}
+
+	
+/* Must be called with enabled interrupts */
+extern inline void sx_long_delay(unsigned long delay)
+{
+	unsigned long i;
+	
+	for (i = jiffies + delay; i > jiffies; ) ;
+}
+
+
+
+/* Set the IRQ using the RTS lines that run to the PAL on the board.... */
+int sx_set_irq ( struct specialix_board *bp)
+{
+	int virq;
+	int i;
+
+	switch (bp->irq) {
+	/* In the same order as in the docs... */
+	case 15: virq = 0;break;
+	case 12: virq = 1;break;
+	case 11: virq = 2;break;
+	case 9:  virq = 3;break;
+	default: printk (KERN_ERR "Speclialix: cannot set irq to %d.\n", bp->irq);
+	         return 0;
+	}
+
+	for (i=0;i<2;i++) {
+		sx_out(bp, CD186x_CAR, i);
+		sx_out(bp, CD186x_MSVRTS, ((virq >> i) & 0x1)? MSVR_RTS:0);
+	}
+	return 1;
+}
+
+
+/* Reset and setup CD186x chip */
+static int sx_init_CD186x(struct specialix_board  * bp)
+{
+	unsigned long flags;
+	int scaler;
+	int rv = 1;
+	
+	save_flags(flags); cli();
+
+	sx_wait_CCR_off(bp);			   /* Wait for CCR ready        */
+	sx_out_off(bp, CD186x_CCR, CCR_HARDRESET);      /* Reset CD186x chip          */
+	sti();
+	sx_long_delay(HZ/20);                      /* Delay 0.05 sec            */
+	cli();
+	sx_out_off(bp, CD186x_GIVR, SX_ID);             /* Set ID for this chip      */
+	sx_out_off(bp, CD186x_GICR, 0);                 /* Clear all bits            */
+	sx_out_off(bp, CD186x_PILR1, SX_ACK_MINT);      /* Prio for modem intr       */
+	sx_out_off(bp, CD186x_PILR2, SX_ACK_TINT);      /* Prio for transmitter intr */
+	sx_out_off(bp, CD186x_PILR3, SX_ACK_RINT);      /* Prio for receiver intr    */
+	/* Set RegAckEn */
+	sx_out_off(bp, CD186x_SRCR, sx_in (bp, CD186x_SRCR) | SRCR_REGACKEN);
+	
+	/* Setting up prescaler. We need 4 ticks per 1 ms */
+	scaler =  SX_OSCFREQ/SPECIALIX_TPS;
+
+	sx_out_off(bp, CD186x_PPRH, scaler >> 8);
+	sx_out_off(bp, CD186x_PPRL, scaler & 0xff);
+
+	if (!sx_set_irq (bp)) {
+		/* Figure out how to pass this along... */
+		printk (KERN_ERR "Cannot set irq to %d.\n", bp->irq);
+		rv = 0;
+	}
+
+	restore_flags(flags);
+	return rv;
+}
+
+
+int read_cross_byte (struct specialix_board *bp, int reg, int bit)
+{
+	int i;
+	int t;
+
+	for (i=0, t=0;i<8;i++) {
+		sx_out_off (bp, CD186x_CAR, i);
+		if (sx_in_off (bp, reg) & bit) 
+			t |= 1 << i;
+	}
+	return t;
+}
+
+
+#ifdef SPECIALIX_TIMER
+void missed_irq (unsigned long data)
+{
+	if (sx_in ((struct specialix_board *)data, CD186x_SRSR) &  
+	                                            (SRSR_RREQint |
+	                                             SRSR_TREQint |
+	                                             SRSR_MREQint)) {
+		printk (KERN_INFO "Missed interrupt... Calling int from timer. \n");
+		sx_interrupt (((struct specialix_board *)data)->irq, 
+		              NULL, NULL);
+	}
+	missed_irq_timer.expires = jiffies + HZ;
+	add_timer (&missed_irq_timer);
+}
+#endif
+
+
+
+/* Main probing routine, also sets irq. */
+static int sx_probe(struct specialix_board *bp)
+{
+	unsigned char val1, val2;
+#if 0
+	int irqs = 0;
+	int retries;
+#endif
+	int rev;
+	int chip;
+
+	if (sx_check_io_range(bp)) 
+		return 1;
+
+	/* Are the I/O ports here ? */
+	sx_out_off(bp, CD186x_PPRL, 0x5a);
+	short_pause ();
+	val1 = sx_in_off(bp, CD186x_PPRL);
+
+	sx_out_off(bp, CD186x_PPRL, 0xa5);
+	short_pause ();
+	val2 = sx_in_off(bp, CD186x_PPRL);
+
+	
+	if ((val1 != 0x5a) || (val2 != 0xa5)) {
+		printk(KERN_INFO "sx%d: specialix IO8+ Board at 0x%03x not found.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+
+	/* Check the DSR lines that Specialix uses as board 
+	   identification */
+	val1 = read_cross_byte (bp, CD186x_MSVR, MSVR_DSR);
+	val2 = read_cross_byte (bp, CD186x_MSVR, MSVR_RTS);
+#ifdef SPECIALIX_DEBUG
+	printk (KERN_DEBUG "sx%d: DSR lines are: %02x, rts lines are: %02x\n", 
+	        board_No(bp),  val1, val2);
+#endif
+	if (val1 != 0xb2) {
+		printk(KERN_INFO "sx%d: specialix IO8+ ID at 0x%03x not found.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+
+
+#if 0
+	/* It's time to find IRQ for this board */
+	for (retries = 0; retries < 5 && irqs <= 0; retries++) {
+		irqs = probe_irq_on();
+		sx_init_CD186x(bp);	       		/* Reset CD186x chip       */
+		sx_out(bp, CD186x_CAR, 2);               /* Select port 2          */
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_TXEN);        /* Enable transmitter     */
+		sx_out(bp, CD186x_IER, IER_TXRDY);       /* Enable tx empty intr   */
+		sx_long_delay(HZ/20);	       		
+		irqs = probe_irq_off(irqs);
+
+#if SPECIALIX_DEBUG > 2
+		printk (KERN_DEBUG "SRSR = %02x, ",  sx_in(bp, CD186x_SRSR));
+		printk (           "TRAR = %02x, ",  sx_in(bp, CD186x_TRAR));
+		printk (           "GIVR = %02x, ",  sx_in(bp, CD186x_GIVR));
+		printk (           "GICR = %02x, ",  sx_in(bp, CD186x_GICR));
+		printk (           "\n");
+#endif
+		/* Reset CD186x again      */
+		if (!sx_init_CD186x(bp)) {
+			/* Hmmm. This is dead code anyway. */
+		}
+#if SPECIALIX_DEBUG > 2
+		printk (KERN_DEBUG "val1 = %02x, val2 = %02x, val3 = %02x.\n", 
+		        val1, val2, val3); 
+#endif
+	
+	}
+	
+#if 0
+	if (irqs <= 0) {
+		printk(KERN_ERR "sx%d: Can't find IRQ for specialix IO8+ board at 0x%03x.\n",
+		       board_No(bp), bp->base);
+		return 1;
+	}
+#endif
+	printk (KERN_INFO "Started with irq=%d, but now have irq=%d.\n", bp->irq, irqs);
+	if (irqs > 0)
+		bp->irq = irqs;
+#endif
+	/* Reset CD186x again  */
+	if (!sx_init_CD186x(bp)) {
+		return -EIO;
+	}
+
+	sx_request_io_range(bp);
+	bp->flags |= SX_BOARD_PRESENT;
+	
+	/* Chip           revcode   pkgtype
+	                  GFRCR     SRCR bit 7
+	   CD180 rev B    0x81      0
+	   CD180 rev C    0x82      0
+	   CD1864 rev A   0x82      1
+	   CD1865 rev A   0x83      1  -- Do not use!!! Does not work. 
+	   CD1865 rev B   0x84      1
+	 -- Thanks to Gwen Wang, Cirrus Logic.
+	 */
+
+	switch (sx_in_off(bp, CD186x_GFRCR)) {
+	case 0x82:chip = 1864;rev='A';break;
+	case 0x83:chip = 1865;rev='A';break;
+	case 0x84:chip = 1865;rev='B';break;
+	case 0x85:chip = 1865;rev='C';break; /* Does not exist at this time */
+	default:chip=-1;rev='x';
+	}
+
+#if SPECIALIX_DEBUG > 2
+	printk (KERN_DEBUG " GFCR = 0x%02x\n", sx_in_off(bp, CD186x_GFRCR) );
+#endif
+
+#ifdef SPECIALIX_TIMER
+	init_timer (&missed_irq_timer);
+	missed_irq_timer.function = missed_irq;
+	missed_irq_timer.data = (unsigned long) bp;
+	missed_irq_timer.expires = jiffies + HZ;
+	add_timer (&missed_irq_timer);
+#endif
+
+	printk(KERN_INFO"sx%d: specialix IO8+ board detected at 0x%03x, IRQ %d, CD%d Rev. %c.\n",
+	       board_No(bp),
+	       bp->base, bp->irq,
+	       chip, rev);
+
+	return 0;
+}
+
+/* 
+ * 
+ *  Interrupt processing routines.
+ * */
+
+extern inline void sx_mark_event(struct specialix_port * port, int event)
+{
+	/* 
+	 * I'm not quite happy with current scheme all serial
+	 * drivers use their own BH routine.
+	 * It seems this easily can be done with one BH routine
+	 * serving for all serial drivers.
+	 * For now I must introduce another one - SPECIALIX_BH.
+	 * Still hope this will be changed in near future.
+	 * -- Dmitry.
+	 */
+	set_bit(event, &port->event);
+	queue_task(&port->tqueue, &tq_specialix);
+	mark_bh(SPECIALIX_BH);
+}
+
+
+extern inline struct specialix_port * sx_get_port(struct specialix_board * bp,
+					       unsigned char const * what)
+{
+	unsigned char channel;
+	struct specialix_port * port;
+	
+	channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF;
+	if (channel < CD186x_NCH) {
+		port = &sx_port[board_No(bp) * SX_NPORT + channel];
+		if (port->flags & ASYNC_INITIALIZED) {
+			return port;
+		}
+	}
+	printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", 
+	       board_No(bp), what, channel);
+	return NULL;
+}
+
+
+extern inline void sx_receive_exc(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char status;
+	unsigned char ch;
+
+	if (!(port = sx_get_port(bp, "Receive")))
+		return;
+
+	tty = port->tty;
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+		printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+		       board_No(bp), port_No(port));
+		return;
+	}
+	
+#ifdef SX_REPORT_OVERRUN	
+	status = sx_in(bp, CD186x_RCSR);
+	if (status & RCSR_OE) {
+		port->overrun++;
+#if SPECIALIX_DEBUG 
+		printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", 
+		       board_No(bp), port_No(port), port->overrun);
+#endif		
+	}
+	status &= port->mark_mask;
+#else	
+	status = sx_in(bp, CD186x_RCSR) & port->mark_mask;
+#endif	
+	ch = sx_in(bp, CD186x_RDR);
+	if (!status) {
+		return;
+	}
+	if (status & RCSR_TOUT) {
+		printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", 
+		       board_No(bp), port_No(port));
+		return;
+		
+	} else if (status & RCSR_BREAK) {
+#ifdef SPECIALIX_DEBUG
+		printk(KERN_DEBUG "sx%d: port %d: Handling break...\n",
+		       board_No(bp), port_No(port));
+#endif
+		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
+		if (port->flags & ASYNC_SAK)
+			do_SAK(tty);
+		
+	} else if (status & RCSR_PE) 
+		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+	
+	else if (status & RCSR_FE) 
+		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+	
+	else if (status & RCSR_OE)
+		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+	
+	else
+		*tty->flip.flag_buf_ptr++ = 0;
+	
+	*tty->flip.char_buf_ptr++ = ch;
+	tty->flip.count++;
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_receive(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char count;
+	
+	if (!(port = sx_get_port(bp, "Receive")))
+		return;
+	
+	tty = port->tty;
+	
+	count = sx_in(bp, CD186x_RDCR);
+	
+#ifdef SX_REPORT_FIFO
+	port->hits[count > 8 ? 9 : count]++;
+#endif	
+	
+	while (count--) {
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n",
+			       board_No(bp), port_No(port));
+			break;
+		}
+		*tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR);
+		*tty->flip.flag_buf_ptr++ = 0;
+		tty->flip.count++;
+	}
+	queue_task(&tty->flip.tqueue, &tq_timer);
+}
+
+
+extern inline void sx_transmit(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char count;
+	
+	
+	if (!(port = sx_get_port(bp, "Transmit")))
+		return;
+	
+	tty = port->tty;
+	
+	if (port->IER & IER_TXEMPTY) {
+		/* FIFO drained */
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXEMPTY;
+		sx_out(bp, CD186x_IER, port->IER);
+		return;
+	}
+	
+	if ((port->xmit_cnt <= 0 && !port->break_length)
+	    || tty->stopped || tty->hw_stopped) {
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXRDY;
+		sx_out(bp, CD186x_IER, port->IER);
+		return;
+	}
+	
+	if (port->break_length) {
+		if (port->break_length > 0) {
+			if (port->COR2 & COR2_ETC) {
+				sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+				sx_out(bp, CD186x_TDR, CD186x_C_SBRK);
+				port->COR2 &= ~COR2_ETC;
+			}
+			count = MIN(port->break_length, 0xff);
+			sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+			sx_out(bp, CD186x_TDR, CD186x_C_DELAY);
+			sx_out(bp, CD186x_TDR, count);
+			if (!(port->break_length -= count))
+				port->break_length--;
+		} else {
+			sx_out(bp, CD186x_TDR, CD186x_C_ESC);
+			sx_out(bp, CD186x_TDR, CD186x_C_EBRK);
+			sx_out(bp, CD186x_COR2, port->COR2);
+			sx_wait_CCR(bp);
+			sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+			port->break_length = 0;
+		}
+		return;
+	}
+	
+	count = CD186x_NFIFO;
+	do {
+		sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]);
+		port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		if (--port->xmit_cnt <= 0)
+			break;
+	} while (--count > 0);
+	
+	if (port->xmit_cnt <= 0) {
+		sx_out(bp, CD186x_CAR, port_No(port));
+		port->IER &= ~IER_TXRDY;
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	if (port->xmit_cnt <= port->wakeup_chars)
+		sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+}
+
+
+extern inline void sx_check_modem(struct specialix_board * bp)
+{
+	struct specialix_port *port;
+	struct tty_struct *tty;
+	unsigned char mcr;
+
+#ifdef SPECIALIX_DEBUG
+	printk (KERN_DEBUG "Modem intr. ");
+#endif
+	if (!(port = sx_get_port(bp, "Modem")))
+		return;
+	
+	tty = port->tty;
+	
+	mcr = sx_in(bp, CD186x_MCR);
+	printk ("mcr = %02x.\n", mcr);
+
+	if ((mcr & MCR_CDCHG)) {
+#ifdef SPECIALIX_DEBUG 
+		printk (KERN_DEBUG "CD just changed... ");
+#endif
+		if (sx_in(bp, CD186x_MSVR) & MSVR_CD) {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Waking up guys in open.\n");
+#endif
+			wake_up_interruptible(&port->open_wait);
+		}
+		else if (!((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		           (port->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Sending HUP.\n");
+#endif
+			queue_task(&port->tqueue_hangup,  
+			           &tq_scheduler);      
+		} else {
+#ifdef SPECIALIX_DEBUG
+			printk ( "Don't need to send HUP.\n");
+#endif
+		}
+	}
+	
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+	if (mcr & MCR_CTSCHG) {
+		if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) {
+			tty->hw_stopped = 0;
+			port->IER |= IER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else {
+			tty->hw_stopped = 1;
+			port->IER &= ~IER_TXRDY;
+		}
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	if (mcr & MCR_DSSXHG) {
+		if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) {
+			tty->hw_stopped = 0;
+			port->IER |= IER_TXRDY;
+			if (port->xmit_cnt <= port->wakeup_chars)
+				sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);
+		} else {
+			tty->hw_stopped = 1;
+			port->IER &= ~IER_TXRDY;
+		}
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */
+	
+	/* Clear change bits */
+	sx_out(bp, CD186x_MCR, 0);
+}
+
+
+/* The main interrupt processing routine */
+static void sx_interrupt(int irq, void * dev_id, struct pt_regs * regs)
+{
+	unsigned char status;
+	unsigned char ack;
+	struct specialix_board *bp;
+	unsigned long loop = 0;
+	int saved_reg;
+
+	bp = IRQ_to_board[irq];
+	
+	if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {
+#ifdef SPECIALIX_DEBUG 
+		printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq);
+#endif
+		return;
+	}
+
+	saved_reg = bp->reg;
+
+	while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) &
+	                                   (SRSR_RREQint |
+		                            SRSR_TREQint |
+	                                    SRSR_MREQint)))) {	
+		if (status & SRSR_RREQint) {
+			ack = sx_in(bp, CD186x_RRAR);
+
+			if (ack == (SX_ID | GIVR_IT_RCV))
+				sx_receive(bp);
+			else if (ack == (SX_ID | GIVR_IT_REXC))
+				sx_receive_exc(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n",
+				       board_No(bp), ack);
+		
+		} else if (status & SRSR_TREQint) {
+			ack = sx_in(bp, CD186x_TRAR);
+
+			if (ack == (SX_ID | GIVR_IT_TX))
+				sx_transmit(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n",
+				       board_No(bp), ack);
+		} else if (status & SRSR_MREQint) {
+			ack = sx_in(bp, CD186x_MRAR);
+
+			if (ack == (SX_ID | GIVR_IT_MODEM)) 
+				sx_check_modem(bp);
+			else
+				printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n",
+				       board_No(bp), ack);
+		
+		} 
+
+		sx_out(bp, CD186x_EOIR, 0);   /* Mark end of interrupt */
+	}
+	bp->reg = saved_reg;
+	outb (bp->reg, bp->base + SX_ADDR_REG);
+}
+
+
+/*
+ *  Routines for open & close processing.
+ */
+
+
+/* Called with disabled interrupts */
+extern inline int sx_setup_board(struct specialix_board * bp)
+{
+	int error;
+
+	if (bp->flags & SX_BOARD_ACTIVE) 
+		return 0;
+
+	error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", NULL);
+
+	if (error) 
+		return error;
+
+	IRQ_to_board[bp->irq] = bp;
+	(void) sx_in (bp, 0); /* Turn ON interrupts. */
+
+	bp->flags |= SX_BOARD_ACTIVE;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+/* Called with disabled interrupts */
+extern inline void sx_shutdown_board(struct specialix_board *bp)
+{
+	if (!(bp->flags & SX_BOARD_ACTIVE))
+		return;
+	
+	bp->flags &= ~SX_BOARD_ACTIVE;
+	
+	free_irq(bp->irq, NULL);
+	(void) sx_in_off (bp, 0); /* Turn off interrupts. */
+
+	IRQ_to_board[bp->irq] = NULL;
+	
+	MOD_DEC_USE_COUNT;
+}
+
+
+/*
+ * Setting up port characteristics. 
+ * Must be called with disabled interrupts
+ */
+static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port)
+{
+	struct tty_struct *tty;
+	unsigned long baud;
+	long tmp;
+	unsigned char cor1 = 0, cor3 = 0;
+	unsigned char mcor1 = 0, mcor2 = 0;
+	static int again=0;
+	
+	if (!(tty = port->tty) || !tty->termios)
+		return;
+
+	port->IER  = 0;
+	port->COR2 = 0;
+	/* Select port on the board */
+	sx_out(bp, CD186x_CAR, port_No(port));
+
+	/* The Specialix board doens't implement the RTS lines.
+	   They are used to set the IRQ level. Don't touch them. */
+	if (SX_CRTSCTS(tty))
+		port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+	else
+		port->MSVR =  (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR);
+#endif
+	baud = C_BAUD(tty);
+	
+	if (baud & CBAUDEX) {
+		baud &= ~CBAUDEX;
+		if (baud < 1 || baud > 2) 
+			port->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			baud += 15;
+	}
+	if (baud == 15) {
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			baud ++;
+		if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			baud += 2;
+	}
+	
+	
+	if (!baud_table[baud]) {
+		/* Drop DTR & exit */
+#ifdef SPECIALIX_DEBUG
+		printk (KERN_DEBUG "Dropping DTR...  Hmm....\n");
+#endif
+		if (!SX_CRTSCTS (tty)) {
+			port -> MSVR &= ~ MSVR_DTR;
+			sx_out(bp, CD186x_MSVR, port->MSVR );
+		} 
+#ifdef DEBUG_SPECIALIX
+		else
+			printk (KERN_DEBUG "Can't drop DTR: no DTR.\n");
+#endif
+		return;
+	} else {
+		/* Set DTR on */
+		if (!SX_CRTSCTS (tty)) {
+			port ->MSVR |= MSVR_DTR;
+		}
+	}
+	
+	/*
+	 * Now we must calculate some speed depended things 
+	 */
+	
+	/* Set baud rate for port */
+	tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
+		CD186x_TPC/2) / CD186x_TPC);
+	if ((tmp < 0x10) && (again < jiffies)) { 
+		again = jiffies + HZ * 60;
+		/* Page 48 of version 2.0 of the CL-CD1865 databook */
+		if (tmp >= 12) {
+			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+			        "Performance degradation is possible.\n",
+			        port_No (port), tmp);
+		} else {
+			printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n"
+			        "Warning: overstressing Cirrus chip. "
+                                "This might not work.\n", 
+			        port_No (port), tmp);
+		}
+	}
+
+	sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); 
+	sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); 
+	sx_out(bp, CD186x_RBPRL, tmp & 0xff); 
+	sx_out(bp, CD186x_TBPRL, tmp & 0xff);
+	
+	baud = (baud_table[baud] + 5) / 10;   /* Estimated CPS */
+	
+	/* Two timer ticks seems enough to wakeup something like SLIP driver */
+	tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO;		
+	port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ?
+					      SERIAL_XMIT_SIZE - 1 : tmp);
+	
+	/* Receiver timeout will be transmission time for 1.5 chars */
+	tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud;
+	tmp = (tmp > 0xff) ? 0xff : tmp;
+	sx_out(bp, CD186x_RTPR, tmp);
+	
+	switch (C_CSIZE(tty)) {
+	 case CS5:
+		cor1 |= COR1_5BITS;
+		break;
+	 case CS6:
+		cor1 |= COR1_6BITS;
+		break;
+	 case CS7:
+		cor1 |= COR1_7BITS;
+		break;
+	 case CS8:
+		cor1 |= COR1_8BITS;
+		break;
+	}
+	
+	if (C_CSTOPB(tty)) 
+		cor1 |= COR1_2SB;
+	
+	cor1 |= COR1_IGNORE;
+	if (C_PARENB(tty)) {
+		cor1 |= COR1_NORMPAR;
+		if (C_PARODD(tty)) 
+			cor1 |= COR1_ODDP;
+		if (I_INPCK(tty)) 
+			cor1 &= ~COR1_IGNORE;
+	}
+	/* Set marking of some errors */
+	port->mark_mask = RCSR_OE | RCSR_TOUT;
+	if (I_INPCK(tty)) 
+		port->mark_mask |= RCSR_FE | RCSR_PE;
+	if (I_BRKINT(tty) || I_PARMRK(tty)) 
+		port->mark_mask |= RCSR_BREAK;
+	if (I_IGNPAR(tty)) 
+		port->mark_mask &= ~(RCSR_FE | RCSR_PE);
+	if (I_IGNBRK(tty)) {
+		port->mark_mask &= ~RCSR_BREAK;
+		if (I_IGNPAR(tty)) 
+			/* Real raw mode. Ignore all */
+			port->mark_mask &= ~RCSR_OE;
+	}
+	/* Enable Hardware Flow Control */
+	if (C_CRTSCTS(tty)) {
+#ifdef SPECIALIX_BRAIN_DAMAGED_CTS
+		port->IER |= IER_DSR | IER_CTS;
+		mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD;
+		mcor2 |= MCOR2_DSROD | MCOR2_CTSOD;
+		tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));
+#else
+		port->COR2 |= COR2_CTSAE; 
+#endif
+	}
+	/* Enable Software Flow Control. FIXME: I'm not sure about this */
+	/* Some people reported that it works, but I still doubt it */
+	if (I_IXON(tty)) {
+		port->COR2 |= COR2_TXIBE;
+		cor3 |= (COR3_FCT | COR3_SCDE);
+		if (I_IXANY(tty))
+			port->COR2 |= COR2_IXM;
+		sx_out(bp, CD186x_SCHR1, START_CHAR(tty));
+		sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty));
+		sx_out(bp, CD186x_SCHR3, START_CHAR(tty));
+		sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty));
+	}
+	if (!C_CLOCAL(tty)) {
+		/* Enable CD check */
+		port->IER |= IER_CD;
+		mcor1 |= MCOR1_CDZD;
+		mcor2 |= MCOR2_CDOD;
+	}
+	
+	if (C_CREAD(tty)) 
+		/* Enable receiver */
+		port->IER |= IER_RXD;
+	
+	/* Set input FIFO size (1-8 bytes) */
+	cor3 |= SPECIALIX_RXFIFO; 
+	/* Setting up CD186x channel registers */
+	sx_out(bp, CD186x_COR1, cor1);
+	sx_out(bp, CD186x_COR2, port->COR2);
+	sx_out(bp, CD186x_COR3, cor3);
+	/* Make CD186x know about registers change */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3);
+	/* Setting up modem option registers */
+#ifdef DEBUG_SPECIALIX
+	printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);
+#endif
+	sx_out(bp, CD186x_MCOR1, mcor1);
+	sx_out(bp, CD186x_MCOR2, mcor2);
+	/* Enable CD186x transmitter & receiver */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN);
+	/* Enable interrupts */
+	sx_out(bp, CD186x_IER, port->IER);
+	/* And finally set the modem lines... */
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+}
+
+
+/* Must be called with interrupts enabled */
+static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port)
+{
+	unsigned long flags;
+	
+	if (port->flags & ASYNC_INITIALIZED)
+		return 0;
+	
+	if (!port->xmit_buf) {
+		/* We may sleep in get_free_page() */
+		unsigned long tmp;
+		
+		if (!(tmp = get_free_page(GFP_KERNEL)))
+			return -ENOMEM;
+
+		if (port->xmit_buf) {
+			free_page(tmp);
+			return -ERESTARTSYS;
+		}
+		port->xmit_buf = (unsigned char *) tmp;
+	}
+		
+	save_flags(flags); cli();
+		
+	if (port->tty) 
+		clear_bit(TTY_IO_ERROR, &port->tty->flags);
+		
+	if (port->count == 1) 
+		bp->count++;
+		
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	sx_change_speed(bp, port);
+	port->flags |= ASYNC_INITIALIZED;
+		
+	restore_flags(flags);
+	return 0;
+}
+
+
+/* Must be called with interrupts disabled */
+static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port)
+{
+	struct tty_struct *tty;
+	
+	if (!(port->flags & ASYNC_INITIALIZED)) 
+		return;
+	
+#ifdef SX_REPORT_OVERRUN
+	printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n",
+	       board_No(bp), port_No(port), port->overrun);
+#endif	
+#ifdef SX_REPORT_FIFO
+	{
+		int i;
+		
+		printk(KERN_INFO "sx%d: port %d: FIFO hits [ ",
+		       board_No(bp), port_No(port));
+		for (i = 0; i < 10; i++) {
+			printk("%ld ", port->hits[i]);
+		}
+		printk("].\n");
+	}
+#endif	
+	if (port->xmit_buf) {
+		free_page((unsigned long) port->xmit_buf);
+		port->xmit_buf = NULL;
+	}
+
+	/* Select port */
+	sx_out(bp, CD186x_CAR, port_No(port));
+
+	if (!(tty = port->tty) || C_HUPCL(tty)) {
+		/* Drop DTR */
+		sx_out(bp, CD186x_MSVDTR, 0);
+	}
+	
+	/* Reset port */
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_SOFTRESET);
+	/* Disable all interrupts from this port */
+	port->IER = 0;
+	sx_out(bp, CD186x_IER, port->IER);
+	
+	if (tty)
+		set_bit(TTY_IO_ERROR, &tty->flags);
+	port->flags &= ~ASYNC_INITIALIZED;
+	
+	if (--bp->count < 0) {
+		printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n",
+		       board_No(bp), bp->count);
+		bp->count = 0;
+	}
+	
+	/*
+	 * If this is the last opened port on the board
+	 * shutdown whole board
+	 */
+	if (!bp->count) 
+		sx_shutdown_board(bp);
+}
+
+	
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+                           struct specialix_port *port)
+{
+	struct wait_queue wait = { current, NULL };
+	struct specialix_board *bp = port_Board(port);
+	int    retval;
+	int    do_clocal = 0;
+	int    CD;
+
+	/*
+	 * If the device is in the middle of being closed, then block
+	 * until it's done, and then try again.
+	 */
+	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
+		interruptible_sleep_on(&port->close_wait);
+		if (port->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) {
+		if (port->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (port->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (port->session != current->session))
+			return -EBUSY;
+		if ((port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (port->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (port->pgrp != current->pgrp))
+			return -EBUSY;
+		port->flags |= ASYNC_CALLOUT_ACTIVE;
+		return 0;
+	}
+	
+	/*
+	 * If non-blocking mode is set, or the port is not enabled,
+	 * then make the check up front and then exit.
+	 */
+	if ((filp->f_flags & O_NONBLOCK) ||
+	    (tty->flags & (1 << TTY_IO_ERROR))) {
+		if (port->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		port->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (port->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (port->normal_termios.c_cflag & CLOCAL) 
+			do_clocal = 1;
+	} else {
+		if (C_CLOCAL(tty))
+			do_clocal = 1;
+	}
+	
+	/*
+	 * Block waiting for the carrier detect and the line to become
+	 * free (i.e., not in use by the callout).  While we are in
+	 * this loop, info->count is dropped by one, so that
+	 * rs_close() knows when to free things.  We restore it upon
+	 * exit, either normal or abnormal.
+	 */
+	retval = 0;
+	add_wait_queue(&port->open_wait, &wait);
+	cli();
+	if (!tty_hung_up_p(filp))
+		port->count--;
+	sti();
+	port->blocked_open++;
+	while (1) {
+		cli();
+		sx_out(bp, CD186x_CAR, port_No(port));
+		CD = sx_in(bp, CD186x_MSVR) & MSVR_CD;
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) {
+			if (SX_CRTSCTS (tty)) {
+				/* Activate RTS */
+				port->MSVR |= MSVR_DTR;
+				sx_out (bp, CD186x_MSVR, port->MSVR);
+			} else {
+				/* Activate DTR */
+				port->MSVR |= MSVR_DTR;
+				sx_out (bp, CD186x_MSVR, port->MSVR);
+			} 
+		}
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(port->flags & ASYNC_INITIALIZED)) {
+			if (port->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+			break;
+		}
+		if (!(port->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(port->flags & ASYNC_CLOSING) &&
+		    (do_clocal || CD))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&port->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		port->count++;
+	port->blocked_open--;
+	if (retval)
+		return retval;
+	
+	port->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}	
+
+
+static int sx_open(struct tty_struct * tty, struct file * filp)
+{
+	int board;
+	int error;
+	struct specialix_port * port;
+	struct specialix_board * bp;
+	unsigned long flags;
+	
+	board = SX_BOARD(MINOR(tty->device));
+
+	if (board > SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT))
+		return -ENODEV;
+	
+	bp = &sx_board[board];
+	port = sx_port + board * SX_NPORT + SX_PORT(MINOR(tty->device));
+
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "Board = %d, bp = %p, port = %p, portno = %d.\n", 
+	        board, bp, port, SX_PORT(MINOR(tty->device)));
+#endif
+
+	if (sx_paranoia_check(port, tty->device, "sx_open"))
+		return -ENODEV;
+
+	if ((error = sx_setup_board(bp)))
+		return error;
+
+	port->count++;
+	tty->driver_data = port;
+	port->tty = tty;
+
+	if ((error = sx_setup_port(bp, port))) 
+		return error;
+	
+	if ((error = block_til_ready(tty, filp, port)))
+		return error;
+
+	if ((port->count == 1) && (port->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SPECIALIX_TYPE_NORMAL)
+			*tty->termios = port->normal_termios;
+		else
+			*tty->termios = port->callout_termios;
+		save_flags(flags); cli();
+		sx_change_speed(bp, port);
+		restore_flags(flags);
+	}
+
+	port->session = current->session;
+	port->pgrp = current->pgrp;
+	return 0;
+}
+
+
+static void sx_close(struct tty_struct * tty, struct file * filp)
+{
+	struct specialix_port *port = (struct specialix_port *) tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+	unsigned long timeout;
+	
+	if (!port || sx_paranoia_check(port, tty->device, "close"))
+		return;
+	
+	save_flags(flags); cli();
+	if (tty_hung_up_p(filp)) {
+		restore_flags(flags);
+		return;
+	}
+	
+	bp = port_Board(port);
+	if ((tty->count == 1) && (port->count != 1)) {
+		printk(KERN_ERR "sx%d: sx_close: bad port count;"
+		       " tty->count is 1, port count is %d\n",
+		       board_No(bp), port->count);
+		port->count = 1;
+	}
+	if (--port->count < 0) {
+		printk(KERN_ERR "sx%d: sx_close: bad port count for tty%d: %d\n",
+		       board_No(bp), port_No(port), port->count);
+		port->count = 0;
+	}
+	if (port->count) {
+		restore_flags(flags);
+		return;
+	}
+	port->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (port->flags & ASYNC_NORMAL_ACTIVE)
+		port->normal_termios = *tty->termios;
+	if (port->flags & ASYNC_CALLOUT_ACTIVE)
+		port->callout_termios = *tty->termios;
+	/*
+	 * Now we wait for the transmit buffer to clear; and we notify 
+	 * the line discipline to only process XON/XOFF characters.
+	 */
+	tty->closing = 1;
+	if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, port->closing_wait);
+	/*
+	 * At this point we stop accepting input.  To do this, we
+	 * disable the receive line status interrupts, and tell the
+	 * interrupt driver to stop checking the data ready bit in the
+	 * line status register.
+	 */
+	port->IER &= ~IER_RXD;
+	if (port->flags & ASYNC_INITIALIZED) {
+		port->IER &= ~IER_TXRDY;
+		port->IER |= IER_TXEMPTY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+		/*
+		 * Before we drop DTR, make sure the UART transmitter
+		 * has completely drained; this is especially
+		 * important if there is a transmit FIFO!
+		 */
+		timeout = jiffies+HZ;
+		while(port->IER & IER_TXEMPTY) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + port->timeout;
+			schedule();
+			if (jiffies > timeout) {
+				printk (KERN_INFO "Timeout waiting for close\n");
+				break;
+			}
+		}
+
+	}
+	sx_shutdown_port(bp, port);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	port->event = 0;
+	port->tty = 0;
+	if (port->blocked_open) {
+		if (port->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + port->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&port->open_wait);
+	}
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&port->close_wait);
+	restore_flags(flags);
+}
+
+
+static int sx_write(struct tty_struct * tty, int from_user, 
+                    const unsigned char *buf, int count)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	int c, total = 0;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_write"))
+		return 0;
+	
+	bp = port_Board(port);
+
+	if (!tty || !port->xmit_buf || !tmp_buf)
+		return 0;
+
+	if (from_user)
+		down(&tmp_buf_sem);
+
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+		                   SERIAL_XMIT_SIZE - port->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			copy_from_user(tmp_buf, buf, c);
+			c = MIN(c, MIN(SERIAL_XMIT_SIZE - port->xmit_cnt - 1,
+			               SERIAL_XMIT_SIZE - port->xmit_head));
+			memcpy(port->xmit_buf + port->xmit_head, tmp_buf, c);
+		} else
+			memcpy(port->xmit_buf + port->xmit_head, buf, c);
+		port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+		port->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (from_user)
+		up(&tmp_buf_sem);
+	if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    !(port->IER & IER_TXRDY)) {
+		port->IER |= IER_TXRDY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	restore_flags(flags);
+	return total;
+}
+
+
+static void sx_put_char(struct tty_struct * tty, unsigned char ch)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+
+	if (sx_paranoia_check(port, tty->device, "sx_put_char"))
+		return;
+
+	if (!tty || !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	
+	if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	port->xmit_buf[port->xmit_head++] = ch;
+	port->xmit_head &= SERIAL_XMIT_SIZE - 1;
+	port->xmit_cnt++;
+	restore_flags(flags);
+}
+
+
+static void sx_flush_chars(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_flush_chars"))
+		return;
+	
+	if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !port->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	port->IER |= IER_TXRDY;
+	sx_out(port_Board(port), CD186x_CAR, port_No(port));
+	sx_out(port_Board(port), CD186x_IER, port->IER);
+	restore_flags(flags);
+}
+
+
+static int sx_write_room(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	int	ret;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_write_room"))
+		return 0;
+
+	ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+
+static int sx_chars_in_buffer(struct tty_struct *tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_chars_in_buffer"))
+		return 0;
+	
+	return port->xmit_cnt;
+}
+
+
+static void sx_flush_buffer(struct tty_struct *tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_flush_buffer"))
+		return;
+
+	save_flags(flags); cli();
+	port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
+	restore_flags(flags);
+	
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+
+static int sx_get_modem_info(struct specialix_port * port, unsigned int *value)
+{
+	struct specialix_board * bp;
+	unsigned char status;
+	unsigned int result;
+	unsigned long flags;
+
+	bp = port_Board(port);
+	save_flags(flags); cli();
+	sx_out(bp, CD186x_CAR, port_No(port));
+	status = sx_in(bp, CD186x_MSVR);
+	restore_flags(flags);
+#ifdef DEBUG_SPECIALIX
+	printk (KERN_DEBUG "Got msvr[%d] = %02x, car = %d.\n", 
+		port_No(port), status, sx_in (bp, CD186x_CAR));
+	printk (KERN_DEBUG "sx_port = %p, port = %p\n", sx_port, port);
+#endif
+	if (SX_CRTSCTS(port->tty)) {
+		result  = /*   (status & MSVR_RTS) ? */ TIOCM_DTR /* : 0) */ 
+		          |   ((status & MSVR_DTR) ? TIOCM_RTS : 0)
+		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+	} else {
+		result  = /*   (status & MSVR_RTS) ? */ TIOCM_RTS /* : 0) */ 
+		          |   ((status & MSVR_DTR) ? TIOCM_DTR : 0)
+		          |   ((status & MSVR_CD)  ? TIOCM_CAR : 0)
+		          |/* ((status & MSVR_DSR) ? */ TIOCM_DSR /* : 0) */
+		          |   ((status & MSVR_CTS) ? TIOCM_CTS : 0);
+	}
+	put_user(result,(unsigned long *) value);
+	return 0;
+}
+
+
+static int sx_set_modem_info(struct specialix_port * port, unsigned int cmd,
+                             unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+	unsigned long flags;
+	struct specialix_board *bp = port_Board(port);
+
+	error = verify_area(VERIFY_READ, value, sizeof(int));
+	if (error) 
+		return error;
+
+	Get_user(arg, (unsigned long *) value);
+	switch (cmd) {
+	case TIOCMBIS: 
+	   /*	if (arg & TIOCM_RTS) 
+			port->MSVR |= MSVR_RTS; */
+	   /*   if (arg & TIOCM_DTR)
+			port->MSVR |= MSVR_DTR; */
+
+		if (SX_CRTSCTS(port->tty)) {
+			if (arg & TIOCM_RTS)
+				port->MSVR |= MSVR_DTR; 
+		} else {
+			if (arg & TIOCM_DTR)
+				port->MSVR |= MSVR_DTR; 
+		}	     
+		break;
+	case TIOCMBIC:
+	  /*	if (arg & TIOCM_RTS)
+			port->MSVR &= ~MSVR_RTS; */
+	  /*    if (arg & TIOCM_DTR)
+			port->MSVR &= ~MSVR_DTR; */
+		if (SX_CRTSCTS(port->tty)) {
+			if (arg & TIOCM_RTS)
+				port->MSVR &= ~MSVR_DTR;
+		} else {
+			if (arg & TIOCM_DTR)
+				port->MSVR &= ~MSVR_DTR;
+		}
+		break;
+	case TIOCMSET:
+	  /* port->MSVR = (arg & TIOCM_RTS) ? (port->MSVR | MSVR_RTS) : 
+						 (port->MSVR & ~MSVR_RTS); */
+	  /* port->MSVR = (arg & TIOCM_DTR) ? (port->MSVR | MSVR_DTR) : 
+						 (port->MSVR & ~MSVR_DTR); */
+		if (SX_CRTSCTS(port->tty)) {
+	  		port->MSVR = (arg & TIOCM_RTS) ? 
+			                         (port->MSVR |  MSVR_DTR) : 
+			                         (port->MSVR & ~MSVR_DTR);
+		} else {
+			port->MSVR = (arg & TIOCM_DTR) ?
+			                         (port->MSVR |  MSVR_DTR):
+			                         (port->MSVR & ~MSVR_DTR);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	save_flags(flags); cli();
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+	return 0;
+}
+
+
+extern inline void sx_send_break(struct specialix_port * port, unsigned long length)
+{
+	struct specialix_board *bp = port_Board(port);
+	unsigned long flags;
+	
+	save_flags(flags); cli();
+	port->break_length = SPECIALIX_TPS / HZ * length;
+	port->COR2 |= COR2_ETC;
+	port->IER  |= IER_TXRDY;
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_COR2, port->COR2);
+	sx_out(bp, CD186x_IER, port->IER);
+	sx_wait_CCR(bp);
+	sx_out(bp, CD186x_CCR, CCR_CORCHG2);
+	sx_wait_CCR(bp);
+	restore_flags(flags);
+}
+
+
+extern inline int sx_set_serial_info(struct specialix_port * port,
+                                     struct serial_struct * newinfo)
+{
+	struct serial_struct tmp;
+	struct specialix_board *bp = port_Board(port);
+	int change_speed;
+	unsigned long flags;
+	int error;
+	
+	error = verify_area(VERIFY_READ, (void *) newinfo, sizeof(tmp));
+	if (error)
+		return error;
+
+	copy_from_user(&tmp, newinfo, sizeof(tmp));
+	
+#if 0	
+	if ((tmp.irq != bp->irq) ||
+	    (tmp.port != bp->base) ||
+	    (tmp.type != PORT_CIRRUS) ||
+	    (tmp.baud_base != (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC) ||
+	    (tmp.custom_divisor != 0) ||
+	    (tmp.xmit_fifo_size != CD186x_NFIFO) ||
+	    (tmp.flags & ~SPECIALIX_LEGAL_FLAGS))
+		return -EINVAL;
+#endif	
+
+	change_speed = ((port->flags & ASYNC_SPD_MASK) !=
+			(tmp.flags & ASYNC_SPD_MASK));
+	
+	if (!suser()) {
+		if ((tmp.close_delay != port->close_delay) ||
+		    (tmp.closing_wait != port->closing_wait) ||
+		    ((tmp.flags & ~ASYNC_USR_MASK) !=
+		     (port->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		port->flags = ((port->flags & ~ASYNC_USR_MASK) |
+		                  (tmp.flags & ASYNC_USR_MASK));
+	} else {
+		port->flags = ((port->flags & ~ASYNC_FLAGS) |
+		                  (tmp.flags & ASYNC_FLAGS));
+		port->close_delay = tmp.close_delay;
+		port->closing_wait = tmp.closing_wait;
+	}
+	if (change_speed) {
+		save_flags(flags); cli();
+		sx_change_speed(bp, port);
+		restore_flags(flags);
+	}
+	return 0;
+}
+
+
+extern inline int sx_get_serial_info(struct specialix_port * port,
+				     struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+	struct specialix_board *bp = port_Board(port);
+	int error;
+	
+	error = verify_area(VERIFY_WRITE, (void *) retinfo, sizeof(tmp));
+	if (error)
+		return error;
+
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = PORT_CIRRUS;
+	tmp.line = port - sx_port;
+	tmp.port = bp->base;
+	tmp.irq  = bp->irq;
+	tmp.flags = port->flags;
+	tmp.baud_base = (SX_OSCFREQ + CD186x_TPC/2) / CD186x_TPC;
+	tmp.close_delay = port->close_delay * HZ/100;
+	tmp.closing_wait = port->closing_wait * HZ/100;
+	tmp.xmit_fifo_size = CD186x_NFIFO;
+	copy_to_user(retinfo, &tmp, sizeof(tmp));
+	return 0;
+}
+
+
+static int sx_ioctl(struct tty_struct * tty, struct file * filp, 
+                    unsigned int cmd, unsigned long arg)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	int error;
+	int retval;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_ioctl"))
+		return -ENODEV;
+	
+	switch (cmd) {
+	 case TCSBRK:	/* SVID version: non-zero arg --> no break */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		if (!arg)
+			sx_send_break(port, HZ/4);	/* 1/4 second */
+		return 0;
+	 case TCSBRKP:	/* support for POSIX tcsendbreak() */
+		retval = tty_check_change(tty);
+		if (retval)
+			return retval;
+		tty_wait_until_sent(tty, 0);
+		sx_send_break(port, arg ? arg*(HZ/10) : HZ/4);
+		return 0;
+	 case TIOCGSOFTCAR:
+		error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
+		if (error)
+			return error;
+		put_user(C_CLOCAL(tty) ? 1 : 0,
+		         (unsigned long *) arg);
+		return 0;
+	 case TIOCSSOFTCAR:
+		Get_user(arg, (unsigned long *) arg);
+		tty->termios->c_cflag =
+			((tty->termios->c_cflag & ~CLOCAL) |
+			(arg ? CLOCAL : 0));
+		return 0;
+	 case TIOCMGET:
+		error = verify_area(VERIFY_WRITE, (void *) arg,
+		                    sizeof(unsigned int));
+		if (error)
+			return error;
+		return sx_get_modem_info(port, (unsigned int *) arg);
+	 case TIOCMBIS:
+	 case TIOCMBIC:
+	 case TIOCMSET:
+		return sx_set_modem_info(port, cmd, (unsigned int *) arg);
+	 case TIOCGSERIAL:	
+		return sx_get_serial_info(port, (struct serial_struct *) arg);
+	 case TIOCSSERIAL:	
+		return sx_set_serial_info(port, (struct serial_struct *) arg);
+	 default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+
+static void sx_throttle(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_throttle"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+
+	/* Use DTR instead of RTS ! */
+	if (SX_CRTSCTS (tty)) 
+		port->MSVR &= ~MSVR_DTR;
+	else {
+		/* Auch!!! I think the system shouldn't call this then. */
+		/* Or maybe we're supposed (allowed?) to do our side of hw
+		   handshake anyway, even when hardware handshake is off. 
+		   When you see this in your logs, please report.... */
+		printk (KERN_ERR "sx%d: Need to throttle, but can't (hardware hs is off)\n",
+	                 port_No (port));
+	}
+	sx_out(bp, CD186x_CAR, port_No(port));
+	if (I_IXOFF(tty)) {
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_SSCH2);
+		sx_wait_CCR(bp);
+	}
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+}
+
+
+static void sx_unthrottle(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_unthrottle"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	/* XXXX Use DTR INSTEAD???? */
+	if (SX_CRTSCTS(tty)) {
+		port->MSVR |= MSVR_DTR;
+	} /* Else clause: see remark in "sx_throttle"... */
+
+	sx_out(bp, CD186x_CAR, port_No(port));
+	if (I_IXOFF(tty)) {
+		sx_wait_CCR(bp);
+		sx_out(bp, CD186x_CCR, CCR_SSCH1);
+		sx_wait_CCR(bp);
+	}
+	sx_out(bp, CD186x_MSVR, port->MSVR);
+	restore_flags(flags);
+}
+
+
+static void sx_stop(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_stop"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	port->IER &= ~IER_TXRDY;
+	sx_out(bp, CD186x_CAR, port_No(port));
+	sx_out(bp, CD186x_IER, port->IER);
+	restore_flags(flags);
+}
+
+
+static void sx_start(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_start"))
+		return;
+	
+	bp = port_Board(port);
+	
+	save_flags(flags); cli();
+	if (port->xmit_cnt && port->xmit_buf && !(port->IER & IER_TXRDY)) {
+		port->IER |= IER_TXRDY;
+		sx_out(bp, CD186x_CAR, port_No(port));
+		sx_out(bp, CD186x_IER, port->IER);
+	}
+	restore_flags(flags);
+}
+
+
+/*
+ * This routine is called from the scheduler tqueue when the interrupt
+ * routine has signalled that a hangup has occurred.  The path of
+ * hangup processing is:
+ *
+ * 	serial interrupt routine -> (scheduler tqueue) ->
+ * 	do_sx_hangup() -> tty->hangup() -> sx_hangup()
+ * 
+ */
+static void do_sx_hangup(void *private_)
+{
+	struct specialix_port	*port = (struct specialix_port *) private_;
+	struct tty_struct	*tty;
+	
+	tty = port->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+}
+
+
+static void sx_hangup(struct tty_struct * tty)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	struct specialix_board *bp;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_hangup"))
+		return;
+	
+	bp = port_Board(port);
+	
+	sx_shutdown_port(bp, port);
+	port->event = 0;
+	port->count = 0;
+	port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	port->tty = 0;
+	wake_up_interruptible(&port->open_wait);
+}
+
+
+static void sx_set_termios(struct tty_struct * tty, struct termios * old_termios)
+{
+	struct specialix_port *port = (struct specialix_port *)tty->driver_data;
+	unsigned long flags;
+				
+	if (sx_paranoia_check(port, tty->device, "sx_set_termios"))
+		return;
+	
+	if (tty->termios->c_cflag == old_termios->c_cflag &&
+	    tty->termios->c_iflag == old_termios->c_iflag)
+		return;
+
+	save_flags(flags); cli();
+	sx_change_speed(port_Board(port), port);
+	restore_flags(flags);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		sx_start(tty);
+	}
+}
+
+
+static void do_specialix_bh(void)
+{
+	 run_task_queue(&tq_specialix);
+}
+
+
+static void do_softint(void *private_)
+{
+	struct specialix_port	*port = (struct specialix_port *) private_;
+	struct tty_struct	*tty;
+	
+	if(!(tty = port->tty)) 
+		return;
+
+	if (clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+
+static int sx_init_drivers(void)
+{
+	int error;
+	int i;
+
+	
+	if (!(tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL))) {
+		printk(KERN_ERR "sx: Couldn't get free page.\n");
+		return 1;
+	}
+	init_bh(SPECIALIX_BH, do_specialix_bh);
+	memset(IRQ_to_board, 0, sizeof(IRQ_to_board));
+	memset(&specialix_driver, 0, sizeof(specialix_driver));
+	specialix_driver.magic = TTY_DRIVER_MAGIC;
+	specialix_driver.name = "ttyW";
+	specialix_driver.major = SPECIALIX_NORMAL_MAJOR;
+	specialix_driver.num = SX_NBOARD * SX_NPORT;
+	specialix_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	specialix_driver.subtype = SPECIALIX_TYPE_NORMAL;
+	specialix_driver.init_termios = tty_std_termios;
+	specialix_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	specialix_driver.flags = TTY_DRIVER_REAL_RAW;
+	specialix_driver.refcount = &specialix_refcount;
+	specialix_driver.table = specialix_table;
+	specialix_driver.termios = specialix_termios;
+	specialix_driver.termios_locked = specialix_termios_locked;
+
+	specialix_driver.open  = sx_open;
+	specialix_driver.close = sx_close;
+	specialix_driver.write = sx_write;
+	specialix_driver.put_char = sx_put_char;
+	specialix_driver.flush_chars = sx_flush_chars;
+	specialix_driver.write_room = sx_write_room;
+	specialix_driver.chars_in_buffer = sx_chars_in_buffer;
+	specialix_driver.flush_buffer = sx_flush_buffer;
+	specialix_driver.ioctl = sx_ioctl;
+	specialix_driver.throttle = sx_throttle;
+	specialix_driver.unthrottle = sx_unthrottle;
+	specialix_driver.set_termios = sx_set_termios;
+	specialix_driver.stop = sx_stop;
+	specialix_driver.start = sx_start;
+	specialix_driver.hangup = sx_hangup;
+
+	specialix_callout_driver = specialix_driver;
+	specialix_callout_driver.name = "cuw";
+	specialix_callout_driver.major = SPECIALIX_CALLOUT_MAJOR;
+	specialix_callout_driver.subtype = SPECIALIX_TYPE_CALLOUT;
+	
+	if ((error = tty_register_driver(&specialix_driver))) {
+		free_page((unsigned long)tmp_buf);
+		printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
+		       error);
+		return 1;
+	}
+	if ((error = tty_register_driver(&specialix_callout_driver))) {
+		free_page((unsigned long)tmp_buf);
+		tty_unregister_driver(&specialix_driver);
+		printk(KERN_ERR "sx: Couldn't register specialix IO8+ callout driver, error = %d\n",
+		       error);
+		return 1;
+	}
+	
+	memset(sx_port, 0, sizeof(sx_port));
+	for (i = 0; i < SX_NPORT * SX_NBOARD; i++) {
+		sx_port[i].callout_termios = specialix_callout_driver.init_termios;
+		sx_port[i].normal_termios  = specialix_driver.init_termios;
+		sx_port[i].magic = SPECIALIX_MAGIC;
+		sx_port[i].tqueue.routine = do_softint;
+		sx_port[i].tqueue.data = &sx_port[i];
+		sx_port[i].tqueue_hangup.routine = do_sx_hangup;
+		sx_port[i].tqueue_hangup.data = &sx_port[i];
+		sx_port[i].close_delay = 50 * HZ/100;
+		sx_port[i].closing_wait = 3000 * HZ/100;
+	}
+	
+	return 0;
+}
+
+
+static void sx_release_drivers(void)
+{
+	free_page((unsigned long)tmp_buf);
+	tty_unregister_driver(&specialix_driver);
+	tty_unregister_driver(&specialix_callout_driver);
+}
+
+
+#ifndef MODULE
+/*
+ * Called at boot time.
+ * 
+ * You can specify IO base for up to SX_NBOARD cards,
+ * using line "specialix=0xiobase1,0xiobase2,.." at LILO prompt.
+ * Note that there will be no probing at default
+ * addresses in this case.
+ *
+ */ 
+void specialix_setup(char *str, int * ints)
+{
+	int i;
+        
+	for (i=0;i<SX_NBOARD;i++) {
+		sx_board[i].base = 0;
+	}
+
+	for (i = 1; i <= ints[0]; i++) {
+		if (i&1)
+			sx_board[i/2].base = ints[i];
+		else
+			sx_board[i/2 -1].irq = ints[i];
+	}
+}
+#endif
+
+/* 
+ * This routine must be called by kernel at boot time 
+ */
+int specialix_init(void) 
+{
+	int i;
+	int found = 0;
+
+	printk(KERN_INFO "sx: Specialix IO8+ driver v" VERSION ", (c) R.E.Wolff 1997.\n");
+	printk(KERN_INFO "sx: derived from work (c) D.Gorodchanin 1994-1996.\n");
+#ifdef CONFIG_SPECIALIX_RTSCTS
+	printk (KERN_INFO "sx: DTR/RTS pin is always RTS.\n");
+#else
+	printk (KERN_INFO "sx: DTR/RTS pin is RTS when CRTSCTS is on.\n");
+#endif
+	
+	if (sx_init_drivers()) 
+		return -EIO;
+
+	for (i = 0; i < SX_NBOARD; i++) 
+		if (sx_board[i].base && !sx_probe(&sx_board[i]))
+			found++;
+
+	if (!found) {
+		sx_release_drivers();
+		printk(KERN_INFO "sx: No specialix IO8+ boards detected.\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#ifdef MODULE
+int iobase[SX_NBOARD]  = {0,};
+
+int irq [SX_NBOARD] = {0,};
+
+/*
+ * You can setup up to 4 boards.
+ * by specifying "iobase=0xXXX iobase1=0xXXX ..." as insmod parameter.
+ * 
+ * More than 4 boards in one computer is not possible, as the card can
+ * only use 4 different interrupts. 
+ *
+ */
+int init_module(void) 
+{
+	int i;
+
+	if (iobase[0] || iobase[1] || iobase[2] || iobase[3]) {
+		for(i = 0; i < SX_NBOARD; i++) {
+			sx_board[i].base = iobase[i];
+			sx_board[i].irq = irq[i];
+		}
+	}
+
+	return specialix_init();
+}
+	
+
+void cleanup_module(void)
+{
+	int i;
+	
+	sx_release_drivers();
+	for (i = 0; i < SX_NBOARD; i++)
+		if (sx_board[i].flags & SX_BOARD_PRESENT) 
+			sx_release_io_range(&sx_board[i]);
+#ifdef SPECIALIX_TIMER
+	del_timer (&missed_irq_timer);
+#endif
+	
+}
+#endif /* MODULE */
diff -urN linux-2.0.31-pre4/drivers/char/specialix_io8.h linux-2.0.31-pre5/drivers/char/specialix_io8.h
--- linux-2.0.31-pre4/drivers/char/specialix_io8.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/specialix_io8.h	2003-08-15 15:03:58.000000000 -0700
@@ -0,0 +1,146 @@
+/*
+ *      linux/drivers/char/specialix_io8.h  -- 
+ *                                   Specialix IO8+ multiport serial driver.
+ *
+ *      Copyright (C) 1997 Roger Wolff (R.E.Wolff@BitWizard.nl)
+ *      Copyright (C) 1994-1996  Dmitry Gorodchanin (begemot@bgm.rosprint.net)
+ *
+ *
+ *      Specialix pays for the development and support of this driver.
+ *      Please DO contact io8-linux@specialix.co.uk if you require
+ *      support.
+ *
+ *      This driver was developped in the BitWizard linux device
+ *      driver service. If you require a linux device driver for your
+ *      product, please contact devices@BitWizard.nl for a quote.
+ *
+ *      This code is firmly based on the riscom/8 serial driver,
+ *      written by Dmitry Gorodchanin. The specialix IO8+ card
+ *      programming information was obtained from the CL-CD1865 Data
+ *      Book, and Specialix document number 6200059: IO8+ Hardware
+ *      Functional Specification.
+ *
+ *      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.
+ *
+ *      This program is distributed in the hope that it will be
+ *      useful, but WITHOUT ANY WARRANTY; without even the implied
+ *      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ *      PURPOSE.  See the GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public
+ *      License along with this program; if not, write to the Free
+ *      Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
+ *      USA.
+ * */
+
+#ifndef __LINUX_SPECIALIX_H
+#define __LINUX_SPECIALIX_H
+
+#include <linux/serial.h>
+
+#ifdef __KERNEL__
+
+#define SX_NBOARD		4
+/* NOTE: Specialix decoder recognizes 4 addresses, but only two are used.... */
+#define SX_IO_SPACE             4
+/* eight ports per board. */
+#define SX_NPORT        	8
+#define SX_BOARD(line)		((line) / SX_NPORT)
+#define SX_PORT(line)		((line) & (SX_NPORT - 1))
+
+
+#define SX_DATA_REG 0     /* Base+0 : Data register */
+#define SX_ADDR_REG 1     /* base+1 : Address register. */
+
+#define MHz *1000000	/* I'm ashamed of myself. */
+
+/* On-board oscillator frequency */
+#define SX_OSCFREQ      (25 MHz/2)
+/* There is a 25MHz crystal on the board, but the chip is in /2 mode */
+
+
+/* Ticks per sec. Used for setting receiver timeout and break length */
+#define SPECIALIX_TPS		4000
+
+/* Yeah, after heavy testing I decided it must be 6.
+ * Sure, You can change it if needed.
+ */
+#define SPECIALIX_RXFIFO	6	/* Max. receiver FIFO size (1-8) */
+
+#define SPECIALIX_MAGIC		0x0907
+
+#define SX_CCR_TIMEOUT 10000   /* CCR timeout. You may need to wait upto
+                                  10 milliseconds before the internal
+                                  processor is available again after
+                                  you give it a command */
+
+#define SX_IOBASE1	0x100
+#define SX_IOBASE2	0x180
+#define SX_IOBASE3	0x250
+#define SX_IOBASE4	0x260
+
+struct specialix_board {
+	unsigned long   flags;
+	unsigned short	base;
+	unsigned char 	irq;
+	signed   char	count;
+	unsigned char	DTR;
+        int reg;
+};
+
+#define SX_BOARD_PRESENT	0x00000001
+#define SX_BOARD_ACTIVE		0x00000002
+
+
+struct specialix_port {
+	int			magic;
+	int			baud_base;
+	int			flags;
+	struct tty_struct 	* tty;
+	int			count;
+	int			blocked_open;
+	int			event;
+	int			timeout;
+	int			close_delay;
+	long			session;
+	long			pgrp;
+	unsigned char 		* xmit_buf;
+	int			custom_divisor;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct termios          normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	short			wakeup_chars;
+	short			break_length;
+	unsigned short		closing_wait;
+	unsigned char		mark_mask;
+	unsigned char		IER;
+	unsigned char		MSVR;
+	unsigned char		COR2;
+#ifdef SX_REPORT_OVERRUN
+	unsigned long		overrun;
+#endif	
+#ifdef SX_REPORT_FIFO
+	unsigned long		hits[10];
+#endif
+};
+
+#endif /* __KERNEL__ */
+#endif /* __LINUX_SPECIALIX_H */
+
+
+
+
+
+
+
+
+
diff -urN linux-2.0.31-pre4/drivers/char/tty_io.c linux-2.0.31-pre5/drivers/char/tty_io.c
--- linux-2.0.31-pre4/drivers/char/tty_io.c	1996-10-31 04:34:58.000000000 -0800
+++ linux-2.0.31-pre5/drivers/char/tty_io.c	2003-08-15 15:03:58.000000000 -0700
@@ -1931,6 +1931,9 @@
 #ifdef CONFIG_BAYCOM
 	baycom_init();
 #endif
+#ifdef CONFIG_SPECIALIX
+	specialix_init();
+#endif
 	pty_init();
 	vcs_init();
 	return 0;
diff -urN linux-2.0.31-pre4/drivers/net/3c509.c linux-2.0.31-pre5/drivers/net/3c509.c
--- linux-2.0.31-pre4/drivers/net/3c509.c	1996-10-29 17:42:40.000000000 -0800
+++ linux-2.0.31-pre5/drivers/net/3c509.c	2003-08-15 15:03:58.000000000 -0700
@@ -1,8 +1,8 @@
 /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
 /*
-	Written 1993-1995 by Donald Becker.
+	Written 1993-1997 by Donald Becker.
 
-	Copyright 1994,1995 by Donald Becker.
+	Copyright 1994-1997 by Donald Becker.
 	Copyright 1993 United States Government as represented by the
 	Director, National Security Agency.	 This software may be used and
 	distributed according to the terms of the GNU Public License,
@@ -31,9 +31,17 @@
 				what the docs say some people definitely
 				get problems with lower (but in card spec)
 				delays
+		v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
+				other cleanups.  -djb
 */
 
-static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.11 4/22/97 becker@cesdis.gsfc.nasa.gov\n";
+/* A few values that may be tweaked. */
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  (400*HZ/1000)
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK      10
 
 #include <linux/module.h>
 
@@ -64,7 +72,7 @@
 
 /* To minimize the size of the driver source I only define operating
    constants if they are used several times.  You'll need the manual
-   if you want to understand driver details. */
+   anyway if you want to understand driver details. */
 /* Offsets from base I/O address. */
 #define EL3_DATA 0x00
 #define EL3_CMD 0x0e
@@ -115,11 +123,13 @@
 
 struct el3_private {
 	struct enet_statistics stats;
+	struct device *next_dev;
 	/* skb send-queue */
 	int head, size;
 	struct sk_buff *queue[SKB_QUEUE_SIZE];
 };
 static int id_port = 0x100;
+static struct device *el3_root_dev = NULL;
 
 static ushort id_read_eeprom(int index);
 static ushort read_eeprom(short ioaddr, int index);
@@ -130,9 +140,7 @@
 static struct enet_statistics *el3_get_stats(struct device *dev);
 static int el3_rx(struct device *dev);
 static int el3_close(struct device *dev);
-#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev);
-#endif
 
 
 
@@ -140,7 +148,7 @@
 {
 	short lrs_state = 0xff, i;
 	ushort ioaddr, irq, if_port;
-	short *phys_addr = (short *)dev->dev_addr;
+	short phys_addr[3];
 	static int current_tag = 0;
 
 	/* First check all slots of the EISA bus.  The next slot address to
@@ -195,6 +203,8 @@
 	outb(0x02, 0xA79);           /* Return to WaitForKey state. */
 	/* Select an open I/O location at 0x1*0 to do contention select. */
 	for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
+		if (check_region(id_port, 1))
+			continue;
 		outb(0x00, id_port);
 		outb(0xff, id_port);
 		if (inb(id_port) & 0x01)
@@ -210,13 +220,6 @@
 	   on cards as they are found.  Cards with their tag set will not
 	   respond to subsequent ID sequences. */
 
-	if (check_region(id_port,1)) {
-	  static int once = 1;
-	  if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",id_port);
-          once = 0;
-          return -ENODEV;
-        }
-
 	outb(0x00, id_port);
 	outb(0x00, id_port);
 	for(i = 0; i < 255; i++) {
@@ -247,13 +250,13 @@
 		if_port = iobase >> 14;
 		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
 	}
-	if (dev->irq > 1  &&  dev->irq < 16)
+	if (dev  &&  dev->irq > 1  &&  dev->irq < 16)
 		irq = dev->irq;
 	else
 		irq = id_read_eeprom(9) >> 12;
 
-	if (dev->base_addr != 0
-		&&      dev->base_addr != (unsigned short)ioaddr) {
+	if (dev  &&  dev->base_addr != 0
+		&&  dev->base_addr != (unsigned short)ioaddr) {
 		return -ENODEV;
 	}
 
@@ -270,9 +273,14 @@
 	/* Free the interrupt so that some other card can use it. */
 	outw(0x0f00, ioaddr + WN0_IRQ);
  found:
+	if (dev == NULL) {
+		dev = init_etherdev(dev, sizeof(struct el3_private));
+	}
+	memcpy(dev->dev_addr, phys_addr, sizeof(phys_addr));
 	dev->base_addr = ioaddr;
 	dev->irq = irq;
 	dev->if_port = if_port;
+
 	request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
 
 	{
@@ -287,11 +295,15 @@
 	printk(", IRQ %d.\n", dev->irq);
 
 	/* Make up a EL3-specific-data structure. */
-	dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
 	if (dev->priv == NULL)
 		return -ENOMEM;
 	memset(dev->priv, 0, sizeof(struct el3_private));
 
+	((struct el3_private *)dev->priv)->next_dev = el3_root_dev;
+	el3_root_dev = dev;
+
 	if (el3_debug > 0)
 		printk(version);
 
@@ -300,9 +312,7 @@
 	dev->hard_start_xmit = &el3_start_xmit;
 	dev->stop = &el3_close;
 	dev->get_stats = &el3_get_stats;
-#ifdef HAVE_MULTICAST
-		dev->set_multicast_list = &set_multicast_list;
-#endif
+	dev->set_multicast_list = &set_multicast_list;
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
@@ -354,7 +364,6 @@
 	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
 
 	if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
-		irq2dev_map[dev->irq] = NULL;
 		return -EAGAIN;
 	}
 
@@ -430,7 +439,7 @@
 	/* Transmitter timeout, serious problems. */
 	if (dev->tbusy) {
 		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 40*HZ/100)
+		if (tickssofar < TX_TIMEOUT)
 			return 1;
 		printk("%s: transmit timed out, Tx_status %2.2x status %4.4x "
 			   "Tx FIFO room %d.\n",
@@ -444,14 +453,6 @@
 		dev->tbusy = 0;
 	}
 
-	if (skb == NULL) {
-		dev_tint(dev);
-		return 0;
-	}
-
-	if (skb->len <= 0)
-		return 0;
-
 	if (el3_debug > 4) {
 		printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n",
 			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
@@ -483,7 +484,11 @@
 		outw(skb->len, ioaddr + TX_FIFO);
 		outw(0x00, ioaddr + TX_FIFO);
 		/* ... and the packet rounded to a doubleword. */
+#ifdef  __powerpc__
+		outsl_unswapped(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#else
 		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+#endif
 
 		dev->trans_start = jiffies;
 		if (inw(ioaddr + TX_FREE) > 1536) {
@@ -516,7 +521,7 @@
 {
 	struct device *dev = (struct device *)dev_id;
 	int ioaddr, status;
-	int i = 0;
+	int i = INTR_WORK;
 
 	if (dev == NULL) {
 		printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
@@ -568,7 +573,7 @@
 			}
 		}
 
-		if (++i > 10) {
+		if (--i < 0) {
 			printk("%s: Infinite loop in interrupt, status %4.4x.\n",
 				   dev->name, status);
 			/* Clear all interrupts. */
@@ -667,13 +672,18 @@
 					   pkt_len, rx_status);
 			if (skb != NULL) {
 				skb->dev = dev;
-				skb_reserve(skb,2);     /* Align IP on 16 byte */
+				skb_reserve(skb, 2);     /* Align IP on 16 byte */
 
 				/* 'skb->data' points to the start of sk_buff data area. */
-				insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
-							(pkt_len + 3) >> 2);
+#ifdef  __powerpc__
+				insl_unswapped(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
+							   (pkt_len + 3) >> 2);
+#else
+				insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
+					 (pkt_len + 3) >> 2);
+#endif
 
-				skb->protocol=eth_type_trans(skb,dev);
+				skb->protocol = eth_type_trans(skb,dev);
 				netif_rx(skb);
 				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
 				lp->stats.rx_packets++;
@@ -692,7 +702,6 @@
 	return 0;
 }
 
-#ifdef HAVE_MULTICAST
 /*
  *     Set or clear the multicast filter for this adaptor.
  */
@@ -717,7 +726,6 @@
 	else
                 outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
 }
-#endif
 
 static int
 el3_close(struct device *dev)
@@ -758,43 +766,50 @@
 }
 
 #ifdef MODULE
-static char devicename[9] = { 0, };
-static struct device dev_3c509 = {
-        devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-        0, 0, 0, 0,
-        0, 0,
-        0, 0, 0, NULL, el3_probe };
-
-static int io  = 0;
-static int irq = 0;
+/* Parameter that may be passed into the module. */
+static int debug = -1;
+static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+static int xcvr[] = {-1, -1, -1, -1, -1, -1, -1, -1};
 
 int
 init_module(void)
 {
-	dev_3c509.base_addr = io;
-        dev_3c509.irq       = irq;
-        if (!EISA_bus && !io) {
-		printk("3c509: WARNING! Module load-time probing works reliably only for EISA bus!!\n");
+	int el3_cards = 0;
+
+	if (debug >= 0)
+		el3_debug = debug;
+
+	el3_root_dev = NULL;
+	while (el3_probe(0) == 0) {
+		if (irq[el3_cards] > 1)
+			el3_root_dev->irq = irq[el3_cards];
+		if (xcvr[el3_cards] >= 0)
+			el3_root_dev->if_port = xcvr[el3_cards];
+		el3_cards++;
 	}
-	if (register_netdev(&dev_3c509) != 0)
-		return -EIO;
-	return 0;
+
+	return el3_cards ? 0 : -ENODEV;
 }
 
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_3c509);
-	kfree_s(dev_3c509.priv,sizeof(struct el3_private));
-	dev_3c509.priv=NULL;
-	/* If we don't do this, we can't re-insmod it later. */
-	release_region(dev_3c509.base_addr, EL3_IO_EXTENT);
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (el3_root_dev) {
+		next_dev = ((struct el3_private *)el3_root_dev->priv)->next_dev;
+		unregister_netdev(el3_root_dev);
+		release_region(el3_root_dev->base_addr, EL3_IO_EXTENT);
+		kfree(el3_root_dev);
+		el3_root_dev = next_dev;
+	}
 }
 #endif /* MODULE */
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c509.c"
  *  version-control: t
  *  kept-new-versions: 5
  *  tab-width: 4
diff -urN linux-2.0.31-pre4/drivers/net/atp.c linux-2.0.31-pre5/drivers/net/atp.c
--- linux-2.0.31-pre4/drivers/net/atp.c	1996-02-29 21:50:43.000000000 -0800
+++ linux-2.0.31-pre5/drivers/net/atp.c	2003-08-15 15:03:58.000000000 -0700
@@ -1,9 +1,9 @@
 /* atp.c: Attached (pocket) ethernet adapter driver for linux. */
 /*
-	This is a driver for a commonly OEMed pocket (parallel port)
-	ethernet adapter.  
+	This is a driver for commonly OEMed pocket (parallel port)
+	ethernet adapters based on the Realtek RTL8002 and RTL8012 chips.
 
-	Written 1993,1994,1995 by Donald Becker.
+	Written 1993-95,1997 by Donald Becker.
 
 	Copyright 1993 United States Government as represented by the
 	Director, National Security Agency.
@@ -19,7 +19,11 @@
 */
 
 static const char *version =
-	"atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+	"atp.c:v1.08 4/1/97 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+/* Operational parameters that may be safely changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((400*HZ)/1000)
 
 /*
 	This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
@@ -33,6 +37,10 @@
 	description is written based on guesses and writing lots of special-purpose
 	code to test my theorized operation.
 
+	In 1997 Realtek made available the documentation for the second generation
+	RTL8012 chip, which has lead to several driver improvements.
+	  http://www.realtek.com.tw/cn/cn.html
+
 					Theory of Operation
 	
 	The RTL8002 adapter seems to be built around a custom spin of the SEEQ
@@ -58,6 +66,10 @@
 	timing and control bits.  The data is then read from status port or written
 	to the data port.
 
+	Correction: the controller has two banks of 16 registers.  The second
+	bank contains only the multicast filter table (now used) and the EEPROM
+	access registers.
+
 	Since the bulk data transfer of the actual packets through the slow
 	parallel port dominates the driver's running time, four distinct data
 	(non-register) transfer modes are provided by the adapter, two in each
@@ -80,6 +92,14 @@
 	generate reasonable object code.  This header file also documents my
 	interpretations of the device registers.
 */
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -103,6 +123,49 @@
 
 #include "atp.h"
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#include <linux/version.h>		/* Evil, but neccessary */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
+#ifdef MODULE
+#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#undef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+#endif /* 1.3.38 */
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+
+#ifdef SA_SHIRQ
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n, instance)
+#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define FREE_IRQ(irqnum, dev) free_irq(irqnum)
+#define REQUEST_IRQ(i,h,f,n, instance) request_irq(i,h,f,n)
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+/* End of kernel compatibility defines. */
+
 /* use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
 #define NET_DEBUG 1
@@ -112,6 +175,9 @@
 /* The number of low I/O ports used by the ethercard. */
 #define ETHERCARD_TOTAL_SIZE	3
 
+/* Sequence to switch an 8012 from printer mux to ethernet mode. */
+static char mux_8012[] = { 0xff, 0xf7, 0xff, 0xfb, 0xf3, 0xfb, 0xff, 0xf7,};
+
 /* This code, written by wwc@super.org, resets the adapter every
    TIMED_CHECKER ticks.  This recovers from an unknown error which
    hangs the device. */
@@ -119,13 +185,11 @@
 #ifdef TIMED_CHECKER
 #include <linux/timer.h>
 static void atp_timed_checker(unsigned long ignored);
-static struct device *atp_timed_dev;
-static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker};
 #endif
 
 /* Index to functions, as function prototypes. */
 
-extern int atp_probe(struct device *dev);
+extern int atp_init(struct device *dev);
 
 static int atp_probe1(struct device *dev, short ioaddr);
 static void get_node_ID(struct device *dev);
@@ -135,14 +199,23 @@
 static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
 static void trigger_send(short ioaddr, int length);
 static int	net_send_packet(struct sk_buff *skb, struct device *dev);
-static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void net_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
 static void net_rx(struct device *dev);
 static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
 static int net_close(struct device *dev);
 static struct enet_statistics *net_get_stats(struct device *dev);
-static void set_multicast_list(struct device *dev);
+#ifdef NEW_MULTICAST
+static void set_rx_mode_8002(struct device *dev);
+static void set_rx_mode_8012(struct device *dev);
+#else
+static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs);
+static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs);
+#endif
 
 
+/* A list of all installed ATP devices, for removing the driver module. */
+static struct device *root_atp_dev = NULL;
+
 /* Check for a network adapter of this type, and return '0' iff one exists.
    If dev->base_addr == 0, probe all likely locations.
    If dev->base_addr == 1, always return failure.
@@ -153,7 +226,7 @@
 atp_init(struct device *dev)
 {
 	int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
-	int base_addr = dev->base_addr;
+	int base_addr = dev ? dev->base_addr : 0;
 
 	if (base_addr > 0x1ff)		/* Check a single specified location. */
 		return atp_probe1(dev, base_addr);
@@ -174,7 +247,8 @@
 
 static int atp_probe1(struct device *dev, short ioaddr)
 {
-	int saved_ctrl_reg, status;
+	struct net_local *lp;
+	int saved_ctrl_reg, status, i;
 
 	outb(0xff, ioaddr + PAR_DATA);
 	/* Save the original value of the Control register, in case we guessed
@@ -182,6 +256,9 @@
 	saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
 	/* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
 	outb(0x04, ioaddr + PAR_CONTROL);
+	/* Turn off the printer multiplexer on the 8012. */
+	for (i = 0; i < 8; i++)
+		outb(mux_8012[i], ioaddr + PAR_DATA);
 	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
 	eeprom_delay(2048);
 	status = read_nibble(ioaddr, CMR1);
@@ -196,6 +273,9 @@
 		outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
 		return 1;
 	}
+
+	dev = init_etherdev(dev, sizeof(struct net_local));
+
 	/* Find the IRQ used by triggering an interrupt. */
 	write_reg_byte(ioaddr, CMR2, 0x01);			/* No accept mode, IRQ out. */
 	write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);	/* Enable Tx and Rx. */
@@ -218,27 +298,29 @@
 		   dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
 		   dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
 
-	/* Leave the hardware in a reset state. */
-    write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+	/* Reset the ethernet hardware and activate the printer pass-through. */
+    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
 
 	if (net_debug)
 		printk(version);
 
 	/* Initialize the device structure. */
 	ether_setup(dev);
-	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
 	if (dev->priv == NULL)
 		return -ENOMEM;
 	memset(dev->priv, 0, sizeof(struct net_local));
 
+	lp = (struct net_local *)dev->priv;
+	lp->chip_type = RTL8002;
+	lp->addr_mode = CMR2h_Normal;
 
-	{
-		struct net_local *lp = (struct net_local *)dev->priv;
-		lp->addr_mode = CMR2h_Normal;
-	}
+	lp->next_module = root_atp_dev;
+	root_atp_dev = dev;
 
 	/* For the ATP adapter the "if_port" is really the data transfer mode. */
-	dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
+	dev->if_port = (dev->mem_start & 0xf) ? (dev->mem_start & 0x7) : 4;
 	if (dev->mem_end & 0xf)
 		net_debug = dev->mem_end & 7;
 
@@ -246,14 +328,9 @@
 	dev->stop		= net_close;
 	dev->hard_start_xmit = net_send_packet;
 	dev->get_stats	= net_get_stats;
-	dev->set_multicast_list = &set_multicast_list;
+	dev->set_multicast_list =
+	  lp->chip_type == RTL8002 ? &set_rx_mode_8002 : &set_rx_mode_8012;
 
-#ifdef TIMED_CHECKER
-	del_timer(&atp_timer);
-	atp_timer.expires = jiffies + TIMED_CHECKER;
-	atp_timed_dev = dev;
-	add_timer(&atp_timer);
-#endif
 	return 0;
 }
 
@@ -322,18 +399,32 @@
    */
 static int net_open(struct device *dev)
 {
+	struct net_local *lp = (struct net_local *)dev->priv;
 
 	/* The interrupt line is turned off (tri-stated) when the device isn't in
 	   use.  That's especially important for "attached" interfaces where the
 	   port or interrupt may be shared. */
+#ifndef SA_SHIRQ
 	if (irq2dev_map[dev->irq] != 0
 		|| (irq2dev_map[dev->irq] = dev) == 0
-		|| request_irq(dev->irq, &net_interrupt, 0, "ATP", NULL)) {
+		|| REQUEST_IRQ(dev->irq, &net_interrupt, 0, "ATP", dev)) {
 		return -EAGAIN;
 	}
+#else
+	if (request_irq(dev->irq, &net_interrupt, 0, "ATP Ethernet", dev))
+		return -EAGAIN;
+#endif
 
+	MOD_INC_USE_COUNT;
 	hardware_init(dev);
 	dev->start = 1;
+
+	init_timer(&lp->timer);
+	lp->timer.expires = RUN_AT(TIMED_CHECKER);
+	lp->timer.data = (unsigned long)dev;
+	lp->timer.function = &atp_timed_checker;    /* timer handler */
+	add_timer(&lp->timer);
+
 	return 0;
 }
 
@@ -345,6 +436,9 @@
 	int ioaddr = dev->base_addr;
     int i;
 
+	/* Turn off the printer multiplexer on the 8012. */
+	for (i = 0; i < 8; i++)
+		outb(mux_8012[i], ioaddr + PAR_DATA);
 	write_reg_high(ioaddr, CMR1, CMR1h_RESET);
 	
     for (i = 0; i < 6; i++)
@@ -418,11 +512,18 @@
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
 
-	if (dev->tbusy) {
-		/* If we get here, some higher level has decided we are broken.
-		   There should really be a "kick me" function call instead. */
-		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 5)
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
+		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		dev_tint(dev);
+		return 0;
+	}
+#endif
+
+	/* Use transmit-while-tbusy as a crude error timer. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start < TX_TIMEOUT)
 			return 1;
 		printk("%s: transmit timed out, %s?\n", dev->name,
 			   inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
@@ -432,21 +533,8 @@
 		hardware_init(dev);
 		dev->tbusy=0;
 		dev->trans_start = jiffies;
-	}
-
-	/* If some higher layer thinks we've missed an tx-done interrupt
-	   we are passed NULL. Caution: dev_tint() handles the cli()/sti()
-	   itself. */
-	if (skb == NULL) {
-		dev_tint(dev);
-		return 0;
-	}
-
-	/* Block a timer-based transmit from overlapping.  This could better be
-	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
-	if (set_bit(0, (void*)&dev->tbusy) != 0)
-		printk("%s: Transmitter access conflict.\n", dev->name);
-	else {
+		return 1;
+	} else {
 		short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
 		unsigned char *buf = skb->data;
 		int flags;
@@ -484,9 +572,13 @@
 /* The typical workload of the driver:
    Handle the network interface interrupts. */
 static void
-net_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+net_interrupt IRQ(int irq, void *dev_instance, struct pt_regs * regs)
 {
+#ifdef SA_SHIRQ
+	struct device *dev = (struct device *)dev_instance;
+#else
 	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
 	struct net_local *lp;
 	int ioaddr, status, boguscount = 20;
 	static int num_tx_since_rx = 0;
@@ -585,11 +677,6 @@
 		int i;
 		for (i = 0; i < 6; i++)
 			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
-#ifdef TIMED_CHECKER
-		del_timer(&atp_timer);
-		atp_timer.expires = jiffies + TIMED_CHECKER;
-		add_timer(&atp_timer);
-#endif
 	}
 
 	/* Tell the adapter that it can go back to using the output line as IRQ. */
@@ -610,17 +697,23 @@
 #ifdef TIMED_CHECKER
 /* This following code fixes a rare (and very difficult to track down)
    problem where the adapter forgets its ethernet address. */
-static void atp_timed_checker(unsigned long ignored)
+static void atp_timed_checker(unsigned long data)
 {
-  int i;
-  int ioaddr = atp_timed_dev->base_addr;
+	struct device *dev = (struct device *)data;
+	int ioaddr = dev->base_addr;
+	struct net_local *lp = (struct net_local *)dev->priv;
+	int tickssofar = jiffies - lp->last_rx_time;
+	int i;
 
-  if (!atp_timed_dev->interrupt)
-	{
-	  for (i = 0; i < 6; i++)
-#if 0
-		if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
-		  {
+	if (tickssofar > 2*HZ  &&  dev->interrupt == 0) {
+#if 1
+		for (i = 0; i < 6; i++)
+			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+		lp->last_rx_time = jiffies;
+#else
+		for (i = 0; i < 6; i++)
+			if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+				{
 			struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
 			write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
 			if (i == 2)
@@ -632,13 +725,10 @@
 			else
 			  lp->stats.rx_errors++;
 		  }
-#else
-	  write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
 #endif
 	}
-  del_timer(&atp_timer);
-  atp_timer.expires = jiffies + TIMED_CHECKER;
-  add_timer(&atp_timer);
+	lp->timer.expires = RUN_AT(TIMED_CHECKER);
+	add_timer(&lp->timer);
 }
 #endif
 
@@ -647,11 +737,7 @@
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-#ifdef notdef
-	ushort header[4];
-#else
 	struct rx_header rx_head;
-#endif
 
 	/* Process the received packet. */
 	outb(EOC+MAR, ioaddr + PAR_DATA);
@@ -661,18 +747,23 @@
 			   rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
 	if ((rx_head.rx_status & 0x77) != 0x01) {
 		lp->stats.rx_errors++;
-		/* Ackkk!  I don't have any documentation on what the error bits mean!
-		   The best I can do is slap the device around a bit. */
+		if (rx_head.rx_status & 0x0004) lp->stats.rx_frame_errors++;
+		else if (rx_head.rx_status & 0x0002) lp->stats.rx_crc_errors++;
 		if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
 								  dev->name, rx_head.rx_status);
-		hardware_init(dev);
+		if  (rx_head.rx_status & 0x0020) {
+			lp->stats.rx_fifo_errors++;
+			write_reg_high(ioaddr, CMR1, CMR1h_TxENABLE);
+			write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+		} else if (rx_head.rx_status & 0x0050)
+			hardware_init(dev);
 		return;
 	} else {
-		/* Malloc up new buffer. */
-		int pkt_len = (rx_head.rx_count & 0x7ff) - 4; 		/* The "-4" is omits the FCS (CRC). */
+		/* Malloc up new buffer. The "-4" is omits the FCS (CRC). */
+		int pkt_len = (rx_head.rx_count & 0x7ff) - 4;
 		struct sk_buff *skb;
 		
-		skb = dev_alloc_skb(pkt_len);
+		skb = DEV_ALLOC_SKB(pkt_len + 2);
 		if (skb == NULL) {
 			printk("%s: Memory squeeze, dropping packet.\n", dev->name);
 			lp->stats.rx_dropped++;
@@ -680,23 +771,20 @@
 		}
 		skb->dev = dev;
 		
+#if LINUX_VERSION_CODE >= 0x10300
+		skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
 		read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
-
-		if (net_debug > 6) {
-			unsigned char *data = skb->data;
-			printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
-				   "%02x%02x%02x %02x%02x..",
-				   data[0], data[1], data[2], data[3], data[4], data[5],
-				   data[6], data[7], data[8], data[9], data[10], data[11],
-				   data[12], data[13]);
-		}
-		
-		skb->protocol=eth_type_trans(skb,dev);
+		skb->protocol = eth_type_trans(skb, dev);
+#else
+		read_block(ioaddr, pkt_len, skb->data, dev->if_port);
+		skb->len = pkt_len;
+#endif
 		netif_rx(skb);
 		lp->stats.rx_packets++;
 	}
  done:
 	write_reg(ioaddr, CMR1, CMR1_NextPkt);
+	lp->last_rx_time = jiffies;
 	return;
 }
 
@@ -730,17 +818,23 @@
 	dev->tbusy = 1;
 	dev->start = 0;
 
+	del_timer(&lp->timer);
+
 	/* Flush the Tx and disable Rx here. */
 	lp->addr_mode = CMR2h_OFF;
 	write_reg_high(ioaddr, CMR2, CMR2h_OFF);
 
 	/* Free the IRQ line. */
 	outb(0x00, ioaddr + PAR_CONTROL);
-	free_irq(dev->irq, NULL);
+	FREE_IRQ(dev->irq, dev);
+#ifndef SA_SHIRQ
 	irq2dev_map[dev->irq] = 0;
+#endif
+
+	/* Reset the ethernet hardware and activate the printer pass-through. */
+    write_reg_high(ioaddr, CMR1, CMR1h_RESET | CMR1h_MUX);
 
-	/* Leave the hardware in a reset state. */
-    write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+	MOD_DEC_USE_COUNT;
 
 	return 0;
 }
@@ -757,29 +851,125 @@
 /*
  *	Set or clear the multicast filter for this adapter.
  */
- 
-static void set_multicast_list(struct device *dev)
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+    unsigned int crc = 0xffffffff;	/* Initial value. */
+    while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 8; --bit >= 0; current_octet >>= 1) {
+			if ((crc ^ current_octet) & 1) {
+				crc >>= 1;
+				crc ^= ethernet_polynomial_le;
+			} else
+				crc >>= 1;
+		}
+    }
+    return crc;
+}
+
+static void
+#ifdef NEW_MULTICAST
+set_rx_mode_8002(struct device *dev)
+#else
+static void set_rx_mode_8002(struct device *dev, int num_addrs, void *addrs);
+#endif
 {
 	struct net_local *lp = (struct net_local *)dev->priv;
 	short ioaddr = dev->base_addr;
-	int num_addrs=dev->mc_list;
-	
-	if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
-		num_addrs=1;
-	/*
-	 *	We must make the kernel realise we had to move
-	 *	into promisc mode or we start all out war on
-	 *	the cable. - AC
-	 */
-	if(num_addrs)
-		dev->flags|=IFF_PROMISC;		
-	lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+
+	if ( dev->mc_count > 0 || (dev->flags & (IFF_ALLMULTI|IFF_PROMISC))) {
+		/* We must make the kernel realise we had to move
+		 *	into promisc mode or we start all out war on
+		 *	the cable. - AC
+		 */
+		dev->flags|=IFF_PROMISC;
+		lp->addr_mode = CMR2h_PROMISC;
+	} else
+		lp->addr_mode = CMR2h_Normal;
 	write_reg_high(ioaddr, CMR2, lp->addr_mode);
 }
+
+static void
+#ifdef NEW_MULTICAST
+set_rx_mode_8012(struct device *dev)
+#else
+static void set_rx_mode_8012(struct device *dev, int num_addrs, void *addrs);
+#endif
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+	short ioaddr = dev->base_addr;
+	unsigned char new_mode, mc_filter[8]; /* Multicast hash filter */
+	int i;
+
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		new_mode = CMR2h_PROMISC;
+	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
+		/* Too many to filter perfectly -- accept all multicasts. */
+		memset(mc_filter, 0xff, sizeof(mc_filter));
+		new_mode = CMR2h_Normal;
+	} else {
+		struct dev_mc_list *mclist;
+
+		memset(mc_filter, 0, sizeof(mc_filter));
+		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+			 i++, mclist = mclist->next)
+			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+					mc_filter);
+		new_mode = CMR2h_Normal;
+	}
+	lp->addr_mode = new_mode;
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT | 0x04); /* Switch to page 1. */
+    for (i = 0; i < 8; i++)
+		write_reg_byte(ioaddr, i, mc_filter[i]);
+	if (net_debug > 2 || 1) {
+		lp->addr_mode = 1;
+		printk("%s: Mode %d, setting multicast filter to",
+			   dev->name, lp->addr_mode);
+		for (i = 0; i < 8; i++)
+			printk(" %2.2x", mc_filter[i]);
+		printk(".\n");
+	}
+
+	write_reg_high(ioaddr, CMR2, lp->addr_mode);
+    write_reg(ioaddr, CMR2, CMR2_IRQOUT); /* Switch back to page 0 */
+}
+
+#ifdef MODULE
+static int debug = 1;
+int
+init_module(void)
+{
+	net_debug = debug;
+	root_atp_dev = NULL;
+	atp_init(0);
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	/* No need to release_region(), since we never snarf it. */
+	while (root_atp_dev) {
+		next_dev = ((struct net_local *)root_atp_dev->priv)->next_module;
+		unregister_netdev(root_atp_dev);
+		kfree(root_atp_dev);
+		root_atp_dev = next_dev;
+	}
+}
+#endif /* MODULE */
+
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c atp.c"
  *  version-control: t
  *  kept-new-versions: 5
  *  tab-width: 4
diff -urN linux-2.0.31-pre4/drivers/net/atp.h linux-2.0.31-pre5/drivers/net/atp.h
--- linux-2.0.31-pre4/drivers/net/atp.h	1995-10-10 06:03:24.000000000 -0700
+++ linux-2.0.31-pre5/drivers/net/atp.h	2003-08-15 15:03:58.000000000 -0700
@@ -1,3 +1,6 @@
+/* Linux header file for the ATP pocket ethernet adapter. */
+/* v1.04 4/1/97 becker@cesdis.gsfc.nasa.gov. */
+
 #include <linux/if_ether.h>
 #include <linux/types.h>
 #include <asm/io.h>
@@ -6,16 +9,19 @@
 #ifdef __KERNEL__
     struct enet_statistics stats;
 #endif
+    struct device *next_module;
+    struct timer_list timer;	/* Media selection timer. */
+    long last_rx_time;		/* Last Rx, in jiffies, to handle Rx hang. */
     ushort saved_tx_size;
-    unsigned char
-	re_tx,			/* Number of packet retransmissions. */
-	tx_unit_busy,
-	addr_mode,		/* Current Rx filter e.g. promiscuous, etc. */
-	pac_cnt_in_tx_buf;
+    unsigned tx_unit_busy:1;
+    unsigned char re_tx,	/* Number of packet retransmissions. */
+      addr_mode,		/* Current Rx filter e.g. promiscuous, etc. */
+      pac_cnt_in_tx_buf,
+      chip_type;
 };
 
 struct rx_header {
-    ushort pad;			/* The first read is always corrupted. */
+    ushort pad;			/* Pad. */
     ushort rx_count;
     ushort rx_status;		/* Unknown bit assignments :-<.  */
     ushort cur_addr;		/* Apparently the current buffer address(?) */
@@ -25,6 +31,8 @@
 #define PAR_STATUS	1
 #define PAR_CONTROL 2
 
+enum chip_type { RTL8002, RTL8012 };
+
 #define Ctrl_LNibRead	0x08	/* LP_PSELECP */
 #define Ctrl_HNibRead	0
 #define Ctrl_LNibWrite	0x08	/* LP_PSELECP */
@@ -47,7 +55,8 @@
     ISR = 10, IMR = 11,			/* Interrupt status and mask. */
     CMR1 = 12,				/* Command register 1. */
     CMR2 = 13,				/* Command register 2. */
-    MAR = 14,				/* Memory address register. */
+    MODSEL = 14,			/* Mode select register. */
+    MAR = 14,				/* Memory address register (?). */
     CMR2_h = 0x1d, };
 
 enum eepage_regs
@@ -59,6 +68,7 @@
 #define ISR_TxErr	0x02
 #define ISRh_RxErr	0x11	/* ISR, high nibble */
 
+#define CMR1h_MUX	0x08	/* Select printer multiplexor on 8012. */
 #define CMR1h_RESET	0x04	/* Reset. */
 #define CMR1h_RxENABLE	0x02	/* Rx unit enable.  */
 #define CMR1h_TxENABLE	0x01	/* Tx unit enable.  */
diff -urN linux-2.0.31-pre4/drivers/net/de4x5.c linux-2.0.31-pre5/drivers/net/de4x5.c
--- linux-2.0.31-pre4/drivers/net/de4x5.c	1996-11-09 09:31:38.000000000 -0800
+++ linux-2.0.31-pre5/drivers/net/de4x5.c	2003-08-15 15:03:58.000000000 -0700
@@ -1,12 +1,35 @@
-/*  de4x5.c: A DIGITAL DE425/DE434/DE435/DE450/DE500 ethernet driver for Linux.
+/*  de4x5.c: A DIGITAL DC21x4x DECchip and DE425/DE434/DE435/DE450/DE500
+             ethernet driver for Linux.
 
     Copyright 1994, 1995 Digital Equipment Corporation.
 
-    This software may be used and distributed according to the terms of
-    the GNU Public License, incorporated herein by reference.
+    Testing resources for this driver have been made available
+    in part by NASA Ames Research Center (mjacob@nas.nasa.gov).
 
-    This driver is written for the Digital Equipment Corporation series
-    of EtherWORKS ethernet cards:
+    The author may be reached at davies@maniac.ultranet.com.
+
+    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.
+
+    THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR   IMPLIED
+    WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+    NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT,  INDIRECT,
+    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+    NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+    USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+    ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+    You should have received a copy of the  GNU General Public License along
+    with this program; if not, write  to the Free Software Foundation, Inc.,
+    675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Originally,   this  driver  was    written  for the  Digital   Equipment
+    Corporation series of EtherWORKS ethernet cards:
 
         DE425 TP/COAX EISA
 	DE434 TP PCI
@@ -14,6 +37,25 @@
 	DE450 TP/COAX/AUI PCI
 	DE500 10/100 PCI Fasternet
 
+    but it  will  now attempt  to  support all  cards which   conform to the
+    Digital Semiconductor   SROM   Specification.    The  driver   currently
+    recognises the following chips:
+
+        DC21040     (no SROM)
+	DC21041[A]
+	DC21140[A]
+
+    I plan to  add DC2114[23]  support ASAP,  time permitting.   So far  the
+    driver is known to work with the following cards:
+
+        KINGSTON
+	Linksys
+	ZNYX342
+	SMC8432
+	SMC9332 (w/new SROM)
+	ZNYX31[45]
+	ZNYX346 10/100 4 port (can act as a 10/100 bridge!) 
+
     The driver has been tested on a relatively busy network using the DE425,
     DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
     16M of data to a DECstation 5000/200 as follows:
@@ -29,12 +71,12 @@
     measurement. Their error is +/-20k on a quiet (private) network and also
     depend on what load the CPU has.
 
-    The author may be reached at davies@maniac.ultranet.com.
-
     =========================================================================
-    This driver has been written  substantially  from scratch, although  its
+    This driver  has been written substantially  from  scratch, although its
     inheritance of style and stack interface from 'ewrk3.c' and in turn from
-    Donald Becker's 'lance.c' should be obvious.
+    Donald Becker's 'lance.c' should be obvious. With the module autoload of
+    every  usable DECchip board,  I  pinched Donald's 'next_module' field to
+    link my modules together.
 
     Upto 15 EISA cards can be supported under this driver, limited primarily
     by the available IRQ lines.  I have  checked different configurations of
@@ -46,43 +88,37 @@
     to the differences in the EISA and PCI CSR address offsets from the base
     address.
 
-    The ability to load  this driver as a  loadable module has been included
-    and used  extensively during the  driver development (to save those long
+    The ability to load this  driver as a loadable  module has been included
+    and used extensively  during the driver development  (to save those long
     reboot sequences).  Loadable module support  under PCI and EISA has been
     achieved by letting the driver autoprobe as if it were compiled into the
-    kernel, except that there is no autoprobing of the IRQ lines. This is of
-    no great  consequence except do make sure  you're not sharing interrupts
-    with  anything that cannot  accommodate  interrupt sharing! The existing
-    register_netdevice() code will only allow one device to be registered at
-    a time. 
-
-    ************************************************************************
-    For now, please only use the 'io=??' assignment (see  2. below, ?? != 0)
-    when loading a module.
-    ************************************************************************
-
-    Essentially, the I/O address and IRQ information  are ignored and filled
-    in later by  the PCI BIOS   during the PCI  probe.  Note  that the board
-    should be in the system at boot time so that its I/O address and IRQ are
-    allocated by the PCI BIOS automatically. 
+    kernel. Do make sure  you're not sharing  interrupts with anything  that
+    cannot accommodate  interrupt  sharing!
 
     To utilise this ability, you have to do 8 things:
 
     0) have a copy of the loadable modules code installed on your system.
     1) copy de4x5.c from the  /linux/drivers/net directory to your favourite
     temporary directory.
-    2) edit the source code near line 3779 to reflect the I/O address you're
-    using (only  if you want to manually  load the module),  or assign these
-    when loading by:
+    2) for fixed  autoprobes (not  recommended),  edit the source code  near
+    line 5005 to reflect the I/O address  you're using, or assign these when
+    loading by:
 
-                   insmod de4x5.o io=0xghh         where g = bus number
+                   insmod de4x5 io=0xghh           where g = bus number
 		                                        hh = device number   
 
+       NB: autoprobing for modules is now supported by default. You may just
+           use:
+
+                   insmod de4x5
+
+           to load all available boards. For a specific board, still use
+	   the 'io=?' above.
     3) compile  de4x5.c, but include -DMODULE in  the command line to ensure
     that the correct bits are compiled (see end of source code).
     4) if you are wanting to add a new  card, goto 5. Otherwise, recompile a
     kernel with the de4x5 configuration turned off and reboot.
-    5) insmod de4x5.o [io=0xghh]
+    5) insmod de4x5 [io=0xghh]
     6) run the net startup bits for your new eth?? interface(s) manually 
     (usually /etc/rc.inet[12] at boot time). 
     7) enjoy!
@@ -98,13 +134,7 @@
     By  default,  the driver will  now   autodetect any  DECchip based card.
     Should you have a need to restrict the driver to DIGITAL only cards, you
     can compile with a  DEC_ONLY define, or if  loading as a module, use the
-    'dec_only=1'  parameter. However, this  "feature" is in no way supported
-    nor  tested in this  driver  and the user  may use  it  at his/her  sole
-    discretion.   I have had  2 conflicting reports that  my  driver will or
-    won't work  with Znyx.   Try  Donald Becker's 'tulip.c' if   this driver
-    doesn't work for you. I will not be supporting Znyx  and SMC cards since
-    I have  no information on  them and  can't test  them in  a system (this
-    applies most particularly to the DC21140 based cards).
+    'dec_only=1'  parameter. 
 
     I've changed the timing routines to  use the kernel timer and scheduling
     functions  so that the  hangs  and other assorted problems that occurred
@@ -125,6 +155,30 @@
     aligned  DMA transfers  and  the  Alphas get   alignment traps with  non
     longword aligned data copies (which makes them really slow). No comment.
 
+    I  have added SROM decoding  routines to make this  driver work with any
+    card that  supports the Digital  Semiconductor SROM spec. This will help
+    all  cards running the dc2114x  series chips in particular.  Cards using
+    the dc2104x  chips should run correctly with  the basic  driver.  I'm in
+    debt to <mjacob@feral.com> for the  testing and feedback that helped get
+    this feature working.  So far we have  tested KINGSTON, SMC8432, SMC9332
+    (with the latest SROM complying  with the SROM spec  V3: their first was
+    broken), ZNYX342  and  LinkSys. ZYNX314 (dual  21041  MAC) and  ZNYX 315
+    (quad 21041 MAC)  cards also  appear  to work despite their  incorrectly
+    wired IRQs.
+
+    I have added a temporary fix for interrupt problems when some SCSI cards
+    share the same interrupt as the DECchip based  cards. The problem occurs
+    because  the SCSI card wants to  grab the interrupt  as a fast interrupt
+    (runs the   service routine with interrupts turned   off) vs.  this card
+    which really needs to run the service routine with interrupts turned on.
+    This driver will  now   add the interrupt service   routine  as  a  fast
+    interrupt if it   is bounced from the   slow interrupt.  THIS IS NOT   A
+    RECOMMENDED WAY TO RUN THE DRIVER  and has been done  for a limited time
+    until  people   sort  out their  compatibility    issues and the  kernel
+    interrupt  service code  is  fixed.   YOU  SHOULD SEPARATE OUT  THE FAST
+    INTERRUPT CARDS FROM THE SLOW INTERRUPT CARDS to ensure that they do not
+    run on the same interrupt. PCMCIA/CardBus is another can of worms...
+
     TO DO:
     ------
 
@@ -208,16 +262,49 @@
                           with <csd@microplex.com>
       0.44   13-Aug-96    Fix RX overflow bug in 2114[023] chips.
                           Fix EISA probe bugs reported by <os2@kpi.kharkov.ua>
-			  and <michael@compurex.com>
+			  and <michael@compurex.com>.
       0.441   9-Sep-96    Change dc21041_autoconf() to probe quiet BNC media
                            with a loopback packet.
       0.442   9-Sep-96    Include AUI in dc21041 media printout. Bug reported
                            by <bhat@mundook.cs.mu.OZ.AU>
+      0.45    8-Dec-96    Include endian functions for PPC use, from work 
+                           by <cort@cs.nmt.edu> and <g.thomas@opengroup.org>.
+      0.451  28-Dec-96    Added fix to allow autoprobe for modules after
+                           suggestion from <mjacob@feral.com>.
+      0.5    30-Jan-97    Added SROM decoding functions.
+                          Updated debug flags.
+			  Fix sleep/wakeup calls for PCI cards, bug reported
+			   by <cross@gweep.lkg.dec.com>.
+			  Added multi-MAC, one SROM feature from discussion
+			   with <mjacob@feral.com>.
+			  Added full module autoprobe capability.
+			  Added attempt to use an SMC9332 with broken SROM.
+			  Added fix for ZYNX multi-mac cards that didn't
+			   get their IRQs wired correctly.
+      0.51   13-Feb-97    Added endian fixes for the SROM accesses from
+			   <paubert@iram.es>
+			  Fix init_connection() to remove extra device reset.
+			  Fix MAC/PHY reset ordering in dc21140m_autoconf().
+			  Fix initialisation problem with lp->timeout in
+			   typeX_infoblock() from <paubert@iram.es>.
+			  Fix MII PHY reset problem from work done by
+			   <paubert@iram.es>.
+      0.52   26-Apr-97    Some changes may not credit the right people -
+                           a disk crash meant I lost some mail.
+			  Change RX interrupt routine to drop rather than 
+			   defer packets to avoid hang reported by 
+			   <g.thomas@opengroup.org>.
+			  Fix srom_exec() to return for COMPACT and type 1
+			   infoblocks.
+			  Added DC21142 and DC21143 functions.
+			  Added byte counters from <phil@tazenda.demon.co.uk>
+			  Added SA_INTERRUPT temporary fix from 
+			   <mjacob@feral.com>.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.52 1997/4/26 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -235,7 +322,8 @@
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
-#include <asm/segment.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -250,14 +338,37 @@
 
 #define c_char const char
 
+#include <linux/version.h>
+#if	LINUX_VERSION_CODE < ((2 << 16) | (1 << 8))
+#define        net_device_stats      enet_statistics
+#define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
+#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
+#define le16_to_cpu(a) cpu_to_le16(a) 
+#define le32_to_cpu(a) cpu_to_le32(a) 
+#ifdef __powerpc__
+#define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
+#define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
+		        (((a) & 0x0000ff00U) <<  8) |\
+		        (((a) & 0x00ff0000U) >>  8) |\
+		        (((a) & 0xff000000U) >> 24))
+#else
+#define cpu_to_le16(a) (a)
+#define cpu_to_le32(a) (a)
+#endif  /* __powerpc__ */
+#include	<asm/segment.h>
+#else
+#include	<asm/uaccess.h>
+#endif  /* LINUX_VERSION_CODE */
+#define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
+
 /*
 ** MII Information
 */
 struct phy_table {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
+    int reset;              /* Hard reset required?                         */
+    int id;                 /* IEEE OUI                                     */
     int ta;                 /* One cycle TA time - 802.3u is confusing here */
-    struct {                /* Non autonegotiation (parallel) speed det. */
+    struct {                /* Non autonegotiation (parallel) speed det.    */
 	int reg;
 	int mask;
 	int value;
@@ -265,19 +376,35 @@
 };
 
 struct mii_phy {
-    int reset;              /* Hard reset required? */
-    int id;                 /* IEEE OUI */
-    int ta;                 /* One cycle TA time */
+    int reset;              /* Hard reset required?                      */
+    int id;                 /* IEEE OUI                                  */
+    int ta;                 /* One cycle TA time                         */
     struct {                /* Non autonegotiation (parallel) speed det. */
 	int reg;
 	int mask;
 	int value;
     } spd;
-    int addr;               /* MII address for the PHY */
+    int addr;               /* MII address for the PHY                   */
+    u_char  *gep;           /* Start of GEP sequence block in SROM       */
+    u_char  *rst;           /* Start of reset sequence in SROM           */
+    u_int mc;               /* Media Capabilities                        */
+    u_int ana;              /* NWay Advertisement                        */
+    u_int fdx;              /* Full DupleX capabilites for each media    */
+    u_int ttm;              /* Transmit Threshold Mode for each media    */
 };
 
 #define DE4X5_MAX_PHY 8     /* Allow upto 8 attached PHY devices per board */
 
+struct sia_phy {
+    u_char mc;              /* Media Code                                */
+    u_char ext;             /* csr13-15 valid when set                   */
+    int csr13;              /* SIA Connectivity Register                 */
+    int csr14;              /* SIA TX/RX Register                        */
+    int csr15;              /* SIA General Register                      */
+    int gepc;               /* SIA GEP Control Information               */
+    int gep;                /* SIA GEP Data                              */
+};
+
 /*
 ** Define the know universe of PHY devices that can be
 ** recognised by this driver
@@ -285,8 +412,8 @@
 static struct phy_table phy_info[] = {
     {0, NATIONAL_TX, 1, {0x19, 0x40, 0x00}},   /* National TX */
     {1, BROADCOM_T4, 1, {0x10, 0x02, 0x02}},   /* Broadcom T4 */
-    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4 */
-    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4 */
+    {0, SEEQ_T4    , 1, {0x12, 0x10, 0x10}},   /* SEEQ T4     */
+    {0, CYPRESS_T4 , 1, {0x05, 0x20, 0x20}}    /* Cypress T4  */
 };
 
 /*
@@ -300,11 +427,23 @@
 #define SMC    1
 #define ACCTON 2
 
+/*
+** SROM Repair definitions. If a broken SROM is detected a card may
+** use this information to help figure out what to do. This is a
+** "stab in the dark" and so far for SMC9332's only.
+*/
+static c_char srom_repair_info[][100] = {
+    {0x00,0x1e,0x00,0x00,0x00,0x08,             /* SMC9332 */
+     0x1f,0x01,0x8f,0x01,0x00,0x01,0x00,0x02,
+     0x01,0x00,0x00,0x78,0xe0,0x01,0x00,0x50,
+     0x00,0x18,}
+};
+
 
 #ifdef DE4X5_DEBUG
 static int de4x5_debug = DE4X5_DEBUG;
 #else
-static int de4x5_debug = 1;
+static int de4x5_debug = (0);
 #endif
 
 #ifdef DE4X5_AUTOSENSE              /* Should be done on a per adapter basis */
@@ -449,7 +588,7 @@
     char id_block_crc;
     char reserved2;
     char version;
-    char num_adapters;
+    char num_controllers;
     char ieee_addr[6];
     char info[100];
     short chksum;
@@ -494,7 +633,7 @@
     int tx_new, tx_old;                     /* TX descriptor ring pointers  */
     char setup_frame[SETUP_FRAME_LEN];      /* Holds MCA and PA info.       */
     char frame[64];                         /* Min sized packet for loopback*/
-    struct enet_statistics stats;           /* Public stats                 */
+    struct net_device_stats stats;           /* Public stats                 */
     struct {
 	u_int bins[DE4X5_PKT_STAT_SZ];      /* Private stats counters       */
 	u_int unicast;
@@ -512,19 +651,21 @@
     char txRingSize;
     int  bus;                               /* EISA or PCI                  */
     int  bus_num;                           /* PCI Bus number               */
+    int  device;                            /* Device number on PCI bus     */
     int  state;                             /* Adapter OPENED or CLOSED     */
     int  chipset;                           /* DC21040, DC21041 or DC21140  */
     s32  irq_mask;                          /* Interrupt Mask (Enable) bits */
     s32  irq_en;                            /* Summary interrupt bits       */
     int  media;                             /* Media (eg TP), mode (eg 100B)*/
     int  c_media;                           /* Remember the last media conn */
+    int  fdx;                               /* media full duplex flag       */
     int  linkOK;                            /* Link is OK                   */
     int  autosense;                         /* Allow/disallow autosensing   */
     int  tx_enable;                         /* Enable descriptor polling    */
-    int  lostMedia;                         /* Possibly lost media          */
     int  setup_f;                           /* Setup frame filtering type   */
     int  local_state;                       /* State within a 'media' state */
     struct mii_phy phy[DE4X5_MAX_PHY];      /* List of attached PHY devices */
+    struct sia_phy sia;                     /* SIA PHY Information          */
     int  active;                            /* Index to active PHY device   */
     int  mii_cnt;                           /* Number of attached PHY's     */
     int  timeout;                           /* Scheduling counter           */
@@ -537,13 +678,31 @@
 	s32 csr0;                           /* Saved Bus Mode Register      */
 	s32 csr6;                           /* Saved Operating Mode Reg.    */
 	s32 csr7;                           /* Saved IRQ Mask Register      */
+	s32 gep;                            /* Saved General Purpose Reg.   */
+	s32 gepc;                           /* Control info for GEP         */
 	s32 csr13;                          /* Saved SIA Connectivity Reg.  */
 	s32 csr14;                          /* Saved SIA TX/RX Register     */
 	s32 csr15;                          /* Saved SIA General Register   */
 	int save_cnt;                       /* Flag if state already saved  */
 	struct sk_buff *skb;                /* Save the (re-ordered) skb's  */
     } cache;
+    struct de4x5_srom srom;                 /* A copy of the SROM           */
+    struct device *next_module;             /* Link to the next module      */
     int rx_ovf;                             /* Check for 'RX overflow' tag  */
+    int useSROM;                            /* For non-DEC card use SROM    */
+    int useMII;                             /* Infoblock using the MII      */
+    int asBitValid;                         /* Autosense bits in GEP?       */
+    int asPolarity;                         /* 0 => asserted high           */
+    int asBit;                              /* Autosense bit number in GEP  */
+    int defMedium;                          /* SROM default medium          */
+    int tcount;                             /* Last infoblock number        */
+    int infoblock_init;                     /* Initialised this infoblock?  */
+    int infoleaf_offset;                    /* SROM infoleaf for controller */
+    s32 infoblock_csr6;                     /* csr6 value in SROM infoblock */
+    int infoblock_media;                    /* infoblock media              */
+    int (*infoleaf_fn)(struct device *);    /* Pointer to infoleaf function */
+    u_char *rst;                            /* Pointer to Type 5 reset info */
+    u_char  ibn;                            /* Infoblock number             */
 };
 
 /*
@@ -558,9 +717,29 @@
     int chipset;
     struct de4x5_srom srom;
     int autosense;
+    int useSROM;
 } bus;
 
 /*
+** To get around certain poxy cards that don't provide an SROM
+** for the second and more DECchip, I have to key off the first
+** chip's address. I'll assume there's not a bad SROM iff:
+**
+**      o the chipset is the same
+**      o the bus number is the same and > 0
+**      o the sum of all the returned hw address bytes is 0 or 0x5fa
+**
+** Also have to save the irq for those cards whose hardware designers
+** can't follow the PCI to PCI Bridge Architecture spec.
+*/
+static struct {
+    int chipset;
+    int bus;
+    int irq;
+    u_char addr[ETH_ALEN];
+} last = {0,};
+
+/*
 ** The transmit ring full condition is described by the tx_old and tx_new
 ** pointers by:
 **    tx_old            = tx_new    Empty ring
@@ -580,7 +759,7 @@
 static int     de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
 static void    de4x5_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static int     de4x5_close(struct device *dev);
-static struct  enet_statistics *de4x5_get_stats(struct device *dev);
+static struct  net_device_stats *de4x5_get_stats(struct device *dev);
 static void    de4x5_local_stats(struct device *dev, char *buf, int pkt_len);
 static void    set_multicast_list(struct device *dev);
 static int     de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
@@ -605,6 +784,7 @@
 static int     dc21040_autoconf(struct device *dev);
 static int     dc21041_autoconf(struct device *dev);
 static int     dc21140m_autoconf(struct device *dev);
+static int     srom_autoconf(struct device *dev);
 static int     de4x5_suspect_state(struct device *dev, int timeout, int prev_state, int (*fn)(struct device *, int), int (*asfn)(struct device *));
 static int     dc21040_state(struct device *dev, int csr13, int csr14, int csr15, int timeout, int next_state, int suspect_state, int (*fn)(struct device *, int));
 static int     test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
@@ -642,6 +822,10 @@
 /*static void    srom_busy(u_int command, u_long address);*/
 static void    sendto_srom(u_int command, u_long addr);
 static int     getfrom_srom(u_long addr);
+static void    srom_map_media(struct device *dev);
+static int     srom_infoleaf_info(struct device *dev);
+static void    srom_init(struct device *dev);
+static void    srom_exec(struct device *dev, u_char *p);
 static int     mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr);
 static void    mii_wr(int data, u_char phyreg, u_char phyaddr, u_long ioaddr);
 static int     mii_rdata(u_long ioaddr);
@@ -655,6 +839,8 @@
 static int     mii_get_phy(struct device *dev);
 static void    SetMulticastFilter(struct device *dev);
 static int     get_hw_addr(struct device *dev);
+static void    srom_repair(struct device *dev, int card);
+static int     test_bad_enet(struct device *dev, int status);
 
 static void    eisa_probe(struct device *dev, u_long iobase);
 static void    pci_probe(struct device *dev, u_long iobase);
@@ -664,20 +850,33 @@
 static char    *build_setup_frame(struct device *dev, int mode);
 static void    disable_ast(struct device *dev);
 static void    enable_ast(struct device *dev, u32 time_out);
-static long    de4x5_switch_to_srl(struct device *dev);
-static long    de4x5_switch_to_mii(struct device *dev);
+static long    de4x5_switch_mac_port(struct device *dev);
 static void    timeout(struct device *dev, void (*fn)(u_long data), u_long data, u_long msec);
+static void    yawn(struct device *dev, int state);
 static int     de4x5_dev_index(char *s);
+static void    link_modules(struct device *dev, struct device *tmp);
 static void    de4x5_dbg_open(struct device *dev);
 static void    de4x5_dbg_mii(struct device *dev, int k);
 static void    de4x5_dbg_media(struct device *dev);
 static void    de4x5_dbg_srom(struct de4x5_srom *p);
 static void    de4x5_dbg_rx(struct sk_buff *skb, int len);
 static int     de4x5_strncmp(char *a, char *b, int n);
+static int     dc21041_infoleaf(struct device *dev);
+static int     dc21140_infoleaf(struct device *dev);
+static int     dc21142_infoleaf(struct device *dev);
+static int     dc21143_infoleaf(struct device *dev);
+static int     type0_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type1_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type2_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type3_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type4_infoblock(struct device *dev, u_char count, u_char *p);
+static int     type5_infoblock(struct device *dev, u_char count, u_char *p);
+static int     compact_infoblock(struct device *dev, u_char count, u_char *p);
 
 #ifdef MODULE
 int  init_module(void);
 void cleanup_module(void);
+static struct  device *unlink_modules(struct device *p);
 static int autoprobed = 0, loading_module = 1;
 # else
 static int autoprobed = 0, loading_module = 0;
@@ -686,7 +885,37 @@
 static char name[DE4X5_NAME_LENGTH + 1];
 static u_char de4x5_irq[] = EISA_ALLOWED_IRQ_LIST;
 static int num_de4x5s = 0, num_eth = 0;
-static int cfrv = 0;
+static int cfrv = 0, useSROM = 0;
+
+/*
+** List the SROM infoleaf functions and chipsets
+*/
+struct InfoLeaf {
+    int chipset;
+    int (*fn)(struct device *);
+};
+static struct InfoLeaf infoleaf_array[] = {
+    {DC21041, dc21041_infoleaf},
+    {DC21140, dc21140_infoleaf},
+    {DC21142, dc21142_infoleaf},
+    {DC21143, dc21143_infoleaf}
+};
+#define INFOLEAF_SIZE (sizeof(infoleaf_array)/(sizeof(int)+sizeof(int *)))
+
+/*
+** List the SROM info block functions
+*/
+static int (*dc_infoblock[])(struct device *dev, u_char, u_char *) = {
+    type0_infoblock,
+    type1_infoblock,
+    type2_infoblock,
+    type3_infoblock,
+    type4_infoblock,
+    type5_infoblock,
+    compact_infoblock
+};
+
+#define COMPACT (sizeof(dc_infoblock)/sizeof(int *) - 1)
 
 /*
 ** Miscellaneous defines...
@@ -703,6 +932,13 @@
     de4x5_ms_delay(1);\
 }
 
+#define PHY_HARD_RESET {\
+    outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */\
+    udelay(1000);                        /* Assert for 1ms */\
+    outl(0x00, DE4X5_GEP);\
+    udelay(2000);                        /* Wait for 2ms */\
+}
+
 
 /*
 ** Autoprobing in modules is allowed here. See the top of the file for
@@ -712,17 +948,12 @@
 int
 de4x5_probe(struct device *dev)
 {
-    int tmp = num_de4x5s, status = -ENODEV;
+    int status = -ENODEV;
     u_long iobase = dev->base_addr;
 
     eisa_probe(dev, iobase);
     pci_probe(dev, iobase);
     
-    if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) {
-	printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name, 
-	       iobase);
-    }
-
     /*
     ** Walk the device list to check that at least one device
     ** initialised OK
@@ -731,7 +962,7 @@
     
     if (dev->priv) status = 0;
     if (iobase == 0) autoprobed = 1;
-    
+
     return status;
 }
 
@@ -739,16 +970,18 @@
 de4x5_hw_init(struct device *dev, u_long iobase)
 {
     struct bus_type *lp = &bus;
-    int tmpbus, tmpchs, status=0;
-    int i, media = *((char *)&(lp->srom) + *((char *)&(lp->srom) + 19) * 3);
+    int i, status=0;
     char *tmp;
     
     /* Ensure we're not sleeping */
-    if (lp->chipset == DC21041) {
-	outl(0, PCI_CFDA);
-	de4x5_ms_delay(10);
+    if (lp->bus == EISA) {
+	outb(WAKEUP, PCI_CFPM);
+    } else {
+	pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				  PCI_CFDA_PSM, WAKEUP);
     }
-    
+    de4x5_ms_delay(10);
+
     RESET_DE4X5;
     
     if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
@@ -758,6 +991,7 @@
     /* 
     ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
     */
+    useSROM = FALSE;
     if (lp->bus == PCI) {
 	PCI_signature(name, lp);
     } else {
@@ -784,9 +1018,6 @@
     }
     printk("%2.2x,\n", dev->dev_addr[i]);
     
-    tmpbus = lp->bus;
-    tmpchs = lp->chipset;
-    
     if (status != 0) {
 	printk("      which has an Ethernet PROM CRC error.\n");
 	return -ENXIO;
@@ -810,26 +1041,23 @@
 	dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
 	lp = (struct de4x5_private *)dev->priv;
 	memset(dev->priv, 0, sizeof(struct de4x5_private));
-	lp->bus = tmpbus;
-	lp->chipset = tmpchs;
+	lp->bus = bus.bus;
+	lp->bus_num = bus.bus_num;
+	lp->device = bus.device;
+	lp->chipset = bus.chipset;
 	lp->cache.priv = tmp;
+	lp->cache.gepc = GEP_INIT;
+	lp->asBit = GEP_SLNK;
+	lp->asPolarity = GEP_SLNK;
+	lp->asBitValid = TRUE;
+	lp->timeout = -1;
+	lp->useSROM = useSROM;
+	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
 
 	/*
-	** Check for an MII interface
-	*/
-	if (media & MEDIA_MII) {                   /* MII interface? */
-	    if (!mii_get_phy(dev)) {
-		printk("%s: MII search failed, no device found when one was expected\n", dev->name);
-		return -ENXIO;
-	    }
-	} else {
-	    mii_get_phy(dev);                      /* Search the MII anyway! */
-	}
-	
-	/*
 	** Choose correct autosensing in case someone messed up
 	*/
-	if (de4x5_autosense & AUTO) {
+	if ((de4x5_autosense & AUTO) || lp->useSROM) {
 	    lp->autosense = AUTO;
 	} else {
 	    if (lp->chipset != DC21140) {
@@ -844,14 +1072,14 @@
 		lp->autosense = de4x5_autosense & 0x00c0;
 	    }
 	}
-	
+	lp->fdx = de4x5_full_duplex;
 	sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
 	
 	/*
 	** Set up the RX descriptor ring (Intels)
 	** Allocate contiguous receive buffers, long word aligned (Alphas) 
 	*/
-#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
 	for (i=0; i<NUM_RX_DESC; i++) {
 	    lp->rx_ring[i].status = 0;
 	    lp->rx_ring[i].des1 = RX_BUFF_SZ;
@@ -871,8 +1099,8 @@
 	tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
 	for (i=0; i<NUM_RX_DESC; i++) {
 	    lp->rx_ring[i].status = 0;
-	    lp->rx_ring[i].des1 = RX_BUFF_SZ;
-	    lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ);
+	    lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
+	    lp->rx_ring[i].buf = cpu_to_le32(virt_to_bus(tmp+i*RX_BUFF_SZ));
 	    lp->rx_ring[i].next = 0;
 	    lp->rx_skb[i] = (struct sk_buff *) 1;     /* Dummy entry */
 	}
@@ -888,8 +1116,8 @@
 	lp->txRingSize = NUM_TX_DESC;
 	    
 	/* Write the end of list marker to the descriptor lists */
-	lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
-	lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+	lp->rx_ring[lp->rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
+	lp->tx_ring[lp->txRingSize - 1].des1 |= cpu_to_le32(TD_TER);
 	    
 	/* Tell the adapter where the TX/RX rings are located. */
 	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
@@ -903,20 +1131,35 @@
 	create_packet(dev, lp->frame, sizeof(lp->frame));
 
 	/* Check if the RX overflow bug needs testing for */
-	tmpchs = cfrv & 0x000000fe;
-	if ((lp->chipset == DC21140) && (tmpchs == 0x20)) {
+	i = cfrv & 0x000000fe;
+	if ((lp->chipset == DC21140) && (i == 0x20)) {
 	    lp->rx_ovf = 1;
 	}
 
-	/* Initialise the adapter state */
+	/* Initialise the SROM pointers if possible */
+	if (lp->useSROM) {
+	    lp->state = INITIALISED;
+	    de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
+	    if (srom_infoleaf_info(dev)) {
+		return -ENXIO;
+	    }
+	    srom_init(dev);
+	}
+
 	lp->state = CLOSED;
 
+	/*
+	** Check for an MII interface
+	*/
+	if ((lp->chipset != DC21040) && (lp->chipset != DC21041)) {
+	    mii_get_phy(dev);
+	}
+	
 	printk("      and requires IRQ%d (provided by %s).\n", dev->irq,
 	       ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG"));
-
     }
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_VERSION) {
 	printk(version);
     }
     
@@ -930,14 +1173,11 @@
     
     dev->mem_start = 0;
     
-    /* Fill in the generic field of the device structure. */
+    /* Fill in the generic fields of the device structure. */
     ether_setup(dev);
     
     /* Let the adapter sleep to save power */
-    if (lp->chipset == DC21041) {
-	outl(0, DE4X5_SICR);
-	outl(CFDA_PSM, PCI_CFDA);
-    }
+    yawn(dev, SLEEP);
     
     return status;
 }
@@ -962,10 +1202,7 @@
     /*
     ** Wake up the adapter
     */
-    if (lp->chipset == DC21041) {
-	outl(0, PCI_CFDA);
-	de4x5_ms_delay(10);
-    }
+    yawn(dev, WAKEUP);
 
     /* 
     ** Re-initialize the DE4X5... 
@@ -977,20 +1214,31 @@
     
     if (request_irq(dev->irq, (void *)de4x5_interrupt, SA_SHIRQ, 
 		                                     lp->adapter_name, dev)) {
-	printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
-	status = -EAGAIN;
-    } else {
-	dev->tbusy = 0;                         
-	dev->start = 1;
-	dev->interrupt = UNMASK_INTERRUPTS;
-	dev->trans_start = jiffies;
-	
-	START_DE4X5;
-	
-	de4x5_setup_intr(dev);
+	printk("de4x5_open(): Requested IRQ%d is busy - attemping FAST/SHARE...", dev->irq);
+	if (request_irq(dev->irq, de4x5_interrupt, SA_INTERRUPT | SA_SHIRQ,
+			                             lp->adapter_name, dev)) {
+	    printk("\n              Cannot get IRQ- reconfigure your hardware.\n");
+	    disable_ast(dev);
+	    de4x5_free_rx_buffs(dev);
+	    de4x5_free_tx_buffs(dev);
+	    yawn(dev, SLEEP);
+	    lp->state = CLOSED;
+	    return -EAGAIN;
+	} else {
+	    printk("\n              Succeeded, but you should reconfigure your hardware to avoid this.\n");
+	    printk("WARNING: there may be IRQ related problems in heavily loaded systems.\n");
+	}
     }
+    dev->tbusy = 0;                         
+    dev->start = 1;
+    dev->interrupt = UNMASK_INTERRUPTS;
+    dev->trans_start = jiffies;
+    
+    START_DE4X5;
+	
+    de4x5_setup_intr(dev);
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_OPEN) {
 	printk("\tsts:  0x%08x\n", inl(DE4X5_STS));
 	printk("\tbmr:  0x%08x\n", inl(DE4X5_BMR));
 	printk("\timr:  0x%08x\n", inl(DE4X5_IMR));
@@ -1037,12 +1285,15 @@
     s32 bmr, omr;
     
     /* Select the MII or SRL port now and RESET the MAC */
-    if (lp->phy[lp->active].id == 0) {
-	de4x5_switch_to_srl(dev);
-    } else {
-	de4x5_switch_to_mii(dev);
+    if (!lp->useSROM) {
+	if (lp->phy[lp->active].id != 0) {
+	    lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	} else {
+	    lp->infoblock_csr6 = OMR_TTM;
+	}
+	de4x5_switch_mac_port(dev);
     }
-    
+
     /* 
     ** Set the programmable burst length to 8 longwords for all the DC21140
     ** Fasternet chips and 4 longwords for all others: DMA errors result
@@ -1063,11 +1314,11 @@
     lp->tx_new = lp->tx_old = 0;
     
     for (i = 0; i < lp->rxRingSize; i++) {
-	lp->rx_ring[i].status = R_OWN;
+	lp->rx_ring[i].status = cpu_to_le32(R_OWN);
     }
     
     for (i = 0; i < lp->txRingSize; i++) {
-	lp->tx_ring[i].status = 0;
+	lp->tx_ring[i].status = cpu_to_le32(0);
     }
     
     barrier();
@@ -1082,7 +1333,7 @@
     sti();                                       /* Ensure timer interrupts */
     for (j=0, i=0;(i<500) && (j==0);i++) {       /* Upto 500ms delay */
 	udelay(1000);
-	if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+	if ((s32)le32_to_cpu(lp->tx_ring[lp->tx_new].status) >= 0) j=1;
     }
     outl(omr, DE4X5_OMR);                        /* Stop everything! */
     
@@ -1137,8 +1388,8 @@
 	} else {
 	    de4x5_put_cache(dev, skb);
 	}
-	if (de4x5_debug > 1) {
-	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  lostMedia:%d\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
+	if (de4x5_debug & DEBUG_TX) {
+	    printk("%s: transmit busy, lost media or stale skb found:\n  STS:%08x\n  tbusy:%ld\n  IMR:%08x\n  OMR:%08x\n Stale skb: %s\n",dev->name, inl(DE4X5_STS), dev->tbusy, inl(DE4X5_IMR), inl(DE4X5_OMR), (lp->tx_skb[lp->tx_new] ? "YES" : "NO"));
 	}
     } else if (skb->len > 0) {
 	/* If we already have stuff queued locally, use that first */
@@ -1151,6 +1402,9 @@
 	    cli();
 	    set_bit(0, (void*)&dev->tbusy);
 	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ 	    lp->stats.tx_bytes += skb->len;
+#endif
 	    outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
 		
 	    lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -1200,6 +1454,9 @@
       printk("%s: Re-entering the interrupt handler.\n", dev->name);
 	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+    synchronize_irq();
+#endif
     dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
@@ -1215,7 +1472,6 @@
 	  de4x5_tx(dev); 
 	    
 	if (sts & STS_LNF) {             /* TP Link has failed */
-	    lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
 	    lp->irq_mask &= ~IMR_LFM;
 	}
 	    
@@ -1253,8 +1509,9 @@
     int entry;
     s32 status;
     
-    for (entry=lp->rx_new; lp->rx_ring[entry].status>=0;entry=lp->rx_new) {
-	status = lp->rx_ring[entry].status;
+    for (entry=lp->rx_new; (s32)le32_to_cpu(lp->rx_ring[entry].status)>=0;
+	                                                    entry=lp->rx_new) {
+	status = (s32)le32_to_cpu(lp->rx_ring[entry].status);
 	
 	if (lp->rx_ovf) {
 	    if (inl(DE4X5_MFC) & MFC_FOCM) {
@@ -1268,7 +1525,7 @@
 	}
 	
 	if (status & RD_LS) {                 /* Valid frame status */
-	    lp->linkOK++;
+	    if (lp->tx_enable) lp->linkOK++;
 	    if (status & RD_ES) {	      /* There was an error. */
 		lp->stats.rx_errors++;        /* Update the error stats. */
 		if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
@@ -1281,31 +1538,35 @@
 		if (status & RD_OF)           lp->pktStats.rx_overflow++;
 	    } else {                          /* A valid frame received */
 		struct sk_buff *skb;
-		short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
+		short pkt_len = (short)(le32_to_cpu(lp->rx_ring[entry].status)
+					                            >> 16) - 4;
 		
 		if ((skb = de4x5_alloc_rx_buff(dev, entry, pkt_len)) == NULL) {
 		    printk("%s: Insufficient memory; nuking packet.\n", 
 			                                            dev->name);
-		    lp->stats.rx_dropped++;   /* Really, deferred. */
-		    break;
-		}
-		de4x5_dbg_rx(skb, pkt_len);
+		    lp->stats.rx_dropped++;
+		} else {
+		    de4x5_dbg_rx(skb, pkt_len);
 
-		/* Push up the protocol stack */
-		skb->protocol=eth_type_trans(skb,dev);
-		netif_rx(skb);
+		    /* Push up the protocol stack */
+		    skb->protocol=eth_type_trans(skb,dev);
+		    netif_rx(skb);
 		    
-		/* Update stats */
-		lp->stats.rx_packets++;
-		de4x5_local_stats(dev, skb->data, pkt_len);
+		    /* Update stats */
+		    lp->stats.rx_packets++;
+#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
+ 		    lp->stats.rx_bytes += pkt_len;
+#endif
+		    de4x5_local_stats(dev, skb->data, pkt_len);
+		}
 	    }
 	    
 	    /* Change buffer ownership for this frame, back to the adapter */
 	    for (;lp->rx_old!=entry;lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
-		lp->rx_ring[lp->rx_old].status = R_OWN;
+		lp->rx_ring[lp->rx_old].status = cpu_to_le32(R_OWN);
 		barrier();
 	    }
-	    lp->rx_ring[entry].status = R_OWN;
+	    lp->rx_ring[entry].status = cpu_to_le32(R_OWN);
 	    barrier();
 	}
 	
@@ -1330,7 +1591,7 @@
     s32 status;
     
     for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
-	status = lp->tx_ring[entry].status;
+	status = (s32)le32_to_cpu(lp->tx_ring[entry].status);
 	if (status < 0) {                     /* Buffer not sent yet */
 	    break;
 	} else if (status != 0x7fffffff) {    /* Not setup frame */
@@ -1342,16 +1603,12 @@
 		if (status & TD_EC) lp->pktStats.excessive_collisions++;
 		if (status & TD_DE) lp->stats.tx_aborted_errors++;
 	    
-		if (status & (TD_LO | TD_NC | TD_EC | TD_LF)) {
-		    lp->lostMedia++;
-		}
 		if (TX_PKT_PENDING) {
 		    outl(POLL_DEMAND, DE4X5_TPD);/* Restart a stalled TX */
 		}
 	    } else {                      /* Packet sent */
 		lp->stats.tx_packets++;
-		lp->lostMedia = 0;        /* Remove transient problem */
-		lp->linkOK++;
+		if (lp->tx_enable) lp->linkOK++;
 	    }
 	    /* Update the collision counter */
 	    lp->stats.collisions += ((status & TD_EC) ? 16 : 
@@ -1384,7 +1641,9 @@
     
     disable_ast(dev);
     
-    if (lp->chipset == DC21140) {
+    if (lp->useSROM) {
+	next_tick = srom_autoconf(dev);
+    } else if (lp->chipset == DC21140) {
 	next_tick = dc21140m_autoconf(dev);
     } else if (lp->chipset == DC21041) {
 	next_tick = dc21041_autoconf(dev);
@@ -1431,8 +1690,8 @@
     outl(omr & ~OMR_SR, DE4X5_OMR);
     while (inl(DE4X5_STS) & STS_RS);
 
-    for (; lp->rx_ring[lp->rx_new].status>=0;) {
-	lp->rx_ring[lp->rx_new].status = R_OWN;
+    for (; (s32)le32_to_cpu(lp->rx_ring[lp->rx_new].status)>=0;) {
+	lp->rx_ring[lp->rx_new].status = cpu_to_le32(R_OWN);
 	lp->rx_new = (++lp->rx_new % lp->rxRingSize);
     }
 
@@ -1452,7 +1711,7 @@
     dev->start = 0;
     dev->tbusy = 1;
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_CLOSE) {
 	printk("%s: Shutting down ethercard, status was %8.8x.\n",
 	       dev->name, inl(DE4X5_STS));
     }
@@ -1474,15 +1733,12 @@
     MOD_DEC_USE_COUNT;
     
     /* Put the adapter to sleep to save power */
-    if (lp->chipset == DC21041) {
-	outl(0, DE4X5_SICR);
-	outl(CFDA_PSM, PCI_CFDA);
-    }
+    yawn(dev, SLEEP);
     
     return 0;
 }
 
-static struct enet_statistics *
+static struct net_device_stats *
 de4x5_get_stats(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
@@ -1529,12 +1785,12 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     
-    lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf);
-    lp->tx_ring[lp->tx_new].des1 &= TD_TER;
-    lp->tx_ring[lp->tx_new].des1 |= flags;
+    lp->tx_ring[lp->tx_new].buf = cpu_to_le32(virt_to_bus(buf));
+    lp->tx_ring[lp->tx_new].des1 &= cpu_to_le32(TD_TER);
+    lp->tx_ring[lp->tx_new].des1 |= cpu_to_le32(flags);
     lp->tx_skb[lp->tx_new] = skb;
     barrier();
-    lp->tx_ring[lp->tx_new].status = T_OWN;
+    lp->tx_ring[lp->tx_new].status = cpu_to_le32(T_OWN);
     barrier();
     
     return;
@@ -1548,7 +1804,7 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    
+
     /* First, double check that the adapter is open */
     if (lp->state == OPEN) {
 	if (dev->flags & IFF_PROMISC) {         /* set promiscuous mode */
@@ -1586,7 +1842,7 @@
     u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
     char *pa;
     unsigned char *addrs;
-    
+
     omr = inl(DE4X5_OMR);
     omr &= ~(OMR_PR | OMR_PM);
     pa = build_setup_frame(dev, ALL);        /* Build the basic frame */
@@ -1639,15 +1895,15 @@
 static void
 eisa_probe(struct device *dev, u_long ioaddr)
 {
-    int i, maxSlots, status;
-    u_short vendor, device;
-    s32 cfid;
+    int i, maxSlots, status, device;
+    u_short vendor;
+    u32 cfid;
     u_long iobase;
     struct bus_type *lp = &bus;
     char name[DE4X5_STRLEN];
     struct device *tmp;
     
-    if (!ioaddr && autoprobed) return;     /* Been here before ! */
+    if (autoprobed) return;                /* Been here before ! */
     
     lp->bus = EISA;
     
@@ -1661,17 +1917,18 @@
 	maxSlots = i + 1;
     }
     
-    for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+    for (status= -ENODEV;(i<maxSlots)&&(dev!=NULL);i++,iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) {
-	    cfid = inl(PCI_CFID);
-	    cfrv = inl(PCI_CFRV);
-	    device = (u_short)(cfid >> 16);
+	    cfid = (u32) inl(PCI_CFID);
+	    cfrv = (u_short) inl(PCI_CFRV);
+	    device = (cfid >> 8) & 0x00ffff00;
 	    vendor = (u_short) cfid;
 	    
 	    /* Read the EISA Configuration Registers */
 	    dev->irq = inb(EISA_REG0);
 	    dev->irq = de4x5_irq[(dev->irq >> 1) & 0x03];
 
+	    if (is_DC2114x) device |= (cfrv & 0x00f0);
 	    lp->chipset = device;
 	    DevicePresent(DE4X5_APROM);
 	    /* Write the PCI Configuration Registers */
@@ -1683,6 +1940,9 @@
 		if ((tmp = alloc_device(dev, iobase)) != NULL) {
 		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
 			num_de4x5s++;
+			if (loading_module) link_modules(dev, tmp);
+		    } else if (loading_module && (tmp != dev)) {
+			kfree(tmp);
 		    }
 		}
 	    } else if (autoprobed) {
@@ -1714,13 +1974,13 @@
 {
     u_char irq;
     u_char pb, pbus, dev_num, dnum, dev_fn;
-    u_short vendor, device, index, status;
+    u_short dev_id, vendor, index, status;
     u_int class = DE4X5_CLASS_CODE;
-    u_int iobase;
+    u_int device, iobase;
     struct bus_type *lp = &bus;
     struct device *tmp;
 
-    if ((!ioaddr || !loading_module) && autoprobed) return;
+    if (autoprobed) return;
     
     if (!pcibios_present()) return;          /* No PCI bus in this machine! */
     
@@ -1738,21 +1998,32 @@
 	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 index++) {
 	dev_num = PCI_SLOT(dev_fn);
-	
+
 	if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
+	    device = 0;
 	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
-	    if (!(is_DC21040 || is_DC21041 || is_DC21140)) continue;
+	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
+	    device = dev_id;
+	    device <<= 8;
+	    if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
+		continue;
+	    }
+
+	    /* Get the chip configuration revision register */
+	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
 
 	    /* Set the device number information */
 	    lp->device = dev_num;
 	    lp->bus_num = pb;
 	    
 	    /* Set the chipset information */
+	    if (is_DC2114x) device |= (cfrv & 0x00f0);
 	    lp->chipset = device;
-	    
-	    /* Get the chip configuration revision register */
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
+	    if (is_DC21142 || is_DC21143) {
+		printk("de4x5: Detected a %s chip. Currently this is unsupported in this driver.\nPlease email the author to request its inclusion!\n", (is_DC21142?"DC21142":"DC21143"));
+		continue;
+	    }
 
 	    /* Get the board I/O address */
 	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
@@ -1778,6 +2049,9 @@
 		    tmp->irq = irq;
 		    if ((status = de4x5_hw_init(tmp, iobase)) == 0) {
 			num_de4x5s++;
+			if (loading_module) link_modules(dev, tmp);
+		    } else if (loading_module && (tmp != dev)) {
+			kfree(tmp);
 		    }
 		}
 	    } else if (autoprobed) {
@@ -1802,9 +2076,17 @@
     struct device *adev = NULL;
     int fixed = 0, new_dev = 0;
 
+    if (!dev) return dev;
     num_eth = de4x5_dev_index(dev->name);
-    if (loading_module) return dev;
-    
+
+    if (loading_module) {
+	if (dev->priv) {
+	    dev = insert_device(dev, iobase, de4x5_probe);
+	}
+	num_eth++;
+	return dev;
+    }
+
     while (1) {
 	if (((dev->base_addr == DE4X5_NDA) || (dev->base_addr==0)) && !adev) {
 	    adev=dev;
@@ -1851,20 +2133,22 @@
 	printk("eth%d: Device not initialised, insufficient memory\n",num_eth);
 	return NULL;
     } else {
-	new->next = dev->next;
-	dev->next = new;
-	dev = dev->next;               /* point to the new device */
-	dev->name = (char *)(dev + 1);
-	if (num_eth > 9999) {
-	    sprintf(dev->name,"eth????");/* New device name */
-	} else {
-	    sprintf(dev->name,"eth%d", num_eth);/* New device name */
+	memset((char *)new, 0, sizeof(struct device)+8);
+	new->name = (char *)(new + 1);
+	new->base_addr = iobase;       /* assign the io address */
+	new->init = init;              /* initialisation routine */
+	if (!loading_module) {
+	    new->next = dev->next;
+	    dev->next = new;
+	    if (num_eth > 9999) {
+		sprintf(new->name,"eth????");/* New device name */
+	    } else {
+		sprintf(new->name,"eth%d", num_eth);/* New device name */
+	    }
 	}
-	dev->base_addr = iobase;       /* assign the io address */
-	dev->init = init;              /* initialisation routine */
     }
 
-    return dev;
+    return new;
 }
 
 static int
@@ -1882,6 +2166,26 @@
     return i;
 }
 
+static void
+link_modules(struct device *dev, struct device *tmp)
+{
+    struct device *p=dev;
+
+    if (p) {
+	while (((struct de4x5_private *)(p->priv))->next_module) {
+	    p = ((struct de4x5_private *)(p->priv))->next_module;
+	}
+
+	if (dev != tmp) {
+	    ((struct de4x5_private *)(p->priv))->next_module = tmp;
+	} else {
+	    ((struct de4x5_private *)(p->priv))->next_module = NULL;
+	}
+    }
+
+    return;
+}
+
 /*
 ** Auto configure the media here rather than setting the port at compile
 ** time. This routine is called by de4x5_init() and when a loss of media is
@@ -1901,13 +2205,16 @@
     disable_ast(dev);
     inl(DE4X5_MFC);                         /* Zero the lost frames counter */
     lp->media = INIT;
-    if (lp->chipset == DC21040) {
+    if (lp->useSROM) {
+	next_tick = srom_autoconf(dev);
+    } else if (lp->chipset == DC21040) {
 	next_tick = dc21040_autoconf(dev);
     } else if (lp->chipset == DC21041) {
 	next_tick = dc21041_autoconf(dev);
     } else if (lp->chipset == DC21140) {
 	next_tick = dc21140m_autoconf(dev);
     }
+
     enable_ast(dev, next_tick);
     
     return (lp->media);
@@ -2046,7 +2353,7 @@
 
     switch (lp->local_state) {
       case 1:
-	if (lp->linkOK && !LOST_MEDIA) {
+	if (lp->linkOK) {
 	    lp->media = prev_state;
 	} else {
 	    lp->local_state++;
@@ -2063,6 +2370,7 @@
 	    lp->media = prev_state;
 	} else {
 	    lp->media = INIT;
+	    lp->tcount++;
 	}
     }
 
@@ -2110,7 +2418,7 @@
       case TP_NW:
 	if (lp->timeout < 0) {
 	    omr = inl(DE4X5_OMR);/* Set up full duplex for the autonegotiate */
-	    outl(omr | OMR_FD, DE4X5_OMR);
+	    outl(omr | OMR_FDX, DE4X5_OMR);
 	}
 	irqs = STS_LNF | STS_LNP;
 	irq_mask = IMR_LFM | IMR_LPM;
@@ -2157,7 +2465,7 @@
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for TP */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = STS_LNF | STS_LNP;
 	    irq_mask = IMR_LFM | IMR_LPM;
@@ -2191,7 +2499,7 @@
 	if (!lp->tx_enable) {
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for AUI */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
 	    irq_mask = 0;
@@ -2222,7 +2530,7 @@
 	  case 0:
 	    if (lp->timeout < 0) {
 		omr = inl(DE4X5_OMR);          /* Set up half duplex for BNC */
-		outl(omr & ~OMR_FD, DE4X5_OMR);
+		outl(omr & ~OMR_FDX, DE4X5_OMR);
 	    }
 	    irqs = 0;
 	    irq_mask = 0;
@@ -2261,7 +2569,7 @@
 	
       case NC:
 	omr = inl(DE4X5_OMR);    /* Set up full duplex for the autonegotiate */
-	outl(omr | OMR_FD, DE4X5_OMR);
+	outl(omr | OMR_FDX, DE4X5_OMR);
 	reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
 	if (lp->media != lp->c_media) {
 	    de4x5_dbg_media(dev);
@@ -2287,34 +2595,45 @@
     int ana, anlpa, cap, cr, slnk, sr, iobase = dev->base_addr;
     int next_tick = DE4X5_AUTOSENSE_MS;
     u_long imr, omr;
-    
+
     switch(lp->media) {
       case INIT: 
-	DISABLE_IRQs;
-	lp->tx_enable = FALSE;
-	lp->timeout = -1;
+        if (lp->timeout < 0) {
+	    DISABLE_IRQs;
+	    lp->tx_enable = FALSE;
+	    lp->linkOK = 0;
+	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
+	}
 	if ((next_tick = de4x5_reset_phy(dev)) < 0) {
 	    next_tick &= ~TIMER_CB;
 	} else {
-	    de4x5_save_skbs(dev);          /* Save non transmitted skb's */
-	    lp->tmp = MII_SR_ASSC;         /* Fake out the MII speed set */
-	    SET_10Mb;
-	    if (lp->autosense == _100Mb) {
-		lp->media = _100Mb;
-	    } else if (lp->autosense == _10Mb) {
-		lp->media = _10Mb;
-	    } else if ((lp->autosense == AUTO) && 
-		                     ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
-		ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
-		ana &= (de4x5_full_duplex ? ~0 : ~MII_ANA_FDAM);
-		mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
-		lp->media = ANS;
-	    } else if (lp->autosense == AUTO) {
-		lp->media = SPD_DET;
-	    } else if (is_spd_100(dev) && is_100_up(dev)) {
-		lp->media = _100Mb;
+	    if (lp->useSROM) {
+		srom_map_media(dev);
+		srom_exec(dev, lp->phy[lp->active].gep);
+		if (lp->infoblock_media == ANS) {
+		    ana = lp->phy[lp->active].ana | MII_ANA_CSMA;
+		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		}
 	    } else {
-		lp->media = NC;
+		lp->tmp = MII_SR_ASSC;     /* Fake out the MII speed set */
+		SET_10Mb;
+		if (lp->autosense == _100Mb) {
+		    lp->media = _100Mb;
+		} else if (lp->autosense == _10Mb) {
+		    lp->media = _10Mb;
+		} else if ((lp->autosense == AUTO) && 
+			            ((sr=is_anc_capable(dev)) & MII_SR_ANC)) {
+		    ana = (((sr >> 6) & MII_ANA_TAF) | MII_ANA_CSMA);
+		    ana &= (lp->fdx ? ~0 : ~MII_ANA_FDAM);
+		    mii_wr(ana, MII_ANA, lp->phy[lp->active].addr, DE4X5_MII);
+		    lp->media = ANS;
+		} else if (lp->autosense == AUTO) {
+		    lp->media = SPD_DET;
+		} else if (is_spd_100(dev) && is_100_up(dev)) {
+		    lp->media = _100Mb;
+		} else {
+		    lp->media = NC;
+		}
 	    }
 	    lp->local_state = 0;
 	    next_tick = dc21140m_autoconf(dev);
@@ -2354,10 +2673,10 @@
 		    if (!(anlpa & MII_ANLPA_RF) && 
 			 (cap = anlpa & MII_ANLPA_TAF & ana)) {
 			if (cap & MII_ANA_100M) {
-			    de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_100M) ? TRUE : FALSE);
 			    lp->media = _100Mb;
 			} else if (cap & MII_ANA_10M) {
-			    de4x5_full_duplex = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
+			    lp->fdx = ((ana & anlpa & MII_ANA_FDAM & MII_ANA_10M) ? TRUE : FALSE);
 
 			    lp->media = _10Mb;
 			}
@@ -2390,7 +2709,7 @@
 	break;
 	
       case _100Mb:                               /* Set 100Mb/s */
-	next_tick = 3000;
+        next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_100Mb;
 	    de4x5_init_connection(dev);
@@ -2398,6 +2717,7 @@
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
 		if (!(is_spd_100(dev) && is_100_up(dev))) {
 		    lp->media = INIT;
+		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
 		}
 	    }
@@ -2405,7 +2725,7 @@
 	break;
 	
       case _10Mb:                                /* Set 10Mb/s */
-	next_tick = 3000;
+        next_tick = 3000;
 	if (!lp->tx_enable) {
 	    SET_10Mb;
 	    de4x5_init_connection(dev);
@@ -2413,6 +2733,7 @@
 	    if (!lp->linkOK && (lp->autosense == AUTO)) {
 		if (!(!is_spd_100(dev) && is_10_up(dev))) {
 		    lp->media = INIT;
+		    lp->tcount++;
 		    next_tick = DE4X5_AUTOSENSE_MS;
 		}
 	    }
@@ -2432,6 +2753,72 @@
     return next_tick;
 }
 
+static int
+srom_autoconf(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    return lp->infoleaf_fn(dev);
+}
+
+/*
+** This mapping keeps the original media codes and FDX flag unchanged.
+** While it isn't strictly necessary, it helps me for the moment...
+*/
+static void
+srom_map_media(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+    lp->fdx = 0;
+    switch(lp->infoblock_media) {
+      case SROM_10BASETF:
+	lp->fdx = TRUE;
+      case SROM_10BASET:
+	if (lp->chipset == DC21140) {
+	    lp->media = _10Mb;
+	} else {
+	    lp->media = TP;
+	}
+	break;
+
+      case SROM_10BASE2:
+	lp->media = BNC;
+	break;
+
+      case SROM_10BASE5:
+	lp->media = AUI;
+	break;
+
+      case SROM_100BASETF:
+	lp->fdx = TRUE;
+      case SROM_100BASET:
+	lp->media = _100Mb;
+	break;
+
+      case SROM_100BASET4:
+	lp->media = _100Mb;
+	break;
+
+      case SROM_100BASEFF:
+	lp->fdx = TRUE;
+      case SROM_100BASEF: 
+	lp->media = _100Mb;
+	break;
+
+      case ANS:
+	lp->media = ANS;
+	break;
+
+      default: 
+	printk("%s: Bad media code [%d] detected in SROM!\n", dev->name, 
+	                                                  lp->infoblock_media);
+	break;
+    }
+
+    return;
+}
+
 static void
 de4x5_init_connection(struct device *dev)
 {
@@ -2442,11 +2829,10 @@
         de4x5_dbg_media(dev);
 	lp->c_media = lp->media;          /* Stop scrolling media messages */
     }
-    de4x5_restore_skbs(dev);
+
     cli();
-    de4x5_rx(dev);
+    de4x5_restore_skbs(dev);
     de4x5_setup_intr(dev);
-    lp->lostMedia = 0;
     lp->tx_enable = YES;
     dev->tbusy = 0;
     sti();
@@ -2456,6 +2842,11 @@
     return;
 }
 
+/*
+** General PHY reset function. Some MII devices don't reset correctly
+** since their MII address pins can float at voltages that are dependent
+** on the signal pin use. Do a double reset to ensure a reset.
+*/
 static int
 de4x5_reset_phy(struct device *dev)
 {
@@ -2463,17 +2854,30 @@
     u_long iobase = dev->base_addr;
     int next_tick = 0;
 
-    if (lp->phy[lp->active].id) {
+    if ((lp->useSROM) || (lp->phy[lp->active].id)) {
 	if (lp->timeout < 0) {
-	    outl(GEP_HRST, DE4X5_GEP);           /* Hard RESET the PHY dev. */
-	    udelay(1000);                        /* Assert for 1ms */
-	    outl(0x00, DE4X5_GEP);
-	    udelay(2000);                        /* Wait for 2ms */
-	    mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+	    if (lp->useSROM) {
+		if (lp->phy[lp->active].rst) { /* MII device specific reset */
+		    srom_exec(dev, lp->phy[lp->active].rst);
+		    srom_exec(dev, lp->phy[lp->active].rst);
+		} else if (lp->rst) {          /* Type 5 infoblock reset */
+		    srom_exec(dev, lp->rst);
+		    srom_exec(dev, lp->rst);
+		}
+	    } else {
+		PHY_HARD_RESET;
+	    }
+	    if (lp->useMII) {
+	        mii_wr(MII_CR_RST, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);
+            }
+        }
+	if (lp->useMII) {
+	    next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
 	}
-	next_tick = test_mii_reg(dev, MII_CR, MII_CR_RST, FALSE, 500);
+    } else if (lp->chipset == DC21140) {
+	PHY_HARD_RESET;
     }
-    
+
     return next_tick;
 }
 
@@ -2546,7 +2950,7 @@
 	lp->timeout = msec/100;
     }
     
-    if (lp->phy[lp->active].id) {
+    if (lp->phy[lp->active].id || lp->useSROM) {
 	gep = ((is_100_up(dev) && is_spd_100(dev)) ? GEP_SLNK : 0);
     } else {
 	gep = (~inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP));
@@ -2594,7 +2998,11 @@
     u_long iobase = dev->base_addr;
     int spd;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->useSROM && !lp->useMII) {
+	spd = (lp->asBitValid & 
+	       (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+ 		 (lp->linkOK & ~lp->asBitValid);
+    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	spd = mii_rd(lp->phy[lp->active].spd.reg, lp->phy[lp->active].addr, DE4X5_MII);
 	spd = ~(spd ^ lp->phy[lp->active].spd.value);
 	spd &= lp->phy[lp->active].spd.mask;
@@ -2611,7 +3019,11 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->useSROM && !lp->useMII) {
+	return ((lp->asBitValid & 
+		 (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+		(lp->linkOK & ~lp->asBitValid));
+    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	/* Double read for sticky bits & temporary drops */
 	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
@@ -2626,7 +3038,11 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->useSROM && !lp->useMII) {
+	return ((lp->asBitValid & 
+		 (lp->asPolarity ^ (inl(DE4X5_GEP) & lp->asBit))) |
+		(lp->linkOK & ~lp->asBitValid));
+    } else if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	/* Double read for sticky bits & temporary drops */
 	mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII) & MII_SR_LKS);
@@ -2641,7 +3057,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
     
-    if (lp->phy[lp->active].id) {
+    if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	return (mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII));
     } else {
 	return 0;
@@ -2670,11 +3086,14 @@
     
     sisr = inl(DE4X5_SISR);
 
-    if ((!(sisr & SISR_NCR)) && (lp->tx_ring[lp->tmp].status < 0) && (--lp->timeout)) {
+    if ((!(sisr & SISR_NCR)) && 
+	((s32)le32_to_cpu(lp->tx_ring[lp->tmp].status) < 0) && 
+	 (--lp->timeout)) {
 	sisr = 100 | TIMER_CB;
     } else {
 	if ((!(sisr & SISR_NCR)) && 
-	    !(lp->tx_ring[lp->tmp].status & (T_OWN | TD_ES)) && lp->timeout) {
+	    !(le32_to_cpu(lp->tx_ring[lp->tmp].status) & (T_OWN | TD_ES)) &&
+	    lp->timeout) {
 	    sisr = 0;
 	} else {
 	    sisr = 1;
@@ -2696,7 +3115,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     struct sk_buff *p;
 
-#if !defined(__alpha__) && !defined(DE4X5_DO_MEMCPY)
+#if !defined(__alpha__) && !defined(__powerpc__) && !defined(DE4X5_DO_MEMCPY)
     struct sk_buff *ret;
     u_long i=0, tmp;
 
@@ -2728,10 +3147,13 @@
     skb_reserve(p, 2);	                               /* Align */
     if (index < lp->rx_old) {                          /* Wrapped buffer */
 	short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
-	memcpy(skb_put(p,tlen), bus_to_virt(lp->rx_ring[lp->rx_old].buf),tlen);
-	memcpy(skb_put(p,len-tlen), bus_to_virt(lp->rx_ring[0].buf), len-tlen);
+	memcpy(skb_put(p,tlen), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),tlen);
+	memcpy(skb_put(p,len-tlen), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[0].buf)), len-tlen);
     } else {                                           /* Linear buffer */
-	memcpy(skb_put(p,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf),len);
+	memcpy(skb_put(p,len), 
+	       bus_to_virt(le32_to_cpu(lp->rx_ring[lp->rx_old].buf)),len);
     }
 		    
     return p;
@@ -2810,13 +3232,26 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
+    int i;
     s32 omr;
 
     if (lp->cache.save_cnt) {
 	STOP_DE4X5;
-	de4x5_cache_state(dev, DE4X5_SAVE_STATE);
-	de4x5_sw_reset(dev);
-	de4x5_cache_state(dev, DE4X5_RESTORE_STATE);
+	outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+	outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+    
+	lp->rx_new = lp->rx_old = 0;
+	lp->tx_new = lp->tx_old = 0;
+    
+	for (i = 0; i < lp->rxRingSize; i++) {
+	    lp->rx_ring[i].status = cpu_to_le32(R_OWN);
+	}
+    
+	for (i = 0; i < lp->txRingSize; i++) {
+	    lp->tx_ring[i].status = cpu_to_le32(0);
+	}
+    
+	barrier();
 	lp->cache.save_cnt--;
 	START_DE4X5;
     }
@@ -2829,7 +3264,6 @@
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     u_long iobase = dev->base_addr;
-    s32 gep;
 
     switch(flag) {
       case DE4X5_SAVE_STATE:
@@ -2848,12 +3282,8 @@
 	outl(lp->cache.csr6, DE4X5_OMR);
 	outl(lp->cache.csr7, DE4X5_IMR);
 	if (lp->chipset == DC21140) {
-	    outl(GEP_INIT, DE4X5_GEP);
-	    gep = (lp->media == _100Mb ? GEP_MODE : 0);
-	    if (!lp->phy[lp->active].id && !de4x5_full_duplex) {
-		gep |= GEP_FDXD;
-	    }
-	    outl(gep, DE4X5_GEP);
+	    outl(lp->cache.gepc, DE4X5_GEP);
+	    outl(lp->cache.gep, DE4X5_GEP);
 	} else {
 	    reset_init_sia(dev, lp->cache.csr13, lp->cache.csr14, 
 			                                      lp->cache.csr15);
@@ -2975,7 +3405,7 @@
 }
 
 /*
-** Create a loopback ethernet packet with an invalid CRC
+** Create a loopback ethernet packet
 */
 static void
 create_packet(struct device *dev, char *frame, int len)
@@ -3069,13 +3499,10 @@
     
     if (lp->chipset == DC21040) {
 	strcpy(name, "DE434/5");
-    } else {
+	return status;
+    } else {                           /* Search for a DEC name in the SROM */
 	int i = *((char *)&lp->srom + 19) * 3;
-	if (lp->chipset == DC21041) {
-	    strncpy(name, (char *)&lp->srom + 26 + i, 8);
-	} else if (lp->chipset == DC21140) {
-	    strncpy(name, (char *)&lp->srom + 26 + i, 8);
-	}
+	strncpy(name, (char *)&lp->srom + 26 + i, 8);
     }
     name[8] = '\0';
     for (i=0; i<siglen; i++) {
@@ -3087,8 +3514,13 @@
 	} else {                        /* Use chip name to avoid confusion */
 	    strcpy(name, (((lp->chipset == DC21040) ? "DC21040" :
 			   ((lp->chipset == DC21041) ? "DC21041" :
-			    ((lp->chipset == DC21140) ? "DC21140" : "UNKNOWN"
-			     )))));
+			    ((lp->chipset == DC21140) ? "DC21140" :
+			     ((lp->chipset == DC21142) ? "DC21142" :
+			      ((lp->chipset == DC21143) ? "DC21143" : "UNKNOWN"
+			     )))))));
+	}
+	if (lp->chipset != DC21041) {
+	    useSROM = TRUE;             /* card is not recognisably DEC */
 	}
     }
     
@@ -3108,9 +3540,10 @@
     if (lp->chipset == DC21040) {
 	outl(0, aprom_addr);           /* Reset Ethernet Address ROM Pointer */
     } else {                           /* Read new srom */
-	short *p = (short *)&lp->srom;
+	u_short tmp, *p = (short *)&lp->srom;
 	for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
-	    *p++ = srom_rd(aprom_addr, i);
+	    tmp = srom_rd(aprom_addr, i);
+	    *p++ = le16_to_cpu(tmp);
 	}
 	de4x5_dbg_srom((struct de4x5_srom *)&lp->srom);
     }
@@ -3118,6 +3551,12 @@
     return;
 }
 
+/*
+** For the bad status case and no SROM, then add one to the previous
+** address. However, need to add one backwards in case we have 0xff
+** as one or more of the bytes. Only the last 3 bytes should be checked
+** as the first three are invariant - assigned to an organisation.
+*/
 static int
 get_hw_addr(struct device *dev)
 {
@@ -3171,6 +3610,12 @@
 	if ((k != chksum) && (dec_only)) status = -1;
     }
 
+    /* If possible, try to fix a broken card - SMC only so far */
+    srom_repair(dev, broken);
+
+    /* Test for a bad enet address */
+    status = test_bad_enet(dev, status);
+
     return status;
 }
 
@@ -3210,6 +3655,55 @@
     return ret;
 }
 
+static void
+srom_repair(struct device *dev, int card)
+{
+    struct bus_type *lp = &bus;
+
+    switch(card) {
+      case SMC:
+	memset((char *)&bus.srom, 0, sizeof(struct de4x5_srom));
+	memcpy(lp->srom.ieee_addr, (char *)dev->dev_addr, ETH_ALEN);
+	memcpy(lp->srom.info, (char *)&srom_repair_info[SMC-1], 100);
+	useSROM = TRUE;
+	break;
+    }
+
+    return;
+}
+
+static int
+test_bad_enet(struct device *dev, int status)
+{
+    struct bus_type *lp = &bus;
+    int i, tmp;
+
+    for (tmp=0,i=0; i<ETH_ALEN; i++) tmp += (u_char)dev->dev_addr[i];
+    if ((tmp == 0) || (tmp == 0x5fa)) {
+	if ((lp->chipset == last.chipset) && 
+	    (lp->bus_num == last.bus) && (lp->bus_num > 0)) {
+	    for (i=0; i<ETH_ALEN; i++) dev->dev_addr[i] = last.addr[i];
+	    for (i=ETH_ALEN-1; i>2; --i) {
+		dev->dev_addr[i] += 1;
+		if (dev->dev_addr[i] != 0) break;
+	    }
+	    for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
+	    if (((*((int *)dev->dev_addr) & 0x00ffffff) == 0x95c000) &&
+		(lp->chipset == DC21040)) {
+		dev->irq = last.irq;
+	    }
+	    status = 0;
+	}
+    } else if (!status) {
+	last.chipset = lp->chipset;
+	last.bus = lp->bus_num;
+	last.irq = dev->irq;
+	for (i=0; i<ETH_ALEN; i++) last.addr[i] = dev->dev_addr[i];
+    }
+
+    return status;
+}
+
 /*
 ** SROM Read
 */
@@ -3321,16 +3815,401 @@
     return tmp;
 }
 
-/*
-** MII Read/Write
-*/
-
 static int
-mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+srom_infoleaf_info(struct device *dev)
 {
-    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */
-    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */
-    mii_wdata(MII_STRD, 4, ioaddr);        /* SFD and Read operation         */
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int i, count;
+    u_char *p;
+
+    /* Find the infoleaf decoder function that matches this chipset */
+    for (i=0; i<INFOLEAF_SIZE; i++) {
+	if (lp->chipset == infoleaf_array[i].chipset) break;
+    }
+    if (i == INFOLEAF_SIZE) {
+	lp->useSROM = FALSE;
+	printk("%s: Cannot find correct chipset for SROM decoding!\n", 
+	                                                          dev->name);
+	return -ENXIO;
+    }
+
+    lp->infoleaf_fn = infoleaf_array[i].fn;
+
+    /* Find the information offset that this function should use */
+    count = *((u_char *)&lp->srom + 19);
+    p  = (u_char *)&lp->srom + 26;
+
+    if (count > 1) {
+	for (i=count; i; --i, p+=3) {
+	    if (lp->device == *p) break;
+	}
+	if (i == 0) {
+	    lp->useSROM = FALSE;
+	    printk("%s: Cannot find correct PCI device [%d] for SROM decoding!\n", 
+	                                               dev->name, lp->device);
+	    return -ENXIO;
+	}
+    }
+
+    lp->infoleaf_offset = TWIDDLE(p+1);
+
+    return 0;
+}
+
+/*
+** This routine loads any type 1 or 3 MII info into the mii device
+** struct and executes any type 5 code to reset PHY devices for this
+** controller.
+** The info for the MII devices will be valid since the index used
+** will follow the discovery process from MII address 1-31 then 0.
+*/
+static void
+srom_init(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+    u_char count;
+
+    p+=2;
+    if (lp->chipset == DC21140) {
+	lp->cache.gepc = (*p++ | GEP_CTRL);
+	outl(lp->cache.gepc, DE4X5_GEP);
+    }
+
+    /* Block count */
+    count = *p++;
+
+    /* Jump the infoblocks to find types */
+    for (;count; --count) {
+	if (*p < 128) {
+	    p += COMPACT_LEN;
+	} else if (*(p+1) == 5) {
+	    type5_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 3) {
+	    type3_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else if (*(p+1) == 1) {
+	    type1_infoblock(dev, 1, p);
+	    p += ((*p & BLOCK_LEN) + 1);
+	} else {
+	    p += ((*p & BLOCK_LEN) + 1);
+	}
+    }
+
+    return;
+}
+
+static void
+srom_exec(struct device *dev, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char count = (p ? *p++ : 0);
+
+    while (count--) {
+	if (lp->chipset == DC21140) {
+	    outl(*p++, DE4X5_GEP);
+	}
+	udelay(2000);                       /* 2ms per action */
+    }
+
+    return;
+}
+
+/*
+** Basically this function is a NOP since it will never be called,
+** unless I implement the DC21041 SROM functions. There's no need
+** since the existing code will be satisfactory for all boards.
+*/
+static int 
+dc21041_infoleaf(struct device *dev)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+dc21140_infoleaf(struct device *dev)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char count = 0;
+    u_char *p = (u_char *)&lp->srom + lp->infoleaf_offset;
+    int next_tick = DE4X5_AUTOSENSE_MS;
+
+    /* Read the connection type */
+    p+=2;
+
+    /* GEP control */
+    lp->cache.gepc = (*p++ | GEP_CTRL);
+
+    /* Block count */
+    count = *p++;
+
+    /* Recursively figure out the info blocks */
+    if (*p < 128) {
+	next_tick = dc_infoblock[COMPACT](dev, count, p);
+    } else {
+	next_tick = dc_infoblock[*(p+1)](dev, count, p);
+    }
+
+    if (lp->tcount == count) {
+	lp->media = NC;
+        if (lp->media != lp->c_media) {
+	    de4x5_dbg_media(dev);
+	    lp->c_media = lp->media;
+	}
+	lp->media = INIT;
+	lp->tcount = 0;
+	lp->tx_enable = FALSE;
+    }
+
+    return next_tick & ~TIMER_CB;
+}
+
+static int 
+dc21142_infoleaf(struct device *dev)
+{
+printk("dc21142_infoleaf()\n");
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+dc21143_infoleaf(struct device *dev)
+{
+printk("dc21143_infoleaf()\n");
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** The compact infoblock is only designed for DC21140[A] chips, so
+** we'll reuse the dc21140m_autoconf function. Non MII media only.
+*/
+static int 
+compact_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char flags, csr6;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+COMPACT_LEN) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+COMPACT_LEN);
+	} else {
+	    return dc_infoblock[*(p+COMPACT_LEN+1)](dev, count, p+COMPACT_LEN);
+	}
+    }
+
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
+        outl(lp->cache.gepc, DE4X5_GEP);
+	lp->infoblock_media = (*p++) & COMPACT_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	lp->defMedium = (flags & 0x40) ? -1 : 0;
+	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->useMII = FALSE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+/*
+** This block describes non MII media for the DC21140[A] only.
+ */
+static int 
+type0_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    if ((lp->media == INIT) && (lp->timeout < 0)) {
+        outl(lp->cache.gepc, DE4X5_GEP);
+	p+=2;
+	lp->infoblock_media = (*p++) & BLOCK0_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	lp->defMedium = (flags & 0x40) ? -1 : 0;
+	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->useMII = FALSE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+/* These functions are under construction! */
+
+static int 
+type1_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    p += 2;
+    if (lp->state == INITIALISED) {
+	lp->ibn = 1;
+	lp->active = *p++;
+	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ttm = TWIDDLE(p);
+	return 0;
+    } else if ((lp->media == INIT) && (lp->timeout < 0)) {
+        lp->ibn = 1;
+        lp->active = *p;
+	lp->infoblock_csr6 = OMR_PS | OMR_HBD;
+	lp->useMII = TRUE;
+	lp->infoblock_media = ANS;
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+static int 
+type2_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+static int 
+type3_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_char flags, csr6, len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    if (lp->state == INITIALISED) {
+	lp->ibn = 3; p += 2;
+        lp->active = *p++;
+	lp->phy[lp->active].gep = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].rst = (*p ? p : 0); p += (*p + 1);
+	lp->phy[lp->active].mc  = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ana = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].fdx = TWIDDLE(p); p += 2;
+	lp->phy[lp->active].ttm = TWIDDLE(p);
+	return 0;
+    } else if (lp->media == INIT) {
+	p+=2;
+	lp->infoblock_media = (*p++) & BLOCK0_MC;
+	lp->cache.gep = *p++;
+	csr6 = *p++;
+	flags = *p++;
+
+	lp->asBitValid = (flags & 0x80) ? 0 : -1;
+	lp->defMedium = (flags & 0x40) ? -1 : 0;
+	lp->asBit = 1 << ((csr6 >> 1) & 0x07);
+	lp->asPolarity = ((csr6 & 0x80) ? -1 : 0) & lp->asBit;
+	lp->infoblock_csr6 = (csr6 & 0x71) << 18;
+	lp->useMII = TRUE;
+
+	de4x5_switch_mac_port(dev);
+    }
+
+    return dc21140m_autoconf(dev);
+}
+
+static int 
+type4_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** This block type provides information for resetting external devices
+** (chips) through the General Purpose Register.
+*/
+static int 
+type5_infoblock(struct device *dev, u_char count, u_char *p)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    u_long iobase = dev->base_addr;
+    u_char i, j, len = (*p & BLOCK_LEN)+1;
+
+    /* Recursively figure out the info blocks */
+    if (--count > lp->tcount) {
+	if (*(p+len) < 128) {
+	    return dc_infoblock[COMPACT](dev, count, p+len);
+	} else {
+	    return dc_infoblock[*(p+len+1)](dev, count, p+len);
+	}
+    }
+
+    /* Must be initializing to run this code */
+    if ((lp->state == INITIALISED) || (lp->media == INIT)) {
+	p+=2;
+        lp->rst = p;
+	i = *p++;
+	for (j=0;i;--i) {
+	    if (lp->chipset == DC21140) {
+		if (!j) {
+		    outl(*p++ | GEP_CTRL, DE4X5_GEP);
+		    j++;
+		}
+		outl(*p++, DE4X5_GEP);
+	    } else if (lp->chipset == DC21142) {
+	    } else if (lp->chipset == DC21143) {
+	    }
+	}
+	    
+    }
+
+    return DE4X5_AUTOSENSE_MS;
+}
+
+/*
+** MII Read/Write
+*/
+
+static int
+mii_rd(u_char phyreg, u_char phyaddr, u_long ioaddr)
+{
+    mii_wdata(MII_PREAMBLE,  2, ioaddr);   /* Start of 34 bit preamble...    */
+    mii_wdata(MII_PREAMBLE, 32, ioaddr);   /* ...continued                   */
+    mii_wdata(MII_STRD, 4, ioaddr);        /* SFD and Read operation         */
     mii_address(phyaddr, ioaddr);          /* PHY address to be accessed     */
     mii_address(phyreg, ioaddr);           /* PHY Register to read           */
     mii_ta(MII_STRD, ioaddr);              /* Turn around time - 2 MDC       */
@@ -3494,23 +4373,25 @@
     return r2;                                  /* (I did it) My way */
 }
 
+/*
+** The SROM spec forces us to search addresses [1-31 0]. Bummer.
+*/
 static int
 mii_get_phy(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
-    int i, j, k, limit=sizeof(phy_info)/sizeof(struct phy_table);
+    int i, j, k, n, limit=sizeof(phy_info)/sizeof(struct phy_table);
     int id;
     
-    /* Issue a hard PHY reset - Broadcom is screwed up otherwise */
-    outl(GEP_HRST, DE4X5_GEP);
-    udelay(1000);                                  /* Assert for 1ms */
-    outl(0x00, DE4X5_GEP);
-    udelay(2000);                                  /* Wait for 2ms */
-    
-    /* Search the MII address space for possible PHY devices */
     lp->active = 0;
-    for (lp->mii_cnt=0, i=1; i<DE4X5_MAX_MII; i++) {
+    lp->useMII = TRUE;
+
+    /* Search the MII address space for possible PHY devices */
+    for (n=0, lp->mii_cnt=0, i=1; !((i==1) && (n==1)); i=(++i)%DE4X5_MAX_MII) {
+	lp->phy[lp->active].addr = i;
+	if (i==0) n++;                             /* Count cycles */
+	while (de4x5_reset_phy(dev)<0) udelay(100);/* Wait for reset */
 	id = mii_get_oui(i, DE4X5_MII); 
 	if ((id == 0) || (id == -1)) continue;     /* Valid ID? */
 	for (j=0; j<limit; j++) {                  /* Search PHY table */
@@ -3521,13 +4402,15 @@
 		       (char *)&phy_info[j], sizeof(struct phy_table));
 		lp->phy[k].addr = i;
 		lp->mii_cnt++;
+		lp->active++;
 	    } else {
 		i = DE4X5_MAX_MII;                 /* Stop the search */
 		j = limit;
 	    }
 	}
     }
-    if (lp->phy[lp->active].id) {                  /* Reset the PHY devices */
+    lp->active = 0;
+    if (lp->phy[0].id) {                           /* Reset the PHY devices */
 	for (k=0; lp->phy[k].id && (k < DE4X5_MAX_PHY); k++) { /*For each PHY*/
 	    mii_wr(MII_CR_RST, MII_CR, lp->phy[k].addr, DE4X5_MII);
 	    while (mii_rd(MII_CR, lp->phy[k].addr, DE4X5_MII) & MII_CR_RST);
@@ -3590,15 +4473,17 @@
 }
 
 static long
-de4x5_switch_to_mii(struct device *dev)
+de4x5_switch_mac_port(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
-    long omr;
-    
+    s32 omr;
+
     /* Assert the OMR_PS bit in CSR6 */
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
-    omr |= (OMR_PS | OMR_HBD);
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR |
+			                                             OMR_FDX));
+    omr |= lp->infoblock_csr6;
+    if (omr & OMR_PS) omr |= OMR_HBD;
     outl(omr, DE4X5_OMR);
     
     /* Soft Reset */
@@ -3606,42 +4491,13 @@
     
     /* Restore the GEP */
     if (lp->chipset == DC21140) {
-	outl(GEP_INIT, DE4X5_GEP);
-	outl(0, DE4X5_GEP);
+	outl(lp->cache.gepc, DE4X5_GEP);
+	outl(lp->cache.gep, DE4X5_GEP);
     }
-    
-    /* Restore CSR6 */
-    outl(omr, DE4X5_OMR);
 
-    /* Reset CSR8 */
-    inl(DE4X5_MFC);
-
-    return omr;
-}
-
-static long
-de4x5_switch_to_srl(struct device *dev)
-{
-    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
-    int iobase = dev->base_addr;
-    long omr;
-    
-    /* Deassert the OMR_PS bit in CSR6 */
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
-    outl(omr, DE4X5_OMR);
-    
-    /* Soft Reset */
-    RESET_DE4X5;
-    
-    /* Restore the GEP */
-    if (lp->chipset == DC21140) {
-	outl(GEP_INIT, DE4X5_GEP);
-	outl(0, DE4X5_GEP);
-    }
-    
     /* Restore CSR6 */
     outl(omr, DE4X5_OMR);
-    
+
     /* Reset CSR8 */
     inl(DE4X5_MFC);
 
@@ -3671,12 +4527,60 @@
 }
 
 static void
+yawn(struct device *dev, int state)
+{
+    struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+    int iobase = dev->base_addr;
+
+    if ((lp->chipset == DC21040) || (lp->chipset == DC21140)) return;
+
+    if(lp->bus == EISA) {
+	switch(state) {
+	  case WAKEUP:
+	    outb(WAKEUP, PCI_CFPM);
+	    de4x5_ms_delay(10);
+	    break;
+
+	  case SNOOZE:
+	    outb(SNOOZE, PCI_CFPM);
+	    break;
+
+	  case SLEEP:
+	    outl(0, DE4X5_SICR);
+	    outb(SLEEP, PCI_CFPM);
+	    break;
+	}
+    } else {
+	switch(state) {
+	  case WAKEUP:
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, WAKEUP);
+	    de4x5_ms_delay(10);
+	    break;
+
+	  case SNOOZE:
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, SNOOZE);
+	    break;
+
+	  case SLEEP:
+	    outl(0, DE4X5_SICR);
+	    pcibios_write_config_byte(lp->bus_num, lp->device << 3, 
+				      PCI_CFDA_PSM, SLEEP);
+	    break;
+	}
+    }
+
+    return;
+}
+
+static void
 de4x5_dbg_open(struct device *dev)
 {
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int i;
     
-    if (de4x5_debug > 1) {
+    if (de4x5_debug & DEBUG_OPEN) {
 	printk("%s: de4x5 opening with irq %d\n",dev->name,dev->irq);
 	printk("\tphysical address: ");
 	for (i=0;i<6;i++) {
@@ -3702,17 +4606,17 @@
 	printk("Descriptor buffers:\nRX: ");
 	for (i=0;i<lp->rxRingSize-1;i++){
 	    if (i < 3) {
-		printk("0x%8.8x  ",lp->rx_ring[i].buf);
+		printk("0x%8.8x  ",le32_to_cpu(lp->rx_ring[i].buf));
 	    }
 	}
-	printk("...0x%8.8x\n",lp->rx_ring[i].buf);
+	printk("...0x%8.8x\n",le32_to_cpu(lp->rx_ring[i].buf));
 	printk("TX: ");
 	for (i=0;i<lp->txRingSize-1;i++){
 	    if (i < 3) {
-		printk("0x%8.8x  ", lp->tx_ring[i].buf);
+		printk("0x%8.8x  ", le32_to_cpu(lp->tx_ring[i].buf));
 	    }
 	}
-	printk("...0x%8.8x\n", lp->tx_ring[i].buf);
+	printk("...0x%8.8x\n", le32_to_cpu(lp->tx_ring[i].buf));
 	printk("Ring size: \nRX: %d\nTX: %d\n", 
 	       (short)lp->rxRingSize, 
 	       (short)lp->txRingSize); 
@@ -3727,7 +4631,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     int iobase = dev->base_addr;
     
-    if (de4x5_debug > 2) {
+    if (de4x5_debug & DEBUG_MII) {
 	printk("\nMII CR:  %x\n",mii_rd(MII_CR,lp->phy[k].addr,DE4X5_MII));
 	printk("MII SR:  %x\n",mii_rd(MII_SR,lp->phy[k].addr,DE4X5_MII));
 	printk("MII ID0: %x\n",mii_rd(MII_ID0,lp->phy[k].addr,DE4X5_MII));
@@ -3754,7 +4658,7 @@
     struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
     
     if (lp->media != lp->c_media) {
-	if (de4x5_debug > 0) {
+	if (de4x5_debug & DEBUG_MEDIA) {
 	    if (lp->chipset != DC21140) {
 		printk("%s: media is %s\n", dev->name,
 		       (lp->media == NC  ? "unconnected!" :
@@ -3786,10 +4690,12 @@
 {
     int i;
 
-    if (de4x5_debug > 1) {
-	printk("Sub-system Vendor ID: %04x\n", (u_short)*(p->sub_vendor_id));
-	printk("Sub-system ID:        %04x\n", (u_short)*(p->sub_system_id));
+    if (de4x5_debug & DEBUG_SROM) {
+	printk("Sub-system Vendor ID: %04x\n", *((u_short *)p->sub_vendor_id));
+	printk("Sub-system ID:        %04x\n", *((u_short *)p->sub_system_id));
 	printk("ID Block CRC:         %02x\n", (u_char)(p->id_block_crc));
+	printk("SROM version:         %02x\n", (u_char)(p->version));
+	printk("# controllers:         %02x\n", (u_char)(p->num_controllers));
 
 	printk("Hardware Address:     ");
 	for (i=0;i<ETH_ALEN-1;i++) {
@@ -3810,7 +4716,7 @@
 {
     int i, j;
 
-    if (de4x5_debug > 2) {
+    if (de4x5_debug & DEBUG_RX) {
 	printk("R: %02x:%02x:%02x:%02x:%02x:%02x <- %02x:%02x:%02x:%02x:%02x:%02x len/SAP:%02x%02x [%d]\n",
 	       (u_char)skb->data[0],
 	       (u_char)skb->data[1],
@@ -3827,7 +4733,7 @@
 	       (u_char)skb->data[12],
 	       (u_char)skb->data[13],
 	       len);
-	if (de4x5_debug > 3) {
+	if (de4x5_debug & DEBUG_RX) {
 	    for (j=0; len>0;j+=16, len-=16) {
 		printk("    %03x: ",j);
 		for (i=0; i<16 && i<len; i++) {
@@ -3854,9 +4760,9 @@
     int i, j, status = 0;
     s32 omr;
     union {
-	u8  addr[(HASH_TABLE_LEN * ETH_ALEN)];
-	u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
-	u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+	u8  addr[144];
+	u16 sval[72];
+	u32 lval[36];
     } tmp;
     
     switch(ioc->cmd) {
@@ -3868,7 +4774,7 @@
 	for (i=0; i<ETH_ALEN; i++) {
 	    tmp.addr[i] = dev->dev_addr[i];
 	}
-	memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	copy_to_user(ioc->data, tmp.addr, ioc->len);
 	
 	break;
       case DE4X5_SET_HWADDR:           /* Set the hardware address */
@@ -3879,7 +4785,7 @@
 	if (!suser())
 	  break;
 	status = 0;
-	memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN);
+	copy_from_user(tmp.addr, ioc->data, ETH_ALEN);
 	for (i=0; i<ETH_ALEN; i++) {
 	    dev->dev_addr[i] = tmp.addr[i];
 	}
@@ -3918,39 +4824,12 @@
 	
 	break;
       case DE4X5_GET_MCA:              /* Get the multicast address table */
-	ioc->len = (HASH_TABLE_LEN >> 3);
-	status = verify_area(VERIFY_WRITE, ioc->data, ioc->len);
-	if (!status) {
-	    memcpy_tofs(ioc->data, lp->setup_frame, ioc->len); 
-	}
-	
 	break;
       case DE4X5_SET_MCA:              /* Set a multicast address */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    if (ioc->len != HASH_TABLE_LEN) {         /* MCA changes */
-		if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
-		    memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
-		    set_multicast_list(dev);
-		}
-	    } else {
-		set_multicast_list(dev);
-	    }
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
       case DE4X5_CLR_MCA:              /* Clear all multicast addresses */
-	if (suser()) {
-	    /******* FIX ME! ********/
-	    set_multicast_list(dev);
-	} else {
-	    status = -EPERM;
-	}
-	
 	break;
-      case DE4X5_MCA_EN:               /* Enable pass all multicast addressing */
+      case DE4X5_MCA_EN:               /* Enable pass all multicast addresses*/
 	if (suser()) {
 	    omr = inl(DE4X5_OMR);
 	    omr |= OMR_PM;
@@ -3967,7 +4846,7 @@
 	  break;
 	
 	cli();
-	memcpy_tofs(ioc->data, &lp->pktStats, ioc->len); 
+	copy_to_user(ioc->data, &lp->pktStats, ioc->len); 
 	sti();
 	
 	break;
@@ -3984,14 +4863,14 @@
       case DE4X5_GET_OMR:              /* Get the OMR Register contents */
 	tmp.addr[0] = inl(DE4X5_OMR);
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
-	    memcpy_tofs(ioc->data, tmp.addr, 1);
+	    copy_to_user(ioc->data, tmp.addr, 1);
 	}
 	
 	break;
       case DE4X5_SET_OMR:              /* Set the OMR Register contents */
 	if (suser()) {
 	    if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
-		memcpy_fromfs(tmp.addr, ioc->data, 1);
+		copy_from_user(tmp.addr, ioc->data, 1);
 		outl(tmp.addr[0], DE4X5_OMR);
 	    }
 	} else {
@@ -4011,11 +4890,12 @@
 	tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
 	ioc->len = j;
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	    copy_to_user(ioc->data, tmp.addr, ioc->len);
 	}
 	break;
 	
-#define DE4X5_DUMP              0x0f /* Dump the DE4X5 Status */
+/*
+#define DE4X5_DUMP              0x0f / * Dump the DE4X5 Status * /
 	
       case DE4X5_DUMP:
 	j = 0;
@@ -4042,22 +4922,22 @@
 	
 	for (i=0;i<lp->rxRingSize-1;i++){
 	    if (i < 3) {
-		tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
 	    }
 	}
-	tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->rx_ring[i].buf); j+=4;
 	for (i=0;i<lp->txRingSize-1;i++){
 	    if (i < 3) {
-		tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+		tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
 	    }
 	}
-	tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+	tmp.lval[j>>2] = (s32)le32_to_cpu(lp->tx_ring[i].buf); j+=4;
 	
 	for (i=0;i<lp->rxRingSize;i++){
-	    tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+	    tmp.lval[j>>2] = le32_to_cpu(lp->rx_ring[i].status); j+=4;
 	}
 	for (i=0;i<lp->txRingSize;i++){
-	    tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+	    tmp.lval[j>>2] = le32_to_cpu(lp->tx_ring[i].status); j+=4;
 	}
 	
 	tmp.lval[j>>2] = inl(DE4X5_BMR);  j+=4;
@@ -4078,7 +4958,7 @@
 	    tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4; 
 	}
 	tmp.lval[j>>2] = lp->phy[lp->active].id; j+=4; 
-	if (lp->phy[lp->active].id) {
+	if (lp->phy[lp->active].id && (!lp->useSROM || lp->useMII)) {
 	    tmp.lval[j>>2] = lp->active; j+=4; 
 	    tmp.lval[j>>2]=mii_rd(MII_CR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
 	    tmp.lval[j>>2]=mii_rd(MII_SR,lp->phy[lp->active].addr,DE4X5_MII); j+=4;
@@ -4102,10 +4982,11 @@
 	
 	ioc->len = j;
 	if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
-	    memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+	    copy_to_user(ioc->data, tmp.addr, ioc->len);
 	}
 	
 	break;
+*/
       default:
 	status = -EOPNOTSUPP;
     }
@@ -4118,85 +4999,60 @@
 ** Note now that module autoprobing is allowed under EISA and PCI. The
 ** IRQ lines will not be auto-detected; instead I'll rely on the BIOSes
 ** to "do the right thing".
-**
-** NB: Current register_netdevice() code does not permit assigning io=0 as
-** this driver will autoprobe all instances of acceptable DECchips. The
-** cleanup_module() code needs work still....(just to unload modules owned
-** by this driver).
-*/
-static char devicename[9] = { 0, };
-static struct device thisDE4X5 = {
-    devicename,   /* device name inserted by /linux/drivers/net/net_init.c  */
-    0, 0, 0, 0,
-    0, 0,         /* I/O address, IRQ                                       */
-    0, 0, 0, NULL, de4x5_probe };
-
-static int io=0x0b; /* EDIT THESE LINES FOR YOUR CONFIGURATION              */
+*/
+#define LP(a) ((struct de4x5_private *)(a))
+static struct device *mdev = NULL;
+static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED        */
 
 int
 init_module(void)
 {
-    struct device *p  = (struct device *)&thisDE4X5;
-    
-    thisDE4X5.base_addr = io;                   /* Now autoprobe the module */
-    thisDE4X5.irq = 0;
-    
-    for (; p!=NULL; p=p->next) {
+    struct device *p;
+
+    if ((mdev = insert_device(NULL, io, de4x5_probe)) == NULL) 
+      return -ENOMEM;
+
+    for (p = mdev; p != NULL; p = LP(p->priv)->next_module) {
 	if (register_netdev(p) != 0)
 	  return -EIO;
     }
-    io=0;
+
     return 0;
 }
 
 void
 cleanup_module(void)
 {
-    struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
-    struct device *p  = (struct device *)&thisDE4X5;
-    int keep_loaded = 0;
-    
-    for (; p!=NULL; p=p->next) {
-	keep_loaded += (p->flags & IFF_UP);     /* Is an interface up?       */
-    }
-    
-    if (keep_loaded) {
-	printk("de4x5: Cannot unload modules - %d interface%s%s still active.\n",
-	       keep_loaded, (keep_loaded>1 ? "s ": " "),
-	       (keep_loaded>1 ? "are": "is"));
-	return;
+    while (mdev != NULL) {
+	mdev = unlink_modules(mdev);
     }
-    
-    for (p=thisDE4X5.next; p!=NULL; p=p->next) {
-	if (p->priv) {                          /* Private area allocated?   */
-	    struct de4x5_private *lp = (struct de4x5_private *)p->priv;
-	    if (lp->cache.buf) {                /* MAC buffers allocated?    */
-		kfree(lp->cache.buf);           /* Free the MAC buffers      */
-	    }
-	    release_region(p->base_addr, (lp->bus == PCI ? 
-					  DE4X5_PCI_TOTAL_SIZE :
-					  DE4X5_EISA_TOTAL_SIZE));
-	    kfree(lp->cache.priv);              /* Free the private area     */
-	}
-	unregister_netdev(p);
-	kfree(p);                               /* Free the device structure */
-    }
-
-    if (thisDE4X5.priv) {
-	if (lp->cache.buf) {                    /* Are MAC buffers allocated */
-            kfree(lp->cache.buf);
-	}
-	release_region(thisDE4X5.base_addr,
-		      (lp->bus == PCI ? 
-		       DE4X5_PCI_TOTAL_SIZE :
-		       DE4X5_EISA_TOTAL_SIZE));
-	kfree(lp->cache.priv);
-	thisDE4X5.priv = NULL;
-    }
-    unregister_netdev(&thisDE4X5);
 
     return;
 }
+
+static struct device *
+unlink_modules(struct device *p)
+{
+    struct device *next = NULL;
+
+    if (p->priv) {                          /* Private areas allocated?  */
+	struct de4x5_private *lp = (struct de4x5_private *)p->priv;
+
+	next = lp->next_module;
+	if (lp->cache.buf) {                /* MAC buffers allocated?    */
+	    kfree(lp->cache.buf);           /* Free the MAC buffers      */
+	}
+	kfree(lp->cache.priv);              /* Free the private area     */
+	release_region(p->base_addr, (lp->bus == PCI ? 
+				      DE4X5_PCI_TOTAL_SIZE :
+				      DE4X5_EISA_TOTAL_SIZE));
+    }
+    unregister_netdev(p);
+    kfree(p);                               /* Free the device structure */
+    
+    return next;
+}
+
 #endif /* MODULE */
 
 
diff -urN linux-2.0.31-pre4/drivers/net/de4x5.h linux-2.0.31-pre5/drivers/net/de4x5.h
--- linux-2.0.31-pre4/drivers/net/de4x5.h	1996-08-20 23:18:08.000000000 -0700
+++ linux-2.0.31-pre5/drivers/net/de4x5.h	2003-08-15 15:03:58.000000000 -0700
@@ -62,6 +62,8 @@
 #define PCI_CBER     iobase+0x0030   /* PCI Expansion ROM Base Address Reg. */
 #define PCI_CFIT     iobase+0x003c   /* PCI Configuration Interrupt Register */
 #define PCI_CFDA     iobase+0x0040   /* PCI Driver Area Register */
+#define PCI_CFDD     iobase+0x0041   /* PCI Driver Dependent Area Register */
+#define PCI_CFPM     iobase+0x0043   /* PCI Power Management Area Register */
 
 /*
 ** EISA Configuration Register 0 bit definitions
@@ -95,16 +97,20 @@
 #define ER3_LSR       0x02           /* Local Software Reset */
 
 /*
-** PCI Configuration ID Register (PCI_CFID)
+** PCI Configuration ID Register (PCI_CFID). The Device IDs are left
+** shifted 8 bits to allow detection of DC21142 and DC21143 variants with
+** the configuration revision register step number.
 */
 #define CFID_DID    0xff00           /* Device ID */
 #define CFID_VID    0x00ff           /* Vendor ID */
-#define DC21040_DID 0x0002           /* Unique Device ID # */
+#define DC21040_DID 0x0200           /* Unique Device ID # */
 #define DC21040_VID 0x1011           /* DC21040 Manufacturer */
-#define DC21041_DID 0x0014           /* Unique Device ID # */
+#define DC21041_DID 0x1400           /* Unique Device ID # */
 #define DC21041_VID 0x1011           /* DC21041 Manufacturer */
-#define DC21140_DID 0x0009           /* Unique Device ID # */
+#define DC21140_DID 0x0900           /* Unique Device ID # */
 #define DC21140_VID 0x1011           /* DC21140 Manufacturer */
+#define DC2114x_DID 0x1900           /* Unique Device ID # */
+#define DC2114x_VID 0x1011           /* DC2114[23] Manufacturer */
 
 /*
 ** Chipset defines
@@ -112,10 +118,16 @@
 #define DC21040     DC21040_DID
 #define DC21041     DC21041_DID
 #define DC21140     DC21140_DID
+#define DC2114x     DC2114x_DID
+#define DC21142     (DC2114x_DID | 0x0010)
+#define DC21143     (DC2114x_DID | 0x0020)
 
 #define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
 #define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
 #define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID))
+#define is_DC2114x ((vendor == DC2114x_VID) && (device == DC2114x_DID))
+#define is_DC21142 ((vendor == DC2114x_VID) && (device == DC21142))
+#define is_DC21143 ((vendor == DC2114x_VID) && (device == DC21143))
 
 /*
 ** PCI Configuration Command/Status Register (PCI_CFCS)
@@ -127,7 +139,7 @@
 #define CFCS_DST    0x06000000       /* DEVSEL Timing         (S) */
 #define CFCS_DPR    0x01000000       /* Data Parity Report    (S) */
 #define CFCS_FBB    0x00800000       /* Fast Back-To-Back     (S) */
-#define CFCS_SLE    0x00000100       /* System Error Enable   (C) */
+#define CFCS_SEE    0x00000100       /* System Error Enable   (C) */
 #define CFCS_PER    0x00000040       /* Parity Error Response (C) */
 #define CFCS_MO     0x00000004       /* Master Operation      (C) */
 #define CFCS_MSA    0x00000002       /* Memory Space Access   (C) */
@@ -138,8 +150,8 @@
 */
 #define CFRV_BC     0xff000000       /* Base Class */
 #define CFRV_SC     0x00ff0000       /* Subclass */
-#define CFRV_SN     0x000000f0       /* Step Number */
-#define CFRV_RN     0x0000000f       /* Revision Number */
+#define CFRV_RN     0x000000f0       /* Revision Number */
+#define CFRV_SN     0x0000000f       /* Step Number */
 #define BASE_CLASS  0x02000000       /* Indicates Network Controller */
 #define SUB_CLASS   0x00000000       /* Indicates Ethernet Controller */
 #define STEP_NUMBER 0x00000020       /* Increments for future chips */
@@ -158,19 +170,46 @@
 #define CBIO_IOSI   0x00000001       /* I/O Space Indicator (RO, value is 1) */
 
 /*
+** PCI Configuration Card Information Structure Register (PCI_CCIS)
+*/
+#define CCIS_ROMI   0xf0000000       /* ROM Image */
+#define CCIS_ASO    0x0ffffff8       /* Address Space Offset */
+#define CCIS_ASI    0x00000007       /* Address Space Indicator */
+
+/*
+** PCI Configuration Subsystem ID Register (PCI_SSID)
+*/
+#define SSID_SSID   0xffff0000       /* Subsystem ID */
+#define SSID_SVID   0x0000ffff       /* Subsystem Vendor ID */
+
+/*
 ** PCI Configuration Expansion ROM Base Address Register (PCI_CBER)
 */
 #define CBER_MASK   0xfffffc00       /* Expansion ROM Base Address Mask */
 #define CBER_ROME   0x00000001       /* ROM Enable */
 
 /*
-** PCI Configuration Driver Area Register (PCI_CFDA)
+** PCI Configuration Interrupt Register (PCI_CFIT)
 */
-#define CFDA_PSM    0x80000000       /* Power Saving Mode */
+#define CFIT_MXLT   0xff000000       /* MAX_LAT Value (0.25us periods) */
+#define CFIT_MNGT   0x00ff0000       /* MIN_GNT Value (0.25us periods) */
+#define CFIT_IRQP   0x0000ff00       /* Interrupt Pin */
+#define CFIT_IRQL   0x000000ff       /* Interrupt Line */
+
+/*
+** PCI Configuration Power Management Area Register (PCI_CFPM)
+*/
+#define SLEEP       0x80             /* Power Saving Sleep Mode */
+#define SNOOZE      0x40             /* Power Saving Snooze Mode */
+#define WAKEUP      0x00             /* Power Saving Wakeup */
+
+#define PCI_CFDA_DSU 0x41            /* 8 bit Configuration Space Address */
+#define PCI_CFDA_PSM 0x43            /* 8 bit Configuration Space Address */
 
 /*
 ** DC21040 Bus Mode Register (DE4X5_BMR)
 */
+#define BMR_RML    0x00200000       /* [Memory] Read Multiple */
 #define BMR_DBO    0x00100000       /* Descriptor Byte Ordering (Endian) */
 #define BMR_TAP    0x000e0000       /* Transmit Automatic Polling */
 #define BMR_DAS    0x00010000       /* Diagnostic Address Space */
@@ -181,6 +220,7 @@
 #define BMR_BAR    0x00000002       /* Bus ARbitration */
 #define BMR_SWR    0x00000001       /* Software Reset */
 
+                                    /* Timings here are for 10BASE-T/AUI only*/
 #define TAP_NOPOLL 0x00000000       /* No automatic polling */
 #define TAP_200US  0x00020000       /* TX automatic polling every 200us */
 #define TAP_800US  0x00040000       /* TX automatic polling every 800us */
@@ -232,18 +272,21 @@
 #define TRBA       0xfffffffc       /* TX Descriptor List Start Address */
 
 /*
-** DC21040 Status Register (DE4X5_STS)
+** Status Register (DE4X5_STS)
 */
+#define STS_GPI    0x04000000       /* General Purpose Port Interrupt */
 #define STS_BE     0x03800000       /* Bus Error Bits */
 #define STS_TS     0x00700000       /* Transmit Process State */
 #define STS_RS     0x000e0000       /* Receive Process State */
 #define STS_NIS    0x00010000       /* Normal Interrupt Summary */
 #define STS_AIS    0x00008000       /* Abnormal Interrupt Summary */
 #define STS_ER     0x00004000       /* Early Receive */
+#define STS_FBE    0x00002000       /* Fatal Bus Error */
 #define STS_SE     0x00002000       /* System Error */
 #define STS_LNF    0x00001000       /* Link Fail */
 #define STS_FD     0x00000800       /* Full-Duplex Short Frame Received */
 #define STS_TM     0x00000800       /* Timer Expired (DC21041) */
+#define STS_ETI    0x00000400       /* Early Transmit Interupt */
 #define STS_AT     0x00000400       /* AUI/TP Pin */
 #define STS_RWT    0x00000200       /* Receive Watchdog Time-Out */
 #define STS_RPS    0x00000100       /* Receive Process Stopped */
@@ -251,6 +294,7 @@
 #define STS_RI     0x00000040       /* Receive Interrupt */
 #define STS_UNF    0x00000020       /* Transmit Underflow */
 #define STS_LNP    0x00000010       /* Link Pass */
+#define STS_ANC    0x00000010       /* Autonegotiation Complete */
 #define STS_TJT    0x00000008       /* Transmit Jabber Time-Out */
 #define STS_TU     0x00000004       /* Transmit Buffer Unavailable */
 #define STS_TPS    0x00000002       /* Transmit Process Stopped */
@@ -283,8 +327,10 @@
 #define INT_CANCEL 0x0001ffff       /* For zeroing all interrupt sources */
 
 /*
-** DC21040 Operation Mode Register (DE4X5_OMR)
+** Operation Mode Register (DE4X5_OMR)
 */
+#define OMR_SC     0x80000000       /* Special Capture Effect Enable */
+#define OMR_RA     0x40000000       /* Receive All */
 #define OMR_SDP    0x02000000       /* SD Polarity - MUST BE ASSERTED */
 #define OMR_SCR    0x01000000       /* Scrambler Mode */
 #define OMR_PCS    0x00800000       /* PCS Function */
@@ -298,7 +344,7 @@
 #define OMR_ST     0x00002000       /* Start/Stop Transmission Command */
 #define OMR_FC     0x00001000       /* Force Collision Mode */
 #define OMR_OM     0x00000c00       /* Operating Mode */
-#define OMR_FD     0x00000200       /* Full Duplex Mode */
+#define OMR_FDX    0x00000200       /* Full Duplex Mode */
 #define OMR_FKD    0x00000100       /* Flaky Oscillator Disable */
 #define OMR_PM     0x00000080       /* Pass All Multicast */
 #define OMR_PR     0x00000040       /* Promiscuous Mode */
@@ -309,27 +355,31 @@
 #define OMR_SR     0x00000002       /* Start/Stop Receive */
 #define OMR_HP     0x00000001       /* Hash/Perfect Receive Filtering Mode */
 
-#define TR_72      0x00000000       /* Threshold set to 72 bytes */
-#define TR_96      0x00004000       /* Threshold set to 96 bytes */
-#define TR_128     0x00008000       /* Threshold set to 128 bytes */
-#define TR_160     0x0000c000       /* Threshold set to 160 bytes */
+#define TR_72      0x00000000       /* Threshold set to 72 (128) bytes */
+#define TR_96      0x00004000       /* Threshold set to 96 (256) bytes */
+#define TR_128     0x00008000       /* Threshold set to 128 (512) bytes */
+#define TR_160     0x0000c000       /* Threshold set to 160 (1024) bytes */
 
 /*
 ** DC21040 Interrupt Mask Register (DE4X5_IMR)
 */
+#define IMR_GPM    0x04000000       /* General Purpose Port Mask */
 #define IMR_NIM    0x00010000       /* Normal Interrupt Summary Mask */
 #define IMR_AIM    0x00008000       /* Abnormal Interrupt Summary Mask */
 #define IMR_ERM    0x00004000       /* Early Receive Mask */
+#define IMR_FBM    0x00002000       /* Fatal Bus Error Mask */
 #define IMR_SEM    0x00002000       /* System Error Mask */
 #define IMR_LFM    0x00001000       /* Link Fail Mask */
 #define IMR_FDM    0x00000800       /* Full-Duplex (Short Frame) Mask */
 #define IMR_TMM    0x00000800       /* Timer Expired Mask (DC21041) */
+#define IMR_ETM    0x00000400       /* Early Transmit Interrupt Mask */
 #define IMR_ATM    0x00000400       /* AUI/TP Switch Mask */
 #define IMR_RWM    0x00000200       /* Receive Watchdog Time-Out Mask */
 #define IMR_RSM    0x00000100       /* Receive Stopped Mask */
 #define IMR_RUM    0x00000080       /* Receive Buffer Unavailable Mask */
 #define IMR_RIM    0x00000040       /* Receive Interrupt Mask */
 #define IMR_UNM    0x00000020       /* Underflow Interrupt Mask */
+#define IMR_ANM    0x00000010       /* Autonegotiation Complete Mask */
 #define IMR_LPM    0x00000010       /* Link Pass */
 #define IMR_TJM    0x00000008       /* Transmit Time-Out Jabber Mask */
 #define IMR_TUM    0x00000004       /* Transmit Buffer Unavailable Mask */
@@ -337,13 +387,7 @@
 #define IMR_TIM    0x00000001       /* Transmit Interrupt Mask */
 
 /*
-** DC21040 Missed Frames Counter (DE4X5_MFC)
-*/
-#define MFC_OVFL   0x00010000       /* Missed Frames Counter Overflow Bit */
-#define MFC_CNTR   0x0000ffff       /* Missed Frames Counter Bits */
-
-/*
-** DC21140 Missed Frames and FIFO Overflow Counters (DE4X5_MFC)
+** Missed Frames and FIFO Overflow Counters (DE4X5_MFC)
 */
 #define MFC_FOCO   0x10000000       /* FIFO Overflow Counter Overflow Bit */
 #define MFC_FOC    0x0ffe0000       /* FIFO Overflow Counter Bits */
@@ -444,7 +488,7 @@
 ** MII Management Auto Negotiation Advertisement Register
 */
 #define MII_ANA_TAF  0x03e0        /* Technology Ability Field */
-#define MII_ANA_T4AM 0x0400        /* T4 Technology Ability Mask */
+#define MII_ANA_T4AM 0x0200        /* T4 Technology Ability Mask */
 #define MII_ANA_TXAM 0x0180        /* TX Technology Ability Mask */
 #define MII_ANA_FDAM 0x0140        /* Full Duplex Technology Ability Mask */
 #define MII_ANA_HDAM 0x02a0        /* Half Duplex Technology Ability Mask */
@@ -459,7 +503,7 @@
 #define MII_ANLPA_ACK  0x4000      /* Remote Acknowledge */
 #define MII_ANLPA_RF   0x2000      /* Remote Fault */
 #define MII_ANLPA_TAF  0x03e0      /* Technology Ability Field */
-#define MII_ANLPA_T4AM 0x0400      /* T4 Technology Ability Mask */
+#define MII_ANLPA_T4AM 0x0200      /* T4 Technology Ability Mask */
 #define MII_ANLPA_TXAM 0x0180      /* TX Technology Ability Mask */
 #define MII_ANLPA_FDAM 0x0140      /* Full Duplex Technology Ability Mask */
 #define MII_ANLPA_HDAM 0x02a0      /* Half Duplex Technology Ability Mask */
@@ -478,6 +522,76 @@
 #define MEDIA_BNC      0x0001      /* BNC Media present */
 
 /*
+** SROM Definitions (Digital Semiconductor Format)
+*/
+#define SROM_SSVID     0x0000      /* Sub-system Vendor ID offset */
+#define SROM_SSID      0x0002      /* Sub-system ID offset */
+#define SROM_CISPL     0x0004      /* CardBus CIS Pointer low offset */
+#define SROM_CISPH     0x0006      /* CardBus CIS Pointer high offset */
+#define SROM_IDCRC     0x0010      /* ID Block CRC offset*/
+#define SROM_RSVD2     0x0011      /* ID Reserved 2 offset */
+#define SROM_SFV       0x0012      /* SROM Format Version offset */
+#define SROM_CCNT      0x0013      /* Controller Count offset */
+#define SROM_HWADD     0x0014      /* Hardware Address offset */
+#define SROM_MRSVD     0x007c      /* Manufacturer Reserved offset*/
+#define SROM_CRC       0x007e      /* SROM CRC offset */
+
+/*
+** SROM Media Connection Definitions
+*/
+#define SROM_10BT      0x0000      /*  10BASE-T half duplex */
+#define SROM_10BTN     0x0100      /*  10BASE-T with Nway */
+#define SROM_10BTF     0x0204      /*  10BASE-T full duplex */
+#define SROM_10BTNLP   0x0400      /*  10BASE-T without Link Pass test */
+#define SROM_10B2      0x0001      /*  10BASE-2 (BNC) */
+#define SROM_10B5      0x0002      /*  10BASE-5 (AUI) */
+#define SROM_100BTH    0x0003      /*  100BASE-T half duplex */
+#define SROM_100BTF    0x0205      /*  100BASE-T full duplex */
+#define SROM_100BT4    0x0006      /*  100BASE-T4 */
+#define SROM_100BFX    0x0007      /*  100BASE-FX half duplex (Fiber) */
+#define SROM_M10BT     0x0009      /*  MII 10BASE-T half duplex */
+#define SROM_M10BTF    0x020a      /*  MII 10BASE-T full duplex */
+#define SROM_M100BT    0x000d      /*  MII 100BASE-T half duplex */
+#define SROM_M100BTF   0x020e      /*  MII 100BASE-T full duplex */
+#define SROM_M100BT4   0x000f      /*  MII 100BASE-T4 */
+#define SROM_M100BF    0x0010      /*  MII 100BASE-FX half duplex */
+#define SROM_M100BFF   0x0211      /*  MII 100BASE-FX full duplex */
+#define SROM_PDA       0x0800      /*  Powerup & Dynamic Autosense */
+#define SROM_PAO       0x8800      /*  Powerup Autosense Only */
+#define SROM_NSMI      0xffff      /*  No Selected Media Information */
+
+/*
+** SROM Media Definitions
+*/
+#define SROM_10BASET   0x0000      /*  10BASE-T half duplex */
+#define SROM_10BASE2   0x0001      /*  10BASE-2 (BNC) */
+#define SROM_10BASE5   0x0002      /*  10BASE-5 (AUI) */
+#define SROM_100BASET  0x0003      /*  100BASE-T half duplex */
+#define SROM_10BASETF  0x0004      /*  10BASE-T full duplex */
+#define SROM_100BASETF 0x0005      /*  100BASE-T full duplex */
+#define SROM_100BASET4 0x0006      /*  100BASE-T4 */
+#define SROM_100BASEF  0x0007      /*  100BASE-FX half duplex */
+#define SROM_100BASEFF 0x0008      /*  100BASE-FX full duplex */
+
+#define BLOCK_LEN      0x7f        /* Extended blocks length mask */
+#define EXT_FIELD      0x40        /* Extended blocks extension field bit */
+#define MEDIA_CODE     0x3f        /* Extended blocks media code mask */
+
+/*
+** SROM Compact Format Block Masks
+*/
+#define COMPACT_FI      0x80       /* Format Indicator */
+#define COMPACT_LEN     0x04       /* Length */
+#define COMPACT_MC      0x3f       /* Media Code */
+
+/*
+** SROM Extended Format Block Type 0 Masks
+*/
+#define BLOCK0_FI      0x80        /* Format Indicator */
+#define BLOCK0_MCS     0x80        /* Media Code byte Sign */
+#define BLOCK0_MC      0x3f        /* Media Code */
+
+/*
 ** DC21040 Full Duplex Register (DE4X5_FDR)
 */
 #define FDR_FDACV  0x0000ffff      /* Full Duplex Auto Configuration Value */
@@ -501,17 +615,21 @@
 #define GEP_FLED 0x00000002        /* Force Activity LED on   (output) */
 #define GEP_MODE 0x00000001        /* 0: 10Mb/s,  1: 100Mb/s           */
 #define GEP_INIT 0x0000011f        /* Setup inputs (0) and outputs (1) */
-
+#define GEP_CTRL 0x00000100        /* GEP control bit                  */
 
 /*
-** DC21040 SIA Status Register (DE4X5_SISR)
+** SIA Status Register (DE4X5_SISR)
 */
 #define SISR_LPC   0xffff0000      /* Link Partner's Code Word */
 #define SISR_LPN   0x00008000      /* Link Partner Negotiable */
 #define SISR_ANS   0x00007000      /* Auto Negotiation Arbitration State */
-#define SISR_NSN   0x00000800      /* Non Stable NLPs Detected */
+#define SISR_NSN   0x00000800      /* Non Stable NLPs Detected (DC21041) */
+#define SISR_TRF   0x00000800      /* Transmit Remote Fault */
+#define SISR_NSND  0x00000400      /* Non Stable NLPs Detected (DC21142) */
 #define SISR_ANR_FDS 0x00000400    /* Auto Negotiate Restart/Full Duplex Sel.*/
+#define SISR_TRA   0x00000200      /* 10BASE-T Receive Port Activity */
 #define SISR_NRA   0x00000200      /* Non Selected Port Receive Activity */
+#define SISR_ARA   0x00000100      /* AUI Receive Port Activity */
 #define SISR_SRA   0x00000100      /* Selected Port Receive Activity */
 #define SISR_DAO   0x00000080      /* PLL All One */
 #define SISR_DAZ   0x00000040      /* PLL All Zero */
@@ -521,7 +639,7 @@
 #define SISR_LKF   0x00000004      /* Link Fail Status */
 #define SISR_NCR   0x00000002      /* Network Connection Error */
 #define SISR_PAUI  0x00000001      /* AUI_TP Indication */
-#define SIA_RESET  0x00000000      /* SIA Reset */
+#define SISR_MRA   0x00000001      /* MII Receive Port Activity */
 
 #define ANS_NDIS   0x00000000      /* Nway disable */
 #define ANS_TDIS   0x00001000      /* Transmit Disable */
@@ -532,7 +650,7 @@
 #define ANS_LCHK   0x00006000      /* Link Check */
 
 /*
-** DC21040 SIA Connectivity Register (DE4X5_SICR)
+** SIA Connectivity Register (DE4X5_SICR)
 */
 #define SICR_SDM   0xffff0000       /* SIA Diagnostics Mode */
 #define SICR_OE57  0x00008000       /* Output Enable 5 6 7 */
@@ -551,14 +669,14 @@
 #define SICR_SIM   0x00000040       /* Serial Interface Input Multiplexer */
 #define SICR_ENI   0x00000020       /* Encoder Input Multiplexer */
 #define SICR_EDP   0x00000010       /* SIA PLL External Input Enable */
-#define SICR_AUI   0x00000008       /* 10Base-T or AUI */
+#define SICR_AUI   0x00000008       /* 10Base-T (0) or AUI (1) */
 #define SICR_CAC   0x00000004       /* CSR Auto Configuration */
 #define SICR_PS    0x00000002       /* Pin AUI/TP Selection */
 #define SICR_SRL   0x00000001       /* SIA Reset */
-#define SICR_RESET 0xffff0000       /* Reset value for SICR */
+#define SIA_RESET  0x00000000       /* SIA Reset Value */
 
 /*
-** DC21040 SIA Transmit and Receive Register (DE4X5_STRR)
+** SIA Transmit and Receive Register (DE4X5_STRR)
 */
 #define STRR_TAS   0x00008000       /* 10Base-T/AUI Autosensing Enable */
 #define STRR_SPP   0x00004000       /* Set Polarity Plus */
@@ -578,8 +696,20 @@
 #define STRR_RESET 0xffffffff       /* Reset value for STRR */
 
 /*
-** DC21040 SIA General Register (DE4X5_SIGR)
+** SIA General Register (DE4X5_SIGR)
 */
+#define SIGR_RMI   0x40000000       /* Receive Match Interrupt */
+#define SIGR_GI1   0x20000000       /* General Port Interrupt 1 */
+#define SIGR_GI0   0x10000000       /* General Port Interrupt 0 */
+#define SIGR_CWE   0x08000000       /* Control Write Enable */
+#define SIGR_RME   0x04000000       /* Receive Match Enable */
+#define SIGR_GEI1  0x02000000       /* GEP Interrupt Enable on Port 1 */
+#define SIGR_GEI0  0x01000000       /* GEP Interrupt Enable on Port 0 */
+#define SIGR_LGS3  0x00800000       /* LED/GEP3 Select */
+#define SIGR_LGS2  0x00400000       /* LED/GEP2 Select */
+#define SIGR_LGS1  0x00200000       /* LED/GEP1 Select */
+#define SIGR_LGS0  0x00100000       /* LED/GEP0 Select */
+#define SIGR_MD    0x000f0000       /* General Purpose Mode and Data */
 #define SIGR_LV2   0x00008000       /* General Purpose LED2 value */
 #define SIGR_LE2   0x00004000       /* General Purpose LED2 enable */
 #define SIGR_FRL   0x00002000       /* Force Receiver Low */
@@ -602,7 +732,8 @@
 ** Receive Descriptor Bit Summary
 */
 #define R_OWN      0x80000000       /* Own Bit */
-#define RD_FL      0x7fff0000       /* Frame Length */
+#define RD_FF      0x40000000       /* Filtering Fail */
+#define RD_FL      0x3fff0000       /* Frame Length */
 #define RD_ES      0x00008000       /* Error Summary */
 #define RD_LE      0x00004000       /* Length Error */
 #define RD_DT      0x00003000       /* Data Type */
@@ -614,6 +745,7 @@
 #define RD_CS      0x00000040       /* Collision Seen */
 #define RD_FT      0x00000020       /* Frame Type */
 #define RD_RJ      0x00000010       /* Receive Watchdog */
+#define RD_RE      0x00000008       /* Report on MII Error */
 #define RD_DB      0x00000004       /* Dribbling Bit */
 #define RD_CE      0x00000002       /* CRC Error */
 #define RD_OF      0x00000001       /* Overflow */
@@ -649,40 +781,53 @@
 #define TD_TCH     0x01000000       /* Second Address Chained */
 #define TD_DPD     0x00800000       /* Disabled Padding */
 #define TD_FT0     0x00400000       /* Filtering Type */
-#define TD_RBS2    0x003ff800       /* Buffer 2 Size */
-#define TD_RBS1    0x000007ff       /* Buffer 1 Size */
+#define TD_TBS2    0x003ff800       /* Buffer 2 Size */
+#define TD_TBS1    0x000007ff       /* Buffer 1 Size */
 
 #define PERFECT_F  0x00000000
 #define HASH_F     TD_FT0
 #define INVERSE_F  TD_FT1
-#define HASH_O_F   TD_FT1| TD_F0
+#define HASH_O_F   (TD_FT1 | TD_F0)
 
 /*
 ** Media / mode state machine definitions
 */
-#define NC              0x0000     /* No Connection */
-#define TP              0x0001     /* 10Base-T */
-#define TP_NW           0x0002     /* 10Base-T with Nway */
-#define BNC             0x0004     /* Thinwire */
-#define AUI             0x0008     /* Thickwire */
+#define NC              0x0000     /* No Connection                        */
+#define TP              0x0001     /* 10Base-T                             */
+#define TP_NW           0x0002     /* 10Base-T with Nway                   */
+#define BNC             0x0004     /* Thinwire                             */
+#define AUI             0x0008     /* Thickwire                            */
 #define BNC_AUI         0x0010     /* BNC/AUI on DC21040 indistinguishable */
-#define ANS             0x0020     /* Intermediate AutoNegotiation State */
-#define ANS_1           0x0021     /* Intermediate AutoNegotiation State */
-
-#define _10Mb           0x0040     /* 10Mb/s Ethernet */
-#define _100Mb          0x0080     /* 100Mb/s Ethernet */
-#define SPD_DET         0x0100     /* Parallel speed detection */
-#define INIT            0x0200     /* Initial state */
-#define EXT_SIA         0x0400     /* External SIA for motherboard chip */
-#define ANS_SUSPECT     0x0802     /* Suspect the ANS (TP) port is down */
-#define TP_SUSPECT      0x0803     /* Suspect the TP port is down */
-#define BNC_AUI_SUSPECT 0x0804     /* Suspect the BNC or AUI port is down */
-#define EXT_SIA_SUSPECT 0x0805     /* Suspect the EXT SIA port is down */
-#define BNC_SUSPECT     0x0806     /* Suspect the BNC port is down */
-#define AUI_SUSPECT     0x0807     /* Suspect the AUI port is down */
-
-#define AUTO            0x4000     /* Auto sense the media or speed */
-#define TIMER_CB        0x80000000 /* Timer callback detection */
+#define ANS             0x0020     /* Intermediate AutoNegotiation State   */
+#define _10Mb           0x0040     /* 10Mb/s Ethernet                      */
+#define _100Mb          0x0080     /* 100Mb/s Ethernet                     */
+#define SPD_DET         0x0100     /* Parallel speed detection             */
+#define INIT            0x0200     /* Initial state                        */
+#define EXT_SIA         0x0400     /* External SIA for motherboard chip    */
+#define ANS_SUSPECT     0x0802     /* Suspect the ANS (TP) port is down    */
+#define TP_SUSPECT      0x0803     /* Suspect the TP port is down          */
+#define BNC_AUI_SUSPECT 0x0804     /* Suspect the BNC or AUI port is down  */
+#define EXT_SIA_SUSPECT 0x0805     /* Suspect the EXT SIA port is down     */
+#define BNC_SUSPECT     0x0806     /* Suspect the BNC port is down         */
+#define AUI_SUSPECT     0x0807     /* Suspect the AUI port is down         */
+#define MII             0x1000     /* MII on the 21143                     */
+
+#define AUTO            0x4000     /* Auto sense the media or speed        */
+#define TIMER_CB        0x80000000 /* Timer callback detection             */
+
+/*
+** DE4X5 DEBUG Options
+*/
+#define DEBUG_NONE      0x0000     /* No DEBUG messages */
+#define DEBUG_VERSION   0x0001     /* Print version message */
+#define DEBUG_MEDIA     0x0002     /* Print media messages */
+#define DEBUG_TX        0x0004     /* Print TX (queue_pkt) messages */
+#define DEBUG_RX        0x0008     /* Print RX (de4x5_rx) messages */
+#define DEBUG_SROM      0x0010     /* Print SROM messages */
+#define DEBUG_MII       0x0020     /* Print MII messages */
+#define DEBUG_OPEN      0x0040     /* Print de4x5_open() messages */
+#define DEBUG_CLOSE     0x0080     /* Print de4x5_close() messages */
+#define DEBUG_PCICFG    0x0100
 
 /*
 ** Miscellaneous
@@ -699,7 +844,6 @@
 #define POLL_DEMAND          1
 
 #define LOST_MEDIA_THRESHOLD 3
-#define LOST_MEDIA           (lp->lostMedia > LOST_MEDIA_THRESHOLD)
 
 #define MASK_INTERRUPTS      1
 #define UNMASK_INTERRUPTS    0
@@ -728,11 +872,16 @@
 */
 #define NO                   0
 #define FALSE                0
-#define CLOSED               0
 
 #define YES                  ~0
 #define TRUE                 ~0
-#define OPEN                 ~0
+
+/*
+** Adapter state
+*/
+#define INITIALISED          0     /* After h/w initialised and mem alloc'd */
+#define CLOSED               1     /* Ready for opening */
+#define OPEN                 2     /* Running */
 
 /*
 ** IEEE OUIs for various PHY vendor/chip combos - Reg 2 values only. Since
@@ -748,55 +897,67 @@
 ** Speed Selection stuff
 */
 #define SET_10Mb {\
-  if (lp->phy[lp->active].id) {\
-    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\
+  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\
+    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\
     if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\
-      mii_wr(MII_CR_10|(de4x5_full_duplex?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
+      mii_wr(MII_CR_10|(lp->fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
     }\
-    omr |= ((de4x5_full_duplex ? OMR_FD : 0) | OMR_TTM);\
+    omr |= ((lp->fdx ? OMR_FDX : 0) | OMR_TTM);\
     outl(omr, DE4X5_OMR);\
-    outl(0, DE4X5_GEP);\
+    if (!lp->useSROM) lp->cache.gep = 0;\
+  } else if (lp->useSROM && !lp->useMII) {\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
+    outl(omr | lp->infoblock_csr6, DE4X5_OMR);\
   } else {\
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
-    omr |= (de4x5_full_duplex ? OMR_FD : 0);\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
     outl(omr | OMR_TTM, DE4X5_OMR);\
-    outl((de4x5_full_duplex ? 0 : GEP_FDXD), DE4X5_GEP);\
+    lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD);\
   }\
 }
 
 #define SET_100Mb {\
-  if (lp->phy[lp->active].id) {\
+  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\
     int fdx=0;\
     if (lp->phy[lp->active].id == NATIONAL_TX) {\
         mii_wr(mii_rd(0x18, lp->phy[lp->active].addr, DE4X5_MII) & ~0x2000,\
                       0x18, lp->phy[lp->active].addr, DE4X5_MII);\
     }\
-    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD);\
+    omr = inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX);\
     sr = mii_rd(MII_SR, lp->phy[lp->active].addr, DE4X5_MII);\
-    if (!(sr & MII_ANA_T4AM) && de4x5_full_duplex) fdx=1;\
+    if (!(sr & MII_ANA_T4AM) && lp->fdx) fdx=1;\
     if ((lp->tmp != MII_SR_ASSC) || (lp->autosense != AUTO)) {\
       mii_wr(MII_CR_100|(fdx?MII_CR_FDM:0), MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
     }\
-    if (fdx) omr |= OMR_FD;\
+    if (fdx) omr |= OMR_FDX;\
     outl(omr, DE4X5_OMR);\
+    if (!lp->useSROM) lp->cache.gep = 0;\
+  } else if (lp->useSROM && !lp->useMII) {\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
+    outl(omr | lp->infoblock_csr6 | OMR_HBD, DE4X5_OMR);\
   } else {\
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
-    omr |= (de4x5_full_duplex ? OMR_FD : 0);\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    omr |= (lp->fdx ? OMR_FDX : 0);\
     outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\
-    outl((de4x5_full_duplex ? 0 : GEP_FDXD) | GEP_MODE, DE4X5_GEP);\
+    lp->cache.gep = (lp->fdx ? 0 : GEP_FDXD) | GEP_MODE;\
   }\
 }
 
 /* FIX ME so I don't jam 10Mb networks */
 #define SET_100Mb_PDET {\
-  if (lp->phy[lp->active].id) {\
+  if ((lp->phy[lp->active].id) && (!lp->useSROM || lp->useMII)) {\
     mii_wr(MII_CR_100|MII_CR_ASSE, MII_CR, lp->phy[lp->active].addr, DE4X5_MII);\
-    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
+    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
+    outl(omr, DE4X5_OMR);\
+  } else if (lp->useSROM && !lp->useMII) {\
+    omr = (inl(DE4X5_OMR) & ~(OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
     outl(omr, DE4X5_OMR);\
   } else {\
-    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FD));\
+    omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR | OMR_FDX));\
     outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);\
-    outl(GEP_FDXD | GEP_MODE, DE4X5_GEP);\
+    lp->cache.gep = (GEP_FDXD | GEP_MODE);\
   }\
 }
 
@@ -817,7 +978,7 @@
 ** Recognised commands for the driver 
 */
 #define DE4X5_GET_HWADDR	0x01 /* Get the hardware address */
-#define DE4X5_SET_HWADDR	0x02 /* Get the hardware address */
+#define DE4X5_SET_HWADDR	0x02 /* Set the hardware address */
 #define DE4X5_SET_PROM  	0x03 /* Set Promiscuous Mode */
 #define DE4X5_CLR_PROM  	0x04 /* Clear Promiscuous Mode */
 #define DE4X5_SAY_BOO	        0x05 /* Say "Boo!" to the kernel log file */
diff -urN linux-2.0.31-pre4/drivers/net/eql.c linux-2.0.31-pre5/drivers/net/eql.c
--- linux-2.0.31-pre4/drivers/net/eql.c	1996-09-22 11:17:08.000000000 -0700
+++ linux-2.0.31-pre5/drivers/net/eql.c	2003-08-15 15:03:58.000000000 -0700
@@ -17,7 +17,7 @@
  */
 
 static const char *version = 
-	"Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n";
+	"Equalizer1996: $Revision: 1.9 $ $Date: 1996/10/12 11:14:37 $ Simon Janes (simon@ncm.com)\n";
 
 /*
  * Sources:
@@ -31,6 +31,9 @@
 
 /*
  * $Log: eql.c,v $
+ * Revision 1.9  1996/10/12 11:14:37  davem
+ * Quick merge to 2.0.20
+ *
  * Revision 1.2  1996/04/11 17:51:52  guru
  * Added one-line eql_remove_slave patch.
  *
@@ -262,6 +265,7 @@
 
 	dev->hard_header	= eql_header; 
 	dev->rebuild_header	= eql_rebuild_header;
+	dev->hard_header_len	= MAX_HEADER; /* enough space for any slave */
 
 	/*
 	 *	Now we undo some of the things that eth_setup does
@@ -371,10 +375,19 @@
 	equalizer_t *eql = (equalizer_t *) dev->priv;
 	struct device *slave_dev = 0;
 	slave_t *slave;
+	struct sk_buff *skb2;
 
 	if (skb == NULL)
 		return 0;
 
+#if 0
+	/* Make a copy we can free so we don't mess up the skb->dev pointer */
+	skb2 = skb_clone(skb, GFP_ATOMIC);
+
+	if (skb2 == NULL)
+		return 1;
+#endif
+
 	eql_schedule_slaves (eql->queue);
   
 	slave_dev = eql_best_slave_dev (eql->queue);
@@ -388,20 +401,48 @@
 				dev->name, eql_number_slaves (eql->queue), skb->len,
 				slave_dev->name);
 #endif
-		dev_queue_xmit (skb, slave_dev, 1);
-		eql->stats->tx_packets++;
-		slave->bytes_queued += skb->len; 
-	}
-	else
-	{
-		/*
-		 *	The alternative for this is the return 1 and have
-		 *	dev_queue_xmit just queue it up on the eql's queue. 
+
+		/* Rip off the fake header */
+		skb_pull(skb,MAX_HEADER);
+
+		/* The original code had no hard header constructed.
+		 * If a frame is fragmented on EQL and then passed to PPP,
+		 * or ISDN, the result will be a panic.
+		 * The solution is to call the hard_header constructor
+		 * for the device we point to just before we send the packet.
+		 * If this fails we drop the packet.
+		 * We don't know any special parameters for the hard_header
+		 * constructor at this point, so we pass in made up values
+		 * that will cause the constructor to fail on every device
+		 * except those that we are allowed to use EQL on:
+		 * PPP, SLIP and ISDN (in some cases!).
+		 * The worst thing that happens is if some fool
+		 * configures EQL to enslave something that needs
+		 * these parameters it throws out packets.
+		 * This is not an issue for the things EQL is intended for
+		 * anyway, and probably would have crashed the kernel
+		 * or sent garbage down the wire previously.
 		 */
 
-		eql->stats->tx_dropped++;
-		dev_kfree_skb(skb, FREE_WRITE);
-	}	  
+		if (slave_dev->hard_header == NULL
+		|| slave_dev->hard_header(skb,slave_dev,
+			ETH_P_IP,NULL,NULL,skb->len) >= 0) {
+			dev_queue_xmit (skb, slave_dev, 1);
+			eql->stats->tx_packets++;
+			slave->bytes_queued += skb->len; 
+			/* dev_kfree_skb(skb, FREE_WRITE); */
+			return 0;
+		}
+	}
+
+	/*
+	 *	The alternative for this is the return 1 and have
+	 *	dev_queue_xmit just queue it up on the eql's queue. 
+	 */
+
+	eql->stats->tx_dropped++;
+	/* dev_kfree_skb(skb2, FREE_WRITE); */
+	dev_kfree_skb(skb, FREE_WRITE);
 	return 0;
 }
 
@@ -417,7 +458,9 @@
 	   unsigned short type, void *daddr, void *saddr, 
 	   unsigned len)
 {
-	return 0;
+	/* Fake header to keep space during buggy IP fragmentation.  */
+	skb_push(skb,MAX_HEADER);
+	return MAX_HEADER;
 }
 
 
diff -urN linux-2.0.31-pre4/drivers/net/ibmtr.c linux-2.0.31-pre5/drivers/net/ibmtr.c
--- linux-2.0.31-pre4/drivers/net/ibmtr.c	1997-04-08 08:47:46.000000000 -0700
+++ linux-2.0.31-pre5/drivers/net/ibmtr.c	2003-08-15 15:03:58.000000000 -0700
@@ -971,14 +971,15 @@
 			} /* ARB response */
 			
 			if (status & SSB_RESP_INT) { /* SSB response */
-						
+				unsigned char retcode;		
 				switch (readb(ti->ssb)) { /* SSB command check */
 					
 				      case XMIT_DIR_FRAME:
 				      case XMIT_UI_FRAME:
-					if (readb(ti->ssb+2)) /* checks ret_code */
+					retcode = readb(ti->ssb+2);
+					if (retcode && (retcode != 0x22)) /* checks ret_code */
 						DPRINTK("xmit ret_code: %02X xmit error code: %02X\n", 
-							(int)readb(ti->ssb+2), (int)readb(ti->ssb+6));		
+							(int)retcode, (int)readb(ti->ssb+6));		
 					else ti->tr_stats.tx_packets++;
 					break;
 					
diff -urN linux-2.0.31-pre4/drivers/net/new_tunnel.c linux-2.0.31-pre5/drivers/net/new_tunnel.c
--- linux-2.0.31-pre4/drivers/net/new_tunnel.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/drivers/net/new_tunnel.c	2003-08-15 15:03:58.000000000 -0700
@@ -161,7 +161,7 @@
 	 *  routing tables
 	 */
 	iph = (struct iphdr *) skb->data;
-	if ((rt = ip_rt_route(iph->daddr, 0)) == NULL)
+	if ((rt = ip_rt_route(iph->daddr, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL)
 	{ 
 		/* No route to host */
 		/* Where did the packet come from? */
@@ -194,7 +194,7 @@
 	}
 	ip_rt_put(rt);
 
-	if ((rt = ip_rt_route(target, 0)) == NULL)
+	if ((rt = ip_rt_route(target, 0, skb->sk?skb->sk->bound_device:NULL)) == NULL)
 	{ 
 		/* No route to host */
 		/* Where did the packet come from? */
diff -urN linux-2.0.31-pre4/drivers/net/ppp.c linux-2.0.31-pre5/drivers/net/ppp.c
--- linux-2.0.31-pre4/drivers/net/ppp.c	1996-10-08 11:21:03.000000000 -0700
+++ linux-2.0.31-pre5/drivers/net/ppp.c	2003-08-15 15:03:58.000000000 -0700
@@ -6,7 +6,7 @@
  *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
  *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
  *
- *  ==FILEVERSION 960528==
+ *  ==FILEVERSION 970703==
  *
  *  NOTE TO MAINTAINERS:
  *     If you modify this file at all, please set the number above to the
@@ -406,7 +406,14 @@
 		skb_queue_head_init (&dev->buffs[indx]);
 
 	/* New-style flags */
+#ifdef IFF_SOFTHEADERS
+	/* Needed to make SOCK_PACKET work correctly in
+	 * memory fussy kernels.
+	 */
+	dev->flags	= IFF_POINTOPOINT|IFF_SOFTHEADERS;
+#else
 	dev->flags	= IFF_POINTOPOINT;
+#endif
 	dev->family	= AF_INET;
 	dev->pa_addr	= 0;
 	dev->pa_brdaddr = 0;
@@ -697,7 +704,7 @@
 
 	if (dev && dev->flags & IFF_UP) {
 		dev_close (dev); /* close the device properly */
-		dev->flags = 0;	 /* prevent recursion */
+		dev->flags &= ~IFF_UP;	 /* prevent recursion */
 	}
 
 	ppp_free_buf (ppp->rbuf);
@@ -3079,7 +3086,7 @@
  * Fetch the pointer to the data
  */
 	len   = skb->len;
-	data  = skb_data(skb);
+	data  = skb_data(skb) + PPP_HARD_HDR_LEN;
 /*
  * Bug trap for null data. Release the skb and bail out.
  */
@@ -3152,7 +3159,12 @@
 			   __u16 type, void *daddr,
 			   void *saddr, unsigned int len)
 {
-	return (0);
+	/* On the PPP device the hard header must be ignored
+	 * by the SOCK_PACKET layer. (Backward compatability).
+	 */
+	skb->mac.raw = skb->data;
+	skb_push(skb,PPP_HARD_HDR_LEN);
+	return PPP_HARD_HDR_LEN;
 }
 
 static int
diff -urN linux-2.0.31-pre4/drivers/pci/pci.c linux-2.0.31-pre5/drivers/pci/pci.c
--- linux-2.0.31-pre4/drivers/pci/pci.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/drivers/pci/pci.c	2003-08-15 15:03:58.000000000 -0700
@@ -47,9 +47,15 @@
 	DEVICE( NCR,		NCR_53C820,	"53c820"),
 	DEVICE( NCR,		NCR_53C825,	"53c825"),
 	DEVICE( NCR,		NCR_53C815,	"53c815"),
+	DEVICE( NCR,		NCR_53C860,	"53c860"),
+	DEVICE( NCR,		NCR_53C896,	"53c896"),
+	DEVICE( NCR,		NCR_53C895,	"53c895"),
+	DEVICE( NCR,		NCR_53C885,	"53c885"),
+	DEVICE( NCR,		NCR_53C875,	"53c875"),
 	DEVICE( ATI,		ATI_68800,      "68800AX"),
 	DEVICE( ATI,		ATI_215CT222,   "215CT222"),
 	DEVICE( ATI,		ATI_210888CX,   "210888CX"),
+	DEVICE( ATI,		ATI_215GT,	"Mach64 GT (Rage II)"),
 	DEVICE( ATI,		ATI_210888GX,   "210888GX"),
 	DEVICE( VLSI,		VLSI_82C592,	"82C592-FC1"),
 	DEVICE( VLSI,		VLSI_82C593,	"82C593-FC1"),
@@ -61,6 +67,7 @@
 	DEVICE( TSENG,		TSENG_W32P_b,	"ET4000W32P rev B"),
 	DEVICE( TSENG,		TSENG_W32P_c,	"ET4000W32P rev C"),
 	DEVICE( TSENG,		TSENG_W32P_d,	"ET4000W32P rev D"),
+	DEVICE( TSENG,		TSENG_ET6000,	"ET6000"),
 	DEVICE( WEITEK,		WEITEK_P9000,	"P9000"),
 	DEVICE( WEITEK,		WEITEK_P9100,	"P9100"),
 	BRIDGE( DEC,		DEC_BRD,	"DC21050", 		0x00),
@@ -69,16 +76,21 @@
 	DEVICE( DEC,		DEC_TULIP_FAST,	"DC21140"),
 	DEVICE( DEC,		DEC_FDDI,	"DEFPA"),
 	DEVICE( DEC,		DEC_TULIP_PLUS,	"DC21041"),
+	DEVICE( DEC,		DEC_21142,	"DC21142"),
 	DEVICE( DEC,		DEC_21052,	"DC21052"),
 	DEVICE( DEC,		DEC_21152,	"DC21152"),
+	DEVICE( CIRRUS,		CIRRUS_7548,	"GD 7548"),
 	DEVICE( CIRRUS,		CIRRUS_5430,	"GD 5430"),
 	DEVICE( CIRRUS,		CIRRUS_5434_4,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_5434_8,	"GD 5434"),
 	DEVICE( CIRRUS,		CIRRUS_5436,	"GD 5436"),
+	DEVICE( CIRRUS,		CIRRUS_5446,	"GD 5446"),
 	DEVICE( CIRRUS,		CIRRUS_6729,	"CL 6729"),
 	DEVICE( CIRRUS,		CIRRUS_7542,	"CL 7542"),
 	DEVICE( CIRRUS,		CIRRUS_7543,	"CL 7543"),
+	DEVICE( CIRRUS,		CIRRUS_7541,	"CL 7541"),
 	DEVICE( IBM,		IBM_82G2675,	"82G2675"),
+	DEVICE( IBM,		IBM_82351,	"82351"),
 	DEVICE( WD,		WD_7197,	"WD 7197"),
 	DEVICE( AMD,		AMD_LANCE,	"79C970"),
 	DEVICE( AMD,		AMD_SCSI,	"53C974"),
@@ -93,6 +105,7 @@
 	DEVICE( CT,		CT_65545,	"65545"),
 	DEVICE( CT,		CT_65548,	"65548"),
 	DEVICE(	CT,		CT_65550,	"65550"),
+	DEVICE( CT,		CT_65554,	"65554"),
 	DEVICE( MIRO,		MIRO_36050,	"ZR36050"),
 	DEVICE( FD,		FD_36C70,	"TMC-18C30"),
 	DEVICE( SI,		SI_6201,	"6201"),
@@ -104,6 +117,8 @@
 	DEVICE( SI,		SI_601,		"85C601"),
 	DEVICE( SI,		SI_5511,		"85C5511"),
 	DEVICE( SI,		SI_5513,		"85C5513"),
+	DEVICE( SI,		SI_5571,	"5571"),
+	DEVICE( SI,		SI_7001,	"7001"),
 	DEVICE( HP,		HP_J2585A,	"J2585A"),
 	DEVICE( HP,		HP_J2585B,	"J2585B (Lassen)"),
 	DEVICE( PCTECH,		PCTECH_RZ1000,  "RZ1000 (buggy)"),
@@ -121,6 +136,9 @@
 	DEVICE( BUSLOGIC,	BUSLOGIC_FLASHPOINT,     "FlashPoint"),
 	DEVICE( OAK,		OAK_OTI107,	"OTI107"),
 	DEVICE( WINBOND2,	WINBOND2_89C940,"NE2000-PCI"),
+	DEVICE( MOTOROLA,	MOTOROLA_MPC105,"MPC105 Eagle"),
+	DEVICE( MOTOROLA,	MOTOROLA_MPC106,"MPC106 Grackle"),
+	DEVICE( MOTOROLA,	MOTOROLA_RAVEN,	"Raven"),
 	DEVICE( PROMISE,	PROMISE_5300,	"DC5030"),
 	DEVICE( N9,		N9_I128,	"Imagine 128"),
 	DEVICE( N9,		N9_I128_2,	"Imagine 128v2"),
@@ -143,14 +161,19 @@
 	DEVICE( CMD,		CMD_646,	"646"),
 	DEVICE( VISION,		VISION_QD8500,	"QD-8500"),
 	DEVICE( VISION,		VISION_QD8580,	"QD-8580"),
+	DEVICE( BROOKTREE,	BROOKTREE_848,	"Bt848"),
 	DEVICE( SIERRA,		SIERRA_STB,	"STB Horizon 64"),
 	DEVICE( ACC,		ACC_2056,	"2056"),
 	DEVICE( WINBOND,	WINBOND_83769,	"W83769F"),
 	DEVICE( WINBOND,	WINBOND_82C105,	"SL82C105"),
+	DEVICE( WINBOND,	WINBOND_83C553,	"W83C553"),
 	DEVICE( 3COM,		3COM_3C590,	"3C590 10bT"),
 	DEVICE( 3COM,		3COM_3C595TX,	"3C595 100bTX"),
 	DEVICE( 3COM,		3COM_3C595T4,	"3C595 100bT4"),
 	DEVICE( 3COM,		3COM_3C595MII,	"3C595 100b-MII"),
+	DEVICE( 3COM,		3COM_3C900TPO,	"3C900 10bTPO"),
+	DEVICE( 3COM,		3COM_3C900COMBO,"3C900 10b Combo"),
+	DEVICE( 3COM,		3COM_3C905TX,	"3C905 100bTX"),
 	DEVICE( AL,		AL_M1445,	"M1445"),
 	DEVICE( AL,		AL_M1449,	"M1449"),
 	DEVICE( AL,		AL_M1451,	"M1451"),
@@ -159,18 +182,26 @@
 	DEVICE( AL,		AL_M1511,	"M1511"),
 	DEVICE( AL,		AL_M1513,	"M1513"),
 	DEVICE( AL,		AL_M4803,	"M4803"),
+	DEVICE( NEOMAGIC,       NEOMAGIC_MAGICGRAPH_NM2070,     "Magicgraph NM2070"),
 	DEVICE( ASP,		ASP_ABP940,	"ABP940"),
+	DEVICE( ASP,		ASP_ABP940U,	"ABP940U"),
 	DEVICE( CERN,		CERN_SPSB_PMC,	"STAR/RD24 SCI-PCI (PMC)"),
 	DEVICE( CERN,		CERN_SPSB_PCI,	"STAR/RD24 SCI-PCI (PMC)"),
 	DEVICE( IMS,		IMS_8849,	"8849"),
 	DEVICE( TEKRAM2,	TEKRAM2_690c,	"DC690c"),
+	DEVICE( TUNDRA,		TUNDRA_CA91C042,"CA91C042 Universe"),
 	DEVICE( AMCC,		AMCC_MYRINET,	"Myrinet PCI (M2-PCI-32)"),
+	DEVICE( AMCC,		AMCC_S5933,	"S5933"),
 	DEVICE( INTERG,		INTERG_1680,	"IGA-1680"),
+	DEVICE( INTERG,         INTERG_1682,    "IGA-1682"),
 	DEVICE( REALTEK,	REALTEK_8029,	"8029"),
 	DEVICE( INIT,		INIT_320P,	"320 P"),
 	DEVICE( VIA,		VIA_82C505,	"VT 82C505"),
 	DEVICE( VIA,		VIA_82C561,	"VT 82C561"),
+	DEVICE( VIA,		VIA_82C586_1,	"VT 82C586 Apollo VP-1"),
 	DEVICE( VIA,		VIA_82C576,	"VT 82C576 3V"),
+	DEVICE( VIA,		VIA_82C585,	"VT 82C585VP Apollo VP-1"),
+	DEVICE( VIA,		VIA_82C586_0,	"VT 82C586 Apollo VP-1"),
 	DEVICE( VIA,		VIA_82C416,	"VT 82C416MV"),
 	DEVICE( VORTEX,		VORTEX_GDT60x0,	"GDT 60x0"),
 	DEVICE( VORTEX,		VORTEX_GDT6000B,"GDT 6000b"),
@@ -191,16 +222,20 @@
 	DEVICE( IMAGINGTECH,	IMAGINGTECH_ICPCI, "MVC IC-PCI"),
 	DEVICE( FORE,		FORE_PCA200PC, "PCA-200PC"),
 	DEVICE( FORE,		FORE_PCA200E,	 "PCA-200E"),
+	DEVICE( IMAGINGTECH,	IMAGINGTECH_ICPCI, "MVC IC-PCI"),
 	DEVICE( PLX,		PLX_9060,	"PCI9060 i960 bridge"),
 	DEVICE( ALLIANCE,	ALLIANCE_PROMOTIO, "Promotion-6410"),
 	DEVICE( ALLIANCE,	ALLIANCE_PROVIDEO, "Provideo"),
 	DEVICE( VMIC,		VMIC_VME,	"VMIVME-7587"),
  	DEVICE( DIGI,		DIGI_RIGHTSWITCH, "RightSwitch SE-6"),
 	DEVICE( MUTECH,		MUTECH_MV1000,	"MV-1000"),
+	DEVICE( TOSHIBA,	TOSHIBA_601,	"Laptop"),
 	DEVICE( ZEITNET,	ZEITNET_1221,	"1221"),
 	DEVICE( ZEITNET,	ZEITNET_1225,	"1225"),
+	DEVICE( OMEGA,		OMEGA_PCMCIA,	"PCMCIA"),
 	DEVICE( SPECIALIX,	SPECIALIX_XIO,	"XIO/SIO host"),
 	DEVICE( SPECIALIX,	SPECIALIX_RIO,	"RIO host"),
+	DEVICE( ZORAN,		ZORAN_36120,	"ZR36120"),
 	DEVICE( COMPEX,		COMPEX_ENET100VG4, "Readylink ENET100-VG4"),
 	DEVICE( COMPEX,		COMPEX_RL2000,	"ReadyLink 2000"),
 	DEVICE( RP,             RP8OCTA,        "RocketPort 8 Oct"),
@@ -211,10 +246,20 @@
 	DEVICE( CYCLADES,	CYCLOM_Y_Hi,	"Cyclom-Y above 1Mbyte"),
 	DEVICE( CYCLADES,	CYCLOM_Z_Lo,	"Cyclom-Z below 1Mbyte"),
 	DEVICE( CYCLADES,	CYCLOM_Z_Hi,	"Cyclom-Z above 1Mbyte"),
+	DEVICE( 3DFX,		3DFX_VOODOO,	"Voodoo"),
+	DEVICE( SIGMA_DESIGNS,	SD_REALMAGIC64GX, "REALmagic64/GX (SD 6425)"),
+	DEVICE( OPTIBASE,	OPTIBASE_FORGE,	"MPEG Forge"),
+	DEVICE( OPTIBASE,	OPTIBASE_FUSION,"MPEG Fusion"),
+	DEVICE( OPTIBASE,	OPTIBASE_VPLEX,	"VideoPlex"),
+	DEVICE( OPTIBASE,	OPTIBASE_VPLEXCC,"VideoPlex CC"),
+	DEVICE( OPTIBASE,	OPTIBASE_VQUEST,"VideoQuest"),
 	DEVICE( SYMPHONY,	SYMPHONY_101,	"82C101"),
 	DEVICE( TEKRAM,		TEKRAM_DC290,	"DC-290"),
 	DEVICE( 3DLABS,		3DLABS_300SX,	"GLINT 300SX"),
+ 	DEVICE( 3DLABS,		3DLABS_DELTA,	"GLINT Delta"),
+ 	DEVICE( 3DLABS,		3DLABS_PERMEDIA,"PERMEDIA"),
 	DEVICE( AVANCE,		AVANCE_2302,	"ALG-2302"),
+	DEVICE( S3,		S3_PLATO_PXS,	"PLATO/PX (system)"),
 	DEVICE( S3,		S3_ViRGE,	"ViRGE"),
 	DEVICE( S3,		S3_TRIO,	"Trio32/Trio64"),
 	DEVICE( S3,		S3_AURORA64VP,	"Aurora64V+"),
@@ -227,11 +272,16 @@
 	DEVICE( S3,		S3_964_1,	"Vision 964-P"),
 	DEVICE( S3,		S3_964_2,	"Vision 964-P"),
 	DEVICE( S3,		S3_968,		"Vision 968"),
+	DEVICE( S3,		S3_TRIO64V2,	"Trio64V2/DX or /GX"),
+	DEVICE( S3,		S3_PLATO_PXG,	"PLATO/PX (graphics)"),
+	DEVICE( S3,		S3_ViRGE_DXGX,	"ViRGE/DX or /GX"),
 	DEVICE( INTEL,		INTEL_82375,	"82375EB"),
 	BRIDGE( INTEL,		INTEL_82424,	"82424ZX Saturn",	0x00),
 	DEVICE( INTEL,		INTEL_82378,	"82378IB"),
 	DEVICE( INTEL,		INTEL_82430,	"82430ZX Aries"),
 	BRIDGE( INTEL,		INTEL_82434,	"82434LX Mercury/Neptune", 0x00),
+	DEVICE( INTEL,		INTEL_82092AA_0,"82092AA PCMCIA bridge"),
+	DEVICE( INTEL,		INTEL_82092AA_1,"82092AA EIDE"),
 	DEVICE( INTEL,		INTEL_7116,	"SAA7116"),
 	DEVICE( INTEL,		INTEL_82596,	"82596"),
 	DEVICE( INTEL,		INTEL_82865,	"82865"),
@@ -239,14 +289,22 @@
 	DEVICE( INTEL,		INTEL_82437,	"82437"),
 	DEVICE( INTEL,		INTEL_82371_0,	"82371 Triton PIIX"),
 	DEVICE( INTEL,		INTEL_82371_1,	"82371 Triton PIIX"),
+	DEVICE( INTEL,		INTEL_82371MX,	"82371MX Mobile PCI I/O IDE Xcelerator (MPIIX)"),
+	DEVICE( INTEL,		INTEL_82430MX,	"82430MX Mobile PCIset"),
 	DEVICE( INTEL,		INTEL_82441,	"82441FX Natoma"),
 	DEVICE( INTEL,		INTEL_82439,	"82439HX Triton II"),
 	DEVICE(	INTEL,		INTEL_82371SB_0,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE(	INTEL,		INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE( INTEL,		INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE( INTEL,		INTEL_82437VX,	"82437VX Triton II"),
-	DEVICE( INTEL,		INTEL_82371AB,	"82371AB 430TX PIIX4"),
+	DEVICE( INTEL,		INTEL_82439TX,	"82439TX"),
+	DEVICE( INTEL,		INTEL_82371AB_0,"82371AB PIIX4"),
+	DEVICE( INTEL,		INTEL_82371AB,  "82371AB PIIX4"),
+	DEVICE( INTEL,		INTEL_82371AB_2,"82371AB PIIX4"),
+	DEVICE( INTEL,		INTEL_82371AB_3,"82371AB PIIX4 Power Management"),
 	DEVICE( INTEL,		INTEL_P6,	"Orion P6"),
+	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion"),
+	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
 	DEVICE( ADAPTEC,	ADAPTEC_7860,	"AIC-7860"),
@@ -262,8 +320,9 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7883,	"AIC-7883U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
-	DEVICE( HER,		HER_STING,	"Stingray"),
-	DEVICE( HER,		HER_STINGARK,	"Stingray ARK 2000PV")
+	DEVICE( ARK,		ARK_STING,	"Stingray"),
+	DEVICE( ARK,		ARK_STINGARK,	"Stingray ARK 2000PV"),
+	DEVICE( ARK,		ARK_2000MT,	"2000MT")
 };
 
 
@@ -487,6 +546,7 @@
 	      case PCI_VENDOR_ID_BUSLOGIC:	return "BusLogic";
 	      case PCI_VENDOR_ID_OAK: 		return "OAK";
 	      case PCI_VENDOR_ID_WINBOND2:	return "Winbond";
+	      case PCI_VENDOR_ID_MOTOROLA:	return "Motorola";
 	      case PCI_VENDOR_ID_PROMISE:	return "Promise Technology";
 	      case PCI_VENDOR_ID_N9:		return "Number Nine";
 	      case PCI_VENDOR_ID_UMC:		return "UMC";
@@ -499,15 +559,18 @@
 	      case PCI_VENDOR_ID_OLICOM:	return "Olicom";
 	      case PCI_VENDOR_ID_CMD:		return "CMD";
 	      case PCI_VENDOR_ID_VISION:	return "Vision";
+	      case PCI_VENDOR_ID_BROOKTREE:	return "Brooktree";
 	      case PCI_VENDOR_ID_SIERRA:	return "Sierra";
 	      case PCI_VENDOR_ID_ACC:		return "ACC MICROELECTRONICS";
 	      case PCI_VENDOR_ID_WINBOND:	return "Winbond";
 	      case PCI_VENDOR_ID_3COM:		return "3Com";
 	      case PCI_VENDOR_ID_AL:		return "Acer Labs";
+	      case PCI_VENDOR_ID_NEOMAGIC:	return "Neomagic";
 	      case PCI_VENDOR_ID_ASP:		return "Advanced System Products";
 	      case PCI_VENDOR_ID_CERN:		return "CERN";
 	      case PCI_VENDOR_ID_IMS:		return "IMS";
 	      case PCI_VENDOR_ID_TEKRAM2:	return "Tekram";
+	      case PCI_VENDOR_ID_TUNDRA:	return "Tundra";
 	      case PCI_VENDOR_ID_AMCC:		return "AMCC";
 	      case PCI_VENDOR_ID_INTERG:	return "Intergraphics";
 	      case PCI_VENDOR_ID_REALTEK:	return "Realtek";
@@ -528,6 +591,7 @@
 	      case PCI_VENDOR_ID_COMPEX:	return "Compex";
 	      case PCI_VENDOR_ID_RP:		return "Comtrol";
 	      case PCI_VENDOR_ID_CYCLADES:	return "Cyclades";
+	      case PCI_VENDOR_ID_SIGMA_DESIGNS:	return "Sigma Designs";
 	      case PCI_VENDOR_ID_SYMPHONY:	return "Symphony";
 	      case PCI_VENDOR_ID_TEKRAM:	return "Tekram";
 	      case PCI_VENDOR_ID_3DLABS:	return "3Dlabs";
@@ -536,7 +600,9 @@
 	      case PCI_VENDOR_ID_INTEL:		return "Intel";
 	      case PCI_VENDOR_ID_ADAPTEC:	return "Adaptec";
 	      case PCI_VENDOR_ID_ATRONICS:	return "Atronics";
+#if 0
 	      case PCI_VENDOR_ID_HER:		return "Hercules";
+#endif
 	      default:				return "Unknown vendor";
 	}
 }
diff -urN linux-2.0.31-pre4/drivers/scsi/in2000.readme linux-2.0.31-pre5/drivers/scsi/in2000.readme
--- linux-2.0.31-pre4/drivers/scsi/in2000.readme	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.31-pre5/drivers/scsi/in2000.readme	1996-06-06 03:00:20.000000000 -0700
@@ -0,0 +1,155 @@
+
+UPDATE NEWS: version 1.28 - 07 May 96
+
+   Tightened up the "interrupts enabled/disabled" discipline
+   in 'in2000_queuecommand()' and maybe 1 or 2 other places.
+   I _think_ it may have been a little too lax, causing an
+   occasional crash during full moon. A fully functional
+   /proc interface is now in place - if you want to play
+   with it, start by doing 'cat /proc/scsi/in2000/0'. You
+   can also use it to change a few run-time parameters on
+   the fly, but it's mostly for debugging. The curious
+   should take a good look at 'in2000_proc_info()' in the
+   in2000.c file to get an understanding of what it's all
+   about; I figure that people who are really into it will
+   want to add features suited to their own needs...
+   Also, sync is now DISABLED by default.
+
+UPDATE NEWS: version 1.27 - 10 Apr 96
+
+   Fixed a well-hidden bug in the adaptive-disconnect code
+   that would show up every now and then during extreme
+   heavy loads involving 2 or more simultaneously active
+   devices. Thanks to Joe Mack for keeping my nose to the
+   grindstone on this one.
+
+UPDATE NEWS: version 1.26 - 07 Mar 96
+
+   1.25 had a nasty bug that bit people with swap partitions
+   and tape drives. Also, in my attempt to guess my way
+   through Intel assembly language, I made an error in the
+   inline code for IO writes. Made a few other changes and
+   repairs - this version (fingers crossed) should work well.
+
+UPDATE NEWS: version 1.25 - 05 Mar 96
+
+   Kernel 1.3.70 interrupt mods added; old kernels still OK.
+   Big help from Bill Earnest and David Willmore on speed
+   testing and optimizing: I think there's a real improvement
+   in this area.
+   New! User-friendly command-line interface for LILO and
+   module loading - the old method is gone, so you'll need
+   to read the comments for 'setup_strings' near the top
+   of in2000.c. For people with CDROM's or other devices
+   that have a tough time with sync negotiation, you can
+   now selectively disable sync on individual devices -
+   search for the 'nosync' keyword in the command-line
+   comments. Some of you disable the BIOS on the card, which
+   caused the auto-detect function to fail; there is now a
+   command-line option to force detection of a ROM-less card.
+
+UPDATE NEWS: version 1.24a - 24 Feb 96
+
+   There was a bug in the synchronous transfer code. Only
+   a few people downloaded before I caught it - could have
+   been worse.
+
+UPDATE NEWS: version 1.24 - 23 Feb 96
+
+   Lots of good changes. Advice from Bill Earnest resulted
+   in much better detection of cards, more efficient usage
+   of the fifo, and (hopefully) faster data transfers. The
+   jury is still out on speed - I hope it's improved some.
+   One nifty new feature is a cool way of doing disconnect/
+   reselect. The driver defaults to what I'm calling
+   'adaptive disconnect' - meaning that each command is
+   evaluated individually as to whether or not it should be
+   run with the option to disconnect/reselect (if the device
+   chooses), or as a "SCSI-bus-hog". When several devices
+   are operating simultaneously, disconnects are usually an
+   advantage. In a single device system, or if only 1 device
+   is being accessed, transfers usually go faster if disconnects
+   are not allowed.
+
+
+
+The default arguments (you get these when you don't give an 'in2000'
+command-line argument, or you give a blank argument) will cause
+the driver to do adaptive disconnect, synchronous transfers, and a
+minimum of debug messages. If you want to fool with the options,
+search for 'setup_strings' near the top of the in2000.c file and
+check the 'hostdata->args' section in in2000.h - but be warned! Not
+everything is working yet (some things will never work, probably).
+I believe that disabling disconnects (DIS_NEVER) will allow you
+to choose a LEVEL2 value higher than 'L2_BASIC', but I haven't
+spent a lot of time testing this. You might try 'ENABLE_CLUSTERING'
+to see what happens: my tests showed little difference either way.
+There's also a define called 'DEFAULT_SX_PER'; this sets the data
+transfer speed for the asynchronous mode. I've put it at 500 ns
+despite the fact that the card could handle settings of 376 or
+252, because I'm not really sure if certain devices or maybe bad
+cables might have trouble at higher speeds. I couldn't find any
+info in my various SCSI references that talk about this in language
+I could understand, so decided to compromise with 500. This is still
+faster than the old driver was set at (I think). Can someone explain
+the significance of the bus transfer speed setting? Do devices on
+the bus ever care what it is? Is cable quality a factor here?
+Regardless, you can choose your own default through the command-
+line with the 'period' keyword.
+
+
+------------------------------------------------
+***********  DIP switch settings  **************
+------------------------------------------------
+
+   sw1-1 sw1-2    BIOS address (hex)
+   -----------------------------------------
+    off   off     C8000 - CBFF0
+    on    off     D8000 - DBFF0
+    off   on      D0000 - D3FF0
+    on    on      BIOS disabled
+
+   sw1-3 sw1-4    IO port address (hex)
+   ------------------------------------
+    off   off     220 - 22F
+    on    off     200 - 20F
+    off   on      110 - 11F
+    on    on      100 - 10F
+
+   sw1-5 sw1-6 sw1-7    Interrupt
+   ------------------------------
+    off   off   off     15
+    off   on    off     14
+    off   off   on      11
+    off   on    on      10
+    on    -     -       disabled
+
+   sw1-8 function depends on BIOS version. In earlier versions this
+   controlled synchronous data transfer support for MSDOS:
+      off = disabled
+      on  = enabled
+   In later ROMs (starting with 01.3 in April 1994) sw1-8 controls
+   the "greater than 2 disk drive" feature that first appeared in
+   MSDOS 5.0 (ignored by linux):
+      off = 2 drives maximum
+      on  = 7 drives maximum
+
+   sw1-9    Floppy controller
+   --------------------------
+    off     disabled
+    on      enabled
+
+------------------------------------------------
+
+   I should mention that Drew Eckhardt's 'Generic NCR5380' sources
+   were my main inspiration, with lots of reference to the IN2000
+   driver currently distributed in the kernel source. I also owe
+   much to a driver written by Hamish Macdonald for Linux-m68k(!).
+   And to Eric Wright for being an ALPHA guinea pig. And to Bill
+   Earnest for 2 tons of great input and information. And to David
+   Willmore for extensive 'bonnie' testing. And to Joe Mack for
+   continual testing and feedback.
+
+
+            John Shifflett    jshiffle@netcom.com
+
diff -urN linux-2.0.31-pre4/drivers/sound/sequencer.c linux-2.0.31-pre5/drivers/sound/sequencer.c
--- linux-2.0.31-pre4/drivers/sound/sequencer.c	1996-08-20 23:18:09.000000000 -0700
+++ linux-2.0.31-pre5/drivers/sound/sequencer.c	2003-08-15 15:03:58.000000000 -0700
@@ -2038,7 +2038,7 @@
   iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * IEV_SZ));
   if (sound_nblocks < 1024)
     sound_nblocks++;;
-  if (queue == NULL)
+  if (iqueue == NULL)
     {
       printk ("Sound: Can't allocate memory for sequencer input queue\n");
       return;
diff -urN linux-2.0.31-pre4/fs/binfmt_elf.c linux-2.0.31-pre5/fs/binfmt_elf.c
--- linux-2.0.31-pre4/fs/binfmt_elf.c	1997-01-02 10:29:30.000000000 -0800
+++ linux-2.0.31-pre5/fs/binfmt_elf.c	2003-08-15 15:03:58.000000000 -0700
@@ -880,7 +880,7 @@
  */
 static inline int maydump(struct vm_area_struct *vma)
 {
-	if (!(vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC)))
+	if (!(vma->vm_flags & (VM_READ|VM_EXEC)))
 		return 0;
 #if 1
 	if (vma->vm_flags & (VM_WRITE|VM_GROWSUP|VM_GROWSDOWN))
@@ -1140,7 +1140,7 @@
 		set_fs(fs);
 		
 		len = current->mm->arg_end - current->mm->arg_start;
-		len = len >= ELF_PRARGSZ ? ELF_PRARGSZ : len;
+		len = (len >= ELF_PRARGSZ-1) ? ELF_PRARGSZ-1 : len;
 		memcpy_fromfs(&psinfo.pr_psargs,
 			      (const char *)current->mm->arg_start, len);
 		for(i = 0; i < len; i++)
diff -urN linux-2.0.31-pre4/fs/isofs/inode.c linux-2.0.31-pre5/fs/isofs/inode.c
--- linux-2.0.31-pre4/fs/isofs/inode.c	1996-08-17 11:19:28.000000000 -0700
+++ linux-2.0.31-pre5/fs/isofs/inode.c	2003-08-15 15:03:58.000000000 -0700
@@ -476,6 +476,34 @@
 		printk("_isofs_bmap: block<0");
 		return 0;
 	}
+
+	/*
+	 * If we are beyond the end of this file, don't give out any
+	 * blocks.
+	 */
+	if( (block << ISOFS_BUFFER_BITS(inode)) > inode->i_size )
+	  {
+	    off_t	max_legal_read_offset;
+
+	    /*
+	     * If we are *way* beyond the end of the file, print a message.
+	     * Access beyond the end of the file up to the next page boundary
+	     * is normal because of the way the page cache works.
+	     * In this case, we just return 0 so that we can properly fill
+	     * the page with useless information without generating any
+	     * I/O errors.
+	     */
+	    max_legal_read_offset = (inode->i_size + PAGE_SIZE - 1) 
+	      & ~(PAGE_SIZE - 1);
+	    if( (block << ISOFS_BUFFER_BITS(inode)) >= max_legal_read_offset )
+	      {
+
+		printk("_isofs_bmap: block>= EOF(%d, %d)", block, 
+		       inode->i_size);
+	      }
+	    return 0;
+	  }
+
 	return (inode->u.isofs_i.i_first_extent >> ISOFS_BUFFER_BITS(inode)) + block;
 }
 
diff -urN linux-2.0.31-pre4/fs/isofs/namei.c linux-2.0.31-pre5/fs/isofs/namei.c
--- linux-2.0.31-pre4/fs/isofs/namei.c	1996-11-30 02:21:20.000000000 -0800
+++ linux-2.0.31-pre5/fs/isofs/namei.c	2003-08-15 15:03:58.000000000 -0700
@@ -242,9 +242,12 @@
 	if (!ino) {
 		char *lcname;
 
-		/* If mounted with check=relaxed (and most likely norock),
-		   then first convert this name to lower case. */
-		if (dir->i_sb->u.isofs_sb.s_name_check == 'r'
+		/* First try the original name. If that doesn't work and the fs
+		 * was mounted with check=relaxed, convert the name to lower
+		 * case and try again.
+		 */
+		if (!(bh = isofs_find_entry(dir,name,len, &ino, &ino_back))
+		    && dir->i_sb->u.isofs_sb.s_name_check == 'r'
 		    && (lcname = kmalloc(len, GFP_KERNEL)) != NULL) {
 			int i;
 			char c;
@@ -256,8 +259,7 @@
 			}
 			bh = isofs_find_entry(dir,lcname,len, &ino, &ino_back);
 			kfree(lcname);
-		} else
-			bh = isofs_find_entry(dir,name,len, &ino, &ino_back);
+		}
 
 		if (!bh) {
 			iput(dir);
diff -urN linux-2.0.31-pre4/include/asm-alpha/socket.h linux-2.0.31-pre5/include/asm-alpha/socket.h
--- linux-2.0.31-pre4/include/asm-alpha/socket.h	1996-03-24 02:09:37.000000000 -0800
+++ linux-2.0.31-pre5/include/asm-alpha/socket.h	2003-08-15 15:03:58.000000000 -0700
@@ -33,4 +33,6 @@
 #define SO_PRIORITY	12
 #define SO_BSDCOMPAT	14
 
+#define SO_BINDTODEVICE 25
+
 #endif /* _ASM_SOCKET_H */
diff -urN linux-2.0.31-pre4/include/asm-i386/pgtable.h linux-2.0.31-pre5/include/asm-i386/pgtable.h
--- linux-2.0.31-pre4/include/asm-i386/pgtable.h	1997-03-28 16:08:17.000000000 -0800
+++ linux-2.0.31-pre5/include/asm-i386/pgtable.h	2003-08-15 15:03:58.000000000 -0700
@@ -375,6 +375,8 @@
 	free_page((unsigned long) pte);
 }
 
+extern const char bad_pmd_string[];
+
 extern inline pte_t * pte_alloc_kernel(pmd_t * pmd, unsigned long address)
 {
 	address = (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
@@ -391,7 +393,7 @@
 		free_page((unsigned long) page);
 	}
 	if (pmd_bad(*pmd)) {
-		printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+		printk(bad_pmd_string, pmd_val(*pmd));
 		pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
 		return NULL;
 	}
@@ -444,7 +446,7 @@
 }
 
 fix:
-	printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
+	printk(bad_pmd_string, pmd_val(*pmd));
 oom:
 	pmd_val(*pmd) = _PAGE_TABLE | (unsigned long) BAD_PAGETABLE;
 	return NULL;
diff -urN linux-2.0.31-pre4/include/asm-i386/socket.h linux-2.0.31-pre5/include/asm-i386/socket.h
--- linux-2.0.31-pre4/include/asm-i386/socket.h	1996-03-24 02:47:39.000000000 -0800
+++ linux-2.0.31-pre5/include/asm-i386/socket.h	2003-08-15 15:03:58.000000000 -0700
@@ -22,4 +22,6 @@
 #define SO_BSDCOMPAT	14
 /* To add :#define SO_REUSEPORT 15 */
 
+#define SO_BINDTODEVICE 25
+
 #endif /* _ASM_SOCKET_H */
diff -urN linux-2.0.31-pre4/include/linux/if.h linux-2.0.31-pre5/include/linux/if.h
--- linux-2.0.31-pre4/include/linux/if.h	1996-10-08 09:45:28.000000000 -0700
+++ linux-2.0.31-pre5/include/linux/if.h	2003-08-15 15:03:58.000000000 -0700
@@ -39,6 +39,12 @@
 #define IFF_SLAVE	0x800		/* slave of a load balancer	*/
 
 #define IFF_MULTICAST	0x1000		/* Supports multicast		*/
+#define IFF_SOFTHEADERS 0x2000		/* Device cannot construct headers
+					 * until broadcast time. Therefore
+					 * SOCK_PACKET must call header
+					 * construction. Private flag.
+					 * Never visible outside of kernel.
+					 */
 
 /*
  * The ifaddr structure contains information about one address
diff -urN linux-2.0.31-pre4/include/linux/interrupt.h linux-2.0.31-pre5/include/linux/interrupt.h
--- linux-2.0.31-pre4/include/linux/interrupt.h	1996-10-31 04:32:07.000000000 -0800
+++ linux-2.0.31-pre5/include/linux/interrupt.h	2003-08-15 15:03:58.000000000 -0700
@@ -33,6 +33,7 @@
 	DIGI_BH,
 	SERIAL_BH,
 	RISCOM8_BH,
+ 	SPECIALIX_BH,
 	BAYCOM_BH,
 	NET_BH,
 	IMMEDIATE_BH,
diff -urN linux-2.0.31-pre4/include/linux/major.h linux-2.0.31-pre5/include/linux/major.h
--- linux-2.0.31-pre4/include/linux/major.h	1996-11-16 03:00:47.000000000 -0800
+++ linux-2.0.31-pre5/include/linux/major.h	2003-08-15 15:03:58.000000000 -0700
@@ -69,6 +69,9 @@
 #define APBLOCK_MAJOR   60   /* AP1000 Block device */
 #define DDV_MAJOR       61   /* AP1000 DDV block device */
 
+#define SPECIALIX_NORMAL_MAJOR 75
+#define SPECIALIX_CALLOUT_MAJOR 76
+
 /*
  * Tests for SCSI devices.
  */
diff -urN linux-2.0.31-pre4/include/linux/net_alias.h linux-2.0.31-pre5/include/linux/net_alias.h
--- linux-2.0.31-pre4/include/linux/net_alias.h	1997-03-28 16:09:02.000000000 -0800
+++ linux-2.0.31-pre5/include/linux/net_alias.h	2003-08-15 15:03:58.000000000 -0700
@@ -2,7 +2,7 @@
  *		NET_ALIAS network device aliasing definitions.
  *
  *
- * Version:	@(#)net_alias.h	0.43   12/20/95
+ * Version:	@(#)net_alias.h	0.50   4/20/97
  *
  * Author:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
  *
@@ -12,6 +12,10 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *	
+ * Fixes:
+ *	Juan Jose Ciarlante	:	Added tx/rx stats for aliases.
+ *	Juan Jose Ciarlante 	:	hash_tab size now in net_alias.c
+ *	Juan Jose Ciarlante	:	added sysctl interface
  */
 
 #ifndef _NET_ALIAS_H
@@ -21,12 +25,6 @@
 #include <linux/if.h>
 #include <linux/netdevice.h>
 
-/*
- * max. alias slot number allowed 
- */
-
-#define NET_ALIAS_MAX_SLOT  256
-
 struct net_alias;
 struct net_alias_info;
 struct net_alias_type;
@@ -47,6 +45,8 @@
   struct device *main_dev;	/* pointer to main device */
   struct net_alias_type *nat;	/* alias type object bound */
   struct net_alias *next;	/* next alias (hashed linked list) */
+  unsigned long rx_lookups;     /* 'fake' rx pkts */
+  unsigned long tx_lookups;     /* 'fake' tx pkts */
 };
 
 
@@ -58,8 +58,11 @@
 struct net_alias_info
 {
   int n_aliases;		/* num aliases */
+  int truesize;                 /* actual malloc size for struct + hashtab */
   struct device *taildev;	/* my last (alias) device */
-  struct net_alias *hash_tab[16]; /* hashed alias table */
+  int max_aliases;              /* max aliases allowed for main device */
+  unsigned hash_tab_size;       /* hash_tab size in elements */
+  struct net_alias *hash_tab[0]; /* hashed alias table */
 };
 
 /*
@@ -110,23 +113,42 @@
   return (dev->alias_info != NULL);
 }
 
-
+/*
+ *	Initialise net_alias module
+ */
 extern void net_alias_init(void);
 
+/*
+ *	dev_get() with added aliasing magic
+ */
 extern struct device * net_alias_dev_get(char *dev_name, int aliasing_ok, int *err, struct sockaddr *sa, void *data);
 extern int net_alias_dev_rehash(struct device *dev, struct sockaddr *sa);
 
+/*
+ *	PROC_FS entries
+ */
 extern int net_alias_getinfo(char *buf, char **, off_t , int , int );
 extern int net_alias_types_getinfo(char *buf, char **, off_t , int , int );
 
+/*
+ *	net_alias_type (address family) registration 
+ */
 extern int register_net_alias_type(struct net_alias_type *nat, int type);
 extern int unregister_net_alias_type(struct net_alias_type *nat);
 
+/*
+ *	get alias device _with_ specified address
+ */
 extern struct device * net_alias_dev_chk(struct device *main_dev, struct sockaddr *sa, int flags_on, int flags_off);
 extern struct device * net_alias_dev_chk32(struct device *main_dev, int family, __u32 addr32, int flags_on, int flags_off);
 
-extern struct device * net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst);
-extern struct device * net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst);
+/*
+ *	get 'closest' device to specified address (returns main_dev if
+ *	nothing better)
+ *	if succesfull, also increment rx stats.
+ */
+extern struct device * net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst);
+extern struct device * net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst);
 
 
 /*
@@ -169,4 +191,30 @@
   return nextdev;
 }
 
+/*
+ *	lookup counters (used for alias devices stats)
+ */
+static __inline__ void net_alias_inc_rx(struct net_alias *alias)
+{
+        if (alias != NULL) alias->rx_lookups++;
+}
+static __inline__ void net_alias_inc_tx(struct net_alias *alias)
+{
+        if (alias != NULL) alias->tx_lookups++;
+}
+
+/*
+ *	To be called when passing down a pkt, to _switch_ from alias device
+ *	to actual device, also incr. alias tx counter.
+ */
+static __inline__ struct device *net_alias_dev_tx(struct device *dev)
+{
+        struct net_alias *alias = dev->my_alias;
+        if (alias) {
+                net_alias_inc_tx(alias);
+                return alias->main_dev;
+        } 
+        return dev;
+}
+
 #endif  /* _NET_ALIAS_H */
diff -urN linux-2.0.31-pre4/include/linux/pci.h linux-2.0.31-pre5/include/linux/pci.h
--- linux-2.0.31-pre4/include/linux/pci.h	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/include/linux/pci.h	2003-08-15 15:03:58.000000000 -0700
@@ -31,14 +31,14 @@
  *	  PCI-CPU bridge or PCI-ISA bridge.
  *	- If you can't find the actual information in your hardware
  *	  booklet, try to read the references of the chip on the board.
- *	- Send all that to linux-pcisupport@cao-vlsi.ibp.fr,
+ *	- Send all that to linux-pcisupport@cck.uni-kl.de
  *	  and I'll add your device to the list as soon as possible
  *
  * BEFORE you send a mail, please check the latest linux releases
  * to be sure it has not been recently added.
  *
  *        Thanks
- *		Frederic Potter.
+ *		Jens Maurer
  */
 
 
@@ -228,11 +228,17 @@
 #define PCI_DEVICE_ID_NCR_53C820	0x0002
 #define PCI_DEVICE_ID_NCR_53C825	0x0003
 #define PCI_DEVICE_ID_NCR_53C815	0x0004
+#define PCI_DEVICE_ID_NCR_53C860	0x0006
+#define PCI_DEVICE_ID_NCR_53C896	0x000b
+#define PCI_DEVICE_ID_NCR_53C895	0x000c
+#define PCI_DEVICE_ID_NCR_53C885	0x000d
+#define PCI_DEVICE_ID_NCR_53C875	0x000f
 
 #define PCI_VENDOR_ID_ATI		0x1002
 #define PCI_DEVICE_ID_ATI_68800		0x4158
 #define PCI_DEVICE_ID_ATI_215CT222	0x4354
 #define PCI_DEVICE_ID_ATI_210888CX	0x4358
+#define PCI_DEVICE_ID_ATI_215GT		0x4754
 #define PCI_DEVICE_ID_ATI_210888GX	0x4758
 
 #define PCI_VENDOR_ID_VLSI		0x1004
@@ -252,6 +258,7 @@
 #define PCI_DEVICE_ID_TSENG_W32P_b	0x3205
 #define PCI_DEVICE_ID_TSENG_W32P_c	0x3206
 #define PCI_DEVICE_ID_TSENG_W32P_d	0x3207
+#define PCI_DEVICE_ID_TSENG_ET6000	0x3208
 
 #define PCI_VENDOR_ID_WEITEK		0x100e
 #define PCI_DEVICE_ID_WEITEK_P9000	0x9001
@@ -264,20 +271,25 @@
 #define PCI_DEVICE_ID_DEC_TULIP_FAST	0x0009
 #define PCI_DEVICE_ID_DEC_FDDI		0x000F
 #define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014
+#define PCI_DEVICE_ID_DEC_21142		0x0019
 #define PCI_DEVICE_ID_DEC_21052		0x0021
 #define PCI_DEVICE_ID_DEC_21152		0x0024
 
 #define PCI_VENDOR_ID_CIRRUS		0x1013
+#define PCI_DEVICE_ID_CIRRUS_7548	0x0038
 #define PCI_DEVICE_ID_CIRRUS_5430	0x00a0
 #define PCI_DEVICE_ID_CIRRUS_5434_4	0x00a4
 #define PCI_DEVICE_ID_CIRRUS_5434_8	0x00a8
 #define PCI_DEVICE_ID_CIRRUS_5436	0x00ac
+#define PCI_DEVICE_ID_CIRRUS_5446	0x00b8
 #define PCI_DEVICE_ID_CIRRUS_6729	0x1100
 #define PCI_DEVICE_ID_CIRRUS_7542	0x1200
 #define PCI_DEVICE_ID_CIRRUS_7543	0x1202
+#define PCI_DEVICE_ID_CIRRUS_7541	0x1204
 
 #define PCI_VENDOR_ID_IBM		0x1014
 #define PCI_DEVICE_ID_IBM_82G2675	0x001d
+#define PCI_DEVICE_ID_IBM_82351		0x0022
 
 #define PCI_VENDOR_ID_WD		0x101c
 #define PCI_DEVICE_ID_WD_7197		0x3296
@@ -304,6 +316,7 @@
 #define PCI_DEVICE_ID_CT_65545		0x00d8
 #define PCI_DEVICE_ID_CT_65548		0x00dc
 #define PCI_DEVICE_ID_CT_65550		0x00e0
+#define PCI_DEVICE_ID_CT_65554		0x00e4
 
 #define PCI_VENDOR_ID_MIRO		0x1031
 #define PCI_DEVICE_ID_MIRO_36050	0x5601
@@ -321,6 +334,8 @@
 #define PCI_DEVICE_ID_SI_601		0x0601
 #define PCI_DEVICE_ID_SI_5511		0x5511
 #define PCI_DEVICE_ID_SI_5513		0x5513
+#define PCI_DEVICE_ID_SI_5571		0x5571
+#define PCI_DEVICE_ID_SI_7001		0x7001
 
 #define PCI_VENDOR_ID_HP		0x103c
 #define PCI_DEVICE_ID_HP_J2585A		0x1030
@@ -352,10 +367,15 @@
 #define PCI_VENDOR_ID_OAK		0x104e
 #define PCI_DEVICE_ID_OAK_OTI107	0x0107
 
-/* Winbond have two vendor ID! See 0x10ad as well */
+/* Winbond have two vendor IDs! See 0x10ad as well */
 #define PCI_VENDOR_ID_WINBOND2		0x1050
 #define PCI_DEVICE_ID_WINBOND2_89C940	0x0940
 
+#define PCI_VENDOR_ID_MOTOROLA		0x1057
+#define PCI_DEVICE_ID_MOTOROLA_MPC105	0x0001
+#define PCI_DEVICE_ID_MOTOROLA_MPC106	0x0002
+#define PCI_DEVICE_ID_MOTOROLA_RAVEN	0x4801
+
 #define PCI_VENDOR_ID_PROMISE		0x105a
 #define PCI_DEVICE_ID_PROMISE_5300	0x5300
 
@@ -402,6 +422,9 @@
 #define PCI_DEVICE_ID_VISION_QD8500	0x0001
 #define PCI_DEVICE_ID_VISION_QD8580	0x0002
 
+#define PCI_VENDOR_ID_BROOKTREE		0x109e
+#define PCI_DEVICE_ID_BROOKTREE_848	0x0350
+
 #define PCI_VENDOR_ID_SIERRA		0x10a8
 #define PCI_DEVICE_ID_SIERRA_STB	0x0000
 
@@ -411,12 +434,16 @@
 #define PCI_VENDOR_ID_WINBOND		0x10ad
 #define PCI_DEVICE_ID_WINBOND_83769	0x0001
 #define PCI_DEVICE_ID_WINBOND_82C105	0x0105
+#define PCI_DEVICE_ID_WINBOND_83C553	0x0565
 
 #define PCI_VENDOR_ID_3COM		0x10b7
 #define PCI_DEVICE_ID_3COM_3C590	0x5900
 #define PCI_DEVICE_ID_3COM_3C595TX	0x5950
 #define PCI_DEVICE_ID_3COM_3C595T4	0x5951
 #define PCI_DEVICE_ID_3COM_3C595MII	0x5952
+#define PCI_DEVICE_ID_3COM_3C900TPO	0x9000
+#define PCI_DEVICE_ID_3COM_3C900COMBO	0x9001
+#define PCI_DEVICE_ID_3COM_3C905TX	0x9050
 
 #define PCI_VENDOR_ID_AL		0x10b9
 #define PCI_DEVICE_ID_AL_M1445		0x1445
@@ -428,8 +455,12 @@
 #define PCI_DEVICE_ID_AL_M1513		0x1513
 #define PCI_DEVICE_ID_AL_M4803		0x5215
 
+#define PCI_VENDOR_ID_NEOMAGIC          0x10c8
+#define PCI_DEVICE_ID_NEOMAGIC_MAGICGRAPH_NM2070 0x0001
+
 #define PCI_VENDOR_ID_ASP		0x10cd
 #define PCI_DEVICE_ID_ASP_ABP940	0x1200
+#define PCI_DEVICE_ID_ASP_ABP940U	0x1300
 
 #define PCI_VENDOR_ID_CERN		0x10dc
 #define PCI_DEVICE_ID_CERN_SPSB_PMC	0x0001
@@ -441,11 +472,16 @@
 #define PCI_VENDOR_ID_TEKRAM2		0x10e1
 #define PCI_DEVICE_ID_TEKRAM2_690c	0x690c
 
+#define PCI_VENDOR_ID_TUNDRA		0x10e3
+#define PCI_DEVICE_ID_TUNDRA_CA91C042	0x0000
+
 #define PCI_VENDOR_ID_AMCC		0x10e8
 #define PCI_DEVICE_ID_AMCC_MYRINET	0x8043
+#define PCI_DEVICE_ID_AMCC_S5933	0x807d
 
 #define PCI_VENDOR_ID_INTERG		0x10ea
 #define PCI_DEVICE_ID_INTERG_1680	0x1680
+#define PCI_DEVICE_ID_INTERG_1682	0x1682
 
 #define PCI_VENDOR_ID_REALTEK		0x10ec
 #define PCI_DEVICE_ID_REALTEK_8029	0x8029
@@ -456,7 +492,10 @@
 #define PCI_VENDOR_ID_VIA		0x1106
 #define PCI_DEVICE_ID_VIA_82C505	0x0505
 #define PCI_DEVICE_ID_VIA_82C561	0x0561
+#define PCI_DEVICE_ID_VIA_82C586_1	0x0571
 #define PCI_DEVICE_ID_VIA_82C576	0x0576
+#define PCI_DEVICE_ID_VIA_82C585	0x0585
+#define PCI_DEVICE_ID_VIA_82C586_0	0x0586
 #define PCI_DEVICE_ID_VIA_82C416	0x1571
 
 #define PCI_VENDOR_ID_VORTEX		0x1119
@@ -503,15 +542,22 @@
 #define PCI_DEVICE_ID_MUTECH_MV1000	0x0001
 
 #define PCI_VENDOR_ID_TOSHIBA		0x1179
+#define PCI_DEVICE_ID_TOSHIBA_601	0x0601
 
 #define PCI_VENDOR_ID_ZEITNET		0x1193
 #define PCI_DEVICE_ID_ZEITNET_1221	0x0001
 #define PCI_DEVICE_ID_ZEITNET_1225	0x0002
 
+#define PCI_VENDOR_ID_OMEGA		0x119b
+#define PCI_DEVICE_ID_OMEGA_PCMCIA     	0x1221
+
 #define PCI_VENDOR_ID_SPECIALIX		0x11cb
 #define PCI_DEVICE_ID_SPECIALIX_XIO	0x4000
 #define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
 
+#define PCI_VENDOR_ID_ZORAN		0x11de
+#define PCI_DEVICE_ID_ZORAN_36120	0x6120
+
 #define PCI_VENDOR_ID_COMPEX		0x11f6
 #define PCI_DEVICE_ID_COMPEX_ENET100VG4	0x0112
 #define PCI_DEVICE_ID_COMPEX_RL2000	0x1401
@@ -528,19 +574,38 @@
 #define PCI_DEVICE_ID_CYCLOM_Z_Lo	0x0200
 #define PCI_DEVICE_ID_CYCLOM_Z_Hi	0x0201
 
+#define PCI_VENDOR_ID_SIGMA_DESIGNS	0x1236
+#define PCI_DEVICE_ID_SD_REALMAGIC64GX	0x6401
+
+#define PCI_VENDOR_ID_3DFX		0x121a
+#define PCI_DEVICE_ID_3DFX_VOODOO	0x0001
+
+#define PCI_VENDOR_ID_SIGMADES		0x1236
+#define PCI_DEVICE_ID_SIGMADES_6425	0x6401
+
+#define PCI_VENDOR_ID_OPTIBASE		0x1255
+#define PCI_DEVICE_ID_OPTIBASE_FORGE	0x1110
+#define PCI_DEVICE_ID_OPTIBASE_FUSION	0x1210
+#define PCI_DEVICE_ID_OPTIBASE_VPLEX	0x2120
+#define PCI_DEVICE_ID_OPTIBASE_VPLEXCC	0x2120
+#define PCI_DEVICE_ID_OPTIBASE_VQUEST	0x2130
+
 #define PCI_VENDOR_ID_SYMPHONY		0x1c1c
 #define PCI_DEVICE_ID_SYMPHONY_101	0x0001
 
 #define PCI_VENDOR_ID_TEKRAM		0x1de1
 #define PCI_DEVICE_ID_TEKRAM_DC290	0xdc29
 
-#define PCI_VENDOR_ID_3DLABS		0x3D3D
+#define PCI_VENDOR_ID_3DLABS		0x3d3d
 #define PCI_DEVICE_ID_3DLABS_300SX	0x0001
+#define PCI_DEVICE_ID_3DLABS_DELTA	0x0003
+#define PCI_DEVICE_ID_3DLABS_PERMEDIA	0x0004
 
 #define PCI_VENDOR_ID_AVANCE		0x4005
 #define PCI_DEVICE_ID_AVANCE_2302	0x2302
 
 #define PCI_VENDOR_ID_S3		0x5333
+#define PCI_DEVICE_ID_S3_PLATO_PXS	0x0551
 #define PCI_DEVICE_ID_S3_ViRGE		0x5631
 #define PCI_DEVICE_ID_S3_TRIO		0x8811
 #define PCI_DEVICE_ID_S3_AURORA64VP	0x8812
@@ -553,6 +618,9 @@
 #define PCI_DEVICE_ID_S3_964_1		0x88d0
 #define PCI_DEVICE_ID_S3_964_2		0x88d1
 #define PCI_DEVICE_ID_S3_968		0x88f0
+#define PCI_DEVICE_ID_S3_TRIO64V2	0x8901
+#define PCI_DEVICE_ID_S3_PLATO_PXG	0x8902
+#define PCI_DEVICE_ID_S3_ViRGE_DXGX	0x8a01
 
 #define PCI_VENDOR_ID_INTEL		0x8086
 #define PCI_DEVICE_ID_INTEL_82375	0x0482
@@ -560,6 +628,8 @@
 #define PCI_DEVICE_ID_INTEL_82378	0x0484
 #define PCI_DEVICE_ID_INTEL_82430	0x0486
 #define PCI_DEVICE_ID_INTEL_82434	0x04a3
+#define PCI_DEVICE_ID_INTEL_82092AA_0	0x1221
+#define PCI_DEVICE_ID_INTEL_82092AA_1	0x1222
 #define PCI_DEVICE_ID_INTEL_7116	0x1223
 #define PCI_DEVICE_ID_INTEL_82596	0x1226
 #define PCI_DEVICE_ID_INTEL_82865	0x1227
@@ -567,14 +637,24 @@
 #define PCI_DEVICE_ID_INTEL_82437	0x122d
 #define PCI_DEVICE_ID_INTEL_82371_0	0x122e
 #define PCI_DEVICE_ID_INTEL_82371_1	0x1230
+#define PCI_DEVICE_ID_INTEL_82371MX	0x1234
+#define PCI_DEVICE_ID_INTEL_82430MX	0x1235
 #define PCI_DEVICE_ID_INTEL_82441	0x1237
 #define PCI_DEVICE_ID_INTEL_82439	0x1250
 #define PCI_DEVICE_ID_INTEL_82371SB_0	0x7000
 #define PCI_DEVICE_ID_INTEL_82371SB_1	0x7010
 #define PCI_DEVICE_ID_INTEL_82371SB_2	0x7020
 #define PCI_DEVICE_ID_INTEL_82437VX	0x7030
+#define PCI_DEVICE_ID_INTEL_82439TX	0x7100
+#define PCI_DEVICE_ID_INTEL_82371AB_0	0x7110
 #define PCI_DEVICE_ID_INTEL_82371AB	0x7111
+#define PCI_DEVICE_ID_INTEL_82371AB_2	0x7112
+#define PCI_DEVICE_ID_INTEL_82371AB_3	0x7113
 #define PCI_DEVICE_ID_INTEL_P6		0x84c4
+#define PCI_DEVICE_ID_INTEL_82450GX	0x84c5
+
+#define PCI_VENDOR_ID_KTI		0x8e2e
+#define PCI_DEVICE_ID_KTI_ET32P2	0x3000
 
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
@@ -595,9 +675,10 @@
 #define PCI_VENDOR_ID_ATRONICS		0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015	0x2015
 
-#define PCI_VENDOR_ID_HER		0xedd8
-#define PCI_DEVICE_ID_HER_STING		0xa091
-#define PCI_DEVICE_ID_HER_STINGARK	0xa099
+#define PCI_VENDOR_ID_ARK		0xedd8
+#define PCI_DEVICE_ID_ARK_STING		0xa091
+#define PCI_DEVICE_ID_ARK_STINGARK	0xa099
+#define PCI_DEVICE_ID_ARK_2000MT	0xa0a1
 
 /*
  * The PCI interface treats multi-function devices as independent
diff -urN linux-2.0.31-pre4/include/linux/proc_fs.h linux-2.0.31-pre5/include/linux/proc_fs.h
--- linux-2.0.31-pre4/include/linux/proc_fs.h	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/include/linux/proc_fs.h	2003-08-15 15:03:58.000000000 -0700
@@ -105,6 +105,7 @@
 	PROC_NET_STRIP_STATUS,
 	PROC_NET_STRIP_TRACE,
 	PROC_NET_IPAUTOFW,
+	PROC_NET_Z8530,
 	PROC_NET_LAST
 };
 
diff -urN linux-2.0.31-pre4/include/linux/route.h linux-2.0.31-pre5/include/linux/route.h
--- linux-2.0.31-pre4/include/linux/route.h	1996-10-08 09:48:40.000000000 -0700
+++ linux-2.0.31-pre5/include/linux/route.h	2003-08-15 15:03:58.000000000 -0700
@@ -52,6 +52,7 @@
 #define RTF_WINDOW	0x0080		/* per route window clamping	  */
 #define RTF_IRTT	0x0100		/* Initial round trip time	  */
 #define RTF_REJECT	0x0200		/* Reject route			  */
+#define RTF_NOTCACHED	0x0400		/* this route isn't cached        */
 
 /*
  *	This structure is passed from the kernel to user space by netlink
diff -urN linux-2.0.31-pre4/include/linux/scc.h linux-2.0.31-pre5/include/linux/scc.h
--- linux-2.0.31-pre4/include/linux/scc.h	1996-04-11 23:49:46.000000000 -0700
+++ linux-2.0.31-pre5/include/linux/scc.h	2003-08-15 15:03:58.000000000 -0700
@@ -1,8 +1,10 @@
-/* $Id: scc.h,v 1.15 1995/11/16 20:19:26 jreuter Exp jreuter $ */
+/* $Id: scc.h,v 1.29 1997/04/10 15:27:21 jreuter Exp jreuter $ */
 
 #ifndef	_SCC_H
 #define	_SCC_H
 
+#include <linux/if_ether.h>
+
 /* selection of hardware types */
 
 #define PA0HZP		0x00	/* hardware type for PA0HZP SCC card and compatible */
@@ -16,19 +18,38 @@
 
 #define SCC_PARANOIA_CHECK	/* tell the user if something is going wrong */
 
-/* ioctl() commands */
-
-#define TIOCSCCCFG	0x2200		/* set hardware parameters */
-#define TIOCSCCINI	0x2201		/* init driver */
-#define TIOCCHANINI	0x2202		/* init channel */
-
-#define TIOCCHANMEM	0x2210		/* adjust buffer pools */
-
-#define TIOCGKISS	0x2282		/* get kiss parameter */
-#define TIOCSKISS	0x2283		/* set kiss parameter */
-
-#define TIOCSCCSTAT	0x2284		/* get scc status */
+/* TTY ioctl() commands */
 
+#define TIOCSCCCFG	 _IOW('Z', 0, sizeof(struct scc_hw_config))	/* set hardware parameters */
+#define TIOCSCCINI	  _IO('Z', 1)					/* init driver */
+#define TIOCSCCCHANINI	 _IOW('Z', 2, sizeof(struct scc_modem))		/* init channel */
+#define TIOCSCCSMEM	 _IOW('Z', 3, sizeof(struct scc_mem_config))	/* adjust buffer pools */
+#define TIOCSCCGKISS	_IOWR('Z', 4, sizeof(struct scc_kiss_cmd))	/* get kiss parameter */
+#define TIOCSCCSKISS	 _IOW('Z', 5, sizeof(struct scc_kiss_cmd))	/* set kiss parameter */
+#define TIOCSCCGSTAT	 _IOR('Z', 6, sizeof(struct scc_stat))		/* get scc status */
+
+/* old TTY ioctl() commands */
+
+#define TIOCSCCCFG_OLD	0x2200
+#define TIOCSCCINI_OLD	0x2201
+#define TIOCCHANINI_OLD	0x2202
+#define TIOCCHANMEM_OLD 0x2210
+#define TIOCSKISS_OLD	0x2282
+#define TIOCGKISS_OLD	0x2283
+#define TIOCSCCSTAT_OLD 0x2284
+
+/* DEV ioctl() commands */
+
+enum SCC_IOCTL_CMD {
+	SIOCSCCRESERVED=SIOCDEVPRIVATE,
+	SIOCSCCCFG,
+	SIOCSCCINI,
+	SIOCSCCCHANINI,
+	SIOCSCCSMEM,
+	SIOCSCCGKISS,
+	SIOCSCCSKISS,
+	SIOCSCCGSTAT
+};
 
 /* magic number */
 
@@ -42,37 +63,60 @@
 
 /* KISS state machine */
 
-#define	KISS_IDLE	0
-#define KISS_DATA	1
-#define KISS_ESCAPE	2
-#define KISS_RXFRAME	3
+enum SCC_KISS_STATES {
+	KISS_IDLE,
+	KISS_DATA,
+	KISS_ESCAPE,
+	KISS_RXFRAME
+};
 
 /* Device parameter control (from WAMPES) */
 
-#define	PARAM_TXDELAY	1
-#define	PARAM_PERSIST	2
-#define	PARAM_SLOTTIME	3
-#define	PARAM_TXTAIL	4
-#define	PARAM_FULLDUP	5
-#define PARAM_SOFTDCD	6	/* was: PARAM_HW */
-#define PARAM_MUTE	7	/* ??? */
-#define PARAM_DTR       8
-#define PARAM_RTS	9
-#define PARAM_SPEED     10
-#define PARAM_ENDDELAY	11	/* ??? */
-#define PARAM_GROUP     12
-#define PARAM_IDLE      13
-#define PARAM_MIN       14
-#define	PARAM_MAXKEY	15
-#define PARAM_WAIT      16
-#define PARAM_MAXDEFER	17
-#define PARAM_TX        18
-#define PARAM_SLIP	19
-#define PARAM_RETURN	255	/* reset kiss mode */
+enum SCC_KISS_PARAMS {
+	PARAM_TXDELAY=1,
+	PARAM_PERSIST,
+	PARAM_SLOTTIME,
+	PARAM_TXTAIL,
+	PARAM_FULLDUP,
+	PARAM_SOFTDCD,		/* was: PARAM_HW */
+	PARAM_MUTE,		/* ??? */
+	PARAM_DTR,
+	PARAM_RTS,
+	PARAM_SPEED,
+	PARAM_ENDDELAY,		/* ??? */
+	PARAM_GROUP,
+	PARAM_IDLE,
+	PARAM_MIN,
+	PARAM_MAXKEY,
+	PARAM_WAIT,
+	PARAM_MAXDEFER,
+	PARAM_TX,
+	PARAM_HWEVENT=31,
+	PARAM_RETURN=255	/* reset kiss mode */
+};
+
+/* fulldup parameter */
+
+enum SCC_KISS_DUPLEX_MODES {
+	KISS_DUPLEX_HALF,	/* normal CSMA operation */
+	KISS_DUPLEX_FULL,	/* fullduplex, key down trx after transmission */
+	KISS_DUPLEX_LINK,	/* fullduplex, key down trx after 'idletime' sec */
+	KISS_DUPLEX_OPTIMA,	/* fullduplex, let the protocol layer control the hw */
+};
+
+/* misc. parameters */
 
 #define TIMER_OFF	65535U	/* to switch off timers */
 #define NO_SUCH_PARAM	65534U	/* param not implemented */
 
+/* HWEVENT parameter */
+
+enum SCC_HWEV_PARAMETERS {
+	HWEV_DCD_ON,
+	HWEV_DCD_OFF,
+	HWEV_ALL_SENT
+};
+
 /* channel grouping */
 
 #define RXGROUP		0x100	/* if set, only tx when all channels clear */
@@ -80,20 +124,26 @@
 
 /* Tx/Rx clock sources */
 
-#define CLK_DPLL	0	/* normal halfduplex operation */
-#define CLK_EXTERNAL	1	/* external clocking (G3RUH/DF9IC modems) */
-#define CLK_DIVIDER	2	/* Rx = DPLL, Tx = divider (fullduplex with */
-				/* modems without clock regeneration */
+enum SCC_CLK_SOURCES {
+	CLK_DPLL,	/* normal halfduplex operation */
+	CLK_EXTERNAL,	/* external clocking (G3RUH/DF9IC modems) */
+	CLK_DIVIDER	/* Rx = DPLL, Tx = divider (fullduplex with */
+			/* modems without clock regeneration */
+};
 
 /* Tx state */
 
-#define TXS_IDLE	0	/* Transmitter off, no data pending */
-#define TXS_BUSY	1	/* waiting for permission to send / tailtime */
-#define TXS_ACTIVE	2	/* Transmitter on, sending data */
-#define TXS_NEWFRAME	3	/* reset CRC and send (next) frame */
+enum SCC_TX_STATES {
+	TXS_IDLE,	/* Transmitter off, no data pending */
+	TXS_BUSY,	/* waiting for permission to send / tailtime */
+	TXS_ACTIVE,	/* Transmitter on, sending data */
+	TXS_NEWFRAME,	/* reset CRC and send (next) frame */
+	TXS_IDLE2,	/* Transmitter on, no data pending */
+	TXS_WAIT,	/* Waiting for Mintime to expire */
+	TXS_TIMEOUT	/* We had a transmission timeout */
+};
 
-#define TX_ON		1	/* command for scc_key_trx() */
-#define TX_OFF		0	/* dto */
+enum SCC_TX_KEY {TX_OFF, TX_ON}; /* command for scc_key_trx() */
 
 /* Buffer management */
 
@@ -115,13 +165,7 @@
 #define Outb(port, val)	outb(val, port)
 #endif
 
-/* some nasty macros (esp. Expired) */
-
-#define TIMER_STOPPED 65535U
-#define Running(k) (scc->k != TIMER_STOPPED)
-#define Expired(k) (scc->k != TIMER_STOPPED) && (!(scc->k) || (--(scc->k) == 0))
-#define Stop_Timer(k) scc->k = TIMER_STOPPED
-
+#define TIMER_OFF 65535U
 
 /* Basic message buffer structure */
 
@@ -140,17 +184,18 @@
 	unsigned char txdelay;		/* Transmit Delay 10 ms/cnt */
 	unsigned char persist;		/* Persistence (0-255) as a % */
 	unsigned char slottime;		/* Delay to wait on persistence hit */
-	unsigned char tailtime;		/* Delay after XMTR OFF */
+	unsigned char tailtime;		/* Delay after last byte written */
 	unsigned char fulldup;		/* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */
 	unsigned char waittime;		/* Waittime before any transmit attempt */
 	unsigned int  maxkeyup;		/* Maximum time to transmit (seconds) */
-	unsigned char mintime;		/* Minimal offtime after MAXKEYUP timeout */
+	unsigned char mintime;		/* Minimal offtime after MAXKEYUP timeout (seconds) */
 	unsigned int  idletime;		/* Maximum idle time in ALWAYS KEYED mode (seconds) */
 	unsigned int  maxdefer;		/* Timer for CSMA channel busy limit */
 	unsigned char tx_inhibit;	/* Transmit is not allowed when set */	
-	unsigned char group;		/* group ID for AX.25 TX interlocking */
-	unsigned char not_slip;		/* set to zero: use SLIP instead of KISS */
-	unsigned char softdcd;		/* use DPLL instead of DCD pin for carrier detect */
+	unsigned char group;		/* Group ID for AX.25 TX interlocking */
+	unsigned char mode;		/* 'normal' or 'hwctrl' mode (unused) */
+
+	unsigned char softdcd;		/* Use DPLL instead of DCD pin for carrier detect */
 };
 
 
@@ -182,6 +227,8 @@
 	unsigned int rxbuffers;	/* allocated rx_buffers */
 	unsigned int txbuffers;	/* allocated tx_buffers */
 	unsigned int bufsize;	/* used buffersize */
+
+	unsigned char is_netdev;/* If set: act as network instead of character device */
 };
 
 
@@ -191,13 +238,11 @@
 	char nrz;		/* NRZ instead of NRZI */	
 };
 
-struct ioctl_command {
+struct scc_kiss_cmd {
 	int  	 command;	/* one of the KISS-Commands defined above */
 	unsigned param;		/* KISS-Param */
 };
 
-/* currently unused */
-
 struct scc_hw_config {
 	io_port data_a;		/* data port channel A */
 	io_port ctrl_a;		/* control port channel A */
@@ -214,14 +259,15 @@
 	char escc;		/* use ext. features of a 8580/85180/85280 */
 };
 
+/* (#) only one INTACK latch allowed. */
+
+
 struct scc_mem_config {
 	unsigned int rxbuffers;
 	unsigned int txbuffers;
 	unsigned int bufsize;
 };
 
-/* (#) only one INTACK latch allowed. */
-	
 
 /* SCC channel structure */
 
@@ -232,6 +278,10 @@
 	struct tty_struct *tty; /* link to tty control structure */
 	char tty_opened;	/* No. of open() calls... */
 	char throttled;		/* driver is throttled  */
+	
+	struct device *dev;	/* link to device control structure */
+	struct enet_statistics dev_stat;
+				/* device statistics */
 		
 	char brand;		/* manufacturer of the board */
 	long clock;		/* used clock */
@@ -239,6 +289,7 @@
 	io_port ctrl;		/* I/O address of CONTROL register */
 	io_port	data;		/* I/O address of DATA register */
 	io_port special;	/* I/O address of special function port */
+	int irq;		/* Number of Interrupt */
 	
 	char option;
 	char enhanced;		/* Enhanced SCC support */
@@ -250,6 +301,7 @@
         struct scc_stat stat;	/* statistical information */
         struct scc_modem modem; /* modem information */
         
+        char mempool;		/* pool empty or allocated? */
         struct mbuf *rx_buffer_pool; /* free buffers for rx/tx frames are */
         struct mbuf *tx_buffer_pool; /* linked in these ring chains */
         
@@ -264,18 +316,9 @@
 	/* Timer */
 	
 	struct timer_list tx_t;	/* tx timer for this channel */
+	struct timer_list tx_wdog;
+				/* tx watchdogs */
 	struct timer_list rx_t; /* rx timer */
-
-	/* rx timer counters */
-	
-	unsigned int t_dwait;	/* wait time (DWAIT) */
-	unsigned int t_slot;	/* channel sample frequency */
-	unsigned int t_txdel;	/* TX delay */
-	unsigned int t_tail;	/* tail time */
-	unsigned int t_maxk;	/* max. key up */
-	unsigned int t_min;	/* minimal key up */
-	unsigned int t_idle;	/* */
-	unsigned int t_mbusy;	/* time until defer if channel busy */	 	
 };
 
 
@@ -564,8 +607,4 @@
 
 /* global functions */
 
-#ifdef PREV_LINUX_1_3_33
-extern long scc_init(long kmem_start);
-#else
 extern int scc_init(void);
-#endif
diff -urN linux-2.0.31-pre4/include/linux/sysctl.h linux-2.0.31-pre5/include/linux/sysctl.h
--- linux-2.0.31-pre4/include/linux/sysctl.h	1997-04-08 08:47:46.000000000 -0700
+++ linux-2.0.31-pre5/include/linux/sysctl.h	2003-08-15 15:03:58.000000000 -0700
@@ -82,6 +82,7 @@
 #define NET_BRIDGE	10
 
 /* /proc/sys/net/core */
+#define NET_CORE_NET_ALIAS_MAX 1
 
 /* /proc/sys/net/ethernet */
 
@@ -98,6 +99,7 @@
 #define NET_IPV4_ARP_CONFIRM_INTERVAL   6
 #define NET_IPV4_ARP_CONFIRM_TIMEOUT	7
 #define NET_IPV4_FORWARD                8
+#define NET_IPV4_DYNADDR		9
 
 /* /proc/sys/net/ipx */
 
diff -urN linux-2.0.31-pre4/include/linux/tty.h linux-2.0.31-pre5/include/linux/tty.h
--- linux-2.0.31-pre4/include/linux/tty.h	1997-03-28 16:08:17.000000000 -0800
+++ linux-2.0.31-pre5/include/linux/tty.h	2003-08-15 15:03:58.000000000 -0700
@@ -292,6 +292,7 @@
 extern int stl_init(void);
 extern int stli_init(void);
 extern int riscom8_init(void);
+extern int specialix_init(void);
 extern int baycom_init(void);
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
diff -urN linux-2.0.31-pre4/include/net/ip.h linux-2.0.31-pre5/include/net/ip.h
--- linux-2.0.31-pre4/include/net/ip.h	1997-04-08 08:47:46.000000000 -0700
+++ linux-2.0.31-pre5/include/net/ip.h	2003-08-15 15:03:58.000000000 -0700
@@ -122,6 +122,9 @@
 
 extern struct ip_mib	ip_statistics;
 
+extern int sysctl_ip_dynaddr;
+int ip_rewrite_addrs(struct sock *sk, struct sk_buff *skb, struct device *dev);
+
 /*
  *	Functions provided by ip_fragment.o
  */
diff -urN linux-2.0.31-pre4/include/net/ipx.h linux-2.0.31-pre5/include/net/ipx.h
--- linux-2.0.31-pre4/include/net/ipx.h	1997-03-28 16:09:02.000000000 -0800
+++ linux-2.0.31-pre5/include/net/ipx.h	2003-08-15 15:03:58.000000000 -0700
@@ -16,6 +16,8 @@
 #include <net/datalink.h>
 #include <linux/ipx.h>
 
+/* #define CONFIG_IPX_INTERN 1 */
+
 typedef struct
 {
 	unsigned long net;
diff -urN linux-2.0.31-pre4/include/net/route.h linux-2.0.31-pre5/include/net/route.h
--- linux-2.0.31-pre4/include/net/route.h	1997-03-28 16:10:57.000000000 -0800
+++ linux-2.0.31-pre5/include/net/route.h	2003-08-15 15:03:58.000000000 -0700
@@ -13,6 +13,7 @@
  *		Alan Cox	:	Reformatted. Added ip_rt_local()
  *		Alan Cox	:	Support for TCP parameters.
  *		Alexey Kuznetsov:	Major changes for new routing code.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *	FIXME:
  *		Make atomic ops more generic and hide them in asm/...
@@ -83,7 +84,7 @@
 extern void		ip_rt_flush(struct device *dev);
 extern void		ip_rt_update(int event, struct device *dev);
 extern void		ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev);
-extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local);
+extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local, struct device *dev);
 extern int		rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy);
 extern int		rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
@@ -131,9 +132,9 @@
 #endif
 
 #ifdef CONFIG_KERNELD
-extern struct rtable * ip_rt_route(__u32 daddr, int local);
+extern struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev);
 #else
-extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
+extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev)
 #ifndef MODULE
 {
 	struct rtable * rth;
@@ -142,7 +143,8 @@
 
 	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
 	{
-		if (rth->rt_dst == daddr)
+		/* If an interface is specified, make sure this route points to it. */
+		if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) )
 		{
 			rth->rt_lastuse = jiffies;
 			atomic_inc(&rth->rt_use);
@@ -151,23 +153,23 @@
 			return rth;
 		}
 	}
-	return ip_rt_slow_route (daddr, local);
+	return ip_rt_slow_route (daddr, local, dev);
 }
 #else
 ;
 #endif
 #endif
 
-extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
-						       __u32 daddr, int local)
+extern __inline__ struct rtable * ip_check_route(struct rtable ** rp, __u32 daddr, 
+						 int local, struct device *dev)
 {
 	struct rtable * rt = *rp;
 
-	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
+	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP) || (dev!=NULL)
 	    || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
 	{
 		ip_rt_put(rt);
-		rt = ip_rt_route(daddr, local);
+		rt = ip_rt_route(daddr, local, dev);
 		*rp = rt;
 	}
 	return rt;
diff -urN linux-2.0.31-pre4/include/net/sock.h linux-2.0.31-pre5/include/net/sock.h
--- linux-2.0.31-pre4/include/net/sock.h	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/include/net/sock.h	2003-08-15 15:03:58.000000000 -0700
@@ -23,6 +23,7 @@
  *	Pauline Middelink	:	identd support
  *		Alan Cox	:	Eliminate low level recv/recvfrom
  *		David S. Miller	:	New socket lookup architecture for ISS.
+ *              Elliot Poger    :       New field for SO_BINDTODEVICE option.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -196,6 +197,7 @@
 				broadcast,
 				nonagle,
 				bsdism;
+	struct device           * bound_device;
 	unsigned long	        lingertime;
 	int			proc;
 
diff -urN linux-2.0.31-pre4/include/net/tcp.h linux-2.0.31-pre5/include/net/tcp.h
--- linux-2.0.31-pre4/include/net/tcp.h	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre5/include/net/tcp.h	2003-08-15 15:03:58.000000000 -0700
@@ -41,7 +41,23 @@
 /* These are AF independant. */
 static __inline__ int tcp_bhashfn(__u16 lport)
 {
-	return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1);
+	return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE-1);
+}
+
+/* Find the next port that hashes h that is larger than lport.
+ * If you change the hash, change this function to match, or you will
+ * break TCP port selection. This function must also NOT wrap around
+ * when the next number exceeds the largest possible port (2^16-1).
+ */
+static __inline__ int tcp_bhashnext(__u16 short lport, __u16 h)
+{
+        __u32 s;	/* don't change this to a smaller type! */
+
+        s = (lport ^ (h ^ tcp_bhashfn(lport)));
+        if (s > lport)
+                return s;
+        s = lport + TCP_BHTABLE_SIZE;
+        return (s ^ (h ^ tcp_bhashfn(s)));
 }
 
 static __inline__ int tcp_sk_bhashfn(struct sock *sk)
@@ -116,7 +132,7 @@
 					   stacks do signed 16bit maths! */
 #define MIN_WINDOW	2048
 #define MAX_ACK_BACKLOG	2
-#define MAX_DUP_ACKS	2
+#define MAX_DUP_ACKS	3
 #define MIN_WRITE_SPACE	2048
 #define TCP_WINDOW_DIFF	2048
 
diff -urN linux-2.0.31-pre4/init/main.c linux-2.0.31-pre5/init/main.c
--- linux-2.0.31-pre4/init/main.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/init/main.c	2003-08-15 15:03:58.000000000 -0700
@@ -173,6 +173,9 @@
 #ifdef CONFIG_RISCOM8
 extern void riscom8_setup(char *str, int *ints);
 #endif
+#ifdef CONFIG_SPECIALIX
+extern void specialix_setup(char *str, int *ints);
+#endif
 #ifdef CONFIG_BAYCOM
 extern void baycom_setup(char *str, int *ints);
 #endif
@@ -426,6 +429,9 @@
 #ifdef CONFIG_RISCOM8
 	{ "riscom8=", riscom8_setup },
 #endif
+#ifdef CONFIG_SPECIALIX
+	{ "specialix=", specialix_setup },
+#endif
 #ifdef CONFIG_BAYCOM
 	{ "baycom=", baycom_setup },
 #endif
diff -urN linux-2.0.31-pre4/kernel/fork.c linux-2.0.31-pre5/kernel/fork.c
--- linux-2.0.31-pre4/kernel/fork.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/kernel/fork.c	2003-08-15 15:03:58.000000000 -0700
@@ -122,6 +122,7 @@
 		*mm = *current->mm;
 		mm->count = 1;
 		mm->def_flags = 0;
+		mm->mmap_sem = MUTEX;
 		tsk->mm = mm;
 		tsk->min_flt = tsk->maj_flt = 0;
 		tsk->cmin_flt = tsk->cmaj_flt = 0;
diff -urN linux-2.0.31-pre4/MAINTAINERS linux-2.0.31-pre5/MAINTAINERS
--- linux-2.0.31-pre4/MAINTAINERS	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/MAINTAINERS	2003-08-15 15:03:58.000000000 -0700
@@ -110,6 +110,12 @@
 M:	jt@hplb.hpl.hp.com
 S:	Maintained
 
+TOKEN-RING NETWORK DRIVER
+P:      Paul Norton
+M:      pnorton@cts.com
+L:      linux-net@vger.rutgers.edu
+S:      Maintained
+
 APM DRIVER
 P:	Rik Faith & Stephen Rothwell
 M:	faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au
@@ -134,6 +140,18 @@
 L:	masq@indyramp.com
 S:	Maintained
 
+AX.25 DAMA SLAVE
+P:	Joerg Reuter
+M:	jreuter@poboxes.com
+L:	linux-hams@vger.rutgers.edu
+S:	Maintained
+
+Z8530 SCC DRIVER FOR AX.25
+P:	Joerg Reuter
+M:	jreuter@poboxes.com
+L:	linux-hams@vger.rutgers.edu
+S:	Maintained
+
 BUSLOGIC SCSI DRIVER
 P:	Leonard N. Zubkoff
 M:	Leonard N. Zubkoff <lnz@dandelion.com>
@@ -306,7 +324,7 @@
 SVGA HANDLING:
 P:	Martin Mares
 M:	mj@k332.feld.cvut.cz
-L:	linux-kernel@vger.rutgers.edu
+L:	linux-video@atrey.karlin.mff.cuni.cz
 S:	Maintained
 
 VFAT FILESYSTEM:
@@ -328,6 +346,13 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+SPECIALIX IO8+ MULTIPORT SERIAL CARD DRIVER
+P:	Roger Wolff
+M:	R.E.Wolff@BitWizard.nl
+M:	io8-linux@specialix.co.uk
+L:	linux-kernel@vger.rutgers.edu ?
+S:	Supported
+
 MOUSE AND MISC DEVICES [GENERAL]
 P:	Alessandro Rubini
 M:	rubini@ipvvis.unipv.it
diff -urN linux-2.0.31-pre4/net/Config.in linux-2.0.31-pre5/net/Config.in
--- linux-2.0.31-pre4/net/Config.in	1996-07-08 00:21:46.000000000 -0700
+++ linux-2.0.31-pre5/net/Config.in	2003-08-15 15:03:58.000000000 -0700
@@ -11,9 +11,6 @@
 fi
 comment ' '
 tristate 'The IPX protocol' CONFIG_IPX
-if [ "$CONFIG_IPX" != "n" ]; then
-  bool 'Full internal IPX network' CONFIG_IPX_INTERN
-fi
 tristate 'Appletalk DDP' CONFIG_ATALK
 bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25
 if [ "$CONFIG_AX25" = "y" ]; then
diff -urN linux-2.0.31-pre4/net/core/dev.c linux-2.0.31-pre5/net/core/dev.c
--- linux-2.0.31-pre4/net/core/dev.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/core/dev.c	2003-08-15 15:03:58.000000000 -0700
@@ -392,7 +392,7 @@
 
 #ifdef CONFIG_NET_ALIAS
 	if (net_alias_is(dev))
-	  	skb->dev = dev = net_alias_main_dev(dev);
+	  	skb->dev = dev = net_alias_dev_tx(dev);
 #endif
 
 	/*
@@ -436,7 +436,18 @@
 					struct sk_buff *skb2;
 					if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
 						break;
+					/* FIXME?: Wrong when the hard_header_len
+					 * is an upper bound. Is this even
+					 * used anywhere?
+					 */
 					skb2->h.raw = skb2->data + dev->hard_header_len;
+					/* On soft header devices we
+					 * yank the header before mac.raw
+					 * back off. This is set by
+					 * dev->hard_header().
+					 */
+					if (dev->flags&IFF_SOFTHEADERS)
+						skb_pull(skb2,skb2->mac.raw-skb2->data);
 					skb2->mac.raw = skb2->data;
 					ptype->func(skb2, skb->dev, ptype);
 				}
@@ -1095,7 +1106,7 @@
 	switch(getset) 
 	{
 		case SIOCGIFFLAGS:	/* Get interface flags */
-			ifr.ifr_flags = dev->flags;
+			ifr.ifr_flags = (dev->flags & ~IFF_SOFTHEADERS);
 			goto rarok;
 
 		case SIOCSIFFLAGS:	/* Set interface flags */
@@ -1117,7 +1128,7 @@
 					IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
 					IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
 					IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
-					| IFF_MULTICAST)) | (dev->flags & IFF_UP);
+					| IFF_MULTICAST)) | (dev->flags & (IFF_SOFTHEADERS|IFF_UP));
 				/*
 				 *	Load in the correct multicast list now the flags have changed.
 				 */				
diff -urN linux-2.0.31-pre4/net/core/net_alias.c linux-2.0.31-pre5/net/core/net_alias.c
--- linux-2.0.31-pre4/net/core/net_alias.c	1996-08-31 23:15:34.000000000 -0700
+++ linux-2.0.31-pre5/net/core/net_alias.c	2003-08-15 15:03:58.000000000 -0700
@@ -2,7 +2,8 @@
  *		NET_ALIAS network device aliasing module.
  *
  *
- * Version:	@(#)net_alias.c	0.43   12/20/95
+ * Version:	@(#)net_alias.c	0.50   4/20/97
+ *
  *
  * Authors:	Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
  *		Marcelo Fabian Roccasalva, <mfroccas@raiz.uncu.edu.ar>
@@ -14,13 +15,16 @@
  *	-	fast hashed alias address lookup
  *	-	net_alias_type objs registration/unreg., module-ables.
  *	-	/proc/net/aliases & /proc/net/alias_types entries
+ *	-	tx and rx stats
+ *	-	/proc/sys/net/core/net_alias_max entry
  * Fixes:
- *			JJC	:	several net_alias_type func. renamed.
- *			JJC	:	net_alias_type object methods now pass 
+ *	Juan Jose Ciarlante	:	several net_alias_type func. renamed.
+ *	Juan Jose Ciarlante	:	net_alias_type object methods now pass 
  *					*this.
- *			JJC	:	xxx_rcv device selection based on <src,dst> 
- *					addrs
+ *	Juan Jose Ciarlante	:	xxx_rcv device selection based on <src,dst> addrs
  *		Andreas Schultz	:	Kerneld support.
+ *	Juan Jose Ciarlante	:	Added tx/rx stats for aliases.
+ *	Juan Jose Ciarlante	:	Added sysctl interface for max aliases per device
  *
  * FIXME:
  *	- User calls sleep/wake_up locking.
@@ -39,10 +43,12 @@
 #include <linux/netdevice.h>
 #include <linux/notifier.h>
 #include <linux/if.h>
+#include <linux/if_ether.h>
 #include <linux/inet.h>
 #include <linux/in.h>
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
+#include <linux/sysctl.h>
 
 #ifdef ALIAS_USER_LAND_DEBUG
 #include "net_alias.h"
@@ -55,11 +61,35 @@
 #include <linux/kerneld.h>
 #endif
 
+
 /*
- * Only allow the following flags to pass from main device to aliases
+ * 	NET_ALIAS_MAX_DEFAULT: max. alias slot number allowed by default,
+ *		can be changed by sysctl
+ *	NET_ALIAS_HASH_TAB_SIZE: hash table size (addr lookup), 1 per aliased 
+ *		device, due to hash optimizations, MUST be 16 or 256 only.
  */
 
-#define  NET_ALIAS_IFF_MASK   (IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
+#define NET_ALIAS_MAX_DEFAULT		256
+
+/* DO NOT CHANGE the line below ! */
+#define NET_ALIAS_HASH_TAB_SIZE(n)  ( ((n)>=NET_ALIAS_MAX_DEFAULT) ? 256 : 16 )
+
+/*
+ *	set default max_aliases per device
+ */
+int sysctl_net_alias_max = NET_ALIAS_MAX_DEFAULT;
+
+/*
+ * 	Only allow the following flags to pass from main device to aliases
+ *	Note that IFF_BROADCAST is not passed by default, this make sense
+ *	because:
+ *	a) if same-net alias: broadcasts are already handled by main device
+ *	b) if diff-net alias: bcasts will be set by 'broadcast' ifconfig opt.
+ *	I prefer this approach instead of setting '-broadcast' for each
+ *	same-net alias device  --JJC.
+ */
+
+#define  NET_ALIAS_IFF_MASK   (IFF_SOFTHEADERS|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
 
 static struct net_alias_type * nat_getbytype(int type);
 static int nat_attach_chg(struct net_alias_type *nat, int delta);
@@ -68,6 +98,7 @@
 
 
 static int net_alias_devinit(struct device *dev);
+static struct enet_statistics *net_alias_dev_stats(struct device *dev);
 static int net_alias_hard_start_xmit(struct sk_buff *skb, struct device *dev);
 static int net_alias_devsetup(struct net_alias *alias, struct net_alias_type *nat, struct sockaddr *sa);
 static struct net_alias **net_alias_slow_findp(struct net_alias_info *alias_info, struct net_alias *alias);
@@ -79,7 +110,22 @@
  * net_alias_type base array, will hold net_alias_type obj hashed list heads.
  */
 
-struct net_alias_type *nat_base[16];
+struct net_alias_type *net_alias_type_base[16];
+
+
+/*
+ *	get_stats function: return rx/tx pkts based on lookups
+ */
+static struct enet_statistics *net_alias_dev_stats(struct device *dev)
+{
+        static struct enet_statistics alias_stats;
+        struct net_alias *alias = dev->my_alias;
+        
+        memset (&alias_stats, 0, sizeof (alias_stats));
+        alias_stats.rx_packets	      = alias->rx_lookups;
+        alias_stats.tx_packets	      = alias->tx_lookups;
+        return &alias_stats;
+}
 
 
 /*
@@ -90,7 +136,7 @@
 nat_getbytype(int type)
 {
   struct net_alias_type *nat;
-  for(nat = nat_base[type & 0x0f]; nat ; nat = nat->next)
+  for(nat = net_alias_type_base[type & 0x0f]; nat ; nat = nat->next)
   {
     if (nat->type == type) return nat;
   }
@@ -119,11 +165,14 @@
  */
 
 static __inline__ unsigned
-HASH(__u32 addr, int af)
+hash_key(unsigned hsize, __u32 addr)
 {
   unsigned tmp = addr ^ (addr>>16); /* 4 -> 2 */
   tmp ^= (tmp>>8);                  /* 2 -> 1 */
-  return (tmp^(tmp>>4)^af) & 0x0f;	    /* 1 -> 1/2 */
+  if (hsize == 256)
+    return (tmp & 0xff);
+  else
+    return (tmp^(tmp>>4)) & 0x0f;	    /* 1 -> 1/2 */
 }
 
 
@@ -135,9 +184,9 @@
  */
 
 static __inline__ int
-nat_hash_key(struct net_alias_type *nat, struct sockaddr *sa)
+nat_hash_key(struct net_alias_type *nat, unsigned hsize, struct sockaddr *sa)
 {
-  return HASH(nat_addr32(nat,sa), sa->sa_family);
+  return hash_key(hsize, nat_addr32(nat,sa));
 }
 
 
@@ -236,13 +285,13 @@
 
 
 static int
-net_alias_open(struct device * dev)
+net_alias_dev_open(struct device * dev)
 {
   return 0;
 }
 
 static int
-net_alias_close(struct device * dev)
+net_alias_dev_close(struct device * dev)
 {
   return 0;
 }
@@ -271,13 +320,16 @@
   dev = &alias->dev;
   memset(dev, '\0', sizeof(struct device));
   family = (sa)? sa->sa_family : main_dev->family;
-
+  alias->rx_lookups = 0;
+  alias->tx_lookups = 0;
   dev->alias_info = NULL;	/* no aliasing recursion */
   dev->my_alias = alias;	/* point to alias */
   dev->name = alias->name;
   dev->type = main_dev->type;
-  dev->open = net_alias_open;
-  dev->stop = net_alias_close;
+  dev->open = net_alias_dev_open;
+  dev->stop = net_alias_dev_close;
+  dev->get_stats = net_alias_dev_stats;
+  
   dev->hard_header_len = main_dev->hard_header_len;
   memcpy(dev->broadcast, main_dev->broadcast, MAX_ADDR_LEN);
   memcpy(dev->dev_addr, main_dev->dev_addr, MAX_ADDR_LEN);
@@ -328,7 +380,7 @@
    */
   
   n_aliases = alias_info->n_aliases;
-  for (idx=0; idx < 16 ; idx++)
+  for (idx=0; idx < alias_info->hash_tab_size ; idx++)
     for (aliasp = &alias_info->hash_tab[idx];*aliasp;aliasp = &(*aliasp)->next)
       if (*aliasp == alias)
 	return aliasp;
@@ -353,6 +405,7 @@
   unsigned long flags;
   int family;
   __u32 addr32;
+  int max_aliases;
   
   /* FIXME: lock */
   alias_info = main_dev->alias_info;
@@ -387,6 +440,19 @@
 #endif
   }
   
+  *err = -EINVAL;
+  
+  if (!alias_info)
+  	/*
+         *	At this point we take the sysctl value(s)
+         */
+  	max_aliases = sysctl_net_alias_max;
+  else
+  	max_aliases = alias_info->max_aliases;
+  
+  if (slot >= max_aliases )     /* 0-based (eth0:0 _IS_ valid) */
+  	return NULL;
+  
   /*
    * do not allow creation over downed devices
    */
@@ -403,10 +469,25 @@
   *err = -ENOMEM;
 
   if (!alias_info)
-  { 
-    alias_info = kmalloc(sizeof(struct net_alias_info), GFP_KERNEL);
-    if (!alias_info) return NULL; /* ENOMEM */
-    memset(alias_info, 0, sizeof(struct net_alias_info));
+  {
+          /*
+           *	Allocate space for struct net_alias_info plus hash table
+           */
+          int truesize;
+
+          /* net_alias_info size */
+          truesize = sizeof(struct net_alias_info);
+          /* add   hash_tab size * sizeof(elem)  */
+          truesize += NET_ALIAS_HASH_TAB_SIZE(max_aliases) * sizeof (struct net_alias *);
+          
+          alias_info = kmalloc( truesize , GFP_KERNEL);
+  
+          if (!alias_info) return NULL; /* ENOMEM */
+          
+          memset(alias_info, 0, truesize);
+          alias_info->truesize = truesize;
+          alias_info->max_aliases = max_aliases;
+          alias_info->hash_tab_size = NET_ALIAS_HASH_TAB_SIZE(max_aliases);
   }
   
   if (!(alias = kmalloc(sizeof(struct net_alias), GFP_KERNEL)))
@@ -453,7 +534,7 @@
    * store hash key in alias: will speed-up rehashing and deletion
    */
   
-  alias->hash = HASH(addr32, family);
+  alias->hash = hash_key(alias_info->hash_tab_size, addr32);
 
   /*
    * insert alias in hashed linked list
@@ -595,7 +676,7 @@
   
   kfree_s(alias, sizeof(struct net_alias));
   if (main_dev->alias_info == NULL)
-    kfree_s(alias_info, sizeof(struct net_alias_info));
+    kfree_s(alias_info, alias_info->truesize);
 
   /*
    * deletion ok (*err=0), NULL device returned.
@@ -723,14 +804,12 @@
   if (!(dev=dev_get(dev_name)))
     return NULL;
   *sptr++=':';
-  
+
   /*
    * fetch slot number
    */
   
   slot = simple_strtoul(sptr,&eptr,10);
-  if (slot >= NET_ALIAS_MAX_SLOT)
-    return NULL;
 
   /*
    * if last char is '-', it is a deletion request
@@ -829,7 +908,7 @@
    * new hash key. if same as old AND same type (family) return;
    */
   
-  n_hash = nat_hash_key(n_nat, sa);
+  n_hash = nat_hash_key(n_nat, alias_info->hash_tab_size, sa);
   if (n_hash == alias->hash && o_nat == n_nat )
     return 0;
 
@@ -905,7 +984,7 @@
   unsigned idx;
   len=sprintf(buffer,"type    name            n_attach\n");
   for (idx=0 ; idx < 16 ; idx++)
-    for (nat = nat_base[idx]; nat ; nat = nat->next)
+    for (nat = net_alias_type_base[idx]; nat ; nat = nat->next)
     {
       len += sprintf(buffer+len, "%-7d %-15s %-7d\n",
 		     nat->type, nat->name,nat->n_attach);
@@ -1027,7 +1106,9 @@
 nat_addr_chk(struct net_alias_type *nat, struct net_alias_info *alias_info, struct sockaddr *sa, int flags_on, int flags_off)
 {
   struct net_alias *alias;
-  for(alias = alias_info->hash_tab[nat_hash_key(nat,sa)];
+  unsigned hsize = alias_info->hash_tab_size;
+  
+  for(alias = alias_info->hash_tab[nat_hash_key(nat,hsize,sa)];
       alias; alias = alias->next)
   {
     if (alias->dev.family != sa->sa_family) continue;
@@ -1052,7 +1133,9 @@
 nat_addr_chk32(struct net_alias_type *nat, struct net_alias_info *alias_info, int family, __u32 addr32, int flags_on, int flags_off)
 {
   struct net_alias *alias;
-  for (alias=alias_info->hash_tab[HASH(addr32,family)];
+  unsigned hsize = alias_info->hash_tab_size;
+  
+  for (alias=alias_info->hash_tab[hash_key(hsize, addr32)];
        alias; alias=alias->next)
   {
     if (alias->dev.family != family) continue;
@@ -1126,7 +1209,7 @@
  */
 
 struct device *
-net_alias_dev_rcv_sel(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
+net_alias_dev_rx(struct device *main_dev, struct sockaddr *sa_src, struct sockaddr *sa_dst)
 {
   int family;
   struct net_alias_type *nat;
@@ -1166,7 +1249,10 @@
 
     dev = nat_addr_chk(nat, alias_info, sa_dst, IFF_UP, 0);
 
-    if (dev != NULL) return dev;
+    if (dev != NULL) {
+            net_alias_inc_rx(dev->my_alias);
+            return dev;
+    }
   }
 
   /*
@@ -1181,24 +1267,29 @@
   /*
    * dev ok only if it is alias of main_dev
    */
-  
-  dev = net_alias_is(dev)?
-    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
 
+  if (net_alias_is(dev)) {
+          struct net_alias *alias=dev->my_alias;
+          if (alias->main_dev == main_dev) {
+                  net_alias_inc_rx(alias);
+                  return dev;
+          }
+  }
+  
   /*
    * do not return NULL.
    */
   
-  return (dev)? dev : main_dev;
+  return main_dev;
 
 }
 
 /*
- * dev_rcv_sel32: dev_rcv_sel for 'pa_addr' protocols.
+ * dev_rx32: dev_rx selection for 'pa_addr' protocols.
  */
 
 struct device *
-net_alias_dev_rcv_sel32(struct device *main_dev, int family, __u32 src, __u32 dst)
+net_alias_dev_rx32(struct device *main_dev, int family, __u32 src, __u32 dst)
 {
   struct net_alias_type *nat;
   struct net_alias_info *alias_info;
@@ -1236,7 +1327,10 @@
   if (dst)
   {
     dev = nat_addr_chk32(nat, alias_info, family, dst, IFF_UP, 0);
-    if (dev) return dev;
+    if (dev) {
+            net_alias_inc_rx(dev->my_alias);
+            return dev;
+    }
   }
   
   /*
@@ -1255,19 +1349,23 @@
   /*
    * dev ok only if it is alias of main_dev
    */
-  
-  dev = net_alias_is(dev)?
-    ( (dev->my_alias->main_dev == main_dev)? dev : NULL) : NULL;
+
+  if (net_alias_is(dev)) {
+          struct net_alias *alias=dev->my_alias;
+          if (alias->main_dev == main_dev) {
+                  net_alias_inc_rx(alias);
+                  return dev;
+          }
+  }
 
   /*
    * do not return NULL.
    */
   
-  return (dev)? dev : main_dev;
+  return main_dev;
   
 }
 
-
 /*
  * device event hook
  */
@@ -1333,8 +1431,8 @@
   hash = nat->type & 0x0f;
   save_flags(flags);
   cli();
-  nat->next = nat_base[hash];
-  nat_base[hash] = nat;
+  nat->next = net_alias_type_base[hash];
+  net_alias_type_base[hash] = nat;
   restore_flags(flags);
   return 0;
 }
@@ -1366,7 +1464,7 @@
   hash = nat->type & 0x0f;
   save_flags(flags);
   cli();
-  for (natp = &nat_base[hash]; *natp ; natp = &(*natp)->next)
+  for (natp = &net_alias_type_base[hash]; *natp ; natp = &(*natp)->next)
   {
     if (nat==(*natp))
     {
@@ -1380,3 +1478,26 @@
   return -EINVAL;
 }
 
+/*
+ *	Log sysctl's net_alias_max changes.
+ */
+int proc_do_net_alias_max(ctl_table *ctl, int write, struct file *filp,
+			    void *buffer, size_t *lenp) 
+{
+        int old = sysctl_net_alias_max;
+        int ret;
+        
+        ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+        if (write) {
+                if (sysctl_net_alias_max != old) {
+                        printk(KERN_INFO "sysctl: net_alias_max changed (max.aliases=%d, hashsize=%d).\n",
+                               sysctl_net_alias_max,
+                               NET_ALIAS_HASH_TAB_SIZE(sysctl_net_alias_max));
+                        if (!sysctl_net_alias_max)
+                                printk(KERN_INFO "sysctl: net_alias creation disabled.\n");
+                        if (!old)
+                                printk(KERN_INFO "sysctl: net_alias creation enabled.\n");
+                }
+        }
+        return ret; 
+}
diff -urN linux-2.0.31-pre4/net/core/sock.c linux-2.0.31-pre5/net/core/sock.c
--- linux-2.0.31-pre4/net/core/sock.c	1996-10-29 17:42:42.000000000 -0800
+++ linux-2.0.31-pre5/net/core/sock.c	2003-08-15 15:03:58.000000000 -0700
@@ -71,6 +71,7 @@
  *		Alan Cox	: 	Generic socket allocation to make hooks
  *					easier (suggested by Craig Metz).
  *		Michael Pall	:	SO_ERROR returns positive errno again
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  * To Fix:
  *
@@ -128,6 +129,7 @@
 	int valbool;
 	int err;
 	struct linger ling;
+	struct ifreq req;
 
 	/*
 	 *	Options without arguments
@@ -232,7 +234,37 @@
 		case SO_BSDCOMPAT:
 			sk->bsdism = valbool;
 			return 0;
-			
+
+#ifdef CONFIG_NET	
+ 	        case SO_BINDTODEVICE:
+ 			/* Bind this socket to a particular device like "eth0",
+ 			 * as specified in an ifreq structure.  If the device 
+ 			 * is "", socket is NOT bound to a device. */
+ 			if (!valbool) {
+ 				sk->bound_device = NULL;
+ 			} else {
+ 				err=verify_area(VERIFY_READ,optval,sizeof(req));
+ 				if(err)
+ 					return err;
+ 				memcpy_fromfs(&req,optval,sizeof(req));
+
+				/* Remove any cached route for this socket. */
+				if (sk->ip_route_cache) {
+					ip_rt_put(sk->ip_route_cache);
+					sk->ip_route_cache=NULL;
+				}
+
+ 				if (*(req.ifr_name) == '\0') {
+ 					sk->bound_device = NULL;
+ 				} else {
+ 					sk->bound_device = dev_get(req.ifr_name);
+ 					if (sk->bound_device == NULL)
+ 						return -EINVAL;
+ 				}
+ 			}
+  			return 0;
+#endif
+
 		default:
 		  	return(-ENOPROTOOPT);
   	}
@@ -462,9 +494,9 @@
 			return NULL;
 		}
 		
-		
+
 		mem=sk->wmem_alloc;
-		
+
 		if(!fallback)
 			skb = sock_wmalloc(sk, size, 0, sk->allocation);
 		else
diff -urN linux-2.0.31-pre4/net/core/sysctl_net_core.c linux-2.0.31-pre5/net/core/sysctl_net_core.c
--- linux-2.0.31-pre4/net/core/sysctl_net_core.c	1996-04-01 22:03:35.000000000 -0800
+++ linux-2.0.31-pre5/net/core/sysctl_net_core.c	2003-08-15 15:03:58.000000000 -0700
@@ -7,7 +7,17 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <linux/config.h>
+
+#ifdef CONFIG_NET_ALIAS
+extern int sysctl_net_alias_max;
+extern int proc_do_net_alias_max(ctl_table *, int, struct file *, void *, size_t *);
+#endif
 
 ctl_table core_table[] = {
+#ifdef CONFIG_NET_ALIAS
+	{NET_CORE_NET_ALIAS_MAX, "net_alias_max", &sysctl_net_alias_max, sizeof(int),
+	 0644, NULL, &proc_do_net_alias_max },
+#endif  
 	{0}
 };
diff -urN linux-2.0.31-pre4/net/ipv4/arp.c linux-2.0.31-pre5/net/ipv4/arp.c
--- linux-2.0.31-pre4/net/ipv4/arp.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/arp.c	2003-08-15 15:03:59.000000000 -0700
@@ -1973,7 +1973,7 @@
 				memcpy(ha, proxy_entry->ha, dev->addr_len);
 				arp_unlock();
 
-				rt = ip_rt_route(tip, 0);
+				rt = ip_rt_route(tip, 0, NULL);
 				if (rt  && rt->rt_dev != dev)
 					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
 				ip_rt_put(rt);
@@ -2046,7 +2046,7 @@
 		if (!dev)
 		{
 			struct rtable * rt;
-			rt = ip_rt_route(ip, 0);
+			rt = ip_rt_route(ip, 0, NULL);
 			if (!rt)
 				return -ENETUNREACH;
 			dev = rt->rt_dev;
diff -urN linux-2.0.31-pre4/net/ipv4/icmp.c linux-2.0.31-pre5/net/ipv4/icmp.c
--- linux-2.0.31-pre4/net/ipv4/icmp.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/icmp.c	2003-08-15 15:03:59.000000000 -0700
@@ -39,6 +39,7 @@
  *		Thomas Quinot	:	ICMP Dest Unreach codes up to 15 are
  *					valid (RFC 1812).
  *		Alan Cox	:	Spoofing and junk icmp protections.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *
  * RFC1122 (Host Requirements -- Comm. Layer) Status:
@@ -993,8 +994,14 @@
  */
 
 /* This should work with the new hashes now. -DaveM */
-extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
-extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+extern struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+					unsigned short rnum, unsigned long laddr,
+					unsigned long paddr, unsigned short pnum,
+					struct device *dev);
+extern struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+					unsigned short rnum, unsigned long laddr,
+					unsigned long paddr, unsigned short pnum,
+					struct device *dev);
 
 int icmp_chkaddr(struct sk_buff *skb)
 {
@@ -1010,7 +1017,8 @@
 			{
 			struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+			sk = tcp_v4_proxy_lookup(th->source, iph->daddr, th->dest,
+						 iph->saddr, 0, 0, skb->dev);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr) return 0;
 			if (sk->daddr != iph->daddr) return 0;
@@ -1024,7 +1032,8 @@
 			{
 			struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+			sk = udp_v4_proxy_lookup(uh->source, iph->daddr, uh->dest,
+						 iph->saddr, 0, 0, skb->dev);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR)
 				return 0;
diff -urN linux-2.0.31-pre4/net/ipv4/ip_alias.c linux-2.0.31-pre5/net/ipv4/ip_alias.c
--- linux-2.0.31-pre4/net/ipv4/ip_alias.c	1996-05-13 02:15:24.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/ip_alias.c	2003-08-15 15:03:59.000000000 -0700
@@ -102,7 +102,7 @@
 	 *	net_alias module will check if returned device is main_dev's alias
 	 */
 
-	rt = ip_rt_route(addr, 0);
+	rt = ip_rt_route(addr, 0, NULL);
 	if(rt)
 	{
 		dev=rt->rt_dev;
diff -urN linux-2.0.31-pre4/net/ipv4/ip_forward.c linux-2.0.31-pre5/net/ipv4/ip_forward.c
--- linux-2.0.31-pre4/net/ipv4/ip_forward.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/ip_forward.c	2003-08-15 15:03:59.000000000 -0700
@@ -177,7 +177,7 @@
 		 * and give it to the IP sender for further processing.
 		 */
 
-		rt = ip_rt_route(target_addr, 0);
+		rt = ip_rt_route(target_addr, 0, NULL);
 
 		if (rt == NULL)
 		{
@@ -419,7 +419,7 @@
 			skb2->dev=dev2;
 #ifdef CONFIG_IP_MROUTE
 			if(is_frag&IPFWD_MULTITUNNEL)
-				ip_encap(skb,skb->len, dev2, raddr);
+				ip_encap(skb, 0, dev2, target_addr);
 			else
 			{
 #endif
diff -urN linux-2.0.31-pre4/net/ipv4/ip_fragment.c linux-2.0.31-pre5/net/ipv4/ip_fragment.c
--- linux-2.0.31-pre4/net/ipv4/ip_fragment.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/ip_fragment.c	2003-08-15 15:03:59.000000000 -0700
@@ -523,7 +523,7 @@
 	prev = NULL;
 	for(next = qp->fragments; next != NULL; next = next->next)
 	{
-		if (next->offset > offset)
+		if (next->offset >= offset)
 			break;	/* bingo! */
 		prev = next;
 	}
diff -urN linux-2.0.31-pre4/net/ipv4/ip_fw.c linux-2.0.31-pre5/net/ipv4/ip_fw.c
--- linux-2.0.31-pre4/net/ipv4/ip_fw.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/ip_fw.c	2003-08-15 15:03:59.000000000 -0700
@@ -206,9 +206,9 @@
 int ip_fw_chk(struct iphdr *ip, struct device *rif, __u16 *redirport, struct ip_fw *chain, int policy, int mode)
 {
 	struct ip_fw *f;
-	struct tcphdr		*tcp=(struct tcphdr *)((unsigned long *)ip+ip->ihl);
-	struct udphdr		*udp=(struct udphdr *)((unsigned long *)ip+ip->ihl);
-	struct icmphdr		*icmp=(struct icmphdr *)((unsigned long *)ip+ip->ihl);
+	struct tcphdr		*tcp=(struct tcphdr *)((__u32 *)ip+ip->ihl);
+	struct udphdr		*udp=(struct udphdr *)((__u32 *)ip+ip->ihl);
+	struct icmphdr		*icmp=(struct icmphdr *)((__u32 *)ip+ip->ihl);
 	__u32			src, dst;
 	__u16			src_port=0xFFFF, dst_port=0xFFFF, icmp_type=0xFF;
 	unsigned short		f_prt=0, prt;
diff -urN linux-2.0.31-pre4/net/ipv4/ip_output.c linux-2.0.31-pre5/net/ipv4/ip_output.c
--- linux-2.0.31-pre4/net/ipv4/ip_output.c	1996-10-29 17:42:42.000000000 -0800
+++ linux-2.0.31-pre5/net/ipv4/ip_output.c	2003-08-15 15:03:59.000000000 -0700
@@ -26,6 +26,8 @@
  *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit
  *					(in case if packet not accepted by
  *					output firewall rules)
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
+ *		Juan Jose Ciarlante:	sk/skb source address rewriting
  */
 
 #include <asm/segment.h>
@@ -66,6 +68,78 @@
 #include <net/netlink.h>
 
 /*
+ *	Allows dynamic re-writing of packet's addresses.
+ *	if value > 1, be verbose about addr rewriting.
+ *	Currently implemented:
+ *		tcp_output.c   if sk->state!=TCP_SYN_SENT
+ *		ip_masq.c      if no packet has been received by tunnel
+ */
+int sysctl_ip_dynaddr = 0;
+
+/*
+ *	Very Promisc source address re-assignation.
+ *	ONLY acceptable if socket is NOT connected yet.
+ *      Caller already checked sysctl_ip_dynaddr != 0 and consistent sk->state
+ *	 (TCP_SYN_SENT for tcp, udp-connect sockets are set TCP_ESTABLISHED)
+ */
+
+int ip_rewrite_addrs (struct sock *sk, struct sk_buff *skb, struct device *dev)
+{
+	u32 new_saddr = dev->pa_addr;
+        struct iphdr *iph;
+        
+        /*
+         *	Be carefull: new_saddr must be !0
+         */
+        if (!new_saddr) {
+                printk(KERN_WARNING "ip_rewrite_addrs(): NULL device \"%s\" addr\n",
+                       dev->name);
+                return 0;
+        }
+        
+        /*
+         *	Ouch!, this should not happen.
+         */
+        if (!sk->saddr || !sk->rcv_saddr) {
+                printk(KERN_WARNING "ip_rewrite_addrs(): not valid sock addrs: saddr=%08lX rcv_saddr=%08lX",
+                       ntohl(sk->saddr), ntohl(sk->rcv_saddr));
+                return 0;
+        }
+        
+        /*
+         *	Be verbose if sysctl value > 1
+         */
+        if (sysctl_ip_dynaddr > 1) {
+                printk(KERN_INFO "ip_rewrite_addrs(): shifting saddr from %s",
+                       in_ntoa(skb->saddr));
+                printk(" to %s\n", in_ntoa(new_saddr));
+        }
+        
+        iph = skb->ip_hdr;
+
+        if (new_saddr != iph->saddr) {
+                iph->saddr = new_saddr;
+                skb->saddr = new_saddr;
+                ip_send_check(iph);
+        } else if (sysctl_ip_dynaddr > 1) {
+                printk(KERN_WARNING "ip_rewrite_addrs(): skb already changed (???).\n");
+                return 0;
+        }
+        
+        /*
+         *	Maybe whe are in a skb chain loop and socket address has
+         *	yet been 'damaged'.
+         */
+        if (new_saddr != sk->saddr) {
+                sk->saddr = new_saddr;
+                sk->rcv_saddr = new_saddr;
+                sk->prot->rehash(sk);
+        } else if (sysctl_ip_dynaddr > 1)
+                printk(KERN_NOTICE "ip_rewrite_addrs(): no change needed for sock\n");
+        return 1;
+}
+
+/*
  *	Loop a packet back to the sender.
  */
  
@@ -218,7 +292,7 @@
 #endif
 	if (rp)
 	{
-		rt = ip_check_route(rp, daddr, skb->localroute);
+		rt = ip_check_route(rp, daddr, skb->localroute, *dev);
 		/*
 		 * If rp != NULL rt_put following below should not
 		 * release route, so that...
@@ -227,7 +301,7 @@
 			atomic_inc(&rt->rt_refcnt);
 	}
 	else
-		rt = ip_rt_route(daddr, skb->localroute);
+		rt = ip_rt_route(daddr, skb->localroute, *dev);
 
 
 	if (*dev == NULL)
@@ -590,7 +664,7 @@
 #endif	
 		rt = ip_check_route(&sk->ip_route_cache, daddr,
 				    sk->localroute || (flags&MSG_DONTROUTE) ||
-				    (opt && opt->is_strictroute));
+				    (opt && opt->is_strictroute), sk->bound_device);
 		if (rt == NULL) 
 		{
 			ip_statistics.IpOutNoRoutes++;
diff -urN linux-2.0.31-pre4/net/ipv4/ip_sockglue.c linux-2.0.31-pre5/net/ipv4/ip_sockglue.c
--- linux-2.0.31-pre4/net/ipv4/ip_sockglue.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/ip_sockglue.c	2003-08-15 15:03:59.000000000 -0700
@@ -12,6 +12,7 @@
  *		Martin Mares	:	TOS setting fixed.
  *		Alan Cox	:	Fixed a couple of oopses in Martin's 
  *					TOS tweaks.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  */
 
 #include <linux/config.h>
@@ -293,7 +294,7 @@
 				/*
 				 *	Not set so scan.
 				 */
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL)
 				{
 					dev=rt->rt_dev;
 					atomic_dec(&rt->rt_use);
@@ -345,7 +346,7 @@
  
 			if(mreq.imr_interface.s_addr==INADDR_ANY) 
 			{
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
+				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0,sk->bound_device))!=NULL)
 			        {
 					dev=rt->rt_dev;
 					atomic_dec(&rt->rt_use);
diff -urN linux-2.0.31-pre4/net/ipv4/packet.c linux-2.0.31-pre5/net/ipv4/packet.c
--- linux-2.0.31-pre4/net/ipv4/packet.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/packet.c	2003-08-15 15:03:59.000000000 -0700
@@ -169,10 +169,11 @@
 	 *	raw protocol and you must do your own fragmentation at this level.
 	 */
 	 
-	if(len>dev->mtu+dev->hard_header_len)
+	if ((dev->flags & IFF_SOFTHEADERS && len>dev->mtu)
+	|| len>dev->mtu+dev->hard_header_len)
   		return -EMSGSIZE;
 
-	skb = sock_wmalloc(sk, len, 0, GFP_KERNEL);
+	skb = sock_wmalloc(sk, len+dev->hard_header_len, 0, GFP_KERNEL);
 
 	/*
 	 *	If the write buffer is full, then tough. At this level the user gets to
@@ -191,6 +192,16 @@
 	 
 	skb->sk = sk;
 	skb->free = 1;
+	if (dev->flags & IFF_SOFTHEADERS) {
+        	/* Do the hard header calls for drivers that must set the
+		 * headers at transmission time by themselves. PPP and ISDN
+	 	 * are the notable offenders here. A proper fix requires some
+         	 * changes at the device driver level, but that is a 2.1 issue.
+         	 */
+        	skb_reserve(skb,dev->hard_header_len);
+		if (dev->hard_header)
+			dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,len);
+	}
 	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	skb->arp = 1;		/* No ARP needs doing on this (complete) frame */
 	skb->protocol = proto;
diff -urN linux-2.0.31-pre4/net/ipv4/rarp.c linux-2.0.31-pre5/net/ipv4/rarp.c
--- linux-2.0.31-pre4/net/ipv4/rarp.c	1996-10-29 17:42:42.000000000 -0800
+++ linux-2.0.31-pre5/net/ipv4/rarp.c	2003-08-15 15:03:59.000000000 -0700
@@ -354,7 +354,7 @@
  *	Is it reachable directly ?
  */
   
-	rt = ip_rt_route(ip, 0);
+	rt = ip_rt_route(ip, 0, NULL);
 	if (rt == NULL)
 		return -ENETUNREACH;
 	dev = rt->rt_dev;
diff -urN linux-2.0.31-pre4/net/ipv4/route.c linux-2.0.31-pre5/net/ipv4/route.c
--- linux-2.0.31-pre4/net/ipv4/route.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/route.c	2003-08-15 15:03:59.000000000 -0700
@@ -42,6 +42,9 @@
  *		Bjorn Ekwall	:	Kerneld route support.
  *		Alan Cox	:	Multicast fixed (I hope)
  * 		Pavel Krauz	:	Limited broadcast fixed
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
+ *		Andi Kleen	:	Don't send multicast addresses to
+ *					kerneld.	
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -226,10 +229,9 @@
 		
 		for ( ; f; f = f->fib_next)
 		{
-			if ((dst ^ f->fib_dst) & fz->fz_mask)
+			if (((dst ^ f->fib_dst) & fz->fz_mask) ||
+			    (f->fib_info->fib_flags & RTF_GATEWAY))
 				continue;
-			if (f->fib_info->fib_flags & RTF_GATEWAY)
-				return NULL;
 			return f;
 		}
 	}
@@ -248,9 +250,11 @@
  *	  Host 193.233.7.129 is locally unreachable,
  *	  but old (<=1.3.37) code will send packets destined for it to eth1.
  *
+ * Calling routine can specify a particular interface by setting dev.  If dev==NULL,
+ * any interface will do.
  */
 
-static struct fib_node * fib_lookup_local(__u32 dst)
+static struct fib_node * fib_lookup_local(__u32 dst, struct device *dev)
 {
 	struct fib_zone * fz;
 	struct fib_node * f;
@@ -268,6 +272,8 @@
 		{
 			if ((dst ^ f->fib_dst) & fz->fz_mask)
 				continue;
+			if ( (dev != NULL) && (dev != f->fib_info->fib_dev) )
+				continue;
 			if (!(f->fib_info->fib_flags & RTF_GATEWAY))
 				return f;
 			longest_match_found = 1;
@@ -290,7 +296,7 @@
  *		route add -host 193.233.7.255 eth0
  */
 
-static struct fib_node * fib_lookup(__u32 dst)
+static struct fib_node * fib_lookup(__u32 dst, struct device *dev)
 {
 	struct fib_zone * fz;
 	struct fib_node * f;
@@ -306,6 +312,8 @@
 		{
 			if ((dst ^ f->fib_dst) & fz->fz_mask)
 				continue;
+			if ( (dev != NULL) && (dev != f->fib_info->fib_dev) )
+				continue;
 			return f;
 		}
 	}
@@ -1255,7 +1263,7 @@
 	struct rt_req * rtr;
 	struct rtable * rt;
 
-	rt = ip_rt_route(dst, 0);
+	rt = ip_rt_route(dst, 0, NULL);
 	if (!rt)
 		return;
 
@@ -1324,7 +1332,7 @@
 		if (rth->rt_gateway != daddr)
 		{
 			ip_rt_fast_unlock();
-			rtg = ip_rt_route(rth->rt_gateway, 0);
+			rtg = ip_rt_route(rth->rt_gateway, 0, NULL);
 			ip_rt_fast_lock();
 		}
 
@@ -1396,7 +1404,7 @@
    
  */
 
-struct rtable * ip_rt_slow_route (__u32 daddr, int local)
+struct rtable * ip_rt_slow_route (__u32 daddr, int local, struct device *dev)
 {
 	unsigned hash = ip_rt_hash_code(daddr)^local;
 	struct rtable * rth;
@@ -1416,9 +1424,9 @@
 	}
 
 	if (local)
-		f = fib_lookup_local(daddr);
+		f = fib_lookup_local(daddr, dev);
 	else
-		f = fib_lookup (daddr);
+		f = fib_lookup (daddr, dev);
 
 	if (f)
 	{
@@ -1493,7 +1501,15 @@
 		rth->rt_gateway = rth->rt_dst;
 
 	if (ip_rt_lock == 1)
-		rt_cache_add(hash, rth);
+	{
+		/* Don't add this to the rt_cache if a device was specified,
+		 * because we might have skipped better routes which didn't
+		 * point at the right device. */
+		if (dev != NULL)
+			rth->rt_flags |= RTF_NOTCACHED;
+		else
+			rt_cache_add(hash, rth);
+	}
 	else
 	{
 		rt_free(rth);
@@ -1510,9 +1526,14 @@
 {
 	if (rt)
 		atomic_dec(&rt->rt_refcnt);
+	
+	/* If this rtable entry is not in the cache, we'd better free it once the
+	 * refcnt goes to zero, because nobody else will... */
+	if ( rt && (rt->rt_flags & RTF_NOTCACHED) && (!rt->rt_refcnt) )
+		rt_free(rt);
 }
 
-struct rtable * ip_rt_route(__u32 daddr, int local)
+struct rtable * ip_rt_route(__u32 daddr, int local, struct device *dev)
 {
 	struct rtable * rth;
 
@@ -1520,7 +1541,8 @@
 
 	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
 	{
-		if (rth->rt_dst == daddr)
+		/* If a network device is specified, make sure this route points to it. */
+		if ( (rth->rt_dst == daddr) && ((dev==NULL) || (dev==rth->rt_dev)) )
 		{
 			rth->rt_lastuse = jiffies;
 			atomic_inc(&rth->rt_use);
@@ -1529,7 +1551,7 @@
 			return rth;
 		}
 	}
-	return ip_rt_slow_route (daddr, local);
+	return ip_rt_slow_route (daddr, local, dev);
 }
 
 /*
diff -urN linux-2.0.31-pre4/net/ipv4/sysctl_net_ipv4.c linux-2.0.31-pre5/net/ipv4/sysctl_net_ipv4.c
--- linux-2.0.31-pre4/net/ipv4/sysctl_net_ipv4.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/sysctl_net_ipv4.c	2003-08-15 15:03:59.000000000 -0700
@@ -18,7 +18,8 @@
 extern int sysctl_arp_confirm_interval;
 extern int sysctl_arp_confirm_timeout;
 
-extern int sysctl_ip_forward; 
+extern int sysctl_ip_forward;
+extern int sysctl_ip_dynaddr;
 static int proc_doipforward(ctl_table *ctl, int write, struct file *filp,
 			    void *buffer, size_t *lenp) 
 {
@@ -58,5 +59,7 @@
          &proc_dointvec},
 	{NET_IPV4_FORWARD, "ip_forward", &sysctl_ip_forward, sizeof(int),
 	 0644, NULL, &proc_doipforward },
+        {NET_IPV4_DYNADDR, "ip_dynaddr",
+         &sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec},
 	{0}
 };
diff -urN linux-2.0.31-pre4/net/ipv4/tcp.c linux-2.0.31-pre5/net/ipv4/tcp.c
--- linux-2.0.31-pre4/net/ipv4/tcp.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/tcp.c	2003-08-15 15:03:59.000000000 -0700
@@ -205,6 +205,7 @@
  *		Theodore Ts'o	:	Do secure TCP sequence numbers.
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					This code is dedicated to John Dyson.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *					
  * To Fix:
  *		Fast path the code. Two things here - fix the window calculation
@@ -471,6 +472,11 @@
 			unsigned char state = sk2->state;
 			int sk2_reuse = sk2->reuse;
 
+			/* Two sockets can be bound to the same port if they're
+			 * bound to different interfaces... */
+			if (sk->bound_device != sk2->bound_device)
+				continue;
+
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
 				if((!sk2_reuse)			||
 				   (!sk_reuse)			||
@@ -523,49 +529,58 @@
 	int retval = 0, i, end, bc;
 
 	SOCKHASH_LOCK();
-	i = tcp_bhashfn(start);
-	end = i + TCP_BHTABLE_SIZE;
-	bc = binding_contour;
-	do {
-		struct sock *sk = tcp_bound_hash[tcp_bhashfn(i)];
-		if(!sk) {
-			retval = (start + i);
-			start  = (retval + 1);
-
-			/* Check for decreasing load. */
-			if(bc != 0)
-				binding_contour = 0;
-			goto done;
-		} else {
-			int j = 0;
-			do { sk = sk->bind_next; } while(++j < size && sk);
-			if(j < size) {
-				best = (start + i);
-				size = j;
-				if(bc && size <= bc) {
-					start = best + 1;
-					goto verify;
-				}
-			}
-		}
-	} while(++i != end);
+        i = tcp_bhashfn(start);
+        end = i + TCP_BHTABLE_SIZE;
+        bc = binding_contour;
+        do {
+                struct sock *sk = tcp_bound_hash[i&(TCP_BHTABLE_SIZE-1)];
+                if(!sk) {
+                        /* find the smallest value no smaller than start
+                         * that has this hash value.
+                         */
+                        retval = tcp_bhashnext(start-1,i&(TCP_BHTABLE_SIZE-1));
+
+                        /* Check for decreasing load. */
+                        if (bc != 0)
+                                binding_contour = 0;
+                        goto done;
+                } else {
+                        int j = 0;
+                        do { sk = sk->bind_next; } while (++j < size && sk);
+                        if (j < size) {
+                                best = i&(TCP_BHTABLE_SIZE-1);
+                                size = j;
+                                if (bc && size <= bc)
+                                        goto verify;
+                        }
+                }
+        } while(++i != end);
+        i = best;
 
-	/* Socket load is increasing, adjust our load average. */
-	binding_contour = size;
+        /* Socket load is increasing, adjust our load average. */
+        binding_contour = size;
 verify:
-	if(size < binding_contour)
-		binding_contour = size;
+        if (size < binding_contour)
+                binding_contour = size;
 
-	if(best > 32767)
-		best -= (32768 - PROT_SOCK);
+        retval = tcp_bhashnext(start-1,i);
 
-	while(tcp_lport_inuse(best))
-		best += TCP_BHTABLE_SIZE;
-	retval = best;
-done:
-	if(start > 32767)
-		start -= (32768 - PROT_SOCK);
+	best = retval;	/* mark the starting point to avoid infinite loops */
+        while(tcp_lport_inuse(retval)) {
+               	retval = tcp_bhashnext(retval,i);
+		if (retval > 32767)	/* Upper bound */
+			retval = tcp_bhashnext(PROT_SOCK,i);
+		if (retval == best) {
+			/* This hash chain is full. No answer. */
+			retval = 0;
+			break;
+		}
+        }
 
+done:
+        start = (retval + 1);
+        if (start > 32767 || start < PROT_SOCK)
+                start = PROT_SOCK;
 	SOCKHASH_UNLOCK();
 
 	return retval;
@@ -731,6 +746,7 @@
 		 * here as well.
 		 */
 		sk->cong_window = 1;
+		sk->cong_count = 0;
 		sk->high_seq = sk->sent_seq;
 		return;
 	}
@@ -1254,7 +1270,10 @@
 					sk->write_seq += copy;
 					seglen -= copy;
 				}
-				if (tcp_size >= sk->mss || (flags & MSG_OOB) || !sk->packets_out)
+				/* If we have a full packet or a new OOB
+				 * message, we have to force this packet out.
+				 */
+				if (tcp_size >= sk->mss || (flags & MSG_OOB))
 					tcp_send_skb(sk, skb);
 				else
 					tcp_enqueue_partial(skb, sk);
@@ -1290,8 +1309,14 @@
 
 			delay = 0;
 			tmp = copy + sk->prot->max_header + 15;
-			if (copy < sk->mss && !(flags & MSG_OOB) && sk->packets_out)
-			{
+			/* If won't fill the current packet, and it's not an OOB message,
+			 * then we might want to delay to allow data in the later parts
+			 * of iov to fill this packet out. Note that if we aren't
+			 * Nagling or there are no packets currently out then the top
+			 * level code in tcp_sendmsg() will force any partial packets out
+			 * after we finish building the largest packets this write allows.
+			 */
+			if (copy < sk->mss && !(flags & MSG_OOB)) {
 				tmp = tmp - copy + sk->mtu + 128;
 				delay = 1;
 			}
@@ -2027,8 +2052,8 @@
 			tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT);
 	}
 
-	release_sock(sk);
 	sk->dead = 1;
+	release_sock(sk);
 
 	if(sk->state == TCP_CLOSE)
 		tcp_v4_unhash(sk);
@@ -2201,6 +2226,9 @@
 	buff->free = 0;
 	buff->localroute = sk->localroute;
 
+	/* If this socket is bound to a particular device, make sure we use it. */
+	dev = sk->bound_device;
+
 	/*
 	 *	Put in the IP header and routing stuff.
 	 */
@@ -2255,11 +2283,15 @@
 	 *	but not bigger than device MTU
 	 */
 
-	if(sk->mtu <32)
-		sk->mtu = 32;	/* Sanity limit */
-
 	sk->mtu = min(sk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
 
+	/* Must check it here, just to be absolutely safe.  If we end up
+	 * with an sk->mtu of zero, we can thus end up with an sk->mss
+	 * of zero, which causes us to bomb out in tcp_do_sendmsg. -DaveM
+	 */
+	if(sk->mtu < 32)
+		sk->mtu = 32;	/* Sanity limit */
+
 	/*
 	 *	Put in the TCP options to say MTU.
 	 */
diff -urN linux-2.0.31-pre4/net/ipv4/tcp_input.c linux-2.0.31-pre5/net/ipv4/tcp_input.c
--- linux-2.0.31-pre4/net/ipv4/tcp_input.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/tcp_input.c	2003-08-15 15:03:59.000000000 -0700
@@ -33,6 +33,7 @@
  *				:	with SYN flooding attacks.
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					This code is dedicated to John Dyson.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  */
 
 #include <linux/config.h>
@@ -154,13 +155,13 @@
 
 /*
  * This code needs to be a bit more clever.
- * Does 2 minute timeouts now. Still just a circular buffer.
+ * Does 300 second timeouts now. Still just a circular buffer.
  * At most 32 validations stored. New validations are ignored
  * if all 32 validations are currently valid. To do otherwise
  * allows a situation in which clearances are forgotten before
  * they can be used (provided valid traffic is coming fast enough).
  * The buffer should really be as long as the number of valid
- * connections we want to accept in an 2 minute period.
+ * connections we want to accept in an 300 second period.
  * 32 is maybe to small. On the other hand, the validation check
  * algorithm has to walk the whole table, which is also stupid.
  * It would be better to have a combined hash/circular buffer.
@@ -185,7 +186,7 @@
 	int i;
 	for (i = 0; i < 32; i++)
 		if (clearances[i].saddr == saddr
-		&& clearances[i].tstamp > jiffies-HZ*120)
+		&& clearances[i].tstamp > jiffies-HZ*300)
 			return 1;
 	return 0;
 }
@@ -207,10 +208,12 @@
 #ifdef CONFIG_SYN_COOKIES
 /*
  *	MTU values we can represent in fall back mode.
- *	FIXME. I sort of picked these out of a hat. I should
- *	probably look around for docs on what common values are.
+ *	These values are partially borrowed from Jeff Weisberg's SunOS
+ *	implementation of SYNCOOKIES. I have added an extra limiting
+ * 	value of 64 to deal with the case of very small MTU values.
+ *	(e.g. long delay packet radio links, 1200 baud modems.)
  */
-static __u32 cookie_mtu[8] = { 64, 128, 256, 296, 512, 576, 1024, 1500 };
+static __u32 cookie_mtu[8] = { 64, 256, 512, 536, 1024, 1440, 1460, 4312 };
 #endif
 
 extern void tcp_v4_hash(struct sock *sk);
@@ -222,27 +225,43 @@
  * to specify the remote port nor the remote address for the
  * connection.  So always assume those are both wildcarded
  * during the search since they can never be otherwise.
- *
- * XXX Later on, hash on both local port _and_ local address,
- * XXX to handle a huge IP alias'd box.  Keep in mind that
- * XXX such a scheme will require us to run through the listener
- * XXX hash twice, once for local addresses bound, and once for
- * XXX the local address wildcarded (because the hash is different).
  */
-static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum)
+static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum,
+					  struct device *dev)
 {
 	struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
 	struct sock *result = NULL;
+	int score, hiscore = 0;
 
 	for(; sk; sk = sk->next) {
 		if(sk->num == hnum) {
 			__u32 rcv_saddr = sk->rcv_saddr;
+			score = 1;
 
+			/* If this socket is bound to a particular IP address,
+			 * does the dest IPaddr of the packet match it?
+			 */
 			if(rcv_saddr) {
-				if(rcv_saddr == daddr)
-					return sk; /* Best possible match. */
-			} else if(!result)
+				if(rcv_saddr != daddr)
+					continue;
+				score++;
+			}
+
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if (sk->bound_device) {
+				if (dev != sk->bound_device)
+					continue;
+				score++;
+			}
+
+			/* Check the score--max is 3. */
+			if (score == 3)
+				return sk; /* Best possible match. */
+			if (score > hiscore) {
+				hiscore = score;
 				result = sk;
+			}
 		}
 	}
 	return result;
@@ -252,7 +271,8 @@
  * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
  */
 static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
-					   u32 saddr, u16 sport, u32 daddr, u16 dport)
+					   u32 saddr, u16 sport, u32 daddr, 
+					   u16 dport, struct device *dev)
 {
 	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
@@ -266,21 +286,23 @@
 		if(sk->daddr		== saddr		&& /* remote address */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
-		   sk->rcv_saddr	== daddr)		   /* local address  */
+		   sk->rcv_saddr	== daddr		&& /* local address  */
+		   ((sk->bound_device==NULL) || (sk->bound_device==dev))  )
 			goto hit; /* You sunk my battleship! */
-	sk = tcp_v4_lookup_longway(daddr, hnum);
+	sk = tcp_v4_lookup_longway(daddr, hnum, dev);
 hit:
 	return sk;
 }
  
-__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport,
+				      struct device *dev)
 {
-	return __tcp_v4_lookup(0, saddr, sport, daddr, dport);
+	return __tcp_v4_lookup(0, saddr, sport, daddr, dport, dev);
 }
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 #define secondlist(hpnum, sk, fpass) \
-({ struct sock *s1; if(!(sk) && (fpass)--) \
+({ struct sock *s1; if((hpnum) && !(sk) && (fpass)--) \
 	s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \
    else \
 	s1 = (sk); \
@@ -295,7 +317,8 @@
 
 struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 				 unsigned short rnum, unsigned long laddr,
-				 unsigned long paddr, unsigned short pnum)
+				 unsigned long paddr, unsigned short pnum,
+				 struct device *dev)
 {
 	struct sock *s, *result = NULL;
 	int badness = -1;
@@ -327,7 +350,12 @@
 					continue;
 				score++;
 			}
-			if(score == 3 && s->num == hnum) {
+			if(s->bound_device) {
+				if (s->bound_device != dev)
+					continue;
+				score++;
+			}
+			if(score == 4 && s->num == hnum) {
 				result = s;
 				break;
 			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
@@ -473,7 +501,7 @@
 	  	switch(opcode)
 	  	{
 	  		case TCPOPT_EOL:
-	  			return;
+	  			goto ende;
 	  		case TCPOPT_NOP:	/* Ref: RFC 793 section 3.1 */
 	  			length--;
 	  			ptr--;		/* the opsize=*ptr++ above was a mistake */
@@ -481,7 +509,7 @@
 	  		
 	  		default:
 	  			if(opsize<=2)	/* Avoid silly options looping forever */
-	  				return;
+	  				goto ende;
 	  			switch(opcode)
 	  			{
 	  				case TCPOPT_MSS:
@@ -497,7 +525,7 @@
 	  			length-=opsize;
 	  	}
 	}
-	if (th->syn) 
+ende:	if (th->syn) 
 	{
 		if (! mss_seen)
 		      sk->mtu=min(sk->mtu, 536);  /* default MSS if none sent */
@@ -590,7 +618,7 @@
 			/* Only let this warning get printed once a minute. */
 			if (jiffies - warning_time > HZ*60) {
 				warning_time = jiffies;
-				printk(KERN_INFO "Warning: possible SYN flooding. Sending cookies.\n");
+				printk(KERN_INFO "Warning: possible SYN flooding on port %d. Sending cookies.\n", ntohs(th->dest));
 			}
 #ifdef CONFIG_RST_COOKIES
 			tcp_send_synack_probe(daddr, saddr, th, &tcp_prot,
@@ -598,6 +626,12 @@
 #endif
 #ifdef CONFIG_SYN_COOKIES
 			send_cookie = 1;
+#else
+			/* If we only have RST cookies we should
+			 * not drop through to the rest of the response code.
+			 */
+			kfree_skb(skb, FREE_READ);
+			return;
 #endif
 #ifdef CONFIG_RST_COOKIES
 		} else if (sk->ack_backlog >= 2*sk->max_ack_backlog) {
@@ -634,6 +668,8 @@
 
 	/* Or else we die! -DaveM */
 	newsk->sklist_next = NULL;
+	/* and die again -- erics */
+	newsk->pprev = NULL;
 
 	newsk->opt = NULL;
 	newsk->ip_route_cache  = NULL;
@@ -667,7 +703,7 @@
 	newsk->rtt = 0;
 	newsk->rto = TCP_TIMEOUT_INIT;
 	newsk->mdev = TCP_TIMEOUT_INIT;
-	newsk->max_window = 0;
+	newsk->max_window = 32;	/* It cannot be left at zero. -DaveM */
 	/*
 	 * See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
@@ -736,8 +772,18 @@
 	newsk->daddr = saddr;
 	newsk->saddr = daddr;
 	newsk->rcv_saddr = daddr;
-	tcp_v4_hash(newsk);
-	add_to_prot_sklist(newsk);
+#ifdef CONFIG_SYN_COOKIES
+	/* Don't actually stuff the socket into the protocol lists
+	 * if we are going to just destroy it anyway. We don't want any
+	 * funnies happening if the next packet arrives before we get
+	 * a chance to clean this one up.
+	 */
+	if (!send_cookie)
+#endif
+	{
+		tcp_v4_hash(newsk);
+		add_to_prot_sklist(newsk);
+	}
 
 	newsk->acked_seq = skb->seq + 1;
 	newsk->copied_seq = skb->seq + 1;
@@ -758,7 +804,8 @@
 	 * 	Note use of sk->user_mss, since user has no direct access to newsk 
 	 */
 
-	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
+			 sk->bound_device);
 	newsk->ip_route_cache = rt;
 	
 	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
@@ -779,6 +826,14 @@
 
 	newsk->mtu = min(newsk->mtu, dev->mtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
 
+	/* Must check it here, just to be absolutely safe.  If we end up
+	 * with a newsk->{max_window,mtu} of zero, we can thus end up with
+	 * a newsk->mss of zero, which causes us to bomb out in
+	 * tcp_do_sendmsg. -DaveM
+	 */
+	if(newsk->mtu < 32)
+		newsk->mtu = 32;
+
 #ifdef CONFIG_SKIP
 	
 	/*
@@ -918,7 +973,7 @@
 	newsk->rtt = 0;
 	newsk->rto = TCP_TIMEOUT_INIT;
 	newsk->mdev = TCP_TIMEOUT_INIT;
-	newsk->max_window = 0;
+	newsk->max_window = 32; /* It cannot be left at zero. -DaveM */
 	/*
 	 * See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
@@ -1001,7 +1056,8 @@
 	newsk->ip_ttl=sk->ip_ttl;
 	newsk->ip_tos=skb->ip_hdr->tos;
 
-	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0,
+			 sk->bound_device);
 	newsk->ip_route_cache = rt;
 	
 	if (rt!=NULL && (rt->rt_flags&RTF_WINDOW))
@@ -1263,8 +1319,12 @@
 			/* We need to be a bit careful to preserve the
 			 * count of packets that are out in the system here.
 			 */
-			sk->ssthresh = max(sk->cong_window >> 1, 2);
+			sk->ssthresh = max(
+				min(sk->cong_window,
+				(sk->window_seq-sk->rcv_ack_seq)/max(sk->mss,1))
+				 >> 1, 2);
 			sk->cong_window = sk->ssthresh+MAX_DUP_ACKS+1;
+			sk->cong_count = 0;
 			tmp = sk->packets_out;
 			tcp_do_retransmit(sk,0);
 			sk->packets_out = tmp;
@@ -1284,6 +1344,7 @@
 		if (sk->rcv_ack_cnt > MAX_DUP_ACKS) {
 			/* Don't allow congestion window to drop to zero. */
 			sk->cong_window = max(sk->ssthresh, 1);
+			sk->cong_count = 0;
 		}
 		sk->window_seq = window_seq;
 		sk->rcv_ack_seq = ack;
@@ -1534,19 +1595,6 @@
 	}
 
 	/*
-	 *	We have nothing queued but space to send. Send any partial
-	 *	packets immediately (end of Nagle rule application).
-	 */
-	 
-	if (sk->packets_out == 0
-	    && sk->partial != NULL
-	    && skb_queue_empty(&sk->write_queue)
-	    && sk->send_head == NULL) 
-	{
-		tcp_send_partial(sk);
-	}
-
-	/*
 	 * In the LAST_ACK case, the other end FIN'd us.  We then FIN'd them, and
 	 * we are now waiting for an acknowledge to our FIN.  The other end is
 	 * already in TIME_WAIT.
@@ -1618,6 +1666,14 @@
 	if (sk->state==TCP_SYN_RECV)
 	{
 		tcp_set_state(sk, TCP_ESTABLISHED);
+
+		/* Must check for peer advertising zero sized window
+		 * or else we get a sk->{mtu,mss} of zero and thus bomb out
+		 * in tcp_do_sendmsg. -DaveM
+		 */
+		if(sk->max_window == 0)
+			sk->max_window = 32;
+
 		tcp_options(sk,th);
 
 #if 0
@@ -1628,11 +1684,7 @@
 		sk->copied_seq = sk->acked_seq;
 		if(!sk->dead)
 			sk->state_change(sk);
-		if(sk->max_window==0)
-		{
-			sk->max_window=32;	/* Sanity check */
-			sk->mss=min(sk->max_window,sk->mtu);
-		}
+
 		/* Reset the RTT estimator to the initial
 		 * state rather than testing to avoid
 		 * updating it on the ACK to the SYN packet.
@@ -1650,11 +1702,20 @@
 	 * If we are retransmitting, and we acked a packet on the retransmit
 	 * queue, and there is still something in the retransmit queue,
 	 * then we can output some retransmission packets.
+	 *
+	 * Note that we need to be a bit careful here about getting the
+	 * correct TIME_WRITE timer set. If we just got an ack of a
+	 * packet we where retransmitting, we will retransmit the next
+	 * packet in the retransmit queue below, and the timeout
+	 * should now start from the time we retransmitted that packet.
+	 * The resetting of the TIME_WRITE timer above will have set it
+	 * relative to the prior transmission time, which would be wrong.
 	 */
 
 	if (sk->send_head != NULL && (flag&2) && sk->retransmits)
 	{
 		tcp_do_retransmit(sk, 1);
+		tcp_reset_xmit_timer(sk, TIME_WRITE, sk->rto);
 	}
 
 	return 1;
@@ -2196,7 +2257,8 @@
 	struct tcphdr *th = (struct tcphdr *)(skb->h.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+	sk = tcp_v4_proxy_lookup(th->dest, iph->saddr, th->source, iph->daddr,
+				 0, 0, skb->dev);
 	if (!sk)
 		return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
@@ -2265,10 +2327,10 @@
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 		if (skb->redirport)
 			sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
-						 dev->pa_addr, skb->redirport);
+						 dev->pa_addr, skb->redirport, dev);
 		else
 #endif
-		sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
+		sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest, dev);
 		if (!sk)
 			goto no_tcp_socket;
 		skb->sk = sk;
@@ -2484,6 +2546,13 @@
 				 */
 				tcp_ack(sk,th,skb->ack_seq,len);
 
+				/* We must check here (before tcp_options) whether
+				 * peer advertised a zero sized window on us, else
+				 * we end up with a zero sk->{mtu,mss} and thus bomb
+				 * out in tcp_do_sendmsg. -DaveM
+				 */
+				if(sk->max_window == 0)
+					sk->max_window = 32;
 
 				/*
 				 *	Ok.. it's good. Set up sequence numbers and
@@ -2507,11 +2576,7 @@
 					sk->state_change(sk);
 					sock_wake_async(sk->socket, 0);
 				}
-				if(sk->max_window==0)
-				{
-					sk->max_window = 32;
-					sk->mss = min(sk->max_window, sk->mtu);
-				}
+
 				/* Reset the RTT estimator to the initial
 				 * state rather than testing to avoid
 				 * updating it on the ACK to the SYN packet.
@@ -2578,7 +2643,7 @@
 			sk->shutdown = SHUTDOWN_MASK;
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 			sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
-						 dev->pa_addr, skb->redirport);
+						 dev->pa_addr, skb->redirport, dev);
 #else
 			sk = NULL;
 #endif
@@ -2679,6 +2744,19 @@
 		kfree_skb(skb, FREE_READ);
 
 	/*
+	 *	If we had a partial packet being help up due to
+	 *	application of Nagle's rule we are now free to send it.
+	 */
+	if (th->ack
+	    && sk->packets_out == 0
+	    && sk->partial != NULL
+	    && skb_queue_empty(&sk->write_queue)
+	    && sk->send_head == NULL) 
+	{
+		tcp_send_partial(sk);
+	}
+
+	/*
 	 *	If our receive queue has grown past its limits,
 	 *	try to prune away duplicates etc..
 	 */
diff -urN linux-2.0.31-pre4/net/ipv4/tcp_output.c linux-2.0.31-pre5/net/ipv4/tcp_output.c
--- linux-2.0.31-pre4/net/ipv4/tcp_output.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/tcp_output.c	2003-08-15 15:03:59.000000000 -0700
@@ -32,6 +32,8 @@
  *				  thus the outgoing device does as well, when
  *				  skb's are on the retransmit queue which still
  *				  refer to the old obsolete destination.
+ *              Elliot Poger    : Added support for SO_BINDTODEVICE.
+ *	Juan Jose Ciarlante	: Added sock dynamic source address rewriting
  */
 
 #include <linux/config.h>
@@ -50,22 +52,10 @@
  *  RECV.NEXT + RCV.WIN fixed until:
  *  RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
  *
- * Experiments against BSD and Solaris machines show that following
- * these rules results in the BSD and Solaris machines making very
- * bad guesses about how much data they can have in flight.
- *
- * Instead we follow the BSD lead and offer a window that gives
- * the size of the current free space, truncated to a multiple
- * of 1024 bytes. If the window is smaller than
- * 	min(sk->mss, MAX_WINDOW/2)
- * then we advertise the window as having size 0, unless this
- * would shrink the window we offered last time.
- * This results in as much as double the throughput as the original
- * implementation.
- *
  * We do BSD style SWS avoidance -- note that RFC1122 only says we
  * must do silly window avoidance, it does not require that we use
- * the suggested algorithm.
+ * the suggested algorithm. Following BSD avoids breaking header
+ * prediction.
  *
  * The "rcvbuf" and "rmem_alloc" values are shifted by 1, because
  * they also contain buffer handling overhead etc, so the window
@@ -73,33 +63,41 @@
  */
 int tcp_new_window(struct sock * sk)
 {
-	unsigned long window;
+	unsigned long window = sk->window;
 	unsigned long minwin, maxwin;
+	unsigned long free_space;
 
 	/* Get minimum and maximum window values.. */
 	minwin = sk->mss;
 	if (!minwin)
 		minwin = sk->mtu;
+	if (!minwin) {
+		printk(KERN_DEBUG "tcp_new_window: mss fell to 0.\n");
+		minwin = 1;
+	}
 	maxwin = sk->window_clamp;
 	if (!maxwin)
 		maxwin = MAX_WINDOW;
+
 	if (minwin > maxwin/2)
 		minwin = maxwin/2;
 
 	/* Get current rcvbuf size.. */
-	window = sk->rcvbuf/2;
-	if (window < minwin) {
+	free_space = sk->rcvbuf/2;
+	if (free_space < minwin) {
 		sk->rcvbuf = minwin*2;
-		window = minwin;
+		free_space = minwin;
 	}
 
 	/* Check rcvbuf against used and minimum window */
-	window -= sk->rmem_alloc/2;
-	if ((long)(window - minwin) < 0)		/* SWS avoidance */
-		window = 0;
+	free_space -= sk->rmem_alloc/2;
+	if ((long)(free_space - minwin) < 0)		/* SWS avoidance */
+		return 0;
+
+	/* Try to avoid the divide and multiply if we can */
+        if (window <= free_space - minwin || window > free_space)
+                window = (free_space/minwin)*minwin;
 
-	if (window > 1023)
-		window &= ~1023;
 	if (window > maxwin)
 		window = maxwin;
 	return window;
@@ -166,8 +164,10 @@
 	 * anything for a long time, in which case we have no reason to
 	 * believe that our congestion window is still correct.
 	 */
-	if (sk->send_head == 0 && (jiffies - sk->idletime) > sk->rto)
+	if (sk->send_head == 0 && (jiffies - sk->idletime) > sk->rto) {
 		sk->cong_window = 1;
+		sk->cong_count = 0;
+	}
 
 	/*
 	 *	Actual processing.
@@ -491,7 +491,8 @@
 			/* ANK: UGLY, but the bug, that was here, should be fixed.
 			 */
 			struct options *  opt = (struct options*)skb->proto_priv;
-			rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr, skb->localroute);
+			rt = ip_check_route(&sk->ip_route_cache, opt->srr?opt->faddr:iph->daddr,
+					    skb->localroute, sk->bound_device);
 	        }
 
 		iph->id = htons(ip_id_count++);
@@ -534,6 +535,8 @@
 				struct sk_buff *skb2 = sk->write_queue.next;
 				while (skb2 && skb2->dev == skb->dev) {
 					skb2->raddr=rt->rt_gateway;
+                                        if (sk->state == TCP_SYN_SENT && sysctl_ip_dynaddr)
+                                                ip_rewrite_addrs (sk, skb2, dev);
 					skb2->dev = dev;
 					skb2->arp=1;
 					if (rt->rt_hh)
@@ -557,6 +560,8 @@
 				}
 			}
 			skb->raddr=rt->rt_gateway;
+                        if (skb->dev !=dev && sk->state == TCP_SYN_SENT && sysctl_ip_dynaddr)
+                                ip_rewrite_addrs(sk, skb, dev);
 			skb->dev=dev;
 			skb->arp=1;
 #ifdef CONFIG_FIREWALL
@@ -987,8 +992,25 @@
 	ptr[2] = ((newsk->mtu) >> 8) & 0xff;
 	ptr[3] =(newsk->mtu) & 0xff;
 	buff->csum = csum_partial(ptr, 4, 0);
+#ifdef CONFIG_SYN_COOKIES
+	/* Don't save buff on the newsk chain if we are going to destroy
+	 * newsk anyway in a second, it just delays getting rid of newsk.
+	 */
+ 	if (destroy) {
+		/* BUFF was charged to NEWSK, _this_ is what we want
+		 * to undo so the SYN cookie can be killed now.  SKB
+		 * is charged to SK, below we will undo that when
+		 * we kfree SKB.
+		 */
+		buff->sk = NULL;
+		atomic_sub(buff->truesize, &newsk->wmem_alloc);
+	}
+#endif
 	tcp_send_check(t1, newsk->saddr, newsk->daddr, sizeof(*t1)+4, buff);
-	newsk->prot->queue_xmit(newsk, ndev, buff, destroy);
+	if (destroy)
+		newsk->prot->queue_xmit(NULL, ndev, buff, 1);
+	else
+		newsk->prot->queue_xmit(newsk, ndev, buff, 0);
 
 
 #ifdef CONFIG_SYN_COOKIES
@@ -1389,7 +1411,9 @@
 	/* Update our starting seq number */
 	skb->seq = ack;
 	th->seq = htonl(ack);
+
 	iph->tot_len = htons(ntohs(iph->tot_len)-diff);
+	ip_send_check(iph);
 
 	/* Get the partial checksum for the IP options */
 	if (th->doff*4 - sizeof(*th) > 0)
diff -urN linux-2.0.31-pre4/net/ipv4/tcp_timer.c linux-2.0.31-pre5/net/ipv4/tcp_timer.c
--- linux-2.0.31-pre4/net/ipv4/tcp_timer.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/tcp_timer.c	2003-08-15 15:03:59.000000000 -0700
@@ -138,7 +138,9 @@
 		return;
 	}
 
-	sk->ssthresh = sk->cong_window >> 1; /* remember window where we lost */
+	/* remember window where we lost */
+	sk->ssthresh = min(sk->cong_window,
+			(sk->window_seq-sk->rcv_ack_seq)/max(sk->mss,1)) >> 1;
 	/* sk->ssthresh in theory can be zero.  I guess that's OK */
 	sk->cong_count = 0;
 	sk->cong_window = 1;
diff -urN linux-2.0.31-pre4/net/ipv4/udp.c linux-2.0.31-pre5/net/ipv4/udp.c
--- linux-2.0.31-pre4/net/ipv4/udp.c	1997-04-08 08:47:47.000000000 -0700
+++ linux-2.0.31-pre5/net/ipv4/udp.c	2003-08-15 15:03:59.000000000 -0700
@@ -52,6 +52,7 @@
  *		David S. Miller	:	New socket lookup architecture for ISS.
  *					Last socket cache retained as it
  *					does have a high hit rate.
+ *              Elliot Poger    :       Added support for SO_BINDTODEVICE.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -130,6 +131,11 @@
 		if((sk2->num == snum) && (sk2 != sk)) {
 			int sk2_reuse = sk2->reuse;
 
+			/* Two sockets can be bound to the same port if they're
+			 * bound to different interfaces... */
+			if (sk->bound_device != sk2->bound_device)
+				continue;
+
 			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
 				if((!sk2_reuse) || (!sk_reuse)) {
 					retval = 1;
@@ -147,7 +153,7 @@
 	return retval;
 }
 
-static inline int udp_lport_inuse(int num)
+static inline int udp_lport_inuse(u16 num)
 {
 	struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
 
@@ -161,36 +167,42 @@
 /* Shared by v4/v6 tcp. */
 unsigned short udp_good_socknum(void)
 {
-	static int start = 0;
-	unsigned short base;
-	int i, best = 0, size = 32767; /* a big num. */
 	int result;
-
-	base = PROT_SOCK + (start & 1023) + 1;
+	static int start = 0;
+	int i, best, best_size_so_far;
 
 	SOCKHASH_LOCK();
-	for(i = 0; i < UDP_HTABLE_SIZE; i++) {
-		struct sock *sk = udp_hash[i];
-		if(!sk) {
-			start = (i + 1 + start) & 1023;
-			result = i + base + 1;
+
+	/* Select initial not-so-random "best" */
+	best = PROT_SOCK + 1 + (start & 1023);
+	best_size_so_far = 32767;	/* "big" num */
+	result = best;
+	for (i = 0; i < UDP_HTABLE_SIZE; i++, result++) {
+		struct sock *sk;
+		int size;
+
+		sk = udp_hash[result & (UDP_HTABLE_SIZE - 1)];
+
+		/* No clashes - take it */
+		if (!sk)
 			goto out;
-		} else {
-			int j = 0;
-			do {
-				if(++j >= size)
-					goto next;
-			} while((sk = sk->next));
-			best = i;
-			size = j;
-		}
-	next:
+
+		/* Is this one better than our best so far? */
+		size = 0;
+		do {
+			if(++size >= best_size_so_far)
+				goto next;
+		} while((sk = sk->next) != NULL);
+		best_size_so_far = size;
+		best = result;
+next:
 	}
 
-	while(udp_lport_inuse(base + best + 1))
+	while (udp_lport_inuse(best))
 		best += UDP_HTABLE_SIZE;
-	result = (best + base + 1);
+	result = best;
 out:
+	start = result;
 	SOCKHASH_UNLOCK();
 	return result;
 }
@@ -255,7 +267,8 @@
 /* UDP is nearly always wildcards out the wazoo, it makes no sense to try
  * harder than this. -DaveM
  */
-__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport,
+				      struct device *dev)
 {
 	struct sock *sk, *result = NULL;
 	unsigned short hnum = ntohs(dport);
@@ -279,7 +292,15 @@
 					continue;
 				score++;
 			}
-			if(score == 3) {
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if (sk->bound_device) {
+				if (dev == sk->bound_device)
+					score++;
+				else
+					continue;  /* mismatch--not this sock */
+			}
+			if(score == 4) {
 				result = sk;
 				break;
 			} else if(score > badness) {
@@ -308,7 +329,8 @@
 
 struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 				 unsigned short rnum, unsigned long laddr,
-				 unsigned long paddr, unsigned short pnum)
+				 unsigned long paddr, unsigned short pnum,
+				 struct device *dev)
 {
 	struct sock *s, *result = NULL;
 	int badness = -1;
@@ -340,7 +362,14 @@
 					continue;
 				score++;
 			}
-			if(score == 3 && s->num == hnum) {
+			/* If this socket is bound to a particular interface,
+			 * did the packet come in on it? */
+			if(s->bound_device) {
+				if (s->bound_device != dev)
+					continue;
+				score++;
+			}
+			if(score == 4 && s->num == hnum) {
 				result = s;
 				break;
 			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
@@ -363,7 +392,8 @@
 					     unsigned short num,
 					     unsigned long raddr,
 					     unsigned short rnum,
-					     unsigned long laddr)
+					     unsigned long laddr,
+					     struct device *dev)
 {
 	struct sock *s = sk;
 	unsigned short hnum = ntohs(num);
@@ -372,6 +402,7 @@
 		    (s->dead && (s->state == TCP_CLOSE))		||
 		    (s->daddr && s->daddr!=raddr)			||
 		    (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
+		    ((s->bound_device) && (s->bound_device!=dev))       ||
 		    (s->rcv_saddr  && s->rcv_saddr != laddr))
 			continue;
 		break;
@@ -408,7 +439,7 @@
 	
 	uh = (struct udphdr *)header;  
    
-	sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source);
+	sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source, NULL);
 	if (sk == NULL) 
 	  	return;	/* No socket for error */
   	
@@ -850,7 +881,7 @@
 	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
 		return -EACCES;			/* Must turn broadcast on first */
   	
-  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute);
+  	rt=ip_rt_route((__u32)usin->sin_addr.s_addr, sk->localroute, sk->bound_device);
   	if (rt==NULL)
   		return -ENETUNREACH;
   	if(!sk->saddr)
@@ -869,8 +900,9 @@
 {
 	lock_sock(sk);
 	sk->state = TCP_CLOSE;
-	release_sock(sk);
 	sk->dead = 1;
+	release_sock(sk);
+	udp_v4_unhash(sk);
 	destroy_sock(sk);
 }
 
@@ -918,7 +950,8 @@
 	struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+	sk = udp_v4_proxy_lookup(uh->dest, iph->saddr, uh->source, iph->daddr,
+				 0, 0, skb->dev);
 	if (!sk)
 		return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
@@ -940,7 +973,7 @@
 
 	SOCKHASH_LOCK();
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
-	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr, skb->dev);
 	if(sk) {
 		struct sock *sknext = NULL;
 
@@ -948,7 +981,7 @@
 			struct sk_buff *skb1 = skb;
 
 			sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
-						   uh->source, daddr);
+						   uh->source, daddr, skb->dev);
 			if(sknext)
 				skb1 = skb_clone(skb, GFP_ATOMIC);
 
@@ -1066,10 +1099,10 @@
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if(skb->redirport)
 		sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
-					 daddr, dev->pa_addr, skb->redirport);
+					 daddr, dev->pa_addr, skb->redirport, dev);
 	else
 #endif
-	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest);
+	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest, dev);
 	
 	if (sk == NULL) 
   	{
diff -urN linux-2.0.31-pre4/net/netsyms.c linux-2.0.31-pre5/net/netsyms.c
--- linux-2.0.31-pre4/net/netsyms.c	2003-08-15 15:03:58.000000000 -0700
+++ linux-2.0.31-pre5/net/netsyms.c	2003-08-15 15:03:59.000000000 -0700
@@ -104,6 +104,7 @@
 	X(ip_options_compile),
 	X(ip_rt_put),
 	X(arp_send),
+	X(arp_bind_cache),
 	X(ip_id_count),
 	X(ip_send_check),
 	X(ip_forward),
