diff -u --recursive --new-file v2.1.14/linux/CREDITS linux/CREDITS
--- v2.1.14/linux/CREDITS	Thu Dec 12 17:02:38 1996
+++ linux/CREDITS	Wed Dec 11 16:59:19 1996
@@ -544,14 +544,13 @@
 D: Selection mechanism
 
 N: Jochen Hein
-E: jochen.hein@informatik.tu-clausthal.de
+E: jochen.hein@delphi.central.de
 P: 1024/4A27F015 25 72 FB E3 85 9F DE 3B  CB 0A DA DA 40 77 05 6C
 D: National Language Support
-D: German Support-Disks for SLS/Slackware called SLT
 D: Linux Internationalization Project
-D: DOSemu
-S: Muehlenweg 19
-S: 34266 Niestetal
+D: German Localization for Linux and GNU software
+S: Frankenstraße
+S: 34131 Kassel
 S: Germany
 
 N: Richard Henderson
diff -u --recursive --new-file v2.1.14/linux/Documentation/00-INDEX linux/Documentation/00-INDEX
--- v2.1.14/linux/Documentation/00-INDEX	Thu Jun  6 14:57:43 1996
+++ linux/Documentation/00-INDEX	Thu Dec 12 16:51:06 1996
@@ -1,8 +1,8 @@
 This is a brief list of all the files in ./linux/Documentation and what
 they contain. If you add a documentation file, please list it here in 
-alphabetical order as well. Note that subdirectories have their own
-index files too.
-							 Thanks -- Paul.
+alphabetical order as well, or risk being hunted down like a rabid dog. 
+Note that subdirectories have their own index files too.
+							 Thanks -- Paul G.
 
 00-INDEX
 	- this file.
@@ -14,6 +14,8 @@
 	- how the boss likes the C code in the kernel to look.
 Configure.help
 	- text file that is used for help when you run "make config"
+IO-mapping.txt
+	- how to access I/O mapped memory from within device drivers.
 SMP.txt
 	- notes, and "To Fix" list for multi-processor Linux. (see smp.tex)
 cdrom/
@@ -24,6 +26,8 @@
 	- plain ASCII listing of all the nodes in /dev/ with major minor #'s
 digiboard.txt
 	- info on the Digiboard PC/X{i,e,eve} multiport boards.
+exception.txt
+	- how linux v2.1 handles exceptions without verify_area etc.
 filesystems/
 	- directory with info on the various filesystems that Linux supports.
 ide.txt
@@ -38,10 +42,16 @@
 	- info on the in-kernel binary support for Java(tm)
 locks.txt
 	- info on file locking implementations, flock() vs. fcntl(), etc.
+logo.gif
+	- Full colour GIF image of Linux logo (penguin)
+logo.txt
+	- Info on creator of above logo & site to get additional images from.
 magic-number.txt
 	- list of magic numbers used to mark/protect kernel data structures.
 mandatory.txt
 	- info on the linux implementation of Sys V mandatory file locking.
+mca.txt
+	- info on supporting Micro Channel Architecture (e.g. PS/2) systems.
 modules.txt
 	- short guide on how to make kernel parts into loadable modules
 networking/
@@ -66,4 +76,7 @@
 	- info on the Unicode character/font mapping used in Linux.
 watchdog.txt
 	- how to auto-reboot Linux if it has "fallen and can't get up". ;-)
+xterm-linux.xpm
+	- XPM image of penguin logo (see logo.txt) sitting on an xterm.
+
 
diff -u --recursive --new-file v2.1.14/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.14/linux/Documentation/Changes	Thu Dec 12 17:02:39 1996
+++ linux/Documentation/Changes	Thu Dec 12 16:51:06 1996
@@ -19,7 +19,7 @@
 generated by texinfo so a diff is useless anyway (though I can
 incorporate one by hand if you insist upon sending it that way ;-).
 
-Last updated: November 20, 1996.
+Last updated: December 3, 1996.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Minimal Requirements
@@ -28,14 +28,15 @@
    Upgrade to at *least* these software revisions before thinking you've
 encountered a bug!
 
-- Kernel modules	 2.0.8
+- Kernel modules	 2.1.13
 - Gnu C 		 2.7.2.1
 - Binutils		 2.7.0.3
-- Linux C Library	 5.4.12
+- Linux C Library	 5.4.13
 - Dynamic Linker (ld.so) 1.8.5
 - Linux C++ Library	 2.7.2.1
 - Procps		 1.01
 - SysVinit		 2.64
+- Util-linux		 2.5
 - Mount                  2.5p
 - Net-tools              1.32-alpha
 - Kbd                    0.91
@@ -60,19 +61,21 @@
 http://sunsite.unc.edu/mdw/HOWTO/ELF-HOWTO.html and upgrade your system
 accordingly.
 
-   For modules to work, you need to be running libc-5.4.7 or greater.
+   For modules to work, you need to be running libc-5.4.x or greater.
 Since updates to libc fix other problems as well (security flaws, for
 example) and since 5.4.7 is missing a few needed symbols, try to get
-the latest 5.4.x you can.  Currently, that is libc-5.4.12.
+the latest 5.4.x you can.  Currently, libc-5.4.13 is the latest public
+release.
 
    If you upgrade to libc-5.4.x, you also have to upgrade your dynamic
-linker (ld.so) to at least 1.8.3, or all sorts of weirdness will happen.
+linker (ld.so) to at least 1.8.5, or all sorts of weirdness will
+happen.  Actually, ld.so-1.8.2 and later will work, but only 1.8.5 is
+widely available, so if you need to upgrade, use it.
 
 Modules
 =======
 
-   You need to upgrade to modules-2.0.8 for kernels 2.1.8 and later.
-Currently, there is no modules package which works with kernel 2.1.1.
+   You need to upgrade to modules-2.1.13 for kernels 2.1.8 and later.
 
 Gnu C
 =====
@@ -105,55 +108,69 @@
 Binutils
 ========
 
-ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/binutils-2.7.0.3.bin.tar.gz
+The 2.7.0.3 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/binutils-2.7.0.3.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/binutils-2.7.0.3.bin.tar.gz
 Installation notes:
-ftp://tsx-11.mit.edu:/pub/linux/packages/GCC/release.binutils-2.7.0.3
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.binutils-2.7.0.3
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.binutils-2.7.0.3
 
 Gnu C
 =====
 
+The 2.7.2.1 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/gcc-2.7.2.1.bin.tar.gz
 ftp://sunsite.unc.edu/pub/Linux/GCC/gcc-2.7.2.1.bin.tar.gz
 Installation notes:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.gcc-2.7.2.1
 ftp://sunsite.unc.edu/pub/Linux/GCC/release.gcc-2.7.2.1
 
 Linux C Library
 ===============
 
-The stable 5.2.18 release:
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.2.18.bin.tar.gz
-Installation notes for 5.2.18:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.2.18
-
-The latest 5.4.12 release (when it gets there):
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.12.bin.tar.gz
-Installation notes for 5.4.12:
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.12
+The 5.4.13 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.13.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.13.bin.tar.gz
+Installation notes for 5.4.13:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.gcc-2.7.2.1
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.13
 
 Linux C++ Library
 =================
 
+The 2.7.2.1 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libg++-2.7.2.1.bin.tar.gz
 ftp://sunsite.unc.edu/pub/Linux/GCC/libg++-2.7.2.1.bin.tar.gz
 Installation notes:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libg++-2.7.2.1
 ftp://sunsite.unc.edu/pub/Linux/GCC/release.libg++-2.7.2.1
 
 Dynamic Linker
 ==============
 
+The 1.8.5 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/ld.so-1.8.5.tar.gz
 ftp://sunsite.unc.edu/pub/Linux/GCC/ld.so-1.8.5.tar.gz
 
 Modules utilities
 =================
 
-ftp://sunsite.unc.edu/pub/Linux/kernel/v2.1/modules-2.0.8.tar.gz
+The 2.1.13 release:
+ftp://tsx-11.mit.edu/pub/linux/sources/system/v2.1/modules-2.1.13.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/kernel/v2.1/modules-2.1.13.tar.gz
 
 Procps utilities
 ================
 
+The 1.01 release:
+ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.01.tgz
 ftp://sunsite.unc.edu/pub/Linux/system/Status/ps/procps-1.01.tgz
 
 SysVinit utilities
 ==================
 
+The 2.64 release:
+ftp://tsx-11.mit.edu/pub/linux/sources/sbin/sysvinit-2.64.tar.gz
 ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.64.tar.gz
 
 Other Info
diff -u --recursive --new-file v2.1.14/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.14/linux/Documentation/Configure.help	Thu Dec 12 17:02:39 1996
+++ linux/Documentation/Configure.help	Thu Dec 12 16:51:07 1996
@@ -127,15 +127,15 @@
 Enhanced IDE/MFM/RLL disk/cdrom/tape support
 CONFIG_BLK_DEV_IDE 
   This will use the full-featured IDE driver to control up to four IDE
-  interfaces, for a combination of up to eight IDE disk/cdrom/tape
-  drives.  Useful information about large (>540MB) IDE disks,
-  soundcard IDE ports, and other topics, is all contained in
-  Documentation/ide.txt.  If you have one or more IDE drives, say Y
-  here.  If your system has no IDE drives, or if memory requirements
-  are really tight, you could say N here, and select the Old harddisk
-  driver instead to save about 13kB of memory in the kernel.  To
-  fine-tune IDE drive/interface parameters for improved performance,
-  look for the hdparm package at
+  interfaces, for a combination of up to eight IDE
+  disk/cdrom/tape/floppy drives.  Useful information about large
+  (>540MB) IDE disks, soundcard IDE ports, module support, and other
+  topics, is all contained in Documentation/ide.txt.  If you have one
+  or more IDE drives, say Y here.  If your system has no IDE drives, or
+  if memory requirements are really tight, you could say N here, and
+  select the Old harddisk driver instead to save about 13kB of memory
+  in the kernel.  To fine-tune IDE drive/interface parameters for
+  improved performance, look for the hdparm package at
   sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/
 
 Old harddisk (MFM/RLL/IDE) driver
@@ -165,7 +165,13 @@
   address (0x1f0), along with IDE drives at the secondary/3rd/4th port
   addresses.  Normally, just say N here; you will then use the new
   driver for all 4 interfaces.
-  
+
+Include IDE/ATA-2 DISK support
+CONFIG_BLK_DEV_IDEDISK
+  This will include enhanced support for MFM/RLL/IDE disks. If you
+  have a MFM/RLL/IDE disk, and there is no special reason to use the
+  old harddisk driver instead, say Y.
+
 Include IDE/ATAPI CDROM support
 CONFIG_BLK_DEV_IDECD
   If you have a CDROM drive using the ATAPI protocol, say Y.  ATAPI is
@@ -173,7 +179,7 @@
   SCSI protocol.  Most new CDROM drives use ATAPI, including the
   NEC-260, Mitsumi FX400, Sony 55E, and just about all non-SCSI
   double(2X), quad(4X), and six(6X) speed drives.  At boot time, the
-  TAPE drive will be identified along with other IDE devices, as "hdb"
+  CDROM drive will be identified along with other IDE devices, as "hdb"
   or "hdc", or something similar.
   If this is your only CDROM drive, you can say N to all other CDROM
   options, but be sure to say Y to the ISO9660 filesystem.  Read the
@@ -190,14 +196,24 @@
   ATAPI is a new protocol used by IDE TAPE and ATAPI drives,
   similar to the SCSI protocol.  At boot time, the TAPE drive will
   be identified along with other IDE devices, as "hdb" or "hdc",
-  or something similar.  Be sure to consult the drivers/block/ide-tape.c
-  and Documentation/ide.txt files for usage information.
+  or something similar, and will be mapped to a character device such
+  as "ht0".  Be sure to consult the drivers/block/ide-tape.c and
+  Documentation/ide.txt files for usage information.
+
+Include IDE/ATAPI FLOPPY support
+CONFIG_BLK_DEV_IDEFLOPPY
+  If you have an IDE floppy drive using the ATAPI protocol, say Y.
+  ATAPI is a new protocol used by IDE CDROM/TAPE/FLOPPY drives,
+  similar to the SCSI protocol.  At boot time, the FLOPPY drive will
+  be identified along with other IDE devices, as "hdb" or "hdc",
+  or something similar.
 
-Support removable IDE interfaces (PCMCIA)
-CONFIG_BLK_DEV_IDE_PCMCIA
-  This option adds code to the IDE driver to handle hot insertion
-  and removal of IDE interfaces and drives, under direction of an
-  external utility (?).  Normally, just say N here.
+SCSI emulation support
+CONFIG_BLK_DEV_IDESCSI
+  This will provide SCSI host adapter emulation for IDE ATAPI devices,
+  and will allow you to use a SCSI driver instead of a native ATAPI
+  driver.  If both SCSI emulation and native support are compiled into
+  the kernel, the native support will be used.  Normally, say N.
 
 CMD640 chipset bugfix/support
 CONFIG_BLK_DEV_CMD640
@@ -538,12 +554,9 @@
   Find out whether you have a PCI motherboard. PCI is the name of a
   bus system, i.e. the way the CPU talks to the other stuff inside
   your box. Other bus systems are ISA, EISA, Microchannel (MCA) or
-  VESA. If you have PCI, say Y, otherwise N. Note1: MCA systems
-  (notably some IBM PS/2's) are not supported by the standard kernels,
-  but patches exist at
-  http://www.undergrad.math.uwaterloo.ca/~cpbeaure/mca-linux.html on
-  the WWW. Note2: some old PCI motherboards have BIOS bugs and may
-  crash if "PCI bios support" is enabled (but they run fine without
+  VESA. If you have PCI, say Y, otherwise N. 
+  Note: some old PCI motherboards have BIOS bugs and may crash if 
+  "PCI bios support" is enabled (but they run fine without
   this option). The PCI-HOWTO, available via ftp (user: anonymous) in
   sunsite.unc.edu:/pub/Linux/docs/HOWTO, contains valuable information
   about which PCI hardware does work under Linux and which doesn't.
@@ -557,6 +570,11 @@
   certain BIOSes if your computer uses a PCI bus system. This is
   recommended; say Y.
 
+MCA support
+CONFIG_MCA
+  MicroChannel Architecture is found in some IBM PS/2 machines and laptops.
+  See Documentation/mca.txt before attempting to build an MCA bus kernel.
+
 Intel 82371 PIIX (Triton I/II) DMA support
 CONFIG_BLK_DEV_TRITON
   If your PCI system uses an IDE harddrive (as opposed to SCSI, say)
@@ -889,7 +907,7 @@
   packets it received. The information is handled by the klogd demon
   which is responsible for kernel messages ("man klogd").
 
-IP: transparent proxying (EXPERIMENTAL)
+IP: transparent proxying
 CONFIG_IP_TRANSPARENT_PROXY
   This enables your Linux firewall to transparently redirect any
   network traffic originating from the local network and destined
@@ -900,7 +918,7 @@
   rules (using the ipfwadm utility) and/or by doing an appropriate
   bind() system call.
 
-IP: masquerading (EXPERIMENTAL)
+IP: masquerading
 CONFIG_IP_MASQUERADE
   If one of the computers on your local network for which your Linux
   box acts as a firewall wants to send something to the outside, your
@@ -919,8 +937,6 @@
   from ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/].)
   Details on how to set things up are contained in the
   IP Masquerading FAQ, available at http://www.indyramp.com/masq/
-  This is EXPERIMENTAL code, which means that it need not be completely
-  stable. If you want this, say Y.
 
 IP: always defragment 
 CONFIG_IP_ALWAYS_DEFRAG 
@@ -958,7 +974,7 @@
   syntax explained in Documentation/networking/alias.txt. If you want
   this, say Y. Most people don't need it and say N.
 
-IP: multicast routing (EXPERIMENTAL)
+IP: multicast routing
 CONFIG_IP_MROUTE
   This is used if you want your machine to act as a router for IP
   packets that have several destination addresses. It is needed on the
@@ -1092,6 +1108,18 @@
   full internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs.
   If you don't know what you are doing, say N.
 
+IPX Type 20 Routing
+CONFIG_IPX_PPROP_ROUTING
+  IPX Type 20 packets are special broadcast messages designed to work
+  across routers.  If you are using an internal network, have multiple
+  interfaces that route IPX, or will want to route IPX connections over
+  ppp to internal networks, setting this will allow the type 20 packets
+  to be propogated to all connected networks.  These packets are used by
+  Novell NETBIOS and the NETBIOS name functions of SMB protocols that
+  work over IPX (e.g. the "Network Neighborhood" on another popular OS
+  cum GUI).  In brief, if your Linux box needs to route IPX packets,
+  this should be set to Y.
+
 Appletalk DDP
 CONFIG_ATALK
   Appletalk is the way Apple computers speak to each other on a
@@ -1181,15 +1209,14 @@
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. The Bridging code is
   still in test. If unsure, say N.
 
-Kernel/User network link driver (EXPERIMENTAL)
+Kernel/User network link driver
 CONFIG_NETLINK
   This driver allows for two-way communication between certain parts
   of the kernel or modules and user processes; the user processes are
   able to read from and write to character special files in the /dev
   directory having major mode 36. So far, the kernel uses it to
   publish some network related information if you enable "Routing
-  messages", below. Say Y if you want to experiment with it; this is
-  EXPERIMENTAL code, which means that it need not be completely stable.
+  messages", below or firewall netlink.
   You need to include this if you want to use arpd, a daemon that
   helps keep the internal ARP cache (a mapping between IP addresses
   and hardware addresses on the local network) small. If unsure, say
@@ -2501,6 +2528,19 @@
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
+Racal-Interlan EISA ES3210 support
+CONFIG_ES3210
+  If you have a network (ethernet) card of this type, say Y and read
+  the Ethernet-HOWTO, available via ftp (user: anonymous) in
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO. This driver is also available
+  as a module ( = code which can be inserted in and removed from the
+  running kernel whenever you want). If you want to compile it as a
+  module, say M here and read Documentation/modules.txt as well as
+  Documentation/networking/net-modules.txt. If you plan to use more
+  than one network card under linux, read the
+  Multiple-Ethernet-mini-HOWTO, available from
+  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+
 Apricot Xen-II on board ethernet
 CONFIG_APRICOT
   If you have a network (ethernet) controller of this type, say Y and
@@ -2660,6 +2700,13 @@
   want). If you want to compile it as a module, say M here and read
   Documentation/modules.txt.
 
+Traffic Shaper (EXPERIMENTAL)
+CONFIG_SHAPER
+  The traffic shaper adds a set of devices that may be attached to the
+  other devices in the kernel and used to limit their bandwidth. The
+  shapecfg program for setting up shaper devices is available from
+  ftp://shadow.cabi.net/....
+
 Support non-SCSI/IDE/ATAPI drives
 CONFIG_CD_NO_IDESCSI
   If you have a CDROM drive that is neither SCSI nor IDE/ATAPI, say Y
@@ -3265,6 +3312,24 @@
   which can be inserted in and removed from the running kernel
   whenever you want), say M here and read Documentation/modules.txt.
 
+Hayes ESP serial port support
+CONFIG_ESP
+  This is a driver which supports Hayes ESP serial ports.  It uses DMA to
+  transfer data to and from the host.  Make sure to read
+  drivers/char/README.esp.  This driver may be compiled as a loadable module.
+
+Hayes ESP serial port DMA channel
+CONFIG_ESP_DMA_CHANNEL
+  This is the DMA channel to be used to transfer data to and from the host.
+  One DMA channel is shared between all the ESP ports.   Valid values are
+  1 and 3.
+
+Hayes ESP serial port trigger level
+CONFIG_ESP_TRIGGER_LEVEL
+  This is the trigger level (in bytes) of the transmit FIFO and the receive
+  FIFO.  Larger values may result in fewer interrupts; however, a value too
+  high could result in data loss.  Valid values are 1 through 1015.
+
 Parallel printer support
 CONFIG_PRINTER
   If you intend to attach a printer to the parallel port of your Linux
@@ -3536,7 +3601,7 @@
   from the running kernel whenever you want). If you want to compile
   it as a module, say M here and read Documentation/modules.txt.
 
-Berkshire Products PC Watchdog card
+Berkshire Products PC Watchdog
 CONFIG_PCWATCHDOG
   This is the driver for the Berkshire Products PC Watchdog card.
   This card simply watches your kernel to make sure it doesn't freeze,
@@ -3548,6 +3613,25 @@
   code which can be inserted in and removed from the running kernel
   whenever you want). If you want to compile it as a module, say M
   here and read Documentation/modules.txt.  Most people will say N.
+
+Support for Revision A cards
+CONFIG_PCWD_REV_A
+  This gives you support for revision A or B of the Berkshire PC Watchdog
+  Card.  The way to tell if you have an A or B style card is to check the
+  manual on page 22.  If it doesn't say "Port 3" at the top, you have a
+  revision A or B card.  Also, if your card doesn't have an audible beep,
+  chances are you have a revision A or B card.
+
+Support for Revision C cards
+CONFIG_PCWD_REV_C
+  This card gives you an audible beep.  It also has support for disabling
+  the card, enabling it, getting firmware versions, and a few other options.
+
+Show card state on reset
+CONFIG_PCWD_SHOW_PREVSTAT
+  Enabling this option will display the previous reset state of your card.
+  It will also give a little more verbose data about the card, or your
+  system's previous status before reset.
 
 Enhanced Real Time Clock Support
 CONFIG_RTC
diff -u --recursive --new-file v2.1.14/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v2.1.14/linux/Documentation/devices.tex	Thu Dec 12 17:02:39 1996
+++ linux/Documentation/devices.tex	Wed Dec 11 16:55:09 1996
@@ -42,7 +42,7 @@
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: November 18, 1996}
+\date{Last revised: December 7, 1996}
 \maketitle
 %
 \noindent
@@ -65,8 +65,8 @@
 appropriate device information.  Also, if you have additional
 information regarding any of the devices listed below, or if I have
 made a mistake, I would greatly appreciate a note.  When sending me
-mail, please include the word ``device'' in the subject so your mail
-won't accidentally get buried!
+mail, {\em please\/} include the word ``device'' in the subject so
+your mail won't accidentally get buried!
 
 Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
 platform only.  Allocations marked (68k/Atari) apply to Linux/68k on
@@ -185,7 +185,9 @@
 \major{58}{}{char }{Hayes ESP serial card -- alternate devices}
 \major{59}{}{char }{sf firewall package}
 \major{60}{--63}{}{Local/experimental use}
-\major{64}{--119}{}{Unallocated}
+\major{64}{}{char }{ENskip kernel encryption package}
+\major{65}{}{char }{Sundance ``plink'' Transputer boards}
+\major{66}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
 \major{128}{--239}{}{Unallocated}
 \major{240}{--254}{}{Local/experimental use}
@@ -333,19 +335,19 @@
 	\minor{64}{/dev/ttyS0}{First serial port}
 	\minordots
 	\minor{127}{/dev/ttyS63}{64th serial port}
-	\minor{128}{/dev/ptyp0}{First pseudo-tty master}
+	\minor{128}{/dev/ptyp0}{First old pseudo-tty master}
 	\minordots
-	\minor{191}{/dev/ptysf}{64th pseudo-tty master}
-	\minor{192}{/dev/ttyp0}{First pseudo-tty slave}
+	\minor{191}{/dev/ptysf}{64th old pseudo-tty master}
+	\minor{192}{/dev/ttyp0}{First old pseudo-tty slave}
 	\minordots
-	\minor{255}{/dev/ttysf}{64th pseudo-tty slave}
+	\minor{255}{/dev/ttysf}{64th old pseudo-tty slave}
 \end{devicelist}
 
 \noindent
 For compatibility with previous versions of Linux, the first 64 PTYs
-are replicated under this device number.  This use will be obsolescent
-with the release of Linux 2.0 and may be removed in a future version
-of Linux.
+are replicated under this device number.  This use is deprecated with
+the release of Linux 2.0 and may be removed in a future version of
+Linux.  To ensure proper operation, do not mix old and new PTY devices.
 
 \begin{devicelist}
 \major{ 5}{}{char }{Alternate TTY devices}
@@ -388,6 +390,11 @@
 	\minordots
 \end{devicelist}
 
+\noindent
+The loopback devices are used to mount filesystems not associated with
+block devices.  The binding to the loopback devices is handled by
+{\bf mount}(8) or {\bf losetup}(8).
+
 \begin{devicelist}
 \major{ 8}{}{block}{SCSI disk devices}
 	\minor{0}{/dev/sda}{First SCSI disk whole disk}
@@ -472,11 +479,6 @@
 	\minor{139}{/dev/openprom}{SPARC OpenBoot PROM}
 \end{devicelist}
 
-\noindent
-The loopback devices are used to mount filesystems not associated with
-block devices.  The binding to the loopback devices is usually handled
-by {\bf mount}(8).
-
 \begin{devicelist}
 \major{11}{}{char }{Raw keyboard device}
 	\minor{0}{/dev/kbd}{Raw keyboard device}
@@ -1162,7 +1164,37 @@
 used, in order to avoid conflict with future assignments.
 
 \begin{devicelist}
-\major{64}{--119}{}{Unallocated}
+\major{64}{}{char }{ENskip kernel encryption package}
+	\minor{0}{/dev/enskip}{Communication with ENskip kernel
+	module}
+\end{devicelist}
+
+\begin{devicelist}
+\major{65}{}{char }{Sundance ``plink'' Transputer boards}
+	\minor{0}{/dev/plink0}{First plink device}
+	\minor{1}{/dev/plink1}{Second plink device}
+	\minor{2}{/dev/plink2}{Third plink device}
+	\minor{3}{/dev/plink3}{Fourth plink device}
+	\minor{64}{/dev/rplink0}{First plink device, raw}
+	\minor{65}{/dev/rplink1}{Second plink device, raw}
+	\minor{66}{/dev/rplink2}{Third plink device, raw}
+	\minor{67}{/dev/rplink3}{Fourth plink device, raw}
+	\minor{128}{/dev/plink0d}{First plink device, debug}
+	\minor{129}{/dev/plink1d}{Second plink device, debug}
+	\minor{130}{/dev/plink2d}{Third plink device, debug}
+	\minor{131}{/dev/plink3d}{Fourth plink device, debug}
+	\minor{192}{/dev/rplink0d}{First plink device, raw, debug}
+	\minor{193}{/dev/rplink1d}{Second plink device, raw, debug}
+	\minor{194}{/dev/rplink2d}{Third plink device, raw, debug}
+	\minor{195}{/dev/rplink3d}{Fourth plink device, raw, debug}
+\end{devicelist}
+
+\noindent
+This is a commercial driver; contact James Howes
+$<$jth@prosig.demon.co.uk$>$ for information. 
+
+\begin{devicelist}
+\major{66}{--119}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
diff -u --recursive --new-file v2.1.14/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v2.1.14/linux/Documentation/devices.txt	Thu Dec 12 17:02:39 1996
+++ linux/Documentation/devices.txt	Wed Dec 11 16:55:09 1996
@@ -2,7 +2,7 @@
 
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		   Last revised: November 18, 1996
+		    Last revised: December 7, 1996
 
 This list is the successor to Rick Miller's Linux Device List, which
 he stopped maintaining when he got busy with other things in 1993.  It
@@ -22,7 +22,7 @@
 appropriate device information.  Also, if you have additional
 information regarding any of the devices listed below, or if I have
 made a mistake, I would greatly appreciate a note.  When sending me
-mail, please include the word "device" in the subject so your mail
+mail, *please* include the word "device" in the subject so your mail
 won't accidentally get buried!
 
 Allocations marked (68k/Amiga) apply to Linux/68k on the Amiga
@@ -160,18 +160,19 @@
 		 64 = /dev/ttyS0	First serial port
 		      ...
 		127 = /dev/ttyS63	64th serial port
-		128 = /dev/ptyp0	First pseudo-tty master
+		128 = /dev/ptyp0	First old pseudo-tty master
 		      ...
-		191 = /dev/ptysf	64th pseudo-tty master
-		192 = /dev/ttyp0	First pseudo-tty slave
+		191 = /dev/ptysf	64th old pseudo-tty master
+		192 = /dev/ttyp0	First old pseudo-tty slave
 		      ...
-		255 = /dev/ttysf	64th pseudo-tty slave
+		255 = /dev/ttysf	64th old pseudo-tty slave
 
 		For compatibility with previous versions of Linux, the
-		first 64 PTYs are replicated under this device
-		number.  This use will be obsolescent with the release
-		of Linux 1.4 and may be removed in a future version of
-		Linux.
+		first 64 PTYs are replicated under this device number.
+		This use is deprecated with the release of Linux 2.0
+		and may be removed in a future version of Linux.  To
+		ensure proper operation, do not mix old and new PTY
+		devices.
 
   5 char	Alternate TTY devices
     		  0 = /dev/tty		Current TTY device
@@ -207,7 +208,7 @@
 
 		The loopback devices are used to mount filesystems not
 		associated with block devices.  The binding to the
-		loopback devices is usually handled by mount(8).
+		loopback devices is handled by mount(8) or losetup(8).
 
   8 block	SCSI disk devices
     		  0 = /dev/sda		First SCSI disk whole disk
@@ -813,7 +814,31 @@
 		assigned official numbers, these ranges should be
 		used, in order to avoid conflicting with future assignments.
 
- 64-119		UNALLOCATED
+ 64 char	ENskip kernel encryption package
+		  0 = /dev/enskip	Communication with ENskip kernel module
+
+ 65 char	Sundance "plink" Transputer boards
+		  0 = /dev/plink0	First plink device
+		  1 = /dev/plink1	Second plink device
+		  2 = /dev/plink2	Third plink device
+		  3 = /dev/plink3	Fourth plink device
+		 64 = /dev/rplink0	First plink device, raw
+		 65 = /dev/rplink1	Second plink device, raw
+		 66 = /dev/rplink2	Third plink device, raw
+		 67 = /dev/rplink3	Fourth plink device, raw
+		128 = /dev/plink0d	First plink device, debug
+		129 = /dev/plink1d	Second plink device, debug
+		130 = /dev/plink2d	Third plink device, debug
+		131 = /dev/plink3d	Fourth plink device, debug
+		192 = /dev/rplink0d	First plink device, raw, debug
+		193 = /dev/rplink1d	Second plink device, raw, debug
+		194 = /dev/rplink2d	Third plink device, raw, debug
+		195 = /dev/rplink3d	Fourth plink device, raw, debug
+
+		This is a commercial driver; contact James Howes
+		<jth@prosig.demon.co.uk> for information.
+
+ 66-119		UNALLOCATED
 
 120-127		LOCAL/EXPERIMENTAL USE
 
@@ -822,6 +847,7 @@
 240-254		LOCAL/EXPERIMENTAL USE
 
 255		RESERVED
+
 
 
 
diff -u --recursive --new-file v2.1.14/linux/Documentation/ide.txt linux/Documentation/ide.txt
--- v2.1.14/linux/Documentation/ide.txt	Sun Nov 10 20:12:07 1996
+++ linux/Documentation/ide.txt	Wed Dec 11 16:45:54 1996
@@ -66,6 +66,7 @@
 	- the hdparm-3.1 package can be used to set PIO modes for some chipsets.
 NEW!	- support for the OPTi 82C621 chipset, courtesy of Jaromir Koutek.
 NEW!	- support for loadable modules
+NEW!	- optional SCSI host adapter emulation for ATAPI devices
 
 
 For work in progress, see the comments in ide.c, ide-cd.c, triton.c, ...
@@ -110,12 +111,12 @@
 Apparently many releases of Slackware 2.2/2.3 have incorrect entries
 in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide
 
-ide.c automatically probes for the primary and secondary interfaces,
+ide.c automatically probes for the standard four IDE interfaces,
 for the drives/geometries attached to those interfaces, and for the
-IRQ numbers being used by the interfaces (normally IRQ14 & IRQ15).
+IRQ numbers being used by the interfaces (normally 14, 15, 11 and 10).
 
-Interfaces beyond the first two are not normally probed for, but may be
-specified using kernel "command line" options.  For example,
+For special cases, interfaces may be specified using kernel "command line"
+options.  For example,
 
 	ide3=0x168,0x36e,10	/* ioports 0x168-0x16f,0x36e, irq 10 */
 
@@ -234,6 +235,25 @@
 under control of ide.c.   To have ide.c also "take over" the primary
 IDE port in this situation, use the "command line" parameter:  ide0=0x1f0
 
+The IDE driver is partly modularized.  The high level disk/cdrom/tape/floppy
+drivers can always be compiled as loadable modules, the chipset drivers
+can only be compiled into the kernel, and the core code (ide.c) can be
+compiled as a loadable module provided no chipset support and no special
+partition table translations are needed.
+
+When using ide.c/ide-tape.c as modules in combination with kerneld, add:
+
+	alias block-major-3 ide-probe
+	alias char-major-37 ide-tape
+
+respectively to /etc/conf.modules.
+
+When ide.c is used as a module, you can pass command line parameters to the
+driver using the "options=" keyword to insmod, while replacing any ',' with
+';'.  For example:
+
+	insmod ide.o options="ide0=serialize ide2=0x1e8;0x3ee;11"
+
 mlord@pobox.com
 snyder@fnald0.fnal.gov
 ================================================================================
@@ -256,6 +276,7 @@
 				older/odd IDE drives.
  "hdx=slow"		: insert a huge pause after each access to the data
 				port. Should be used only as a last resort.
+ "hdx=swapdata"		: when the drive is a disk, byte swap all data
 
  "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in MHz,
 				where "xx" is between 20 and 66 inclusive,
@@ -283,6 +304,7 @@
 				This is the default for most chipsets,
 				except the cmd640.
  "idex=serialize"	: do not overlap operations on idex and ide(x^1)
+ "idex=reset"		: reset interface after probe
 
  The following are valid ONLY on ide0,
  and the defaults for the base,ctl ports must not be altered.
diff -u --recursive --new-file v2.1.14/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- v2.1.14/linux/Documentation/ioctl-number.txt	Wed Oct  9 08:55:16 1996
+++ linux/Documentation/ioctl-number.txt	Wed Dec 11 16:06:42 1996
@@ -1,7 +1,7 @@
 Ioctl Numbers
-5 Oct 1996
+2 Dec 1996
 Michael Chastain
-<mec@duracef.shout.net>
+<mec@shout.net>
 
 If you are adding new ioctl's to the kernel, you should use the _IO
 macros defined in <linux/ioctl.h>:
@@ -51,7 +51,7 @@
     code to call verify_area to validate parameters.
 
 This table lists ioctls visible from user land for Linux/i386.  It is
-current to Linux 2.1.1
+current to Linux 2.1.14.
 
 Code	Seq#	Include File		Comments
 ========================================================
@@ -64,10 +64,8 @@
 0x09	all	linux/md.h
 0x12	all	linux/fs.h
 0x20	all	linux/cm206.h
-0x22	all	linux/scc.h		conflict! (version 2.01 of z8530drv)
-0x22	all	scsi/sg.h		conflict!
+0x22	all	scsi/sg.h
 'A'	all	linux/apm_bios.h
-'B'	all	linux/baycom.h
 'C'	all	linux/soundcard.h
 'F'	all	linux/fb.h
 'I'	all	linux/isdn.h
@@ -86,7 +84,6 @@
 'V'	all	linux/vt.h
 'W'	00-1F	linux/pcwd.h
 'Y'	all	linux/cyclades.h
-'Z'	all	linux/scc.h		version 2.2 of z8530drv
 'a'	all	various, see http://lrcwww.epfl.ch/linux-atm/magic.html
 'c'	all	linux/comstats.h
 'f'	all	linux/ext2_fs.h
@@ -102,7 +99,9 @@
 'v'	all	linux/ext2_fs.h
 'w'	all	CERN SCI driver (in development)
 0x89	00-0F	asm-i386/sockios.h
-0x89	10-FF	linux/sockios.h
+0x89	10-DF	linux/sockios.h
+0x89	E0-EF	linux/sockios.h		SIOCPROTOPRIVATE range
+0x89	F0-FF	linux/sockios.h		SIOCDEVPRIVATE range
 0x90	00	linux/sbpcd.h
 0x99	00-0F	537-Addinboard driver (in development, e-mail contact:
 					b.kohl@ipn-b.comlink.apc.org)
diff -u --recursive --new-file v2.1.14/linux/Documentation/mca.txt linux/Documentation/mca.txt
--- v2.1.14/linux/Documentation/mca.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/mca.txt	Thu Dec 12 16:51:07 1996
@@ -0,0 +1,210 @@
+i386 Micro Channel Architecture Support
+=======================================
+
+MCA support is enabled using the CONFIG_MCA define.  A machine with a MCA
+bus will have the kernel variable MCA_bus set, assuming the BIOS feature
+bits are set properly (see arch/i386/boot/setup.S for information on
+how this detection is done).
+
+Adapter Detection
+=================
+
+The ideal MCA adapter detection is done through the use of the
+Programmable Option Select registers.  Generic functions for doing
+this have been added in include/linux/mca.h and arch/i386/kernel/mca.c.
+Everything needed to detect adapters and read (and write) configuration
+information is there.  A number of MCA-specific drivers already use
+this.  The typical probe code looks like the following:
+
+	#include <linux/mca.h>
+
+	unsigned char pos2, pos3, pos4, pos5;
+	struct device* dev;
+	int slot;
+
+	if( MCA_bus ) {
+		slot = mca_find_adapter( ADAPTER_ID, 0 );
+		if( slot == MCA_NOTFOUND ) {
+			return ENODEV;
+		}
+		/* optional - see below */
+		mca_set_adapter_name( slot, "adapter name & description" );
+		mca_set_adapter_procfn( slot, dev_getinfo, dev );
+
+		/* read the POS registers.  Most devices only need
+		2 and 3 */
+		pos2 = mca_read_stored_pos( slot, 2 );
+		pos3 = mca_read_stored_pos( slot, 3 );
+		pos4 = mca_read_stored_pos( slot, 4 );
+		pos5 = mca_read_stored_pos( slot, 5 );
+	} else {
+		return ENODEV;
+	}
+
+	/* extract configuration from pos[2345] and set everything up */
+
+Loadable modules should modify this to test that the specified IRQ and
+IO ports (plus whatever other stuff) match.  See 3c523.c for example
+code.
+
+Keep in mind that devices should never directly access the POS registers
+(via inb(), outb(), etc).  While it's generally safe, there is a small
+potential for blowing up hardware when it's done at the wrong time.
+Furthermore, accessing a POS register disables a device temporarily.
+This is usually okay during startup, but do _you_ want to rely on it?
+During initial configuration, mca_init() reads all the POS registers
+into memory.  mca_read_stored_pos() accesses that data.  mca_read_pos()
+and mca_write_pos() are also available for (safer) direct POS access,
+but their use is _highly_ discouraged.  mca_write_pos() is particularly
+dangerous, as it is possible for adapters to be put in inconsistent
+states (i.e. sharing IO address, etc) and may result in crashes, toasted
+hardware, and operator injury.
+
+User level drivers (such as the AGX X server) can use /proc/mca to find
+adapters (see below).
+
+Some MCA adapters can also be detected via the usual ISA-style device
+probing (many SCSI adapters, for example).  This sort of thing is highly
+discouraged.  Perfectly good information is available telling you what's
+there, so there's no excuse for messing with random IO ports.  However,
+we MCA people still appreciate any ISA-style driver that will work with
+our hardware.  You take what you can get...
+
+Level-Triggered Interrupts
+==========================
+
+Because MCA uses level-triggered interrupts, a few problems arise with
+what might best be described as the ISA mindset and its effects on
+drivers.  These sorts of problems are expected to become less common as
+more people use shared IRQs on PCI machines.
+
+In general, an interrupt must be acknowledged not only at the ICU (which
+is done automagically by the kernel), but at the device level.  In
+particular, IRQ 0 must be reset after a timer interrupt (now done in
+arch/i386/kernel/time.c) or the first timer interrupt hangs the system.
+There were also problems with the 1.3.x floppy drivers, but that seems
+to have been fixed.
+
+IRQs are also shareable, and most MCA-specific devices should be coded
+with shared IRQs in mind.
+
+/proc/mca
+=========
+
+I did a major rewrite of /proc/mca.  It is now a directory containing
+various files for adapters and other stuff.
+
+	/proc/mca/pos		Straight listing of POS registers
+	/proc/mca/slot[1-8]	Information on adapter in specific slot
+	/proc/mca/video		Same for integrated video
+	/proc/mca/scsi		Same for integrated SCSI
+	/proc/mca/machine	Machine information
+
+Device drivers can easily add their own information function for
+specific slots (including integrated ones) via the
+mca_set_adapter_procfn() call.  Drivers that support this are ESDI, IBM
+SCSI, and 3c523.  If a device is also a module, make sure that the proc
+function is removed in the module cleanup.  This will require storing
+the slot information in a private structure somewhere.  See the 3c523
+driver for details.
+
+Your typical proc function will look something like this:
+
+	static int
+	dev_getinfo( char* buf, int slot, void* d ) {
+		struct device* dev = (struct device*) d;
+		int len = 0;
+
+		len += sprintf( buf+len, "Device: %s\n", dev->name );
+		len += sprintf( buf+len, "IRQ: %d\n", dev->irq );
+		len += sprintf( buf+len, "IO Port: %#lx-%#lx\n", ... );
+		...
+
+		return len;
+	}
+
+Some of the standard MCA information will already be printed, so don't
+bother repeating it.  Don't try putting in more that 3K of information.
+
+Enable this function with:
+	mca_set_adapter_procfn( slot, dev_getinfo, dev );
+
+Disable it with:
+	mca_set_adapter_procfn( slot, NULL, NULL );
+
+It is also recommended that, even if you don't write a proc function, to
+set the name of the adapter (i.e. "PS/2 ESDI Controller") via
+mca_set_adapter_name( int slot, char* name ).  Up to 30 characters are
+used.
+
+MCA Device Drivers
+==================
+
+Currently, there are a number of MCA-specific device drivers.
+
+1) PS/2 ESDI
+	drivers/block/ps2esdi.c
+	include/linux/ps2esdi.h
+   Uses major number 36, and should use /dev files /dev/eda, /dev/edb.
+   Supports two drives, but only one controller.  Usually requires the
+   command-line args ed=cyl,head,sec
+
+2) PS/2 SCSI
+	drivers/scsi/ibmmca.c
+	drivers/scsi/ibmmca.h
+   The driver for the IBM SCSI subsystem.  Includes both integrated
+   controllers and adapter cards.  May require command-line arg
+   ibmmcascsi=pun to force detection of an adapter.
+
+3) 3c523
+	drivers/net/3c523.c
+	drivers/net/3c523.h
+   3Com 3c523 Etherlink/MC ethernet driver.
+
+4) SMC Ultra/MCA
+	drivers/net/smc-mca.c
+	drivers/net/smc-mca.h
+	Elite/A (8013EP/A) and Elite10T/A (8013WP/A) ethernet driver
+
+As well, drivers/char/psaux.c was modified to support IRQ sharing (it's
+#ifdef CONFIG_MCA'ed, for your convenience, although PCI users might be
+able to use it...)
+
+The serial drivers were modified to support the extended IO port range
+of the typical MCA system (also #ifdef CONFIG_MCA).
+
+The following devices work with existing drivers:
+1) Token-ring
+2) Future Domain SCSI (MCS-600, MCS-700, not MCS-350)
+3) Adaptec 1640 SCSI (aha1542 driver)
+4) Buslogic SCSI (various)
+
+Bugs & Other Weirdness
+======================
+
+NMIs tend to occur with MCA machines because of various hardware
+weirdness, bus timeouts, and many other non-critical things.  Those of
+you who have NMI problems should probably set the CONFIG_IGNORE_NMI flag
+somewhere.  NMIs seem to be particularly common on the model 70.
+
+Various Pentium machines have serious problems with the FPU test in
+bugs.h.  You may need to comment out the FPU test before you can even
+boot.  This occurs, as far as we know, on the Pentium-equipped 85s, 95s,
+and some servers.  The PCI/MCA PC 750s are fine as far as I can tell.
+
+The model 80 has a raft of problems that are just too weird and unique
+to get into here.  Some people have no trouble while others have nothing
+but problems.  I'd suspect the problems are related to the age of the
+average 80 and accompanying hardware deterioration.
+
+Credits
+=======
+A whole pile of people have contributed to the MCA code.  I'd include
+their names here, but I don't have a list handy.  Check the MCA Linux
+home page (URL below) for an out-of-date list.
+
+=====================================================================
+http://glycerine.cetmm.uni.edu/mca
+
+Chris Beauregard
+chrisb@truespectra.com
diff -u --recursive --new-file v2.1.14/linux/Documentation/networking/00-INDEX linux/Documentation/networking/00-INDEX
--- v2.1.14/linux/Documentation/networking/00-INDEX	Thu Jun  6 14:57:43 1996
+++ linux/Documentation/networking/00-INDEX	Thu Dec 12 16:51:07 1996
@@ -20,6 +20,8 @@
 	- info and "insmod" parameters for all network driver modules.
 ppp.txt
 	- info on what software you should use to run PPP.
+shaper.txt
+	- info on the module that can shape/limit transmitted traffic.
 tcp.txt
 	- short blurb on how TCP output takes place.
 tulip.txt
diff -u --recursive --new-file v2.1.14/linux/Documentation/networking/shaper.txt linux/Documentation/networking/shaper.txt
--- v2.1.14/linux/Documentation/networking/shaper.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/shaper.txt	Thu Dec 12 16:51:07 1996
@@ -0,0 +1,49 @@
+Traffic Shaper For Linux
+
+This is the current ALPHA release of the traffic shaper for Linux. It works
+within the following limits:
+
+o	Minimum shaping speed is currently about 9600 baud (it can only
+shape down to 1 byte per clock tick)
+
+o	Maximum is about 256K, it will go above this but get a bit blocky.
+
+o	If you ifconfig the master device that a shaper is attached to down
+then your machine will follow.
+
+o	The shaper must be a module.
+
+
+Setup:
+
+	A shaper device is configured using the shapeconfig program.
+Typically you will do something like this
+
+shapecfg attach shaper0 eth1
+shapecfg speed shaper0 64000
+ifconfig shaper0 myhost netmask 255.255.255.240 broadcast 1.2.3.4.255 up
+route add -net some.network netmask a.b.c.d dev shaper0
+
+The shaper should have the same IP address as the device it is attached to
+for normal use.
+
+Gotchas:
+
+	The shaper shapes transmitted traffic. Its rather impossible to
+shape received traffic except at the end (or a router) transmiting it.
+
+	Gated/routed/rwhod/mrouted all see the shaper as an additional device
+and will treat it as such unless patched. Note that for mrouted you can run
+mrouted tunnels via a traffic shaper to control bandwidth usage.
+
+	The shaper is device/route based. This makes it very easy to use
+with any setup BUT less flexible. You may well want to combine this patch
+with Mike McLagan <mmclagan@linux.org>'s patch to allow routes to be
+specified by source/destination pairs.
+
+	There is no "borrowing" or "sharing" scheme. This is a simple
+traffic limiter. I'd like to implement Van Jacobson and Sally Floyd's CBQ
+architecture into Linux one day (maybe in 2.1 sometime) and do this with
+style.
+
+Alan
diff -u --recursive --new-file v2.1.14/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.14/linux/MAINTAINERS	Fri Nov 22 18:28:12 1996
+++ linux/MAINTAINERS	Thu Dec 12 16:51:07 1996
@@ -43,11 +43,7 @@
 
 7.	Happy hacking.
 
-
-[This file is new: I've just put the existing network contacts in, other
- people please add yourselves] -- AC
-
-		-----------------------------------
+ 		-----------------------------------
 
 Maintainers List (try to look for most precise areas first)
 
@@ -113,8 +109,8 @@
 
 APPLETALK NETWORK LAYER
 P:	Alan Cox & University Of Michigan
-M:	net-patches@lxorguk.ukuu.org.uk, Cc: netatalk@umich.edu
-L:	[Someone fill in the netatalk list here]
+M:	net-patches@lxorguk.ukuu.org.uk
+L:	netatalk@umich.edu
 S:	Maintained
 
 AX.25 NETWORK LAYER
@@ -190,7 +186,7 @@
 S:	Odd fixes (e.g., new signatures)
 
 SCSI TAPE DRIVER
-P:	Kai Mäkisara
+P:	Kai Mdkisara
 M:	Kai.Makisara@metla.fi
 L:	linux-scsi@vger.rutgers.edu
 S:	Maintained
@@ -254,7 +250,8 @@
 P:	Alan Cox
 M:	net-patches@lxorguk.ukuu.org.uk
 L:	linux-net@vger.rutgers.edu
-S:	Odd Fixes <-> Maintained subject to workloads
+W:	http://www.uk.linux.org/NetNews.html
+S:	Maintained
 
 PPP PROTOCOL DRIVERS AND COMPRESSORS
 P:	Al Longyear
diff -u --recursive --new-file v2.1.14/linux/Makefile linux/Makefile
--- v2.1.14/linux/Makefile	Thu Dec 12 17:02:39 1996
+++ linux/Makefile	Mon Dec  2 14:03:21 1996
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 14
+SUBLEVEL = 15
 
 ARCH = i386
 
diff -u --recursive --new-file v2.1.14/linux/README linux/README
--- v2.1.14/linux/README	Sat Oct  5 16:58:33 1996
+++ linux/README	Thu Dec 12 16:51:08 1996
@@ -19,6 +19,9 @@
 of the message "subscribe linux-kernel" or "subscribe linux-kernel-digest"
 for a daily digest of the mailing list (it is a high-traffic list.)
 
+However, please make sure you don't ask questions which are already answered
+in various files in the Documentation directory.  See DOCUMENTATION below.
+
 WHAT IS LINUX?
 
   Linux is a Unix clone written from scratch by Linus Torvalds with
@@ -51,7 +54,9 @@
  - There are various readme's in the kernel Documentation/ subdirectory:
    these typically contain kernel-specific installation notes for some 
    drivers for example. See ./Documentation/00-INDEX for a list of what
-   is contained in each file.
+   is contained in each file.  Please read the Changes file, as it
+   contains information about the problems, which may result by upgrading
+   your kernel.
 
 INSTALLING the kernel:
 
diff -u --recursive --new-file v2.1.14/linux/arch/alpha/lib/Makefile linux/arch/alpha/lib/Makefile
--- v2.1.14/linux/arch/alpha/lib/Makefile	Thu Dec 12 17:02:40 1996
+++ linux/arch/alpha/lib/Makefile	Sun Dec  1 21:27:21 1996
@@ -8,10 +8,10 @@
 	strchr.o strrchr.o \
 	copy_user.o clear_user.o strncpy_from_user.o strlen_user.o
 
-ifeq($(CONFIG_IPV6),y)
+ifeq ($(CONFIG_IPV6),y)
   OBJS += csum_ipv6_magic.o
 else
-  ifeq($(CONFIG_IPV6),m)
+  ifeq ($(CONFIG_IPV6),m)
     OBJS += csum_ipv6_magic.o
   endif
 endif
diff -u --recursive --new-file v2.1.14/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.14/linux/arch/i386/config.in	Tue Nov 12 15:56:02 1996
+++ linux/arch/i386/config.in	Thu Dec 12 16:51:08 1996
@@ -30,6 +30,7 @@
     bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
   fi
 fi
+bool 'MCA support' CONFIG_MCA
 bool 'System V IPC' CONFIG_SYSVIPC
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
diff -u --recursive --new-file v2.1.14/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.14/linux/arch/i386/defconfig	Tue Nov 19 15:53:53 1996
+++ linux/arch/i386/defconfig	Thu Dec 12 17:04:43 1996
@@ -21,6 +21,7 @@
 CONFIG_NET=y
 # CONFIG_MAX_16M is not set
 CONFIG_PCI=y
+# CONFIG_MCA is not set
 CONFIG_SYSVIPC=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
@@ -44,6 +45,7 @@
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
 CONFIG_BLK_DEV_CMD640=y
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_RZ1000=y
@@ -75,7 +77,7 @@
 #
 # CONFIG_INET_PCTCP is not set
 # CONFIG_INET_RARP is not set
-# CONFIG_NO_PATH_MTU_DISCOVERY is not set
+CONFIG_PATH_MTU_DISCOVERY=y
 CONFIG_IP_NOSR=y
 CONFIG_SKB_LARGE=y
 
@@ -110,6 +112,7 @@
 # CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
 # CONFIG_FDDI is not set
+# CONFIG_DLCI is not set
 # CONFIG_PLIP is not set
 # CONFIG_PPP is not set
 # CONFIG_NET_RADIO is not set
@@ -156,6 +159,7 @@
 # CONFIG_CYCLADES is not set
 # CONFIG_STALDRV is not set
 # CONFIG_RISCOM8 is not set
+# CONFIG_ESP is not set
 # CONFIG_PRINTER is not set
 # CONFIG_MOUSE is not set
 # CONFIG_UMISC is not set
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
--- v2.1.14/linux/arch/i386/kernel/Makefile	Mon Sep  9 13:55:58 1996
+++ linux/arch/i386/kernel/Makefile	Thu Dec 12 16:51:08 1996
@@ -24,6 +24,10 @@
 O_OBJS   := process.o signal.o entry.o traps.o irq.o vm86.o bios32.o \
             ptrace.o ioport.o ldt.o setup.o time.o sys_i386.o ksyms.o
 
+ifdef CONFIG_MCA
+O_OBJS += mca.o
+endif
+
 ifdef SMP
 
 O_OBJS += smp.o
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- v2.1.14/linux/arch/i386/kernel/entry.S	Tue Nov 12 15:56:02 1996
+++ linux/arch/i386/kernel/entry.S	Thu Dec 12 16:51:08 1996
@@ -280,6 +280,9 @@
 	ALIGN
 	.globl ret_from_sys_call
 ret_from_sys_call:
+#ifdef __SMP__
+	GET_CURRENT
+#endif
 	cmpl $0,SYMBOL_NAME(intr_count)
 	jne 1f
 9:	movl SYMBOL_NAME(bh_mask),%eax
@@ -572,7 +575,7 @@
 	.long SYMBOL_NAME(sys_iopl)		/* 110 */
 	.long SYMBOL_NAME(sys_vhangup)
 	.long SYMBOL_NAME(sys_idle)
-	.long SYMBOL_NAME(sys_vm86)
+	.long SYMBOL_NAME(sys_vm86old)
 	.long SYMBOL_NAME(sys_wait4)
 	.long SYMBOL_NAME(sys_swapoff)		/* 115 */
 	.long SYMBOL_NAME(sys_sysinfo)
@@ -625,6 +628,7 @@
 	.long SYMBOL_NAME(sys_mremap)
 	.long SYMBOL_NAME(sys_setresuid)
 	.long SYMBOL_NAME(sys_getresuid)
-	.rept NR_syscalls-165
+	.long SYMBOL_NAME(sys_vm86)
+	.rept NR_syscalls-166
 		.long SYMBOL_NAME(sys_ni_syscall)
 	.endr
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.1.14/linux/arch/i386/kernel/head.S	Mon Sep 23 09:33:40 1996
+++ linux/arch/i386/kernel/head.S	Thu Dec 12 16:51:08 1996
@@ -34,9 +34,28 @@
 	mov %ax,%es
 	mov %ax,%fs
 	mov %ax,%gs
+#ifdef __SMP__
+	orw %bx,%bx
+	jz 1f
+/*
+ *	New page tables may be in 4Mbyte page mode
+ */
+#ifdef GAS_KNOWS_CR4
+	movl %cr4,%eax		# Turn on 4Mb pages
+	orl $16,%eax
+	movl %eax,%cr4
+#else
+	.byte 0x0f,0x20,0xe0
+	orl $16,%eax
+	.byte 0x0f,0x22,0xe0
+#endif
+	movl %eax,%cr3		/* flush TLB as per app note */
+	movl %cr0,%eax
+#endif
 /*
  * Setup paging (the tables are already set up, just switch them on)
  */
+1:
 	movl $0x101000,%eax
 	movl %eax,%cr3		/* set the page table pointer.. */
 	movl %cr0,%eax
@@ -54,10 +73,12 @@
 /*
  *	Set up the stack
  */
+        movl $(KERNEL_DS),%eax         /* walken modif */
 	mov %ax,%ss
 	xorl %eax,%eax
 	movw %cx, %ax
 	movl %eax,%esp
+	addl $0xC0000000, %esp		/* shift it to the upper mapping */
 	pushl $0
 	popfl
 	jmp checkCPUtype
@@ -151,6 +172,8 @@
 	popfl
 	incl SYMBOL_NAME(have_cpuid)	# we have CPUID
 	/* get processor type */
+				# LINUS WE HAVE A BUG HERE - MUST CHECK WITH
+				# CPUID#0 THAT CPUID#1 IS SUPPORTED...
 	movl $1, %eax		# Use the CPUID instruction to 
 	.byte 0x0f, 0xa2	# check the processor type
 	movb %al, %cl		# save reg for future use
@@ -187,11 +210,11 @@
 2:	movl %eax,%cr0
 	call check_x87
 #ifdef __SMP__
-	movb ready,%eax
-	orb %eax,%eax
-	jz 3f	
+	movb ready,%al		# First CPU if 0
+	orb %al,%al
+	jz 4f			# First CPU skip this stuff
 #ifdef GAS_KNOWS_CR4
-	movl %cr4,%eax
+	movl %cr4,%eax		# Turn on 4Mb pages
 	orl $16,%eax
 	movl %eax,%cr4
 #else
@@ -199,13 +222,14 @@
 	orl $16,%eax
 	.byte 0x0f,0x22,0xe0
 #endif
-	jmp 4f
+	movl %cr3, %eax		# Intel specification clarification says
+	movl %eax, %cr3		# to do this. Maybe it makes a difference.
+				# Who knows ?
 #endif
-3:
+4:
 #ifdef __SMP__
 	incb ready
 #endif
-4:
 	lgdt gdt_descr
 	lidt idt_descr
 	ljmp $(KERNEL_CS),$1f
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/ksyms.c linux/arch/i386/kernel/ksyms.c
--- v2.1.14/linux/arch/i386/kernel/ksyms.c	Tue Nov 19 15:53:53 1996
+++ linux/arch/i386/kernel/ksyms.c	Thu Dec 12 16:51:08 1996
@@ -1,7 +1,9 @@
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/smp.h>
 #include <linux/user.h>
 #include <linux/elfcore.h>
+#include <linux/mca.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
@@ -31,6 +33,17 @@
 	X(kernel_counter),
 	X(active_kernel_processor),
 	X(smp_invalidate_needed),
+#endif
+#ifdef CONFIG_MCA
+	/* Adapter probing and info methods. */
+	X(mca_write_pos),
+	X(mca_read_pos),
+	X(mca_read_stored_pos),
+	X(mca_set_adapter_name),
+	X(mca_get_adapter_name),
+	X(mca_set_adapter_procfn),
+	X(mca_isenabled),
+	X(mca_isadapter),
 #endif
 #include <linux/symtab_end.h>
 };
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/ldt.c linux/arch/i386/kernel/ldt.c
--- v2.1.14/linux/arch/i386/kernel/ldt.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/i386/kernel/ldt.c	Wed Dec 11 16:41:23 1996
@@ -61,7 +61,7 @@
 	return (last >= first && last < TASK_SIZE);
 }
 
-static int write_ldt(void * ptr, unsigned long bytecount)
+static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
 {
 	struct modify_ldt_ldt_s ldt_info;
 	unsigned long *lp;
@@ -75,10 +75,10 @@
 
 	copy_from_user(&ldt_info, ptr, sizeof(ldt_info));
 
-	if (ldt_info.contents == 3 || ldt_info.entry_number >= LDT_ENTRIES)
+	if ((ldt_info.contents == 3 && (oldmode || ldt_info.seg_not_present == 0)) || ldt_info.entry_number >= LDT_ENTRIES)
 		return -EINVAL;
 
-	if (!limits_ok(&ldt_info))
+	if (!limits_ok(&ldt_info) && (oldmode || ldt_info.seg_not_present == 0))
 		return -EINVAL;
 
 	if (!current->ldt) {
@@ -95,7 +95,14 @@
 	
 	lp = (unsigned long *) &current->ldt[ldt_info.entry_number];
    	/* Allow LDTs to be cleared by the user. */
-   	if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+   	if (ldt_info.base_addr == 0 && ldt_info.limit == 0
+		&& (oldmode ||
+			(  ldt_info.contents == 0
+			&& ldt_info.read_exec_only == 1
+			&& ldt_info.seg_32bit == 0
+			&& ldt_info.limit_in_pages == 0
+			&& ldt_info.seg_not_present == 1
+			&& ldt_info.useable == 0 )) ) {
 		*lp = 0;
 		*(lp+1) = 0;
 		return 0;
@@ -111,6 +118,7 @@
 		  (ldt_info.limit_in_pages << 23) |
 		  ((ldt_info.seg_not_present ^1) << 15) |
 		  0x7000;
+	if (!oldmode) *(lp+1) |= (ldt_info.useable << 20);
 	return 0;
 }
 
@@ -119,6 +127,8 @@
 	if (func == 0)
 		return read_ldt(ptr, bytecount);
 	if (func == 1)
-		return write_ldt(ptr, bytecount);
+		return write_ldt(ptr, bytecount, 1);
+	if (func == 0x11)
+		return write_ldt(ptr, bytecount, 0);
 	return -ENOSYS;
 }
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/mca.c linux/arch/i386/kernel/mca.c
--- v2.1.14/linux/arch/i386/kernel/mca.c	Thu Jan  1 02:00:00 1970
+++ linux/arch/i386/kernel/mca.c	Thu Dec 12 16:51:08 1996
@@ -0,0 +1,621 @@
+/*
+ *  linux/arch/i386/kernel/mca.c
+ *  Written by Martin Kolinek, February 1996
+ *
+ * Changes:
+ *   July 28, 1996: fixed up integrated SCSI detection. Chris Beauregard
+ *   August 3rd, 1996: made mca_info local, made integrated registers
+ *     accessible through standard function calls, added name field,
+ *     more sanity checking. Chris Beauregard
+ *   August 9, 1996: Rewrote /proc/mca.  cpbeaure
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/mca.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/proc_fs.h>
+#include <linux/mman.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/ioport.h>
+#include <asm/uaccess.h>
+
+/* This structure holds MCA information. Each (plug-in) adapter has 
+ * eight POS registers. Then the machine may have integrated video and
+ * SCSI subsystems, which also have eight POS registers.
+ * Other miscellaneous information follows.
+*/
+struct MCA_adapter {
+	unsigned char pos[8];	/* POS registers */
+	char name[32];		/* name of the device - provided by driver */
+	char procname[8];	/* name of /proc/mca file */
+	MCA_ProcFn procfn;	/* /proc info callback */
+	void* dev;		/* device/context info for callback */
+};
+
+struct MCA_info {
+	/* one for each of the 8 possible slots, plus one for integrated SCSI
+	and one for integrated video. */
+	struct MCA_adapter slot[MCA_NUMADAPTERS];
+};
+
+/* The mca_info structure pointer. If MCA bus is present, the function
+ * mca_probe() is invoked. The function puts motherboard, then all
+ * adapters into setup mode, allocates and fills an MCA_info structure,
+ * and points this pointer to the structure. Otherwise the pointer 
+ * is set to zero.
+*/
+static struct MCA_info* mca_info = 0;
+
+/*MCA registers*/
+#define MCA_MOTHERBOARD_SETUP_REG  0x94
+#define MCA_ADAPTER_SETUP_REG      0x96
+#define MCA_POS_REG(n)             (0x100+(n))
+
+#define MCA_ENABLED	0x01	/* POS 2, set if adapter enabled */
+
+/*--------------------------------------------------------------------*/
+
+#ifdef CONFIG_PROC_FS
+static long mca_do_proc_init( long memory_start, long memory_end );
+static int mca_default_procfn( char* buf, int slot );
+
+static long proc_mca_read( struct inode*, struct file*, char* buf, unsigned long count );
+static struct file_operations proc_mca_operations = {
+	NULL, proc_mca_read,
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+static struct inode_operations proc_mca_inode_operations = {
+	&proc_mca_operations,
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+/*--------------------------------------------------------------------*/
+
+long mca_init(long memory_start, long memory_end)
+{
+	unsigned int  i, j;
+	int foundscsi = 0;
+
+	/* WARNING: Be careful when making changes here. Putting an adapter
+	 * and the motherboard simultaneously into setup mode may result in 
+	 * damage to chips (according to The Indispensible PC Hardware Book 
+	 * by Hans-Peter Messmer). Also, we disable system interrupts (so  	
+	 * that we are not disturbed in the middle of this).
+	*/
+
+	/*
+	 *	Make sure the MCA bus is present
+	 */
+	
+	if (!MCA_bus)
+		return memory_start;
+	cli();
+
+	/*
+	 *	Allocate MCA_info structure (at address divisible by 8)
+	 */ 
+
+	if( ((memory_start+7)&(~7)) > memory_end ) 
+	{
+	  	/* uh oh */
+	  	return memory_start;
+	}
+
+	mca_info = (struct MCA_info*) ((memory_start+7)&(~7));
+	memory_start = ((long)mca_info) + sizeof(struct MCA_info);
+
+	/*
+	 *	Make sure adapter setup is off
+	 */
+
+	outb_p(0, MCA_ADAPTER_SETUP_REG);
+
+	/*
+	 *	Put motherboard into video setup mode, read integrated video 
+	 * pos registers, and turn motherboard setup off.
+	 */
+
+	outb_p(0xdf, MCA_MOTHERBOARD_SETUP_REG);
+	mca_info->slot[MCA_INTEGVIDEO].name[0] = 0;
+	for (j=0; j<8; j++) {
+		mca_info->slot[MCA_INTEGVIDEO].pos[j] = inb_p(MCA_POS_REG(j)); 
+	}
+
+	/* Put motherboard into scsi setup mode, read integrated scsi
+	 * pos registers, and turn motherboard setup off.
+	 *
+	 * It seems there are two possible SCSI registers.  Martin says that
+	 * for the 56,57, 0xf7 is the one, but fails on the 76.
+	 * Alfredo (apena@vnet.ibm.com) says
+	 * 0xfd works on his machine.  We'll try both of them.  I figure it's
+	 * a good bet that only one could be valid at a time.  This could
+	 * screw up though if one is used for something else on the other
+	 * machine.
+	*/
+
+	outb_p(0xf7, MCA_MOTHERBOARD_SETUP_REG);
+	mca_info->slot[MCA_INTEGSCSI].name[0] = 0;
+	for (j=0; j<8; j++)  {
+		if( (mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j))) != 0xff ) 
+		{
+	  		/* 0xff all across means no device.  0x00 means something's
+	  		broken, but a device is probably there.  However, if you get
+	  		0x00 from a motherboard register it won't matter what we
+	  		find.  For the record, on the 57SLC, the integrated SCSI
+	  		adapter has 0xffff for the adapter ID, but nonzero for
+	  		other registers.  */
+			foundscsi = 1;
+		}
+	}
+	if( !foundscsi ) 
+	{
+		/* 
+		 *	Didn't find it at 0xfd, try somewhere else... 
+		 */
+		outb_p(0xfd, MCA_MOTHERBOARD_SETUP_REG);
+		for (j=0; j<8; j++) 
+			mca_info->slot[MCA_INTEGSCSI].pos[j] = inb_p(MCA_POS_REG(j)); 
+	}
+
+	/* turn off motherboard setup */
+	outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
+
+	/*
+	 *	Now loop over MCA slots: put each adapter into setup mode, and
+	 *	read its pos registers. Then put adapter setup off.
+	 */
+
+	for (i=0; i<MCA_MAX_SLOT_NR; i++) {
+		outb_p(0x8|(i&0xf), MCA_ADAPTER_SETUP_REG);
+		for (j=0; j<8; j++)  mca_info->slot[i].pos[j]=inb_p(MCA_POS_REG(j)); 
+		mca_info->slot[i].name[0] = 0;
+	}
+	outb_p(0, MCA_ADAPTER_SETUP_REG);
+
+	/*
+	 *	Enable interrupts and return memory start
+	 */
+	sti();
+
+	request_region(0x60,0x01,"system control port B (MCA)");
+	request_region(0x90,0x01,"arbitration (MCA)");
+	request_region(0x91,0x01,"card Select Feedback (MCA)");
+	request_region(0x92,0x01,"system Control port A (MCA)");
+	request_region(0x94,0x01,"system board setup (MCA)");
+	request_region(0x96,0x02,"POS (MCA)");
+	request_region(0x100,0x08,"POS (MCA)");
+
+#ifdef CONFIG_PROC_FS
+	memory_start = mca_do_proc_init( memory_start, memory_end );
+#endif
+
+	return memory_start;
+}
+
+/*--------------------------------------------------------------------*/
+
+int mca_find_adapter( int id, int start ) 
+{
+	int slot_id = 0;
+	unsigned char status = 0;
+
+	if( mca_info == 0 || id == 0 || id == 0xffff ) {
+		return MCA_NOTFOUND;
+	}
+
+	for( ; start >= 0 && start < MCA_NUMADAPTERS; start += 1 ) {
+		slot_id = (mca_info->slot[start].pos[1] << 8)
+			+ mca_info->slot[start].pos[0];
+		status = mca_info->slot[start].pos[2];
+
+		/* not sure about this.  There's no point in returning
+		adapters that aren't enabled, since they can't actually
+		be used.  However, they might be needed for statistical
+		purposes or something... */
+		if( !(status & MCA_ENABLED) ) {
+			continue;
+		}
+
+		if( id == slot_id ) {
+			return start;
+		}
+	}
+
+	return MCA_NOTFOUND;
+} /* mca_find_adapter() */
+
+/*--------------------------------------------------------------------*/
+
+unsigned char mca_read_stored_pos( int slot, int reg ) 
+{
+	if( slot < 0 || slot >= MCA_NUMADAPTERS || mca_info == 0 ) return 0;
+	if( reg < 0 || reg >= 8 ) return 0;
+	return mca_info->slot[slot].pos[reg];
+} /* mca_read_stored_pos() */
+
+/*--------------------------------------------------------------------*/
+
+unsigned char mca_read_pos( int slot, int reg ) 
+{
+	unsigned int byte = 0;
+
+	if( slot < 0 || slot >= MCA_MAX_SLOT_NR || mca_info == 0 ) return 0;
+	if( reg < 0 || reg >= 8 ) return 0;
+
+	cli();
+
+	/*make sure motherboard setup is off*/
+	outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
+
+	/* read in the appropriate register */
+	outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
+	byte = inb_p(MCA_POS_REG(reg)); 
+	outb_p(0, MCA_ADAPTER_SETUP_REG);
+
+	sti();
+
+	/* make sure the stored values are consistent, while we're here */
+	mca_info->slot[slot].pos[reg] = byte;
+
+	return byte;
+} /* mca_read_pos() */
+
+/*--------------------------------------------------------------------*/
+/* Note that this a technically a Bad Thing, as IBM tech stuff says
+	you should only set POS values through their utilities.
+	However, some devices such as the 3c523 recommend that you write
+	back some data to make sure the configuration is consistent.
+	I'd say that IBM is right, but I like my drivers to work.
+	This function can't do checks to see if multiple devices end up
+	with the same resources, so you might see magic smoke if someone
+	screws up.  */
+
+void mca_write_pos( int slot, int reg, unsigned char byte ) 
+{
+	if( slot < 0 || slot >= MCA_MAX_SLOT_NR ) return;
+	if( reg < 0 || reg >= 8 ) return;
+	if (mca_info == 0 )  return;
+
+	cli();
+
+	/*make sure motherboard setup is off*/
+	outb_p(0xff, MCA_MOTHERBOARD_SETUP_REG);
+
+	/* read in the appropriate register */
+	outb_p(0x8|(slot&0xf), MCA_ADAPTER_SETUP_REG);
+	outb_p( byte, MCA_POS_REG(reg) );
+	outb_p(0, MCA_ADAPTER_SETUP_REG);
+
+	sti();
+
+	/* update the global register list, while we have the byte */
+	mca_info->slot[slot].pos[reg] = byte;
+} /* mca_write_pos() */
+
+/*--------------------------------------------------------------------*/
+
+void mca_set_adapter_name( int slot, char* name ) 
+{
+	if( mca_info == 0 ) return;
+
+	if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
+		strncpy( mca_info->slot[slot].name, name,
+			sizeof(mca_info->slot[slot].name) );
+	}
+}
+
+void mca_set_adapter_procfn( int slot, MCA_ProcFn procfn, void* dev)
+{
+	if( mca_info == 0 ) return;
+
+	if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
+		mca_info->slot[slot].procfn = procfn;
+		mca_info->slot[slot].dev = dev;
+	}
+}
+
+char *mca_get_adapter_name( int slot ) 
+{
+	if( mca_info == 0 ) return 0;
+
+	if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
+		return mca_info->slot[slot].name;
+	}
+
+	return 0;
+}
+
+int mca_isadapter( int slot )
+{
+	if( mca_info == 0 ) return 0;
+
+	if( slot >= MCA_MAX_SLOT_NR ) {
+		/* some integrated adapters have 0xffff for an ID, but
+		are still there. VGA, for example. */
+		int i;
+		for( i = 0; i < 8; i ++ ) {
+			if( mca_info->slot[slot].pos[i] != 0xff ) {
+				return 1;
+			}
+		}
+		return 0;
+	} else if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
+		return (mca_info->slot[slot].pos[0] != 0xff ||
+			mca_info->slot[slot].pos[1] != 0xff);
+	}
+
+	return 0;
+}
+
+int mca_isenabled( int slot )
+{
+	if( mca_info == 0 ) return 0;
+
+	if( slot >= 0 && slot < MCA_NUMADAPTERS ) {
+		return (mca_info->slot[slot].pos[2] & MCA_ENABLED);
+	}
+
+	return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+#ifdef CONFIG_PROC_FS
+
+int  get_mca_info(char *buf) 
+{
+	int  i, j, len = 0; 
+
+	if( MCA_bus && mca_info != 0 ) 
+	{
+		/*
+		 *	Format pos registers of eight MCA slots
+		 */
+		for (i=0; i<MCA_MAX_SLOT_NR; i++) 
+		{
+			len += sprintf(buf+len, "Slot %d: ", i+1);
+			for (j=0; j<8; j++) 
+				len += sprintf(buf+len, "%02x ", mca_info->slot[i].pos[j]);
+			len += sprintf( buf+len, " %s\n", mca_info->slot[i].name );
+		}    
+
+		/*
+		 *	Format pos registers of integrated video subsystem
+		 */
+
+		len += sprintf(buf+len, "Video: ");
+		for (j=0; j<8; j++) 
+			len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGVIDEO].pos[j]);
+		len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGVIDEO].name );
+
+		/*
+		 *	Format pos registers of integrated SCSI subsystem
+		 */
+	
+		len += sprintf(buf+len, "SCSI: ");
+		for (j=0; j<8; j++) 
+			len += sprintf(buf+len, "%02x ", mca_info->slot[MCA_INTEGSCSI].pos[j]);
+		len += sprintf( buf+len, " %s\n", mca_info->slot[MCA_INTEGSCSI].name );
+	} 
+	else 
+	{
+	  	/* 
+	  	 *	Leave it empty if MCA not detected
+		 *	this should never happen 
+		 */
+	}
+
+	return len;
+}
+
+
+/*--------------------------------------------------------------------*/
+long mca_do_proc_init( long memory_start, long memory_end )
+{
+	int i = 0;
+	struct proc_dir_entry* node = 0;
+
+	if( mca_info == 0 ) return memory_start;	/* never happens */
+
+	proc_register( &proc_mca, &(struct proc_dir_entry) {
+		PROC_MCA_REGISTERS, 3, "pos", S_IFREG|S_IRUGO,
+		1, 0, 0, 0, &proc_mca_inode_operations,} );
+
+	proc_register( &proc_mca, &(struct proc_dir_entry) {
+		PROC_MCA_MACHINE, 7, "machine", S_IFREG|S_IRUGO,
+		1, 0, 0, 0, &proc_mca_inode_operations,} );
+
+	/* initialize /proc entries for existing adapters */
+	for( i = 0; i < MCA_NUMADAPTERS; i += 1 ) {
+		mca_info->slot[i].procfn = 0;
+		mca_info->slot[i].dev = 0;
+
+		if( ! mca_isadapter( i ) ) continue;
+		if( memory_start + sizeof(struct proc_dir_entry) > memory_end ) {
+			continue;
+		}
+		node = (struct proc_dir_entry*) memory_start;
+		memory_start += sizeof(struct proc_dir_entry);
+
+		if( i < MCA_MAX_SLOT_NR ) {
+			node->low_ino = PROC_MCA_SLOT + i;
+			node->namelen = sprintf( mca_info->slot[i].procname,
+				"slot%d", i+1 );
+		} else if( i == MCA_INTEGVIDEO ) {
+			node->low_ino = PROC_MCA_VIDEO;
+			node->namelen = sprintf( mca_info->slot[i].procname,
+				"video" );
+		} else if( i == MCA_INTEGSCSI ) {
+			node->low_ino = PROC_MCA_SCSI;
+			node->namelen = sprintf( mca_info->slot[i].procname,
+				"scsi" );
+		}
+		node->name = mca_info->slot[i].procname;
+		node->mode = S_IFREG | S_IRUGO;
+		node->ops = &proc_mca_inode_operations;
+		proc_register( &proc_mca, node );
+	}
+
+	return memory_start;
+} /* mca_do_proc_init() */
+
+/*--------------------------------------------------------------------*/
+
+int mca_default_procfn( char* buf, int slot ) 
+{
+	int len = 0, i;
+
+	/* this really shouldn't happen... */
+	if( mca_info == 0 ) {
+		*buf = 0;
+		return 0;
+	}
+
+	/* print out the basic information */
+
+	if( slot < MCA_MAX_SLOT_NR ) {
+		len += sprintf( buf+len, "Slot: %d\n", slot+1 );
+	} else if( slot == MCA_INTEGSCSI ) {
+		len += sprintf( buf+len, "Integrated SCSI Adapter\n" );
+	} else if( slot == MCA_INTEGVIDEO ) {
+		len += sprintf( buf+len, "Integrated Video Adapter\n" );
+	}
+	if( mca_info->slot[slot].name[0] ) {
+		/* drivers might register a name without /proc handler... */
+		len += sprintf( buf+len, "Adapter Name: %s\n",
+			mca_info->slot[slot].name );
+	} else {
+		len += sprintf( buf+len, "Adapter Name: Unknown\n" );
+	}
+	len += sprintf( buf+len, "Id: %02x%02x\n",
+		mca_info->slot[slot].pos[1], mca_info->slot[slot].pos[0] );
+	len += sprintf( buf+len, "Enabled: %s\nPOS: ",
+		mca_isenabled(slot) ? "Yes" : "No" );
+	for (i=0; i<8; i++) {
+		len += sprintf(buf+len, "%02x ", mca_info->slot[slot].pos[i]);
+	}
+	buf[len++] = '\n';
+	buf[len] = 0;
+
+	return len;
+} /* mca_default_procfn() */
+
+static int get_mca_machine_info( char* buf ) 
+{
+	int len = 0;
+
+	len += sprintf( buf+len, "Model Id: 0x%x\n", machine_id );
+	len += sprintf( buf+len, "Submodel Id: 0x%x\n", machine_submodel_id );
+	len += sprintf( buf+len, "BIOS Revision: 0x%x\n", BIOS_revision );
+
+	return len;
+}
+
+/*
+static int mca_not_implemented( char* buf ) 
+{
+	return sprintf( buf, "Sorry, not implemented yet...\n" );
+}
+*/
+
+static int mca_fill( char* page, int pid, int type, char** start,
+	off_t offset, int length)
+{
+	int len = 0;
+	int slot = 0;
+
+	switch( type ) {
+		case PROC_MCA_REGISTERS:
+			return get_mca_info( page );
+		case PROC_MCA_MACHINE:
+			return get_mca_machine_info( page );
+		case PROC_MCA_VIDEO:
+			slot = MCA_INTEGVIDEO;
+			break;
+		case PROC_MCA_SCSI:
+			slot = MCA_INTEGSCSI;
+			break;
+		default:
+			if( type < PROC_MCA_SLOT || type >= PROC_MCA_LAST ) {
+				return -EBADF;
+			}
+			slot = type - PROC_MCA_SLOT;
+			break;
+	}
+
+	/* if we made it here, we better have a valid slot */
+
+	/* get the standard info */
+	len = mca_default_procfn( page, slot );
+
+	/* do any device-specific processing, if there is any */
+	if( mca_info->slot[slot].procfn ) {
+		len += mca_info->slot[slot].procfn( page+len, slot,
+			mca_info->slot[slot].dev );
+	}
+
+	return len;
+} /* mca_fill() */
+
+/*
+ *	Blatantly stolen from fs/proc/array.c, and thus is probably overkill 
+ */
+
+#define PROC_BLOCK_SIZE	(3*1024)
+
+long proc_mca_read( struct inode* inode, struct file* file,
+	char* buf, unsigned long count)
+{
+	unsigned long page;
+	char *start;
+	int length;
+	int end;
+	unsigned int type, pid;
+	struct proc_dir_entry *dp;
+
+	if (count < 0)
+		return -EINVAL;
+	if (count > PROC_BLOCK_SIZE)
+		count = PROC_BLOCK_SIZE;
+	if (!(page = __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+	type = inode->i_ino;
+	pid = type >> 16;
+	type &= 0x0000ffff;
+	start = 0;
+	dp = (struct proc_dir_entry *) inode->u.generic_ip;
+	length = mca_fill((char *) page, pid, type,
+			    &start, file->f_pos, count);
+	if (length < 0) {
+		free_page(page);
+		return length;
+	}
+	if (start != 0) {
+		/* We have had block-adjusting processing! */
+		copy_to_user(buf, start, length);
+		file->f_pos += length;
+		count = length;
+	} else {
+		/* Static 4kB (or whatever) block capacity */
+		if (file->f_pos >= length) {
+			free_page(page);
+			return 0;
+		}
+		if (count + file->f_pos > length)
+			count = length - file->f_pos;
+		end = count + file->f_pos;
+		copy_to_user(buf, (char *) page + file->f_pos, count);
+		file->f_pos = end;
+	}
+	free_page(page);
+	return count;
+} /* proc_mca_read() */
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v2.1.14/linux/arch/i386/kernel/setup.c	Sun Nov 10 20:12:08 1996
+++ linux/arch/i386/kernel/setup.c	Thu Dec 12 16:51:08 1996
@@ -53,6 +53,12 @@
  * Bus types ..
  */
 int EISA_bus = 0;
+int MCA_bus = 0;
+
+/* for MCA, but anyone else can use it if they want */
+unsigned int machine_id = 0;
+unsigned int machine_submodel_id = 0;
+unsigned int BIOS_revision = 0;
 
 /*
  * Setup options
@@ -62,6 +68,10 @@
 #ifdef CONFIG_APM
 struct apm_bios_info apm_bios_info;
 #endif
+struct sys_desc_table_struct {
+	unsigned short length;
+	unsigned char table[0];
+};
 
 unsigned char aux_device_present;
 
@@ -94,6 +104,7 @@
 #define KERNEL_START (*(unsigned long *) (PARAM+0x214))
 #define INITRD_START (*(unsigned long *) (PARAM+0x218))
 #define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
+#define SYS_DESC_TABLE (*(struct sys_desc_table_struct*)(PARAM+0x220))
 #define COMMAND_LINE ((char *) (PARAM+2048))
 #define COMMAND_LINE_SIZE 256
 
@@ -124,6 +135,12 @@
 #ifdef CONFIG_APM
 	apm_bios_info = APM_BIOS_INFO;
 #endif
+	if( SYS_DESC_TABLE.length != 0 ) {
+		MCA_bus = SYS_DESC_TABLE.table[3] &0x2;
+		machine_id = SYS_DESC_TABLE.table[0];
+		machine_submodel_id = SYS_DESC_TABLE.table[1];
+		BIOS_revision = SYS_DESC_TABLE.table[2];
+	}
 	aux_device_present = AUX_DEVICE_INFO;
 	memory_end = (1<<20) + (EXT_MEM_K<<10);
 	memory_end &= PAGE_MASK;
@@ -199,7 +216,7 @@
 	/* request io space for devices used on all i[345]86 PC'S */
 	request_region(0x00,0x20,"dma1");
 	request_region(0x40,0x20,"timer");
-	request_region(0x80,0x20,"dma page reg");
+	request_region(0x80,0x10,"dma page reg");
 	request_region(0xc0,0x20,"dma2");
 	request_region(0xf0,0x10,"npu");
 }
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.1.14/linux/arch/i386/kernel/signal.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/i386/kernel/signal.c	Wed Dec 11 16:41:23 1996
@@ -82,7 +82,10 @@
 #define COPY(x) regs->x = context->x
 #define COPY_SEG(seg) \
 { unsigned int tmp = context->seg; \
-if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \
+if (   (tmp & 0xfffc)     /* not a NULL selectors */ \
+    && (tmp & 0x4) != 0x4 /* not a LDT selector */ \
+    && (tmp & 3) != 3     /* not a RPL3 GDT selector */ \
+   ) goto badframe; \
 regs->x##seg = tmp; }
 #define COPY_SEG_STRICT(seg) \
 { unsigned int tmp = context->seg; \
@@ -90,7 +93,10 @@
 regs->x##seg = tmp; }
 #define GET_SEG(seg) \
 { unsigned int tmp = context->seg; \
-if ((tmp & 0xfffc) && (tmp & 3) != 3) goto badframe; \
+if (   (tmp & 0xfffc)     /* not a NULL selectors */ \
+    && (tmp & 0x4) != 0x4 /* not a LDT selector */ \
+    && (tmp & 3) != 3     /* not a RPL3 GDT selector */ \
+   ) goto badframe; \
 __asm__("mov %w0,%%" #seg: :"r" (tmp)); }
 	struct sigcontext * context;
 	struct pt_regs * regs;
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.14/linux/arch/i386/kernel/smp.c	Wed Sep 25 11:55:23 1996
+++ linux/arch/i386/kernel/smp.c	Thu Dec 12 16:51:08 1996
@@ -19,6 +19,9 @@
  *		Alan Cox	:	By repeated request 8) - Total BogoMIP report.
  *		Greg Wright	:	Fix for kernel stacks panic.
  *		Erich Boleyn	:	MP v1.4 and additional changes.
+ *	Matthias Sattler	:	Changes for 2.1 kernel map.
+ *	Michel Lespinasse	:	Changes for 2.1 kernel map.
+ *
  */
 
 #include <linux/kernel.h>
@@ -35,8 +38,48 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 #include <asm/smp.h>
+#include <asm/io.h>
 
 /*
+ *	Some notes on processor bugs:
+ *
+ *	Pentium and Pentium Pro (and all CPU's) have bugs. The Linux issues
+ *	for SMP are handled as follows.
+ *
+ *	Pentium Pro
+ *		Occasional delivery of 'spurious interrupt' as trap #16. This
+ *	is very very rare. The kernel logs the event and recovers
+ *
+ *	Pentium
+ *		There is a marginal case where REP MOVS on 100MHz SMP
+ *	machines with B stepping processors can fail. XXX should provide
+ *	an L1cache=Writethrough or L1cache=off option. 
+ *
+ *		B stepping CPU's may hang. There are hardware work arounds
+ *	for this. We warn about it in case your board doesnt have the work
+ *	arounds. Basically thats so I can tell anyone with a B stepping
+ *	CPU and SMP problems "tough".
+ *
+ *	Specific items [From Pentium Processor Specification Update]
+ *
+ *	1AP.	Linux doesn't use remote read
+ *	2AP.	Linux doesn't trust APIC errors
+ *	3AP.	We work around this
+ *	4AP.	Linux never generated 3 interrupts of the same priority
+ *		to cause a lost local interrupt.
+ *	5AP.	Remote read is never used
+ *	9AP.	XXX NEED TO CHECK WE HANDLE THIS XXX
+ *	10AP.	XXX NEED TO CHECK WE HANDLE THIS XXX
+ *	11AP.	Linux read the APIC between writes to avoid this, as per
+ *		the documentation. Make sure you preserve this as it affects
+ *		the C stepping chips too.
+ *
+ *	If this sounds worrying believe me these bugs are ___RARE___ and
+ *	there's about nothing of note with C stepping upwards.
+ */
+ 
+ 
+/*
  *	Why isn't this somewhere standard ??
  */
  
@@ -47,7 +90,9 @@
 	return b;
 }
 
+static int smp_b_stepping = 0;				/* Set if we find a B stepping CPU			*/
 
+static int max_cpus = -1;				/* Setup configured maximum number of CPUs to activate	*/
 int smp_found_config=0;					/* Have we found an SMP box 				*/
 
 unsigned long cpu_present_map = 0;			/* Bitmask of existing CPU's 				*/
@@ -57,6 +102,7 @@
 volatile int cpu_logical_map[NR_CPUS];			/* which logical number maps to which CPU		*/
 volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};	/* We always use 0 the rest is ready for parallel delivery */
 volatile unsigned long smp_invalidate_needed;		/* Used for the invalidate map that's also checked in the spinlock */
+volatile unsigned long kstack_ptr;			/* Stack vector for booting CPU's			*/
 struct cpuinfo_x86 cpu_data[NR_CPUS];			/* Per cpu bogomips and other parameters 		*/
 static unsigned int num_processors = 1;			/* Internal processor count				*/
 static unsigned long io_apic_addr = 0xFEC00000;		/* Address of the I/O apic (not yet used) 		*/
@@ -65,7 +111,7 @@
 static int smp_activated = 0;				/* Tripped once we need to start cross invalidating 	*/
 int apic_version[NR_CPUS];				/* APIC version number					*/
 static volatile int smp_commenced=0;			/* Tripped when we start scheduling 		    	*/
-unsigned long apic_addr=0xFEE00000;			/* Address of APIC (defaults to 0xFEE00000)		*/
+unsigned long apic_addr = 0xFEE00000;			/* Address of APIC (defaults to 0xFEE00000)		*/
 unsigned long nlong = 0;				/* dummy used for apic_reg address + 0x20		*/
 unsigned char *apic_reg=((unsigned char *)(&nlong))-0x20;/* Later set to the ioremap() of the APIC 		*/
 unsigned long apic_retval;				/* Just debugging the assembler.. 			*/
@@ -104,6 +150,25 @@
 #define SMP_PRINTK(x)
 #endif
 
+/*
+ *	Setup routine for controlling SMP activation
+ *
+ *	Command-line option of "nosmp" or "maxcpus=0" will disable SMP
+ *      activation entirely (the MPS table probe still happens, though).
+ *
+ *	Command-line option of "maxcpus=<NUM>", where <NUM> is an integer
+ *	greater than 0, limits the maximum number of CPUs activated in
+ *	SMP mode to <NUM>.
+ */
+
+void smp_setup(char *str, int *ints)
+{
+	if (ints && ints[0] > 0)
+		max_cpus = ints[1];
+	else
+		max_cpus = 0;
+}
+
 
 /* 
  *	Checksum an MP configuration block.
@@ -183,7 +248,7 @@
 	printk("APIC at: 0x%lX\n",mpc->mpc_lapic);
 
 	/* set the local APIC address */
-	apic_addr = mpc->mpc_lapic;
+	apic_addr = (unsigned long)phys_to_virt((unsigned long)mpc->mpc_lapic);
 	
 	/*
 	 *	Now process the configuration blocks.
@@ -259,7 +324,7 @@
 	                                printk("I/O APIC #%d Version %d at 0x%lX.\n",
 	                                	m->mpc_apicid,m->mpc_apicver,
 	                                	m->mpc_apicaddr);
-	                                io_apic_addr = m->mpc_apicaddr;
+	                                io_apic_addr = (unsigned long)phys_to_virt(m->mpc_apicaddr);
 	                        }
                                 mpt+=sizeof(*m);
                                 count+=sizeof(*m); 
@@ -295,7 +360,7 @@
  
 int smp_scan_config(unsigned long base, unsigned long length)
 {
-	unsigned long *bp=(unsigned long *)base;
+	unsigned long *bp=phys_to_virt(base);
 	struct intel_mp_floating *mpf;
 	
 	SMP_PRINTK(("Scan SMP from %p for %ld bytes.\n",
@@ -466,13 +531,14 @@
 unsigned long smp_alloc_memory(unsigned long mem_base)
 {
 	int size=(num_processors-1)*PAGE_SIZE;		/* Number of stacks needed */
+
 	/*
 	 *	Our stacks have to be below the 1Mb line, and mem_base on entry
 	 *	is 4K aligned.
 	 */
 	 
-	if(mem_base+size>=0x9F000)
-		panic("smp_alloc_memory: Insufficient low memory for kernel stacks.\n");
+	if(virt_to_phys((void *)(mem_base+size))>=0x9F000)
+		panic("smp_alloc_memory: Insufficient low memory for kernel stacks 0x%lx.\n", mem_base);
 	kstack_base=(void *)mem_base;
 	mem_base+=size;
 	kstack_end=(void *)mem_base;
@@ -505,6 +571,8 @@
 	c->x86=x86;
 	c->x86_model=x86_model;
 	c->x86_mask=x86_mask;
+	if(x86_mask>=1 && x86_mask<=4)
+		smp_b_stepping=1;		/* Remember we have B step CPUs */
 	c->x86_capability=x86_capability;
 	c->fdiv_bug=fdiv_bug;
 	c->wp_works_ok=wp_works_ok;		/* Always assumed the same currently */
@@ -529,8 +597,14 @@
 	/*
 	 *	Lets the callin's below out of their loop.
 	 */
+	SMP_PRINTK(("Setting commenced=1, go go go\n"));
 	smp_commenced=1;
 }
+
+void pointless_func(void)
+{
+	;
+}
  
 void smp_callin(void)
 {
@@ -551,6 +625,8 @@
 	 *	Get our bogomips.
 	 */	
 	calibrate_delay();
+	SMP_PRINTK(("Stack at about %p\n",&cpuid));
+	
 	/*
 	 *	Save our processor parameters
 	 */
@@ -566,18 +642,20 @@
 /*	printk("Testing faulting...\n");
 	*(long *)0=1;		 OOPS... */
 	local_flush_tlb();
+	
 	while(!smp_commenced);
+	
+	local_flush_tlb();
+	
 	if (cpu_number_map[cpuid] == -1)
 		while(1);
-	local_flush_tlb();
 	SMP_PRINTK(("Commenced..\n"));
-	
+	local_flush_tlb();
 	load_TR(cpu_number_map[cpuid]);
-/*	while(1);*/
 }
 
 /*
- *	Cycle through the processors sending pentium IPI's to boot each.
+ *	Cycle through the processors sending APIC IPI's to boot each.
  */
  
 void smp_boot_cpus(void)
@@ -585,6 +663,7 @@
 	int i;
 	int cpucount=0;
 	unsigned long cfg;
+	pgd_t maincfg;
 	void *stack;
 	extern unsigned long init_user_stack[];
 	
@@ -608,6 +687,16 @@
 	active_kernel_processor=boot_cpu_id;
 
 	/*
+	 *	If SMP should be disabled, then really disable it!
+	 */
+
+	if (!max_cpus && smp_found_config)
+	{
+		smp_found_config = 0;
+		printk("SMP mode deactivated, forcing use of dummy APIC emulation.\n");
+	}
+
+	/*
 	 *	If we don't conform to the Intel MPS standard, get out
 	 *	of here now!
 	 */
@@ -686,7 +775,8 @@
 		if (i == boot_cpu_id)
 			continue;
 		
-		if (cpu_present_map & (1 << i))
+		if ((cpu_present_map & (1 << i))
+		    && (max_cpus < 0 || max_cpus > cpucount+1))
 		{
 			unsigned long send_status, accept_status;
 			int timeout, num_starts, j;
@@ -698,7 +788,8 @@
 			stack=get_kernel_stack();	/* We allocated these earlier */
 			if(stack==NULL)
 				panic("No memory for processor stacks.\n");
-			kernel_stacks[i]=stack;
+				
+			kernel_stacks[i]=(void *)phys_to_virt((unsigned long)stack);
 			install_trampoline(stack);
 
 			printk("Booting processor %d stack %p: ",i,stack);			/* So we set what's up   */
@@ -719,8 +810,11 @@
 			CMOS_WRITE(0xa, 0xf);
 			pg0[0]=7;
 			local_flush_tlb();
-			*((volatile unsigned short *) 0x469) = ((unsigned long)stack)>>4;
-			*((volatile unsigned short *) 0x467) = 0;
+			SMP_PRINTK(("1.\n"));
+			*((volatile unsigned short *) phys_to_virt(0x469)) = ((unsigned long)stack)>>4;
+			SMP_PRINTK(("2.\n"));
+			*((volatile unsigned short *) phys_to_virt(0x467)) = 0;
+			SMP_PRINTK(("3.\n"));
 			
 			/*
 			 *	Protect it again
@@ -729,6 +823,17 @@
 			pg0[0]= cfg;
 			local_flush_tlb();
 
+			/*	walken modif
+			 *	enable mapping of the first 4M at virtual
+			 *	address zero
+			 */
+
+			maincfg=swapper_pg_dir[0];
+			((unsigned long *)swapper_pg_dir)[0]=0x102007;
+
+			/* no need to local_flush_tlb :
+			   we are setting this up for the slave processor ! */
+
 			/*
 			 *	Be paranoid about clearing APIC errors.
 			 */
@@ -800,7 +905,8 @@
 				SMP_PRINTK(("Sending STARTUP #%d.\n",j));
 
 				apic_write(APIC_ESR, 0);
-			
+				SMP_PRINTK(("After apic_write.\n"));
+
 				/*
 				 *	STARTUP IPI
 				 */
@@ -812,11 +918,14 @@
 				cfg&=~0xCDFFF;								/* Clear bits 		*/
 				cfg |= (APIC_DEST_FIELD
 					| APIC_DEST_DM_STARTUP
-					| (((int) stack) >> 12) );					/* Boot on the stack 	*/		
+					| (((int)virt_to_phys(stack)) >> 12));					/* Boot on the stack 	*/		
+				SMP_PRINTK(("Before start apic_write.\n"));
 				apic_write(APIC_ICR, cfg);						/* Kick the second 	*/
 
+				SMP_PRINTK(("Startup point 1.\n"));
 				timeout = 0;
 				do {
+				        SMP_PRINTK(("Sleeping.\n")); udelay(1000000);	
 					udelay(10);
 				} while ( (send_status = (apic_read(APIC_ICR) & 0x1000))
 					  && (timeout++ < 1000));
@@ -824,6 +933,7 @@
 
 				accept_status = (apic_read(APIC_ESR) & 0xEF);
 			}
+			SMP_PRINTK(("After Startup.\n"));
 
 			if (send_status)		/* APIC never delivered?? */
 				printk("APIC never delivered???\n");
@@ -847,15 +957,24 @@
 				}
 				else
 				{
-					if(*((volatile unsigned char *)8192)==0xA5)
+					if(*((volatile unsigned char *)phys_to_virt(8192))==0xA5)
 						printk("Stuck ??\n");
 					else
 						printk("Not responding.\n");
 				}
 			}
+			SMP_PRINTK(("CPU has booted.\n"));
+
+                        /*      walken modif
+                         *      restore mapping of the first 4M
+                         */
+
+                        swapper_pg_dir[0]=maincfg;
+                        
+                        local_flush_tlb();
 
 			/* mark "stuck" area as not stuck */
-			*((volatile unsigned long *)8192) = 0;
+			*((volatile unsigned long *)phys_to_virt(8192)) = 0;
 		}
 		
 		/* 
@@ -885,7 +1004,7 @@
 
 	CMOS_WRITE(0, 0xf);
 
-	*((volatile long *) 0x467) = 0;
+	*((volatile long *) phys_to_virt(0x467)) = 0;
 
 	/*
 	 *	Restore old page 0 entry.
@@ -898,6 +1017,7 @@
 	 *	Allow the user to impress friends.
 	 */
 	
+	SMP_PRINTK(("Before bogomips.\n"));
 	if(cpucount==0)
 	{
 		printk("Error: only one processor found.\n");
@@ -915,9 +1035,13 @@
 			cpucount+1, 
 			(bogosum+2500)/500000,
 			((bogosum+2500)/5000)%100);
+		SMP_PRINTK(("Before bogocount - setting activated=1.\n"));
 		smp_activated=1;
 		smp_num_cpus=cpucount+1;
 	}
+	if(smp_b_stepping)
+		printk("WARNING: SMP operation may be unreliable with B stepping processors.\n");
+	SMP_PRINTK(("Boot done.\n"));
 }
 
 
@@ -977,9 +1101,9 @@
 		panic("CPU #%d: Message pass %d but pass in progress by %d of %d\n",
 			smp_processor_id(),msg,message_cpu, smp_msg_id);
 	}
+
 	message_cpu=smp_processor_id();
 	
-
 	/*
 	 *	We are busy
 	 */
@@ -1094,7 +1218,7 @@
 	if(smp_activated && smp_processor_id()!=active_kernel_processor)
 		panic("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor);
 /*	printk("SMI-");*/
-	
+
 	/*
 	 *	The assignment is safe because it's volatile so the compiler cannot reorder it,
 	 *	because the i586 has strict memory ordering and because only the kernel lock holder
@@ -1134,11 +1258,13 @@
 
 void smp_reschedule_irq(int cpl, struct pt_regs *regs)
 {
+/*#define DEBUGGING_SMP_RESCHED*/
 #ifdef DEBUGGING_SMP_RESCHED
 	static int ct=0;
 	if(ct==0)
 	{
 		printk("Beginning scheduling on CPU#%d\n",smp_processor_id());
+		udelay(1000000);
 		ct=1;
 	}
 #endif	
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v2.1.14/linux/arch/i386/kernel/time.c	Tue Nov 12 15:56:02 1996
+++ linux/arch/i386/kernel/time.c	Thu Dec 12 16:51:08 1996
@@ -371,6 +371,21 @@
 	   closely for now.. */
 	/*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */
 	    
+#ifdef CONFIG_MCA
+	if( MCA_bus ) {
+		/* The PS/2 uses level-triggered interrupts.  You can't
+		turn them off, nor would you want to (any attempt to
+		enable edge-triggered interrupts usually gets intercepted by a
+		special hardware circuit).  Hence we have to acknowledge
+		the timer interrupt.  Through some incredibly stupid
+		design idea, the reset for IRQ 0 is done by setting the
+		high bit of the PPI port B (0x61).  Note that some PS/2s,
+		notably the 55SX, work fine if this is removed.  */
+
+		irq = inb_p( 0x61 );	/* read the current state */
+		outb_p( irq|0x80, 0x61 );	/* reset the IRQ */
+	}
+#endif
 }
 
 #ifndef	CONFIG_APM	/* cycle counter may be unreliable */
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/trampoline.S linux/arch/i386/kernel/trampoline.S
--- v2.1.14/linux/arch/i386/kernel/trampoline.S	Mon Oct 30 10:30:17 1995
+++ linux/arch/i386/kernel/trampoline.S	Thu Dec 12 16:51:08 1996
@@ -52,15 +52,27 @@
 
 	.word	0,0,0,0		! unused
 
-	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
-	.word	0x0000		! base address=0
-	.word	0x9A00		! code read/exec
-	.word	0x00C0		! granularity=4096, 386
+!walken modif
+        .word   0xFFFF          ! 4Gb - (0x100000*0x1000 = 4Gb)
+        .word   0x0000          ! base address=0
+        .word   0x9A00          ! code read/exec
+        .word   0x00CF          ! granularity=4096, 386 (+5th nibble of limit)
 
-	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
-	.word	0x0000		! base address=0
-	.word	0x9200		! data read/write
-	.word	0x00C0		! granularity=4096, 386
+        .word   0xFFFF          ! 4Gb - (0x100000*0x1000 = 4Gb)
+        .word   0x0000          ! base address=0
+        .word   0x9200          ! data read/write
+        .word   0x00CF          ! granularity=4096, 386 (+5th nibble of limit)
+!walken modif
+
+!	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
+!	.word	0x0000		! base address=0
+!	.word	0x9A00		! code read/exec
+!	.word	0x00C0		! granularity=4096, 386
+
+!	.word	0x07FF		! 8Mb - limit=2047 (2048*4096=8Mb)
+!	.word	0x0000		! base address=0
+!	.word	0x9200		! data read/write
+!	.word	0x00C0		! granularity=4096, 386
 
 idt_48:
 	.word	0			! idt limit=0
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v2.1.14/linux/arch/i386/kernel/traps.c	Sun Nov 10 20:12:08 1996
+++ linux/arch/i386/kernel/traps.c	Thu Dec 12 15:57:52 1996
@@ -44,6 +44,20 @@
 	die_if_kernel(str,regs,error_code); \
 }
 
+#define DO_VM86_ERROR(trapnr, signr, str, name, tsk) \
+asmlinkage void do_##name(struct pt_regs * regs, long error_code) \
+{ \
+	if (regs->eflags & VM_MASK) { \
+		if (!handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, trapnr)) \
+			return; \
+		/* else fall through */ \
+	} \
+	tsk->tss.error_code = error_code; \
+	tsk->tss.trap_no = trapnr; \
+	force_sig(signr, tsk); \
+	die_if_kernel(str,regs,error_code); \
+}
+
 #define get_seg_byte(seg,addr) ({ \
 register unsigned char __res; \
 __asm__("push %%fs;mov %%ax,%%fs;movb %%fs:%2,%%al;pop %%fs" \
@@ -164,12 +178,12 @@
 	do_exit(SIGSEGV);
 }
 
-DO_ERROR( 0, SIGFPE,  "divide error", divide_error, current)
-DO_ERROR( 3, SIGTRAP, "int3", int3, current)
-DO_ERROR( 4, SIGSEGV, "overflow", overflow, current)
-DO_ERROR( 5, SIGSEGV, "bounds", bounds, current)
+DO_VM86_ERROR( 0, SIGFPE,  "divide error", divide_error, current)
+DO_VM86_ERROR( 3, SIGTRAP, "int3", int3, current)
+DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow, current)
+DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds, current)
 DO_ERROR( 6, SIGILL,  "invalid operand", invalid_op, current)
-DO_ERROR( 7, SIGSEGV, "device not available", device_not_available, current)
+DO_VM86_ERROR( 7, SIGSEGV, "device not available", device_not_available, current)
 DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current)
 DO_ERROR( 9, SIGFPE,  "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math)
 DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current)
@@ -181,7 +195,7 @@
 asmlinkage void do_general_protection(struct pt_regs * regs, long error_code)
 {
 	if (regs->eflags & VM_MASK) {
-		handle_vm86_fault((struct vm86_regs *) regs, error_code);
+		handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
 		return;
 	}
 	die_if_kernel("general protection",regs,error_code);
@@ -206,7 +220,7 @@
 asmlinkage void do_debug(struct pt_regs * regs, long error_code)
 {
 	if (regs->eflags & VM_MASK) {
-		handle_vm86_debug((struct vm86_regs *) regs, error_code);
+		handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, 1);
 		return;
 	}
 	force_sig(SIGTRAP, current);
diff -u --recursive --new-file v2.1.14/linux/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- v2.1.14/linux/arch/i386/kernel/vm86.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/i386/kernel/vm86.c	Wed Dec 11 16:41:23 1996
@@ -30,6 +30,11 @@
  * Hopefully these problems do not actually matter for anything.
  */
 
+
+#define KVM86	((struct kernel_vm86_struct *)regs)
+#define VMPI 	KVM86->vm86plus
+
+
 /*
  * 8- and 16-bit register defines..
  */
@@ -50,7 +55,12 @@
 #define SAFE_MASK	(0xDD5)
 #define RETURN_MASK	(0xDFF)
 
-asmlinkage struct pt_regs * save_v86_state(struct vm86_regs * regs)
+#define VM86_REGS_PART2 orig_eax
+#define VM86_REGS_SIZE1 \
+        ( (unsigned)( & (((struct kernel_vm86_regs *)0)->VM86_REGS_PART2) ) )
+#define VM86_REGS_SIZE2 (sizeof(struct kernel_vm86_regs) - VM86_REGS_SIZE1)
+
+asmlinkage struct pt_regs * save_v86_state(struct kernel_vm86_regs * regs)
 {
 	unsigned long tmp;
 
@@ -59,16 +69,17 @@
 		do_exit(SIGSEGV);
 	}
 	set_flags(regs->eflags, VEFLAGS, VIF_MASK | current->tss.v86mask);
-	tmp = copy_to_user(&current->tss.vm86_info->regs,regs,sizeof(*regs));
+	tmp = copy_to_user(&current->tss.vm86_info->regs,regs, VM86_REGS_SIZE1);
+	tmp += copy_to_user(&current->tss.vm86_info->regs.VM86_REGS_PART2,
+		&regs->VM86_REGS_PART2, VM86_REGS_SIZE2);
 	tmp += put_user(current->tss.screen_bitmap,&current->tss.vm86_info->screen_bitmap);
 	if (tmp) {
 		printk("vm86: could not access userspace vm86_info\n");
 		do_exit(SIGSEGV);
 	}
-	tmp = current->tss.esp0;
 	current->tss.esp0 = current->saved_kernel_stack;
 	current->saved_kernel_stack = 0;
-	return (struct pt_regs *) tmp;
+	return KVM86->regs32;
 }
 
 static void mark_screen_rdonly(struct task_struct * tsk)
@@ -103,37 +114,100 @@
 	flush_tlb();
 }
 
-asmlinkage int sys_vm86(struct vm86_struct * v86)
+
+
+static do_vm86_irq_handling(int subfunction, int irqnumber);
+static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk);
+
+asmlinkage int sys_vm86old(struct vm86_struct * v86)
 {
-	struct vm86_struct info;
+	struct kernel_vm86_struct info; /* declare this _on top_,
+					 * this avoids wasting of stack space.
+					 * This remains on the stack until we
+					 * return to 32 bit user space.
+					 */
 	struct task_struct *tsk = current;
-	struct pt_regs * pt_regs = (struct pt_regs *) &v86;
+	int tmp;
 
 	if (tsk->saved_kernel_stack)
 		return -EPERM;
-	if (copy_from_user(&info,v86,sizeof(info)))
+	tmp  = copy_from_user(&info, v86, VM86_REGS_SIZE1);
+	tmp += copy_from_user(&info.regs.VM86_REGS_PART2, &v86->regs.VM86_REGS_PART2,
+		(long)&info.vm86plus - (long)&info.regs.VM86_REGS_PART2);
+	if (tmp)
 		return -EFAULT;
+	memset(&info.vm86plus, 0, (int)&info.regs32 - (int)&info.vm86plus);
+	info.regs32 = (struct pt_regs *) &v86;
+	tsk->tss.vm86_info = v86;
+	do_sys_vm86(&info, tsk);
+	return 0;	/* we never return here */
+}
+
+
+asmlinkage int sys_vm86(unsigned long subfunction, struct vm86plus_struct * v86)
+{
+	struct kernel_vm86_struct info; /* declare this _on top_,
+					 * this avoids wasting of stack space.
+					 * This remains on the stack until we
+					 * return to 32 bit user space.
+					 */
+	struct task_struct *tsk = current;
+	int tmp;
+
+	switch (subfunction) {
+		case VM86_REQUEST_IRQ:
+		case VM86_FREE_IRQ:
+		case VM86_GET_IRQ_BITS:
+		case VM86_GET_AND_RESET_IRQ:
+			return do_vm86_irq_handling(subfunction,(int)v86);
+		case VM86_PLUS_INSTALL_CHECK:
+			/* NOTE: on old vm86 stuff this will return the error
+			   from verify_area(), because the subfunction is
+			   interpreted as (invalid) address to vm86_struct.
+			   So the installation check works.
+			 */
+			return 0;
+	}
+
+	/* we come here only for functions VM86_ENTER, VM86_ENTER_NO_BYPASS */
+	if (tsk->saved_kernel_stack)
+		return -EPERM;
+	tmp  = copy_from_user(&info, v86, VM86_REGS_SIZE1);
+	tmp += copy_from_user(&info.regs.VM86_REGS_PART2, &v86->regs.VM86_REGS_PART2,
+		(long)&info.regs32 - (long)&info.regs.VM86_REGS_PART2);
+	if (tmp)
+		return -EFAULT;
+	info.regs32 = (struct pt_regs *) &subfunction;
+	info.vm86plus.is_vm86pus = 1;
+	tsk->tss.vm86_info = (struct vm86_struct *)v86;
+	do_sys_vm86(&info, tsk);
+	return 0;	/* we never return here */
+}
+
+
+static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk)
+{
 /*
  * make sure the vm86() system call doesn't try to do anything silly
  */
-	info.regs.__null_ds = 0;
-	info.regs.__null_es = 0;
+	info->regs.__null_ds = 0;
+	info->regs.__null_es = 0;
 
 /* we are clearing fs,gs later just before "jmp ret_from_sys_call",
  * because starting with Linux 2.1.x they aren't no longer saved/restored
  */
-	
+
 /*
  * The eflags register is also special: we cannot trust that the user
  * has set it up safely, so this makes sure interrupt etc flags are
  * inherited from protected mode.
  */
- 	VEFLAGS = info.regs.eflags;
-	info.regs.eflags &= SAFE_MASK;
-	info.regs.eflags |= pt_regs->eflags & ~SAFE_MASK;
-	info.regs.eflags |= VM_MASK;
+ 	VEFLAGS = info->regs.eflags;
+	info->regs.eflags &= SAFE_MASK;
+	info->regs.eflags |= info->regs32->eflags & ~SAFE_MASK;
+	info->regs.eflags |= VM_MASK;
 
-	switch (info.cpu_type) {
+	switch (info->cpu_type) {
 		case CPU_286:
 			tsk->tss.v86mask = 0;
 			break;
@@ -151,24 +225,23 @@
 /*
  * Save old state, set default return value (%eax) to 0
  */
-	pt_regs->eax = 0;
+	info->regs32->eax = 0;
 	tsk->saved_kernel_stack = tsk->tss.esp0;
-	tsk->tss.esp0 = (unsigned long) pt_regs;
-	tsk->tss.vm86_info = v86;
+	tsk->tss.esp0 = (unsigned long) &info->VM86_TSS_ESP0;
 
-	tsk->tss.screen_bitmap = info.screen_bitmap;
-	if (info.flags & VM86_SCREEN_BITMAP)
+	tsk->tss.screen_bitmap = info->screen_bitmap;
+	if (info->flags & VM86_SCREEN_BITMAP)
 		mark_screen_rdonly(tsk);
 	__asm__ __volatile__(
 		"xorl %%eax,%%eax; mov %%ax,%%fs; mov %%ax,%%gs\n\t"
 		"movl %0,%%esp\n\t"
 		"jmp ret_from_sys_call"
 		: /* no outputs */
-		:"r" (&info.regs), "b" (tsk) : "ax");
-	return 0;
+		:"r" (&info->regs), "b" (tsk) : "ax");
+	/* we never return here */
 }
 
-static inline void return_to_32bit(struct vm86_regs * regs16, int retval)
+static inline void return_to_32bit(struct kernel_vm86_regs * regs16, int retval)
 {
 	struct pt_regs * regs32;
 
@@ -179,24 +252,24 @@
 		: : "r" (regs32), "b" (current));
 }
 
-static inline void set_IF(struct vm86_regs * regs)
+static inline void set_IF(struct kernel_vm86_regs * regs)
 {
 	VEFLAGS |= VIF_MASK;
 	if (VEFLAGS & VIP_MASK)
 		return_to_32bit(regs, VM86_STI);
 }
 
-static inline void clear_IF(struct vm86_regs * regs)
+static inline void clear_IF(struct kernel_vm86_regs * regs)
 {
 	VEFLAGS &= ~VIF_MASK;
 }
 
-static inline void clear_TF(struct vm86_regs * regs)
+static inline void clear_TF(struct kernel_vm86_regs * regs)
 {
 	regs->eflags &= ~TF_MASK;
 }
 
-static inline void set_vflags_long(unsigned long eflags, struct vm86_regs * regs)
+static inline void set_vflags_long(unsigned long eflags, struct kernel_vm86_regs * regs)
 {
 	set_flags(VEFLAGS, eflags, current->tss.v86mask);
 	set_flags(regs->eflags, eflags, SAFE_MASK);
@@ -204,7 +277,7 @@
 		set_IF(regs);
 }
 
-static inline void set_vflags_short(unsigned short flags, struct vm86_regs * regs)
+static inline void set_vflags_short(unsigned short flags, struct kernel_vm86_regs * regs)
 {
 	set_flags(VFLAGS, flags, current->tss.v86mask);
 	set_flags(regs->eflags, flags, SAFE_MASK);
@@ -212,7 +285,7 @@
 		set_IF(regs);
 }
 
-static inline unsigned long get_vflags(struct vm86_regs * regs)
+static inline unsigned long get_vflags(struct kernel_vm86_regs * regs)
 {
 	unsigned long flags = regs->eflags & RETURN_MASK;
 
@@ -223,10 +296,10 @@
 
 static inline int is_revectored(int nr, struct revectored_struct * bitmap)
 {
-	unsigned long map;
-	if (get_user(map, bitmap->__map + (nr >> 5)))
-		return 1;
-	return test_bit(nr & ((1 << 5)-1), &map);
+	__asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0"
+		:"=r" (nr)
+		:"m" (*bitmap),"r" (nr));
+	return nr;
 }
 
 /*
@@ -302,15 +375,15 @@
 	: "0" (ptr), "1" (base)); \
 __res; })
 
-static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned long sp)
+static void do_int(struct kernel_vm86_regs *regs, int i, unsigned char * ssp, unsigned long sp)
 {
 	unsigned long *intr_ptr, segoffs;
-	
+
 	if (regs->cs == BIOSSEG)
 		goto cannot_handle;
-	if (is_revectored(i, &current->tss.vm86_info->int_revectored))
+	if (is_revectored(i, &KVM86->int_revectored))
 		goto cannot_handle;
-	if (i==0x21 && is_revectored(AH(regs),&current->tss.vm86_info->int21_revectored))
+	if (i==0x21 && is_revectored(AH(regs),&KVM86->int21_revectored))
 		goto cannot_handle;
 	intr_ptr = (unsigned long *) (i << 2);
 	if (get_user(segoffs, intr_ptr))
@@ -331,24 +404,40 @@
 	return_to_32bit(regs, VM86_INTx + (i << 8));
 }
 
-void handle_vm86_debug(struct vm86_regs * regs, long error_code)
+
+
+int handle_vm86_trap(struct kernel_vm86_regs * regs, long error_code, int trapno)
 {
-#if 0
-	do_int(regs, 1, (unsigned char *) (regs->ss << 4), SP(regs));
-#else
+	if (VMPI.is_vm86pus) {
+		if ( (trapno==3) || (trapno==1) )
+			return_to_32bit(regs, VM86_TRAP + (trapno << 8));
+		do_int(regs, trapno, (unsigned char *) (regs->ss << 4), SP(regs));
+		return 1;
+	}
+	if (trapno !=1)
+		return 0; /* we let this handle by the calling routine */
 	if (current->flags & PF_PTRACED)
 		current->blocked &= ~(1 << (SIGTRAP-1));
 	send_sig(SIGTRAP, current, 1);
-	current->tss.trap_no = 1;
+	current->tss.trap_no = trapno;
 	current->tss.error_code = error_code;
-#endif
+	return 0;
 }
 
-void handle_vm86_fault(struct vm86_regs * regs, long error_code)
+
+void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code)
 {
 	unsigned char *csp, *ssp;
 	unsigned long ip, sp;
 
+#define CHECK_IF_IN_TRAP \
+	if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \
+		pushw(ssp,sp,popw(ssp,sp) | TF_MASK);
+#define VM86_FAULT_RETURN \
+	if (VMPI.force_return_for_pic  && (VEFLAGS & IF_MASK)) \
+		return_to_32bit(regs, VM86_PICRETURN); \
+	return;
+	                                   
 	csp = (unsigned char *) (regs->cs << 4);
 	ssp = (unsigned char *) (regs->ss << 4);
 	sp = SP(regs);
@@ -365,58 +454,70 @@
 			SP(regs) -= 4;
 			IP(regs) += 2;
 			pushl(ssp, sp, get_vflags(regs));
-			return;
+			VM86_FAULT_RETURN;
 
 		/* popfd */
 		case 0x9d:
 			SP(regs) += 4;
 			IP(regs) += 2;
+			CHECK_IF_IN_TRAP
 			set_vflags_long(popl(ssp, sp), regs);
-			return;
+			VM86_FAULT_RETURN;
 
 		/* iretd */
 		case 0xcf:
 			SP(regs) += 12;
 			IP(regs) = (unsigned short)popl(ssp, sp);
 			regs->cs = (unsigned short)popl(ssp, sp);
+			CHECK_IF_IN_TRAP
 			set_vflags_long(popl(ssp, sp), regs);
-			return;
+			VM86_FAULT_RETURN;
+		/* need this to avoid a fallthrough */
+		default:
+			return_to_32bit(regs, VM86_UNKNOWN);
 		}
-		break;
 
 	/* pushf */
 	case 0x9c:
 		SP(regs) -= 2;
 		IP(regs)++;
 		pushw(ssp, sp, get_vflags(regs));
-		return;
+		VM86_FAULT_RETURN;
 
 	/* popf */
 	case 0x9d:
 		SP(regs) += 2;
 		IP(regs)++;
+		CHECK_IF_IN_TRAP
 		set_vflags_short(popw(ssp, sp), regs);
-		return;
+		VM86_FAULT_RETURN;
 
 	/* int xx */
-	case 0xcd:
+	case 0xcd: {
+	        int intno=popb(csp, ip);
 		IP(regs) += 2;
-		do_int(regs, popb(csp, ip), ssp, sp);
+		if (VMPI.vm86dbg_active) {
+			if ( (1 << (intno &7)) & VMPI.vm86dbg_intxxtab[intno >> 3] )
+				return_to_32bit(regs, VM86_INTx + (intno << 8));
+		}
+		do_int(regs, intno, ssp, sp);
 		return;
+	}
 
 	/* iret */
 	case 0xcf:
 		SP(regs) += 6;
 		IP(regs) = popw(ssp, sp);
 		regs->cs = popw(ssp, sp);
+		CHECK_IF_IN_TRAP
 		set_vflags_short(popw(ssp, sp), regs);
-		return;
+		VM86_FAULT_RETURN;
 
 	/* cli */
 	case 0xfa:
 		IP(regs)++;
 		clear_IF(regs);
-		return;
+		VM86_FAULT_RETURN;
 
 	/* sti */
 	/*
@@ -428,11 +529,122 @@
 	case 0xfb:
 		IP(regs)++;
 		set_IF(regs);
+		VM86_FAULT_RETURN;
+
+	default:
+		return_to_32bit(regs, VM86_UNKNOWN);
+	}
+}
+
+/* ---------------- vm86 special IRQ passing stuff ----------------- */
+
+#define VM86_IRQNAME		"vm86irq"
+
+static struct vm86_irqs {
+	struct task_struct *tsk;
+	int sig;
+} vm86_irqs[16] = {{0},}; 
+static int irqbits=0;
+
+#define ALLOWED_SIGS ( 1 /* 0 = don't send a signal */ \
+	| (1 << SIGUSR1) | (1 << SIGUSR2) | (1 << SIGIO)  | (1 << SIGURG) \
+	| (1 << SIGUNUSED) )
+	
+static void irq_handler(int intno, void *dev_id, struct pt_regs * regs) {
+	int irq_bit;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+	irq_bit = 1 << intno;
+	if ((irqbits & irq_bit) || ! vm86_irqs[intno].tsk) {
+		restore_flags(flags);
 		return;
 	}
+	irqbits |= irq_bit;
+	if (vm86_irqs[intno].sig)
+		send_sig(vm86_irqs[intno].sig, vm86_irqs[intno].tsk, 1);
+	/* else user will poll for IRQs */
+	restore_flags(flags);
+}
 
-	/*
-	 * We didn't recognize it, let the emulator take care of it..
-	 */
-	return_to_32bit(regs, VM86_UNKNOWN);
+static inline void free_vm86_irq(int irqnumber)
+{
+	free_irq(irqnumber,0);
+	vm86_irqs[irqnumber].tsk = 0;
+	irqbits &= ~(1 << irqnumber);
+}
+
+static inline int task_valid(struct task_struct *tsk)
+{
+	struct task_struct *p;
+
+	for_each_task(p) {
+		if ((p == tsk) && (p->sig)) return 1;
+	}
+	return 0;
+}
+
+static inline void handle_irq_zombies(void)
+{
+	int i;
+	for (i=3; i<16; i++) {
+		if (vm86_irqs[i].tsk) {
+			if (task_valid(vm86_irqs[i].tsk)) continue;
+			free_vm86_irq(i);
+		}
+	}
+}
+
+static inline int get_and_reset_irq(int irqnumber)
+{
+	int bit;
+	unsigned long flags;
+	
+	if ( (irqnumber<3) || (irqnumber>15) ) return 0;
+	if (vm86_irqs[irqnumber].tsk != current) return 0;
+	save_flags(flags);
+	cli();
+	bit = irqbits & (1 << irqnumber);
+	irqbits &= ~bit;
+	restore_flags(flags);
+	return bit;
+}
+
+
+static int do_vm86_irq_handling(int subfunction, int irqnumber)
+{
+	int ret;
+	switch (subfunction) {
+		case VM86_GET_AND_RESET_IRQ: {
+			return get_and_reset_irq(irqnumber);
+		}
+		case VM86_GET_IRQ_BITS: {
+			return irqbits;
+		}
+		case VM86_REQUEST_IRQ: {
+			int sig = irqnumber >> 8;
+			int irq = irqnumber & 255;
+			handle_irq_zombies();
+			if (!suser()) return -EPERM;
+			if (!((1 << sig) & ALLOWED_SIGS)) return -EPERM;
+			if ( (irq<3) || (irq>15) ) return -EPERM;
+			if (vm86_irqs[irq].tsk) return -EPERM;
+			ret = request_irq(irq, &irq_handler, 0, VM86_IRQNAME, 0);
+			if (ret) return ret;
+			vm86_irqs[irq].sig = sig;
+			vm86_irqs[irq].tsk = current;
+			return irq;
+		}
+		case  VM86_FREE_IRQ: {
+			handle_irq_zombies();
+			if ( (irqnumber<3) || (irqnumber>15) ) return -EPERM;
+			if (!vm86_irqs[irqnumber].tsk) return 0;
+			if (vm86_irqs[irqnumber].tsk != current) return -EPERM;
+			free_vm86_irq(irqnumber);
+			return 0;
+		}
+	}
+	return -EINVAL;
 }
+
diff -u --recursive --new-file v2.1.14/linux/arch/i386/lib/checksum.c linux/arch/i386/lib/checksum.c
--- v2.1.14/linux/arch/i386/lib/checksum.c	Mon Sep 23 11:26:30 1996
+++ linux/arch/i386/lib/checksum.c	Thu Dec 12 16:51:08 1996
@@ -70,8 +70,7 @@
 2:	    movl %%edx, %%ecx
 	    andl $0x1c, %%edx
 	    je 4f
-	    shrl $2, %%edx
-	    testl %%esi, %%esi
+	    shrl $2, %%edx	# This clears CF
 3:	    adcl (%%esi), %%eax
 	    lea 4(%%esi), %%esi
 	    dec %%edx
@@ -117,52 +116,48 @@
 	addw %%bx, %%ax
 	adcl $0, %%eax
 2:
-	movl %%ecx, %%edx
+	pushl %%ecx
 	shrl $5, %%ecx
 	jz 2f
 	testl %%esi, %%esi
 1:	movl (%%esi), %%ebx
+	movl 4(%%esi), %%edx
 	adcl %%ebx, %%eax
 	movl %%ebx, (%%edi)
-
-	movl 4(%%esi), %%ebx
-	adcl %%ebx, %%eax
-	movl %%ebx, 4(%%edi)
+	adcl %%edx, %%eax
+	movl %%edx, 4(%%edi)
 
 	movl 8(%%esi), %%ebx
+	movl 12(%%esi), %%edx
 	adcl %%ebx, %%eax
 	movl %%ebx, 8(%%edi)
-
-	movl 12(%%esi), %%ebx
-	adcl %%ebx, %%eax
-	movl %%ebx, 12(%%edi)
+	adcl %%edx, %%eax
+	movl %%edx, 12(%%edi)
 
 	movl 16(%%esi), %%ebx
+	movl 20(%%esi), %%edx
 	adcl %%ebx, %%eax
 	movl %%ebx, 16(%%edi)
-
-	movl 20(%%esi), %%ebx
-	adcl %%ebx, %%eax
-	movl %%ebx, 20(%%edi)
+	adcl %%edx, %%eax
+	movl %%edx, 20(%%edi)
 
 	movl 24(%%esi), %%ebx
+	movl 28(%%esi), %%edx
 	adcl %%ebx, %%eax
 	movl %%ebx, 24(%%edi)
-
-	movl 28(%%esi), %%ebx
-	adcl %%ebx, %%eax
-	movl %%ebx, 28(%%edi)
+	adcl %%edx, %%eax
+	movl %%edx, 28(%%edi)
 
 	lea 32(%%esi), %%esi
 	lea 32(%%edi), %%edi
 	dec %%ecx
 	jne 1b
 	adcl $0, %%eax
-2:	movl %%edx, %%ecx
-	andl $28, %%edx
+2:	popl %%edx
+	movl %%edx, %%ecx
+	andl $0x1c, %%edx
 	je 4f
-	shrl $2, %%edx
-	testl %%esi, %%esi
+	shrl $2, %%edx		# This clears CF
 3:	movl (%%esi), %%ebx
 	adcl %%ebx, %%eax
 	movl %%ebx, (%%edi)
diff -u --recursive --new-file v2.1.14/linux/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- v2.1.14/linux/arch/i386/mm/fault.c	Fri Nov  1 17:13:14 1996
+++ linux/arch/i386/mm/fault.c	Thu Dec 12 16:51:08 1996
@@ -165,7 +165,7 @@
 
 	/* Are we prepared to handle this fault?  */
 	if ((fixup = search_exception_table(regs->eip)) != 0) {
-		printk("Exception at %lx (%lx)\n", regs->eip, fixup);
+		printk(KERN_DEBUG "Exception at %lx (%lx)\n", regs->eip, fixup);
 		regs->eip = fixup;
 		return;
 	}
diff -u --recursive --new-file v2.1.14/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.1.14/linux/arch/i386/mm/init.c	Tue Oct 29 19:58:02 1996
+++ linux/arch/i386/mm/init.c	Thu Dec 12 16:51:08 1996
@@ -152,7 +152,6 @@
 		 * Intel Pentium cpu, unfortunately the SMP kernel can't
 		 * handle the 4MB page table optimizations yet
 		 */
-#ifndef __SMP__
 		/*
 		 * This will create page tables that
 		 * span up to the next 4MB virtual
@@ -177,7 +176,6 @@
 			address += 4*1024*1024;
 			continue;
 		}
-#endif
 		/* map the memory at virtual addr 0xC0000000 */
 		/* pg_table is physical at this point */
 		pg_table = (pte_t *) (PAGE_MASK & pgd_val(pg_dir[768]));
@@ -224,6 +222,9 @@
 #ifdef __SMP__
 	/*
 	 * But first pinch a few for the stack/trampoline stuff
+	 *	FIXME: Don't need the extra page at 4K, but need to fix
+	 *	trampoline before removing it. (see the GDT stuff)
+	 *
 	 */
 	start_low_mem += PAGE_SIZE;				/* 32bit startup code */
 	start_low_mem = smp_alloc_memory(start_low_mem); 	/* AP processor stacks */
diff -u --recursive --new-file v2.1.14/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.1.14/linux/drivers/block/Config.in	Sun Nov 10 20:12:08 1996
+++ linux/drivers/block/Config.in	Thu Dec 12 16:51:08 1996
@@ -15,6 +15,7 @@
   dep_tristate '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE
   dep_tristate '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE
   dep_tristate '   Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
+  dep_tristate '   SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE
   if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
     bool '   CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
     if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
@@ -40,6 +41,9 @@
       bool '      UMC 8672        support' CONFIG_BLK_DEV_UMC8672
     fi
   fi
+fi
+if [ "$CONFIG_MCA" = "y" ]; then
+  bool 'PS/2 ESDI harddisk support' CONFIG_BLK_DEV_PS2
 fi
 
 comment 'Additional Block Devices'
diff -u --recursive --new-file v2.1.14/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.1.14/linux/drivers/block/Makefile	Tue Nov 12 15:56:05 1996
+++ linux/drivers/block/Makefile	Thu Dec 12 16:51:08 1996
@@ -72,6 +72,11 @@
 L_OBJS += triton.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_PS2),y)
+L_OBJS += ps2esdi.o
+endif
+
+
 ifeq ($(CONFIG_BLK_DEV_DTC2278),y)
 L_OBJS += dtc2278.o
 endif
diff -u --recursive --new-file v2.1.14/linux/drivers/block/hd.c linux/drivers/block/hd.c
--- v2.1.14/linux/drivers/block/hd.c	Tue Oct 29 19:58:03 1996
+++ linux/drivers/block/hd.c	Thu Dec 12 16:51:08 1996
@@ -17,11 +17,15 @@
  *
  *  IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
  *  and general streamlining by Mark Lord.
+ *
+ *  Removed 99% of above. Use Mark's ide driver for those options.
+ *  This is now a lightweight ST-506 driver. (Paul Gortmaker)
+ *
  */
-
-#define DEFAULT_MULT_COUNT  0	/* set to 0 to disable multiple mode at boot */
-#define DEFAULT_UNMASK_INTR 0	/* set to 0 to *NOT* unmask irq's more often */
-
+  
+/* Uncomment the following if you want verbose error reports. */
+/* #define VERBOSE_ERRORS */
+  
 #include <asm/irq.h>
 #include <linux/errno.h>
 #include <linux/signal.h>
@@ -73,16 +77,13 @@
  */
 struct hd_i_struct {
 	unsigned int head,sect,cyl,wpcom,lzone,ctl;
-	};
-static struct hd_driveid *hd_ident_info[MAX_HD] = {0, };
+};
 	
 #ifdef HD_TYPE
 static struct hd_i_struct hd_info[] = { HD_TYPE };
-struct hd_i_struct bios_info[] = { HD_TYPE };
 static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct)));
 #else
 static struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
-struct hd_i_struct bios_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
 static int NR_HD = 0;
 #endif
 
@@ -115,14 +116,14 @@
 
 	if (ints[0] != 3)
 		return;
-	if (bios_info[0].head != 0)
+	if (hd_info[0].head != 0)
 		hdind=1;
-	bios_info[hdind].head  = hd_info[hdind].head = ints[2];
-	bios_info[hdind].sect  = hd_info[hdind].sect = ints[3];
-	bios_info[hdind].cyl   = hd_info[hdind].cyl = ints[1];
-	bios_info[hdind].wpcom = hd_info[hdind].wpcom = 0;
-	bios_info[hdind].lzone = hd_info[hdind].lzone = ints[1];
-	bios_info[hdind].ctl   = hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+	hd_info[hdind].head = ints[2];
+	hd_info[hdind].sect = ints[3];
+	hd_info[hdind].cyl = ints[1];
+	hd_info[hdind].wpcom = 0;
+	hd_info[hdind].lzone = ints[1];
+	hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
 	NR_HD = hdind+1;
 }
 
@@ -134,6 +135,7 @@
 	devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
 	save_flags (flags);
 	sti();
+#ifdef VERBOSE_ERRORS
 	printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
 	if (stat & BUSY_STAT)	printk("Busy ");
 	if (stat & READY_STAT)	printk("DriveReady ");
@@ -164,6 +166,15 @@
 		}
 		printk("\n");
 	}
+#else
+	printk("hd%c: %s: status=0x%02x.\n", devc, msg, stat & 0xff);
+	if ((stat & ERR_STAT) == 0) {
+		hd_error = 0;
+	} else {
+		hd_error = inb(HD_ERROR);
+		printk("hd%c: %s: error=0x%02x.\n", devc, msg, hd_error & 0xff);
+	}
+#endif	/* verbose errors */
 	restore_flags (flags);
 }
 
@@ -246,113 +257,6 @@
 }
 
 static void hd_request (void);
-static unsigned int identified  [MAX_HD] = {0,}; /* 1 = drive ID already displayed   */
-static unsigned int unmask_intr [MAX_HD] = {0,}; /* 1 = unmask IRQs during I/O       */
-static unsigned int max_mult    [MAX_HD] = {0,}; /* max sectors for MultMode         */
-static unsigned int mult_req    [MAX_HD] = {0,}; /* requested MultMode count         */
-static unsigned int mult_count  [MAX_HD] = {0,}; /* currently enabled MultMode count */
-static struct request WCURRENT;
-
-static void fixstring (unsigned char *s, int bytecount)
-{
-	unsigned char *p, *end = &s[bytecount &= ~1];	/* bytecount must be even */
-
-	/* convert from big-endian to little-endian */
-	for (p = end ; p != s;) {
-		unsigned short *pp = (unsigned short *) (p -= 2);
-		*pp = (*pp >> 8) | (*pp << 8);
-	}
-
-	/* strip leading blanks */
-	while (s != end && *s == ' ')
-		++s;
-
-	/* compress internal blanks and strip trailing blanks */
-	while (s != end && *s) {
-		if (*s++ != ' ' || (s != end && *s && *s != ' '))
-			*p++ = *(s-1);
-	}
-
-	/* wipe out trailing garbage */
-	while (p != end)
-		*p++ = '\0';
-}
-
-static void identify_intr(void)
-{
-	unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
-	unsigned short stat = inb_p(HD_STATUS);
-	struct hd_driveid *id = hd_ident_info[dev];
-
-	if (unmask_intr[dev])
-		sti();
-	if (stat & (BUSY_STAT|ERR_STAT)) {
-		printk ("  hd%c: non-IDE device, %dMB, CHS=%d/%d/%d\n", dev+'a',
-			hd_info[dev].cyl*hd_info[dev].head*hd_info[dev].sect / 2048,
-			hd_info[dev].cyl, hd_info[dev].head, hd_info[dev].sect);
-		if (id != NULL) {
-			hd_ident_info[dev] = NULL;
-			kfree_s (id, 512);
-		}
-	} else {
-		insw(HD_DATA, id, 256); /* get ID info */
-		max_mult[dev] = id->max_multsect;
-		if ((id->field_valid&1) && id->cur_cyls && id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) {
-			/*
-			 * Extract the physical drive geometry for our use.
-			 * Note that we purposely do *not* update the bios_info.
-			 * This way, programs that use it (like fdisk) will 
-			 * still have the same logical view as the BIOS does,
-			 * which keeps the partition table from being screwed.
-			 */
-			hd_info[dev].cyl  = id->cur_cyls;
-			hd_info[dev].head = id->cur_heads;
-			hd_info[dev].sect = id->cur_sectors; 
-		}
-		fixstring (id->serial_no, sizeof(id->serial_no));
-		fixstring (id->fw_rev, sizeof(id->fw_rev));
-		fixstring (id->model, sizeof(id->model));
-		printk ("  hd%c: %.40s, %dMB w/%dKB Cache, CHS=%d/%d/%d, MaxMult=%d\n",
-			dev+'a', id->model, id->cyls*id->heads*id->sectors/2048,
-			id->buf_size/2, bios_info[dev].cyl, bios_info[dev].head,
-			bios_info[dev].sect, id->max_multsect);
-		/*
-		 * Early model Quantum drives go weird at this point,
-		 *   but doing a recalibrate seems to "fix" them.
-		 * (Doing a full reset confuses some other model Quantums)
-		 */
-		if (!strncmp(id->model, "QUANTUM", 7))
-			special_op[dev] = recalibrate[dev] = 1;
-	}
-#if (HD_DELAY > 0)
-	last_req = read_timer();
-#endif
-	hd_request();
-	return;
-}
-
-static void set_multmode_intr(void)
-{
-	unsigned int dev = DEVICE_NR(CURRENT->rq_dev), stat = inb_p(HD_STATUS);
-
-	if (unmask_intr[dev])
-		sti();
-	if (stat & (BUSY_STAT|ERR_STAT)) {
-		mult_req[dev] = mult_count[dev] = 0;
-		dump_status("set multmode failed", stat);
-	} else {
-		if ((mult_count[dev] = mult_req[dev]))
-			printk ("  hd%c: enabled %d-sector multiple mode\n",
-				dev+'a', mult_count[dev]);
-		else
-			printk ("  hd%c: disabled multiple mode\n", dev+'a');
-	}
-#if (HD_DELAY > 0)
-	last_req = read_timer();
-#endif
-	hd_request();
-	return;
-}
 
 static int drive_busy(void)
 {
@@ -398,17 +302,6 @@
 	}
 	if (++i < NR_HD) {
 		special_op[i] = recalibrate[i] = 1;
-		if (unmask_intr[i]) {
-			unmask_intr[i] = DEFAULT_UNMASK_INTR;
-			printk("hd%c: reset irq-unmasking to %d\n",i+'a',
-				DEFAULT_UNMASK_INTR);
-		}
-		if (mult_req[i] || mult_count[i]) {
-			mult_count[i] = 0;
-			mult_req[i] = DEFAULT_MULT_COUNT;
-			printk("hd%c: reset multiple mode to %d\n",i+'a',
-				DEFAULT_MULT_COUNT);
-		}
 		hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
 			hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
 		if (reset)
@@ -471,11 +364,8 @@
 
 static void read_intr(void)
 {
-	unsigned int dev = DEVICE_NR(CURRENT->rq_dev);
-	int i, retries = 100000, msect = mult_count[dev], nsect;
+	int i, retries = 100000;
 
-	if (unmask_intr[dev])
-		sti();			/* permit other IRQs during xfer */
 	do {
 		i = (unsigned) inb_p(HD_STATUS);
 		if (i & BUSY_STAT)
@@ -490,28 +380,20 @@
 	hd_request();
 	return;
 ok_to_read:
-	if (msect) {
-		if ((nsect = CURRENT->current_nr_sectors) > msect)
-			nsect = msect;
-		msect -= nsect;
-	} else
-		nsect = 1;
-	insw(HD_DATA,CURRENT->buffer,nsect<<8);
-	CURRENT->sector += nsect;
-	CURRENT->buffer += nsect<<9;
+	insw(HD_DATA,CURRENT->buffer,256);
+	CURRENT->sector++;
+	CURRENT->buffer += 512;
 	CURRENT->errors = 0;
-	i = (CURRENT->nr_sectors -= nsect);
-
+	i = --CURRENT->nr_sectors;
+	--CURRENT->current_nr_sectors;
 #ifdef DEBUG
-	printk("hd%c: read: sectors(%ld-%ld), remaining=%ld, buffer=0x%08lx\n",
-		dev+'a', CURRENT->sector, CURRENT->sector+nsect,
-		CURRENT->nr_sectors, (unsigned long) CURRENT->buffer+(nsect<<9));
+	printk("hd%c: read: sector %ld, remaining = %ld, buffer=0x%08lx\n",
+		dev+'a', CURRENT->sector, CURRENT->nr_sectors,
+		(unsigned long) CURRENT->buffer+512));
 #endif
-	if ((CURRENT->current_nr_sectors -= nsect) <= 0)
+	if (CURRENT->current_nr_sectors <= 0)
 		end_request(1);
 	if (i > 0) {
-		if (msect)
-			goto ok_to_read;
 		SET_INTR(&read_intr);
 		return;
 	}
@@ -524,66 +406,11 @@
 	return;
 }
 
-static inline void multwrite (unsigned int dev)
-{
-	unsigned int mcount = mult_count[dev];
-
-	while (mcount--) {
-		outsw(HD_DATA,WCURRENT.buffer,256);
-		if (!--WCURRENT.nr_sectors)
-			return;
-		WCURRENT.buffer += 512;
-		if (!--WCURRENT.current_nr_sectors) {
-			WCURRENT.bh = WCURRENT.bh->b_reqnext;
-			if (WCURRENT.bh == NULL)
-				panic("buffer list corrupted\n");
-			WCURRENT.current_nr_sectors = WCURRENT.bh->b_size>>9;
-			WCURRENT.buffer             = WCURRENT.bh->b_data;
-		}
-	}
-}
-
-static void multwrite_intr(void)
-{
-	int i;
-	unsigned int dev = DEVICE_NR(WCURRENT.rq_dev);
-
-	if (unmask_intr[dev])
-		sti();
-	if (OK_STATUS(i=inb_p(HD_STATUS))) {
-		if (i & DRQ_STAT) {
-			if (WCURRENT.nr_sectors) {
-				multwrite(dev);
-				SET_INTR(&multwrite_intr);
-				return;
-			}
-		} else {
-			if (!WCURRENT.nr_sectors) {	/* all done? */
-				for (i = CURRENT->nr_sectors; i > 0;){
-					i -= CURRENT->current_nr_sectors;
-					end_request(1);
-				}
-#if (HD_DELAY > 0)
-				last_req = read_timer();
-#endif
-				if (CURRENT)
-					hd_request();
-				return;
-			}
-		}
-	}
-	dump_status("multwrite_intr", i);
-	bad_rw_intr();
-	hd_request();
-}
-
 static void write_intr(void)
 {
 	int i;
 	int retries = 100000;
 
-	if (unmask_intr[DEVICE_NR(WCURRENT.rq_dev)])
-		sti();
 	do {
 		i = (unsigned) inb_p(HD_STATUS);
 		if (i & BUSY_STAT)
@@ -660,17 +487,6 @@
 		hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
 		return reset;
 	}
-	if (!identified[dev]) {
-		identified[dev]  = 1;
-		unmask_intr[dev] = DEFAULT_UNMASK_INTR;
-		mult_req[dev]    = DEFAULT_MULT_COUNT;
-		hd_out(dev,0,0,0,0,WIN_IDENTIFY,&identify_intr);
-		return reset;
-	}
-	if (mult_req[dev] != mult_count[dev]) {
-		hd_out(dev,mult_req[dev],0,0,0,WIN_SETMULT,&set_multmode_intr);
-		return reset;
-	}
 	if (hd_info[dev].head > 16) {
 		printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a');
 		end_request(0);
@@ -686,10 +502,8 @@
  *
  * Interrupts are still masked (by default) whenever we are exchanging
  * data/cmds with a drive, because some drives seem to have very poor
- * tolerance for latency during I/O.  For devices which don't suffer from
- * that problem (most don't), the unmask_intr[] flag can be set to unmask
- * other interrupts during data/cmd transfers (by defining DEFAULT_UNMASK_INTR
- * to 1, or by using "hdparm -u1 /dev/hd?" from the shell).
+ * tolerance for latency during I/O. The IDE driver has support to unmask
+ * interrupts for non-broken hardware, so use that driver if required.
  */
 static void hd_request(void)
 {
@@ -738,31 +552,21 @@
 		dev+'a', (CURRENT->cmd == READ)?"read":"writ",
 		cyl, head, sec, nsect, (unsigned long) CURRENT->buffer);
 #endif
-	if (!unmask_intr[dev])
-		cli();
 	if (CURRENT->cmd == READ) {
-		unsigned int cmd = mult_count[dev] > 1 ? WIN_MULTREAD : WIN_READ;
-		hd_out(dev,nsect,sec,head,cyl,cmd,&read_intr);
+		hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
 		if (reset)
 			goto repeat;
 		return;
 	}
 	if (CURRENT->cmd == WRITE) {
-		if (mult_count[dev])
-			hd_out(dev,nsect,sec,head,cyl,WIN_MULTWRITE,&multwrite_intr);
-		else
-			hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
+		hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
 		if (reset)
 			goto repeat;
 		if (wait_DRQ()) {
 			bad_rw_intr();
 			goto repeat;
 		}
-		if (mult_count[dev]) {
-			WCURRENT = *CURRENT;
-			multwrite(dev);
-		} else
-			outsw(HD_DATA,CURRENT->buffer,256);
+		outsw(HD_DATA,CURRENT->buffer,256);
 		return;
 	}
 	panic("unknown hd-command");
@@ -780,7 +584,6 @@
 {
 	struct hd_geometry *loc = (struct hd_geometry *) arg;
 	int dev, err;
-	unsigned long flags;
 
 	if ((!inode) || !(inode->i_rdev))
 		return -EINVAL;
@@ -793,11 +596,11 @@
 			err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
 			if (err)
 				return err;
-			put_user(bios_info[dev].head,
+			put_user(hd_info[dev].head,
 				(char *) &loc->heads);
-			put_user(bios_info[dev].sect,
+			put_user(hd_info[dev].sect,
 				(char *) &loc->sectors);
-			put_user(bios_info[dev].cyl,
+			put_user(hd_info[dev].cyl,
 				(short *) &loc->cylinders);
 			put_user(hd[MINOR(inode->i_rdev)].start_sect,
 				(long *) &loc->start);
@@ -830,57 +633,6 @@
 		case BLKRRPART: /* Re-read partition tables */
 			return revalidate_hddisk(inode->i_rdev, 1);
 
-		case HDIO_SET_UNMASKINTR:
-			if (!suser()) return -EACCES;
-			if ((arg > 1) || (MINOR(inode->i_rdev) & 0x3F))
-				return -EINVAL;
-			unmask_intr[dev] = arg;
-			return 0;
-
-                case HDIO_GET_UNMASKINTR:
-			if (!arg)  return -EINVAL;
-			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
-			if (err)
-				return err;
-			put_user(unmask_intr[dev], (long *) arg);
-			return 0;
-
-                case HDIO_GET_MULTCOUNT:
-			if (!arg)  return -EINVAL;
-			err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
-			if (err)
-				return err;
-			put_user(mult_count[dev], (long *) arg);
-			return 0;
-
-		case HDIO_SET_MULTCOUNT:
-			if (!suser()) return -EACCES;
-			if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
-			save_flags(flags);
-			cli();	/* a prior request might still be in progress */
-			if (arg > max_mult[dev])
-				err = -EINVAL;	/* out of range for device */
-			else if (mult_req[dev] != mult_count[dev]) {
-				special_op[dev] = 1;
-				err = -EBUSY;	/* busy, try again */
-			} else {
-				mult_req[dev] = arg;
-				special_op[dev] = 1;
-				err = 0;
-			}
-			restore_flags(flags);
-			return err;
-
-		case HDIO_GET_IDENTITY:
-			if (!arg)  return -EINVAL;
-			if (MINOR(inode->i_rdev) & 0x3F) return -EINVAL;
-			if (hd_ident_info[dev] == NULL)  return -ENOMSG;
-			err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(struct hd_driveid));
-			if (err)
-				return err;
-			copy_to_user((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
-			return 0;
-
 		RO_IOCTLS(inode->i_rdev,arg);
 		default:
 			return -EINVAL;
@@ -926,7 +678,7 @@
 	hd,		/* hd struct */
 	hd_sizes,	/* block sizes */
 	0,		/* number */
-	(void *) bios_info,	/* internal */
+	NULL,		/* internal use, not presently used */
 	NULL		/* next */
 };
 	
@@ -953,21 +705,21 @@
  */
 static void hd_geninit(struct gendisk *ignored)
 {
-	int i;
+	int drive;
 
 #ifdef __i386__
 	if (!NR_HD) {
 		extern struct drive_info drive_info;
 		unsigned char *BIOS = (unsigned char *) &drive_info;
-		int cmos_disks, drive;
+		int cmos_disks;
 
 		for (drive=0 ; drive<2 ; drive++) {
-			bios_info[drive].cyl   = hd_info[drive].cyl = *(unsigned short *) BIOS;
-			bios_info[drive].head  = hd_info[drive].head = *(2+BIOS);
-			bios_info[drive].wpcom = hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
-			bios_info[drive].ctl   = hd_info[drive].ctl = *(8+BIOS);
-			bios_info[drive].lzone = hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
-			bios_info[drive].sect  = hd_info[drive].sect = *(14+BIOS);
+			hd_info[drive].cyl = *(unsigned short *) BIOS;
+			hd_info[drive].head = *(2+BIOS);
+			hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
+			hd_info[drive].ctl = *(8+BIOS);
+			hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
+			hd_info[drive].sect = *(14+BIOS);
 #ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp
 			if (hd_info[drive].cyl && NR_HD == drive)
 				NR_HD++;
@@ -1004,20 +756,12 @@
 				NR_HD = 1;
 	}
 #endif /* __i386__ */
-	i = NR_HD;
-	while (i-- > 0) {
-		/*
-		 * The newer E-IDE BIOSs handle drives larger than 1024
-		 * cylinders by increasing the number of logical heads
-		 * to keep the number of logical cylinders below the
-		 * sacred INT13 limit of 1024 (10 bits).  If that is
-		 * what's happening here, we'll find out and correct
-		 * it later when "identifying" the drive.
-		 */
-		hd[i<<6].nr_sects = bios_info[i].head *
-				bios_info[i].sect * bios_info[i].cyl;
-		hd_ident_info[i] = (struct hd_driveid *) kmalloc(512,GFP_KERNEL);
-		special_op[i] = 1;
+	for (drive=0 ; drive < NR_HD ; drive++) {
+		hd[drive<<6].nr_sects = hd_info[drive].head *
+			hd_info[drive].sect * hd_info[drive].cyl;
+		printk ("hd%c: %ldMB, CHS=%d/%d/%d\n", drive+'a',
+			hd[drive<<6].nr_sects / 2048, hd_info[drive].cyl,
+			hd_info[drive].head, hd_info[drive].sect);
 	}
 	if (NR_HD) {
 		if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
@@ -1030,7 +774,8 @@
 	}
 	hd_gendisk.nr_real = NR_HD;
 
-	for(i=0;i<(MAX_HD << 6);i++) hd_blocksizes[i] = 1024;
+	for(drive=0; drive < (MAX_HD << 6); drive++)
+		hd_blocksizes[drive] = 1024;
 	blksize_size[MAJOR_NR] = hd_blocksizes;
 }
 
@@ -1063,7 +808,7 @@
 
 #define DEVICE_BUSY busy[target]
 #define USAGE access_count[target]
-#define CAPACITY (bios_info[target].head*bios_info[target].sect*bios_info[target].cyl)
+#define CAPACITY (hd_info[target].head*hd_info[target].sect*hd_info[target].cyl)
 /* We assume that the the bios parameters do not change, so the disk capacity
    will not change */
 #undef MAYBE_REINIT
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.14/linux/drivers/block/ide-cd.c	Thu Dec 12 17:02:40 1996
+++ linux/drivers/block/ide-cd.c	Wed Dec 11 16:45:54 1996
@@ -143,6 +143,7 @@
  * 4.02  Dec 01, 1996  -- Applied patch from Gadi Oxman <gadio@netvision.net.il>
  *                          to fix the drive door locking problems.
  *
+ * 4.03  Dec 04, 1996  -- Added DSC overlap support.
  *
  * MOSTLY DONE LIST:
  *  Query the drive to find what features are available
@@ -969,6 +970,53 @@
 					      &cdrom_read_intr);
 }
 
+#define IDECD_SEEK_THRESHOLD	(1000)			/* 1000 blocks */
+#define IDECD_SEEK_TIMER	(2 * WAIT_MIN_SLEEP)	/* 40 ms */
+#define IDECD_SEEK_TIMEOUT	(20 * IDECD_SEEK_TIMER) /* 0.8 sec */
+
+static void cdrom_seek_intr (ide_drive_t *drive)
+{
+	struct cdrom_info *info = drive->driver_data;
+	int stat;
+	static int retry = 10;
+
+	if (cdrom_decode_status (drive, 0, &stat)) return;
+	CDROM_CONFIG_FLAGS(drive)->seeking = 1;
+
+	if (retry && jiffies - info->start_seek > IDECD_SEEK_TIMER) {
+		if (--retry == 0) {
+			printk ("%s: disabled DSC seek overlap\n", drive->name);
+			drive->dsc_overlap = 0;
+		}
+	}
+}
+
+static void cdrom_start_seek_continuation (ide_drive_t *drive)
+{
+	struct packet_command pc;
+	struct request *rq = HWGROUP(drive)->rq;
+	int sector, frame, nskip;
+
+	sector = rq->sector;
+	nskip = (sector % SECTORS_PER_FRAME);
+	if (nskip > 0)
+		sector -= nskip;
+	frame = sector / SECTORS_PER_FRAME;
+
+	memset (&pc.c, 0, sizeof (pc.c));
+	pc.c[0] = SEEK;
+	put_unaligned(htonl (frame), (unsigned int *) &pc.c[2]);
+	(void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c), &cdrom_seek_intr);
+}
+
+static void cdrom_start_seek (ide_drive_t *drive, unsigned int block)
+{
+	struct cdrom_info *info = drive->driver_data;
+
+	info->dma = 0;
+	info->start_seek = jiffies;
+	cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation);
+}
 
 /*
  * Start a read request from the CD-ROM.
@@ -1250,8 +1298,28 @@
 	} else if (rq -> cmd != READ) {
 		printk ("ide-cd: bad cmd %d\n", rq -> cmd);
 		cdrom_end_request (0, drive);
-	} else
-		cdrom_start_read (drive, block);
+	} else {
+		struct cdrom_info *info = drive->driver_data;
+
+		if (CDROM_CONFIG_FLAGS(drive)->seeking) {
+			unsigned long elpased = jiffies - info->start_seek;
+			int stat = GET_STAT();
+
+			if ((stat & SEEK_STAT) != SEEK_STAT) {
+				if (elpased < IDECD_SEEK_TIMEOUT) {
+					ide_stall_queue (drive, IDECD_SEEK_TIMER);
+					return;
+				}
+				printk ("%s: DSC timeout\n", drive->name);
+			}
+			CDROM_CONFIG_FLAGS(drive)->seeking = 0;
+		}
+		if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap)
+			cdrom_start_seek (drive, block);
+		else
+			cdrom_start_read (drive, block);
+		info->last_block = block;
+	}
 }
 
 
@@ -2831,6 +2899,7 @@
 	ide_cdrom,			/* media */
 	0,				/* busy */
 	1,				/* supports_dma */
+	1,				/* supports_dsc_overlap */
 	ide_cdrom_cleanup,		/* cleanup */
 	ide_do_rw_cdrom,		/* do_request */
 	NULL,				/* ??? or perhaps
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.1.14/linux/drivers/block/ide-cd.h	Tue Nov 12 15:56:05 1996
+++ linux/drivers/block/ide-cd.h	Wed Dec 11 16:45:54 1996
@@ -76,6 +76,7 @@
 #define ALLOW_MEDIUM_REMOVAL    0x1e
 #define READ_CAPACITY		0x25
 #define READ_10                 0x28
+#define SEEK			0x2b
 #define MODE_SENSE_10           0x5a
 #define MODE_SELECT_10          0x55
 #define READ_CD                 0xbe
@@ -125,7 +126,8 @@
 	__u8 is_changer       : 1; /* Drive is a changer. */
 	__u8 supp_disc_present: 1; /* Changer can report exact contents
 				      of slots. */
-	__u8 reserved         : 7;
+	__u8 seeking          : 1; /* Seeking in progress */
+	__u8 reserved         : 6;
 };
 #define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_cyl))
 
@@ -383,6 +385,8 @@
 	struct request request_sense_request;
 	struct packet_command request_sense_pc;
 	int dma;
+	unsigned long last_block;
+	unsigned long start_seek;
 	/* Buffer to hold mechanism status and changer slot table. */
 	struct atapi_changer_info *changer_info;
 
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.1.14/linux/drivers/block/ide-disk.c	Sun Nov 10 20:12:09 1996
+++ linux/drivers/block/ide-disk.c	Wed Dec 11 16:45:54 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-disk.c	Version 1.0  Oct   6, 1996
+ *  linux/drivers/block/ide-disk.c	Version 1.01  Nov  25, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -36,8 +36,9 @@
  *  code is still sprinkled about.  Think of it as a major evolution, with
  *  inspiration from lots of linux users, esp.  hamish@zot.apana.org.au
  *
- * Version 1.0		move disk only code from ide.c to ide-disk.c
+ * Version 1.00		move disk only code from ide.c to ide-disk.c
  *			support optional byte-swapping of all data
+ * Version 1.01		fix previous byte-swapping code
  */
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
@@ -85,9 +86,12 @@
 
 static inline void idedisk_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	ide_output_data(drive, buffer, wcount);
-	if (drive->bswap)
+	if (drive->bswap) {
+		idedisk_bswap_data(buffer, wcount);
+		ide_output_data(drive, buffer, wcount);
 		idedisk_bswap_data(buffer, wcount);
+	} else
+		ide_output_data(drive, buffer, wcount);
 }
 
 /*
@@ -501,6 +505,7 @@
 	ide_disk,		/* media */
 	0,			/* busy */
 	1,			/* supports_dma */
+	0,			/* supports_dsc_overlap */
 	NULL,			/* cleanup */
 	do_rw_disk,		/* do_request */
 	NULL,			/* end_request */
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.1.14/linux/drivers/block/ide-floppy.c	Sun Nov 10 20:12:09 1996
+++ linux/drivers/block/ide-floppy.c	Wed Dec 11 16:45:54 1996
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-floppy.c	Version 0.2 - ALPHA	Oct  31, 1996
+ * linux/drivers/block/ide-floppy.c	Version 0.3 - ALPHA	Dec   2, 1996
  *
  * Copyright (C) 1996 Gadi Oxman <gadio@netvision.net.il>
  */
@@ -15,6 +15,7 @@
  *
  * Ver 0.1   Oct 17 96   Initial test version, mostly based on ide-tape.c.
  * Ver 0.2   Oct 31 96   Minor changes.
+ * Ver 0.3   Dec  2 96   Fixed error recovery bug.
  */
 
 #include <linux/config.h>
@@ -451,21 +452,26 @@
 static void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
 {
 	ide_drive_t *drive = hwgroup->drive;
+	idefloppy_floppy_t *floppy = drive->driver_data;
 	struct request *rq = hwgroup->rq;
+	int error;
 
 #if IDEFLOPPY_DEBUG_LOG
 	printk (KERN_INFO "Reached idefloppy_end_request\n");
 #endif /* IDEFLOPPY_DEBUG_LOG */
 
+	switch (uptodate) {
+		case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
+		case 1: error = 0; break;
+		default: error = uptodate;
+	}
+	if (error)
+		floppy->failed_pc = NULL;
 	if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
 		ide_end_request (uptodate, hwgroup);
 		return;
 	}
-	switch (uptodate) {
-		case 0: rq->errors = IDEFLOPPY_ERROR_GENERAL; break;
-		case 1: rq->errors = 0; break;
-		default: rq->errors = uptodate;
-	}
+	rq->errors = error;
 	ide_end_drive_cmd (drive, 0, 0);
 }
 
@@ -967,8 +973,11 @@
 #endif /* IDEFLOPPY_DEBUG_LOG */
 
 	if (rq->errors >= ERROR_MAX) {
-		printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
-			drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+		if (floppy->failed_pc != NULL)
+			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+				drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+		else
+			printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
 		idefloppy_end_request (0, HWGROUP(drive));
 		return;
 	}
@@ -1367,6 +1376,7 @@
 	ide_floppy,		/* media */
 	0,			/* busy */
 	1,			/* supports_dma */
+	0,			/* supports_dsc_overlap */
 	idefloppy_cleanup,	/* cleanup */
 	idefloppy_do_request,	/* do_request */
 	idefloppy_end_request,	/* end_request */
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide-probe.c linux/drivers/block/ide-probe.c
--- v2.1.14/linux/drivers/block/ide-probe.c	Fri Nov 22 18:28:17 1996
+++ linux/drivers/block/ide-probe.c	Wed Dec 11 16:45:54 1996
@@ -535,14 +535,11 @@
 		hwgroup = match->hwgroup;
 	} else {
 		hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
-		hwgroup->hwif 	 = hwgroup->next_hwif = hwif->next = hwif;
+		memset(hwgroup, 0, sizeof(ide_hwgroup_t));
+		hwgroup->hwif 	 = hwif->next = hwif;
 		hwgroup->rq      = NULL;
 		hwgroup->handler = NULL;
-		if (hwif->drives[0].present)
-			hwgroup->drive = &hwif->drives[0];
-		else
-			hwgroup->drive = &hwif->drives[1];
-		hwgroup->poll_timeout = 0;
+		hwgroup->drive   = NULL;
 		init_timer(&hwgroup->timer);
 		hwgroup->timer.function = &ide_timer_expiry;
 		hwgroup->timer.data = (unsigned long) hwgroup;
@@ -567,6 +564,16 @@
 	hwif->next = hwgroup->hwif->next;
 	hwgroup->hwif->next = hwif;
 
+	for (index = 0; index < MAX_DRIVES; ++index) {
+		ide_drive_t *drive = &hwif->drives[index];
+		if (!drive->present)
+			continue;
+		if (!hwgroup->drive)
+			hwgroup->drive = drive;
+		drive->next = hwgroup->drive->next;
+		hwgroup->drive->next = drive;
+	}
+	hwgroup->hwif = HWIF(hwgroup->drive);
 	restore_flags(flags);	/* safe now that hwif->hwgroup is set up */
 
 #ifndef __mc68000__
@@ -671,7 +678,9 @@
 		(void) unregister_blkdev (hwif->major, hwif->name);
 	} else {
 		init_gendisk(hwif);
+		blk_dev[hwif->major].data = hwif;
 		blk_dev[hwif->major].request_fn = rfn;
+		blk_dev[hwif->major].queue = ide_get_queue;
 		read_ahead[hwif->major] = 8;	/* (4kB) */
 		hwif->present = 1;	/* success */
 	}
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide-tape.c linux/drivers/block/ide-tape.c
--- v2.1.14/linux/drivers/block/ide-tape.c	Sun Nov 10 20:12:09 1996
+++ linux/drivers/block/ide-tape.c	Wed Dec 11 16:45:55 1996
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/block/ide-tape.c	Version 1.10 - BETA	Nov   5, 1996
+ * linux/drivers/block/ide-tape.c	Version 1.11 - BETA	Dec   2, 1996
  *
  * Copyright (C) 1995, 1996 Gadi Oxman <gadio@netvision.net.il>
  *
@@ -202,6 +202,10 @@
  *                       Some portability fixes.
  *                       Removed ide-tape.h.
  *                       Additional minor changes.
+ * Ver 1.11  Dec  2 96   Bug fix in previous DSC timeout handling.
+ *                       Use ide_stall_queue() for DSC overlap.
+ *                       Use the maximum speed rather than the current speed
+ *                        to compute the request service time.
  *
  * Here are some words from the first releases of hd.c, which are quoted
  * in ide.c and apply here as well:
@@ -732,7 +736,8 @@
 /*
  * 	IDETAPE_PC_RQ is used to queue a packet command in the request queue.
  */
-#define IDETAPE_PC_RQ			90
+#define IDETAPE_PC_RQ1			90
+#define IDETAPE_PC_RQ2			91
 
 /*
  *	IDETAPE_READ_RQ and IDETAPE_WRITE_RQ are used by our
@@ -752,13 +757,6 @@
 #define IDETAPE_RQ_CMD(cmd) 		((cmd >= IDETAPE_FIRST_RQ) && (cmd <= IDETAPE_LAST_RQ))
 
 /*
- *	We are now able to postpone an idetape request in the stage
- *	where it is polling for DSC and service requests from the other
- *	ide device meanwhile.
- */
-#define	IDETAPE_RQ_POSTPONED		0x1234
-
-/*
  *	Error codes which are returned in rq->errors to the higher part
  *	of the driver.
  */
@@ -1019,9 +1017,6 @@
 	u8		reserved[2];		/* Reserved */
 } idetape_medium_partition_page_t;
 
-#define IDETAPE_MIN(a,b)	((a)<(b) ? (a):(b))
-#define	IDETAPE_MAX(a,b)	((a)>(b) ? (a):(b))
-
 /*
  *	Run time configurable parameters.
  */
@@ -1052,7 +1047,7 @@
 {
 	struct buffer_head *bh = pc->bh;
 	int count;
-	
+
 	while (bcount) {
 #if IDETAPE_DEBUG_BUGS
 		if (bh == NULL) {
@@ -1061,7 +1056,7 @@
 			return;
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
-		count = IDETAPE_MIN (bh->b_size - bh->b_count, bcount);
+		count = IDE_MIN (bh->b_size - bh->b_count, bcount);
 		atapi_input_bytes (drive, bh->b_data + bh->b_count, count);
 		bcount -= count; bh->b_count += count;
 		if (bh->b_count == bh->b_size) {
@@ -1077,7 +1072,7 @@
 {
 	struct buffer_head *bh = pc->bh;
 	int count;
-	
+
 	while (bcount) {
 #if IDETAPE_DEBUG_BUGS
 		if (bh == NULL) {
@@ -1085,7 +1080,7 @@
 			return;
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
-		count = IDETAPE_MIN (pc->b_count, bcount);
+		count = IDE_MIN (pc->b_count, bcount);
 		atapi_output_bytes (drive, pc->b_data, count);
 		bcount -= count; pc->b_data += count; pc->b_count -= count;
 		if (!pc->b_count) {
@@ -1113,7 +1108,7 @@
 			return;
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
-		count = IDETAPE_MIN (bh->b_size, bcount);
+		count = IDE_MIN (bh->b_size, bcount);
 		bh->b_count = count;
 		if (bh->b_count == bh->b_size)
 			bh = bh->b_reqnext;
@@ -1124,40 +1119,6 @@
 #endif /* CONFIG_BLK_DEV_TRITON */
 
 /*
- *	idetape_poll_for_dsc gets invoked by a timer (which was set
- *	by idetape_postpone_request) to reinsert our postponed request
- *	into the request queue.
- *
- * 	Note that the procedure done here is different than the method
- *	we are using in idetape_queue_pc_head - There we are putting
- *	request(s) before our currently called request.
- *
- *	Here, on the other hand, HWGROUP(drive)->rq is not our request
- *	but rather a request to another device. Therefore, we will let
- *	it finish and only then service our postponed request --> We don't
- *	touch HWGROUP(drive)->rq.
- */
-static void idetape_poll_for_dsc (unsigned long data)
-{
-	ide_drive_t *drive=(ide_drive_t *) data;
-	idetape_tape_t *tape = drive->driver_data;
-
-	del_timer (&tape->dsc_timer);
-
-#if IDETAPE_DEBUG_LOG
-	printk (KERN_INFO "ide-tape: Putting back postponed request\n");
-#endif /* IDETAPE_DEBUG_LOG */
-#if IDETAPE_DEBUG_BUGS
-	if (tape->postponed_rq == NULL) {
-		printk (KERN_ERR "tape->postponed_rq is NULL in idetape_poll_for_dsc\n");
-		return;
-	}
-#endif /* IDETAPE_DEBUG_BUGS */
-
-	(void) ide_do_drive_cmd (drive, tape->postponed_rq, ide_next);
-}
-
-/*
  *	idetape_postpone_request postpones the current request so that
  *	ide.c will be able to service requests from another device on
  *	the same hwgroup while we are polling for DSC.
@@ -1165,33 +1126,9 @@
 static void idetape_postpone_request (ide_drive_t *drive)
 {
 	idetape_tape_t *tape = drive->driver_data;
-	struct request *rq;
-	
-#if IDETAPE_DEBUG_LOG
-	printk (KERN_INFO "Reached idetape_postpone_request\n");
-#endif /* IDETAPE_DEBUG_LOG */
-#if IDETAPE_DEBUG_BUGS
-	if (tape->postponed_rq != NULL)
-		printk (KERN_ERR "ide-tape.c bug - postponed_rq not NULL in idetape_postpone_request\n");
-#endif /* IDETAPE_DEBUG_BUGS */
-
-	/*
-	 *	Set the timer parameters.
-	 */
-	tape->dsc_timer.expires=jiffies + tape->dsc_polling_frequency;
-	tape->dsc_timer.data=(unsigned long) drive;
-	tape->dsc_timer.function = &idetape_poll_for_dsc;
-	init_timer (&tape->dsc_timer);
-
-	/*
-	 * Remove current request from the request queue:
-	 */
-	tape->postponed_rq = rq = HWGROUP(drive)->rq;
-	rq->rq_status = IDETAPE_RQ_POSTPONED;
-	blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
-	HWGROUP(drive)->rq = NULL;
 
-	add_timer(&tape->dsc_timer);		/* Activate the polling timer */
+	tape->postponed_rq = HWGROUP(drive)->rq;
+	ide_stall_queue(drive, tape->dsc_polling_frequency);
 }
 
 /*
@@ -1222,7 +1159,7 @@
 
 	ide_init_drive_cmd (rq);
 	rq->buffer = (char *) pc;
-	rq->cmd = IDETAPE_PC_RQ;
+	rq->cmd = IDETAPE_PC_RQ1;
 	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
 }
 
@@ -1402,7 +1339,7 @@
 			return;
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
-		count = IDETAPE_MIN (bh->b_size - bh->b_count, n);
+		count = IDE_MIN (bh->b_size - bh->b_count, n);
 		copy_from_user (bh->b_data + bh->b_count, buf, count);
 		n -= count; bh->b_count += count; buf += count;
 		if (bh->b_count == bh->b_size) {
@@ -1426,7 +1363,7 @@
 			return;
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
-		count = IDETAPE_MIN (tape->b_count, n);
+		count = IDE_MIN (tape->b_count, n);
 		copy_to_user (buf, tape->b_data, count);
 		n -= count; tape->b_data += count; tape->b_count -= count; buf += count;
 		if (!tape->b_count) {
@@ -1477,7 +1414,7 @@
 	printk (KERN_INFO "Reached idetape_increase_max_pipeline_stages\n");
 #endif /* IDETAPE_DEBUG_LOG */
 
-	tape->max_stages = IDETAPE_MIN (tape->max_stages + IDETAPE_INCREASE_STAGES_RATE, IDETAPE_MAX_PIPELINE_STAGES);
+	tape->max_stages = IDE_MIN (tape->max_stages + IDETAPE_INCREASE_STAGES_RATE, IDETAPE_MAX_PIPELINE_STAGES);
 }
 
 /*
@@ -2295,7 +2232,7 @@
 	}
 #if IDETAPE_DEBUG_BUGS
 	if (postponed_rq != NULL)
-		if (postponed_rq->rq_status != RQ_ACTIVE || rq != postponed_rq) {
+		if (rq != postponed_rq) {
 			printk (KERN_ERR "ide-tape: ide-tape.c bug - Two DSC requests were queued\n");
 			idetape_end_request (0,HWGROUP (drive));
 			return;
@@ -2309,6 +2246,8 @@
 	 *	the other device meanwhile.
 	 */
 	status.all = GET_STAT();
+	if (!drive->dsc_overlap && rq->cmd != IDETAPE_PC_RQ2)
+		set_bit (IDETAPE_IGNORE_DSC, &tape->flags);
 	if (!clear_bit (IDETAPE_IGNORE_DSC, &tape->flags) && !status.b.dsc) {
 		if (postponed_rq == NULL) {
 			tape->dsc_polling_start = jiffies;
@@ -2316,7 +2255,7 @@
 			tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT;
 		} else if ((signed long) (jiffies - tape->dsc_timeout) > 0) {
 			printk (KERN_ERR "ide-tape: %s: DSC timeout\n", tape->name);
-			if (rq->cmd == IDETAPE_PC_RQ)
+			if (rq->cmd == IDETAPE_PC_RQ2)
 				idetape_media_access_finished (drive);
 			else
 				ide_do_reset (drive);
@@ -2340,13 +2279,13 @@
 			rq->errors = IDETAPE_ERROR_EOD;
 			idetape_end_request (1, HWGROUP(drive));
 			return;
-		case IDETAPE_PC_RQ:
-			if (postponed_rq != NULL) {
-				idetape_media_access_finished (drive);
-				return;
-			}
+		case IDETAPE_PC_RQ1:
 			pc=(idetape_pc_t *) rq->buffer;
+			rq->cmd = IDETAPE_PC_RQ2;
 			break;
+		case IDETAPE_PC_RQ2:
+			idetape_media_access_finished (drive);
+			return;
 		default:
 			printk (KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n");
 			idetape_end_request (0,HWGROUP (drive));
@@ -2381,7 +2320,7 @@
 
 	ide_init_drive_cmd (&rq);
 	rq.buffer = (char *) pc;
-	rq.cmd = IDETAPE_PC_RQ;
+	rq.cmd = IDETAPE_PC_RQ1;
 	return ide_do_drive_cmd (drive, &rq, ide_wait);
 }
 
@@ -2640,11 +2579,11 @@
 	
 	while (bcount) {
 		bh = tape->merge_stage->bh;
-		count = IDETAPE_MIN (tape->stage_size, bcount);
+		count = IDE_MIN (tape->stage_size, bcount);
 		bcount -= count;
 		blocks = count / tape->tape_block_size;
 		while (count) {
-			bh->b_count = IDETAPE_MIN (count, bh->b_size);
+			bh->b_count = IDE_MIN (count, bh->b_size);
 			memset (bh->b_data, 0, bh->b_count);
 			count -= bh->b_count;
 			bh = bh->b_reqnext;
@@ -2839,7 +2778,7 @@
 static ide_drive_t *get_drive_ptr (kdev_t i_rdev)
 {
 	unsigned int i = MINOR(i_rdev) & ~0x80;
-	
+
 	if (i >= MAX_HWIFS * MAX_DRIVES)
 		return NULL;
 	return (idetape_chrdevs[i].drive);
@@ -2948,7 +2887,7 @@
 {
 	ide_drive_t *drive = get_drive_ptr (inode->i_rdev);
 	idetape_tape_t *tape = drive->driver_data;
-	int bytes_read,temp,actually_read=0, original_count = count;
+	int bytes_read,temp,actually_read=0;
 
 #if IDETAPE_DEBUG_LOG
 	printk (KERN_INFO "Reached idetape_chrdev_read\n");
@@ -2988,7 +2927,7 @@
 	if (count==0)
 		return (0);
 	if (tape->merge_stage_size) {
-		actually_read=IDETAPE_MIN (tape->merge_stage_size,count);
+		actually_read=IDE_MIN (tape->merge_stage_size,count);
 		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, actually_read);
 		buf += actually_read; tape->merge_stage_size -= actually_read; count-=actually_read;
 	}
@@ -3003,13 +2942,13 @@
 		bytes_read=idetape_add_chrdev_read_request (drive, tape->capabilities.ctl);
 		if (bytes_read <= 0)
 			goto finish;
-		temp=IDETAPE_MIN (count,bytes_read);
+		temp=IDE_MIN (count,bytes_read);
 		idetape_copy_stage_to_user (tape, buf, tape->merge_stage, temp);
 		actually_read+=temp;
 		tape->merge_stage_size=bytes_read-temp;
 	}
 finish:
-	if (actually_read < original_count && test_bit (IDETAPE_FILEMARK, &tape->flags))
+	if (!actually_read && test_bit (IDETAPE_FILEMARK, &tape->flags))
 		idetape_space_over_filemarks (drive, MTFSF, 1);
 	return (actually_read);
 }
@@ -3063,7 +3002,7 @@
 			tape->merge_stage_size=0;
 		}
 #endif /* IDETAPE_DEBUG_BUGS */
-		actually_written=IDETAPE_MIN (tape->stage_size-tape->merge_stage_size,count);
+		actually_written=IDE_MIN (tape->stage_size-tape->merge_stage_size,count);
 		idetape_copy_stage_from_user (tape, tape->merge_stage, buf, actually_written);
 		buf+=actually_written;tape->merge_stage_size+=actually_written;count-=actually_written;
 
@@ -3577,9 +3516,11 @@
 {
 	ide_hwif_t *hwif = HWIF(drive);
 	unsigned long t1, tmid, tn, t;
+	u16 speed;
 
 	drive->driver_data = tape;
 	drive->ready_stat = 0;			/* An ATAPI device ignores DRDY */
+	drive->dsc_overlap = 1;
 	memset (tape, 0, sizeof (idetape_tape_t));
 	tape->drive = drive;
 	tape->minor = minor;
@@ -3609,9 +3550,10 @@
 	 *	good latency and good system throughput. It will be nice to
 	 *	have all this configurable in run time at some point.
 	 */
-	t1 = (tape->stage_size * HZ) / (tape->capabilities.speed * 1000);
-	tmid = (tape->capabilities.buffer_size * 32 * HZ) / (tape->capabilities.speed * 125);
-	tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (tape->capabilities.speed * 1000);
+	speed = IDE_MAX (tape->capabilities.speed, tape->capabilities.max_speed);
+	t1 = (tape->stage_size * HZ) / (speed * 1000);
+	tmid = (tape->capabilities.buffer_size * 32 * HZ) / (speed * 125);
+	tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (speed * 1000);
 
 	if (tape->max_stages) {
 		if (drive->using_dma)
@@ -3624,12 +3566,12 @@
 		}
 	} else
 		t = t1;
-	t = IDETAPE_MIN (t, tmid);
+	t = IDE_MIN (t, tmid);
 
 	/*
 	 *	Ensure that the number we got makes sense.
 	 */
-	tape->best_dsc_rw_frequency = IDETAPE_MAX (IDETAPE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN);
+	tape->best_dsc_rw_frequency = IDE_MAX (IDE_MIN (t, IDETAPE_DSC_RW_MAX), IDETAPE_DSC_RW_MIN);
 	if (tape->best_dsc_rw_frequency != t) {
 		printk (KERN_NOTICE "ide-tape: Although the recommended polling period is %lu jiffies\n", t);
 		printk (KERN_NOTICE "ide-tape: we will use %lu jiffies\n", tape->best_dsc_rw_frequency);
@@ -3681,6 +3623,7 @@
 	ide_tape,		/* media */
 	1,			/* busy */
 	1,			/* supports_dma */
+	1,			/* supports_dsc_overlap */
 	idetape_cleanup,	/* cleanup */
 	idetape_do_request,	/* do_request */
 	idetape_end_request,	/* end_request */
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.14/linux/drivers/block/ide.c	Sun Nov 10 20:12:09 1996
+++ linux/drivers/block/ide.c	Wed Dec 11 16:45:55 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.60  Nov   5, 1996
+ *  linux/drivers/block/ide.c	Version 6.00  Dec   4, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -269,6 +269,13 @@
  *			add m68k code from Geert Uytterhoeven
  *			probe all interfaces by default
  *			add ioctl to (re)probe an interface
+ * Version 6.00		use per device request queues
+ *			attempt to optimize shared hwgroup performance
+ *			add ioctl to manually adjust bandwidth algorithms
+ *			add kerneld support for the probe module
+ *			fix bug in ide_error()
+ *			fix bug in the first ide_get_lock() call for Atari
+ *			don't flush leftover data for ATAPI devices
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -294,6 +301,8 @@
 #include <linux/hdreg.h>
 #include <linux/genhd.h>
 #include <linux/malloc.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
 
 #include <asm/byteorder.h>
 #include <asm/irq.h>
@@ -301,11 +310,6 @@
 #include <asm/io.h>
 #include <asm/bitops.h>
 
-#ifdef CONFIG_PCI
-#include <linux/bios32.h>
-#include <linux/pci.h>
-#endif /* CONFIG_PCI */
-
 #include "ide.h"
 #include "ide_modes.h"
 
@@ -354,18 +358,14 @@
 	restore_flags(flags);
 	return (t - i);
 }
+#endif /* DISK_RECOVERY_TIME */
 
-static void set_recovery_timer (ide_hwif_t *hwif)
+static inline void set_recovery_timer (ide_hwif_t *hwif)
 {
+#if (DISK_RECOVERY_TIME > 0)
 	hwif->last_time = read_timer();
-}
-#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
-
-#else
-
-#define SET_RECOVERY_TIMER(drive)
-
 #endif /* DISK_RECOVERY_TIME */
+}
 
 /*
  * Do not even *think* about calling this!
@@ -820,7 +820,8 @@
 	}
 	save_flags(flags);
 	cli();
-	blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+	drive->queue = rq->next;
+	blk_dev[MAJOR(rq->rq_dev)].current_request = NULL;
 	HWGROUP(drive)->rq = NULL;
 	rq->rq_status = RQ_INACTIVE;
 	if (rq->sem != NULL)
@@ -840,21 +841,19 @@
 	ide_sti();
 	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
 #if FANCY_STATUS_DUMPS
-	if (drive->media == ide_disk) {
-		printk(" { ");
-		if (stat & BUSY_STAT)
-			printk("Busy ");
-		else {
-			if (stat & READY_STAT)	printk("DriveReady ");
-			if (stat & WRERR_STAT)	printk("DeviceFault ");
-			if (stat & SEEK_STAT)	printk("SeekComplete ");
-			if (stat & DRQ_STAT)	printk("DataRequest ");
-			if (stat & ECC_STAT)	printk("CorrectedError ");
-			if (stat & INDEX_STAT)	printk("Index ");
-			if (stat & ERR_STAT)	printk("Error ");
-		}
-		printk("}");
+	printk(" { ");
+	if (stat & BUSY_STAT)
+		printk("Busy ");
+	else {
+		if (stat & READY_STAT)	printk("DriveReady ");
+		if (stat & WRERR_STAT)	printk("DeviceFault ");
+		if (stat & SEEK_STAT)	printk("SeekComplete ");
+		if (stat & DRQ_STAT)	printk("DataRequest ");
+		if (stat & ECC_STAT)	printk("CorrectedError ");
+		if (stat & INDEX_STAT)	printk("Index ");
+		if (stat & ERR_STAT)	printk("Error ");
 	}
+	printk("}");
 #endif	/* FANCY_STATUS_DUMPS */
 	printk("\n");
 	if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
@@ -907,6 +906,8 @@
 {
 	int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
 
+	if (drive->media != ide_disk)
+		return;
 	while (i > 0) {
 		unsigned long buffer[16];
 		unsigned int wcount = (i > 16) ? 16 : i;
@@ -951,7 +952,8 @@
 	if (rq->errors >= ERROR_MAX) {
 		if (drive->driver != NULL)
 			DRIVER(drive)->end_request(0, HWGROUP(drive));
- 		ide_end_request(0, HWGROUP(drive));
+		else
+	 		ide_end_request(0, HWGROUP(drive));
 	} else {
 		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
 			++rq->errors;
@@ -1093,24 +1095,20 @@
 /*
  * do_request() initiates handling of a new I/O request
  */
-static inline void do_request (ide_hwif_t *hwif, struct request *rq)
+static inline void do_request (ide_hwgroup_t *hwgroup, ide_hwif_t *hwif, ide_drive_t *drive)
 {
-	unsigned int minor, unit;
 	unsigned long block, blockend;
-	ide_drive_t *drive = NULL;
+	struct request *rq = drive->queue;
+	unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
 
 	ide_sti();
 #ifdef DEBUG
 	printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
-	minor = MINOR(rq->rq_dev);
-	unit = minor >> PARTN_BITS;
-	if (MAJOR(rq->rq_dev) != hwif->major || unit >= MAX_DRIVES) {
-		printk("%s: bad device number: %s\n",
-		       hwif->name, kdevname(rq->rq_dev));
+	if (unit >= MAX_DRIVES) {
+		printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev));
 		goto kill_rq;
 	}
-	drive = &hwif->drives[unit];
 #ifdef DEBUG
 	if (rq->bh && !buffer_locked(rq->bh)) {
 		printk("%s: block not locked\n", drive->name);
@@ -1129,17 +1127,18 @@
 	if (block == 0 && drive->remap_0_to_1)
 		block = 1;  /* redirect MBR access to EZ-Drive partn table */
 #endif /* FAKE_FDISK_FOR_EZDRIVE */
-	((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
 #if (DISK_RECOVERY_TIME > 0)
 	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
 #endif
 
-	SELECT_DRIVE(hwif,drive);
+	hwgroup->hwif = hwif;
+	hwgroup->drive = drive;
+	SELECT_DRIVE(hwif, drive);
 	if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 		printk("%s: drive not ready for command\n", drive->name);
 		return;
 	}
-	
+
 	if (!drive->special.all) {
 		if (rq->cmd == IDE_DRIVE_CMD) {
 			execute_drive_cmd(drive, rq);
@@ -1155,10 +1154,96 @@
 	do_special(drive);
 	return;
 kill_rq:
-	if (drive != NULL && drive->driver != NULL)
+	if (drive->driver != NULL)
 		DRIVER(drive)->end_request(0, HWGROUP(drive));
 	else
-		ide_end_request(0, hwif->hwgroup);
+		ide_end_request(0, hwgroup);
+}
+
+/*
+ * ide_stall_queue() can be used by a drive to give excess bandwidth back
+ * to the hwgroup by sleeping for timeout jiffies.
+ */
+void ide_stall_queue (ide_drive_t *drive, unsigned long timeout)
+{
+	if (timeout > WAIT_WORSTCASE)
+		timeout = WAIT_WORSTCASE;
+	drive->sleep = timeout + jiffies;
+}
+
+#define WAKEUP(drive)	((drive)->service_start + 2 * (drive)->service_time)
+
+/*
+ * choose_drive() selects the next drive which will be serviced.
+ */
+static inline ide_drive_t *choose_drive (ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive, *best;
+
+repeat:	
+	best = NULL;
+	drive = hwgroup->drive;
+	do {
+		if (!drive->queue)
+			continue;
+		if (drive->sleep && drive->sleep > jiffies)
+			continue;
+		if (!best) {
+			best = drive;
+			continue;
+		}
+		if (drive->sleep && (!best->sleep || drive->sleep < best->sleep))
+			best = drive;
+		if (!best->sleep && WAKEUP(drive) < WAKEUP(best))
+			best = drive;
+	} while ((drive = drive->next) != hwgroup->drive);
+	if (best != hwgroup->drive && best && best->service_time > WAIT_MIN_SLEEP && !best->sleep && best->nice1) {
+		long t = (signed) (WAKEUP(best) - jiffies);
+		if (t >= WAIT_MIN_SLEEP) {
+			/*
+			 * We *may* have some time to spare, but first let's see if
+			 * someone can potentially benefit from our nice mood today..
+			 */
+			drive = best->next;
+			do {
+				if (drive->sleep)	/* this drive tried to be nice to us */
+					continue;
+				if (WAKEUP(drive) > jiffies - best->service_time && WAKEUP(drive) < jiffies + t) {
+					ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP));
+					goto repeat;
+				}
+			} while ((drive = drive->next) != best);
+		}
+	}
+	return best;
+}
+
+static inline void ide_leave_hwgroup (ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	unsigned long sleep = 0;
+
+	hwgroup->rq = NULL;
+	do {
+		blk_dev[HWIF(drive)->major].current_request = NULL;
+		if (!drive->sleep)
+			continue;
+		if (!sleep) {
+			sleep = drive->sleep;
+			continue;
+		}
+		if (drive->sleep < sleep)
+			sleep = drive->sleep;
+	} while ((drive = drive->next) != hwgroup->drive);
+	if (sleep) {
+		if (sleep < jiffies + WAIT_MIN_SLEEP)
+			sleep = jiffies + WAIT_MIN_SLEEP;
+		hwgroup->timer.expires = sleep;
+		add_timer(&hwgroup->timer);
+	} else {	/* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
+		ide_release_lock(&ide_lock);
+		hwgroup->active = 0;
+	}
 }
 
 /*
@@ -1184,32 +1269,35 @@
 		return;
 	}
 	do {
-		ide_hwif_t *hwif = hwgroup->hwif;
-		struct request *rq;
-		if ((rq = hwgroup->rq) == NULL) {
-			if (hwif->sharing_irq && hwgroup->drive) /* set nIEN */
-				OUT_BYTE(hwgroup->drive->ctl|2,hwif->io_ports[IDE_CONTROL_OFFSET]);
-			/*
-			 * hwgroup->next_hwif is different from hwgroup->hwif
-			 * only when a request is inserted using "ide_next".
-			 * This saves wear and tear on IDE tapes.
-			 */
-			hwif = hwgroup->next_hwif;
-			do {
-				rq = blk_dev[hwif->major].current_request;
-				if (rq != NULL && rq->rq_status != RQ_INACTIVE)
-					goto got_rq;
-			} while ((hwif = hwif->next) != hwgroup->next_hwif);
-			ide_release_lock(&ide_lock);
-			return;		/* no work left for this hwgroup */
+		ide_drive_t *drive = choose_drive(hwgroup);
+		if (drive != NULL) {
+			ide_hwif_t *hwif = HWIF(drive);
+			if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif)
+				OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
+			drive->sleep = 0;
+			blk_dev[hwif->major].current_request = hwgroup->rq = drive->queue;
+			drive->service_start = jiffies;
+			do_request(hwgroup, hwif, drive);
+			cli();
+		} else {
+			ide_leave_hwgroup(hwgroup);	/* no work left for this hwgroup */
+			return;
 		}
-	got_rq:	
-		do_request(hwgroup->hwif = hwgroup->next_hwif = hwif, hwgroup->rq = rq);
-		cli();
 	} while (hwgroup->handler == NULL);
 }
 
 /*
+ * ide_get_queue() returns the queue which corresponds to a given device.
+ */
+struct request **ide_get_queue (kdev_t dev)
+{
+	struct blk_dev_struct *bdev = blk_dev + MAJOR(dev);
+	ide_hwif_t *hwif = bdev->data;
+
+	return &hwif->drives[DEVICE_NR(dev) & 1].queue;
+}
+
+/*
  * do_hwgroup_request() invokes ide_do_request() after first masking
  * all possible interrupts for the current hwgroup.  This prevents race
  * conditions in the event that an unexpected interrupt occurs while
@@ -1226,7 +1314,9 @@
 		ide_hwif_t *hgif = hwgroup->hwif;
 		ide_hwif_t *hwif = hgif;
 
+		del_timer(&hwgroup->timer);
 		ide_get_lock(&ide_lock, ide_intr, hwgroup);
+		hwgroup->active = 1;
 		do {
 			disable_irq(hwif->irq);
 		} while ((hwif = hwif->next) != hgif);
@@ -1267,25 +1357,28 @@
 {
 	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
 	ide_drive_t   *drive   = hwgroup->drive;
+	ide_handler_t *handler;
 	unsigned long flags;
 
 	save_flags(flags);
 	cli();
 
-	if (hwgroup->poll_timeout != 0) { /* polling in progress? */
-		ide_handler_t *handler = hwgroup->handler;
+	if ((handler = hwgroup->handler) != NULL) {
 		hwgroup->handler = NULL;
-		handler(drive);
-	} else if (hwgroup->handler == NULL) {	 /* not waiting for anything? */
-		ide_sti(); /* drive must have responded just as the timer expired */
-		printk("%s: marginal timeout\n", drive->name);
-	} else {
-		hwgroup->handler = NULL;	/* abort the operation */
-		if (hwgroup->hwif->dmaproc)
-			(void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
-		ide_error(drive, "irq timeout", GET_STAT());
-	}
-	if (hwgroup->handler == NULL)
+		if (hwgroup->poll_timeout != 0)	/* polling in progress? */
+			handler(drive);
+		else {				/* abort the operation */
+			if (hwgroup->hwif->dmaproc)
+				(void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
+			ide_error(drive, "irq timeout", GET_STAT());
+		}
+		cli();
+		if (hwgroup->handler == NULL) {
+			set_recovery_timer(HWIF(drive));
+			drive->service_time = jiffies - drive->service_start;
+			do_hwgroup_request (hwgroup);
+		}
+	} else
 		do_hwgroup_request (hwgroup);
 	restore_flags(flags);
 }
@@ -1368,7 +1461,8 @@
 		handler(drive);
 		cli();	/* this is necessary, as next rq may be different irq */
 		if (hwgroup->handler == NULL) {
-			SET_RECOVERY_TIMER(HWIF(drive));
+			set_recovery_timer(HWIF(drive));
+			drive->service_time = jiffies - drive->service_start;
 			ide_do_request(hwgroup);
 		}
 	} else {
@@ -1394,9 +1488,6 @@
 				ide_drive_t *drive = &hwif->drives[unit];
 				if (drive->present)
 					return drive;
-			} else if (major == IDE0_MAJOR && unit < 4) {
-				printk("ide: probable bad entry for /dev/hd%c\n", 'a'+unit);
-				printk("ide: to fix it, run:  /usr/src/linux/scripts/MAKEDEV.ide\n");
 			}
 			break;
 		}
@@ -1443,15 +1534,14 @@
  * If action is ide_end, then the rq is queued at the end of the
  * request queue, and the function returns immediately without waiting
  * for the new rq to be completed. This is again intended for careful
- * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
- * when operating in the pipelined operation mode).
+ * use by the ATAPI tape/cdrom driver code.
  */
 int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
 {
 	unsigned long flags;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	unsigned int major = HWIF(drive)->major;
 	struct request *cur_rq;
-	struct blk_dev_struct *bdev = &blk_dev[major];
 	struct semaphore sem = MUTEX_LOCKED;
 
 	if (IS_PROMISE_DRIVE && rq->buffer != NULL)
@@ -1461,24 +1551,16 @@
 	rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
 	if (action == ide_wait)
 		rq->sem = &sem;
-	unplug_device(bdev);
 
 	save_flags(flags);
 	cli();
-	if (action == ide_next)
-		HWGROUP(drive)->next_hwif = HWIF(drive);
-	cur_rq = bdev->current_request;
+	cur_rq = drive->queue;
 
 	if (cur_rq == NULL || action == ide_preempt) {
 		rq->next = cur_rq;
-		bdev->current_request = rq;
-		if (action == ide_preempt) {
-			HWGROUP(drive)->rq = NULL;
-		} else
-		if (HWGROUP(drive)->rq == NULL) {  /* is this necessary (?) */
-			bdev->request_fn();
-			cli();
-		}
+		drive->queue = rq;
+		if (action == ide_preempt)
+			hwgroup->rq = NULL;
 	} else {
 		if (action == ide_wait || action == ide_end) {
 			while (cur_rq->next != NULL)	/* find end of list */
@@ -1487,6 +1569,10 @@
 		rq->next = cur_rq->next;
 		cur_rq->next = rq;
 	}
+	if (!hwgroup->active) {
+		do_hwgroup_request(hwgroup);
+		cli();
+	}
 	if (action == ide_wait  && rq->rq_status != RQ_INACTIVE)
 		down(&sem);	/* wait for it to be serviced */
 	restore_flags(flags);
@@ -1564,14 +1650,21 @@
 
 static void ide_init_module (int type)
 {
+	int found = 0;
 	ide_module_t *module = ide_modules;
 	
 	while (module) {
-		if (module->type == type)
+		if (module->type == type) {
+			found = 1;
 			(void) module->init();
+		}
 		module = module->next;
 	}
 	revalidate_drives();
+#ifdef CONFIG_KERNELD
+	if (!found && type == IDE_PROBE_MODULE)
+		(void) request_module("ide-probe");
+#endif /* CONFIG_KERNELD */
 }
 
 static int ide_open(struct inode * inode, struct file * filp)
@@ -1630,7 +1723,7 @@
 void ide_unregister (unsigned int index)
 {
 	struct gendisk *gd, **gdp;
-	ide_drive_t *drive;
+	ide_drive_t *drive, *d;
 	ide_hwif_t *hwif, *g;
 	ide_hwgroup_t *hwgroup;
 	int irq_count = 0, unit;
@@ -1651,11 +1744,6 @@
 			goto abort;
 		if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
 			goto abort;
-		if (drive->id != NULL) {
-			kfree(drive->id);
-			drive->id = NULL;
-		}
-		drive->present = 0;
 	}
 	hwif->present = 0;
 	hwgroup = hwif->hwgroup;
@@ -1684,15 +1772,31 @@
 	 * Remove us from the hwgroup, and free
 	 * the hwgroup if we were the only member
 	 */
+	d = hwgroup->drive;
+	for (index = 0; index < MAX_DRIVES; ++index) {
+		drive = &hwif->drives[index];
+		if (!drive->present)
+			continue;
+		while (hwgroup->drive->next != drive)
+			hwgroup->drive = hwgroup->drive->next;
+		hwgroup->drive->next = drive->next;
+		if (hwgroup->drive == drive)
+			hwgroup->drive = NULL;
+		if (drive->id != NULL) {
+			kfree(drive->id);
+			drive->id = NULL;
+		}
+		drive->present = 0;
+	}
+	if (d->present)
+		hwgroup->drive = d;
 	while (hwgroup->hwif->next != hwif)
 		hwgroup->hwif = hwgroup->hwif->next;
 	hwgroup->hwif->next = hwif->next;
 	if (hwgroup->hwif == hwif)
-		hwgroup->hwif = hwif->next;
-	if (hwgroup->next_hwif == hwif)
-		hwgroup->next_hwif = hwif->next;
-	if (hwgroup->hwif == hwif)
 		kfree(hwgroup);
+	else
+		hwgroup->hwif = HWIF(hwgroup->drive);
 
 	/*
 	 * Remove us from the kernel's knowledge
@@ -1700,6 +1804,8 @@
 	unregister_blkdev(hwif->major, hwif->name);
 	kfree(blksize_size[hwif->major]);
 	blk_dev[hwif->major].request_fn = NULL;
+	blk_dev[hwif->major].data = NULL;
+	blk_dev[hwif->major].queue = NULL;
 	blksize_size[hwif->major] = NULL;
 	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
 		if (*gdp == hwif->gd)
@@ -1731,13 +1837,8 @@
 		}
 		for (index = 0; index < MAX_HWIFS; ++index) {
 			hwif = &ide_hwifs[index];
-			if (!hwif->present) {
-				ide_init_hwif_ports(hwif->io_ports, data_port, &hwif->irq);
-				if (ctl_port)
-					hwif->io_ports[IDE_CONTROL_OFFSET] = ctl_port;
-				hwif->irq = irq;
+			if (!hwif->present)
 				goto found;
-			}
 		}
 		for (index = 0; index < MAX_HWIFS; index++)
 			ide_unregister(index);
@@ -1748,6 +1849,10 @@
 		ide_unregister(index);
 	if (hwif->present)
 		return -1;
+	ide_init_hwif_ports(hwif->io_ports, data_port, &hwif->irq);
+	if (ctl_port)
+		hwif->io_ports[IDE_CONTROL_OFFSET] = ctl_port;
+	hwif->irq = irq;
 	hwif->noprobe = 0;
 	ide_init_module(IDE_PROBE_MODULE);
 	ide_init_module(IDE_DRIVER_MODULE);
@@ -1828,6 +1933,18 @@
 		case HDIO_GET_NOWERR:
 			return put_user(drive->bad_wstat == BAD_R_STAT, (long *) arg);
 
+		case HDIO_GET_NICE:
+		{
+			long nice = 0;
+
+			nice |= drive->dsc_overlap << IDE_NICE_DSC_OVERLAP;
+			nice |= drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP;
+			nice |= drive->nice0 << IDE_NICE_0;
+			nice |= drive->nice1 << IDE_NICE_1;
+			nice |= drive->nice2 << IDE_NICE_2;
+			return put_user(nice, (long *) arg);
+		}
+
 		case HDIO_SET_DMA:
 			if (!suser()) return -EACCES;
 			if (drive->driver != NULL && !DRIVER(drive)->supports_dma)
@@ -1955,6 +2072,19 @@
 				return -EIO;
 			return 0;
 		}
+		case HDIO_SET_NICE:
+			if (!suser()) return -EACCES;
+			if (drive->driver == NULL)
+				return -EPERM;
+			if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
+				return -EPERM;
+			drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1;
+			if (drive->dsc_overlap && !DRIVER(drive)->supports_dsc_overlap) {
+				drive->dsc_overlap = 0;
+				return -EPERM;
+			}
+			drive->nice1 = (arg >> IDE_NICE_1) & 1;
+			return 0;
 
 		RO_IOCTLS(inode->i_rdev, arg);
 
@@ -2058,7 +2188,7 @@
 			}
 			if (++n == max_vals)
 				break;
-			if (*s == ',')
+			if (*s == ',' || *s == ';')
 				++s;
 		}
 		if (!*s)
@@ -2439,10 +2569,10 @@
 #endif /* CONFIG_PCI */
 
 /*
- * ide_init_pci() finds/initializes "known" PCI IDE interfaces
+ * probe_for_hwifs() finds/initializes "known" IDE interfaces
  *
- * This routine should ideally be using pcibios_find_class() to find
- * all IDE interfaces, but that function causes some systems to "go weird".
+ * This routine should ideally be using pcibios_find_class() to find all
+ * PCI IDE interfaces, but that function causes some systems to "go weird".
  */
 static void probe_for_hwifs (void)
 {
@@ -2489,10 +2619,13 @@
 	 */
 	probe_for_hwifs ();
 
+	/*
+	 * Probe for devices
+	 */
 #ifdef CONFIG_BLK_DEV_IDE
 #ifdef __mc68000__
 	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
-		ide_get_lock(&ide_lock, ide_intr, NULL);
+		ide_get_lock(&ide_lock, NULL, NULL);
 		disable_irq(ide_hwifs[0].irq);
 	}
 #endif /* __mc68000__ */
@@ -2507,6 +2640,9 @@
 #endif /* __mc68000__ */
 #endif /* CONFIG_BLK_DEV_IDE */
 
+	/*
+	 * Attempt to match drivers for the available drives
+	 */
 #ifdef CONFIG_BLK_DEV_IDEDISK
 	(void) idedisk_init();
 #endif /* CONFIG_BLK_DEV_IDEDISK */
@@ -2519,6 +2655,9 @@
 #ifdef CONFIG_BLK_DEV_IDEFLOPPY
 	(void) idefloppy_init();
 #endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+	(void) idescsi_init();
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 }
 
 static int default_cleanup (ide_drive_t *drive)
@@ -2595,6 +2734,10 @@
 	unsigned int unit, index, i;
 	ide_drive_t *drive;
 
+	for (index = 0; index < MAX_HWIFS; ++index)
+		if (ide_hwifs[index].present) goto search;
+	ide_init_module(IDE_PROBE_MODULE);
+search:
 	for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
 		for (unit = 0; unit < MAX_DRIVES; ++unit) {
 			drive = &ide_hwifs[index].drives[unit];
@@ -2613,15 +2756,19 @@
 	save_flags(flags);
 	cli();
 	if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL ||
-	    drive->busy || drive->usage || drive->media != driver->media) {
+	    drive->busy || drive->usage) {
 	    	restore_flags(flags);
 		return 1;
 	}
 	drive->driver = driver;
 	setup_driver_defaults(drive);
 	restore_flags(flags);
-	if (driver->supports_dma && !drive->using_dma && drive->autotune != 2 && HWIF(drive)->dmaproc != NULL)
-		(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
+	if (drive->autotune != 2) {
+		if (driver->supports_dma && HWIF(drive)->dmaproc != NULL)
+			(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
+		drive->dsc_overlap = (drive->next != drive && driver->supports_dsc_overlap);
+		drive->nice1 = 1;
+	}
 	drive->revalidate = 1;
 	return 0;
 }
@@ -2691,6 +2838,7 @@
 	 */
 	X(ide_timer_expiry),		X(ide_intr),
 	X(ide_geninit),			X(ide_fops),
+	X(ide_get_queue),
 	X(do_ide0_request),
 #if MAX_HWIFS > 1
 	X(do_ide1_request),
@@ -2714,7 +2862,7 @@
 	X(ide_do_reset),		X(ide_init_drive_cmd),
 	X(ide_do_drive_cmd),		X(ide_end_drive_cmd),
 	X(ide_end_request),		X(ide_revalidate_disk),
-	X(ide_cmd),
+	X(ide_cmd),			X(ide_stall_queue),
 
 	X(ide_register),		X(ide_unregister),
 #include <linux/symtab_end.h>
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.1.14/linux/drivers/block/ide.h	Tue Nov 12 15:56:05 1996
+++ linux/drivers/block/ide.h	Thu Dec 12 17:33:46 1996
@@ -145,6 +145,9 @@
 #define PARTN_MASK	((1<<PARTN_BITS)-1)	/* a useful bit mask */
 #define MAX_DRIVES	2	/* per interface; 2 assumed by lots of code */
 #define SECTOR_WORDS	(512 / 4)	/* number of 32bit words per sector */
+#define IDE_LARGE_SEEK(b1,b2,t)	(((b1) > (b2) + (t)) || ((b2) > (b1) + (t)))
+#define IDE_MIN(a,b)	((a)<(b) ? (a):(b))
+#define IDE_MAX(a,b)	((a)>(b) ? (a):(b))
 
 /*
  * Timeouts for various operations:
@@ -158,6 +161,7 @@
 #define WAIT_PIDENTIFY	(1*HZ)	/* 1sec   - should be less than 3ms (?) */
 #define WAIT_WORSTCASE	(30*HZ)	/* 30sec  - worst case when spinning up */
 #define WAIT_CMD	(10*HZ)	/* 10sec  - maximum wait for an IRQ to happen */
+#define WAIT_MIN_SLEEP	(2*HZ/100)	/* 20msec - minimum sleep time */
 
 #if defined(CONFIG_BLK_DEV_HT6560B) || defined(CONFIG_BLK_DEV_PROMISE)
 #define SELECT_DRIVE(hwif,drive)				\
@@ -175,6 +179,7 @@
  * Now for the data we need to maintain per-drive:  ide_drive_t
  */
 
+#define ide_scsi	0x21
 #define ide_disk	0x20
 #define ide_cdrom	0x5
 #define ide_tape	0x1
@@ -192,6 +197,11 @@
 	} special_t;
 
 typedef struct ide_drive_s {
+	struct request		*queue;	/* request queue */
+	struct ide_drive_s 	*next;	/* circular list of hwgroup drives */
+	unsigned long sleep;		/* sleep until this time */
+	unsigned long service_start;	/* time we started last request */
+	unsigned long service_time;	/* service time of last request */
 	special_t	special;	/* special action flags */
 	unsigned present	: 1;	/* drive is physically present */
 	unsigned noprobe 	: 1;	/* from:  hdx=noprobe */
@@ -208,6 +218,11 @@
 	unsigned autotune	: 2;	/* 1=autotune, 2=noautotune, 0=default */
 	unsigned revalidate	: 1;	/* request revalidation */
 	unsigned bswap		: 1;	/* flag: byte swap data */
+	unsigned dsc_overlap	: 1;	/* flag: DSC overlap */
+	unsigned atapi_overlap	: 1;	/* flag: ATAPI overlap (not supported) */
+	unsigned nice0		: 1;	/* flag: give obvious excess bandwidth */
+	unsigned nice1		: 1;	/* flag: give potential excess bandwidth */
+	unsigned nice2		: 1;	/* flag: give a share in our own bandwidth */
 #if FAKE_FDISK_FOR_EZDRIVE
 	unsigned remap_0_to_1	: 1;	/* flag: partitioned with ezdrive */
 #endif /* FAKE_FDISK_FOR_EZDRIVE */
@@ -325,17 +340,17 @@
 	ide_handler_t		*handler;/* irq handler, if active */
 	ide_drive_t		*drive;	/* current drive */
 	ide_hwif_t		*hwif;	/* ptr to current hwif in linked-list */
-	ide_hwif_t		*next_hwif; /* next selected hwif (for tape) */
 	struct request		*rq;	/* current request */
 	struct timer_list	timer;	/* failsafe timer */
 	struct request		wrq;	/* local copy of current write rq */
 	unsigned long		poll_timeout;	/* timeout value during long polls */
+	int			active;	/* set when servicing requests */
 	} ide_hwgroup_t;
 
 /*
  * Subdrivers support.
  */
-#define IDE_SUBDRIVER_VERSION	0
+#define IDE_SUBDRIVER_VERSION	1
 
 typedef int	(ide_cleanup_proc)(ide_drive_t *);
 typedef void 	(ide_do_request_proc)(ide_drive_t *, struct request *, unsigned long);
@@ -352,6 +367,7 @@
 	byte				media;
 	unsigned busy			: 1;
 	unsigned supports_dma		: 1;
+	unsigned supports_dsc_overlap	: 1;
 	ide_cleanup_proc		*cleanup;
 	ide_do_request_proc		*do_request;
 	ide_end_request_proc		*end_request;
@@ -513,8 +529,7 @@
  * If action is ide_end, then the rq is queued at the end of the
  * request queue, and the function returns immediately without waiting
  * for the new rq to be completed. This is again intended for careful
- * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
- * when operating in the pipelined operation mode).
+ * use by the ATAPI tape/cdrom driver code.
  */
 int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
  
@@ -538,7 +553,16 @@
  */
 void ide_multwrite (ide_drive_t *drive, unsigned int mcount);
 
-void ide_revalidate_drives (void);
+/*
+ * ide_stall_queue() can be used by a drive to give excess bandwidth back
+ * to the hwgroup by sleeping for timeout jiffies.
+ */
+void ide_stall_queue (ide_drive_t *drive, unsigned long timeout);
+
+/*
+ * ide_get_queue() returns the queue which corresponds to a given device.
+ */
+struct request **ide_get_queue (kdev_t dev);
 
 void ide_timer_expiry (unsigned long data);
 void ide_intr (int irq, void *dev_id, struct pt_regs *regs);
@@ -559,6 +583,9 @@
 extern struct file_operations ide_fops[];
 #endif
 
+#ifdef CONFIG_BLK_DEV_IDEDISK
+int idedisk_init (void);
+#endif /* CONFIG_BLK_DEV_IDEDISK */
 #ifdef CONFIG_BLK_DEV_IDECD
 int ide_cdrom_init (void);
 #endif /* CONFIG_BLK_DEV_IDECD */
@@ -568,9 +595,9 @@
 #ifdef CONFIG_BLK_DEV_IDEFLOPPY
 int idefloppy_init (void);
 #endif /* CONFIG_BLK_DEV_IDEFLOPPY */
-#ifdef CONFIG_BLK_DEV_IDEDISK
-int idedisk_init (void);
-#endif /* CONFIG_BLK_DEV_IDEDISK */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+int idescsi_init (void);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 
 int ide_register_module (ide_module_t *module);
 void ide_unregister_module (ide_module_t *module);
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.14/linux/drivers/block/ll_rw_blk.c	Fri Sep 20 12:50:55 1996
+++ linux/drivers/block/ll_rw_blk.c	Thu Dec 12 16:51:08 1996
@@ -81,6 +81,16 @@
  */
 int * hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
 
+static inline struct request **get_queue(kdev_t dev)
+{
+	int major = MAJOR(dev);
+	struct blk_dev_struct *bdev = blk_dev + major;
+
+	if (bdev->queue)
+		return bdev->queue(dev);
+	return &blk_dev[major].current_request;
+}
+
 /*
  * remove the plug and let it rip..
  */
@@ -94,7 +104,7 @@
 	if (dev->current_request == &dev->plug) {
 		struct request * next = dev->plug.next;
 		dev->current_request = next;
-		if (next) {
+		if (next || dev->queue) {
 			dev->plug.next = NULL;
 			(dev->request_fn)();
 		}
@@ -111,6 +121,8 @@
  */
 static inline void plug_device(struct blk_dev_struct * dev)
 {
+	if (dev->current_request)
+		return;
 	dev->current_request = &dev->plug;
 	queue_task_irq_off(&dev->plug_tq, &tq_disk);
 }
@@ -233,7 +245,7 @@
 
 void add_request(struct blk_dev_struct * dev, struct request * req)
 {
-	struct request * tmp;
+	struct request * tmp, **current_request;
 	short		 disk_index;
 
 	switch (MAJOR(req->rq_dev)) {
@@ -255,12 +267,14 @@
 	}
 
 	req->next = NULL;
+	current_request = get_queue(req->rq_dev);
 	cli();
 	if (req->bh)
 		mark_buffer_clean(req->bh);
-	if (!(tmp = dev->current_request)) {
-		dev->current_request = req;
-		(dev->request_fn)();
+	if (!(tmp = *current_request)) {
+		*current_request = req;
+		if (dev->current_request != &dev->plug)
+			(dev->request_fn)();
 		sti();
 		return;
 	}
@@ -358,7 +372,7 @@
 	 * Try to coalesce the new request with old requests
 	 */
 	cli();
-	req = blk_dev[major].current_request;
+	req = *get_queue(bh->b_rdev);
 	if (!req) {
 		/* MD and loop can't handle plugging without deadlocking */
 		if (major != MD_MAJOR && major != LOOP_MAJOR)
@@ -378,7 +392,8 @@
 		 * All other drivers need to jump over the first entry, as that
 		 * entry may be busy being processed and we thus can't change it.
 		 */
-	        req = req->next;
+		if (req == blk_dev[major].current_request)
+	        	req = req->next;
 		if (!req)
 			break;
 		/* fall through */
@@ -614,6 +629,7 @@
 
 	for (dev = blk_dev + MAX_BLKDEV; dev-- != blk_dev;) {
 		dev->request_fn      = NULL;
+		dev->queue           = NULL;
 		dev->current_request = NULL;
 		dev->plug.rq_status  = RQ_INACTIVE;
 		dev->plug.cmd        = -1;
@@ -642,6 +658,9 @@
 #endif
 #ifdef CONFIG_BLK_DEV_HD
 	hd_init();
+#endif
+#ifdef CONFIG_BLK_DEV_PS2
+	ps2esdi_init();
 #endif
 #ifdef CONFIG_BLK_DEV_XD
 	xd_init();
diff -u --recursive --new-file v2.1.14/linux/drivers/block/ps2esdi.c linux/drivers/block/ps2esdi.c
--- v2.1.14/linux/drivers/block/ps2esdi.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/block/ps2esdi.c	Thu Dec 12 16:51:08 1996
@@ -0,0 +1,1178 @@
+/* ps2esdi driver based on assembler code by Arindam Banerji,
+   written by Peter De Schrijver */
+/* Reassuring note to IBM : This driver was NOT developed by vice-versa
+   engineering the PS/2's BIOS */
+/* Dedicated to Wannes, Tofke, Ykke, Godot, Killroy and all those 
+   other lovely fish out there... */
+/* This code was written during the long and boring WINA 
+   elections 1994 */
+/* Thanks to Arindam Banerij for giving me the source of his driver */
+/* This code may be freely distributed and modified in any way, 
+   as long as these notes remain intact */
+
+/*  Revised: 05/07/94 by Arindam Banerji (axb@cse.nd.edu) */
+/*  Revised: 09/08/94 by Peter De Schrijver (stud11@cc4.kuleuven.ac.be)
+   Thanks to Arindam Banerij for sending me the docs of the adapter */
+
+/* BA Modified for ThinkPad 720 by Boris Ashkinazi */
+/*                    (bash@vnet.ibm.com) 08/08/95 */
+
+/* Modified further for ThinkPad-720C by Uri Blumenthal */
+/*                    (uri@watson.ibm.com) Sep 11, 1995 */
+
+/* TODO : 
+   + Timeouts
+   + Get disk parameters
+   + DMA above 16MB
+   + reset after read/write error
+ */
+
+#include <linux/config.h>
+#include <linux/major.h>
+
+#ifdef  CONFIG_BLK_DEV_PS2
+
+#define MAJOR_NR PS2ESDI_MAJOR
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/genhd.h>
+#include <linux/ps2esdi.h>
+#include <linux/blk.h>
+#include <linux/mca.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/dma.h>
+#include <asm/uaccess.h>
+
+#define PS2ESDI_IRQ 14
+#define MAX_HD 1
+#define MAX_RETRIES 5
+#define MAX_16BIT 65536
+#define ESDI_TIMEOUT   0xf000
+#define ESDI_STAT_TIMEOUT 4
+
+#define TYPE_0_CMD_BLK_LENGTH 2
+#define TYPE_1_CMD_BLK_LENGTH 4
+
+
+static void reset_ctrl(void);
+
+int ps2esdi_init(void);
+
+static void ps2esdi_geninit(struct gendisk *ignored);
+
+static void do_ps2esdi_request(void);
+
+static void ps2esdi_readwrite(int cmd, u_char drive, u_int block, u_int count);
+
+static void ps2esdi_fill_cmd_block(u_short * cmd_blk, u_short cmd,
+u_short cyl, u_short head, u_short sector, u_short length, u_char drive);
+
+static int ps2esdi_out_cmd_blk(u_short * cmd_blk);
+
+static void ps2esdi_prep_dma(char *buffer, u_short length, u_char dma_xmode);
+
+static void ps2esdi_interrupt_handler(int irq, void *dev_id,
+				      struct pt_regs *regs);
+static void (*current_int_handler) (u_int) = NULL;
+static void ps2esdi_normal_interrupt_handler(u_int);
+static void ps2esdi_initial_reset_int_handler(u_int);
+static void ps2esdi_geometry_int_handler(u_int);
+
+static void ps2esdi_continue_request(void);
+
+static int ps2esdi_open(struct inode *inode, struct file *file);
+
+static void ps2esdi_release(struct inode *inode, struct file *file);
+
+static int ps2esdi_ioctl(struct inode *inode, struct file *file,
+			 u_int cmd, u_long arg);
+
+static int ps2esdi_reread_partitions(int dev);
+
+static int ps2esdi_read_status_words(int num_words, int max_words, u_short * buffer);
+
+static void dump_cmd_complete_status(u_int int_ret_code);
+
+static void ps2esdi_get_device_cfg(void);
+
+void ps2esdi_reset_timer(unsigned long unused);
+
+u_int dma_arb_level;		/* DMA arbitration level */
+
+static struct wait_queue *ps2esdi_int = NULL, *ps2esdi_wait_open = NULL;
+int no_int_yet;
+static int access_count[MAX_HD] =
+{0,};
+static char ps2esdi_valid[MAX_HD] =
+{0,};
+static int ps2esdi_sizes[MAX_HD << 6] =
+{0,};
+static int ps2esdi_blocksizes[MAX_HD << 6] =
+{0,};
+static int ps2esdi_drives = 0;
+static struct hd_struct ps2esdi[MAX_HD << 6];
+static u_short io_base;
+static struct timer_list esdi_timer =
+{NULL, NULL, 0, 0L, ps2esdi_reset_timer};
+static int reset_status;
+int tp720esdi = 0;		/* Is it Integrated ESDI of ThinkPad-720? */
+
+struct ps2esdi_i_struct {
+	unsigned int head, sect, cyl, wpcom, lzone, ctl;
+};
+
+#if 0
+#if 0				/* try both - I don't know which one is better... UB */
+struct ps2esdi_i_struct ps2esdi_info[] =
+{
+	{4, 48, 1553, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}};
+#else
+struct ps2esdi_i_struct ps2esdi_info[] =
+{
+	{64, 32, 161, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}};
+#endif
+#endif
+struct ps2esdi_i_struct ps2esdi_info[] =
+{
+	{0, 0, 0, 0, 0, 0},
+	{0, 0, 0, 0, 0, 0}};
+
+static struct file_operations ps2esdi_fops =
+{
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	ps2esdi_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	ps2esdi_open,		/* open */
+	ps2esdi_release,	/* release */
+	block_fsync		/* fsync */
+};
+
+static struct gendisk ps2esdi_gendisk =
+{
+	MAJOR_NR,		/* Major number */
+	"ed",			/* Major name */
+	6,			/* Bits to shift to get real from partition */
+	1 << 6,			/* Number of partitions per real disk */
+	MAX_HD,			/* maximum number of real disks */
+	ps2esdi_geninit,	/* init function */
+	ps2esdi,		/* hd struct */
+	ps2esdi_sizes,		/* block sizes */
+	0,			/* number */
+	(void *) ps2esdi_info,	/* internal */
+	NULL			/* next */
+};
+
+/* initialization routine called by ll_rw_blk.c   */
+int ps2esdi_init(void)
+{
+
+	/* register the device - pass the name, major number and operations
+	   vector .                                                 */
+	if (register_blkdev(MAJOR_NR, "ed", &ps2esdi_fops)) {
+		printk("%s: Unable to get major number %d\n", DEVICE_NAME, MAJOR_NR);
+		return -1;
+	}
+	/* set up some global information - indicating device specific info */
+	blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+	read_ahead[MAJOR_NR] = 8;	/* 8 sector (4kB) read ahead */
+
+	/* some minor housekeeping - setup the global gendisk structure */
+	ps2esdi_gendisk.next = gendisk_head;
+	gendisk_head = &ps2esdi_gendisk;
+	return 0;
+
+}				/* ps2esdi_init */
+
+/* handles boot time command line parameters */
+void tp720_setup(char *str, int *ints)
+{
+	/* no params, just sets the tp720esdi flag if it exists */
+
+	printk("%s: TP 720 ESDI flag set\n", DEVICE_NAME);
+	tp720esdi = 1;
+}
+
+void ed_setup(char *str, int *ints)
+{
+	int hdind = 0;
+
+	/* handles 3 parameters only - corresponding to
+	   1. Number of cylinders
+	   2. Number of heads
+	   3. Sectors/track
+	 */
+
+	if (ints[0] != 3)
+		return;
+
+	/* print out the information - seen at boot time */
+	printk("%s: ints[0]=%d ints[1]=%d ints[2]=%d ints[3]=%d\n",
+	       DEVICE_NAME, ints[0], ints[1], ints[2], ints[3]);
+
+	/* set the index into device specific information table */
+	if (ps2esdi_info[0].head != 0)
+		hdind = 1;
+
+	/* set up all the device information */
+	ps2esdi_info[hdind].head = ints[2];
+	ps2esdi_info[hdind].sect = ints[3];
+	ps2esdi_info[hdind].cyl = ints[1];
+	ps2esdi_info[hdind].wpcom = 0;
+	ps2esdi_info[hdind].lzone = ints[1];
+	ps2esdi_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
+#if 0				/* this may be needed for PS2/Mod.80, but it hurts ThinkPad! */
+	ps2esdi_drives = hdind + 1;	/* increment index for the next time */
+#endif
+}				/* ed_setup */
+
+static int ps2esdi_getinfo(char *buf, int slot, void *d)
+{
+	int len = 0;
+
+	len += sprintf(buf + len, "DMA Arbitration Level: %d\n",
+		       dma_arb_level);
+	len += sprintf(buf + len, "IO Port: %x\n", io_base);
+	len += sprintf(buf + len, "IRQ: 14\n");
+	len += sprintf(buf + len, "Drives: %d\n", ps2esdi_drives);
+
+	return len;
+}
+
+/* ps2 esdi specific initialization - called thru the gendisk chain */
+static void ps2esdi_geninit(struct gendisk *ignored)
+{
+	/*
+
+	   The first part contains the initialization code
+	   for the ESDI disk subsystem.  All we really do
+	   is search for the POS registers of the controller
+	   to do some simple setup operations.  First, we
+	   must ensure that the controller is installed,
+	   enabled, and configured as PRIMARY.  Then we must
+	   determine the DMA arbitration level being used by
+	   the controller so we can handle data transfer
+	   operations properly.  If all of this works, then
+	   we will set the INIT_FLAG to a non-zero value.
+	 */
+
+	int slot = 0, i, reset_start, reset_end;
+	u_char status;
+	unsigned short adapterID;
+
+	if ((slot = mca_find_adapter(INTG_ESDI_ID, 0)) != MCA_NOTFOUND) {
+		adapterID = INTG_ESDI_ID;
+		printk("%s: integrated ESDI adapter found in slot %d\n",
+		       DEVICE_NAME, slot+1);
+#ifndef MODULE
+		mca_set_adapter_name(slot, "PS/2 Integrated ESDI");
+#endif
+	} else if ((slot = mca_find_adapter(NRML_ESDI_ID, 0)) != -1) {
+		adapterID = NRML_ESDI_ID;
+		printk("%s: normal ESDI adapter found in slot %d\n",
+		       DEVICE_NAME, slot+1);
+		mca_set_adapter_name(slot, "PS/2 ESDI");
+	} else {
+		return;
+	}
+
+	mca_set_adapter_procfn(slot, (MCA_ProcFn) ps2esdi_getinfo, NULL);
+
+	/* Found the slot - read the POS register 2 to get the necessary
+	   configuration and status information.  POS register 2 has the
+	   following information :
+	   Bit           Function
+	   7             reserved = 0
+	   6             arbitration method
+	   0 - fairness enabled
+	   1 - fairness disabled, linear priority assignment
+	   5-2           arbitration level
+	   1             alternate address
+	   1              alternate address
+	   0 - use addresses 0x3510 - 0x3517
+	   0             adapter enable
+	 */
+
+	status = mca_read_stored_pos(slot, 2);
+	/* is it enabled ? */
+	if (!(status & STATUS_ENABLED)) {
+		printk("%s: ESDI adapter disabled\n", DEVICE_NAME);
+		return;
+	}
+	/* try to grab IRQ, and try to grab a slow IRQ if it fails, so we can
+	   share with the SCSI driver */
+	if (request_irq(PS2ESDI_IRQ, ps2esdi_interrupt_handler,
+		  SA_INTERRUPT | SA_SHIRQ, "PS/2 ESDI", &ps2esdi_gendisk)
+	    && request_irq(PS2ESDI_IRQ, ps2esdi_interrupt_handler,
+			   SA_SHIRQ, "PS/2 ESDI", &ps2esdi_gendisk)
+	    ) {
+		printk("%s: Unable to get IRQ %d\n", DEVICE_NAME, PS2ESDI_IRQ);
+		return;
+	}
+	if (status & STATUS_ALTERNATE)
+		io_base = ALT_IO_BASE;
+	else
+		io_base = PRIMARY_IO_BASE;
+
+	/* get the dma arbitration level */
+	dma_arb_level = (status >> 2) & 0xf;
+
+	/* BA */
+	printk("%s: DMA arbitration level : %d\n",
+	       DEVICE_NAME, dma_arb_level);
+
+	LITE_ON;
+	current_int_handler = ps2esdi_initial_reset_int_handler;
+	reset_ctrl();
+	reset_status = 0;
+	reset_start = jiffies;
+	while (!reset_status) {
+		esdi_timer.expires = 100;
+		esdi_timer.data = 0;
+		esdi_timer.next = esdi_timer.prev = NULL;
+		add_timer(&esdi_timer);
+		sleep_on(&ps2esdi_int);
+	}
+	reset_end = jiffies;
+	LITE_OFF;
+	printk("%s: reset interrupt after %d jiffies,  %u.%02u secs\n",
+	       DEVICE_NAME, reset_end - reset_start, (reset_end - reset_start) / HZ,
+	       (reset_end - reset_start) % HZ);
+
+
+	/* Integrated ESDI Disk and Controller has only one drive! */
+	if (adapterID == INTG_ESDI_ID)	/* if not "normal" PS2 ESDI adapter */
+		ps2esdi_drives = 1;	/* then we have only one physical disk! */
+
+
+
+	/* finally this part sets up some global data structures etc. */
+
+	ps2esdi_get_device_cfg();
+
+	/* some annoyance in the above routine returns TWO drives?
+	 Is something else happining in the background?
+	 Regaurdless we fix the # of drives again. AJK */
+	/* Integrated ESDI Disk and Controller has only one drive! */
+	if (adapterID == INTG_ESDI_ID)	/* if not "normal" PS2 ESDI adapter */
+		ps2esdi_drives = 1;	/* Not three or two, ONE DAMNIT! */
+
+	current_int_handler = ps2esdi_normal_interrupt_handler;
+
+	ps2esdi_gendisk.nr_real = ps2esdi_drives;
+
+	for (i = 0; i < ps2esdi_drives; i++) {
+		ps2esdi[i << 6].nr_sects =
+		    ps2esdi_info[i].head *
+		    ps2esdi_info[i].sect *
+		    ps2esdi_info[i].cyl;
+		ps2esdi_valid[i] = 1;
+	}
+	for (i = 0; i < (MAX_HD << 6); i++)
+		ps2esdi_blocksizes[i] = 1024;
+	blksize_size[MAJOR_NR] = ps2esdi_blocksizes;
+}				/* ps2esdi_geninit */
+
+
+static void ps2esdi_get_device_cfg(void)
+{
+	u_short cmd_blk[TYPE_0_CMD_BLK_LENGTH];
+
+	/*BA */ printk("%s: Drive 0\n", DEVICE_NAME);
+	current_int_handler = ps2esdi_geometry_int_handler;
+	cmd_blk[0] = CMD_GET_DEV_CONFIG | 0x600;
+	cmd_blk[1] = 0;
+	no_int_yet = TRUE;
+	ps2esdi_out_cmd_blk(cmd_blk);
+	if (no_int_yet)
+		sleep_on(&ps2esdi_int);
+
+	if (ps2esdi_drives > 1) {
+		printk("%s: Drive 1\n", DEVICE_NAME);	/*BA */
+		cmd_blk[0] = CMD_GET_DEV_CONFIG | (1 << 5) | 0x600;
+		cmd_blk[1] = 0;
+		no_int_yet = TRUE;
+		ps2esdi_out_cmd_blk(cmd_blk);
+		if (no_int_yet)
+			sleep_on(&ps2esdi_int);
+	}			/* if second physical drive is present */
+	return;
+}
+
+/* strategy routine that handles most of the IO requests */
+static void do_ps2esdi_request(void)
+{
+	u_int block, count;
+	/* since, this routine is called with interrupts cleared - they 
+	   must be before it finishes  */
+	sti();
+
+#if 0
+	printk("%s:got request. device : %d minor : %d command : %d  sector : %ld count : %ld, buffer: %p\n",
+	       DEVICE_NAME,
+	       CURRENT_DEV, MINOR(CURRENT->rq_dev),
+	       CURRENT->cmd, CURRENT->sector,
+	       CURRENT->nr_sectors, CURRENT->buffer);
+#endif
+
+	/* standard macro that ensures that requests are really on the
+	   list + sanity checks.                     */
+	INIT_REQUEST;
+
+	if (virt_to_bus((u_int) CURRENT->buffer + CURRENT->nr_sectors * 512) > 16 * MB) {
+		printk("%s: DMA above 16MB not supported\n", DEVICE_NAME);
+		end_request(FAIL);
+		if (CURRENT)
+			do_ps2esdi_request();
+		return;
+	}			/* check for above 16Mb dmas */
+	if ((CURRENT_DEV < ps2esdi_drives) &&
+	    (CURRENT->sector + CURRENT->nr_sectors <=
+	     ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects)) {
+#if 0
+		printk("%s:got request. device : %d minor : %d command : %d  sector : %ld count : %ld\n",
+		       DEVICE_NAME,
+		       CURRENT_DEV, MINOR(CURRENT->dev),
+		       CURRENT->cmd, CURRENT->sector,
+		       CURRENT->nr_sectors);
+#endif
+
+
+		block = CURRENT->sector + ps2esdi[MINOR(CURRENT->rq_dev)].start_sect;
+
+#if 0
+		printk("%s: blocknumber : %d\n", DEVICE_NAME, block);
+#endif
+		count = CURRENT->nr_sectors;
+		switch (CURRENT->cmd) {
+		case READ:
+			ps2esdi_readwrite(READ, CURRENT_DEV, block, count);
+			return;
+			break;
+		case WRITE:
+			ps2esdi_readwrite(WRITE, CURRENT_DEV, block, count);
+			return;
+			break;
+		default:
+			printk("%s: Unknown command\n", DEVICE_NAME);
+			end_request(FAIL);
+			if (CURRENT)
+				do_ps2esdi_request();
+			break;
+		}		/* handle different commands */
+	}
+	/* is request is valid */ 
+	else {
+		printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives,
+		       CURRENT->sector, ps2esdi[MINOR(CURRENT->rq_dev)].nr_sects);
+		end_request(FAIL);
+		if (CURRENT)
+			do_ps2esdi_request();
+	}
+
+}				/* main strategy routine */
+
+/* resets the ESDI adapter */
+static void reset_ctrl(void)
+{
+
+	u_long expire;
+	u_short status;
+
+	/* enable interrupts on the controller */
+	status = inb(ESDI_INTRPT);
+	outb((status & 0xe0) | ATT_EOI, ESDI_ATTN);	/* to be sure we don't have
+							   any interrupt pending... */
+	outb_p(CTRL_ENABLE_INTR, ESDI_CONTROL);
+
+	/* read the ESDI status port - if the controller is not busy,
+	   simply do a soft reset (fast) - otherwise we'll have to do a
+	   hard (slow) reset.  */
+	if (!(inb_p(ESDI_STATUS) & STATUS_BUSY)) {
+		/*BA */ printk("%s: soft reset...\n", DEVICE_NAME);
+		outb_p(CTRL_SOFT_RESET, ESDI_ATTN);
+	}
+	/* soft reset */ 
+	else {
+		/*BA */
+		printk("%s: hard reset...\n", DEVICE_NAME);
+		outb_p(CTRL_HARD_RESET, ESDI_CONTROL);
+		expire = jiffies + 200;
+		while (jiffies < expire);
+		outb_p(1, ESDI_CONTROL);
+	}			/* hard reset */
+
+
+}				/* reset the controller */
+
+/* called by the strategy routine to handle read and write requests */
+static void ps2esdi_readwrite(int cmd, u_char drive, u_int block, u_int count)
+{
+
+	u_short track, head, cylinder, sector;
+	u_short cmd_blk[TYPE_1_CMD_BLK_LENGTH];
+
+	/* do some relevant arithmatic */
+	CURRENT->current_nr_sectors =
+	    (count < (2 * MAX_16BIT / SECT_SIZE)) ? count : (2 * MAX_16BIT / SECT_SIZE);
+	track = block / ps2esdi_info[drive].sect;
+	head = track % ps2esdi_info[drive].head;
+	cylinder = track / ps2esdi_info[drive].head;
+	sector = block % ps2esdi_info[drive].sect;
+
+#if 0
+	printk("%s: cyl=%d head=%d sect=%d\n", DEVICE_NAME, cylinder, head, sector);
+#endif
+	/* call the routine that actually fills out a command block */
+	ps2esdi_fill_cmd_block
+	    (cmd_blk,
+	     (cmd == READ) ? CMD_READ : CMD_WRITE,
+	     cylinder, head, sector,
+	     CURRENT->current_nr_sectors, drive);
+
+	/* send the command block to the controller */
+	if (ps2esdi_out_cmd_blk(cmd_blk)) {
+		printk("%s: Controller failed\n", DEVICE_NAME);
+		if ((++CURRENT->errors) < MAX_RETRIES)
+			return do_ps2esdi_request();
+		else {
+			end_request(FAIL);
+			if (CURRENT)
+				do_ps2esdi_request();
+		}
+	}
+	/* check for failure to put out the command block */ 
+	else {
+#if 0
+		printk("%s: waiting for xfer\n", DEVICE_NAME);
+#endif
+		/* turn disk lights on */
+		LITE_ON;
+	}
+
+}				/* ps2esdi_readwrite */
+
+/* fill out the command block */
+static void ps2esdi_fill_cmd_block(u_short * cmd_blk, u_short cmd,
+ u_short cyl, u_short head, u_short sector, u_short length, u_char drive)
+{
+
+	cmd_blk[0] = (drive << 5) | cmd;
+	cmd_blk[1] = length;
+	cmd_blk[2] = ((cyl & 0x1f) << 11) | (head << 5) | sector;
+	cmd_blk[3] = (cyl & 0x3E0) >> 5;
+
+}				/* fill out the command block */
+
+/* write a command block to the controller */
+static int ps2esdi_out_cmd_blk(u_short * cmd_blk)
+{
+
+	int i, j;
+	u_char status;
+
+	/* enable interrupts */
+	outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+
+	/* do not write to the controller, if it is busy */
+	for (i = jiffies + ESDI_STAT_TIMEOUT; (i > jiffies) && (inb(ESDI_STATUS) &
+							  STATUS_BUSY););
+
+#if 0
+	printk("%s: i(1)=%d\n", DEVICE_NAME, i);
+#endif
+
+	/* if device is still busy - then just time out */
+	if (inb(ESDI_STATUS) & STATUS_BUSY) {
+		printk("%s: ps2esdi_out_cmd timed out (1)\n", DEVICE_NAME);
+		return ERROR;
+	}			/* timeout ??? */
+	/* Set up the attention register in the controller */
+	outb(((*cmd_blk) & 0xE0) | 1, ESDI_ATTN);
+
+#if 0
+	printk("%s: sending %d words to controller\n", DEVICE_NAME, (((*cmd_blk) >> 14) + 1) << 1);
+#endif
+
+	/* one by one send each word out */
+	for (i = (((*cmd_blk) >> 14) + 1) << 1; i; i--) {
+		status = inb(ESDI_STATUS);
+		for (j = jiffies + ESDI_STAT_TIMEOUT;
+		     (j > jiffies) && (status & STATUS_BUSY) &&
+		   (status & STATUS_CMD_INF); status = inb(ESDI_STATUS));
+		if ((status & (STATUS_BUSY | STATUS_CMD_INF)) == STATUS_BUSY) {
+#if 0
+			printk("%s: sending %04X\n", DEVICE_NAME, *cmd_blk);
+#endif
+			outw(*cmd_blk++, ESDI_CMD_INT);
+		} else {
+			printk("%s: ps2esdi_out_cmd timed out while sending command (status=%02X)\n",
+			       DEVICE_NAME, status);
+			return ERROR;
+		}
+	}			/* send all words out */
+	return OK;
+}				/* send out the commands */
+
+
+/* prepare for dma - do all the necessary setup */
+static void ps2esdi_prep_dma(char *buffer, u_short length, u_char dma_xmode)
+{
+	u_int tc;
+	
+	buffer=(char *)virt_to_bus(buffer);
+
+#if 0
+	printk("ps2esdi: b_wait: %p\n", CURRENT->bh->b_wait);
+#endif
+	cli();
+
+	outb(dma_arb_level | DMA_MASK_CHAN, PORT_DMA_FN);
+
+	outb(dma_arb_level | DMA_WRITE_ADDR, PORT_DMA_FN);
+	outb((u_int) buffer & (u_int) 0xff, PORT_DMA_EX);
+	outb(((u_int) buffer >> 8) & (u_int) 0xff, PORT_DMA_EX);
+	outb(((u_int) buffer >> 16) & (u_int) 0xff, PORT_DMA_EX);
+
+	outb(dma_arb_level | DMA_WRITE_TC, PORT_DMA_FN);
+	tc = (length * SECT_SIZE / 2) - 1;
+	outb(tc & 0xff, PORT_DMA_EX);
+	outb((tc >> 8) & 0xff, PORT_DMA_EX);
+
+	outb(dma_arb_level | DMA_WRITE_MODE, PORT_DMA_FN);
+	outb(dma_xmode, PORT_DMA_EX);
+
+	outb(dma_arb_level | DMA_UNMASK_CHAN, PORT_DMA_FN);
+
+	sti();
+
+}				/* prepare for dma */
+
+
+
+static void ps2esdi_interrupt_handler(int irq, void *dev_id,
+				      struct pt_regs *regs)
+{
+	u_int int_ret_code;
+
+	if (inb(ESDI_STATUS) & STATUS_INTR) {
+		int_ret_code = inb(ESDI_INTRPT);
+		if (current_int_handler) {
+			/* Disable adapter interrupts till processing is finished */
+			outb(CTRL_DISABLE_INTR, ESDI_CONTROL);
+			current_int_handler(int_ret_code);
+		} else
+			printk("%s: help ! No interrupt handler.\n", DEVICE_NAME);
+	} else {
+		return;
+	}
+}
+
+static void ps2esdi_initial_reset_int_handler(u_int int_ret_code)
+{
+
+	switch (int_ret_code & 0xf) {
+	case INT_RESET:
+		/*BA */
+		printk("%s: initial reset completed.\n", DEVICE_NAME);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		wake_up(&ps2esdi_int);
+		break;
+	case INT_ATTN_ERROR:
+		printk("%s: Attention error. interrupt status : %02X\n", DEVICE_NAME,
+		       int_ret_code);
+		printk("%s: status: %02x\n", DEVICE_NAME, inb(ESDI_STATUS));
+		break;
+	default:
+		printk("%s: initial reset handler received interrupt: %02X\n",
+		       DEVICE_NAME, int_ret_code);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		break;
+	}
+	outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+}
+
+
+static void ps2esdi_geometry_int_handler(u_int int_ret_code)
+{
+	u_int status, drive_num;
+	unsigned long rba;
+	int i;
+
+	drive_num = int_ret_code >> 5;
+	switch (int_ret_code & 0xf) {
+	case INT_CMD_COMPLETE:
+		for (i = ESDI_TIMEOUT; i & !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL); i--);
+		if (!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) {
+			printk("%s: timeout reading status word\n", DEVICE_NAME);
+			outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+			break;
+		}
+		status = inw(ESDI_STT_INT);
+		if ((status & 0x1F) == CMD_GET_DEV_CONFIG) {
+#define REPLY_WORDS 5		/* we already read word 0 */
+			u_short reply[REPLY_WORDS];
+
+			if (ps2esdi_read_status_words((status >> 8) - 1, REPLY_WORDS, reply)) {
+				/*BA */
+				printk("%s: Device Configuration Status for drive %u\n",
+				       DEVICE_NAME, drive_num);
+
+				printk("%s: Spares/cyls: %u", DEVICE_NAME, reply[0] >> 8);
+
+				printk
+				    ("Config bits: %s%s%s%s%s\n",
+				     (reply[0] & CONFIG_IS) ? "Invalid Secondary, " : "",
+				     ((reply[0] & CONFIG_ZD) && !(reply[0] & CONFIG_IS))
+				 ? "Zero Defect, " : "Defects Present, ",
+				     (reply[0] & CONFIG_SF) ? "Skewed Format, " : "",
+				     (reply[0] & CONFIG_FR) ? "Removable, " : "Non-Removable, ",
+				     (reply[0] & CONFIG_RT) ? "No Retries" : "Retries");
+
+				rba = reply[1] | ((unsigned long) reply[2] << 16);
+				printk("%s: Number of RBA's: %lu\n", DEVICE_NAME, rba);
+
+				printk("%s: Physical number of cylinders: %u, Sectors/Track: %u, Heads: %u\n",
+				       DEVICE_NAME, reply[3], reply[4] >> 8, reply[4] & 0xff);
+
+				if (!ps2esdi_info[drive_num].head) {
+					ps2esdi_info[drive_num].head = 64;
+					ps2esdi_info[drive_num].sect = 32;
+					ps2esdi_info[drive_num].cyl = rba / (64 * 32);
+					ps2esdi_info[drive_num].wpcom = 0;
+					ps2esdi_info[drive_num].lzone = ps2esdi_info[drive_num].cyl;
+					ps2esdi_info[drive_num].ctl = 8;
+					if (tp720esdi) {	/* store the retrieved parameters */
+						ps2esdi_info[0].head = reply[4] & 0Xff;
+						ps2esdi_info[0].sect = reply[4] >> 8;
+						ps2esdi_info[0].cyl = reply[3];
+						ps2esdi_info[0].wpcom = 0;
+						ps2esdi_info[0].lzone = reply[3];
+					} else {
+						ps2esdi_drives++;
+					}
+				}
+#ifdef OBSOLETE
+				if (!ps2esdi_info[drive_num].head) {
+					ps2esdi_info[drive_num].head = reply[4] & 0Xff;
+					ps2esdi_info[drive_num].sect = reply[4] >> 8;
+					ps2esdi_info[drive_num].cyl = reply[3];
+					ps2esdi_info[drive_num].wpcom = 0;
+					ps2esdi_info[drive_num].lzone = reply[3];
+					if (tp720esdi) {	/* store the retrieved parameters */
+						ps2esdi_info[0].head = reply[4] & 0Xff;
+						ps2esdi_info[0].sect = reply[4] >> 8;
+						ps2esdi_info[0].cyl = reply[3];
+						ps2esdi_info[0].wpcom = 0;
+						ps2esdi_info[0].lzone = reply[3];
+					} else {
+						ps2esdi_drives++;
+					}
+				}
+#endif
+
+			} else
+				printk("%s: failed while getting device config\n", DEVICE_NAME);
+#undef REPLY_WORDS
+		} else
+			printk("%s: command %02X unknown by geometry handler\n",
+			       DEVICE_NAME, status & 0x1f);
+
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		break;
+
+	case INT_ATTN_ERROR:
+		printk("%s: Attention error. interrupt status : %02X\n", DEVICE_NAME,
+		       int_ret_code);
+		printk("%s: Device not available\n", DEVICE_NAME);
+		break;
+	case INT_CMD_ECC:
+	case INT_CMD_RETRY:
+	case INT_CMD_ECC_RETRY:
+	case INT_CMD_WARNING:
+	case INT_CMD_ABORT:
+	case INT_CMD_FAILED:
+	case INT_DMA_ERR:
+	case INT_CMD_BLK_ERR:
+		/*BA */ printk("%s: Whaa. Error occurred...\n", DEVICE_NAME);
+		dump_cmd_complete_status(int_ret_code);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		break;
+	default:
+		printk("%s: Unknown interrupt reason: %02X\n",
+		       DEVICE_NAME, int_ret_code & 0xf);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		break;
+	}
+
+	wake_up(&ps2esdi_int);
+	no_int_yet = FALSE;
+	outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+
+}
+
+static void ps2esdi_normal_interrupt_handler(u_int int_ret_code)
+{
+	u_int status;
+	int i;
+
+	switch (int_ret_code & 0x0f) {
+	case INT_TRANSFER_REQ:
+		ps2esdi_prep_dma(CURRENT->buffer, CURRENT->current_nr_sectors,
+		    (CURRENT->cmd == READ) ? DMA_READ_16 : DMA_WRITE_16);
+		outb(CTRL_ENABLE_DMA | CTRL_ENABLE_INTR, ESDI_CONTROL);
+		break;
+
+	case INT_ATTN_ERROR:
+		printk("%s: Attention error. interrupt status : %02X\n", DEVICE_NAME,
+		       int_ret_code);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		break;
+
+	case INT_CMD_COMPLETE:
+		for (i = ESDI_TIMEOUT; i & !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL); i--);
+		if (!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) {
+			printk("%s: timeout reading status word\n", DEVICE_NAME);
+			outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+			outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+			if ((++CURRENT->errors) < MAX_RETRIES)
+				do_ps2esdi_request();
+			else {
+				end_request(FAIL);
+				if (CURRENT)
+					do_ps2esdi_request();
+			}
+			break;
+		}
+		status = inw(ESDI_STT_INT);
+		switch (status & 0x1F) {
+		case (CMD_READ & 0xff):
+		case (CMD_WRITE & 0xff):
+			LITE_OFF;
+			outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+			outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+#if 0
+			printk("ps2esdi: cmd_complete b_wait: %p\n", CURRENT->bh->b_wait);
+#endif
+			ps2esdi_continue_request();
+			break;
+		default:
+			printk("%s: interrupt for unknown command %02X\n",
+			       DEVICE_NAME, status & 0x1f);
+			outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+			outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+			break;
+		}
+		break;
+	case INT_CMD_ECC:
+	case INT_CMD_RETRY:
+	case INT_CMD_ECC_RETRY:
+		LITE_OFF;
+		dump_cmd_complete_status(int_ret_code);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		ps2esdi_continue_request();
+		break;
+	case INT_CMD_WARNING:
+	case INT_CMD_ABORT:
+	case INT_CMD_FAILED:
+	case INT_DMA_ERR:
+		LITE_OFF;
+		dump_cmd_complete_status(int_ret_code);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		if ((++CURRENT->errors) < MAX_RETRIES)
+			do_ps2esdi_request();
+		else {
+			end_request(FAIL);
+			if (CURRENT)
+				do_ps2esdi_request();
+		}
+		break;
+
+	case INT_CMD_BLK_ERR:
+		dump_cmd_complete_status(int_ret_code);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		end_request(FAIL);
+		if (CURRENT)
+			do_ps2esdi_request();
+		break;
+
+	case INT_CMD_FORMAT:
+		printk("%s: huh ? Who issued this format command ?\n"
+		       ,DEVICE_NAME);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		break;
+
+	case INT_RESET:
+		/* BA printk("%s: reset completed.\n", DEVICE_NAME) */ ;
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		break;
+
+	default:
+		printk("%s: Unknown interrupt reason: %02X\n",
+		       DEVICE_NAME, int_ret_code & 0xf);
+		outb((int_ret_code & 0xe0) | ATT_EOI, ESDI_ATTN);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		break;
+	}
+
+}				/* handle interrupts */
+
+
+static void ps2esdi_continue_request(void)
+{
+	if (CURRENT->nr_sectors -= CURRENT->current_nr_sectors) {
+		CURRENT->buffer += CURRENT->current_nr_sectors * SECT_SIZE;
+		CURRENT->sector += CURRENT->current_nr_sectors;
+		do_ps2esdi_request();
+	} else {
+		end_request(SUCCES);
+		if (CURRENT)
+			do_ps2esdi_request();
+	}
+}
+
+
+
+static int ps2esdi_read_status_words(int num_words,
+				     int max_words,
+				     u_short * buffer)
+{
+	int i;
+
+	for (; max_words && num_words; max_words--, num_words--, buffer++) {
+		for (i = ESDI_TIMEOUT; i && !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL); i--);
+		if (!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) {
+			printk("%s: timeout reading status word\n", DEVICE_NAME);
+			return FAIL;
+		}
+		*buffer = inw(ESDI_STT_INT);
+	}
+	return SUCCES;
+}
+
+
+
+
+static void dump_cmd_complete_status(u_int int_ret_code)
+{
+#define WAIT_FOR_STATUS \
+  for(i=ESDI_TIMEOUT;i && !(inb(ESDI_STATUS) & STATUS_STAT_AVAIL);i--); \
+    if(!(inb(ESDI_STATUS) & STATUS_STAT_AVAIL)) { \
+    printk("%s: timeout reading status word\n",DEVICE_NAME); \
+    return; \
+    }
+
+	int i, word_count;
+	u_short stat_word;
+	u_long rba;
+
+	printk("%s: Device: %u, interrupt ID: %02X\n",
+	       DEVICE_NAME, int_ret_code >> 5,
+	       int_ret_code & 0xf);
+
+	WAIT_FOR_STATUS;
+	stat_word = inw(ESDI_STT_INT);
+	word_count = (stat_word >> 8) - 1;
+	printk("%s: %u status words, command: %02X\n", DEVICE_NAME, word_count,
+	       stat_word & 0xff);
+
+	if (word_count--) {
+		WAIT_FOR_STATUS;
+		stat_word = inw(ESDI_STT_INT);
+		printk("%s: command status code: %02X, command error code: %02X\n",
+		       DEVICE_NAME, stat_word >> 8, stat_word & 0xff);
+	}
+	if (word_count--) {
+		WAIT_FOR_STATUS;
+		stat_word = inw(ESDI_STT_INT);
+		printk("%s: device error code: %s%s%s%s%s,%02X\n", DEVICE_NAME,
+		       (stat_word & 0x1000) ? "Ready, " : "Not Ready, ",
+		  (stat_word & 0x0800) ? "Selected, " : "Not Selected, ",
+		       (stat_word & 0x0400) ? "Write Fault, " : "",
+		       (stat_word & 0x0200) ? "Track 0, " : "",
+		(stat_word & 0x0100) ? "Seek or command complete, " : "",
+		       stat_word >> 8);
+	}
+	if (word_count--) {
+		WAIT_FOR_STATUS;
+		stat_word = inw(ESDI_STT_INT);
+		printk("%s: Blocks to do: %u", DEVICE_NAME, stat_word);
+	}
+	if (word_count -= 2) {
+		WAIT_FOR_STATUS;
+		rba = inw(ESDI_STT_INT);
+		WAIT_FOR_STATUS;
+		rba |= inw(ESDI_STT_INT) << 16;
+		printk(", Last Cyl: %u Head: %u Sector: %u\n",
+		       (u_short) ((rba & 0x1ff80000) >> 11),
+		 (u_short) ((rba & 0x7E0) >> 5), (u_short) (rba & 0x1f));
+	} else
+		printk("\n");
+
+	if (word_count--) {
+		WAIT_FOR_STATUS;
+		stat_word = inw(ESDI_STT_INT);
+		printk("%s: Blocks required ECC: %u", DEVICE_NAME, stat_word);
+	}
+	printk("\n");
+
+#undef WAIT_FOR_STATUS
+
+}
+
+
+
+static int ps2esdi_open(struct inode *inode, struct file *file)
+{
+	int dev = DEVICE_NR(MINOR(inode->i_rdev));
+
+#if 0
+	printk("%s: dev= %d\n", DEVICE_NAME, dev);
+#endif
+
+	if (dev < ps2esdi_drives) {
+		while (!ps2esdi_valid[dev])
+			sleep_on(&ps2esdi_wait_open);
+
+		access_count[dev]++;
+
+		return (0);
+	} else
+		return (-ENODEV);
+}
+
+
+
+static void ps2esdi_release(struct inode *inode, struct file *file)
+{
+	int dev = DEVICE_NR(MINOR(inode->i_rdev));
+
+	if (dev < ps2esdi_drives) {
+		sync_dev(dev);
+		access_count[dev]--;
+	}
+}
+
+
+
+static int ps2esdi_ioctl(struct inode *inode,
+			 struct file *file, u_int cmd, u_long arg)
+{
+
+	struct ps2esdi_geometry *geometry = (struct ps2esdi_geometry *) arg;
+	int dev = DEVICE_NR(MINOR(inode->i_rdev)), err;
+
+	if (inode && (dev < ps2esdi_drives))
+		switch (cmd) {
+		case HDIO_GETGEO:
+			if (arg) {
+				if ((err = verify_area(VERIFY_WRITE, geometry, sizeof(*geometry))))
+					return (err);
+				put_user(ps2esdi_info[dev].head, (char *) &geometry->heads);
+				put_user(ps2esdi_info[dev].sect, (char *) &geometry->sectors);
+				put_user(ps2esdi_info[dev].cyl, (short *) &geometry->cylinders);
+				put_user(ps2esdi[MINOR(inode->i_rdev)].start_sect,
+					    (long *) &geometry->start);
+
+				return (0);
+			}
+			break;
+		case BLKRASET:
+			if (!suser())
+				return -EACCES;
+			if (!inode->i_rdev)
+				return -EINVAL;
+			if (arg > 0xff)
+				return -EINVAL;
+			read_ahead[MAJOR(inode->i_rdev)] = arg;
+			return 0;
+		case BLKGETSIZE:
+			if (arg) {
+				if ((err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long))))
+					 return (err);
+				put_user(ps2esdi[MINOR(inode->i_rdev)].nr_sects, (long *) arg);
+
+				return (0);
+			}
+			break;
+		case BLKFLSBUF:
+			if (!suser())
+				return -EACCES;
+			if (!inode->i_rdev)
+				return -EINVAL;
+			fsync_dev(inode->i_rdev);
+			invalidate_buffers(inode->i_rdev);
+			return 0;
+
+		case BLKRRPART:
+			return (ps2esdi_reread_partitions(inode->i_rdev));
+			RO_IOCTLS(inode->i_rdev, arg);
+		}
+	return (-EINVAL);
+}
+
+
+
+static int ps2esdi_reread_partitions(int dev)
+{
+	int target = DEVICE_NR(MINOR(dev));
+	int start = target << ps2esdi_gendisk.minor_shift;
+	int partition;
+
+	cli();
+	ps2esdi_valid[target] = (access_count[target] != 1);
+	sti();
+	if (ps2esdi_valid[target])
+		return (-EBUSY);
+
+	for (partition = ps2esdi_gendisk.max_p - 1;
+	     partition >= 0; partition--) {
+		sync_dev(MAJOR_NR << 8 | start | partition);
+		invalidate_inodes(MAJOR_NR << 8 | start | partition);
+		invalidate_buffers(MAJOR_NR << 8 | start | partition);
+		ps2esdi_gendisk.part[start + partition].start_sect = 0;
+		ps2esdi_gendisk.part[start + partition].nr_sects = 0;
+	};
+
+	ps2esdi_gendisk.part[start].nr_sects = ps2esdi_info[target].head *
+	    ps2esdi_info[target].cyl * ps2esdi_info[target].sect;
+	resetup_one_dev(&ps2esdi_gendisk, target);
+
+	ps2esdi_valid[target] = 1;
+	wake_up(&ps2esdi_wait_open);
+
+	return (0);
+}
+
+void ps2esdi_reset_timer(unsigned long unused)
+{
+
+	int status;
+
+	status = inb(ESDI_INTRPT);
+	if ((status & 0xf) == INT_RESET) {
+		outb((status & 0xe0) | ATT_EOI, ESDI_ATTN);
+		outb(CTRL_ENABLE_INTR, ESDI_CONTROL);
+		reset_status = 1;
+	}
+	wake_up(&ps2esdi_int);
+}
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v2.1.14/linux/drivers/cdrom/cdu31a.c	Tue Oct 29 19:58:04 1996
+++ linux/drivers/cdrom/cdu31a.c	Wed Dec 11 16:05:16 1996
@@ -1979,8 +1979,7 @@
       return -EIO;
    }
 
-   err = verify_area(VERIFY_READ, (char *) arg, sizeof(schi)) ||
-         verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
+   err = verify_area(VERIFY_WRITE, (char *) arg, sizeof(schi));
    if (err) return err;
 
    copy_from_user(&schi, (char *) arg, sizeof(schi));
diff -u --recursive --new-file v2.1.14/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v2.1.14/linux/drivers/char/ChangeLog	Fri Nov 15 23:49:08 1996
+++ linux/drivers/char/ChangeLog	Sun Dec  8 21:21:15 1996
@@ -1,3 +1,57 @@
+Wed Dec  4 07:51:52 1996  Theodre Ts'o  <tytso@localhost.mit.edu>
+
+	* serial.c (change_speed): Use save_flags(); cli() and
+		restore_flags() in order to ensure we don't accidentally
+		turn on interrupts when starting up the port.
+		(startup): Move the insertion of serial structure into the
+		IRQ chain earlier into the startup processing.  Interrupts
+		should be off this whole time, but we eventually will want
+		to reduce this window.
+
+Thu Nov 21 10:05:22 1996  Theodre Ts'o  <tytso@localhost.mit.edu>
+
+	* tty_ioctl.c (tty_wait_until_sent): Always check the driver
+ 		wait_until_ready routine, even if there are no characters
+ 		in the xmit buffer.  (There may be charactes in the device
+ 		FIFO.)
+		(n_tty_ioctl): Add new flag tty->flow_stopped which
+ 		indicates whether the tty is stopped due to a request by
+ 		the TCXONC ioctl (used by tcflow).  If so, don't let an
+ 		incoming XOFF character restart the tty.  The tty can only
+ 		be restarted by another TCXONC request.
+
+	* tty_io.c (start_tty): Don't allow the tty to be restarted if
+		tty->flow_stopped is true.
+
+	* n_tty.c (n_tty_receive_char): If tty->flow_stopped is true, and
+ 		IXANY is set, don't eat a character trying to restart the
+ 		tty.
+
+	* serial.c (startup): Remove need for MCR_noint from the
+ 		async_struct structure.  Only turn on DTR and RTS if the
+ 		baud rate is not zero.
+		(change_speed): More accurately calculate the timeout
+ 		value based on the word size.  Move responsibility of
+ 		hangup when speed becomes B0 to rs_set_termios()
+		(set_serial_info): When changing the UART type set the
+ 		current xmit_fifo_size as well as the permanent
+ 		xmit_fifo_size.
+		(rs_ioctl): Fix TCSBRK (used by tcdrain) and TCSBRKP
+ 		ioctls to return EINTR if interrupted by a signal.
+		(rs_set_termios): If the baud rate changes to or from B0,
+ 		this function is now responsible for setting or clearing
+ 		DTR and RTS.  DTR and RTS are only be changed on the
+ 		transition to or from the B0 state.
+		(rs_close): Wait for the characters to drain based on
+ 		info->timeout.  At low baud rates (50bps), it may take a
+ 		long time for the FIFO to completely drain out!
+		(rs_wait_until_sent): Fixed timeout handling.  Now
+ 		releases control to the scheduler, but checks frequently
+ 		enough so that the function is sensitive enough to pass
+ 		the timing requirements of the NIST-PCTS.
+		(block_til_ready): When opening the device, don't turn on
+ 		DTR and RTS if the baud rate is B0.
+
 Thu Nov 14 00:06:09 1996  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
 	* serial.c (autoconfig): Fix autoconfiguration problems;
diff -u --recursive --new-file v2.1.14/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.14/linux/drivers/char/Config.in	Thu Aug  1 15:43:04 1996
+++ linux/drivers/char/Config.in	Thu Dec 12 16:51:08 1996
@@ -13,6 +13,11 @@
   tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
 fi
 tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
+tristate 'Hayes ESP serial port support' CONFIG_ESP
+if [ "$CONFIG_ESP" = "y" -o "$CONFIG_ESP" = "m" ]; then
+  int '  DMA channel' CONFIG_ESP_DMA_CHANNEL 1
+  int '  FIFO trigger level' CONFIG_ESP_TRIGGER_LEVEL 768
+fi
 tristate 'Parallel printer support' CONFIG_PRINTER
 
 
@@ -27,18 +32,25 @@
 	fi
 fi
 
-bool 'Support for user misc device modules' CONFIG_UMISC
+if [ "$CONFIG_MODULES" = "y" ]; then
+	bool 'Support for user misc device modules' CONFIG_UMISC
+fi
 
-bool 'QIC-02 tape support' CONFIG_QIC02_TAPE
-if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
-  bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF
-  if [ "$CONFIG_QIC02_DYNCONF" != "y" ]; then
+tristate 'QIC-02 tape support' CONFIG_QIC02_TAPE
+if [ "$CONFIG_QIC02_TAPE" = "y" -o "$CONFIG_QIC02_TAPE" = "m" ]; then
+  if [ "$CONFIG_QIC02_TAPE" = "y" ]; then
+    bool 'Do you want runtime configuration for QIC-02' CONFIG_QIC02_DYNCONF
+    if [ "$CONFIG_QIC02_DYNCONF" = "y" ]; then
+      comment 'Setting runtime QIC-02 configuration is done with qic02conf'
+      comment 'from the tpqic02-support package.  It is available at'
+      comment 'ftp://titus.cfw.com/pub/Linux/util/'
+    else
+      comment 'Edit configuration parameters in ./include/linux/tpqic02.h!'
+    fi
+  fi
+  if [ "$CONFIG_QIC02_TAPE" = "m" ]; then
     comment 'Edit configuration parameters in ./include/linux/tpqic02.h!'
-  else
-    comment 'Setting runtime QIC-02 configuration is done with qic02conf'
-    comment 'from the tpqic02-support package.  It is available at'
-    comment 'ftp://titus.cfw.com/pub/Linux/util/'
-fi
+  fi
 fi
 
 tristate 'Ftape (QIC-80/Travan) support' CONFIG_FTAPE
@@ -67,6 +79,11 @@
      tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
   fi
   tristate '   Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
+  if [ "$CONFIG_PCWATCHDOG" != "n" ]; then
+      bool '       Support for Revision A cards' CONFIG_PCWD_REV_A
+      bool '       Support for Revision C cards' CONFIG_PCWD_REV_C
+      bool '       Show card state on reset' CONFIG_PCWD_SHOW_PREVSTAT
+  fi
 fi
 bool 'Enhanced Real Time Clock Support' CONFIG_RTC
 endmenu
diff -u --recursive --new-file v2.1.14/linux/drivers/char/Makefile linux/drivers/char/Makefile
--- v2.1.14/linux/drivers/char/Makefile	Fri Nov 22 18:28:17 1996
+++ linux/drivers/char/Makefile	Wed Dec 11 17:16:07 1996
@@ -78,6 +78,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_ESP),y)
+L_OBJS += esp.o
+else
+  ifeq ($(CONFIG_ESP),m)
+  M_OBJS += esp.o
+  endif
+endif
+
 ifeq ($(CONFIG_ATIXL_BUSMOUSE),y)
 M = y
 L_OBJS += atixlmouse.o
@@ -171,7 +179,11 @@
 endif
 
 ifdef CONFIG_QIC02_TAPE
-L_OBJS += tpqic02.o 
+  ifeq ($(CONFIG_QIC02_TAPE),m)
+    M_OBJS += tpqic02.o
+  else
+    L_OBJS += tpqic02.o 
+  endif
 endif
 
 ifeq ($(CONFIG_FTAPE),y)
diff -u --recursive --new-file v2.1.14/linux/drivers/char/README.esp linux/drivers/char/README.esp
--- v2.1.14/linux/drivers/char/README.esp	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/README.esp	Wed Dec 11 17:16:07 1996
@@ -0,0 +1,94 @@
+HAYES ESP DRIVERS VERSION 1.0
+
+Features:
+
+- Uses the enhanced mode of the ESP card, allowing a wider range of
+  interrupts and features than compatibilty mode
+- Uses DMA to transfer data to and from the ESP's FIFOs, reducing CPU load
+- Supports primary and secondary ports
+
+The driver can be compiled as a module.  The module will be called 'esp.o'.
+The IRQs to use can be specified by using the irq= option.  The format is:
+
+irq=[0x100],[0x140],[0x180],[0x200],[0x240],[0x280],[0x300],[0x380]
+
+The address in brackets is the base address of the card.  The IRQ of
+nonexistant cards can be set to 0.  If and IRQ of a card that does exist is set
+to 0, the driver will attempt to guess at the correct IRQ.  For example, to set
+the IRQ of the card at address 0x300 to 12, the insmod command would be:
+
+insmod esp irq=0,0,0,0,0,0,12,0
+
+The custom divisor can be set by using the divisor= option.  The format is the
+same as for the irq= option.  Each divisor value is a series of hex digits,
+with each digit representing the divisor to use for a corresponding port.  The
+divisor value is constructed RIGHT TO LEFT.  Specifying a nonzero divisor value
+will automatically set the spd_cust flag.  To calculate the divisor to use for
+a certain baud rate, divide the port's base baud (921600) by the desired rate.
+For example, to set the divisor of the primary port at 0x300 to 4 and the
+divisor of the secondary port at 0x308 to 8, the insmod command would be:
+
+insmod esp divisor=0,0,0,0,0,0,0x84,0
+
+The dma= option can be used to set the DMA channel.  The channel can be either
+1 or 3.  For example, to set the dma channel to 3, the insmod command would be:
+
+insmod esp dma=3
+
+The trigger= option can be used to set the FIFO trigger levels.  This specifies
+when the ESP card should send an interrupt.  Larger values will decrease the
+number of interrupts; however, a value too high may result in data loss.
+Valid values are 1 through 1015, with 768 being the default.  For example, to
+set the trigger levels to 512 bytes, the insmod command would be:
+
+insmod esp trigger=512
+
+Multiple options can be listed on the insmod command line by separating each
+option with a space.  For example:
+
+insmod esp dma=3 trigger=512
+
+The esp module can be automatically loaded when needed.  To cause this to
+happen, add the following lines to /etc/conf.modules (replacing the last line
+with options for your configuration):
+
+alias char-major-57 esp
+alias char-major-58 esp
+options esp irq=0,0,0,0,0,0,3,0 divisor=0,0,0,0,0,0,0x4,0
+
+You may also need to run 'depmod -a'.
+
+Devices must be created manually.  To create the devices, note the output from
+the module after it is inserted.  The output will appear in the location where
+kernel messages usually appear (usually /var/adm/messages).  Create two devices
+for each 'tty' mentioned, one with major of 57 and the other with major of 58.
+The minor number should be the same as the tty number reported.  The commands
+would be (replace ? with the tty number):
+
+mknod /dev/ttyP? c 57 ?
+mknod /dev/cup? c 58 ?
+
+For example, if the following line appears:
+
+Oct 24 18:17:23 techno kernel: ttyP8 at 0x0140 (irq = 3) is an ESP primary port
+
+...two devices should be created:
+
+mknod /dev/ttyP8 c 57 8
+mknod /dev/cup8 c 58 8
+
+You may need to set the permissions on the devices:
+
+chmod 666 /dev/ttyP*
+chmod 666 /dev/cup*
+
+The ESP module and the serial module should not conflict (they can be used at
+the same time).  After the ESP module has been loaded the ports on the ESP card
+will no longer be accessable by the serial driver.
+
+If I/O errors are experienced when accessing the port, check for IRQ and DMA
+conflicts ('cat /proc/interrupts' and 'cat /proc/dma' for a list of IRQs and
+DMAs currently in use).
+
+Enjoy!
+Andrew J. Robinson <arobinso@nyx.net>
diff -u --recursive --new-file v2.1.14/linux/drivers/char/esp.c linux/drivers/char/esp.c
--- v2.1.14/linux/drivers/char/esp.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/esp.c	Wed Dec 11 17:16:07 1996
@@ -0,0 +1,2582 @@
+/*
+ *  esp.c - driver for Hayes ESP serial cards
+ *
+ *  --- Notices from serial.c, upon which this driver is based ---
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ *
+ *  Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92.  Now
+ *  much more extensible to support other serial cards based on the
+ *  16450/16550A UART's.  Added support for the AST FourPort and the
+ *  Accent Async board.  
+ *
+ *  set_serial_info fixed to set the flags, custom divisor, and uart
+ * 	type fields.  Fix suggested by Michael K. Johnson 12/12/92.
+ *
+ *  11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk>
+ *
+ *  03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
+ *
+ *  rs_set_termios fixed to look also for changes of the input
+ *      flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
+ *                                            Bernd Anhäupl 05/17/96.
+ *
+ * --- End of notices from serial.c ---
+ *
+ * Support for the ESP serial card by Andrew J. Robinson
+ *     <arobinso@nyx.net> (Card detection routine taken from a patch
+ *     by Dennis J. Boylan).  Patches to allow use with 2.1.x contributed
+ *     by Chris Faylor.
+ *
+ * This module exports the following rs232 io functions:
+ *
+ *	int esp_init(void);
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/config.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#include <asm/dma.h>
+#include <linux/malloc.h>
+#include <asm/uaccess.h>
+
+#include "esp.h"
+
+#define NR_PORTS 64	/* maximum number of ports */
+#define NR_PRIMARY 8	/* maximum number of primary ports */
+
+/* The following variables can be set by giving module options */
+static int irq[NR_PRIMARY] = {0,0,0,0,0,0,0,0};	/* IRQ for each base port */
+static unsigned int divisor[NR_PRIMARY] = {0,0,0,0,0,0,0,0};
+	/* custom divisor for each port */
+static unsigned int dma = CONFIG_ESP_DMA_CHANNEL; /* DMA channel */
+static unsigned int trigger = CONFIG_ESP_TRIGGER_LEVEL; /* FIFO trigger level */
+/* END */
+
+static char *dma_buffer;
+
+#define DMA_BUFFER_SZ 1024
+
+#define WAKEUP_CHARS 1024
+
+static char *serial_name = "ESP driver";
+static char *serial_version = "1.0";
+
+DECLARE_TASK_QUEUE(tq_esp);
+
+static struct tty_driver esp_driver, esp_callout_driver;
+static int serial_refcount;
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
+
+/*
+ * Serial driver configuration section.  Here are the various options:
+ *
+ * SERIAL_PARANOIA_CHECK
+ * 		Check the magic number for the esp_structure where
+ * 		ever possible.
+ */
+
+#undef SERIAL_PARANOIA_CHECK
+#define SERIAL_DO_RESTART
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+
+#define _INLINE_ inline
+  
+#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
+#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
+ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
+#else
+#define DBG_CNT(s)
+#endif
+
+static struct esp_struct *IRQ_ports[16];
+
+static void autoconfig(struct esp_struct * info);
+static void change_speed(struct esp_struct *info);
+	
+/*
+ * This assumes you have a 1.8432 MHz clock for your UART.
+ *
+ * It'd be nice if someone built a serial card with a 24.576 MHz
+ * clock, since the 16550A is capable of handling a top speed of 1.5
+ * megabits/second; but this requires the faster clock.
+ */
+#define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))
+
+/* Standard COM flags (except for COM4, because of the 8514 problem) */
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+static inline int serial_paranoia_check(struct esp_struct *info,
+					kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null esp_struct for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != ESP_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/*
+ * This is used to figure out the divisor speeds and the timeouts
+ */
+static int baud_table[] = {
+	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+	9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 };
+
+static inline unsigned int serial_in(struct esp_struct *info, int offset)
+{
+	return inb(info->port + offset);
+}
+
+static inline void serial_out(struct esp_struct *info, int offset, int value)
+{
+	outb(value, info->port+offset);
+}
+
+static inline int __get_order(unsigned long size)
+{
+	int order;
+
+	size = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
+	order = -1;
+	do {
+		size >>= 1;
+		order++;
+	} while (size);
+
+	return order;
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+	
+	save_flags(flags); cli();
+	if (info->IER & UART_IER_THRI) {
+		info->IER &= ~UART_IER_THRI;
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+		serial_out(info, UART_ESI_CMD2, info->IER);
+	}
+
+	restore_flags(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_start"))
+		return;
+	
+	save_flags(flags); cli();
+	if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+		serial_out(info, UART_ESI_CMD2, info->IER);
+	}
+	restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct esp_struct *info,
+				  int event)
+{
+	info->event |= 1 << event;
+	queue_task_irq_off(&info->tqueue, &tq_esp);
+	mark_bh(ESP_BH);
+}
+
+static _INLINE_ void receive_chars_dma(struct esp_struct *info, int *dma_bytes,
+	int *dma_direction, unsigned int *who_dma)
+{
+	unsigned int num_chars;
+
+        if (*dma_bytes) {
+		info->stat_flags |= STAT_NEED_DMA;
+        	return;
+	}
+
+	info->stat_flags &= ~(STAT_RX_TIMEOUT | STAT_NEED_DMA);
+
+	serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
+	serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
+	num_chars = serial_in(info, UART_ESI_STAT1) << 8;
+	num_chars |= serial_in(info, UART_ESI_STAT2);
+
+	if (!num_chars)
+		return;
+        
+        *dma_bytes = num_chars;
+	*dma_direction = DMA_MODE_READ;
+	*who_dma = info->port;
+        disable_dma(dma);
+        clear_dma_ff(dma);
+        set_dma_mode(dma, DMA_MODE_READ);
+        set_dma_addr(dma, virt_to_bus(dma_buffer));
+        set_dma_count(dma, num_chars);
+        enable_dma(dma);
+        serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX);
+}
+
+static void do_ttybuf(void *private_)
+{
+	struct esp_struct	*info = (struct esp_struct *) private_;
+	struct tty_struct	*tty;
+	int avail_bytes, x_bytes;
+	unsigned long int flags;
+
+	save_flags(flags); cli();
+	tty = info->tty;
+
+	if (!tty) {
+		restore_flags(flags);
+		return;
+	}
+
+	avail_bytes = TTY_FLIPBUF_SIZE - tty->flip.count;
+
+	if (avail_bytes) {
+		if (info->tty_buf->count < avail_bytes)
+			x_bytes = info->tty_buf->count;
+		else
+			x_bytes = avail_bytes;
+
+		tty->flip.count += x_bytes;
+		memcpy(tty->flip.char_buf_ptr, info->tty_buf->char_buf,
+			x_bytes);
+		memcpy(tty->flip.flag_buf_ptr, info->tty_buf->flag_buf,
+			x_bytes);
+		tty->flip.char_buf_ptr += x_bytes;
+		tty->flip.flag_buf_ptr += x_bytes;
+		info->tty_buf->count -= x_bytes;
+		info->tty_buf->char_buf_ptr -= x_bytes;
+		info->tty_buf->flag_buf_ptr -= x_bytes;
+
+		if (info->tty_buf->count) {
+			memmove(info->tty_buf->char_buf,
+				info->tty_buf->char_buf + x_bytes,
+				info->tty_buf->count);
+			queue_task_irq_off(&info->tty_buf->tqueue,
+						&tq_timer);
+		}
+
+		queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+	} else {
+		queue_task_irq_off(&info->tty_buf->tqueue, &tq_timer);
+	}
+
+	restore_flags(flags);
+}
+
+static _INLINE_ void receive_chars_dma_done(struct esp_struct *info, 
+	int *dma_bytes, int *dma_direction, unsigned int *who_dma, int status)
+{
+	struct tty_struct *tty = info->tty;
+	int num_bytes, bytes_left, x_bytes;
+	struct tty_flip_buffer *buffer;
+
+	if (!(*dma_bytes) || (*who_dma != info->port))
+		return;
+
+	disable_dma(dma);
+	clear_dma_ff(dma);
+
+	num_bytes = *dma_bytes - get_dma_residue(dma);
+
+	buffer = &(tty->flip);
+	bytes_left = num_bytes;
+
+	if (info->tty_buf->count && (tty->flip.count < TTY_FLIPBUF_SIZE))
+		do_ttybuf(info);
+
+	while (bytes_left > 0) {
+		if ((buffer->count + bytes_left) > TTY_FLIPBUF_SIZE)
+			x_bytes = TTY_FLIPBUF_SIZE - buffer->count;
+		else
+			x_bytes = bytes_left;
+
+		memcpy(buffer->char_buf_ptr,
+			 dma_buffer + (num_bytes - bytes_left), x_bytes);
+		buffer->char_buf_ptr += x_bytes;
+		buffer->count += x_bytes;
+		memset(buffer->flag_buf_ptr, 0, x_bytes);
+		buffer->flag_buf_ptr += x_bytes;
+		bytes_left -= x_bytes;
+
+		if (bytes_left > 0) {
+			if (buffer == info->tty_buf)
+				break;
+			else
+				buffer = info->tty_buf;
+		}
+	}
+
+	if (num_bytes > 0) {
+		buffer->flag_buf_ptr--;
+
+		status >>= 8;
+		status &= (0x1c & info->read_status_mask);
+
+		if (status & info->ignore_status_mask) {
+			buffer->count--;
+			buffer->char_buf_ptr--;
+			buffer->flag_buf_ptr--;
+		} else if (status & 0x10) {
+			*buffer->flag_buf_ptr = TTY_BREAK;
+			if (info->flags & ASYNC_SAK)
+				do_SAK(tty);
+		} else if (status & 0x08)
+			*buffer->flag_buf_ptr = TTY_FRAME;
+		else if (status & 0x04)
+			*buffer->flag_buf_ptr = TTY_PARITY;
+	
+		buffer->flag_buf_ptr++;
+		
+		if (buffer == info->tty_buf)
+			queue_task_irq_off(&info->tty_buf->tqueue, &tq_timer);
+
+		queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+	}
+
+	if (*dma_bytes != num_bytes) {
+		*dma_bytes = 0;
+		receive_chars_dma(info, dma_bytes, dma_direction, who_dma);
+	} else
+		*dma_bytes = 0;
+}
+
+static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int *dma_bytes,
+	int *dma_direction, unsigned int *who_dma)
+{
+	int count;
+	
+	if (*dma_bytes) {
+		info->stat_flags |= STAT_NEED_DMA;
+		return;
+	}
+
+	info->stat_flags &= ~STAT_NEED_DMA;
+
+	if ((info->xmit_cnt <= 0) || info->tty->stopped ||
+    		info->tty->hw_stopped) {
+		info->IER &= ~UART_IER_THRI;
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+		serial_out(info, UART_ESI_CMD2, info->IER);
+		return;
+	}
+	
+	serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
+	serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
+	count = serial_in(info, UART_ESI_STAT1) << 8;
+	count |= serial_in(info, UART_ESI_STAT2);
+
+	if (!count)
+		return;
+
+	count = ((count < info->xmit_cnt) ? count : info->xmit_cnt);
+
+	if (info->xmit_tail + count <= ESP_XMIT_SIZE) {
+		memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
+			count);
+	} else {
+		int i = ESP_XMIT_SIZE - info->xmit_tail;
+		memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
+			i);
+		memcpy(&(dma_buffer[i]), info->xmit_buf, count - i);
+	}
+
+	info->xmit_cnt -= count;
+	info->xmit_tail = (info->xmit_tail + count) & (ESP_XMIT_SIZE - 1);
+
+	if (info->xmit_cnt < WAKEUP_CHARS)
+		rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+		printk("THRE...");
+#endif
+
+	if (info->xmit_cnt <= 0) {
+		info->IER &= ~UART_IER_THRI;
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+		serial_out(info, UART_ESI_CMD2, info->IER);
+	}
+
+        *dma_bytes = count;
+	*dma_direction = DMA_MODE_WRITE;
+	*who_dma = info->port;
+        disable_dma(dma);
+        clear_dma_ff(dma);
+        set_dma_mode(dma, DMA_MODE_WRITE);
+        set_dma_addr(dma, virt_to_bus(dma_buffer));
+        set_dma_count(dma, count);
+        enable_dma(dma);
+        serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
+}
+
+static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info,
+	int *dma_bytes, int *dma_direction, unsigned int *who_dma)
+{
+	int num_bytes;
+
+	if (!(*dma_bytes) || (*who_dma != info->port))
+		return;
+
+	disable_dma(dma);
+	clear_dma_ff(dma);
+
+	num_bytes = *dma_bytes - get_dma_residue(dma);
+
+	if (*dma_bytes != num_bytes)
+	{
+		*dma_bytes -= num_bytes;
+		memmove(dma_buffer, dma_buffer + num_bytes, *dma_bytes);
+		*dma_direction = DMA_MODE_WRITE;
+        	disable_dma(dma);
+        	clear_dma_ff(dma);
+        	set_dma_mode(dma, DMA_MODE_WRITE);
+        	set_dma_addr(dma, virt_to_bus(dma_buffer));
+        	set_dma_count(dma, *dma_bytes);
+        	enable_dma(dma);
+        	serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
+	} else
+		*dma_bytes = 0;
+}
+
+static _INLINE_ void check_modem_status(struct esp_struct *info)
+{
+	int	status;
+	
+	serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
+	status = serial_in(info, UART_ESI_STAT2);
+
+	if (status & UART_MSR_ANY_DELTA) {
+		/* update input line counters */
+		if (status & UART_MSR_TERI)
+			info->icount.rng++;
+		if (status & UART_MSR_DDSR)
+			info->icount.dsr++;
+		if (status & UART_MSR_DDCD)
+			info->icount.dcd++;
+		if (status & UART_MSR_DCTS)
+			info->icount.cts++;
+		wake_up_interruptible(&info->delta_msr_wait);
+	}
+
+	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttys%d CD now %s...", info->line,
+		       (status & UART_MSR_DCD) ? "on" : "off");
+#endif		
+		if (status & UART_MSR_DCD)
+			wake_up_interruptible(&info->open_wait);
+		else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			   (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("scheduling hangup...");
+#endif
+			queue_task_irq_off(&info->tqueue_hangup,
+					   &tq_scheduler);
+		}
+	}
+}
+
+static _INLINE_ void tx_flowed_on(struct esp_struct *info)
+{
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+	printk("CTS tx start...");
+#endif
+	info->tty->hw_stopped = 0;
+	info->IER |= UART_IER_THRI;
+	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); /* set mask */
+	serial_out(info, UART_ESI_CMD2, info->IER);
+	rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP);
+}
+
+static _INLINE_ void tx_flowed_off(struct esp_struct *info)
+{
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+	printk("CTS tx stop...");
+#endif
+	info->tty->hw_stopped = 1;
+	info->IER &= ~UART_IER_THRI;
+	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); /* set mask */
+	serial_out(info, UART_ESI_CMD2, info->IER);
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct esp_struct * info, *stop_port;
+	unsigned err_status;
+	unsigned int scratch;
+	int pre_bytes;
+	int check_dma_only = 0;
+	static int dma_bytes;
+	static int dma_direction;
+	static int who_dma;
+	
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+	
+	/* This routine will currently check ALL ports when an interrupt */
+	/* is received from ANY port */
+
+	stop_port = info = IRQ_ports[irq];
+
+	if (!info)
+		return;
+
+	do {
+		if (!info->tty || (check_dma_only &&
+			!(info->stat_flags & STAT_NEED_DMA))) {
+			info = info->next_port;
+			continue;
+		}
+
+		pre_bytes = dma_bytes;
+		err_status = 0;
+		scratch = serial_in(info, UART_ESI_SID);
+		if (scratch & 0x04) { /* error - check for rx timeout */
+			serial_out(info, UART_ESI_CMD1, ESI_GET_ERR_STAT);
+			err_status = serial_in(info, UART_ESI_STAT1) << 8;
+			err_status |= serial_in(info, UART_ESI_STAT2);
+
+			if (err_status & 0x0100)
+				info->stat_flags |= STAT_RX_TIMEOUT;
+
+			if (err_status & 0x2000)	/* UART status */
+				check_modem_status(info);
+
+			if (err_status & 0x8000)	/* Start break */
+				wake_up_interruptible(&info->break_wait);
+
+			if (err_status & 0x0002)	/* tx off */
+				tx_flowed_off(info);
+
+			if (err_status & 0x0004)	/* tx on */
+				tx_flowed_on(info);
+		}
+		
+		if ((scratch & 0x88)  || /* DMA completed or timed out */
+			(err_status & 0x1c00) /* receive error */)
+			if (dma_direction == DMA_MODE_READ)
+				receive_chars_dma_done(info, &dma_bytes,
+					&dma_direction, &who_dma, err_status);
+			else
+				transmit_chars_dma_done(info, &dma_bytes,
+					&dma_direction, &who_dma);
+
+		if (((scratch & 0x01) ||
+			(info->stat_flags & STAT_RX_TIMEOUT)) &&
+			(info->IER & UART_IER_RDI))
+			receive_chars_dma(info, &dma_bytes, &dma_direction,
+				&who_dma);
+
+		if ((scratch & 0x02) && (info->IER & UART_IER_THRI))
+			transmit_chars_dma(info, &dma_bytes, &dma_direction,
+				&who_dma);
+
+		info->last_active = jiffies;
+
+		if (pre_bytes && !dma_bytes)  /* released DMA */
+			stop_port = info;
+
+		info = info->next_port;
+
+		if ((info->irq != irq) || (info == IRQ_ports[irq]))
+			check_dma_only = 1;
+
+		if (check_dma_only && dma_bytes)
+			info = stop_port;
+	} while (info != stop_port);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_esp);
+}
+
+static void do_softint(void *private_)
+{
+	struct esp_struct	*info = (struct esp_struct *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * 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_serial_hangup() -> tty->hangup() -> esp_hangup()
+ * 
+ */
+static void do_serial_hangup(void *private_)
+{
+	struct esp_struct	*info = (struct esp_struct *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	tty_hangup(tty);
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver:  routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port.  Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+static void esp_basic_init(struct esp_struct * info)
+{
+	/* put ESPC in enhanced mode */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_MODE);
+	serial_out(info, UART_ESI_CMD2, 0x31);
+
+	/* disable interrupts for now */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+	serial_out(info, UART_ESI_CMD2, 0x00);
+
+	/* set interrupt and DMA channel */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_IRQ);
+	serial_out(info, UART_ESI_CMD2, (dma << 4) | 0x01);
+	serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ);
+	if (info->line % 8)	/* secondary port */
+		serial_out(info, UART_ESI_CMD2, 0x0d);	/* shared */
+	else if (info->irq == 9)
+		serial_out(info, UART_ESI_CMD2, 0x02);
+	else
+		serial_out(info, UART_ESI_CMD2, info->irq);
+
+	/* set error status mask (check this) */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_ERR_MASK);
+	serial_out(info, UART_ESI_CMD2, 0xbd);
+	serial_out(info, UART_ESI_CMD2, 0x06);
+
+	/* set DMA timeout */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_DMA_TMOUT);
+	serial_out(info, UART_ESI_CMD2, 0xff);
+
+	/* set FIFO trigger levels */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
+	serial_out(info, UART_ESI_CMD2, trigger / 256);
+	serial_out(info, UART_ESI_CMD2, trigger % 256);
+	serial_out(info, UART_ESI_CMD2, trigger / 256);
+	serial_out(info, UART_ESI_CMD2, trigger % 256);
+
+	/* Set clock scaling */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_PRESCALAR);
+	serial_out(info, UART_ESI_CMD2, ESPC_SCALE);
+}
+
+static int startup(struct esp_struct * info)
+{
+	unsigned long flags;
+	int	retval;
+	int next_irq;
+	struct esp_struct *next_info = 0;
+        unsigned int num_chars;
+
+	save_flags(flags); cli();
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		restore_flags(flags);
+		return 0;
+	}
+
+	if (!info->port || !info->type) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		restore_flags(flags);
+		return 0;
+	}
+
+	if (!info->xmit_buf) {
+		info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL);
+		if (!info->xmit_buf) {
+			restore_flags(flags);
+			return -ENOMEM;
+		}
+	}
+
+	if (!info->tty_buf) {
+		info->tty_buf = (struct tty_flip_buffer *)kmalloc(
+			sizeof(struct tty_flip_buffer), GFP_KERNEL);
+
+		if (!info->tty_buf) {
+			free_page((unsigned long) info->xmit_buf);
+			info->xmit_buf = 0;
+			restore_flags(flags);
+			return -ENOMEM;
+		}
+
+		memset(info->tty_buf, 0, sizeof(struct tty_flip_buffer));
+		info->tty_buf->tqueue.routine = do_ttybuf;
+		info->tty_buf->tqueue.data = info;
+	} 
+
+	info->tty_buf->count = 0;
+	info->tty_buf->char_buf_ptr = info->tty_buf->char_buf;
+	info->tty_buf->flag_buf_ptr = info->tty_buf->flag_buf;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttys%d (irq %d)...", info->line, info->irq);
+#endif
+
+	/* Flush the RX buffer.  Using the ESI flush command may cause */
+	/* wild interrupts, so read all the data instead. */
+
+	serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
+	serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
+	num_chars = serial_in(info, UART_ESI_STAT1) << 8;
+	num_chars |= serial_in(info, UART_ESI_STAT2);
+
+	while (num_chars > 1) {
+		inw(info->port + UART_ESI_RX);
+		num_chars -= 2;
+	}
+
+	if (num_chars)
+		serial_in(info, UART_ESI_RX);
+
+	/* set receive character timeout */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
+	serial_out(info, UART_ESI_CMD2, 0xff);
+
+	info->stat_flags = 0;
+
+	/*
+	 * Allocate the IRQ if necessary
+	 */
+	if (!IRQ_ports[info->irq]) {
+		retval = request_irq(info->irq, rs_interrupt_single,
+					SA_INTERRUPT, "esp", NULL);
+
+		if (!retval) {
+			int i = 1;
+
+			while ((i < 16) && !IRQ_ports[i])
+				i++;	
+
+			if (i == 16) {
+				dma_buffer = (char *)__get_dma_pages(GFP_KERNEL,
+					__get_order(DMA_BUFFER_SZ));
+
+				if (!dma_buffer)
+					retval = -ENOMEM;
+				else
+					retval = request_dma(dma, "esp");
+
+				if (retval)
+					free_irq(info->irq, NULL);
+			}
+		}
+
+		if (retval) {
+			restore_flags(flags);
+			if (suser()) {
+				if (info->tty)
+					set_bit(TTY_IO_ERROR,
+						&info->tty->flags);
+				return 0;
+			} else
+				return retval;
+		}
+	}
+
+	info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+	info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
+#if defined(__alpha__) && !defined(CONFIG_PCI)
+	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
+	info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
+#endif
+
+	serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+	serial_out(info, UART_ESI_CMD2, UART_MCR);
+	serial_out(info, UART_ESI_CMD2, info->MCR);
+	
+	/*
+	 * Finally, enable interrupts
+	 */
+	/* info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; */
+	info->IER = UART_IER_RLSI | UART_IER_RDI | UART_IER_DMA_TMOUT |
+			UART_IER_DMA_TC;
+	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+	serial_out(info, UART_ESI_CMD2, info->IER);
+	
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+	/* Remove port from "closed" chain */
+	if (info->next_port)
+		info->next_port->prev_port = info->prev_port;
+	if (info->prev_port)
+		info->prev_port->next_port = info->next_port;
+	else
+		IRQ_ports[0] = info->next_port;
+
+	/*
+	 * Insert serial port into IRQ chain.
+	 */
+	next_irq = info->irq;
+
+	do {
+		next_info = IRQ_ports[next_irq];
+		
+		if (++next_irq > 15)
+			next_irq = 1;
+	} while (!next_info && (next_irq != info->irq));
+
+	if (!next_info) {
+		info->next_port = info;
+		info->prev_port = info;
+	} else {
+		info->next_port = next_info;
+		info->prev_port = next_info->prev_port;
+		next_info->prev_port->next_port = info;
+		next_info->prev_port = info;
+	}
+
+	IRQ_ports[info->irq] = info;
+
+	/*
+	 * set the speed of the serial port
+	 */
+	change_speed(info);
+
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct esp_struct * info)
+{
+	unsigned long	flags;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       info->irq);
+#endif
+	
+	save_flags(flags); cli(); /* Disable interrupts */
+
+	/*
+	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+	 * here so the queue might never be waken up
+	 */
+	wake_up_interruptible(&info->delta_msr_wait);
+	wake_up_interruptible(&info->break_wait);
+	
+	/*
+	 * First unlink the serial port from the IRQ chain...
+	 */
+	info->next_port->prev_port = info->prev_port;
+	info->prev_port->next_port = info->next_port;
+
+	if (IRQ_ports[info->irq] == info) {
+		if ((info->next_port == info) ||
+			(info->next_port->irq != info->irq))
+			IRQ_ports[info->irq] = 0;
+		else
+			IRQ_ports[info->irq] = info->next_port;
+	}
+
+	/* Stick it on the "closed" chain */
+	info->next_port = IRQ_ports[0];
+	if (info->next_port)
+		info->next_port->prev_port = info;
+	info->prev_port = 0;
+	IRQ_ports[0] = info;
+	
+	/*
+	 * Free the IRQ, if necessary
+	 */
+	if (!IRQ_ports[info->irq]) {
+		int i = 1;
+
+		while ((i < 16) && !IRQ_ports[i])
+			i++;
+
+		if (i == 16) {
+			free_dma(dma);
+			free_pages((unsigned int)dma_buffer, 
+				__get_order(DMA_BUFFER_SZ));
+			dma_buffer = 0;
+		}
+
+		free_irq(info->irq, NULL);
+	}
+
+	if (info->xmit_buf) {
+		free_page((unsigned long) info->xmit_buf);
+		info->xmit_buf = 0;
+	}
+
+	if (info->tty_buf && !info->tty_buf->tqueue.sync) {
+		kfree(info->tty_buf);
+		info->tty_buf = 0;
+	}
+
+	info->IER = 0;
+	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+	serial_out(info, UART_ESI_CMD2, 0x00);
+
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+		info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+		info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);
+	}
+
+	serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+	serial_out(info, UART_ESI_CMD2, UART_MCR);
+	serial_out(info, UART_ESI_CMD2, info->MCR_noint);
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+	
+	info->flags &= ~ASYNC_INITIALIZED;
+	restore_flags(flags);
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct esp_struct *info)
+{
+	unsigned short port;
+	int	quot = 0;
+	unsigned cflag,cval;
+	int	i;
+	unsigned char flow1 = 0, flow2 = 0;
+
+	if (!info->tty || !info->tty->termios)
+		return;
+	cflag = info->tty->termios->c_cflag;
+	if (!(port = info->port))
+		return;
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (i < 1 || i > 2) 
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			i += 15;
+	}
+	if (i == 15) {
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			i += 1;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			i += 2;
+                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                        i += 3;
+                if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                        i += 4;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
+			quot = info->custom_divisor;
+	}
+	if (quot) {
+		info->timeout = ((1024*HZ*15*quot) /
+				 info->baud_base) + 2;
+	} else if (baud_table[i] == 134) {
+		quot = (2*info->baud_base / 269);
+		info->timeout = (1024*HZ*30/269) + 2;
+	} else if (baud_table[i]) {
+		quot = info->baud_base / baud_table[i];
+		info->timeout = (1024*HZ*15/baud_table[i]) + 2;
+	} else {
+		quot = 0;
+		info->timeout = 0;
+	}
+	if (quot) {
+		info->MCR |= UART_MCR_DTR;
+		info->MCR_noint |= UART_MCR_DTR;
+		cli();
+		serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+		serial_out(info, UART_ESI_CMD2, UART_MCR);
+		serial_out(info, UART_ESI_CMD2, info->MCR);
+		sti();
+	} else {
+		info->MCR &= ~UART_MCR_DTR;
+		info->MCR_noint &= ~UART_MCR_DTR;
+		cli();
+		serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+		serial_out(info, UART_ESI_CMD2, UART_MCR);
+		serial_out(info, UART_ESI_CMD2, info->MCR);
+		sti();
+		return;
+	}
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	      case CS5: cval = 0x00; break;
+	      case CS6: cval = 0x01; break;
+	      case CS7: cval = 0x02; break;
+	      case CS8: cval = 0x03; break;
+	      default:  cval = 0x00; break;	/* too keep GCC shut... */
+	}
+	if (cflag & CSTOPB) {
+		cval |= 0x04;
+	}
+	if (cflag & PARENB)
+		cval |= UART_LCR_PARITY;
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+	
+	/* CTS flow control flag and modem status interrupts */
+	/* info->IER &= ~UART_IER_MSI; */
+	if (cflag & CRTSCTS) {
+		info->flags |= ASYNC_CTS_FLOW;
+		/* info->IER |= UART_IER_MSI; */
+		flow1 = 0x04;
+		flow2 = 0x10;
+	} else
+		info->flags &= ~ASYNC_CTS_FLOW;
+	if (cflag & CLOCAL)
+		info->flags &= ~ASYNC_CHECK_CD;
+	else {
+		info->flags |= ASYNC_CHECK_CD;
+		/* info->IER |= UART_IER_MSI; */
+	}
+
+	/*
+	 * Set up parity check flag
+	 */
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+	info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+	if (I_INPCK(info->tty))
+		info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+	if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
+		info->read_status_mask |= UART_LSR_BI;
+	
+	info->ignore_status_mask = 0;
+#if 0
+	/* This should be safe, but for some broken bits of hardware... */
+	if (I_IGNPAR(info->tty)) {
+		info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+		info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
+	}
+#endif
+	if (I_IGNBRK(info->tty)) {
+		info->ignore_status_mask |= UART_LSR_BI;
+		info->read_status_mask |= UART_LSR_BI;
+		/*
+		 * If we're ignore parity and break indicators, ignore 
+		 * overruns too.  (For real raw support).
+		 */
+		if (I_IGNPAR(info->tty)) {
+			info->ignore_status_mask |= UART_LSR_OE | \
+				UART_LSR_PE | UART_LSR_FE;
+			info->read_status_mask |= UART_LSR_OE | \
+				UART_LSR_PE | UART_LSR_FE;
+		}
+	}
+
+	if (I_IXOFF(info->tty))
+		flow1 |= 0x81;
+
+	cli();
+	/* set baud */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_BAUD);
+	serial_out(info, UART_ESI_CMD2, quot >> 8);
+	serial_out(info, UART_ESI_CMD2, quot & 0xff);
+
+	/* set data bits, parity, etc. */
+	serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+	serial_out(info, UART_ESI_CMD2, UART_LCR);
+	serial_out(info, UART_ESI_CMD2, cval);
+
+	/* Enable flow control */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CNTL);
+	serial_out(info, UART_ESI_CMD2, flow1);
+	serial_out(info, UART_ESI_CMD2, flow2);
+
+	/* set flow control characters (XON/XOFF only) */
+	if (I_IXOFF(info->tty)) {
+		serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CHARS);
+		serial_out(info, UART_ESI_CMD2, START_CHAR(info->tty));
+		serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->tty));
+		serial_out(info, UART_ESI_CMD2, 0x10);
+		serial_out(info, UART_ESI_CMD2, 0x21);
+		switch (cflag & CSIZE) {
+			case CS5:
+				serial_out(info, UART_ESI_CMD2, 0x1f);
+				break;
+			case CS6:
+				serial_out(info, UART_ESI_CMD2, 0x3f);
+				break;
+			case CS7:
+			case CS8:
+				serial_out(info, UART_ESI_CMD2, 0x7f);
+				break;
+			default:
+				serial_out(info, UART_ESI_CMD2, 0xff);
+				break;
+		}
+	}
+
+	/* Set high/low water */
+	serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL);
+	serial_out(info, UART_ESI_CMD2, 0x03);
+	serial_out(info, UART_ESI_CMD2, 0xfc);
+	serial_out(info, UART_ESI_CMD2, (trigger + 4) / 256);
+	serial_out(info, UART_ESI_CMD2, (trigger + 4) % 256);
+
+	sti();
+}
+
+static void rs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_put_char"))
+		return;
+
+	if (!tty || !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	if (info->xmit_cnt >= ESP_XMIT_SIZE - 1) {
+		restore_flags(flags);
+		return;
+	}
+
+	info->xmit_buf[info->xmit_head++] = ch;
+	info->xmit_head &= ESP_XMIT_SIZE-1;
+	info->xmit_cnt++;
+	restore_flags(flags);
+}
+
+static void rs_flush_chars(struct tty_struct *tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+	unsigned long flags;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
+		return;
+
+	if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
+	    !info->xmit_buf)
+		return;
+
+	save_flags(flags); cli();
+	if (!(info->IER & UART_IER_THRI)) {
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+		serial_out(info, UART_ESI_CMD2, info->IER);
+	}
+	restore_flags(flags);
+}
+
+static int rs_write(struct tty_struct * tty, int from_user,
+		    const unsigned char *buf, int count)
+{
+	int	c, total = 0;
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+	unsigned long flags;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_write"))
+		return 0;
+
+	if (!tty || !info->xmit_buf || !tmp_buf)
+		return 0;
+	    
+	if (from_user)
+		down(&tmp_buf_sem);
+	save_flags(flags);
+	while (1) {
+		cli();		
+		c = MIN(count, MIN(ESP_XMIT_SIZE - info->xmit_cnt - 1,
+				   ESP_XMIT_SIZE - info->xmit_head));
+		if (c <= 0)
+			break;
+
+		if (from_user) {
+			copy_from_user(tmp_buf, buf, c);
+			c = MIN(c, MIN(ESP_XMIT_SIZE - info->xmit_cnt - 1,
+				       ESP_XMIT_SIZE - info->xmit_head));
+			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+		} else
+			memcpy(info->xmit_buf + info->xmit_head, buf, c);
+		info->xmit_head = (info->xmit_head + c) & (ESP_XMIT_SIZE-1);
+		info->xmit_cnt += c;
+		restore_flags(flags);
+		buf += c;
+		count -= c;
+		total += c;
+	}
+	if (from_user)
+		up(&tmp_buf_sem);
+	if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
+	    !(info->IER & UART_IER_THRI)) {
+		info->IER |= UART_IER_THRI;
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK); /* set mask */
+		serial_out(info, UART_ESI_CMD2, info->IER);
+	}
+	restore_flags(flags);
+	return total;
+}
+
+static int rs_write_room(struct tty_struct *tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+	int	ret;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_write_room"))
+		return 0;
+	ret = ESP_XMIT_SIZE - info->xmit_cnt - 1;
+	if (ret < 0)
+		ret = 0;
+	return ret;
+}
+
+static int rs_chars_in_buffer(struct tty_struct *tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
+		return 0;
+	return info->xmit_cnt;
+}
+
+static void rs_flush_buffer(struct tty_struct *tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+				
+	if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
+		return;
+	cli();
+	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+	sti();
+	wake_up_interruptible(&tty->write_wait);
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_throttle()
+ * 
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void rs_throttle(struct tty_struct * tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "rs_throttle"))
+		return;
+	
+	info->IER &= ~UART_IER_RDI;
+	cli();
+	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+	serial_out(info, UART_ESI_CMD2, info->IER);
+	serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
+	serial_out(info, UART_ESI_CMD2, 0x00);
+	sti();
+}
+
+static void rs_unthrottle(struct tty_struct * tty)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+#ifdef SERIAL_DEBUG_THROTTLE
+	char	buf[64];
+	
+	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	       tty->ldisc.chars_in_buffer(tty));
+#endif
+
+	if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
+		return;
+	
+	info->IER |= UART_IER_RDI;
+	cli();
+	serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+	serial_out(info, UART_ESI_CMD2, info->IER);
+	serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
+	serial_out(info, UART_ESI_CMD2, 0xff);
+	sti();
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+static int get_serial_info(struct esp_struct * info,
+			   struct serial_struct * retinfo)
+{
+	struct serial_struct tmp;
+  
+	if (!retinfo)
+		return -EFAULT;
+	memset(&tmp, 0, sizeof(tmp));
+	tmp.type = info->type;
+	tmp.line = info->line;
+	tmp.port = info->port;
+	tmp.irq = info->irq;
+	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
+	tmp.close_delay = info->close_delay;
+	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
+	tmp.hub6 = 0;
+	copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+	return 0;
+}
+
+static int set_serial_info(struct esp_struct * info,
+			   struct serial_struct * new_info)
+{
+	struct serial_struct new_serial;
+	struct esp_struct old_info;
+	unsigned int		change_irq;
+	int 			retval = 0;
+	struct esp_struct *current_async;
+	unsigned long flags = 0;
+
+	if (!new_info)
+		return -EFAULT;
+	copy_from_user(&new_serial,new_info,sizeof(new_serial));
+	old_info = *info;
+
+	if ((info->type != new_serial.type) ||
+		(new_serial.hub6) ||
+		(info->port != new_serial.port) ||
+		(info->baud_base != new_serial.baud_base) ||
+		(new_serial.irq > 15) ||
+		(new_serial.irq < 1))
+		return -EINVAL;
+
+	change_irq = new_serial.irq != info->irq;
+
+	if (change_irq && (info->line % 8))
+		return -EINVAL;
+
+	if (!suser()) {
+		if (change_irq || 
+		    (new_serial.baud_base != info->baud_base) ||
+		    (new_serial.close_delay != info->close_delay) ||
+		    ((new_serial.flags & ~ASYNC_USR_MASK) !=
+		     (info->flags & ~ASYNC_USR_MASK)))
+			return -EPERM;
+		info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+			       (new_serial.flags & ASYNC_USR_MASK));
+		info->custom_divisor = new_serial.custom_divisor;
+		goto check_and_exit;
+	}
+
+	if (new_serial.irq == 2)
+		new_serial.irq = 9;
+
+	if (change_irq) {
+		save_flags(flags); cli();
+
+		current_async = info;
+		do {
+			if ((current_async->line >= info->line) &&
+				(current_async->line < (info->line + 8))) {
+				if (current_async == info) {
+					if (current_async->count > 1) {
+						restore_flags(flags);
+						return -EBUSY;
+					}
+				} else {
+					restore_flags(flags);
+					return -EBUSY;
+				}
+			}
+			
+			current_async = current_async->next_port;
+		} while (current_async != info);
+	}
+
+	/*
+	 * OK, past this point, all the error checking has been done.
+	 * At this point, we start making changes.....
+	 */
+
+	info->baud_base = new_serial.baud_base;
+	info->flags = ((info->flags & ~ASYNC_FLAGS) |
+			(new_serial.flags & ASYNC_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
+	info->close_delay = new_serial.close_delay * HZ/100;
+	info->closing_wait = new_serial.closing_wait * HZ/100;
+
+	release_region(info->port,8);
+	if (change_irq) {
+		/*
+		 * We need to shutdown the serial port at the old
+		 * port/irq combination.
+		 */
+		shutdown(info);
+
+		current_async = IRQ_ports[0];
+		while (current_async != 0) {
+			if ((current_async->line >= info->line) &&
+				(current_async->line < (info->line + 8)))
+				current_async->irq = new_serial.irq;
+
+			current_async = current_async->next_port;
+		}
+
+		serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ);
+		if (info->irq == 9)
+			serial_out(info, UART_ESI_CMD2, 0x02);
+		else
+			serial_out(info, UART_ESI_CMD2, info->irq);
+
+		restore_flags(flags);
+	}
+	if(info->type != PORT_UNKNOWN)
+		request_region(info->port,8,"esp(set)");
+
+	
+check_and_exit:
+	if (!info->port || !info->type)
+		return 0;
+	if (info->flags & ASYNC_INITIALIZED) {
+		if (((old_info.flags & ASYNC_SPD_MASK) !=
+		     (info->flags & ASYNC_SPD_MASK)) ||
+		    (old_info.custom_divisor != info->custom_divisor))
+			change_speed(info);
+	} else
+		retval = startup(info);
+	return retval;
+}
+
+
+/*
+ * get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * 	    is emptied.  On bus types like RS485, the transmitter must
+ * 	    release the bus after transmitting. This must be done when
+ * 	    the transmit shift register is empty, not be done when the
+ * 	    transmit holding register is empty.  This functionality
+ * 	    allows an RS485 driver to be written in user space. 
+ */
+static int get_lsr_info(struct esp_struct * info, unsigned int *value)
+{
+	unsigned char status;
+	unsigned int result;
+
+	cli();
+	serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
+	status = serial_in(info, UART_ESI_STAT1);
+	sti();
+	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+	return put_user(result,value);
+}
+
+
+static int get_modem_info(struct esp_struct * info, unsigned int *value)
+{
+	unsigned char control, status;
+	unsigned int result;
+
+	control = info->MCR;
+	cli();
+	serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
+	status = serial_in(info, UART_ESI_STAT2);
+	sti();
+	result =  ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+		| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+		| ((status  & UART_MSR_DCD) ? TIOCM_CAR : 0)
+		| ((status  & UART_MSR_RI) ? TIOCM_RNG : 0)
+		| ((status  & UART_MSR_DSR) ? TIOCM_DSR : 0)
+		| ((status  & UART_MSR_CTS) ? TIOCM_CTS : 0);
+	return put_user(result,value);
+}
+
+static int set_modem_info(struct esp_struct * info, unsigned int cmd,
+			  unsigned int *value)
+{
+	int error;
+	unsigned int arg;
+
+	error = get_user(arg, value);
+	if (error)
+		return error;
+
+	switch (cmd) {
+	case TIOCMBIS: 
+		if (arg & TIOCM_RTS) {
+			info->MCR |= UART_MCR_RTS;
+			info->MCR_noint |= UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR |= UART_MCR_DTR;
+			info->MCR_noint |= UART_MCR_DTR;
+		}
+		break;
+	case TIOCMBIC:
+		if (arg & TIOCM_RTS) {
+			info->MCR &= ~UART_MCR_RTS;
+			info->MCR_noint &= ~UART_MCR_RTS;
+		}
+		if (arg & TIOCM_DTR) {
+			info->MCR &= ~UART_MCR_DTR;
+			info->MCR_noint &= ~UART_MCR_DTR;
+		}
+		break;
+	case TIOCMSET:
+		info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		info->MCR_noint = ((info->MCR_noint
+				    & ~(UART_MCR_RTS | UART_MCR_DTR))
+				   | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+				   | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
+		break;
+	default:
+		return -EINVAL;
+	}
+	cli();
+	serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+	serial_out(info, UART_ESI_CMD2, UART_MCR);
+	serial_out(info, UART_ESI_CMD2, info->MCR);
+	sti();
+	return 0;
+}
+
+static int do_autoconfig(struct esp_struct * info)
+{
+	int			retval;
+	
+	if (!suser())
+		return -EPERM;
+	
+	if (info->count > 1)
+		return -EBUSY;
+	
+	shutdown(info);
+
+	cli();
+	autoconfig(info);
+	sti();
+
+	retval = startup(info);
+	if (retval)
+		return retval;
+	return 0;
+}
+
+
+/*
+ * This routine sends a break character out the serial port.
+ */
+static void send_break(	struct esp_struct * info, int duration)
+{
+	if (!info->port)
+		return;
+	cli();
+	serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+	serial_out(info, UART_ESI_CMD2, 0x01);
+
+	interruptible_sleep_on(&info->break_wait);
+
+	current->state = TASK_INTERRUPTIBLE;
+	current->timeout = jiffies + duration;
+	schedule();
+
+	serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
+	serial_out(info, UART_ESI_CMD2, 0x00);
+	sti();
+}
+
+static int rs_ioctl(struct tty_struct *tty, struct file * file,
+		    unsigned int cmd, unsigned long arg)
+{
+	int error;
+	struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+	int retval;
+	struct async_icount cprev, cnow;	/* kernel counter temps */
+	struct serial_icounter_struct *p_cuser;	/* user space */
+
+	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
+		return -ENODEV;
+
+	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
+	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
+	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
+	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
+		if (tty->flags & (1 << TTY_IO_ERROR))
+		    return -EIO;
+	}
+	
+	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)
+				send_break(info, 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);
+			send_break(info, arg ? arg*(HZ/10) : HZ/4);
+			return 0;
+		case TIOCGSOFTCAR:
+			return put_user(C_CLOCAL(tty) ? 1 : 0,
+				    (int *) arg);
+		case TIOCSSOFTCAR:
+			error = get_user(arg, (unsigned int *)arg);
+			if (error)
+				return error;
+			tty->termios->c_cflag =
+				((tty->termios->c_cflag & ~CLOCAL) |
+				 (arg ? CLOCAL : 0));
+			return 0;
+		case TIOCMGET:
+			return get_modem_info(info, (unsigned int *) arg);
+		case TIOCMBIS:
+		case TIOCMBIC:
+		case TIOCMSET:
+			return set_modem_info(info, cmd, (unsigned int *) arg);
+		case TIOCGSERIAL:
+			error = verify_area(VERIFY_WRITE, (void *) arg,
+						sizeof(struct serial_struct));
+			if (error)
+				return error;
+			return get_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSSERIAL:
+			error = verify_area(VERIFY_READ, (void *) arg,
+						sizeof(struct serial_struct));
+			if (error)
+				return error;
+			return set_serial_info(info,
+					       (struct serial_struct *) arg);
+		case TIOCSERCONFIG:
+			return do_autoconfig(info);
+
+		case TIOCSERGWILD:
+			return put_user(0L, (unsigned long *) arg);
+
+		case TIOCSERGETLSR: /* Get line status register */
+			    return get_lsr_info(info, (unsigned int *) arg);
+
+		case TIOCSERSWILD:
+			if (!suser())
+				return -EPERM;
+			return 0;
+
+		/*
+		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
+		 * - mask passed in arg for lines of interest
+ 		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+		 * Caller should use TIOCGICOUNT to see which one it was
+		 */
+		 case TIOCMIWAIT:
+			cli();
+			cprev = info->icount;	/* note the counters on entry */
+			sti();
+			while (1) {
+				interruptible_sleep_on(&info->delta_msr_wait);
+				/* see if a signal did it */
+				if (current->signal & ~current->blocked)
+					return -ERESTARTSYS;
+				cli();
+				cnow = info->icount;	/* atomic copy */
+				sti();
+				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+					return -EIO; /* no change => error */
+				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+					return 0;
+				}
+				cprev = cnow;
+			}
+			/* NOTREACHED */
+
+		/* 
+		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+		 * Return: write counters to the user passed counter struct
+		 * NB: both 1->0 and 0->1 transitions are counted except for
+		 *     RI where only 0->1 is counted.
+		 */
+		case TIOCGICOUNT:
+			cli();
+			cnow = info->icount;
+			sti();
+			p_cuser = (struct serial_icounter_struct *) arg;
+			if ((error = put_user(cnow.cts, &p_cuser->cts)))
+				return error;
+			if ((error = put_user(cnow.dsr, &p_cuser->dsr)))
+				return error;
+			if ((error = put_user(cnow.rng, &p_cuser->rng)))
+				return error;
+			if ((error = put_user(cnow.dcd, &p_cuser->dcd)))
+				return error;
+
+			return 0;
+
+		default:
+			return -ENOIOCTLCMD;
+		}
+	return 0;
+}
+
+static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
+{
+	struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+
+	if (   (tty->termios->c_cflag == old_termios->c_cflag)
+	    && (   RELEVANT_IFLAG(tty->termios->c_iflag) 
+		== RELEVANT_IFLAG(old_termios->c_iflag)))
+	  return;
+
+	change_speed(info);
+
+	if ((old_termios->c_cflag & CRTSCTS) &&
+	    !(tty->termios->c_cflag & CRTSCTS)) {
+		tty->hw_stopped = 0;
+		rs_start(tty);
+	}
+
+#if 0
+	/*
+	 * No need to wake up processes in open wait, since they
+	 * sample the CLOCAL flag once, and don't recheck it.
+	 * XXX  It's not clear whether the current behavior is correct
+	 * or not.  Hence, this may change.....
+	 */
+	if (!(old_termios->c_cflag & CLOCAL) &&
+	    (tty->termios->c_cflag & CLOCAL))
+		wake_up_interruptible(&info->open_wait);
+#endif
+}
+
+/*
+ * ------------------------------------------------------------
+ * rs_close()
+ * 
+ * This routine is called when the serial port gets closed.  First, we
+ * wait for the last remaining data to be sent.  Then, we unlink its
+ * async structure from the interrupt chain if necessary, and we free
+ * that IRQ if nothing is left in the chain.
+ * ------------------------------------------------------------
+ */
+static void rs_close(struct tty_struct *tty, struct file * filp)
+{
+	struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+	unsigned long flags;
+	unsigned long timeout;
+
+	if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
+		return;
+	
+	save_flags(flags); cli();
+	
+	if (tty_hung_up_p(filp)) {
+		DBG_CNT("before DEC-hung");
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	
+#ifdef SERIAL_DEBUG_OPEN
+	printk("rs_close ttys%d, count = %d\n", info->line, info->count);
+#endif
+	if ((tty->count == 1) && (info->count != 1)) {
+		/*
+		 * Uh, oh.  tty->count is 1, which means that the tty
+		 * structure will be freed.  Info->count should always
+		 * be one in these conditions.  If it's greater than
+		 * one, we've got real problems, since it means the
+		 * serial port won't be shutdown.
+		 */
+		printk("rs_close: bad serial port count; tty->count is 1, "
+		       "info->count is %d\n", info->count);
+		info->count = 1;
+	}
+	if (--info->count < 0) {
+		printk("rs_close: bad serial port count for ttys%d: %d\n",
+		       info->line, info->count);
+		info->count = 0;
+	}
+	if (info->count) {
+		DBG_CNT("before DEC-2");
+		MOD_DEC_USE_COUNT;
+		restore_flags(flags);
+		return;
+	}
+	info->flags |= ASYNC_CLOSING;
+	/*
+	 * Save the termios structure, since this port may have
+	 * separate termios for callout and dialin.
+	 */
+	if (info->flags & ASYNC_NORMAL_ACTIVE)
+		info->normal_termios = *tty->termios;
+	if (info->flags & ASYNC_CALLOUT_ACTIVE)
+		info->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 (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
+		tty_wait_until_sent(tty, info->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.
+	 */
+	/* info->IER &= ~UART_IER_RLSI; */
+	info->IER &= ~UART_IER_RDI;
+	info->read_status_mask &= ~UART_LSR_DR;
+	if (info->flags & ASYNC_INITIALIZED) {
+		serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
+		serial_out(info, UART_ESI_CMD2, info->IER);
+
+		/* disable receive timeout */
+		serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
+		serial_out(info, UART_ESI_CMD2, 0x00);
+
+		/*
+		 * 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;
+		serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
+		serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
+		while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
+			(serial_in(info, UART_ESI_STAT2) != 0xff)) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->timeout;
+			schedule();
+			if (jiffies > timeout)
+				break;
+			serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
+			serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
+		}
+	}
+	shutdown(info);
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+	tty->closing = 0;
+	info->event = 0;
+	info->tty = 0;
+
+	if (info->tty_buf) {
+		while (info->tty_buf->tqueue.sync) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + HZ / 10;
+			schedule();
+		}
+
+		kfree(info->tty_buf);
+		info->tty_buf = 0;
+	}
+			
+	if (info->blocked_open) {
+		if (info->close_delay) {
+			current->state = TASK_INTERRUPTIBLE;
+			current->timeout = jiffies + info->close_delay;
+			schedule();
+		}
+		wake_up_interruptible(&info->open_wait);
+	}
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+			 ASYNC_CLOSING);
+	wake_up_interruptible(&info->close_wait);
+	MOD_DEC_USE_COUNT;
+	restore_flags(flags);
+}
+
+/*
+ * esp_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void esp_hangup(struct tty_struct *tty)
+{
+	struct esp_struct * info = (struct esp_struct *)tty->driver_data;
+	
+	if (serial_paranoia_check(info, tty->device, "esp_hangup"))
+		return;
+	
+	rs_flush_buffer(tty);
+	shutdown(info);
+	info->event = 0;
+	info->count = 0;
+	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+	info->tty = 0;
+	wake_up_interruptible(&info->open_wait);
+}
+
+/*
+ * ------------------------------------------------------------
+ * esp_open() and friends
+ * ------------------------------------------------------------
+ */
+static int block_til_ready(struct tty_struct *tty, struct file * filp,
+			   struct esp_struct *info)
+{
+	struct wait_queue wait = { current, NULL };
+	int		retval;
+	int		do_clocal = 0;
+
+	/*
+	 * 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) ||
+	    (info->flags & ASYNC_CLOSING)) {
+		if (info->flags & ASYNC_CLOSING)
+			interruptible_sleep_on(&info->close_wait);
+#ifdef SERIAL_DO_RESTART
+		if (info->flags & ASYNC_HUP_NOTIFY)
+			return -EAGAIN;
+		else
+			return -ERESTARTSYS;
+#else
+		return -EAGAIN;
+#endif
+	}
+
+	/*
+	 * If this is a callout device, then just make sure the normal
+	 * device isn't being used.
+	 */
+	if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+		if (info->flags & ASYNC_NORMAL_ACTIVE)
+			return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_SESSION_LOCKOUT) &&
+		    (info->session != current->session))
+		    return -EBUSY;
+		if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (info->flags & ASYNC_PGRP_LOCKOUT) &&
+		    (info->pgrp != current->pgrp))
+		    return -EBUSY;
+		info->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 (info->flags & ASYNC_CALLOUT_ACTIVE)
+			return -EBUSY;
+		info->flags |= ASYNC_NORMAL_ACTIVE;
+		return 0;
+	}
+
+	if (info->flags & ASYNC_CALLOUT_ACTIVE) {
+		if (info->normal_termios.c_cflag & CLOCAL)
+			do_clocal = 1;
+	} else {
+		if (tty->termios->c_cflag & CLOCAL)
+			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(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready before block: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	cli();
+	if (!tty_hung_up_p(filp)) 
+		info->count--;
+	sti();
+	info->blocked_open++;
+	while (1) {
+		cli();
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE)) {
+			unsigned int scratch;
+
+			serial_out(info, UART_ESI_CMD1, ESI_READ_UART);
+			serial_out(info, UART_ESI_CMD2, UART_MCR);
+			scratch = serial_in(info, UART_ESI_STAT1);
+			serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+			serial_out(info, UART_ESI_CMD2, UART_MCR);
+			serial_out(info, UART_ESI_CMD2,
+				scratch | UART_MCR_DTR | UART_MCR_RTS);
+		}
+		sti();
+		current->state = TASK_INTERRUPTIBLE;
+		if (tty_hung_up_p(filp) ||
+		    !(info->flags & ASYNC_INITIALIZED)) {
+#ifdef SERIAL_DO_RESTART
+			if (info->flags & ASYNC_HUP_NOTIFY)
+				retval = -EAGAIN;
+			else
+				retval = -ERESTARTSYS;	
+#else
+			retval = -EAGAIN;
+#endif
+			break;
+		}
+
+		serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
+		if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD)
+			do_clocal = 1;
+
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    !(info->flags & ASYNC_CLOSING) &&
+		    (do_clocal))
+			break;
+		if (current->signal & ~current->blocked) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+#ifdef SERIAL_DEBUG_OPEN
+		printk("block_til_ready blocking: ttys%d, count = %d\n",
+		       info->line, info->count);
+#endif
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&info->open_wait, &wait);
+	if (!tty_hung_up_p(filp))
+		info->count++;
+	info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+	printk("block_til_ready after blocking: ttys%d, count = %d\n",
+	       info->line, info->count);
+#endif
+	if (retval)
+		return retval;
+	info->flags |= ASYNC_NORMAL_ACTIVE;
+	return 0;
+}	
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain.   It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int esp_open(struct tty_struct *tty, struct file * filp)
+{
+	struct esp_struct	*info;
+	int 			i, retval, line;
+	unsigned long		page;
+
+	line = MINOR(tty->device) - tty->driver.minor_start;
+	if ((line < 0) || (line >= NR_PORTS))
+		return -ENODEV;
+
+	/* check whether or not the port is on the closed chain */
+
+	info = IRQ_ports[0];
+
+	while (info && (info->line != line))
+		info = info->next_port;
+
+	/* if the port is not on the closed chain, look for it on the */
+	/* open chain */
+
+	if (!info) {
+		i = 1;
+
+		while ((i < 16) && !IRQ_ports[i])
+			i++;
+
+		if (i < 16) {
+			info = IRQ_ports[i];
+
+			do {
+				if (info->line == line)
+					break;
+				info = info->next_port;
+			} while (info != IRQ_ports[i]);
+		}
+	}
+
+	if (!info || (info->line != line) || 
+		serial_paranoia_check(info, tty->device, "esp_open"))
+		return -ENODEV;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("esp_open %s%d, count = %d\n", tty->driver.name, info->line,
+	       info->count);
+#endif
+	MOD_INC_USE_COUNT;
+	info->count++;
+	tty->driver_data = info;
+	info->tty = tty;
+
+	if (!tmp_buf) {
+		page = get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
+		if (tmp_buf)
+			free_page(page);
+		else
+			tmp_buf = (unsigned char *) page;
+	}
+	
+	/*
+	 * Start up serial port
+	 */
+	retval = startup(info);
+	if (retval)
+		return retval;
+
+	retval = block_til_ready(tty, filp, info);
+	if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+		printk("esp_open returning after block_til_ready with %d\n",
+		       retval);
+#endif
+		return retval;
+	}
+
+	if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+		if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+			*tty->termios = info->normal_termios;
+		else 
+			*tty->termios = info->callout_termios;
+		change_speed(info);
+	}
+
+	info->session = current->session;
+	info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("esp_open ttys%d successful...", info->line);
+#endif
+	return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * esp_init() and friends
+ *
+ * esp_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void show_serial_version(void)
+{
+ 	printk(KERN_INFO "%s version %s (DMA %u, trigger level %u)\n",
+		serial_name, serial_version, dma, trigger);
+}
+
+/*
+ * This routine is called by esp_init() to initialize a specific serial
+ * port.  It determines what type of UART chip this serial port is
+ * using: 8250, 16450, 16550, 16550A.  The important question is
+ * whether or not this UART is a 16550A or not, since this will
+ * determine whether or not we can use its FIFO features or not.
+ */
+static void autoconfig(struct esp_struct * info)
+{
+	unsigned char status1, status2, scratch;
+	unsigned port = info->port;
+	unsigned long flags;
+
+	info->type = PORT_UNKNOWN;
+	
+	if (!port)
+		return;
+
+	save_flags(flags); cli();
+	
+	/*
+	 * Check for ESP card
+	 */
+	scratch = serial_in(info, UART_ESI_BASE);
+	if (scratch == 0xf3) {
+		serial_out(info, UART_ESI_CMD1, 0x00);
+		serial_out(info, UART_ESI_CMD1, 0x01);
+		status1 = serial_in(info, UART_ESI_STAT2);
+		status2 = status1 & 0x70;
+		if (status2 != 0x20) {
+			printk(" Old ESP found at %x\n",info->port);
+		} else {
+			serial_out(info, UART_ESI_CMD1, 0x02);
+			status1 = serial_in(info, UART_ESI_STAT1) & 0x03;
+			if (!(info->irq)) {
+				if ((status1 == 0x00) || (status1 == 0x02))
+					info->irq = 4;
+				else
+					info->irq = 3;
+			}
+			info->type = PORT_16550A;
+			request_region(port,8,"esp(auto)");
+
+			/* put card in enhanced mode */
+			/* this prevents access through */
+			/* the "old" IO ports */
+			esp_basic_init(info);
+
+			/* clear out MCR */
+			serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
+			serial_out(info, UART_ESI_CMD2, UART_MCR);
+			serial_out(info, UART_ESI_CMD2, 0x00);
+		}
+	}
+
+	restore_flags(flags);
+}
+
+/*
+ * The serial driver boot-time initialization code!
+ */
+int esp_init(void)
+{
+	int i, offset;
+	struct esp_struct * info;
+	int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380};
+	
+	init_bh(ESP_BH, do_serial_bh);
+
+	for (i = 0; i < 16; i++) {
+		IRQ_ports[i] = 0;
+	}
+
+	if ((dma != 1) && (dma != 3))
+		dma = CONFIG_ESP_DMA_CHANNEL;
+
+	if ((trigger < 1) || (trigger > 1015))
+		trigger = 768;
+	
+	show_serial_version();
+
+	/* Initialize the tty_driver structure */
+	
+	memset(&esp_driver, 0, sizeof(struct tty_driver));
+	esp_driver.magic = TTY_DRIVER_MAGIC;
+	esp_driver.name = "ttyP";
+	esp_driver.major = ESP_IN_MAJOR;
+	esp_driver.minor_start = 0;
+	esp_driver.num = NR_PORTS;
+	esp_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	esp_driver.subtype = SERIAL_TYPE_NORMAL;
+	esp_driver.init_termios = tty_std_termios;
+	esp_driver.init_termios.c_cflag =
+		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	esp_driver.flags = TTY_DRIVER_REAL_RAW;
+	esp_driver.refcount = &serial_refcount;
+	esp_driver.table = serial_table;
+	esp_driver.termios = serial_termios;
+	esp_driver.termios_locked = serial_termios_locked;
+
+	esp_driver.open = esp_open;
+	esp_driver.close = rs_close;
+	esp_driver.write = rs_write;
+	esp_driver.put_char = rs_put_char;
+	esp_driver.flush_chars = rs_flush_chars;
+	esp_driver.write_room = rs_write_room;
+	esp_driver.chars_in_buffer = rs_chars_in_buffer;
+	esp_driver.flush_buffer = rs_flush_buffer;
+	esp_driver.ioctl = rs_ioctl;
+	esp_driver.throttle = rs_throttle;
+	esp_driver.unthrottle = rs_unthrottle;
+	esp_driver.set_termios = rs_set_termios;
+	esp_driver.stop = rs_stop;
+	esp_driver.start = rs_start;
+	esp_driver.hangup = esp_hangup;
+
+	/*
+	 * The callout device is just like normal device except for
+	 * major number and the subtype code.
+	 */
+	esp_callout_driver = esp_driver;
+	esp_callout_driver.name = "cup";
+	esp_callout_driver.major = ESP_OUT_MAJOR;
+	esp_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+	if (tty_register_driver(&esp_driver))
+		panic("Couldn't register serial driver\n");
+	if (tty_register_driver(&esp_callout_driver))
+		panic("Couldn't register callout driver\n");
+	
+	info = (struct esp_struct *)kmalloc(sizeof(struct esp_struct),
+		GFP_KERNEL);
+	if (!info)
+		panic("Could not allocate memory for device information\n");
+	memset((void *)info, 0, sizeof(struct esp_struct));
+
+	i = 0;
+	offset = 0;
+
+	do {
+		info->port = esp[i] + offset;
+		info->baud_base = BASE_BAUD;
+		info->custom_divisor = (divisor[i] >> (offset / 2)) & 0xf;
+		info->flags = STD_COM_FLAGS;
+		if (info->custom_divisor)
+			info->flags |= ASYNC_SPD_CUST;
+		info->irq = irq[i];
+
+		info->magic = ESP_MAGIC;
+		info->line = (i * 8) + (offset / 8);
+		info->tty = 0;
+		info->type = PORT_UNKNOWN;
+		info->close_delay = 5*HZ/10;
+		info->closing_wait = 30*HZ;
+		info->tqueue.routine = do_softint;
+		info->tqueue.data = info;
+		info->tqueue_hangup.routine = do_serial_hangup;
+		info->tqueue_hangup.data = info;
+		info->callout_termios = esp_callout_driver.init_termios;
+		info->normal_termios = esp_driver.init_termios;
+
+		if (info->irq == 2)
+			info->irq = 9;
+
+		autoconfig(info);
+		if (info->type == PORT_UNKNOWN) {
+			i++;
+			offset = 0;
+			continue;
+		}
+		if (IRQ_ports[0])
+			IRQ_ports[0]->prev_port = info;
+		info->next_port = IRQ_ports[0];
+		info->prev_port = 0;
+		IRQ_ports[0] = info;
+		printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ",
+			info->line, info->port, info->irq);
+		if (info->line % 8)
+			printk("secondary port\n");
+		else {
+			printk("primary port\n");
+			irq[i] = info->irq;
+		}
+
+		info = (struct esp_struct *)kmalloc(sizeof(struct esp_struct),
+			GFP_KERNEL);
+		if (!info)
+			panic("Could not allocate memory for device information\n");
+		memset((void *)info, 0, sizeof(struct esp_struct));
+
+		if (offset == 56) {
+			i++;
+			offset = 0;
+		} else {
+			offset += 8;
+		}
+	} while (i < NR_PRIMARY);
+
+	/* free the last port memory allocation */
+	kfree(info);
+
+	return 0;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	return esp_init();
+}
+
+void cleanup_module(void) 
+{
+	unsigned long flags;
+	int e1, e2;
+	struct esp_struct *current_async, *temp_async;
+
+	/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
+	save_flags(flags);
+	cli();
+	if ((e1 = tty_unregister_driver(&esp_driver)))
+		printk("SERIAL: failed to unregister serial driver (%d)\n",
+		       e1);
+	if ((e2 = tty_unregister_driver(&esp_callout_driver)))
+		printk("SERIAL: failed to unregister callout driver (%d)\n", 
+		       e2);
+	restore_flags(flags);
+
+	current_async = IRQ_ports[0];
+	while (current_async != 0) {
+		if (current_async->type != PORT_UNKNOWN)
+			release_region(current_async->port, 8);
+		current_async = current_async->next_port;
+	}
+
+	if (dma_buffer)
+		free_pages((unsigned int)dma_buffer,
+			__get_order(DMA_BUFFER_SZ));
+
+	if (tmp_buf)
+		free_page((unsigned long)tmp_buf);
+
+	/* free the port information */
+	current_async = IRQ_ports[0];
+	while (current_async != 0) {
+		temp_async = current_async->next_port;
+		kfree(current_async);
+		current_async = temp_async;
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.14/linux/drivers/char/esp.h linux/drivers/char/esp.h
--- v2.1.14/linux/drivers/char/esp.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/char/esp.h	Wed Dec 11 17:16:07 1996
@@ -0,0 +1,101 @@
+#ifndef ESP_H
+#define ESP_H
+
+#define ESP_IN_MAJOR	57	/* major dev # for dial in */
+#define ESP_OUT_MAJOR	58	/* major dev # for dial out */
+#define ESPC_SCALE 	3
+#define UART_ESI_BASE	0x00
+#define UART_ESI_SID	0x01
+#define UART_ESI_RX	0x02
+#define UART_ESI_TX	0x02
+#define UART_ESI_CMD1	0x04
+#define UART_ESI_CMD2	0x05
+#define UART_ESI_STAT1	0x04
+#define UART_ESI_STAT2	0x05
+#define UART_ESI_RWS	0x07
+
+#define UART_IER_DMA_TMOUT	0x80
+#define UART_IER_DMA_TC		0x08
+
+#define ESI_SET_IRQ		0x04
+#define ESI_SET_DMA_TMOUT	0x05
+#define ESI_SET_SRV_MASK	0x06
+#define ESI_SET_ERR_MASK	0x07
+#define ESI_SET_FLOW_CNTL	0x08
+#define ESI_SET_FLOW_CHARS	0x09
+#define ESI_SET_FLOW_LVL	0x0a
+#define ESI_SET_TRIGGER		0x0b
+#define ESI_SET_RX_TIMEOUT	0x0c
+#define ESI_SET_FLOW_TMOUT	0x0d
+#define ESI_WRITE_UART		0x0e
+#define ESI_READ_UART		0x0f
+#define ESI_SET_MODE		0x10
+#define ESI_GET_ERR_STAT	0x12
+#define ESI_GET_UART_STAT	0x13
+#define ESI_GET_RX_AVAIL	0x14
+#define ESI_GET_TX_AVAIL	0x15
+#define ESI_START_DMA_RX	0x16
+#define ESI_START_DMA_TX	0x17
+#define ESI_ISSUE_BREAK		0x1a
+#define ESI_FLUSH_RX		0x1b
+#define ESI_FLUSH_TX		0x1c
+#define ESI_SET_BAUD		0x1d
+#define ESI_SET_ENH_IRQ		0x1f
+#define ESI_SET_REINTR		0x20
+#define ESI_SET_PRESCALAR	0x23
+#define ESI_NO_COMMAND		0xff
+
+#define STAT_RX_TIMEOUT		0x01
+#define STAT_NEED_DMA		0x02
+
+#define ESP_EVENT_WRITE_WAKEUP	0
+#define ESP_MAGIC		0x53ee
+#define ESP_XMIT_SIZE		4096
+
+struct esp_struct {
+	int			magic;
+	int			baud_base;
+	int			port;
+	int			irq;
+	int			flags; 		/* defined in tty.h */
+	int			type; 		/* UART type */
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			stat_flags;
+	int			custom_divisor;
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	int			IER; 	/* Interrupt Enable Register */
+	int			MCR; 	/* Modem control register */
+	int			MCR_noint; /* MCR with interrupts off */
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tty_flip_buffer	*tty_buf;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	struct wait_queue	*delta_msr_wait;
+	struct wait_queue	*break_wait;
+	struct async_icount	icount;	/* kernel counters for the 4 input interrupts */
+	struct esp_struct	*next_port; /* For the linked list */
+	struct esp_struct	*prev_port;
+};
+
+
+#endif /* ESP_H */
+
diff -u --recursive --new-file v2.1.14/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.1.14/linux/drivers/char/n_tty.c	Sun Nov 10 20:12:11 1996
+++ linux/drivers/char/n_tty.c	Sun Dec  8 21:21:15 1996
@@ -355,7 +355,8 @@
 		return;
 	}
 	
-	if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) {
+	if (tty->stopped && !tty->flow_stopped &&
+	    I_IXON(tty) && I_IXANY(tty)) {
 		start_tty(tty);
 		return;
 	}
diff -u --recursive --new-file v2.1.14/linux/drivers/char/pcwd.c linux/drivers/char/pcwd.c
--- v2.1.14/linux/drivers/char/pcwd.c	Fri Aug 23 09:11:25 1996
+++ linux/drivers/char/pcwd.c	Thu Dec 12 16:51:09 1996
@@ -15,6 +15,10 @@
  *		check_region command due to Alan's suggestion.
  * 960821	Made changes to compile in newer 2.0.x kernels.  Added
  *		"cold reboot sense" entry.
+ * 960825	Made a few changes to code, deleted some defines and made
+ *		typedefs to replace them.  Made heartbeat reset only available
+ *		via ioctl, and removed the write routine.
+ * 960828	Added new items for PC Watchdog Rev.C card.
  */
 
 #include <linux/module.h>
@@ -24,6 +28,7 @@
 #include <linux/sched.h>
 #include <linux/tty.h>
 #include <linux/timer.h>
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/wait.h>
 #include <linux/string.h>
@@ -33,37 +38,67 @@
 #include <linux/miscdevice.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
-#include <linux/pcwd.h>
-
+#include <linux/watchdog.h>
 #include <asm/io.h>
+#include <asm/uaccess.h>
 
-#define WD_VER                  "0.50 (08/21/96)"
-#define	WD_MINOR		130	/* Minor device number */
+typedef struct pcwd_ioports {
+	int first_port;
+	int range;
+} IOPS;
 
-#define	WD_TIMEOUT		3	/* 1 1/2 seconds for a timeout */
+/*
+** These are the auto-probe addresses available for the Rev.A version of the
+** PC Watchdog card.
+*/
+
+static IOPS pcwd_ioports[] = {
+	{ 0x270, 3 },
+	{ 0x350, 3 },
+	{ 0x370, 3 },
+	{ 0x000, 0 }
+};
 
-#define WD_TIMERRESET_PORT1     0x270	/* Reset port - first choice */
-#define WD_TIMERRESET_PORT2     0x370	/* Reset port - second choice */
-#define WD_CTLSTAT_PORT1        0x271	/* Control port - first choice */
-#define WD_CTLSTAT_PORT2        0x371	/* Control port - second choice */
-#define	WD_PORT_EXTENT		2	/* Takes up two addresses */
+#ifdef DEBUG
+#define dprintk(x)	printk(x)
+#else
+#define dprintk(x)
+#endif
 
+#ifdef CONFIG_PCWD_REV_A
+#define CARD_REV	"A"
+#define PORT_OFFSET	0
+#define PORT_RANGE	2
 #define WD_WDRST                0x01	/* Previously reset state */
 #define WD_T110                 0x02	/* Temperature overheat sense */
 #define WD_HRTBT                0x04	/* Heartbeat sense */
 #define WD_RLY2                 0x08	/* External relay triggered */
 #define WD_SRLY2                0x80	/* Software external relay triggered */
+#endif
+#ifdef CONFIG_PCWD_REV_C
+#define CARD_REV	"C"
+#define PORT_OFFSET	1
+#define PORT_RANGE	4
+#define WD_WDRST                0x01	/* Previously reset state */
+#define WD_T110                 0x04	/* Temperature overheat sense */
+#endif
 
-static int current_ctlport, current_readport;
-static int is_open, is_eof;
+#define WD_VER                  "0.52 (08/28/96)"
+#define	WD_MINOR		130	/* Minor device number */
+
+#define	WD_TIMEOUT		3	/* 1 1/2 seconds for a timeout */
+
+
+static int current_readport;
+static int is_open, initial_status, supports_temp, mode_debug;
 
 int pcwd_checkcard(void)
 {
 	int card_dat, prev_card_dat, found = 0, count = 0, done = 0;
 
 	/* As suggested by Alan Cox */
-	if (check_region(current_ctlport, WD_PORT_EXTENT)) {
-		printk("pcwd: Port 0x%x unavailable.\n", current_ctlport);
+	if (check_region(current_readport, PORT_RANGE)) {
+		printk("pcwd: Port 0x%x unavailable.\n", current_readport);
 		return 0;
 	}
 
@@ -71,11 +106,13 @@
 	prev_card_dat = 0x00;
 
 	prev_card_dat = inb(current_readport);
+	if (prev_card_dat == 0xFF) {
+		dprintk(("pcwd: No card detected at 0x%03x\n", current_readport));
+		return 0;
+	}
 
 	while(count < WD_TIMEOUT) {
-#ifdef	DEBUG
-		printk("pcwd: Run #%d on port 0x%03x\n", count, current_readport);
-#endif
+		dprintk(("pcwd: Run #%d on port 0x%03x\n", count, current_readport));
 
 	/* Read the raw card data from the port, and strip off the
 	   first 4 bits */
@@ -96,9 +133,7 @@
 			count++;
 			done = 1;
 
-#ifdef	DEBUG
-			printk("pcwd: I show nothing on this port.\n");
-#endif
+			dprintk(("pcwd: I show nothing on this port.\n"));
 		}
 
 	/* If there's a heart beat in both instances, then this means we
@@ -109,9 +144,8 @@
 			(!done)) {
 			found = 1;
 			done = 1;
-#ifdef	DEBUG
-			printk("pcwd: I show alternate heart beats.  Card detected.\n");
-#endif
+			
+			dprintk(("pcwd: I show alternate heart beats.  Card detected.\n"));
 			break;
 		}
 
@@ -125,9 +159,7 @@
 
 		if ((card_dat == prev_card_dat) && (!done)) {
 			count++;
-#ifdef	DEBUG
-			printk("pcwd: The card data is exactly the same (possibility).\n");
-#endif
+			dprintk(("pcwd: The card data is exactly the same (possibility).\n"));
 			done = 1;
 		}
 
@@ -137,9 +169,7 @@
 		if ((card_dat != prev_card_dat) && (!done)) {
 			done = 1;
 			found = 1;
-#ifdef	DEBUG
-			printk("pcwd: I show alternate heart beats.  Card detected.\n");
-#endif
+			dprintk(("pcwd: I show alternate heart beats.  Card detected.\n"));
 			break;
 		}
 
@@ -156,43 +186,35 @@
 {
 	int card_status = 0x0000;
 
-	card_status = inb(current_readport);
+	initial_status = card_status = inb(current_readport + PORT_OFFSET);
 
 	if (card_status & WD_WDRST)
 		printk("pcwd: Previous reboot was caused by the card.\n");
 
-	if (card_status & WD_T110)
-		printk("pcwd: CPU overheat sense.\n");
+	if (supports_temp)
+		if(card_status & WD_T110)
+			printk("pcwd: CPU overheat sense.\n");
 
 	if ((!(card_status & WD_WDRST)) &&
 	    (!(card_status & WD_T110)))
 		printk("pcwd: Cold boot sense.\n");
 }
 
-static int pcwd_return_data(void)
-{
-	return(inb(current_readport));
-}
-
-static int pcwd_write(struct inode *inode, struct file *file, const char *data,
-	int len)
+static void pcwd_send_heartbeat(void)
 {
 	int wdrst_stat;
 
 	if (!is_open)
-		return -EIO;
+		return;
 
-#ifdef	DEBUG
-	printk("pcwd: write request\n");
-#endif
+	dprintk(("pcwd: heartbeat\n"));
 
 	wdrst_stat = inb_p(current_readport);
 	wdrst_stat &= 0x0F;
 
 	wdrst_stat |= WD_WDRST;
 
-	outb_p(wdrst_stat, current_ctlport);
-
+	outb_p(wdrst_stat, current_readport + PORT_OFFSET);
 	return(1);
 }
 
@@ -200,58 +222,118 @@
 	unsigned int cmd, unsigned long arg)
 {
 	int i, cdat, rv;
-
+	static struct watchdog_ident ident=
+	{
+		WDIOF_OVERHEAT|WDIOF_CARDRESET,
+#ifdef CONFIG_PCWD_REV_A	
+		1,
+#else
+		3,
+#endif				
+		"PCWD revision "CARD_REV"."
+	};
+		
 	switch(cmd) {
 	default:
 		return -ENOIOCTLCMD;
 
-	case PCWD_GETSTAT:
+	case WDIOC_GETSUPPORT:
+		i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct watchdog_info));
+		if (i)
+			return i;
+		else
+			return copy_to_user(arg, &ident, sizeof(ident));
+
+	case WDIOC_GETSTATUS:
 		i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int));
 		if (i)
 			return i;
 		else {
-			cdat = pcwd_return_data();
+			cdat = inb(current_readport);
 			rv = 0;
 
 			if (cdat & WD_WDRST)
-				rv |= 0x01;
+				rv |= WDIOF_CARDRESET;
 
 			if (cdat & WD_T110)
-				rv |= 0x02;
+				rv |= WDIOF_OVERHEAT;
 
-			put_user(rv, (int *) arg);
-			return 0;
+			return put_user(rv, (int *) arg);
 		}
 		break;
 
-	case PCWD_PING:
-		pcwd_write(NULL, NULL, NULL, 1);	/* Is this legal? */
+	case WDIOC_GETBOOTSTATUS:
+		i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int));
+		if (i)
+			return i;
+		else {
+			int rv;
+			rv = 0;
+
+			if (initial_status & WD_WDRST)
+				rv |= WDIOF_CARDRESET;
+
+			if (initial_status & WD_T110)
+				rv |= WDIOF_OVERHEAT;
+			return put_user(rv, (int *) arg);
+		}
 		break;
+
+	case WDIOC_GETTEMP:
+		i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int));
+		if (i)
+			return i;
+		else {
+			int rv;
+
+			rv = 0;
+			if ((supports_temp) && (mode_debug == 0)) {
+				rv = inb(current_readport);
+				return put_user(rv, (int*) arg);
+			} else
+				return put_user(rv, (int*) arg);
+		}
+
+	case WDIOC_KEEPALIVE:
+		pcwd_send_heartbeat();
+		return 0;
 	}
 
 	return 0;
 }
 
+static long pcwd_write(struct file *file, struct inode *inode, const char *buf, unsigned long len)
+{
+	if(len)
+	{
+		pcwd_send_heartbeat();
+		return 1;
+	}
+}
+
 static int pcwd_open(struct inode *ino, struct file *filep)
 {
-#ifdef	DEBUG
-	printk("pcwd: open request\n");
-#endif
+	dprintk(("pcwd: open request\n"));
 
 	MOD_INC_USE_COUNT;
-	is_eof = 0;
 	return(0);
 }
 
 static void pcwd_close(struct inode *ino, struct file *filep)
 {
-#ifdef	DEBUG
-	printk("pcwd: close request\n");
-#endif
+	dprintk(("pcwd: close request\n"));
 
 	MOD_DEC_USE_COUNT;
 }
 
+static void get_support(void)
+{
+#ifdef CONFIG_PCWD_REV_C
+	if (inb(current_readport) != 0xF0)
+#endif	
+		supports_temp = 1;
+}
+
 static struct file_operations pcwd_fops = {
 	NULL,		/* Seek */
 	NULL,		/* Read */
@@ -276,48 +358,57 @@
 int pcwatchdog_init(void)
 #endif
 {
-#ifdef	DEBUG
-	printk("pcwd: Success.\n");
-#endif
-	printk("pcwd: v%s Ken Hollis (khollis@bitgate.com)\n", WD_VER);
+	int i, found = 0;
 
-#ifdef	DEBUG
-	printk("pcwd: About to perform card autosense loop.\n");
-#endif
+	dprintk(("pcwd: Success.\n"));
+	printk(KERN_INFO "pcwd: v%s Ken Hollis (khollis@bitgate.com)\n", WD_VER);
+
+	dprintk(("pcwd: About to perform card autosense loop.\n"));
 
-	is_eof = 0;
+	/* Initial variables */
 	is_open = 0;
+	supports_temp = 0;
+	mode_debug = 0;
+	initial_status = 0x0000;
 
-	current_ctlport = WD_TIMERRESET_PORT1;
-	current_readport = WD_CTLSTAT_PORT1;
+	dprintk(("pcwd: Revision " CARD_REV " support defined.\n"));
+
+	for (i = 0; pcwd_ioports[i].first_port != 0; i++) {
+		current_readport = pcwd_ioports[i].first_port;
 
 	if (!pcwd_checkcard()) {
-#ifdef	DEBUG
-		printk("pcwd: Trying port 0x370.\n");
-#endif
+		dprintk(("pcwd: Trying port 0x%03x.\n", pcwd_ioports[i].first_port));
+		if (pcwd_checkcard()) {
+			found = 1;
+			break;
+		}
+	}
 
-		current_ctlport = WD_TIMERRESET_PORT2;
-		current_readport = WD_CTLSTAT_PORT2;
+	if (!found) {
+		printk("pcwd: No card detected.\n");
+		return(-EIO);
+	}
 
-		if (!pcwd_checkcard()) {
-			printk("pcwd: No card detected, or wrong port assigned.\n");
-			return(-EIO);
-		} else
-			printk("pcwd: Watchdog Rev.A detected at port 0x370\n");
-	} else
-		printk("pcwd: Watchdog Rev.A detected at port 0x270\n");
+	is_open = 1;
 
-	pcwd_showprevstate();
+	get_support();
 
-#ifdef	DEBUG
-	printk("pcwd: Requesting region entry\n");
+#ifdef	CONFIG_PCWD_REV_A
+	printk("pcwd: PC Watchdog (REV.A) detected at port 0x%03x\n", current_readport);
+#endif
+#ifdef	CONFIG_PCWD_REV_C
+	printk("pcwd: PC Watchdog (REV.C) detected at port 0x%03x -%stemp. support\n",
+		current_readport, (supports_temp) ? " Has " : " No ");
 #endif
 
-	request_region(current_ctlport, WD_PORT_EXTENT, "PCWD Rev.A (Berkshire)");
-
-#ifdef	DEBUG
-	printk("pcwd: character device creation.\n");
+#ifdef	CONFIG_PCWD_SHOW_PREVSTAT
+  	pcwd_showprevstate();
 #endif
+	dprintk(("pcwd: Requesting region entry\n"));
+
+	request_region(current_readport, PORT_RANGE, "PCWD Rev." CARD_REV "(Berkshire)");
+
+	dprintk(("pcwd: character device creation.\n"));
 
 	misc_register(&pcwd_miscdev);
 
@@ -328,10 +419,9 @@
 void cleanup_module(void)
 {
 	misc_deregister(&pcwd_miscdev);
-	release_region(current_ctlport, 2);
-#ifdef	DEBUG
-	printk("pcwd: Cleanup successful.\n");
-#endif
+	release_region(current_readport, PORT_RANGE);
+
+	dprintk(("pcwd: Cleanup successful.\n"));
 }
 #endif
 
@@ -339,9 +429,7 @@
 ** TODO:
 **
 **	Both Revisions:
-**	o) Support for revision B of the Watchdog Card
 **	o) Implement the rest of the IOCTLs as discussed with Alan Cox
-**	o) Implement only card heartbeat reset via IOCTL, not via write
 **	o) Faster card detection routines
 **	o) /proc device creation
 **
diff -u --recursive --new-file v2.1.14/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v2.1.14/linux/drivers/char/psaux.c	Tue Oct 29 19:58:06 1996
+++ linux/drivers/char/psaux.c	Thu Dec 12 16:51:09 1996
@@ -270,7 +270,11 @@
 	poll_aux_status();
 	/* reenable kbd bh */
 	enable_bh(KEYBOARD_BH);
+#ifdef CONFIG_MCA
+	free_irq(AUX_IRQ, inode);
+#else
 	free_irq(AUX_IRQ, NULL);
+#endif
 	MOD_DEC_USE_COUNT;
 }
 
@@ -319,7 +323,11 @@
 		return -EBUSY;
 	}
 	queue->head = queue->tail = 0;		/* Flush input queue */
+#ifdef CONFIG_MCA
+	if (request_irq(AUX_IRQ, aux_interrupt, MCA_bus ? SA_SHIRQ : 0, "PS/2 Mouse", inode)) {
+#else
 	if (request_irq(AUX_IRQ, aux_interrupt, 0, "PS/2 Mouse", NULL)) {
+#endif
 		aux_count--;
 		return -EBUSY;
 	}
diff -u --recursive --new-file v2.1.14/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.14/linux/drivers/char/serial.c	Thu Dec 12 17:02:42 1996
+++ linux/drivers/char/serial.c	Thu Dec 12 16:51:09 1996
@@ -50,7 +50,7 @@
 #include <asm/bitops.h>
 
 static char *serial_name = "Serial driver";
-static char *serial_version = "4.21";
+static char *serial_version = "4.22";
 
 DECLARE_TASK_QUEUE(tq_serial);
 
@@ -83,6 +83,7 @@
 #undef SERIAL_DEBUG_INTR
 #undef SERIAL_DEBUG_OPEN
 #undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
 
 #define RS_STROBE_TIME (10*HZ)
 #define RS_ISR_PASS_LIMIT 256
@@ -222,6 +223,14 @@
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS42 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS43 */
 #endif
+#ifdef CONFIG_MCA
+	{ 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },
+	{ 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },
+	{ 0, BASE_BAUD, 0x4220, 3, STD_COM_FLAGS },
+	{ 0, BASE_BAUD, 0x4228, 3, STD_COM_FLAGS },
+	{ 0, BASE_BAUD, 0x5220, 3, STD_COM_FLAGS },
+	{ 0, BASE_BAUD, 0x5228, 3, STD_COM_FLAGS },
+#endif
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))
@@ -1040,6 +1049,16 @@
 	}
 
 	/*
+	 * Insert serial port into IRQ chain.
+	 */
+	info->prev_port = 0;
+	info->next_port = IRQ_ports[state->irq];
+	if (info->next_port)
+		info->next_port->prev_port = info;
+	IRQ_ports[state->irq] = info;
+	figure_IRQ_timeout(state->irq);
+
+	/*
 	 * Clear the interrupt registers.
 	 */
      /* (void) serial_inp(info, UART_LSR); */   /* (see above) */
@@ -1051,19 +1070,23 @@
 	 * Now, initialize the UART 
 	 */
 	serial_outp(info, UART_LCR, UART_LCR_WLEN8);	/* reset DLAB */
-	if (info->flags & ASYNC_FOURPORT) {
+
+	info->MCR = 0;
+	if (info->tty->termios->c_cflag & CBAUD)
 		info->MCR = UART_MCR_DTR | UART_MCR_RTS;
-		info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
+	if (info->flags & ASYNC_FOURPORT) {
+		if (state->irq == 0)
+			info->MCR |= UART_MCR_OUT1;
 	} else {
-		info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
-		info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
+		if (state->irq != 0)
+			info->MCR |= UART_MCR_OUT2;
 	}
 #if defined(__alpha__) && !defined(CONFIG_PCI)
+	/*
+	 * DEC did something gratutiously wrong....
+	 */
 	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
-	info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
 #endif
-	if (state->irq == 0)
-		info->MCR = info->MCR_noint;
 	serial_outp(info, UART_MCR, info->MCR);
 	
 	/*
@@ -1092,16 +1115,6 @@
 	info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
 
 	/*
-	 * Insert serial port into IRQ chain.
-	 */
-	info->prev_port = 0;
-	info->next_port = IRQ_ports[state->irq];
-	if (info->next_port)
-		info->next_port->prev_port = info;
-	IRQ_ports[state->irq] = info;
-	figure_IRQ_timeout(state->irq);
-
-	/*
 	 * Set up serial timers...
 	 */
 	timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
@@ -1183,13 +1196,19 @@
 	if (info->flags & ASYNC_FOURPORT) {
 		/* reset interrupts on the AST Fourport board */
 		(void) inb((info->port & 0xFE0) | 0x01F);
-	}
+		info->MCR |= UART_MCR_OUT1;
+	} else
+		info->MCR &= ~UART_MCR_OUT2;
+#if defined(__alpha__) && !defined(CONFIG_PCI)
+	/*
+	 * DEC did something gratutiously wrong....
+	 */
+	info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
+#endif
 	
-	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+	if (!info->tty || (info->tty->termios->c_cflag & HUPCL))
 		info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
-		info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);
-	}
-	serial_outp(info, UART_MCR, info->MCR_noint);
+	serial_outp(info, UART_MCR, info->MCR);
 
 	/* disable FIFO's */	
 	serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
@@ -1223,13 +1242,36 @@
 	unsigned short port;
 	int	quot = 0, baud_base;
 	unsigned cflag, cval, fcr = 0;
-	int	i;
+	int	i, bits;
+	unsigned long	flags;
 
 	if (!info->tty || !info->tty->termios)
 		return;
 	cflag = info->tty->termios->c_cflag;
 	if (!(port = info->port))
 		return;
+
+	/* byte size and parity */
+	switch (cflag & CSIZE) {
+	      case CS5: cval = 0x00; bits = 7; break;
+	      case CS6: cval = 0x01; bits = 8; break;
+	      case CS7: cval = 0x02; bits = 9; break;
+	      case CS8: cval = 0x03; bits = 10; break;
+	      /* Never happens, but GCC is too dumb to figure it out */
+	      default:  cval = 0x00; bits = 7; break;
+	      }
+	if (cflag & CSTOPB) {
+		cval |= 0x04;
+		bits++;
+	}
+	if (cflag & PARENB) {
+		cval |= UART_LCR_PARITY;
+		bits++;
+	}
+	if (!(cflag & PARODD))
+		cval |= UART_LCR_EPAR;
+
+	/* Determine divisor based on baud rate */
 	i = cflag & CBAUD;
 	if (i & CBAUDEX) {
 		i &= ~CBAUDEX;
@@ -1251,42 +1293,20 @@
 			quot = info->state->custom_divisor;
 	}
 	baud_base = info->state->baud_base;
-	if (quot) {
-		info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
-				 baud_base) + 2;
-	} else if (baud_table[i] == 134) {
-		quot = (2*baud_base / 269);
-		info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
-	} else if (baud_table[i]) {
-		quot = baud_base / baud_table[i];
-		info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
-	} else {
-		quot = 0;
-		info->timeout = 0;
-	}
 	if (!quot) {
-		info->MCR &= ~UART_MCR_DTR;
-		info->MCR_noint &= ~UART_MCR_DTR;
-		cli();
-		serial_out(info, UART_MCR, info->MCR);
-		sti();
-		return;
-	}
-	/* byte size and parity */
-	switch (cflag & CSIZE) {
-	      case CS5: cval = 0x00; break;
-	      case CS6: cval = 0x01; break;
-	      case CS7: cval = 0x02; break;
-	      case CS8: cval = 0x03; break;
-	      default:  cval = 0x00; break;	/* too keep GCC shut... */
-	}
-	if (cflag & CSTOPB) {
-		cval |= 0x04;
+		if (baud_table[i] == 134)
+			/* Special case since 134 is really 134.5 */
+			quot = (2*baud_base / 269);
+		else if (baud_table[i])
+			quot = baud_base / baud_table[i];
+		/* If the quotient is ever zero, default to 9600 bps */
+		if (!quot)
+			quot = baud_base / 9600;
 	}
-	if (cflag & PARENB)
-		cval |= UART_LCR_PARITY;
-	if (!(cflag & PARODD))
-		cval |= UART_LCR_EPAR;
+	info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
+	info->timeout += HZ/50;		/* Add .02 seconds of slop */
+
+	/* Set up FIFO's */
 	if (uart_config[info->state->type].flags & UART_USE_FIFO) {
 		if ((info->state->baud_base / quot) < 2400)
 			fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
@@ -1344,7 +1364,7 @@
 	 */
 	if ((cflag & CREAD) == 0)
 		info->ignore_status_mask |= UART_LSR_DR;
-	cli();
+	save_flags(flags); cli();
 	if (uart_config[info->state->type].flags & UART_STARTECH) {
 		serial_outp(info, UART_LCR, 0xBF);
 		serial_outp(info, UART_EFR,
@@ -1357,7 +1377,7 @@
 		serial_outp(info, UART_FCR, fcr); 	/* set fcr */
 	serial_outp(info, UART_LCR, cval);		/* reset DLAB */
 	serial_outp(info, UART_FCR, fcr); 	/* set fcr */
-	sti();
+	restore_flags(flags);
 }
 
 static void rs_put_char(struct tty_struct *tty, unsigned char ch)
@@ -1529,10 +1549,9 @@
 	if (I_IXOFF(tty))
 		rs_send_xchar(tty, STOP_CHAR(tty));
 
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios->c_cflag & CRTSCTS)
 		info->MCR &= ~UART_MCR_RTS;
-		info->MCR_noint &= ~UART_MCR_RTS;
-	}
+
 	cli();
 	serial_out(info, UART_MCR, info->MCR);
 	sti();
@@ -1557,10 +1576,8 @@
 		else
 			rs_send_xchar(tty, START_CHAR(tty));
 	}
-	if (tty->termios->c_cflag & CRTSCTS) {
+	if (tty->termios->c_cflag & CRTSCTS)
 		info->MCR |= UART_MCR_RTS;
-		info->MCR_noint |= UART_MCR_RTS;
-	}
 	cli();
 	serial_out(info, UART_MCR, info->MCR);
 	sti();
@@ -1685,7 +1702,7 @@
 	if (!state->port || !state->type)
 		return 0;
 	if (state->type != old_state.type)
-		state->xmit_fifo_size =
+		info->xmit_fifo_size = state->xmit_fifo_size =
 			uart_config[state->type].dfl_xmit_fifo_size;
 	if (state->flags & ASYNC_INITIALIZED) {
 		if (((old_state.flags & ASYNC_SPD_MASK) !=
@@ -1752,33 +1769,21 @@
 		return error;
 	switch (cmd) {
 	case TIOCMBIS: 
-		if (arg & TIOCM_RTS) {
+		if (arg & TIOCM_RTS)
 			info->MCR |= UART_MCR_RTS;
-			info->MCR_noint |= UART_MCR_RTS;
-		}
-		if (arg & TIOCM_DTR) {
+		if (arg & TIOCM_DTR)
 			info->MCR |= UART_MCR_DTR;
-			info->MCR_noint |= UART_MCR_DTR;
-		}
 		break;
 	case TIOCMBIC:
-		if (arg & TIOCM_RTS) {
+		if (arg & TIOCM_RTS)
 			info->MCR &= ~UART_MCR_RTS;
-			info->MCR_noint &= ~UART_MCR_RTS;
-		}
-		if (arg & TIOCM_DTR) {
+		if (arg & TIOCM_DTR)
 			info->MCR &= ~UART_MCR_DTR;
-			info->MCR_noint &= ~UART_MCR_DTR;
-		}
 		break;
 	case TIOCMSET:
 		info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
 			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
 			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
-		info->MCR_noint = ((info->MCR_noint
-				    & ~(UART_MCR_RTS | UART_MCR_DTR))
-				   | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
-				   | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
 		break;
 	default:
 		return -EINVAL;
@@ -1821,11 +1826,17 @@
 		return;
 	current->state = TASK_INTERRUPTIBLE;
 	current->timeout = jiffies + duration;
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);
+#endif
 	cli();
 	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
 	schedule();
 	serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
 	sti();
+#ifdef SERIAL_DEBUG_SEND_BREAK
+	printk("done jiffies=%lu\n", jiffies);
+#endif
 }
 
 /*
@@ -2012,15 +2023,24 @@
 			if (retval)
 				return retval;
 			tty_wait_until_sent(tty, 0);
-			if (!arg)
+			if (current->signal & ~current->blocked)
+				return -EINTR;
+			if (!arg) {
 				send_break(info, HZ/4);	/* 1/4 second */
+				if (current->signal & ~current->blocked)
+					return -EINTR;
+			}
 			return 0;
 		case TCSBRKP:	/* support for POSIX tcsendbreak() */
 			retval = tty_check_change(tty);
 			if (retval)
 				return retval;
 			tty_wait_until_sent(tty, 0);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
 			send_break(info, arg ? arg*(HZ/10) : HZ/4);
+			if (current->signal & ~current->blocked)
+				return -EINTR;
 			return 0;
 		case TIOCGSOFTCAR:
 			return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg);
@@ -2179,14 +2199,29 @@
 
 	change_speed(info);
 
+	/* Handle transition to B0 status */
+	if ((old_termios->c_cflag & CBAUD) &&
+	    !(tty->termios->c_cflag & CBAUD)) {
+		info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+		cli();
+		serial_out(info, UART_MCR, info->MCR);
+		sti();
+	}
+	
+	/* Handle transition away from B0 status */
 	if (!(old_termios->c_cflag & CBAUD) &&
 	    (tty->termios->c_cflag & CBAUD)) {
 		info->MCR |= UART_MCR_DTR;
-		info->MCR_noint |= UART_MCR_DTR;
+		if (!tty->hw_stopped ||
+		    !(tty->termios->c_cflag & CRTSCTS)) {
+			info->MCR |= UART_MCR_RTS;
+		}
 		cli();
 		serial_out(info, UART_MCR, info->MCR);
 		sti();
 	}
+	
+	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
@@ -2293,7 +2328,7 @@
 		 * has completely drained; this is especially
 		 * important if there is a transmit FIFO!
 		 */
-		rs_wait_until_sent(tty, HZ);
+		rs_wait_until_sent(tty, info->timeout);
 	}
 	shutdown(info);
 	if (tty->driver.flush_buffer)
@@ -2324,25 +2359,48 @@
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 {
 	struct async_struct * info = (struct async_struct *)tty->driver_data;
-	unsigned long orig_jiffies;
+	unsigned long orig_jiffies, char_time;
+	int lsr;
 	
 	if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
 		return;
 
 	orig_jiffies = jiffies;
-	current->state = TASK_INTERRUPTIBLE;
-	current->counter = 0;	/* make us low-priority */
-	while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
-		current->timeout = jiffies + info->timeout;
+	/*
+	 * Set the check interval to be 1/5 of the estimated time to
+	 * send a single character, and make it at least 1.  The check
+	 * interval should also be less than the timeout.
+	 * 
+	 * Note: we have to use pretty tight timings here to satisfy
+	 * the NIST-PCTS.
+	 */
+	char_time = (info->timeout - HZ/50) / info->xmit_fifo_size;
+	char_time = char_time / 5;
+	if (char_time == 0)
+		char_time = 1;
+	if (timeout)
+	  char_time = MIN(char_time, timeout);
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+	printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time);
+	printk("jiff=%lu...", jiffies);
+#endif
+	while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) {
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+		printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
+#endif
+		current->state = TASK_INTERRUPTIBLE;
+		current->counter = 0;	/* make us low-priority */
+		current->timeout = jiffies + char_time;
 		schedule();
 		if (current->signal & ~current->blocked)
 			break;
-		if (timeout && ((orig_jiffies + timeout) > jiffies))
-			break;
-		if (jiffies > timeout)
+		if (timeout && ((orig_jiffies + timeout) < jiffies))
 			break;
 	}
 	current->state = TASK_RUNNING;
+#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+	printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
+#endif
 }
 
 /*
@@ -2457,7 +2515,8 @@
 	info->blocked_open++;
 	while (1) {
 		cli();
-		if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
+		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
+		    (tty->termios->c_cflag & CBAUD))
 			serial_out(info, UART_MCR,
 				   serial_inp(info, UART_MCR) |
 				   (UART_MCR_DTR | UART_MCR_RTS));
@@ -2751,10 +2810,15 @@
 	
 	if (!state->port)
 		return;
+		
 	info = &scr_info;	/* This is just for serial_{in,out} */
+
 	info->magic = SERIAL_MAGIC;
 	info->port = state->port;
 	info->flags = state->flags;
+
+	if(check_region(info->port,8))
+		return;		/* Area in use */
 
 	save_flags(flags); cli();
 	
diff -u --recursive --new-file v2.1.14/linux/drivers/char/softdog.c linux/drivers/char/softdog.c
--- v2.1.14/linux/drivers/char/softdog.c	Sat Jun 29 12:00:46 1996
+++ linux/drivers/char/softdog.c	Thu Dec 12 16:51:09 1996
@@ -1,18 +1,19 @@
 /*
- *	SoftDog	0.04:	A Software Watchdog Device
+ *	SoftDog	0.05:	A Software Watchdog Device
  *
- *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
- *
- *	Email us for quotes on Linux software and driver development. 
- *
- *			-----------------------
+ *	(c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved.
+ *				http://www.cymru.net
  *
  *	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.
+ *	
+ *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
  *
- *			-----------------------
+ *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
  *	Software only watchdog driver. Unlike its big brother the WDT501P
  *	driver this won't always recover a failed machine.
@@ -31,6 +32,8 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <asm/uaccess.h>
 
 #define WATCHDOG_MINOR	130
 #define TIMER_MARGIN	60		/* (secs) Default is 1 minute */
@@ -93,7 +96,7 @@
 #endif	
 }
 
-static int softdog_write(struct inode *inode, struct file *file, const char *data, int len)
+static void softdog_ping(void)
 {
 	/*
 	 *	Refresh the timer.
@@ -101,7 +104,49 @@
 	del_timer(&watchdog_ticktock);
 	watchdog_ticktock.expires=jiffies + (soft_margin * HZ);
 	add_timer(&watchdog_ticktock);
-	return 1;
+	return;
+}
+
+static long softdog_write(struct inode *inode, struct file *file, const char *data, unsigned long len)
+{
+	/*
+	 *	Refresh the timer.
+	 */
+	if(len)
+	{
+		softdog_ping();
+		return 1;
+	}
+	return 0;
+}
+
+static int softdog_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	int i;
+	static struct watchdog_info ident=
+	{
+		0,
+		0,
+		"Software Watchdog"
+	};
+	switch(cmd)
+	{
+		default:
+			return -ENOIOCTLCMD;
+		case WDIOC_GETSUPPORT:
+			i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct watchdog_info));
+			if (i)
+				return i;
+			else
+				return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident));
+		case WDIOC_GETSTATUS:
+		case WDIOC_GETBOOTSTATUS:
+			return put_user(0,(int *)arg);
+		case WDIOC_KEEPALIVE:
+			softdog_ping();
+			return 0;
+	}
 }
 
 static struct file_operations softdog_fops=
@@ -111,7 +156,7 @@
 	softdog_write,	/* Write */
 	NULL,		/* Readdir */
 	NULL,		/* Select */
-	NULL,		/* Ioctl */
+	softdog_ioctl,	/* Ioctl */
 	NULL,		/* MMap */
 	softdog_open,
 	softdog_release,
@@ -131,7 +176,7 @@
 	misc_register(&softdog_miscdev);
 	init_timer(&watchdog_ticktock);
 	watchdog_ticktock.function=watchdog_fire;
-	printk("Software Watchdog Timer: 0.04, timer margin: %d sec\n", soft_margin);
+	printk("Software Watchdog Timer: 0.05, timer margin: %d sec\n", soft_margin);
 }	
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.14/linux/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c
--- v2.1.14/linux/drivers/char/tpqic02.c	Tue Oct 29 19:58:09 1996
+++ linux/drivers/char/tpqic02.c	Thu Dec 12 15:47:41 1996
@@ -200,6 +200,15 @@
 
 #define REALLY_SLOW_IO		/* it sure is ... */
 
+#include <linux/config.h>
+
+#ifdef MODULE
+  #ifdef CONFIG_QIC02_DYNCONF
+    #error dynamic configuration as module not implemented!
+  #endif
+#endif
+ 
+#include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/timer.h>
 #include <linux/fs.h>
@@ -210,9 +219,9 @@
 #include <linux/fcntl.h>
 #include <linux/delay.h>
 #include <linux/tpqic02.h>
-#include <linux/config.h>
 #include <linux/mm.h>
-
+#include <linux/malloc.h>
+ 
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/io.h>
@@ -333,8 +342,12 @@
  * at 512 bytes, to prevent problems with 64k boundaries.
  */
 
+#ifdef MODULE
+static char *qic02_tape_buf = NULL;
+#else
 static volatile char qic02_tape_buf[TPQBUF_SIZE+TAPE_BLKSIZE];
 /* A really good compiler would be able to align this at 512 bytes... :-( */
+#endif /* MODULE */
 
 static unsigned long buffaddr;	/* aligned physical address of buffer */
 
@@ -1920,7 +1933,6 @@
 static long qic02_tape_read(struct inode * inode, struct file * filp,
 	char * buf, unsigned long count)
 {
-	int error;
 	kdev_t dev = inode->i_rdev;
 	unsigned short flags = filp->f_flags;
 	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
@@ -1946,12 +1958,6 @@
 	if (status_bytes_wr)	/* Once written, no more reads, 'till after WFM. */
 		return -EACCES;
 
-
-	/* Make sure buffer is safe to write into. */
-	error = verify_area(VERIFY_WRITE, buf, count);
-	if (error)
-		return error;
-
 	/* This is rather ugly because it has to implement a finite state
 	 * machine in order to handle the EOF situations properly.
 	 */
@@ -2039,7 +2045,10 @@
 			}
 			/* copy buffer to user-space in one go */
 			if (bytes_done>0)
-				copy_to_user( (void *) buf, (void *) bus_to_virt(buffaddr), bytes_done);
+				if (copy_to_user( (void *) buf, (void *) bus_to_virt(buffaddr), 
+						bytes_done))
+					return -EFAULT;
+
 #if 1
 			/* Checks Ton's patch below */
 			if ((return_read_eof == NO) && (status_eof_detected == YES)) {
@@ -2096,7 +2105,6 @@
 static long qic02_tape_write(struct inode * inode, struct file * filp,
 	const char * buf, unsigned long count)
 {
-	int error;
 	kdev_t dev = inode->i_rdev;
 	unsigned short flags = filp->f_flags;
 	unsigned long bytes_todo, bytes_done, total_bytes_done = 0;
@@ -2130,11 +2138,6 @@
 		return -EACCES;	/* don't even try when write protected */
 	}
 
-	/* Make sure buffer is safe to read from. */
-	error = verify_area(VERIFY_READ, buf, count);
-	if (error)
-		return error;
-
 	if (doing_read == YES)
 		terminate_read(0);
 
@@ -2167,7 +2170,9 @@
 
 		/* copy from user to DMA buffer and initiate transfer. */
 		if (bytes_todo>0) {
-			copy_from_user( (void *) bus_to_virt(buffaddr), (const void *) buf, bytes_todo);
+			if (copy_from_user( (void *) bus_to_virt(buffaddr), 
+					(const void *) buf, bytes_todo))
+				return -EFAULT;
 
 /****************** similar problem with read() at FM could happen here at EOT.
  ******************/
@@ -2259,11 +2264,19 @@
 		       kdevname(dev), flags);
 	}
 
-	if (MINOR(dev)==255)	/* special case for resetting */
+#ifdef MODULE
+       MOD_INC_USE_COUNT;
+#endif
+
+	if (MINOR(dev)==255) {	/* special case for resetting */
+#ifdef MODULE
+                MOD_DEC_USE_COUNT;
+#endif
 		if (suser())
 			return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO;
 		else
 			return -EPERM;
+	}
 
 	if (status_dead==YES)
 		/* Allow `mt reset' ioctl() even when already open()ed. */
@@ -2271,6 +2284,9 @@
 
 	/* Only one at a time from here on... */
 	if (filp->f_count>1) {	/* filp->f_count==1 for the first open() */
+#ifdef MODULE
+                MOD_DEC_USE_COUNT;
+#endif
 		return -EBUSY;
 	}
 
@@ -2318,6 +2334,9 @@
 
 	if (s != TE_OK) {
 		tpqputs(TPQD_ALWAYS, "open: sense() failed");
+#ifdef MODULE
+                MOD_DEC_USE_COUNT;
+#endif
 		return -EIO;
 	}
 
@@ -2327,6 +2346,9 @@
 	 */
 	if ((tperror.exs & TP_ST0) && (tperror.exs & TP_CNI)) {
 		tpqputs(TPQD_ALWAYS, "No tape present.");
+#ifdef MODULE
+                MOD_DEC_USE_COUNT;
+#endif
 		return -EIO;
 	}
 
@@ -2355,6 +2377,9 @@
 		s = do_qic_cmd(QCMD_REWIND, TIM_R);
 		if (s != 0) {
 			tpqputs(TPQD_ALWAYS, "open: rewind failed");
+#ifdef MODULE
+			MOD_DEC_USE_COUNT;
+#endif
 			return -EIO;
 		}
 	}
@@ -2366,12 +2391,18 @@
 	if (status_dead==YES) {
 		tpqputs(TPQD_ALWAYS, "open: tape dead, attempting reset");
 		if (tape_reset(1)!=TE_OK) {
+#ifdef MODULE
+			MOD_DEC_USE_COUNT;
+#endif
 			return -ENXIO;
 		} else {
 			status_dead = NO;
 			if (tp_sense(~(TP_ST1|TP_ILL)) != TE_OK) {
 				tpqputs(TPQD_ALWAYS, "open: tp_sense() failed\n");
 				status_dead = YES;	/* try reset next time */
+#ifdef MODULE
+				MOD_DEC_USE_COUNT;
+#endif
 				return -EIO;
 			}
 		}
@@ -2425,6 +2456,9 @@
 	if (s != 0) {
 		status_dead = YES;	/* force reset */
 		current_tape_dev = 0; /* earlier 0xff80 */
+#ifdef MODULE
+		MOD_DEC_USE_COUNT;
+#endif
 		return -EIO;
 	}
 
@@ -2441,6 +2475,9 @@
 		       kdevname(dev));
 
 	if (status_zombie==YES)		/* don't rewind in zombie mode */
+#ifdef MODULE
+		MOD_DEC_USE_COUNT;
+#endif
 		return;
 
 	/* Terminate any pending write cycle. Terminating the read-cycle
@@ -2458,7 +2495,9 @@
 		tpqputs(TPQD_REWIND, "release: Doing rewind...");
 		(void) do_qic_cmd(QCMD_REWIND, TIM_R);
 	}
-
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
 	return;
 } /* qic02_tape_release */
 
@@ -2558,7 +2597,6 @@
 		     unsigned int iocmd, unsigned long ioarg)
 {
 	int error;
-	short i;
 	int dev_maj = MAJOR(inode->i_rdev);
 	int c;
 	struct mtop operation;
@@ -2588,9 +2626,9 @@
 	if (c == DDIOCSDBG) {
 		if (!suser())
 			return -EPERM;
-		error = verify_area(VERIFY_READ, (int *) ioarg, sizeof(int));
-		if (error) return error;
-		c = get_user(sizeof(int), (int *) ioarg);
+		error = get_user(c, (int *) ioarg);
+		if (error)
+			return error;
 		if (c==0) {
 			QIC02_TAPE_DEBUG = 0;
 			return 0;
@@ -2614,15 +2652,11 @@
 			return -EFAULT;
 		}
 
-		/* check for valid user address */
-		error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(qic02_tape_dynconf));
-		if (error)
-			return error;
-		/* copy current settings to user space */
+		/* check for valid user address and copy current settings to user space */
 		stp = (char *) &qic02_tape_dynconf;
 		argp = (char *) ioarg;
-		for (i=0; i<sizeof(qic02_tape_dynconf); i++) 
-			put_user(*stp++, argp++);
+		if (copy_to_user(stp, argp, sizeof(qic02_tape_dynconf)))
+			return -EFAULT;
 		return 0;
 
 	} else if (c == (MTIOCSETCONFIG & IOCCMD_MASK)) {
@@ -2641,14 +2675,12 @@
 			return -EPERM;
 		if ((doing_read!=NO) || (doing_write!=NO))
 			return -EBUSY;
-		error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(qic02_tape_dynconf));
-		if (error)
-			return error;
 
 		/* copy struct from user space to kernel space */
 		stp = (char *) &qic02_tape_dynconf;
 		argp = (char *) ioarg;
-		copy_from_user(stp, argp, sizeof(qic02_tape_dynconf));
+		if (copy_from_user(stp, argp, sizeof(qic02_tape_dynconf)))
+			return -EFAULT;
 		if (status_zombie==NO)
 			qic02_release_resources();	/* and go zombie */
 		if (update_ifc_masks(qic02_tape_dynconf.ifc_type))
@@ -2672,14 +2704,12 @@
 			tpqputs(TPQD_ALWAYS, "sizeof(struct mtop) does not match!");
 			return -EFAULT;
 		}
-		error = verify_area(VERIFY_READ, (char *) ioarg, sizeof(operation));
-		if (error)
-			return error;
 
 		/* copy mtop struct from user space to kernel space */
 		stp = (char *) &operation;
 		argp = (char *) ioarg;
-		copy_from_user(stp, argp, sizeof(operation));
+		if (copy_from_user(stp, argp, sizeof(operation)))
+			return -EFAULT;
 
 		/* ---note: mt_count is signed, negative seeks must be
 		 * ---	    translated to seeks in opposite direction!
@@ -2727,11 +2757,6 @@
 			return -EFAULT;
 		}
 
-		/* check for valid user address */
-		error =	verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(ioctl_status));
-		if (error)
-			return error;
-
 		/* It appears (gmt(1)) that it is normal behaviour to
 		 * first set the status with MTNOP, and then to read
 		 * it out with MTIOCGET
@@ -2740,8 +2765,8 @@
 		/* copy results to user space */
 		stp = (char *) &ioctl_status;
 		argp = (char *) ioarg;
-		for (i=0; i<sizeof(ioctl_status); i++) 
-			put_user(*stp++, argp++);
+		if (copy_to_user(stp, argp, sizeof(ioctl_status)))
+			return -EFAULT;
 		return 0;
 
 
@@ -2755,11 +2780,6 @@
 			return -EFAULT;
 		}
 
-		/* check for valid user address */
-		error = verify_area(VERIFY_WRITE, (void *) ioarg, sizeof(ioctl_tell));
-		if (error)
-			return error;
-
 		tpqputs(TPQD_IOCTLS, "MTTELL reading block address");
 		if ((doing_read==YES) || (doing_write==YES))
 			finish_rw(AR_QCMDV_TELL_BLK);
@@ -2773,8 +2793,8 @@
 		/* copy results to user space */
 		stp = (char *) &ioctl_tell;
 		argp = (char *) ioarg;
-		for (i=0; i<sizeof(ioctl_tell); i++) 
-			put_user(*stp++, argp++);
+		if (copy_to_user(stp, argp, sizeof(ioctl_status)))
+			return -EFAULT;
 		return 0;
 
 	} else
@@ -2914,16 +2934,39 @@
 
 	printk(TPQIC02_NAME ": DMA buffers: %u blocks", NR_BLK_BUF);
 
-	/* Setup the page-address for the dma transfer.
-	 * This assumes a one-to-one identity mapping between
-	 * kernel addresses and physical memory.
+	/* 
+	 * Setup the page-address for the dma transfer.
 	 */
+#ifdef MODULE
+        qic02_tape_buf = kmalloc(TPQBUF_SIZE+TAPE_BLKSIZE, GFP_DMA);
+        if (qic02_tape_buf == NULL) {
+#ifndef CONFIG_QIC02_DYNCONF
+        /*
+         * irq and dma were requested by qic_get_resources, so
+         * relase them only when _not_ using DYNCONF
+         */
+		qic02_release_resources();
+#endif
+		return -ENODEV;
+       }
+       buffaddr = align_buffer(virt_to_bus((unsigned long *)qic02_tape_buf),
+                               TAPE_BLKSIZE);
+       printk(", at address 0x%lx (0x%lx)\n", buffaddr,
+              virt_to_bus((unsigned long *)qic02_tape_buf));
+#else /* no MODULE */
 	buffaddr = align_buffer(virt_to_bus(qic02_tape_buf), TAPE_BLKSIZE);
 	printk(", at address 0x%lx (0x%lx)\n", buffaddr, (unsigned long) &qic02_tape_buf);
+#endif /* MODULE */
+
 
 #ifndef CONFIG_MAX_16M
 	if (buffaddr+TPQBUF_SIZE>=0x1000000) {
 		printk(TPQIC02_NAME ": DMA buffer *must* be in lower 16MB\n");
+#ifdef MODULE
+		qic02_release_resources();
+                kfree(qic02_tape_buf);
+#endif
+
 		return -ENODEV;
 	}
 #endif
@@ -2932,8 +2975,7 @@
 	if (register_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME, &qic02_tape_fops)) {
 		printk(TPQIC02_NAME ": Unable to get chrdev major %d\n", QIC02_TAPE_MAJOR);
 #ifndef CONFIG_QIC02_DYNCONF
-		free_irq(QIC02_TAPE_IRQ, NULL);
-		free_dma(QIC02_TAPE_DMA);
+		qic02_release_resources();
 #endif
 		return -ENODEV;
 	}
@@ -2947,10 +2989,8 @@
 	if (tape_reset(0)!=TE_OK || tp_sense(TP_WRP|TP_POR|TP_CNI)!=TE_OK) {
 		/* No drive detected, so vanish */
 		tpqputs(TPQD_ALWAYS, "No drive detected -- driver going on vacation...");
-		status_dead = YES;
-		free_irq(QIC02_TAPE_IRQ, NULL);
-		free_dma(QIC02_TAPE_DMA);
 		unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME);
+		qic02_release_resources();
 		return -ENODEV;
 	} else {
 		if (is_exception()) {
@@ -2973,3 +3013,22 @@
 	return 0;
 } /* qic02_tape_init */
 
+#ifdef MODULE
+
+int init_module(void) {
+	return qic02_tape_init();
+}
+
+void cleanup_module(void) {
+	unsigned long flags;
+       
+	save_flags(flags); 
+	cli(); 
+	unregister_chrdev(QIC02_TAPE_MAJOR, TPQIC02_NAME); 
+	qic02_release_resources();
+	if (qic02_tape_buf) kfree(qic02_tape_buf); 
+	sti(); 
+	restore_flags(flags); 
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.14/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.14/linux/drivers/char/tty_io.c	Fri Nov  1 17:13:16 1996
+++ linux/drivers/char/tty_io.c	Wed Dec 11 17:16:07 1996
@@ -705,7 +705,7 @@
 
 void start_tty(struct tty_struct *tty)
 {
-	if (!tty->stopped)
+	if (!tty->stopped || tty->flow_stopped)
 		return;
 	tty->stopped = 0;
 	if (tty->link && tty->link->packet) {
@@ -1923,6 +1923,9 @@
 		panic("Couldn't register /dev/console driver\n");
 	
 	kbd_init();
+#ifdef CONFIG_ESP  /* init ESP before rs, so rs doesn't see the port */
+	esp_init();
+#endif
 #ifdef CONFIG_SERIAL
 	rs_init();
 #endif
diff -u --recursive --new-file v2.1.14/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v2.1.14/linux/drivers/char/tty_ioctl.c	Thu Dec 12 17:02:42 1996
+++ linux/drivers/char/tty_ioctl.c	Sun Dec  8 21:21:15 1996
@@ -47,8 +47,7 @@
 #ifdef TTY_DEBUG_WAIT_UNTIL_SENT
 	printk("%s wait until sent...\n", tty_name(tty));
 #endif
-	if (!tty->driver.chars_in_buffer ||
-	    !tty->driver.chars_in_buffer(tty))
+	if (!tty->driver.chars_in_buffer)
 		return;
 	add_wait_queue(&tty->write_wait, &wait);
 	current->counter = 0;	/* make us low-priority */
@@ -462,10 +461,16 @@
 				return retval;
 			switch (arg) {
 			case TCOOFF:
-				stop_tty(tty);
+				if (!tty->flow_stopped) {
+					tty->flow_stopped = 1;
+					stop_tty(tty);
+				}
 				break;
 			case TCOON:
-				start_tty(tty);
+				if (tty->flow_stopped) {
+					tty->flow_stopped = 0;
+					start_tty(tty);
+				}
 				break;
 			case TCIOFF:
 				if (STOP_CHAR(tty) != __DISABLED_CHAR)
diff -u --recursive --new-file v2.1.14/linux/drivers/char/vga.c linux/drivers/char/vga.c
--- v2.1.14/linux/drivers/char/vga.c	Tue Oct 29 19:58:10 1996
+++ linux/drivers/char/vga.c	Thu Dec 12 16:51:09 1996
@@ -33,6 +33,10 @@
  * because it causes screen to flicker, by Mitja Horvat
  * 5-May-96 <mitja.horvat@guest.arnes.si>
  *
+ * Use 2 outw instead of 4 outb_p to reduce erroneous text 
+ * flashing on RHS of screen during heavy console scrolling .
+ * Oct 1996, Paul Gortmaker.
+ *
  */
 
 #include <linux/sched.h>
@@ -67,6 +71,29 @@
 #define dac_reg (0x3c8)
 #define dac_val (0x3c9)
 
+/*
+ * By replacing the four outb_p with two back to back outw, we can reduce
+ * the window of opportunity to see text mislocated to the RHS of the
+ * console during heavy scrolling activity. However there is the remote
+ * possibility that some pre-dinosaur hardware won't like the back to back
+ * I/O. Since the Xservers get away with it, we should be able to as well.
+ */
+static inline void write_vga(unsigned char reg, unsigned int val)
+{
+#ifndef SLOW_VGA
+	unsigned int v1, v2;
+
+	v1 = reg + (val & 0xff00);
+	v2 = reg + 1 + ((val << 8) & 0xff00);
+	outw(v1, video_port_reg);
+	outw(v2, video_port_reg);
+#else
+	outb_p(reg, video_port_reg);
+	outb_p(val >> 8, video_port_val);
+	outb_p(reg+1, video_port_reg);
+	outb_p(val & 0xff, video_port_val);
+#endif
+}
 
 void
 set_palette (void)
@@ -94,10 +121,7 @@
 
 	save_flags(flags); cli();
 	__origin = offset;
-	outb_p(12, video_port_reg);
-	outb_p(offset >> 8, video_port_val);
-	outb_p(13, video_port_reg);
-	outb_p(offset, video_port_val);
+	write_vga(12, offset);
 	restore_flags(flags);
 }
 
@@ -110,10 +134,7 @@
   /* This is inefficient, we could just put the cursor at 0xffff,
      but perhaps the delays due to the inefficiency are useful for
      some hardware... */
-	outb_p(14, video_port_reg);
-	outb_p(0xff&((video_mem_term-video_mem_base)>>9), video_port_val);
-	outb_p(15, video_port_reg);
-	outb_p(0xff&((video_mem_term-video_mem_base)>>1), video_port_val);
+	write_vga(14, (video_mem_term - video_mem_base + 1)>>1);
 }
 
 void
@@ -127,10 +148,7 @@
 		__set_origin(__real_origin);
 	save_flags(flags); cli();
 	if (deccm) {
-		outb_p(14, video_port_reg);
-		outb_p(0xff&((pos-video_mem_base)>>9), video_port_val);
-		outb_p(15, video_port_reg);
-		outb_p(0xff&((pos-video_mem_base)>>1), video_port_val);
+		write_vga(14, (pos - video_mem_base)>>1);
 	} else
 		hide_cursor();
 	restore_flags(flags);
diff -u --recursive --new-file v2.1.14/linux/drivers/char/wd501p.h linux/drivers/char/wd501p.h
--- v2.1.14/linux/drivers/char/wd501p.h	Mon May 20 08:08:36 1996
+++ linux/drivers/char/wd501p.h	Thu Dec 12 16:51:09 1996
@@ -51,8 +51,10 @@
 
 #ifdef CONFIG_WDT501_FAN	/* Full board, Fan has no tachometer */
 #define FEATUREMAP1		0
+#define WDT_OPTION_MASK		(WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT)
 #else
 #define FEATUREMAP1		WDC_SR_FANGOOD
+#define WDT_OPTION_MASK		(WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER|WDIOF_EXTERN1|WDIOF_EXTERN2)
 #endif
 
 #define FEATUREMAP2		0
@@ -65,6 +67,7 @@
 #ifdef CONFIG_WDT_500		/* Minimal board */
 #define FEATUREMAP1		(WDC_SR_TGOOD|WDC_SR_FANGOOD)
 #define FEATUREMAP2		(WDC_SR_PSUOVER|WDC_SR_PSUUNDR)
+#define WDT_OPTION_MASK		(WDIOF_OVERHEAT)
 #endif
 
 #ifndef FEATUREMAP1
diff -u --recursive --new-file v2.1.14/linux/drivers/char/wdt.c linux/drivers/char/wdt.c
--- v2.1.14/linux/drivers/char/wdt.c	Tue Nov 19 15:53:54 1996
+++ linux/drivers/char/wdt.c	Thu Dec 12 16:51:09 1996
@@ -1,23 +1,27 @@
 /*
- *	Industrial Computer Source WDT500/501 driver for Linux 1.3.x
+ *	Industrial Computer Source WDT500/501 driver for Linux 2.1.x
  *
- *	(c) Copyright 1995	CymruNET Ltd
- *				Innovation Centre
- *				Singleton Park
- *				Swansea
- *				Wales
- *				UK
- *				SA2 8PP
+ *	(c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved.
+ *				http://www.cymru.net
  *
- *	http://www.cymru.net
+ *	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.
+ *	
+ *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
  *
- *	This driver is provided under the GNU public license, incorporated
- *	herein by reference. The driver is provided without warranty or 
- *	support.
+ *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
- *	Release 0.05.
+ *	Release 0.06.
  *
- *	Some changes by Dave Gregorich to fix modularisation and minor bugs.
+ *	Fixes
+ *		Dave Gregorich	:	Modularisation and minor bugs
+ *		Alan Cox	:	Added the watchdog ioctl() stuff
+ *		Alan Cox	:	Fixed the reboot problem (as noted by
+ *					Matt Crocker).
  */
 
 #include <linux/config.h>
@@ -28,6 +32,7 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/miscdevice.h>
+#include <linux/watchdog.h>
 #include "wd501p.h"
 #include <linux/malloc.h>
 #include <linux/ioport.h>
@@ -35,6 +40,7 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <linux/notifier.h>
 
 static int wdt_is_open=0;
 
@@ -69,7 +75,33 @@
  *	Kernel methods.
  */
  
-static void wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static int wdt_status(void)
+{
+	/*
+	 *	Status register to bit flags
+	 */
+	 
+	int flag=0;
+	unsigned char status=inb_p(WDT_SR);
+	status|=FEATUREMAP1;
+	status&=~FEATUREMAP2;	
+	
+	if(!(status&WDC_SR_TGOOD))
+		flag|=WDIOF_OVERHEAT;
+	if(!(status&WDC_SR_PSUOVER))
+		flag|=WDIOF_POWEROVER;
+	if(!(status&WDC_SR_PSUUNDR))
+		flag|=WDIOF_POWERUNDER;
+	if(!(status&WDC_SR_FANGOOD))
+		flag|=WDIOF_FANFAULT;
+	if(status&WDC_SR_ISOI0)
+		flag|=WDIOF_EXTERN1;
+	if(status&WDC_SR_ISII1)
+		flag|=WDIOF_EXTERN2;
+	return flag;
+}
+
+void wdt_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	/*
 	 *	Read the status register see what is up and
@@ -111,14 +143,23 @@
 	return -ESPIPE;
 }
 
-static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+static void wdt_ping(void)
 {
 	/* Write a watchdog value */
 	inb_p(WDT_DC);
 	wdt_ctr_mode(1,2);
 	wdt_ctr_load(1,WD_TIMO);		/* Timeout */
 	outb_p(0, WDT_DC);
-	return count;
+}
+
+static long wdt_write(struct inode *inode, struct file *file, const char *buf, unsigned long count)
+{
+	if(count)
+	{
+		wdt_ping();
+		return 1;
+	}
+	return 0;
 }
 
 /*
@@ -150,7 +191,41 @@
 static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	unsigned long arg)
 {
-	return -EINVAL;
+	int i;
+	static struct watchdog_info ident=
+	{
+		WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
+			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT,
+		1,
+		"WDT500/501"
+	};
+	
+	ident.options&=WDT_OPTION_MASK;	/* Mask down to the card we have */
+	switch(cmd)
+	{
+		default:
+			return -ENOIOCTLCMD;
+		case WDIOC_GETSUPPORT:
+			i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(struct watchdog_info));
+			if (i)
+				return i;
+			else
+				return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident));
+
+		case WDIOC_GETSTATUS:
+			i = verify_area(VERIFY_WRITE, (void*) arg, sizeof(int));
+			if (i)
+				return i;
+			else 
+			{
+				return put_user(wdt_status(),(int *)arg);
+			}
+		case WDIOC_GETBOOTSTATUS:
+			return put_user(0, (int *)arg);
+		case WDIOC_KEEPALIVE:
+			wdt_ping();
+			return 0;
+	}
 }
 
 static int wdt_open(struct inode *inode, struct file *file)
@@ -197,6 +272,22 @@
 }
 
 /*
+ *	Notifier for system down
+ */
+
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+	void *unused)
+{
+	if(code==SYS_DOWN || code==SYS_HALT)
+	{
+		/* Turn the card off */
+		inb_p(WDT_DC);
+		wdt_ctr_load(2,0);
+	}
+	return NOTIFY_DONE;
+}
+ 
+/*
  *	Kernel Interfaces
  */
  
@@ -229,6 +320,18 @@
 };
 #endif
 
+/*
+ *	The WDT card needs to learn about soft shutdowns in order to
+ *	turn the timebomb registers off. 
+ */
+ 
+static struct notifier_block wdt_notifier=
+{
+	wdt_notify_sys,
+	NULL,
+	0
+};
+
 #ifdef MODULE
 
 int init_module(void)
@@ -244,6 +347,7 @@
 	misc_register(&temp_miscdev);
 #endif	
 	request_region(io, 8, "wdt501");
+	notifier_chain_register(&boot_notifier_list, &wdt_notifier);
 	return 0;
 }
 
@@ -253,6 +357,7 @@
 #ifdef CONFIG_WDT_501	
 	misc_deregister(&temp_miscdev);
 #endif	
+	notifier_chain_unregister(&boot_notifier_list, &wdt_notifier);
 	release_region(io,8);
 	free_irq(irq, NULL);
 }
@@ -272,6 +377,7 @@
 	misc_register(&temp_miscdev);
 #endif	
 	request_region(io, 8, "wdt501");
+	notifier_chain_register(&boot_notifier_list, &wdt_notifier);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.14/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v2.1.14/linux/drivers/net/3c509.c	Thu Dec 12 17:02:42 1996
+++ linux/drivers/net/3c509.c	Thu Dec 12 16:51:09 1996
@@ -167,7 +167,11 @@
 		}
 	}
 
-#ifdef CONFIG_MCA
+/*
+ * This has to be coded according to Documentation/mca.txt before
+ * this driver can be used with the 3c529 MCA cards.
+ */
+#if 0 /* #ifdef CONFIG_MCA */
 	if (MCA_bus) {
 		mca_adaptor_select_mode(1);
 		for (i = 0; i < 8; i++)
diff -u --recursive --new-file v2.1.14/linux/drivers/net/3c523.c linux/drivers/net/3c523.c
--- v2.1.14/linux/drivers/net/3c523.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/3c523.c	Thu Dec 12 16:51:09 1996
@@ -0,0 +1,1326 @@
+/* 
+net-3-driver for the 3c523 Etherlink/MC card (i82586 Ethernet chip)
+
+
+This is an extension to the Linux operating system, and is covered by the
+same Gnu Public License that covers that work.
+
+Copyright 1995, 1996 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca)
+
+This is basically Michael Hipp's ni52 driver, with a new probing
+algorithm and some minor changes to the 82586 CA and reset routines.
+Thanks a lot Michael for a really clean i82586 implementation!  Unless
+otherwise documented in ni52.c, any bugs are mine.
+
+Contrary to the Ethernet-HOWTO, this isn't based on the 3c507 driver in
+any way.  The ni52 is a lot easier to modify.
+
+sources:
+  ni52.c
+
+  Crynwr packet driver collection was a great reference for my first
+  attempt at this sucker.  The 3c507 driver also helped, until I noticed
+  that ni52.c was a lot nicer.
+
+  EtherLink/MC: Micro Channel Ethernet Adapter Technical Reference
+  Manual, courtesy of 3Com CardFacts, documents the 3c523-specific
+  stuff.  Information on CardFacts is found in the Ethernet HOWTO.
+  Also see <a href="http://www.3com.com/">
+
+  Microprocessor Communications Support Chips, T.J. Byers, ISBN
+  0-444-01224-9, has a section on the i82586.  It tells you just enough
+  to know that you really don't want to learn how to program the chip.
+
+  The original device probe code was stolen from ps2esdi.c
+
+Known Problems:
+  Since most of the code was stolen from ni52.c, you'll run across the
+  same bugs in the 0.62 version of ni52.c, plus maybe a few because of
+  the 3c523 idiosynchacies.  The 3c523 has 16K of RAM though, so there
+  shouldn't be the overrun problem that the 8K ni52 has.
+
+  This driver is for a 16K adapter.  It should work fine on the 64K
+  adapters, but it will only use one of the 4 banks of RAM.  Modifying
+  this for the 64K version would require a lot of heinous bank
+  switching, which I'm sure not interested in doing.  If you try to
+  implement a bank switching version, you'll basically have to remember
+  what bank is enabled and do a switch everytime you access a memory
+  location that's not current.  You'll also have to remap pointers on
+  the driver side, because it only knows about 16K of the memory.
+  Anyone desperate or masochistic enough to try?
+
+  It seems to be stable now when multiple transmit buffers are used.  I
+  can't see any performance difference, but then I'm working on a 386SX.
+
+  Multicast doesn't work.  It doesn't even pretend to work.  Don't use
+  it.  Don't compile your kernel with multicast support.  I don't know
+  why.
+
+Features:
+  This driver is useable as a loadable module.  If you try to specify an
+  IRQ or a IO address (via insmod 3c523.o irq=xx io=0xyyy), it will
+  search the MCA slots until it finds a 3c523 with the specified
+  parameters.
+
+  This driver should support multiple ethernet cards, but I can't test
+  that.  If someone would I'd greatly appreciate it.
+
+  This has been tested with both BNC and TP versions, internal and
+  external transceivers.  Haven't tested with the 64K version (that I
+  know of).
+
+History:
+  Jan 1st, 1996
+  	first public release
+  Feb 4th, 1996
+	update to 1.3.59, incorporated multicast diffs from ni52.c
+  Feb 15th, 1996
+  	added shared irq support
+
+  $Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/mca.h>
+#include <asm/processor.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "3c523.h"
+
+/*************************************************************************/
+#define DEBUG       /* debug on */
+#define SYSBUSVAL 0 /* 1 = 8 Bit, 0 = 16 bit - 3c523 only does 16 bit*/
+
+#define make32(ptr16) (p->memtop + (short) (ptr16) )
+#define make24(ptr32) ((char *) (ptr32) - p->base)
+#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop ))
+
+/*************************************************************************/
+/*
+    Tables to which we can map values in the configuration registers.
+*/
+static int irq_table[] = {12, 7, 3, 9};
+static int csr_table[] = {0x300, 0x1300, 0x2300, 0x3300};
+static int shm_table[] = {0x0c0000, 0x0c8000, 0x0d0000, 0x0d8000};
+
+/******************* how to calculate the buffers *****************************
+
+
+  * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+  * --------------- in a different (more stable?) mode. Only in this mode it's
+  *                 possible to configure the driver with 'NO_NOPCOMMANDS'
+
+sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
+sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
+sizeof(rfd) = 24; sizeof(rbd) = 12; 
+sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
+sizeof(nop_cmd) = 8; 
+
+  * if you don't know the driver, better do not change this values: */
+
+#define RECV_BUFF_SIZE 1524 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1524 /* slightly oversized */
+#define NUM_XMIT_BUFFS 4    /* config for both, 8K and 16K shmem */
+#define NUM_RECV_BUFFS_8  1 /* config for 8K shared mem */
+#define NUM_RECV_BUFFS_16 6 /* config for 16K shared mem */
+
+#if (NUM_XMIT_BUFFS == 1)
+#define NO_NOPCOMMANDS      /* only possible with NUM_XMIT_BUFFS=1 */
+#endif
+
+/**************************************************************************/
+
+#define DELAY(x) {int i=jiffies; \
+                  if(loops_per_sec == 1) \
+                     while(i+(x)>jiffies); \
+                  else \
+                     __delay((loops_per_sec>>5)*x); \
+                 }
+
+/* a much shorter delay: */
+#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+
+/* wait for command with timeout: */
+#define WAIT_4_SCB_CMD() { int i; \
+  for(i=0;i<1024;i++) { \
+    if(!p->scb->cmd) break; \
+    DELAY_16(); \
+    if(i == 1023) { \
+      printk("%s:%d: scb_cmd timed out .. resetting i82586\n",\
+      	dev->name,__LINE__); \
+      elmc_id_reset586(); } } }
+
+static void    elmc_interrupt(int irq,void *dev_id,struct pt_regs *reg_ptr);
+static int     elmc_open(struct device *dev);
+static int     elmc_close(struct device *dev);
+static int     elmc_send_packet(struct sk_buff *,struct device *);
+static struct  enet_statistics *elmc_get_stats(struct device *dev);
+static void    set_multicast_list(struct device *dev);
+
+/* helper-functions */
+static int     init586(struct device *dev);
+static int     check586(struct device *dev,char *where,unsigned size);
+static void    alloc586(struct device *dev);
+static void    startrecv586(struct device *dev);
+static void   *alloc_rfa(struct device *dev,void *ptr);
+static void    elmc_rcv_int(struct device *dev);
+static void    elmc_xmt_int(struct device *dev);
+static void    elmc_rnr_int(struct device *dev);
+
+struct priv {
+  struct enet_statistics stats;
+  unsigned long base;
+  char *memtop;
+  volatile struct rfd_struct  *rfd_last,*rfd_top,*rfd_first;
+  volatile struct scp_struct  *scp;  /* volatile is important */
+  volatile struct iscp_struct *iscp; /* volatile is important */
+  volatile struct scb_struct  *scb;  /* volatile is important */
+  volatile struct tbd_struct  *xmit_buffs[NUM_XMIT_BUFFS];
+  volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS];
+#if (NUM_XMIT_BUFFS == 1)
+  volatile struct nop_cmd_struct *nop_cmds[2];
+#else
+  volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS];
+#endif
+  volatile int    nop_point,num_recv_buffs;
+  volatile char  *xmit_cbuffs[NUM_XMIT_BUFFS];
+  volatile int    xmit_count,xmit_last;
+  volatile int slot;
+};
+
+#define elmc_attn586()  {elmc_do_attn586(dev->base_addr,ELMC_CTRL_INTE);}
+#define elmc_reset586() {elmc_do_reset586(dev->base_addr,ELMC_CTRL_INTE);}
+
+/* with interrupts disabled - this will clear the interrupt bit in the
+3c523 control register, and won't put it back.  This effectively
+disables interrupts on the card. */
+#define elmc_id_attn586()  {elmc_do_attn586(dev->base_addr,0);}
+#define elmc_id_reset586() {elmc_do_reset586(dev->base_addr,0);}
+
+/*************************************************************************/
+/*
+	Do a Channel Attention on the 3c523.  This is extremely board dependent.
+*/
+static
+void
+elmc_do_attn586( int ioaddr, int ints ) {
+    /* the 3c523 requires a minimum of 500 ns.  The delays here might be
+    a little too large, and hence they may cut the performance of the
+    card slightly.  If someone who knows a little more about Linux
+    timing would care to play with these, I'd appreciate it. */
+
+    /* this bit masking stuff is crap.  I'd rather have separate
+    registers with strobe triggers for each of these functions.  <sigh>
+    Ya take what ya got. */
+
+    outb( ELMC_CTRL_RST|0x3|ELMC_CTRL_CA|ints, ioaddr + ELMC_CTRL);
+    DELAY_16();	/* > 500 ns */
+    outb( ELMC_CTRL_RST|0x3|ints, ioaddr + ELMC_CTRL);
+}
+
+/*************************************************************************/
+/*
+	Reset the 82586 on the 3c523.  Also very board dependent.
+*/
+static
+void
+elmc_do_reset586( int ioaddr, int ints ) {
+    /* toggle the RST bit low then high */
+    outb( 0x3|ELMC_CTRL_LBK, ioaddr + ELMC_CTRL );
+    DELAY_16();	/* > 500 ns */
+    outb( ELMC_CTRL_RST|ELMC_CTRL_LBK|0x3, ioaddr + ELMC_CTRL );
+
+	elmc_do_attn586( ioaddr, ints );
+}
+
+/**********************************************
+ * close device 
+ */
+
+static
+int
+elmc_close(struct device *dev) {
+  elmc_id_reset586(); /* the hard way to stop the receiver */
+
+  free_irq(dev->irq, dev);
+
+  dev->start = 0;
+  dev->tbusy = 0;
+
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+
+  return 0;
+}
+
+/**********************************************
+ * open device 
+ */
+
+static
+int
+elmc_open(struct device *dev) {
+
+  elmc_id_attn586();	/* disable interrupts */
+
+  if(request_irq( dev->irq, &elmc_interrupt, SA_SHIRQ|SA_SAMPLE_RANDOM,
+  	"3c523", dev )
+  ) {    
+    printk( "%s: couldn't get irq %d\n", dev->name, dev->irq );
+    elmc_id_reset586();
+    return -EAGAIN;
+  }  
+
+  alloc586(dev);
+  init586(dev);  
+  startrecv586(dev);
+
+  dev->interrupt = 0;
+  dev->tbusy = 0;
+  dev->start = 1;
+
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+
+  return 0; /* most done by init */
+}
+
+/**********************************************
+ * Check to see if there's an 82586 out there. 
+ */
+
+static
+int
+check586( struct device *dev, char *where, unsigned size) {
+  struct priv *p = (struct priv *) dev->priv;
+  char *iscp_addrs[2];
+  int i = 0;
+
+  p->base = (unsigned long) where + size - 0x01000000;
+  p->memtop = where + size;
+  p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
+  memset((char *)p->scp,0, sizeof(struct scp_struct));
+  p->scp->sysbus = SYSBUSVAL;        /* 1 = 8Bit-Bus, 0 = 16 Bit */
+  
+  iscp_addrs[0] = where;
+  iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct);
+
+  for( i = 0; i < 2; i ++ ) {
+    p->iscp = (struct iscp_struct *) iscp_addrs[i];
+    memset((char *)p->iscp,0, sizeof(struct iscp_struct));
+
+    p->scp->iscp = make24(p->iscp);
+    p->iscp->busy = 1;
+
+    elmc_id_reset586();
+
+    /* reset586 does an implicit CA */
+
+    /* apparently, you sometimes have to kick the 82586 twice... */
+    elmc_id_attn586();
+
+    if(p->iscp->busy) { /* i82586 clears 'busy' after successful init */
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+/******************************************************************
+ * set iscp at the right place, called by elmc_probe and open586. 
+ */
+
+void
+alloc586( struct device *dev ) {
+  struct priv *p =  (struct priv *) dev->priv; 
+
+  elmc_id_reset586();
+  DELAY(2);
+
+  p->scp  = (struct scp_struct *)  (p->base + SCP_DEFAULT_ADDRESS);
+  p->scb  = (struct scb_struct *)  (dev->mem_start);
+  p->iscp = (struct iscp_struct *) ((char *)p->scp - sizeof(struct iscp_struct));
+
+  memset((char *) p->iscp,0,sizeof(struct iscp_struct));
+  memset((char *) p->scp ,0,sizeof(struct scp_struct));
+
+  p->scp->iscp = make24(p->iscp);
+  p->scp->sysbus = SYSBUSVAL;
+  p->iscp->scb_offset = make16(p->scb);
+
+  p->iscp->busy = 1;
+  elmc_id_reset586();
+  elmc_id_attn586();
+
+  DELAY(2); 
+
+  if(p->iscp->busy) {
+    printk("%s: Init-Problems (alloc).\n",dev->name);
+  }
+
+  memset((char *)p->scb,0,sizeof(struct scb_struct));
+}
+
+/*****************************************************************/
+static int
+elmc_getinfo( char* buf, int slot, void* d ) {
+	int len = 0;
+	struct device* dev = (struct device*) d;
+	int i;
+
+	if( dev == NULL ) return len;
+
+	len += sprintf( buf+len, "Revision: 0x%x\n",
+		inb( dev->base_addr + ELMC_REVISION ) & 0xf );
+	len += sprintf( buf+len, "IRQ: %d\n", dev->irq );
+	len += sprintf( buf+len, "IO Address: %#lx-%#lx\n", dev->base_addr,
+		dev->base_addr+ELMC_IO_EXTENT );
+	len += sprintf( buf+len, "Memory: %#lx-%#lx\n", dev->mem_start,
+		dev->mem_end-1 );
+	len += sprintf( buf+len, "Transceiver: %s\n", dev->if_port ?
+		"External" : "Internal" );
+	len += sprintf( buf+len, "Device: %s\n", dev->name );
+	len += sprintf( buf+len, "Hardware Address:" );
+	for (i = 0; i < 6; i ++ ) {
+		len += sprintf( buf+len, " %02x", dev->dev_addr[i] );
+	}
+	buf[len++] = '\n';
+	buf[len] = 0;
+
+	return len;
+} /* elmc_getinfo() */
+
+/*****************************************************************/
+int
+elmc_probe(struct device *dev) {
+    static int slot = 0;
+    int base_addr = dev ? dev->base_addr : 0;
+    int irq = dev ? dev->irq : 0;
+    u_char status = 0;
+    u_char revision = 0;
+    int i = 0;
+    unsigned int size = 0;
+
+    if( MCA_bus == 0 ) {
+    	return ENODEV;
+    }
+   
+	/* search through the slots for the 3c523. */
+	slot = mca_find_adapter( ELMC_MCA_ID, 0 );
+	while( slot != -1 ) {
+		status = mca_read_stored_pos( slot, 2 );
+
+		/*
+			If we're trying to match a specified irq or IO address,
+			we'll reject a match unless it's what we're looking for.
+		*/
+		if( base_addr || irq ) {
+			/* we're looking for a card at a particular place */
+
+			if( irq &&
+				irq != irq_table[ (status & ELMC_STATUS_IRQ_SELECT) >> 6]
+			) {
+				slot = mca_find_adapter( ELMC_MCA_ID, slot + 1 );
+				continue;
+			}
+
+			if( base_addr && base_addr !=
+				csr_table[ (status & ELMC_STATUS_CSR_SELECT) >> 1]
+			) {
+				slot = mca_find_adapter( ELMC_MCA_ID, slot + 1 );
+				continue;
+			}
+		}
+
+		/* found what we're looking for... */
+		break;
+	}
+
+	/* we didn't find any 3c523 in the slots we checked for */
+	if( slot == MCA_NOTFOUND ) {
+		return ((base_addr || irq) ? ENXIO : ENODEV);
+	}
+
+	mca_set_adapter_name( slot, "3Com 3c523 Etherlink/MC" );
+	mca_set_adapter_procfn( slot, (MCA_ProcFn) elmc_getinfo, dev );
+
+    /* if we get this far, adapter has been found - carry on */
+    printk("%s: 3c523 adapter found in slot %d\n", dev->name, slot + 1);
+
+    /* Now we extract configuration info from the card.
+	The 3c523 provides information in two of the POS registers, but 
+	the second one is only needed if we want to tell the card what IRQ
+	to use.  I suspect that whoever sets the thing up initially would
+	prefer we don't screw with those things.
+
+	Note we we read the status info when we found the card...
+	
+	See 3c523.h for more details.
+	*/
+
+    /* revision is stored in the first 4 bits of the revision register */
+    revision = inb( dev->base_addr + ELMC_REVISION ) & 0xf;
+
+    /* figure out our irq */
+    dev->irq = irq_table[ (status & ELMC_STATUS_IRQ_SELECT) >> 6];
+
+    /* according to docs, we read the interrupt and write it back to
+    the IRQ select register, since the POST might not configure the IRQ
+    properly. */
+    switch( dev->irq ) {
+    	case 3:
+    		mca_write_pos( slot, 3, 0x04 );
+    		break;
+    	case 7:
+    		mca_write_pos( slot, 3, 0x02 );
+    		break;
+    	case 9:
+    		mca_write_pos( slot, 3, 0x08 );
+    		break;
+    	case 12:
+    		mca_write_pos( slot, 3, 0x01 );
+    		break;
+    }
+    
+    /* Our IO address? */
+    dev->base_addr = csr_table[ (status & ELMC_STATUS_CSR_SELECT) >> 1];
+
+    request_region( dev->base_addr, ELMC_IO_EXTENT,"3c523");
+
+    dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL); 
+    if (dev->priv == NULL) {
+	return -ENOMEM;
+    }
+    memset((char *) dev->priv,0,sizeof(struct priv));
+
+    ((struct priv *) (dev->priv))->slot = slot;
+
+    printk("%s: 3Com 3c523 Rev 0x%x at %#lx\n", dev->name, (int) revision,
+    	dev->base_addr);
+
+    /* Determine if we're using the on-board transceiver (i.e. coax) or
+        an external one.  The information is pretty much useless, but I
+        guess it's worth brownie points. */
+    dev->if_port = (status & ELMC_STATUS_DISABLE_THIN);
+
+    /* The 3c523 has a 24K chunk of memory.  The first 16K is the
+        shared memory, while the last 8K is for the EtherStart BIOS ROM.
+        Which we don't care much about here.  We'll just tell Linux that
+        we're using 16K.  MCA won't permit adress space conflicts caused
+        by not mapping the other 8K. */
+    dev->mem_start = shm_table[ (status & ELMC_STATUS_MEMORY_SELECT) >> 3];
+
+  /* We're using MCA, so it's a given that the information about memory
+  	size is correct.  The Crynwr drivers do something like this. */
+
+  elmc_id_reset586();	/* seems like a good idea before checking it... */
+
+  size = 0x4000; /* check for 16K mem */
+  if(!check586(dev,(char *) dev->mem_start,size)) {
+	printk("%s: memprobe, Can't find memory at 0x%lx!\n", dev->name,
+		dev->mem_start );
+	release_region( dev->base_addr, ELMC_IO_EXTENT );
+	return ENODEV;
+  }
+  dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+  
+  ((struct priv *) (dev->priv))->base =  dev->mem_start + size - 0x01000000;
+  alloc586(dev);
+
+  elmc_id_reset586();	/* make sure it doesn't generate spurious ints */
+
+  /* set number of receive-buffs according to memsize */
+  ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16;
+
+  /* dump all the assorted information */
+  printk("%s: IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->name,
+  	dev->irq, dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
+
+  /* The hardware address for the 3c523 is stored in the first six
+  	bytes of the IO address. */
+  printk( "%s: hardware address ", dev->name );
+  for (i = 0; i < 6; i ++ ) {
+   dev->dev_addr[i] = inb(dev->base_addr + i);
+   printk(" %02x", dev->dev_addr[i]);
+  }
+  printk( "\n" );
+
+  dev->open            = &elmc_open;
+  dev->stop            = &elmc_close;
+  dev->get_stats       = &elmc_get_stats;
+  dev->hard_start_xmit = &elmc_send_packet;
+  dev->set_multicast_list = &set_multicast_list;
+
+  ether_setup(dev);
+
+  dev->tbusy = 0;
+  dev->interrupt = 0;
+  dev->start = 0;
+
+  /* note that we haven't actually requested the IRQ from the kernel.
+  That gets done in elmc_open().  I'm not sure that's such a good idea,
+  but it works, so I'll go with it. */
+
+  return 0;
+}
+
+/********************************************** 
+ * init the chip (elmc-interrupt should be disabled?!)
+ * needs a correct 'allocated' memory
+ */
+
+static
+int
+init586(struct device *dev) {
+  void *ptr;
+  unsigned long s;
+  int i,result=0;
+  struct priv *p = (struct priv *) dev->priv;
+  volatile struct configure_cmd_struct  *cfg_cmd;
+  volatile struct iasetup_cmd_struct *ias_cmd;
+  volatile struct tdr_cmd_struct *tdr_cmd;
+  volatile struct mcsetup_cmd_struct *mc_cmd;
+  struct dev_mc_list *dmi=dev->mc_list;
+  int num_addrs=dev->mc_count;
+
+  ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct));
+
+  cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */
+  cfg_cmd->cmd_status = 0;
+  cfg_cmd->cmd_cmd    = CMD_CONFIGURE | CMD_LAST;
+  cfg_cmd->cmd_link   = 0xffff;
+
+  cfg_cmd->byte_cnt   = 0x0a; /* number of cfg bytes */
+  cfg_cmd->fifo       = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+  cfg_cmd->sav_bf     = 0x40; /* hold or discard bad recv frames (bit 7) */
+  cfg_cmd->adr_len    = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
+  cfg_cmd->priority   = 0x00;
+  cfg_cmd->ifs        = 0x60;
+  cfg_cmd->time_low   = 0x00;
+  cfg_cmd->time_high  = 0xf2;
+  cfg_cmd->promisc    = 0;
+  if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+  {
+	cfg_cmd->promisc=1;
+	dev->flags|=IFF_PROMISC;
+  }
+  cfg_cmd->carr_coll  = 0x00;
+ 
+  p->scb->cbl_offset = make16(cfg_cmd);
+
+  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  elmc_id_attn586();
+ 
+  s = jiffies; /* warning: only active with interrupts on !! */
+  while(!(cfg_cmd->cmd_status & STAT_COMPL)) {
+    if(jiffies-s > 30) break;
+  }
+
+  if((cfg_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK)) {
+    printk("%s (elmc): configure command failed: %x\n",dev->name,cfg_cmd->cmd_status);
+    return 1; 
+  }
+
+    /*
+     * individual address setup
+     */
+  ias_cmd = (struct iasetup_cmd_struct *)ptr;
+
+  ias_cmd->cmd_status = 0;
+  ias_cmd->cmd_cmd    = CMD_IASETUP | CMD_LAST;
+  ias_cmd->cmd_link   = 0xffff;
+
+  memcpy((char *)&ias_cmd->iaddr,(char *) dev->dev_addr,ETH_ALEN);
+
+  p->scb->cbl_offset = make16(ias_cmd);
+
+  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  elmc_id_attn586();
+
+  s = jiffies;
+  while(!(ias_cmd->cmd_status & STAT_COMPL))  {
+    if(jiffies-s > 30) break;
+  }
+
+  if((ias_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) {
+    printk("%s (elmc): individual address setup command failed: %04x\n",dev->name,ias_cmd->cmd_status);
+    return 1; 
+  }
+
+   /* 
+    * TDR, wire check .. e.g. no resistor e.t.c 
+    */
+  tdr_cmd = (struct tdr_cmd_struct *)ptr;
+
+  tdr_cmd->cmd_status  = 0;
+  tdr_cmd->cmd_cmd     = CMD_TDR | CMD_LAST;
+  tdr_cmd->cmd_link    = 0xffff;
+  tdr_cmd->status      = 0;
+
+  p->scb->cbl_offset = make16(tdr_cmd);
+
+  p->scb->cmd = CUC_START; /* cmd.-unit start */
+  elmc_attn586();
+
+  s = jiffies; 
+  while(!(tdr_cmd->cmd_status & STAT_COMPL)) {
+    if(jiffies - s > 30) {
+      printk("%s: %d Problems while running the TDR.\n",dev->name,__LINE__);
+      result = 1;
+      break;
+    }
+  }
+
+  if(!result) {
+    DELAY(2); /* wait for result */
+    result = tdr_cmd->status;
+
+    p->scb->cmd = p->scb->status & STAT_MASK;
+    elmc_id_attn586(); /* ack the interrupts */
+
+    if(result & TDR_LNK_OK) {
+    	/* empty */
+    } else if(result & TDR_XCVR_PRB) {
+      printk("%s: TDR: Transceiver problem!\n",dev->name);
+    } else if(result & TDR_ET_OPN) {
+      printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+    } else if(result & TDR_ET_SRT) {
+      if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */
+        printk("%s: TDR: Detected a short circuit %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+    } else {
+      printk("%s: TDR: Unknown status %04x\n",dev->name,result);
+    }
+  }
+ 
+   /* 
+    * ack interrupts 
+    */
+  p->scb->cmd = p->scb->status & STAT_MASK;
+  elmc_id_attn586();
+
+   /*
+    * alloc nop/xmit-cmds
+    */
+#if (NUM_XMIT_BUFFS == 1)
+  for(i=0;i<2;i++) {
+    p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+    p->nop_cmds[i]->cmd_cmd    = CMD_NOP;
+    p->nop_cmds[i]->cmd_status = 0;
+    p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i]));
+    ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+  }
+  p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
+  ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+#else
+  for(i=0;i<NUM_XMIT_BUFFS;i++) {
+    p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+    p->nop_cmds[i]->cmd_cmd    = CMD_NOP;
+    p->nop_cmds[i]->cmd_status = 0;
+    p->nop_cmds[i]->cmd_link   = make16((p->nop_cmds[i]));
+    ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+    p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+    ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+  }
+#endif
+
+  ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */ 
+
+  /* 
+   * Multicast setup
+   */
+  
+  if(dev->mc_count) {
+    /* I don't understand this: do we really need memory after the init? */
+    int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+    if(len <= 0) {
+      printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name);
+    } else {
+      if(len < num_addrs) {
+        num_addrs = len;
+        printk("%s: Sorry, can only apply %d MC-Address(es).\n",
+	    dev->name, num_addrs);
+      }
+      mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+      mc_cmd->cmd_status = 0;
+      mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+      mc_cmd->cmd_link = 0xffff;
+      mc_cmd->mc_cnt = num_addrs * 6;
+      for(i=0;i<num_addrs;i++) {
+	    memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6);
+	    dmi=dmi->next;
+      }
+      p->scb->cbl_offset = make16(mc_cmd);
+      p->scb->cmd = CUC_START;
+      elmc_id_attn586();
+      s = jiffies;
+      while(!(mc_cmd->cmd_status & STAT_COMPL)) {
+        if(jiffies - s > 30)
+          break;
+	  }
+      if(!(mc_cmd->cmd_status & STAT_COMPL)) {
+        printk("%s: Can't apply multicast-address-list.\n",dev->name);
+	  }
+    }
+  }
+
+  /*
+   * alloc xmit-buffs / init xmit_cmds
+   */
+  for(i=0;i<NUM_XMIT_BUFFS;i++) {
+    p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
+    ptr = (char *) ptr + XMIT_BUFF_SIZE;
+    p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
+    ptr = (char *) ptr + sizeof(struct tbd_struct);
+    if((void *)ptr > (void *)p->iscp) {
+      printk("%s: not enough shared-mem for your configuration!\n",dev->name);
+      return 1;
+    }   
+    memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
+    memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
+    p->xmit_cmds[i]->cmd_status = STAT_COMPL;
+    p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
+    p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
+    p->xmit_buffs[i]->next = 0xffff;
+    p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i]));
+  }
+
+  p->xmit_count = 0; 
+  p->xmit_last  = 0;
+#ifndef NO_NOPCOMMANDS
+  p->nop_point  = 0;
+#endif
+
+   /*
+    * 'start transmitter' (nop-loop)
+    */
+#ifndef NO_NOPCOMMANDS
+  p->scb->cbl_offset = make16(p->nop_cmds[0]);
+  p->scb->cmd = CUC_START;
+  elmc_id_attn586();
+  WAIT_4_SCB_CMD();
+#else
+  p->xmit_cmds[0]->cmd_link = 0xffff;
+  p->xmit_cmds[0]->cmd_cmd  = CMD_XMIT | CMD_LAST | CMD_INT;
+#endif
+
+  return 0;
+}
+
+/******************************************************
+ * This is a helper routine for elmc_rnr_int() and init586(). 
+ * It sets up the Receive Frame Area (RFA).
+ */
+
+static
+void*
+alloc_rfa(struct device *dev,void *ptr) {
+  volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr;
+  volatile struct rbd_struct *rbd;
+  int i;
+  struct priv *p = (struct priv *) dev->priv;
+
+  memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs);
+  p->rfd_first = rfd;
+
+  for(i = 0; i < p->num_recv_buffs; i++) {
+    rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs);
+  }
+  rfd[p->num_recv_buffs-1].last = RFD_SUSP;   /* RU suspend */
+
+  ptr = (void *) (rfd + p->num_recv_buffs);
+
+  rbd = (struct rbd_struct *) ptr;
+  ptr = (void *) (rbd + p->num_recv_buffs);
+
+   /* clr descriptors */
+  memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs);
+
+  for(i=0;i<p->num_recv_buffs;i++) {
+    rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs));
+    rbd[i].size = RECV_BUFF_SIZE;
+    rbd[i].buffer = make24(ptr);
+    ptr = (char *) ptr + RECV_BUFF_SIZE;
+  }
+
+  p->rfd_top  = p->rfd_first;
+  p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
+
+  p->scb->rfa_offset		= make16(p->rfd_first);
+  p->rfd_first->rbd_offset	= make16(rbd);
+
+  return ptr;
+}
+
+
+/**************************************************
+ * Interrupt Handler ...
+ */
+
+static
+void
+elmc_interrupt(int irq, void *dev_id, struct pt_regs *reg_ptr) {
+  struct device *dev = (struct device *) dev_id;
+  unsigned short stat;
+  struct priv *p;
+
+  if (dev == NULL) {
+    printk ("elmc-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2));
+    return;
+  } else if( !dev->start ) {
+    /* The 3c523 has this habit of generating interrupts during the
+    	reset.  I'm not sure if the ni52 has this same problem, but it's
+    	really annoying if we haven't finished initializing it.  I was
+    	hoping all the elmc_id_* commands would disable this, but I
+    	might have missed a few. */
+
+    elmc_id_attn586(); /* ack inter. and disable any more */
+    return;
+  } else if( !(ELMC_CTRL_INT & inb( dev->base_addr+ELMC_CTRL )) ) {
+  	/* wasn't this device */
+  	return;
+  }
+
+  /* reading ELMC_CTRL also clears the INT bit. */
+
+  p = (struct priv *) dev->priv;
+
+  dev->interrupt = 1;
+
+  while((stat=p->scb->status & STAT_MASK)) {
+    p->scb->cmd = stat;
+    elmc_attn586(); /* ack inter. */
+
+   if(stat & STAT_CX) {
+      /* command with I-bit set complete */
+      elmc_xmt_int(dev);
+   }
+
+   if(stat & STAT_FR) {
+     /* received a frame */
+     elmc_rcv_int(dev);
+   }
+
+#ifndef NO_NOPCOMMANDS
+    if(stat & STAT_CNA) {
+      /* CU went 'not ready' */
+      if(dev->start) {
+        printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+	  }
+    }
+#endif
+
+    if(stat & STAT_RNR) {
+      /* RU went 'not ready' */
+
+      if(p->scb->status & RU_SUSPEND) {
+        /* special case: RU_SUSPEND */
+
+        WAIT_4_SCB_CMD();
+        p->scb->cmd = RUC_RESUME;
+        elmc_attn586();
+      } else {
+        printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+        elmc_rnr_int(dev); 
+      }
+    }
+    WAIT_4_SCB_CMD(); /* wait for ack. (elmc_xmt_int can be faster than ack!!) */
+    if(p->scb->cmd) {  /* timed out? */
+      break;
+    }
+  }
+
+  dev->interrupt = 0;
+}
+
+/*******************************************************
+ * receive-interrupt
+ */
+
+static
+void
+elmc_rcv_int(struct device *dev) {
+  int status;
+  unsigned short totlen;
+  struct sk_buff *skb;
+  struct rbd_struct *rbd;
+  struct priv *p = (struct priv *) dev->priv;
+
+  for(;(status = p->rfd_top->status) & STAT_COMPL;) {
+      rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
+
+      if(status & STAT_OK) /* frame received without error? */
+      {
+        if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */
+        {
+          totlen &= RBD_MASK; /* length of this frame */
+          rbd->status = 0;
+          skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
+          if(skb != NULL) {
+             skb->dev = dev;
+            skb_reserve(skb,2);		/* 16 byte alignment */
+            memcpy(skb_put(skb,totlen),(char *) p->base+(unsigned long) rbd->buffer, totlen);
+            skb->protocol=eth_type_trans(skb,dev);
+            netif_rx(skb);
+            p->stats.rx_packets++;
+          } else {
+            p->stats.rx_dropped++;
+		  }
+        } else {
+          printk("%s: received oversized frame.\n",dev->name);
+          p->stats.rx_dropped++;
+        }
+      } else /* frame !(ok), only with 'save-bad-frames' */ {
+        printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
+        p->stats.rx_errors++;
+      }
+      p->rfd_top->status = 0;
+      p->rfd_top->last = RFD_SUSP;
+      p->rfd_last->last = 0;        /* delete RU_SUSP  */
+      p->rfd_last = p->rfd_top;
+      p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
+  }
+}
+
+/**********************************************************
+ * handle 'Receiver went not ready'. 
+ */
+
+static
+void
+elmc_rnr_int(struct device *dev) {
+  struct priv *p = (struct priv *) dev->priv;
+
+  p->stats.rx_errors++;
+
+  WAIT_4_SCB_CMD();    /* wait for the last cmd */
+  p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
+  elmc_attn586(); 
+  WAIT_4_SCB_CMD();    /* wait for accept cmd. */
+
+  alloc_rfa(dev,(char *)p->rfd_first);
+  startrecv586(dev); /* restart RU */
+
+  printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status);
+
+}
+
+/**********************************************************
+ * handle xmit - interrupt
+ */
+
+static
+void
+elmc_xmt_int(struct device *dev) {
+  int status;
+  struct priv *p = (struct priv *) dev->priv;
+
+  status = p->xmit_cmds[p->xmit_last]->cmd_status;
+  if(!(status & STAT_COMPL)) {
+    printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
+  }
+
+  if(status & STAT_OK) {
+    p->stats.tx_packets++;
+    p->stats.collisions += (status & TCMD_MAXCOLLMASK);
+  } else {
+    p->stats.tx_errors++;
+    if(status & TCMD_LATECOLL) {
+      printk("%s: late collision detected.\n",dev->name);
+      p->stats.collisions++;
+    } else if(status & TCMD_NOCARRIER) {
+      p->stats.tx_carrier_errors++;
+      printk("%s: no carrier detected.\n",dev->name);
+    } else if(status & TCMD_LOSTCTS) {
+      printk("%s: loss of CTS detected.\n",dev->name);
+    } else if(status & TCMD_UNDERRUN) {
+      p->stats.tx_fifo_errors++;
+      printk("%s: DMA underrun detected.\n",dev->name);
+    } else if(status & TCMD_MAXCOLL) {
+      printk("%s: Max. collisions exceeded.\n",dev->name);
+      p->stats.collisions += 16;
+    } 
+  }
+
+#if (NUM_XMIT_BUFFS != 1)
+  if( (++p->xmit_last) == NUM_XMIT_BUFFS) {
+    p->xmit_last = 0;
+  }
+#endif
+
+  dev->tbusy = 0;
+  mark_bh(NET_BH);
+}
+
+/***********************************************************
+ * (re)start the receiver
+ */ 
+
+static
+void
+startrecv586(struct device *dev)
+{
+  struct priv *p = (struct priv *) dev->priv;
+
+  p->scb->rfa_offset = make16(p->rfd_first);
+  p->scb->cmd = RUC_START;
+  elmc_attn586();		/* start cmd. */
+  WAIT_4_SCB_CMD();	/* wait for accept cmd. (no timeout!!) */
+}
+
+/******************************************************
+ * send frame 
+ */
+
+static
+int
+elmc_send_packet(struct sk_buff *skb, struct device *dev)
+{
+  int len;
+#ifndef NO_NOPCOMMANDS
+  int next_nop;
+#endif
+  struct priv *p = (struct priv *) dev->priv;
+
+  if(dev->tbusy) {
+    int tickssofar = jiffies - dev->trans_start;
+    if (tickssofar < 5) {
+      return 1;
+    }
+
+    /* COMMAND-UNIT active? */
+    if(p->scb->status & CU_ACTIVE) {
+      dev->tbusy = 0;
+#ifdef DEBUG
+      printk("%s: strange ... timeout with CU active?!?\n",dev->name);
+      printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point);
+#endif
+      p->scb->cmd = CUC_ABORT;
+      elmc_attn586();
+      WAIT_4_SCB_CMD();
+      p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
+      p->scb->cmd = CUC_START;
+      elmc_attn586();
+      WAIT_4_SCB_CMD();
+      dev->trans_start = jiffies;
+      return 0;
+    } else {
+#ifdef DEBUG
+      printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
+      printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+#endif
+      elmc_close(dev);
+      elmc_open(dev);
+    }
+    dev->trans_start = jiffies;
+    return 0;
+  }
+
+  if(skb == NULL) {
+    dev_tint(dev);
+    return 0;
+  }
+
+  if (skb->len <= 0) {
+    return 0;
+  }
+  if(skb->len > XMIT_BUFF_SIZE) {
+    printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %ld bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len);
+    return 0;
+  }
+
+  if (set_bit(0, (void*)&dev->tbusy) != 0) {
+     printk("%s: Transmitter access conflict.\n", dev->name);
+  } else {
+    memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
+    len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+
+#if (NUM_XMIT_BUFFS == 1)
+#  ifdef NO_NOPCOMMANDS
+    p->xmit_buffs[0]->size = TBD_LAST | len;
+    for(i=0;i<16;i++) {
+      p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+      p->scb->cmd = CUC_START;
+      p->xmit_cmds[0]->cmd_status = 0;
+
+      elmc_attn586();
+      dev->trans_start = jiffies;
+      if(!i) {
+        dev_kfree_skb(skb,FREE_WRITE);
+	  }
+      WAIT_4_SCB_CMD();
+      if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+      {
+        break;
+      }
+      if(p->xmit_cmds[0]->cmd_status) {
+        break;
+	  }
+      if(i==15) {
+        printk("%s: Can't start transmit-command.\n",dev->name);
+	  }
+    }
+#  else
+    next_nop = (p->nop_point + 1) & 0x1;
+    p->xmit_buffs[0]->size = TBD_LAST | len;
+
+    p->xmit_cmds[0]->cmd_link   = p->nop_cmds[next_nop]->cmd_link 
+                                = make16((p->nop_cmds[next_nop]));
+    p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;
+
+    p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
+    dev->trans_start = jiffies;
+    p->nop_point = next_nop;
+    dev_kfree_skb(skb,FREE_WRITE);
+#  endif
+#else
+    p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len;
+    if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS ) {
+      next_nop = 0;
+    }
+
+    p->xmit_cmds[p->xmit_count]->cmd_status  = 0;
+    p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link 
+                                          = make16((p->nop_cmds[next_nop]));
+    p->nop_cmds[next_nop]->cmd_status = 0;
+
+    p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
+    dev->trans_start = jiffies;
+    p->xmit_count = next_nop;
+  
+    cli();
+    if(p->xmit_count != p->xmit_last) {
+      dev->tbusy = 0;
+    }
+    sti();
+    dev_kfree_skb(skb,FREE_WRITE);
+#endif
+  }
+  return 0;
+}
+
+/*******************************************
+ * Someone wanna have the statistics 
+ */
+
+static
+struct enet_statistics*
+elmc_get_stats( struct device *dev ) {
+  struct priv *p = (struct priv *) dev->priv;
+  unsigned short crc,aln,rsc,ovrn;
+
+  crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
+  p->scb->crc_errs -= crc;
+  aln = p->scb->aln_errs;
+  p->scb->aln_errs -= aln;
+  rsc = p->scb->rsc_errs;
+  p->scb->rsc_errs -= rsc;
+  ovrn = p->scb->ovrn_errs;
+  p->scb->ovrn_errs -= ovrn;
+
+  p->stats.rx_crc_errors += crc;
+  p->stats.rx_fifo_errors += ovrn;
+  p->stats.rx_frame_errors += aln;
+  p->stats.rx_dropped += rsc;
+
+  return &p->stats;
+}
+
+/********************************************************
+ * Set MC list ..  
+ */
+
+static
+void
+set_multicast_list(struct device *dev) {
+  if(!dev->start) {
+    /* without a running interface, promiscuous doesn't work */
+    return;
+  }
+
+  dev->start = 0;
+  alloc586(dev);
+  init586(dev);  
+  startrecv586(dev);
+  dev->start = 1;
+}
+
+/*************************************************************************/
+
+#ifdef MODULE
+char kernel_version[] = UTS_RELEASE;
+static struct device dev_elmc = {
+	"        " /*"3c523"*/, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, elmc_probe };
+	
+
+int irq=0;
+int io=0;
+
+int
+init_module(void) {
+	struct device *dev = &dev_elmc;
+
+	dev->base_addr=io;
+	dev->irq=irq;
+	if (register_netdev(dev) != 0) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+void
+cleanup_module(void) {
+	struct device *dev = &dev_elmc;
+
+	if (MOD_IN_USE) {
+		printk("3c523: device busy, remove delayed\n");
+	} else {
+  		/* shutdown interrupts on the card */
+		elmc_id_reset586();
+
+  		if( dev->irq != 0 ) {
+	      /* this should be done by close, but if we failed to
+	      initialize properly something may have gotten hosed. */
+	      free_irq( dev->irq, dev );
+	      dev->irq = 0;
+	    }
+
+
+
+		if( dev->base_addr != 0 ) {
+	  	  release_region( dev->base_addr, ELMC_IO_EXTENT );
+		  dev->base_addr = 0;
+		}
+		irq = 0;
+		io = 0;
+
+		unregister_netdev(dev);
+
+    		mca_set_adapter_procfn( ((struct priv *) (dev->priv))->slot,
+    			NULL, NULL );
+
+		kfree_s(dev->priv,sizeof(struct priv));
+		dev->priv=NULL;
+	}
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.14/linux/drivers/net/3c523.h linux/drivers/net/3c523.h
--- v2.1.14/linux/drivers/net/3c523.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/3c523.h	Thu Dec 12 16:51:09 1996
@@ -0,0 +1,355 @@
+#ifndef _3c523_INCLUDE_
+#define _3c523_INCLUDE_
+/*
+	This is basically a hacked version of ni52.h, for the 3c523
+	Etherlink/MC.
+*/
+
+/*
+ * Intel i82586 Ethernet definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * Copyright 1995 by Chris Beauregard (cpbeaure@undergrad.math.uwaterloo.ca)
+ *
+ * See 3c523.c for details.
+ *
+ * $Header: /home/chrisb/linux-1.2.13-3c523/drivers/net/RCS/3c523.h,v 1.6 1996/01/20 05:09:00 chrisb Exp chrisb $
+ */
+
+/*
+ * where to find the System Configuration Pointer (SCP)
+ */
+#define SCP_DEFAULT_ADDRESS 0xfffff4
+
+
+/*
+ * System Configuration Pointer Struct
+ */
+
+struct scp_struct
+{
+  unsigned short zero_dum0;	/* has to be zero */
+  unsigned char  sysbus;	/* 0=16Bit,1=8Bit */
+  unsigned char  zero_dum1;	/* has to be zero for 586 */
+  unsigned short zero_dum2;
+  unsigned short zero_dum3;
+  char          *iscp;		/* pointer to the iscp-block */
+};
+
+
+/*
+ * Intermediate System Configuration Pointer (ISCP)
+ */
+struct iscp_struct
+{
+  unsigned char  busy;          /* 586 clears after successful init */
+  unsigned char  zero_dummy;    /* hast to be zero */
+  unsigned short scb_offset;    /* pointeroffset to the scb_base */
+  char          *scb_base;      /* base-address of all 16-bit offsets */
+};
+
+/*
+ * System Control Block (SCB)
+ */
+struct scb_struct
+{
+  unsigned short status;        /* status word */
+  unsigned short cmd;           /* command word */
+  unsigned short cbl_offset;    /* pointeroffset, command block list */
+  unsigned short rfa_offset;    /* pointeroffset, receive frame area */
+  unsigned short crc_errs;      /* CRC-Error counter */
+  unsigned short aln_errs;      /* allignmenterror counter */
+  unsigned short rsc_errs;      /* Resourceerror counter */
+  unsigned short ovrn_errs;     /* OVerrunerror counter */
+};
+
+/*
+ * possible command values for the command word
+ */
+#define RUC_MASK	0x0070	/* mask for RU commands */
+#define RUC_NOP		0x0000	/* NOP-command */
+#define RUC_START	0x0010	/* start RU */
+#define RUC_RESUME	0x0020	/* resume RU after suspend */
+#define RUC_SUSPEND	0x0030	/* suspend RU */
+#define RUC_ABORT	0x0040	/* abort receiver operation immediately */
+
+#define CUC_MASK	0x0700	/* mask for CU command */
+#define CUC_NOP		0x0000	/* NOP-command */
+#define CUC_START	0x0100	/* start execution of 1. cmd on the CBL */
+#define CUC_RESUME	0x0200	/* resume after suspend */
+#define CUC_SUSPEND	0x0300	/* Suspend CU */
+#define CUC_ABORT	0x0400	/* abort command operation immediately */
+
+#define ACK_MASK	0xf000	/* mask for ACK command */
+#define ACK_CX		0x8000	/* acknowledges STAT_CX */
+#define ACK_FR		0x4000	/* ack. STAT_FR */
+#define ACK_CNA		0x2000	/* ack. STAT_CNA */
+#define ACK_RNR		0x1000	/* ack. STAT_RNR */
+
+/*
+ * possible status values for the status word
+ */
+#define STAT_MASK	0xf000	/* mask for cause of interrupt */
+#define STAT_CX		0x8000	/* CU finished cmd with its I bit set */
+#define STAT_FR		0x4000	/* RU finished receiving a frame */
+#define STAT_CNA	0x2000	/* CU left active state */
+#define STAT_RNR	0x1000	/* RU left ready state */
+
+#define CU_STATUS	0x700	/* CU status, 0=idle */
+#define CU_SUSPEND	0x100	/* CU is suspended */
+#define CU_ACTIVE	0x200	/* CU is active */
+
+#define RU_STATUS	0x70	/* RU status, 0=idle */
+#define RU_SUSPEND	0x10	/* RU suspended */
+#define RU_NOSPACE	0x20	/* RU no resources */
+#define RU_READY	0x40	/* RU is ready */
+
+/*
+ * Receive Frame Descriptor (RFD)
+ */
+struct rfd_struct
+{
+  unsigned short status;	/* status word */
+  unsigned short last;		/* Bit15,Last Frame on List / Bit14,suspend */
+  unsigned short next;		/* linkoffset to next RFD */
+  unsigned short rbd_offset;	/* pointeroffset to RBD-buffer */
+  unsigned char  dest[6];	/* ethernet-address, destination */
+  unsigned char  source[6];	/* ethernet-address, source */
+  unsigned short length;	/* 802.3 frame-length */
+  unsigned short zero_dummy;	/* dummy */
+};
+
+#define RFD_LAST     0x8000	/* last: last rfd in the list */
+#define RFD_SUSP     0x4000	/* last: suspend RU after  */
+#define RFD_ERRMASK  0x0fe1     /* status: errormask */
+#define RFD_MATCHADD 0x0002     /* status: Destinationaddress !matches IA */
+#define RFD_RNR      0x0200	/* status: receiver out of resources */
+
+/*
+ * Receive Buffer Descriptor (RBD)
+ */
+struct rbd_struct 
+{
+  unsigned short status;	/* status word,number of used bytes in buff */
+  unsigned short next;		/* pointeroffset to next RBD */
+  char          *buffer;	/* receive buffer address pointer */
+  unsigned short size;		/* size of this buffer */
+  unsigned short zero_dummy;    /* dummy */
+};
+
+#define RBD_LAST	0x8000	/* last buffer */
+#define RBD_USED	0x4000	/* this buffer has data */
+#define RBD_MASK	0x3fff	/* size-mask for length */
+
+/*
+ * Statusvalues for Commands/RFD
+ */
+#define STAT_COMPL   0x8000	/* status: frame/command is complete */
+#define STAT_BUSY    0x4000	/* status: frame/command is busy */
+#define STAT_OK      0x2000	/* status: frame/command is ok */
+
+/*
+ * Action-Commands
+ */
+#define CMD_NOP		0x0000	/* NOP */
+#define CMD_IASETUP	0x0001	/* initial address setup command */
+#define CMD_CONFIGURE	0x0002	/* configure command */
+#define CMD_MCSETUP	0x0003	/* MC setup command */
+#define CMD_XMIT	0x0004	/* transmit command */
+#define CMD_TDR		0x0005	/* time domain reflectometer (TDR) command */
+#define CMD_DUMP	0x0006	/* dump command */
+#define CMD_DIAGNOSE	0x0007	/* diagnose command */
+
+/*
+ * Action command bits
+ */
+#define CMD_LAST	0x8000	/* indicates last command in the CBL */
+#define CMD_SUSPEND	0x4000	/* suspend CU after this CB */
+#define CMD_INT		0x2000	/* generate interrupt after execution */
+
+/*
+ * NOP - command
+ */
+struct nop_cmd_struct
+{
+  unsigned short cmd_status;	/* status of this command */
+  unsigned short cmd_cmd;       /* the command itself (+bits) */
+  unsigned short cmd_link;      /* offsetpointer to next command */
+};
+
+/*
+ * IA Setup command
+ */
+struct iasetup_cmd_struct 
+{
+  unsigned short cmd_status;
+  unsigned short cmd_cmd;
+  unsigned short cmd_link;
+  unsigned char  iaddr[6];
+};
+
+/*
+ * Configure command 
+ */
+struct configure_cmd_struct
+{
+  unsigned short cmd_status;
+  unsigned short cmd_cmd;
+  unsigned short cmd_link;
+  unsigned char  byte_cnt;   /* size of the config-cmd */
+  unsigned char  fifo;       /* fifo/recv monitor */
+  unsigned char  sav_bf;     /* save bad frames (bit7=1)*/
+  unsigned char  adr_len;    /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/
+  unsigned char  priority;   /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */
+  unsigned char  ifs;        /* inter frame spacing */
+  unsigned char  time_low;   /* slot time low */
+  unsigned char  time_high;  /* slot time high(0-2) and max. retries(4-7) */
+  unsigned char  promisc;    /* promisc-mode(0) , et al (1-7) */
+  unsigned char  carr_coll;  /* carrier(0-3)/collision(4-7) stuff */
+  unsigned char  fram_len;   /* minimal frame len */
+  unsigned char  dummy;	     /* dummy */
+};
+
+/*
+ * Multicast Setup command 
+ */
+struct mcsetup_cmd_struct 
+{
+  unsigned short cmd_status;
+  unsigned short cmd_cmd;
+  unsigned short cmd_link;
+  unsigned short mc_cnt;		/* number of bytes in the MC-List */
+  unsigned char  mc_list[0][6];  	/* pointer to 6 bytes entries */
+};
+
+/*
+ * transmit command 
+ */
+struct transmit_cmd_struct 
+{
+  unsigned short cmd_status;
+  unsigned short cmd_cmd;
+  unsigned short cmd_link;
+  unsigned short tbd_offset;	/* pointeroffset to TBD */
+  unsigned char  dest[6];       /* destination address of the frame */
+  unsigned short length;	/* user defined: 802.3 length / Ether type */
+};
+
+#define TCMD_ERRMASK     0x0fa0
+#define TCMD_MAXCOLLMASK 0x000f
+#define TCMD_MAXCOLL     0x0020
+#define TCMD_HEARTBEAT   0x0040
+#define TCMD_DEFERRED    0x0080
+#define TCMD_UNDERRUN    0x0100
+#define TCMD_LOSTCTS     0x0200
+#define TCMD_NOCARRIER   0x0400
+#define TCMD_LATECOLL    0x0800
+
+struct tdr_cmd_struct
+{
+  unsigned short cmd_status;
+  unsigned short cmd_cmd;
+  unsigned short cmd_link;
+  unsigned short status;
+};
+
+#define TDR_LNK_OK	0x8000	/* No link problem identified */
+#define TDR_XCVR_PRB	0x4000	/* indicates a transceiver problem */
+#define TDR_ET_OPN	0x2000	/* open, no correct termination */
+#define TDR_ET_SRT	0x1000	/* TDR detected a short circuit */
+#define TDR_TIMEMASK	0x07ff	/* mask for the time field */
+
+/*
+ * Transmit Buffer Descriptor (TBD)
+ */
+struct tbd_struct
+{
+  unsigned short size;		/* size + EOF-Flag(15) */
+  unsigned short next;          /* pointeroffset to next TBD */
+  char          *buffer;        /* pointer to buffer */
+};
+
+#define TBD_LAST 0x8000         /* EOF-Flag, indicates last buffer in list */
+
+/*************************************************************************/
+/*
+Verbatim from the Crynwyr stuff:
+
+    The 3c523 responds with adapter code 0x6042 at slot
+registers xxx0 and xxx1.  The setup register is at xxx2 and
+contains the following bits:
+
+0: card enable
+2,1: csr address select
+    00 = 0300
+    01 = 1300
+    10 = 2300
+    11 = 3300
+4,3: shared memory address select
+    00 = 0c0000
+    01 = 0c8000
+    10 = 0d0000
+    11 = 0d8000
+5: set to disable on-board thinnet
+7,6: (read-only) shows selected irq
+    00 = 12
+    01 = 7
+    10 = 3
+    11 = 9
+
+The interrupt-select register is at xxx3 and uses one bit per irq.
+
+0: int 12
+1: int 7
+2: int 3
+3: int 9
+
+    Again, the documentation stresses that the setup register
+should never be written.  The interrupt-select register may be
+written with the value corresponding to bits 7.6 in
+the setup register to insure corret setup.
+*/
+
+/* Offsets from the base I/O address. */
+#define	ELMC_SA		0	/* first 6 bytes are IEEE network address */
+#define ELMC_CTRL	6	/* control & status register */
+#define ELMC_REVISION	7	/* revision register, first 4 bits only */
+#define ELMC_IO_EXTENT  8
+
+/* these are the bit selects for the port register 2 */
+#define ELMC_STATUS_ENABLED	0x01
+#define ELMC_STATUS_CSR_SELECT	0x06
+#define ELMC_STATUS_MEMORY_SELECT	0x18
+#define ELMC_STATUS_DISABLE_THIN	0x20
+#define ELMC_STATUS_IRQ_SELECT	0xc0
+
+/* this is the card id used in the detection code.  You might recognize
+it from @6042.adf */
+#define ELMC_MCA_ID 0x6042
+
+/*
+   The following define the bits for the control & status register
+
+   The bank select registers can be used if more than 16K of memory is
+   on the card.  For some stupid reason, bank 3 is the one for the
+   bottom 16K, and the card defaults to bank 0.  So we have to set the
+   bank to 3 before the card will even think of operating.  To get bank
+   3, set BS0 and BS1 to high (of course...)
+*/
+#define ELMC_CTRL_BS0	0x01	/* RW bank select */
+#define ELMC_CTRL_BS1	0x02	/* RW bank select */
+#define ELMC_CTRL_INTE	0x04	/* RW interrupt enable, assert high */
+#define ELMC_CTRL_INT	0x08	/* R interrupt active, assert high */
+/*#define ELMC_CTRL_*	0x10*/	/* reserved */
+#define ELMC_CTRL_LBK	0x20	/* RW loopback enable, assert high */
+#define ELMC_CTRL_CA	0x40	/* RW channel attention, assert high */
+#define ELMC_CTRL_RST	0x80	/* RW 82586 reset, assert low */
+
+/* some handy compound bits */
+
+/* normal operation should have bank 3 and RST high, ints enabled */
+#define ELMC_NORMAL (ELMC_CTRL_INTE|ELMC_CTRL_RST|0x3)
+
+#endif /* _3c523_INCLUDE_ */
diff -u --recursive --new-file v2.1.14/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v2.1.14/linux/drivers/net/8390.c	Tue Nov 19 15:53:54 1996
+++ linux/drivers/net/8390.c	Thu Dec 12 16:51:09 1996
@@ -30,7 +30,8 @@
   Paul Gortmaker	: exchange static int ei_pingpong for a #define,
 			  also add better Tx error handling.
   Paul Gortmaker	: rewrite Rx overrun handling as per NS specs.
-  Alexey Kuznetsov	: use the software multicast filter.
+  Alexey Kuznetsov	: use the 8390's six bit hash multicast filter.
+  Paul Gortmaker	: tweak ANK's above multicast changes a bit.
 
 
   Sources:
@@ -679,73 +680,90 @@
 }
 
 /*
- *	Set or clear the multicast filter for this adaptor.
- *	(Don't assume 8bit char..)
+ * Update the given Autodin II CRC value with another data byte.
  */
- 
-extern inline __u32 upd_8390_crc(__u8 b, __u32 x)
+static inline u32 update_crc(u8 byte, u32 current_crc)
 {
-	int i;
-	__u8 ah=0;
-	for(i=0;i<8;i++)
-	{
-		__u8 carry = (x>>31);
-		x<<=1;
-		ah = ((ah<<1)|carry)^b;
-		
-		if(ah&1)
-			x^=0x04C11DB7;
-		ah>>=1;
-		b>>=1;
+	int bit;
+	u8 ah = 0;
+
+	for (bit=0; bit<8; bit++) {
+		u8 carry = (current_crc>>31);
+		current_crc <<= 1;
+		ah = ((ah<<1) | carry) ^ byte;
+		if (ah&1)
+			current_crc ^= 0x04C11DB7;	/* CRC polynomial */
+		ah >>= 1;
+		byte >>= 1;
 	}
-	return x;
+	return current_crc;
 }
 
-extern __inline void make_8390_mc_bits(__u8 *bits, struct device *dev)
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+static inline void make_mc_bits(u8 *bits, struct device *dev)
 {
 	struct dev_mc_list *dmi;
-	memset(bits,0,8);
-	
-	for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
-	{
+
+	for (dmi=dev->mc_list; dmi; dmi=dmi->next) {
 		int i;
-		__u32 x;
-		if(dmi->dmi_addrlen!=6)
-			continue;	/* !! */
-		x=0xFFFFFFFFUL;
-		for(i=0;i<6;i++)
-			x = upd_8390_crc(dmi->dmi_addr[i],x);
-		bits[x>>29] |= (1<<((x>>26)&7));
+		u32 crc;
+		if (dmi->dmi_addrlen != ETH_ALEN) {
+			printk(KERN_INFO "%s: invalid multicast address length given.\n", dev->name);
+			continue;
+		}
+		crc = 0xffffffff;	/* initial CRC value */
+		for (i=0; i<ETH_ALEN; i++)
+			crc = update_crc(dmi->dmi_addr[i], crc);
+		/* 
+		 * The 8390 uses the 6 most significant bits of the
+		 * CRC to index the multicast table.
+		 */
+		bits[crc>>29] |= (1<<((crc>>26)&7));
 	}
 }
 
+/*
+ *	Set or clear the multicast filter for this adaptor.
+ */
+ 
 static void set_multicast_list(struct device *dev)
 {
 	short ioaddr = dev->base_addr;
-    
+	int i;
+	unsigned long flags;
+	struct ei_device *ei = (struct ei_device*)dev->priv;
+
+	if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+		memset(ei->mcfilter, 0, 8);
+		if (dev->mc_list)
+			make_mc_bits(ei->mcfilter, dev);
+	} else
+		memset(ei->mcfilter, 0xFF, 8);	/* mcast set to accept-all */
+
+	/* 
+	 * DP8390 manuals don't specify any magic sequence for altering
+	 * the multicast regs on an already running card. To be safe, we
+	 * ensure multicast mode is off prior to loading up the new hash
+	 * table. If this proves to be not enough, we can always resort
+	 * to stopping the NIC, loading the table and then restarting.
+	 */
+	if (dev->start)
+		outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
+	save_flags(flags);
+	cli();
+	outb_p(E8390_NODMA + E8390_PAGE1, ioaddr + E8390_CMD);
+	for(i = 0; i < 8; i++)
+		outb_p(ei->mcfilter[i], ioaddr + EN1_MULT + i);
+	outb_p(E8390_NODMA + E8390_PAGE0, ioaddr + E8390_CMD);
+	restore_flags(flags);
+
 	if(dev->flags&IFF_PROMISC)
-	{
 		outb_p(E8390_RXCONFIG | 0x18, ioaddr + EN0_RXCR);
-	}
-	else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
-	{
-		unsigned long flags;
-		__u8 mc_bits[8];
-		int i;
-		
-		if(dev->flags&IFF_ALLMULTI)
-			memset(mc_bits,0xFF,8);
-		else
-			make_8390_mc_bits(mc_bits,dev);
-		save_flags(flags);
-		cli();
-		outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, ioaddr);
-		for(i = 0; i < 8; i++)
-				outb_p(mc_bits[i], ioaddr + EN1_MULT + i);
-		outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, ioaddr);
+	else if(dev->flags&IFF_ALLMULTI || dev->mc_list)
 		outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
-		restore_flags(flags);
-	} 
 	else
 		outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
 }
@@ -806,20 +824,15 @@
     outb_p(0xFF, e8390_base + EN0_ISR);
     outb_p(0x00,  e8390_base + EN0_IMR);
     
-    /* Copy the station address into the DS8390 registers,
-       and set the multicast hash bitmap to receive all multicasts. */
+    /* Copy the station address into the DS8390 registers. */
     save_flags(flags);
     cli();
     outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
     for(i = 0; i < 6; i++) {
 		outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
     }
-    /* Initialize the multicast list to accept-all.  If we enable multicast
-       the higher levels can do the filtering. */
-    for(i = 0; i < 8; i++)
-		outb_p(0xff, e8390_base + EN1_MULT + i);
     
-    outb_p(ei_local->rx_start_page,	 e8390_base + EN1_CURPAG);
+    outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
     outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
     restore_flags(flags);
     dev->tbusy = 0;
@@ -833,8 +846,7 @@
 		outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
 		/* 3c503 TechMan says rxconfig only after the NIC is started. */
 		outb_p(E8390_RXCONFIG,	e8390_base + EN0_RXCR); /* rx on,  */
-		dev->set_multicast_list(dev);		/* Get the multicast status right if this
-							   was a reset. */
+		set_multicast_list(dev);	/* (re)load the mcast table */
     }
     return;
 }
diff -u --recursive --new-file v2.1.14/linux/drivers/net/8390.h linux/drivers/net/8390.h
--- v2.1.14/linux/drivers/net/8390.h	Sat Sep 14 11:25:38 1996
+++ linux/drivers/net/8390.h	Thu Dec 12 16:51:09 1996
@@ -59,6 +59,7 @@
   void (*get_8390_hdr)(struct device *, struct e8390_pkt_hdr *, int);
   void (*block_output)(struct device *, int, const unsigned char *, int);
   void (*block_input)(struct device *, int, struct sk_buff *, int);
+  unsigned char mcfilter[8];
   unsigned open:1;
   unsigned word16:1;  /* We have the 16-bit (vs 8-bit) version of the card. */
   unsigned txing:1;		/* Transmit Active */
diff -u --recursive --new-file v2.1.14/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.14/linux/drivers/net/Config.in	Tue Nov 19 15:53:54 1996
+++ linux/drivers/net/Config.in	Thu Dec 12 16:51:09 1996
@@ -22,6 +22,9 @@
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       tristate '3c505 support' CONFIG_ELPLUS
       tristate '3c507 support' CONFIG_EL16
+      if [ "$CONFIG_MCA" = "y" ]; then
+        tristate '3c523 support' CONFIG_ELMC
+      fi
     fi
     tristate '3c509/3c579 support' CONFIG_EL3
     tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
@@ -33,7 +36,11 @@
   bool 'Western Digital/SMC cards' CONFIG_NET_VENDOR_SMC
   if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
     tristate 'WD80*3 support' CONFIG_WD80x3
-    tristate 'SMC Ultra support' CONFIG_ULTRA
+    if [ "$CONFIG_MCA" = "y" ]; then
+		tristate 'SMC Ultra MCA support' CONFIG_ULTRA
+    else
+		tristate 'SMC Ultra support' CONFIG_ULTRA
+    fi
     tristate 'SMC 9194 support' CONFIG_SMC9194
   fi
   bool 'Other ISA cards' CONFIG_NET_ISA
@@ -73,6 +80,7 @@
     tristate 'DECchip Tulip (dc21x4x) PCI support' CONFIG_DEC_ELCP
     tristate 'Digi Intl. RightSwitch SE-X support' CONFIG_DGRS
     if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+      tristate 'Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210
       bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
     fi
   fi
@@ -89,13 +97,11 @@
   bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX
 fi
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  tristate 'Frame relay DLCI support (EXPERIMENTAL)' CONFIG_DLCI
-  if [ "$CONFIG_DLCI" = "y" -o "$CONFIG_DLCI" = "m" ]; then
-    int '  Max open DLCI' CONFIG_DLCI_COUNT 24
-    int '  Max DLCI per device' CONFIG_DLCI_MAX 8
-    dep_tristate '  SDLA (Sangoma S502/S508) support' CONFIG_SDLA $CONFIG_DLCI
-  fi
+tristate 'Frame relay DLCI support' CONFIG_DLCI
+if [ "$CONFIG_DLCI" = "y" -o "$CONFIG_DLCI" = "m" ]; then
+  int '  Max open DLCI' CONFIG_DLCI_COUNT 24
+  int '  Max DLCI per device' CONFIG_DLCI_MAX 8
+  dep_tristate '  SDLA (Sangoma S502/S508) support' CONFIG_SDLA $CONFIG_DLCI
 fi
 
 tristate 'PLIP (parallel port) support' CONFIG_PLIP
@@ -120,7 +126,7 @@
   fi
   tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
   tristate 'WaveLAN support' CONFIG_WAVELAN
-  tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC
+  tristate 'WIC Radio IP bridge' CONFIG_WIC
 fi
 
 tristate 'SLIP (serial line) support' CONFIG_SLIP
@@ -133,5 +139,9 @@
 bool 'Token Ring driver support' CONFIG_TR
 if [ "$CONFIG_TR" = "y" ]; then
   tristate 'IBM Tropic chipset based adaptor support' CONFIG_IBMTR
+fi
+
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Traffic Shaper (EXPERIMENTAL)' CONFIG_SHAPER
 fi
 
diff -u --recursive --new-file v2.1.14/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.14/linux/drivers/net/Makefile	Tue Nov 19 15:53:55 1996
+++ linux/drivers/net/Makefile	Thu Dec 12 16:51:09 1996
@@ -49,15 +49,23 @@
   endif
 endif
 
+ifeq ($(CONFIG_SHAPER),y)
+L_OBJS += shaper.o
+else
+  ifeq ($(CONFIG_SHAPER),m)
+  M_OBJS += shaper.o
+  endif
+endif
+
 ifeq ($(CONFIG_SK_G16),y)
 L_OBJS += sk_g16.o
 endif
 
 ifeq ($(CONFIG_NET_IPIP),y)
-L_OBJS += new_tunnel.o
+L_OBJS += tunnel.o
 else
   ifeq ($(CONFIG_NET_IPIP),m)
-  M_OBJS += new_tunnel.o
+  M_OBJS += tunnel.o
   endif
 endif
 
@@ -139,6 +147,9 @@
 ifeq ($(CONFIG_ULTRA),y)
 L_OBJS += smc-ultra.o
 CONFIG_8390_BUILTIN = y
+  ifeq ($(CONFIG_MCA),y)
+  L_OBJS += smc-mca.o
+  endif
 else
   ifeq ($(CONFIG_ULTRA),m)
   CONFIG_8390_MODULE = y
@@ -156,6 +167,17 @@
   endif
 endif
 
+ifeq ($(CONFIG_ES3210),y)
+L_OBJS += es3210.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_ES3210),m)
+  CONFIG_8390_MODULE = y
+  M_OBJS += es3210.o
+  endif
+endif
+
+
 ifeq ($(CONFIG_PLIP),y)
 L_OBJS += plip.o
 else
@@ -289,6 +311,14 @@
 else
   ifeq ($(CONFIG_EL16),m)
   M_OBJS += 3c507.o
+  endif
+endif
+
+ifeq ($(CONFIG_ELMC),y)
+L_OBJS += 3c523.o
+else
+  ifeq ($(CONFIG_ELMC),m)
+  M_OBJS += 3c523.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.14/linux/drivers/net/README.multicast linux/drivers/net/README.multicast
--- v2.1.14/linux/drivers/net/README.multicast	Tue Nov 19 15:53:55 1996
+++ linux/drivers/net/README.multicast	Tue Dec  3 09:24:15 1996
@@ -46,7 +46,7 @@
 smc-ultra	YES		YES		YES		Hardware
 sunlance	YES		YES		YES		Software(#)
 tulip		YES		YES		YES		Hardware
-wavelan		--------Buggy--------		YES		N/A
+wavelan		YES		PROMISC		YES		Hardware
 wd		YES		YES		YES		Hardware
 znet		YES		YES		YES		Software
 
diff -u --recursive --new-file v2.1.14/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.14/linux/drivers/net/Space.c	Tue Nov 12 15:56:06 1996
+++ linux/drivers/net/Space.c	Thu Dec 12 16:51:09 1996
@@ -41,6 +41,7 @@
 extern int tulip_probe(struct device *dev);
 extern int hp100_probe(struct device *dev);
 extern int ultra_probe(struct device *dev);
+extern int ultramca_probe(struct device *dev);
 extern int wd_probe(struct device *dev);
 extern int el2_probe(struct device *dev);
 extern int ne_probe(struct device *dev);
@@ -63,8 +64,10 @@
 extern int wavelan_probe(struct device *);
 #endif	/* defined(CONFIG_WAVELAN) */
 extern int el16_probe(struct device *);
+extern int elmc_probe(struct device *);
 extern int elplus_probe(struct device *);
 extern int ac3200_probe(struct device *);
+extern int es_probe(struct device *);
 extern int e2100_probe(struct device *);
 extern int ni52_probe(struct device *);
 extern int ni65_probe(struct device *);
@@ -110,6 +113,9 @@
 #endif	
 #if defined(CONFIG_ULTRA)
 	&& ultra_probe(dev)
+#if defined(CONFIG_MCA)
+        && ultramca_probe(dev)
+#endif
 #endif
 #if defined(CONFIG_SMC9194)
 	&& smc_init(dev)
@@ -129,6 +135,9 @@
 #ifdef CONFIG_AC3200		/* Ansel Communications EISA 3200. */
 	&& ac3200_probe(dev)
 #endif
+#ifdef CONFIG_ES3210
+	&& es_probe(dev)
+#endif
 #ifdef CONFIG_E2100		/* Cabletron E21xx series. */
 	&& e2100_probe(dev)
 #endif
@@ -180,6 +189,9 @@
 #ifdef CONFIG_EL16		/* 3c507 */
 	&& el16_probe(dev)
 #endif
+#ifdef CONFIG_ELMC		/* 3c523 */
+	&& elmc_probe(dev)
+#endif
 #ifdef CONFIG_ELPLUS		/* 3c505 */
 	&& elplus_probe(dev)
 #endif
@@ -402,7 +414,6 @@
 
 #endif 
 #ifdef CONFIG_NET_IPIP
-#ifdef CONFIG_IP_FORWARD
 	extern int tunnel_init(struct device *);
 	
 	static struct device tunnel_dev1 = 
@@ -435,7 +446,6 @@
 #   undef	NEXT_DEV
 #   define	NEXT_DEV	(&tunnel_dev0)
 
-#endif 
 #endif
 
 #ifdef CONFIG_AP1000
diff -u --recursive --new-file v2.1.14/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v2.1.14/linux/drivers/net/arcnet.c	Thu Dec 12 17:02:42 1996
+++ linux/drivers/net/arcnet.c	Thu Dec 12 16:51:09 1996
@@ -613,8 +613,7 @@
 
 int arcnetA_header(struct sk_buff *skb,struct device *dev,
 		unsigned short type,void *daddr,void *saddr,unsigned len);
-int arcnetA_rebuild_header(void *eth,struct device *dev,unsigned long raddr,
-		struct sk_buff *skb);
+int arcnetA_rebuild_header(struct sk_buff *skb);
 unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev);
 
 #ifdef CONFIG_ARCNET_ETH
@@ -635,8 +634,7 @@
 	int length,u_char saddr, u_char daddr);
 int arcnetS_header(struct sk_buff *skb,struct device *dev,
 		unsigned short type,void *daddr,void *saddr,unsigned len);
-int arcnetS_rebuild_header(void *eth,struct device *dev,unsigned long raddr,
-		struct sk_buff *skb);
+int arcnetS_rebuild_header(struct sk_buff *skb);
 unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev);
 #endif
 
@@ -2481,11 +2479,6 @@
                 		return;
                 	}
 	                		
-	                /* I don't know what this is for, but it DOES avoid
-	                 * warnings...
-	                 */
-	                skb->free=1;
-	                
                 	soft=(struct ClientData *)skb->data;
                 	
                 	skb->len=sizeof(struct ClientData);
@@ -2692,15 +2685,17 @@
  * (or in future other address resolution) has completed on this
  * sk_buff. We now let ARP fill in the other fields.
  */
-int arcnetA_rebuild_header(void *buff,struct device *dev,unsigned long dst,
-		struct sk_buff *skb)
+int arcnetA_rebuild_header(struct sk_buff *skb)
 {
-	struct ClientData *head = (struct ClientData *)buff;
+	struct ClientData *head = (struct ClientData *)skb->data;
+	struct device *dev=skb->dev;
 	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
 	int status;
 
 	/*
 	 * Only ARP and IP are currently supported
+	 *
+	 * FIXME: Anyone want to spec IPv6 over ARCnet ?
 	 */
 	 
 	if(head->protocol_id != ARC_P_IP) 
@@ -2720,7 +2715,7 @@
 #ifdef CONFIG_INET	 
 	BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
 			head->saddr,head->daddr,head->protocol_id);
-	status=arp_find(&(head->daddr), dst, dev, dev->pa_addr, skb)? 1 : 0;
+	status=arp_find(&(head->daddr),skb)? 1 : 0;
 	BUGMSG(D_DURING," rebuilt: from %d to %d; protocol %Xh\n",
 			head->saddr,head->daddr,head->protocol_id);
 	return status;
@@ -3163,10 +3158,10 @@
  * (or in future other address resolution) has completed on this
  * sk_buff. We now let ARP fill in the other fields.
  */
-int arcnetS_rebuild_header(void *buff,struct device *dev,unsigned long dst,
-		struct sk_buff *skb)
+int arcnetS_rebuild_header(struct sk_buff *skb)
 {
-	struct S_ClientData *head = (struct S_ClientData *)buff;
+	struct device *dev=skb->dev;
+	struct S_ClientData *head = (struct S_ClientData *)skb->data;
 	struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
 
 	/*
@@ -3188,7 +3183,7 @@
 	 * Try to get ARP to resolve the header.
 	 */
 #ifdef CONFIG_INET	 
-	return arp_find(&(head->daddr), dst, dev, dev->pa_addr, skb)? 1 : 0;
+	return arp_find(&(head->daddr),skb)? 1 : 0;
 #else
 	return 0;	
 #endif	
diff -u --recursive --new-file v2.1.14/linux/drivers/net/bpqether.c linux/drivers/net/bpqether.c
--- v2.1.14/linux/drivers/net/bpqether.c	Tue Nov 12 15:56:06 1996
+++ linux/drivers/net/bpqether.c	Thu Dec 12 16:51:09 1996
@@ -294,17 +294,14 @@
 			return -ENOMEM;
 		}
 
-		newskb->free = 1;
 		newskb->arp  = 1;
-		newskb->sk   = skb->sk;
+		if (skb->sk)
+			skb_set_owner_w(newskb, skb->sk);
 
 		skb_reserve(newskb, AX25_BPQ_HEADER_LEN);
 		memcpy(skb_put(newskb, size), skb->data, size);
 		dev_kfree_skb(skb, FREE_WRITE);
 		skb = newskb;
-
-		if (skb->sk != NULL)
-			atomic_add(skb->truesize, &skb->sk->wmem_alloc);
 	}
 	
 	skb->protocol = htons(ETH_P_AX25);
diff -u --recursive --new-file v2.1.14/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.14/linux/drivers/net/de4x5.c	Tue Nov 12 15:56:06 1996
+++ linux/drivers/net/de4x5.c	Wed Dec 11 17:07:41 1996
@@ -213,11 +213,13 @@
                            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>.
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:v0.442 96/11/7 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:v0.45 96/12/8 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -236,6 +238,7 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <asm/uaccess.h>
+#include <asm/byteorder.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -851,7 +854,7 @@
 	** 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 +874,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 +891,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);
@@ -1063,11 +1066,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 +1085,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! */
     
@@ -1253,8 +1256,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) {
@@ -1281,7 +1285,8 @@
 		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", 
@@ -1302,10 +1307,10 @@
 	    
 	    /* 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 +1335,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 */
@@ -1431,8 +1436,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);
     }
 
@@ -1529,12 +1534,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;
@@ -2670,11 +2675,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 +2704,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 +2736,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;
@@ -3702,17 +3713,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); 
@@ -4042,22 +4053,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;
diff -u --recursive --new-file v2.1.14/linux/drivers/net/dummy.c linux/drivers/net/dummy.c
--- v2.1.14/linux/drivers/net/dummy.c	Mon May 20 08:08:36 1996
+++ linux/drivers/net/dummy.c	Thu Dec 12 16:51:09 1996
@@ -93,6 +93,7 @@
 
 	/* Fill in the fields of the device structure with ethernet-generic values. */
 	ether_setup(dev);
+	dev->tx_queue_len = 0;
 	dev->flags |= IFF_NOARP;
 
 	return 0;
diff -u --recursive --new-file v2.1.14/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v2.1.14/linux/drivers/net/eql.c	Fri Nov  1 17:13:18 1996
+++ linux/drivers/net/eql.c	Thu Dec 12 16:51:09 1996
@@ -156,11 +156,6 @@
 static int eql_slave_xmit(struct sk_buff *skb, struct device *dev); /*  */
 
 static struct enet_statistics *eql_get_stats(struct device *dev); /*  */
-static int eql_header(struct sk_buff *skb, struct device *dev, 
-		      unsigned short type, void *daddr, void *saddr, 
-		      unsigned len); /*  */
-static int eql_rebuild_header(void *buff, struct device *dev, 
-			      unsigned long raddr, struct sk_buff *skb); /*  */
 
 /* ioctl() handlers
    ---------------- */
@@ -261,9 +256,6 @@
 	for (i = 0; i < DEV_NUMBUFFS; i++)
 		skb_queue_head_init(&dev->buffs[i]);
 
-	dev->hard_header	= eql_header; 
-	dev->rebuild_header	= eql_rebuild_header;
-
 	/*
 	 *	Now we undo some of the things that eth_setup does
 	 * 	that we don't like 
@@ -389,7 +381,9 @@
 				dev->name, eql_number_slaves (eql->queue), skb->len,
 				slave_dev->name);
 #endif
-		dev_queue_xmit (skb, slave_dev, 1);
+		skb->dev = slave_dev;
+		skb->priority = 1;
+		dev_queue_xmit (skb);
 		eql->stats->tx_packets++;
 		slave->bytes_queued += skb->len; 
 	}
@@ -411,21 +405,6 @@
 {
 	equalizer_t *eql = (equalizer_t *) dev->priv;
 	return eql->stats;
-}
-
-
-static int  eql_header(struct sk_buff *skb, struct device *dev, 
-	   unsigned short type, void *daddr, void *saddr, 
-	   unsigned len)
-{
-	return 0;
-}
-
-
-static int eql_rebuild_header(void *buff, struct device *dev, 
-		   unsigned long raddr, struct sk_buff *skb)
-{
-	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.14/linux/drivers/net/es3210.c linux/drivers/net/es3210.c
--- v2.1.14/linux/drivers/net/es3210.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/es3210.c	Thu Dec 12 16:51:09 1996
@@ -0,0 +1,438 @@
+/*
+	es3210.c
+
+	Linux driver for Racal-Interlan ES3210 EISA Network Adapter
+
+	Copyright (C) 1996, Paul Gortmaker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	Information and Code Sources:
+
+	1) The existing myriad of Linux 8390 drivers written by Donald Becker.
+
+	2) Once again Russ Nelson's asm packet driver provided additional info.
+
+	3) Info for getting IRQ and sh-mem gleaned from the EISA cfg files.
+	   Too bad it doesn't work -- see below.
+
+	The ES3210 is an EISA shared memory NS8390 implementation. Note 
+	that all memory copies to/from the board must be 32bit transfers. 
+	Which rules out using eth_io_copy_and_sum() in this driver.
+
+	Apparently there are two slightly different revisions of the
+	card, since there are two distinct EISA cfg files (!rii0101.cfg
+	and !rii0102.cfg) One has media select in the cfg file and the
+	other doesn't. Hopefully this will work with either.
+
+	That is about all I can tell you about it, having never actually 
+	even seen one of these cards. :)  Try http://www.interlan.com
+	if you want more info.
+
+	Thanks go to Mark Salazar for testing v0.02 of this driver.
+
+	Bugs, to-fix, etc:
+
+	1) The EISA cfg ports that are *supposed* to have the IRQ and shared
+	   mem values just read 0xff all the time. Hrrmpf. Apparently the
+	   same happens with the packet driver as the code for reading
+	   these registers is disabled there. In the meantime, boot with:
+	   ether=<IRQ>,0,0x<shared_mem_addr>,eth0 to override the IRQ and
+	   shared memory detection. (The i/o port detection is okay.)
+
+	2) Module support currently untested. Probably works though.
+
+*/
+
+static const char *version =
+	"es3210.c: Driver revision v0.03, 14/09/96\n";
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+int es_probe(struct device *dev);
+int es_probe1(struct device *dev, int ioaddr);
+
+static int es_open(struct device *dev);
+static int es_close(struct device *dev);
+
+static void es_reset_8390(struct device *dev);
+
+static void es_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page);
+static void es_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset);
+static void es_block_output(struct device *dev, int count, const unsigned char *buf, const start_page);
+
+#define ES_START_PG	0x00    /* First page of TX buffer		*/
+#define ES_STOP_PG	0x40    /* Last page +1 of RX ring		*/
+
+#define ES_IO_EXTENT	0x37	/* The cfg file says 0xc90 -> 0xcc7	*/
+#define ES_ID_PORT	0xc80	/* Same for all EISA cards 		*/
+#define ES_SA_PROM	0xc90	/* Start of e'net addr.			*/
+#define ES_RESET_PORT	0xc84	/* From the packet driver source	*/
+#define ES_NIC_OFFSET	0xca0	/* Hello, the 8390 is *here*		*/
+
+#define ES_ADDR0	0x02	/* 3 byte vendor prefix			*/
+#define ES_ADDR1	0x07
+#define ES_ADDR2	0x01
+
+/*
+ * Two card revisions. EISA ID's are always rev. minor, rev. major,, and 
+ * then the three vendor letters stored in 5 bits each, with an "a" = 1.
+ * For eg: "rii" = 10010 01001 01001 = 0x4929, which is how the EISA
+ * config utility determines automagically what config file(s) to use.
+ */
+#define ES_EISA_ID1	0x01012949	/* !rii0101.cfg 		*/
+#define ES_EISA_ID2	0x02012949	/* !rii0102.cfg 		*/
+
+#define ES_CFG1		0xcc0	/* IOPORT(1) --> IOPORT(6) in cfg file	*/
+#define ES_CFG2		0xcc1
+#define ES_CFG3		0xcc2
+#define ES_CFG4		0xcc3
+#define ES_CFG5		0xcc4
+#define ES_CFG6		0xc84	/* NB: 0xc84 is also "reset" port.	*/
+
+/*
+ *	You can OR any of the following bits together and assign it
+ *	to ES_DEBUG to get verbose driver info during operation.
+ *	Some of these don't do anything yet.
+ */
+
+#define ES_D_PROBE	0x01
+#define ES_D_RX_PKT	0x02
+#define ES_D_TX_PKT	0x04
+#define ED_D_IRQ	0x08
+
+#define ES_DEBUG	0
+
+static unsigned char lo_irq_map[] = {3, 4, 5, 6, 7, 9, 10};
+static unsigned char hi_irq_map[] = {11, 12, 0, 14, 0, 0, 0, 15};
+
+/* 
+ *	Probe for the card. The best way is to read the EISA ID if it
+ *	is known. Then we check the prefix of the station address
+ *	PROM for a match against the Racal-Interlan assigned value.
+ */
+
+int es_probe(struct device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+
+	if (ioaddr > 0x1ff)		/* Check a single specified location. */
+		return es_probe1(dev, ioaddr);
+	else if (ioaddr > 0)		/* Don't probe at all. */
+		return ENXIO;
+
+	if (!EISA_bus) {
+#if ES_DEBUG & ES_D_PROBE
+		printk("es3210.c: Not EISA bus. Not probing high ports.\n");
+#endif
+		return ENXIO;
+	}
+
+	/* EISA spec allows for up to 16 slots, but 8 is typical. */
+	for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+		if (check_region(ioaddr + ES_SA_PROM, ES_IO_EXTENT))
+			continue;
+		if (es_probe1(dev, ioaddr) == 0)
+			return 0;
+	}
+
+	return ENODEV;
+}
+
+int es_probe1(struct device *dev, int ioaddr)
+{
+	int i;
+	unsigned long eisa_id;
+
+#if ES_DEBUG & ES_D_PROBE
+	printk("es3210.c: probe at %#x, ID %#8x\n", ioaddr, inl(ioaddr + ES_ID_PORT));
+	printk("es3210.c: config regs: %#x %#x %#x %#x %#x %#x\n",
+		inb(ioaddr + ES_CFG1), inb(ioaddr + ES_CFG2), inb(ioaddr + ES_CFG3),
+		inb(ioaddr + ES_CFG4), inb(ioaddr + ES_CFG5), inb(ioaddr + ES_CFG6));
+#endif
+
+
+/*	Check the EISA ID of the card. */
+	eisa_id = inl(ioaddr + ES_ID_PORT);
+	if ((eisa_id != ES_EISA_ID1) && (eisa_id != ES_EISA_ID2)) {
+		return ENODEV;
+	}
+	
+/*	Check the Racal vendor ID as well. */
+	if (inb(ioaddr + ES_SA_PROM + 0) != ES_ADDR0
+		|| inb(ioaddr + ES_SA_PROM + 1) != ES_ADDR1
+		|| inb(ioaddr + ES_SA_PROM + 2) != ES_ADDR2 ) {
+		printk("es3210.c: card not found");
+		for(i = 0; i < ETHER_ADDR_LEN; i++)
+			printk(" %02x", inb(ioaddr + ES_SA_PROM + i));
+		printk(" (invalid prefix).\n");
+		return ENODEV;
+	}
+
+	/* We should have a "dev" from Space.c or the static module table. */
+	if (dev == NULL) {
+		printk("es3210.c: Passed a NULL device.\n");
+		dev = init_etherdev(0, 0);
+	}
+
+	printk("es3210.c: ES3210 rev. %ld at %#x, node", eisa_id>>24, ioaddr);
+	for(i = 0; i < ETHER_ADDR_LEN; i++) 
+		printk(" %02x", (dev->dev_addr[i] = inb(ioaddr + ES_SA_PROM + i)));
+
+	/* Snarf the interrupt now. */
+	if (dev->irq == 0) {
+		unsigned char hi_irq = inb(ioaddr + ES_CFG2) & 0x07;
+		unsigned char lo_irq = inb(ioaddr + ES_CFG1) & 0xfe;
+
+		if (hi_irq != 0) {
+			dev->irq = hi_irq_map[hi_irq - 1];
+		} else {
+			int i = 0;
+			while (lo_irq > (1<<i)) i++;
+			dev->irq = lo_irq_map[i];
+		}
+		printk(" using IRQ %d", dev->irq);
+#if ES_DEBUG & ES_D_PROBE
+		printk("es3210.c: hi_irq %#x, lo_irq %#x, dev->irq = %d\n",
+					hi_irq, lo_irq, dev->irq);
+#endif
+	} else {
+		if (dev->irq == 2)
+			dev->irq = 9;			/* Doh! */
+		printk(" assigning IRQ %d", dev->irq);
+	}
+
+	if (request_irq(dev->irq, ei_interrupt, 0, "es3210", NULL)) {
+		printk (" unable to get IRQ %d.\n", dev->irq);
+		return EAGAIN;
+	}
+
+	if (dev->mem_start == 0) {
+		unsigned char mem_enabled = inb(ioaddr + ES_CFG2) & 0xc0;
+		unsigned char mem_bits = inb(ioaddr + ES_CFG3) & 0x07;
+
+		if (mem_enabled != 0x80) {
+			printk(" shared mem disabled - giving up\n");
+			free_irq(dev->irq, NULL);
+			return -ENXIO;
+		}
+		dev->mem_start = 0xC0000 + mem_bits*0x4000;
+		printk(" using ");
+	} else {
+		printk(" assigning ");
+	}
+
+	dev->mem_end = dev->rmem_end = dev->mem_start
+		+ (ES_STOP_PG - ES_START_PG)*256;
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+
+	printk("mem %#lx-%#lx\n", dev->mem_start, dev->mem_end-1);
+
+	/* Allocate dev->priv and fill in 8390 specific dev fields. */
+	if (ethdev_init(dev)) {
+		printk (" unable to allocate memory for dev->priv.\n");
+		free_irq(dev->irq, NULL);
+		return -ENOMEM;
+	}
+
+#if ES_DEBUG & ES_D_PROBE
+	if (inb(ioaddr + ES_CFG5))
+		printk("es3210: Warning - DMA channel enabled, but not used here.\n");
+#endif
+	/* Note, point at the 8390, and not the card... */
+	dev->base_addr = ioaddr + ES_NIC_OFFSET;
+	request_region(ioaddr + ES_SA_PROM, ES_IO_EXTENT, "es3210");
+
+
+	ei_status.name = "ES3210";
+	ei_status.tx_start_page = ES_START_PG;
+	ei_status.rx_start_page = ES_START_PG + TX_PAGES;
+	ei_status.stop_page = ES_STOP_PG;
+	ei_status.word16 = 1;
+
+	if (ei_debug > 0)
+		printk(version);
+
+	ei_status.reset_8390 = &es_reset_8390;
+	ei_status.block_input = &es_block_input;
+	ei_status.block_output = &es_block_output;
+	ei_status.get_8390_hdr = &es_get_8390_hdr;
+
+	dev->open = &es_open;
+	dev->stop = &es_close;
+	NS8390_init(dev, 0);
+	return 0;
+}
+
+/*
+ *	Reset as per the packet driver method. Judging by the EISA cfg
+ *	file, this just toggles the "Board Enable" bits (bit 2 and 0).
+ */
+
+static void es_reset_8390(struct device *dev)
+{
+	unsigned short ioaddr = dev->base_addr;
+	unsigned long end;
+
+	outb(0x04, ioaddr + ES_RESET_PORT);
+	if (ei_debug > 1) printk("%s: resetting the ES3210...", dev->name);
+
+	end = jiffies + 2*HZ/100;
+        while ((signed)(end - jiffies) > 0) continue;
+
+	ei_status.txing = 0;
+	outb(0x01, ioaddr + ES_RESET_PORT);
+	if (ei_debug > 1) printk("reset done\n");
+
+	return;
+}
+
+/*
+ *	Note: In the following three functions is the implicit assumption
+ *	that the associated memcpy will only use "rep; movsl" as long as
+ *	we keep the counts as some multiple of doublewords. This is a
+ *	requirement of the hardware, and also prevents us from using
+ *	eth_io_copy_and_sum() since we can't guarantee it will limit 
+ *	itself to doubleword access.
+ */
+
+/*
+ *	Grab the 8390 specific header. Similar to the block_input routine, but
+ *	we don't need to be concerned with ring wrap as the header will be at
+ *	the start of a page, so we optimize accordingly. (A single doubleword.)
+ */
+
+static void
+es_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = dev->mem_start + ((ring_page - ES_START_PG)<<8);
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	hdr->count = (hdr->count + 3) & ~3;     /* Round up allocation. */
+}
+
+/*	
+ *	Block input and output are easy on shared memory ethercards, the only
+ *	complication is when the ring buffer wraps. The count will already
+ *	be rounded up to a doubleword value via es_get_8390_hdr() above.
+ */
+
+static void es_block_input(struct device *dev, int count, struct sk_buff *skb,
+						  int ring_offset)
+{
+	unsigned long xfer_start = dev->mem_start + ring_offset - (ES_START_PG<<8);
+
+	if (xfer_start + count > dev->rmem_end) {
+		/* Packet wraps over end of ring buffer. */
+		int semi_count = dev->rmem_end - xfer_start;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+	} else {
+		/* Packet is in one chunk. */
+		memcpy_fromio(skb->data, xfer_start, count);
+	}
+}
+
+static void es_block_output(struct device *dev, int count,
+				const unsigned char *buf, int start_page)
+{
+	unsigned long shmem = dev->mem_start + ((start_page - ES_START_PG)<<8);
+
+	count = (count + 3) & ~3;     /* Round up to doubleword */
+	memcpy_toio(shmem, buf, count);
+}
+
+static int es_open(struct device *dev)
+{
+	ei_open(dev);
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+static int es_close(struct device *dev)
+{
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	ei_close(dev);
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+#ifdef MODULE
+#define MAX_ES_CARDS	4	/* Max number of ES3210 cards per module */
+#define NAMELEN		8	/* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ES_CARDS] = { 0, };
+static struct device dev_es3210[MAX_ES_CARDS] = {
+	{
+		NULL,		/* assign a chunk of namelist[] below */
+		0, 0, 0, 0,
+		0, 0,
+		0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_ES_CARDS] = { 0, };
+static int irq[MAX_ES_CARDS]  = { 0, };
+static int mem[MAX_ES_CARDS] = { 0, };
+
+int
+init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
+		struct device *dev = &dev_es3210[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->mem_start = mem[this_dev];		/* Currently ignored by driver */
+		dev->init = es_probe;
+		/* Default is to only install one card. */
+		if (io[this_dev] == 0 && this_dev != 0) break;
+		if (register_netdev(dev) != 0) {
+			printk(KERN_WARNING "es3210.c: No es3210 card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) return 0;	/* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
+	}
+
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ES_CARDS; this_dev++) {
+		struct device *dev = &dev_es3210[this_dev];
+		if (dev->priv != NULL) {
+			kfree(dev->priv);
+			dev->priv = NULL;
+			free_irq(dev->irq, NULL);
+			irq2dev_map[dev->irq] = NULL;
+			release_region(dev->base_addr, ES_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.1.14/linux/drivers/net/i82586.h linux/drivers/net/i82586.h
--- v2.1.14/linux/drivers/net/i82586.h	Sun Jul  2 10:30:37 1995
+++ linux/drivers/net/i82586.h	Tue Dec  3 09:24:15 1996
@@ -259,8 +259,13 @@
 {
 	ach_t		mcs_h;
 	unsigned short	mcs_cnt;	/* No. of bytes of MC addresses	*/
-	unsigned short	mcs_data[3];	/* The first MC address ..	*/
+#if 0
+	unsigned char	mcs_data[ADDR_LEN]; /* The first MC address ..	*/
+	...
+#endif
 };
+
+#define I82586_MAX_MULTICAST_ADDRESSES	128	/* Hardware hashed filter */
 
 /*
  * The Transmit Action Command.
diff -u --recursive --new-file v2.1.14/linux/drivers/net/loopback.c linux/drivers/net/loopback.c
--- v2.1.14/linux/drivers/net/loopback.c	Tue Nov 19 15:53:56 1996
+++ linux/drivers/net/loopback.c	Thu Dec 12 16:51:09 1996
@@ -61,7 +61,6 @@
 static int loopback_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
-	int unlock=1;
   
 	if (skb == NULL || dev == NULL) 
 		return(0);
@@ -71,24 +70,18 @@
 	 *	instead are lobbed from tx queue to rx queue 
 	 */
 
-	if(skb->free==0)
+	if(skb->users != 1)
 	{
 	  	struct sk_buff *skb2=skb;
 	  	skb=skb_clone(skb, GFP_ATOMIC);		/* Clone the buffer */
-	  	dev_kfree_skb(skb2, FREE_WRITE);
-	  	if(skb==NULL)
-	  		return 0;
-  		unlock=0;
-	}
-	else if(skb->sk)
-	{
-	  	/*
-	  	 *	Packet sent but looped back around. Cease to charge
-	  	 *	the socket for the frame.
-	  	 */
-		atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
-	  	skb->sk->write_space(skb->sk);
+	  	if(skb==NULL) {
+			kfree_skb(skb2, FREE_WRITE);
+			return 0;
+		}
+	  	kfree_skb(skb2, FREE_WRITE);
 	}
+	else
+		skb_orphan(skb);
 
 	skb->protocol=eth_type_trans(skb,dev);
 	skb->dev=dev;
@@ -96,8 +89,6 @@
 	skb->ip_summed = CHECKSUM_UNNECESSARY;
 #endif
 	netif_rx(skb);
-	if(unlock)
-	  	skb_device_unlock(skb);
   
 	stats->rx_packets++;
 	stats->tx_packets++;
@@ -125,9 +116,11 @@
 	dev->tbusy		= 0;
 	dev->hard_start_xmit	= loopback_xmit;
 	dev->hard_header	= eth_header;
+	dev->hard_header_cache	= eth_header_cache;
+	dev->header_cache_update= eth_header_cache_update;
 	dev->hard_header_len	= ETH_HLEN;		/* 14			*/
 	dev->addr_len		= ETH_ALEN;		/* 6			*/
-	dev->tx_queue_len	= 50000;		/* No limit on loopback */
+	dev->tx_queue_len	= 0;
 	dev->type		= ARPHRD_LOOPBACK;	/* 0x0001		*/
 	dev->rebuild_header	= eth_rebuild_header;
 	dev->open		= loopback_open;
diff -u --recursive --new-file v2.1.14/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v2.1.14/linux/drivers/net/ne.c	Wed Oct  9 08:55:19 1996
+++ linux/drivers/net/ne.c	Thu Dec 12 16:51:10 1996
@@ -24,6 +24,7 @@
     Paul Gortmaker	: multiple card support for module users.
     Paul Gortmaker	: Support for PCI ne2k clones, similar to lance.c
     Paul Gortmaker	: Allow users with bad cards to avoid full probe.
+    Paul Gortmaker	: PCI probe changes, more PCI cards supported.
 
 */
 
@@ -61,11 +62,22 @@
 /* Do we have a non std. amount of memory? (in units of 256 byte pages) */
 /* #define PACKETBUF_MEMSIZE	0x40 */
 
-/* ---- No user-serviceable parts below ---- */
-
-/* A zero-terminated list of I/O addresses to be probed. */
+/* A zero-terminated list of I/O addresses to be probed at boot. */
+#ifndef MODULE
 static unsigned int netcard_portlist[] =
 { 0x300, 0x280, 0x320, 0x340, 0x360, 0};
+#endif
+
+#ifdef CONFIG_PCI
+/* Ack! People are making PCI ne2000 clones! Oh the horror, the horror... */
+static struct { unsigned short vendor, dev_id;}
+pci_clone_list[] = {
+	{PCI_VENDOR_ID_REALTEK,		PCI_DEVICE_ID_REALTEK_8029},
+	{PCI_VENDOR_ID_WINBOND2,	PCI_DEVICE_ID_WINBOND2_89C940},
+	{PCI_VENDOR_ID_COMPEX,		PCI_DEVICE_ID_COMPEX_RL2000},
+	{0,}
+};
+#endif
 
 #ifdef SUPPORT_NE_BAD_CLONES
 /* A list of bad clones that we none-the-less recognize. */
@@ -80,10 +92,14 @@
     {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}},  /* Outlaw 4-Dimension cards. */
     {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
     {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */
+    {"COMPEX","COMPEX16",{0x00,0x80,0x48}}, /* Broken ISA Compex cards */
+    {"E-LAN100", "E-LAN200", {0x00, 0x00, 0x5d}}, /* Broken ne1000 clones */
     {0,}
 };
 #endif
 
+/* ---- No user-serviceable parts below ---- */
+
 #define NE_BASE	 (dev->base_addr)
 #define NE_CMD	 	0x00
 #define NE_DATAPORT	0x10	/* NatSemi-defined port window offset. */
@@ -100,6 +116,9 @@
 
 int ne_probe(struct device *dev);
 static int ne_probe1(struct device *dev, int ioaddr);
+#ifdef CONFIG_PCI
+static int ne_probe_pci(struct device *dev);
+#endif
 
 static int ne_open(struct device *dev);
 static int ne_close(struct device *dev);
@@ -139,13 +158,15 @@
 {"ne", ne_probe1, NE_IO_EXTENT, netcard_portlist};
 #else
 
-/*  Note that this probe only picks up one card at a time, even for multiple
-    PCI ne2k cards. Use "ether=0,0,eth1" if you have a second PCI ne2k card.
-    This keeps things consistent regardless of the bus type of the card. */
+/* 
+ * Note that at boot, this probe only picks up one card at a time, even for 
+ * multiple PCI ne2k cards. Use "ether=0,0,eth1" if you have a second PCI 
+ * ne2k card.  This keeps things consistent regardless of the bus type of 
+ * the card.
+ */
 
 int ne_probe(struct device *dev)
 {
-    int i;
     int base_addr = dev ? dev->base_addr : 0;
 
     /* First check any supplied i/o locations. User knows best. <cough> */
@@ -154,53 +175,70 @@
     else if (base_addr != 0)	/* Don't probe at all. */
 	return ENXIO;
 
+#ifdef CONFIG_PCI
     /* Then look for any installed PCI clones */
-#if defined(CONFIG_PCI)
-    if (pcibios_present()) {
-	int pci_index;
-	for (pci_index = 0; pci_index < 8; pci_index++) {
-		unsigned char pci_bus, pci_device_fn;
-		unsigned int pci_ioaddr;
-
-		/* Currently only Realtek are making PCI ne2k clones. */
-		if (pcibios_find_device (PCI_VENDOR_ID_REALTEK,
-				PCI_DEVICE_ID_REALTEK_8029, pci_index,
-				&pci_bus, &pci_device_fn) != 0)
-			break;	/* OK, now try to probe for std. ISA card */
-		pcibios_read_config_byte(pci_bus, pci_device_fn,
-				PCI_INTERRUPT_LINE, &pci_irq_line);
-		pcibios_read_config_dword(pci_bus, pci_device_fn,
-				PCI_BASE_ADDRESS_0, &pci_ioaddr);
-		/* Strip the I/O address out of the returned value */
-		pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
-		/* Avoid already found cards from previous ne_probe() calls */
-		if (check_region(pci_ioaddr, NE_IO_EXTENT))
-			continue;
-		printk("ne.c: PCI BIOS reports ne2000 clone at i/o %#x, irq %d.\n",
-				pci_ioaddr, pci_irq_line);
-		if (ne_probe1(dev, pci_ioaddr) != 0) {	/* Shouldn't happen. */
-			printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr);
-			break;	/* Hrmm, try to probe for ISA card... */
-		}
-		pci_irq_line = 0;
-		return 0;
-	}
-    }
-#endif  /* defined(CONFIG_PCI) */
+    if (pcibios_present() && (ne_probe_pci(dev) == 0)) 
+	return 0;
+#endif
 
+#ifndef MODULE
     /* Last resort. The semi-risky ISA auto-probe. */
-    for (i = 0; netcard_portlist[i]; i++) {
-	int ioaddr = netcard_portlist[i];
+    for (base_addr = 0; netcard_portlist[base_addr] != 0; base_addr++) {
+	int ioaddr = netcard_portlist[base_addr];
 	if (check_region(ioaddr, NE_IO_EXTENT))
 	    continue;
 	if (ne_probe1(dev, ioaddr) == 0)
 	    return 0;
     }
+#endif
 
     return ENODEV;
 }
 #endif
 
+#ifdef CONFIG_PCI
+static int ne_probe_pci(struct device *dev)
+{
+	int i;
+
+	for (i = 0; pci_clone_list[i].vendor != 0; i++) {
+		unsigned char pci_bus, pci_device_fn;
+		unsigned int pci_ioaddr;
+		int pci_index;
+		
+		for (pci_index = 0; pci_index < 8; pci_index++) {
+			if (pcibios_find_device (pci_clone_list[i].vendor,
+					pci_clone_list[i].dev_id, pci_index,
+					&pci_bus, &pci_device_fn) != 0)
+				break;	/* No more of these type of cards */
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+					PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Strip the I/O address out of the returned value */
+			pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+			/* Avoid already found cards from previous calls */
+			if (check_region(pci_ioaddr, NE_IO_EXTENT))
+				continue;
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+					PCI_INTERRUPT_LINE, &pci_irq_line);
+			break;	/* Beauty -- got a valid card. */
+		}
+		if (pci_irq_line == 0) continue;	/* Try next PCI ID */
+		printk("ne.c: PCI BIOS reports %s %s at i/o %#x, irq %d.\n",
+				pci_strvendor(pci_clone_list[i].vendor),
+				pci_strdev(pci_clone_list[i].vendor, pci_clone_list[i].dev_id),
+				pci_ioaddr, pci_irq_line);
+		if (ne_probe1(dev, pci_ioaddr) != 0) {	/* Shouldn't happen. */
+			printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr);
+			pci_irq_line = 0;
+			return -ENXIO;
+		}
+		pci_irq_line = 0;
+		return 0;
+	}
+	return -ENODEV;
+}
+#endif  /* CONFIG_PCI */
+
 static int ne_probe1(struct device *dev, int ioaddr)
 {
     int i;
@@ -308,8 +346,8 @@
 	for (i = 0; i < 16; i++)
 		SA_prom[i] = SA_prom[i+i];
     
-    if (pci_irq_line)
-	wordlength = 2;		/* Catch broken cards mentioned above. */
+    if (pci_irq_line || ioaddr >= 0x400)
+	wordlength = 2;		/* Catch broken PCI cards mentioned above. */
 
     if (wordlength == 2) {
 	/* We must set the 8390 for word mode. */
@@ -359,9 +397,8 @@
 
     }
 
-    if (pci_irq_line) {
+    if (pci_irq_line)
 	dev->irq = pci_irq_line;
-    }
 
     if (dev->irq < 2) {
 	autoirq_setup(0);
@@ -694,8 +731,9 @@
 
 static int io[MAX_NE_CARDS] = { 0, };
 static int irq[MAX_NE_CARDS]  = { 0, };
+static int bad[MAX_NE_CARDS]  = { 0, };	/* 0xbad = bad sig or no reset ack */
 
-/* This is set up so that no autoprobe takes place. We can't guarantee
+/* This is set up so that no ISA autoprobe takes place. We can't guarantee
 that the ne2k probe is the last 8390 based probe to take place (as it
 is at boot) and so the probe will get confused by any other 8390 cards.
 ISA device autoprobes on a running machine are not recommended anyway. */
@@ -709,19 +747,20 @@
 		struct device *dev = &dev_ne[this_dev];
 		dev->name = namelist+(NAMELEN*this_dev);
 		dev->irq = irq[this_dev];
+		dev->mem_end = bad[this_dev];
 		dev->base_addr = io[this_dev];
 		dev->init = ne_probe;
-		if (io[this_dev] == 0)  {
-			if (this_dev != 0) break; /* only complain once */
-			printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
-			return -EPERM;
-		}
-		if (register_netdev(dev) != 0) {
-			printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]);
-			if (found != 0) return 0;	/* Got at least one. */
-			return -ENXIO;
+		if (register_netdev(dev) == 0) {
+			found++;
+			continue;
 		}
-		found++;
+		if (found != 0) 	/* Got at least one. */
+			return 0;
+		if (io[this_dev] != 0)
+			printk(KERN_WARNING "ne.c: No NE*000 card found at i/o = %#x\n", io[this_dev]);
+		else
+			printk(KERN_NOTICE "ne.c: No PCI cards found. Use \"io=0xNNN\" value(s) for ISA cards.\n");
+		return -ENXIO;
 	}
 
 	return 0;
diff -u --recursive --new-file v2.1.14/linux/drivers/net/net_init.c linux/drivers/net/net_init.c
--- v2.1.14/linux/drivers/net/net_init.c	Tue Nov 19 15:53:56 1996
+++ linux/drivers/net/net_init.c	Thu Dec 12 16:51:10 1996
@@ -193,7 +193,7 @@
 	dev->hard_header	= eth_header;
 	dev->rebuild_header 	= eth_rebuild_header;
 	dev->set_mac_address 	= eth_mac_addr;
-	dev->header_cache_bind 	= eth_header_cache_bind;
+	dev->hard_header_cache	= eth_header_cache;
 	dev->header_cache_update= eth_header_cache_update;
 
 	dev->type		= ARPHRD_ETHER;
diff -u --recursive --new-file v2.1.14/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c
--- v2.1.14/linux/drivers/net/new_tunnel.c	Fri Jul 19 08:24:05 1996
+++ linux/drivers/net/new_tunnel.c	Thu Jan  1 02:00:00 1970
@@ -1,434 +0,0 @@
-/* tunnel.c: an IP tunnel driver
-
-	The purpose of this driver is to provide an IP tunnel through
-	which you can tunnel network traffic transparently across subnets.
-
-	This was written by looking at Nick Holloway's dummy driver
-	Thanks for the great code!
-
-		-Sam Lantinga	(slouken@cs.ucdavis.edu)  02/01/95
-		
-	Minor tweaks:
-		Cleaned up the code a little and added some pre-1.3.0 tweaks.
-		dev->hard_header/hard_header_len changed to use no headers.
-		Comments/bracketing tweaked.
-		Made the tunnels use dev->name not tunnel: when error reporting.
-		Added tx_dropped stat
-		
-		-Alan Cox	(Alan.Cox@linux.org) 21 March 95
-
-	Reworked:
-		Changed to tunnel to destination gateway in addition to the
-			tunnel's pointopoint address
-		Almost completely rewritten
-		Note:  There is currently no firewall or ICMP handling done.
-
-		-Sam Lantinga	(slouken@cs.ucdavis.edu) 02/13/96
-		
-	Note:
-		The old driver is in tunnel.c if you have funnies with the
-		new one.
-*/
-
-/* Things I wish I had known when writing the tunnel driver:
-
-	When the tunnel_xmit() function is called, the skb contains the
-	packet to be sent (plus a great deal of extra info), and dev
-	contains the tunnel device that _we_ are.
-
-	When we are passed a packet, we are expected to fill in the
-	source address with our source IP address.
-
-	What is the proper way to allocate, copy and free a buffer?
-	After you allocate it, it is a "0 length" chunk of memory
-	starting at zero.  If you want to add headers to the buffer
-	later, you'll have to call "skb_reserve(skb, amount)" with
-	the amount of memory you want reserved.  Then, you call
-	"skb_put(skb, amount)" with the amount of space you want in
-	the buffer.  skb_put() returns a pointer to the top (#0) of
-	that buffer.  skb->len is set to the amount of space you have
-	"allocated" with skb_put().  You can then write up to skb->len
-	bytes to that buffer.  If you need more, you can call skb_put()
-	again with the additional amount of space you need.  You can
-	find out how much more space you can allocate by calling 
-	"skb_tailroom(skb)".
-	Now, to add header space, call "skb_push(skb, header_len)".
-	This creates space at the beginning of the buffer and returns
-	a pointer to this new space.  If later you need to strip a
-	header from a buffer, call "skb_pull(skb, header_len)".
-	skb_headroom() will return how much space is left at the top
-	of the buffer (before the main data).  Remember, this headroom
-	space must be reserved before the skb_put() function is called.
-*/
-
-#include <linux/module.h>
-#include <linux/config.h>	/* for CONFIG_IP_FORWARD */
-
-/* Only two headers!! :-) */
-#include <net/ip.h>
-#include <linux/if_arp.h>
-
-
-/*#define TUNNEL_DEBUG*/
-
-/* 
- *	Our header is a simple IP packet with no options
- */
- 
-#define tunnel_hlen	sizeof(struct iphdr)
-
-/*
- *	Okay, this needs to be high enough that we can fit a "standard"
- *	ethernet header and an IP tunnel header into the outgoing packet.
- *	[36 bytes]
- */
- 
-#define TUNL_HLEN	(((ETH_HLEN+15)&~15)+tunnel_hlen)
-
-
-static int tunnel_open(struct device *dev)
-{
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-static int tunnel_close(struct device *dev)
-{
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-#ifdef TUNNEL_DEBUG
-void print_ip(struct iphdr *ip)
-{
-	unsigned char *ipaddr;
-
-	printk("IP packet:\n");
-	printk("--- header len = %d\n", ip->ihl*4);
-	printk("--- ip version: %d\n", ip->version);
-	printk("--- ip protocol: %d\n", ip->protocol);
-	ipaddr=(unsigned char *)&ip->saddr;
-	printk("--- source address: %u.%u.%u.%u\n", 
-			*ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
-	ipaddr=(unsigned char *)&ip->daddr;
-	printk("--- destination address: %u.%u.%u.%u\n", 
-			*ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
-	printk("--- total packet len: %d\n", ntohs(ip->tot_len));
-}
-#endif
-
-/*
- *	This function assumes it is being called from dev_queue_xmit()
- *	and that skb is filled properly by that function.
- */
-
-static int tunnel_xmit(struct sk_buff *skb, struct device *dev)
-{
-	struct enet_statistics *stats;		/* This device's statistics */
-	struct rtable *rt;     			/* Route to the other host */
-	struct device *tdev;			/* Device to other host */
-	struct iphdr  *iph;			/* Our new IP header */
-	__u32          target;			/* The other host's IP address */
-	int      max_headroom;			/* The extra header space needed */
-
-	/*
-	 *	Return if there is nothing to do.  (Does this ever happen?)
-	 */
-	if (skb == NULL || dev == NULL) {
-#ifdef TUNNEL_DEBUG
-		printk ( KERN_INFO "tunnel: Nothing to do!\n" );
-#endif
-		return 0;
-	}
-
-	/* 
-	 *	Make sure we are not busy (check lock variable) 
-	 */
-	 
-	stats = (struct enet_statistics *)dev->priv;
-	cli();
-	if (dev->tbusy != 0) 
-	{
-		sti();
-		stats->tx_errors++;
-		return(1);
-	}
-	dev->tbusy = 1;
-	sti();
-  
-	/*printk("-");*/
-	/*
-	 *  First things first.  Look up the destination address in the 
-	 *  routing tables
-	 */
-	iph = (struct iphdr *) skb->data;
-	if ((rt = ip_rt_route(iph->daddr, 0)) == NULL)
-	{ 
-		/* No route to host */
-		/* Where did the packet come from? */
-		/*icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);*/
-		printk ( KERN_INFO "%s: Packet with no route!\n", dev->name);
-		dev->tbusy=0;
-		stats->tx_errors++;
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-
-	/*
-	 * Get the target address (other end of IP tunnel)
-	 */
-	if (rt->rt_flags & RTF_GATEWAY)
-		target = rt->rt_gateway;
-	else
-		target = dev->pa_dstaddr;
-
-	if ( ! target )
-	{	/* No gateway to tunnel through? */
-		/* Where did the packet come from? */
-		/*icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);*/
-		printk ( KERN_INFO "%s: Packet with no target gateway!\n", dev->name);
-		ip_rt_put(rt);
-		dev->tbusy=0;
-		stats->tx_errors++;
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-	ip_rt_put(rt);
-
-	if ((rt = ip_rt_route(target, 0)) == NULL)
-	{ 
-		/* No route to host */
-		/* Where did the packet come from? */
-		/*icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);*/
-		printk ( KERN_INFO "%s: Can't reach target gateway!\n", dev->name);
-		dev->tbusy=0;
-		stats->tx_errors++;
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-	tdev = rt->rt_dev;
-
-	if (tdev == dev)
-	{ 
-		/* Tunnel to ourselves?  -- I don't think so. */
-		printk ( KERN_INFO "%s: Packet targetted at myself!\n" , dev->name);
-		ip_rt_put(rt);
-		dev->tbusy=0;
-		stats->tx_errors++;
-		dev_kfree_skb(skb, FREE_WRITE);
-		return 0;
-	}
-
-#ifdef TUNNEL_DEBUG
-	printk("Old IP Header....\n");
-	print_ip(iph);
-#endif
-
-	/*
-	 * Okay, now see if we can stuff it in the buffer as-is.
-	 */
-	max_headroom = (((tdev->hard_header_len+15)&~15)+tunnel_hlen);
-#ifdef TUNNEL_DEBUG
-printk("Room left at head: %d\n", skb_headroom(skb));
-printk("Room left at tail: %d\n", skb_tailroom(skb));
-printk("Required room: %d, Tunnel hlen: %d\n", max_headroom, TUNL_HLEN);
-#endif
-	if (skb_headroom(skb) >= max_headroom && skb->free) {
-		skb->h.iph = (struct iphdr *) skb_push(skb, tunnel_hlen);
-		skb_device_unlock(skb);
-	} else {
-		struct sk_buff *new_skb;
-
-		if ( !(new_skb = dev_alloc_skb(skb->len+max_headroom)) ) 
-		{
-			printk( KERN_INFO "%s: Out of memory, dropped packet\n",
-				dev->name);
-			ip_rt_put(rt);
-  			dev->tbusy = 0;
-  			stats->tx_dropped++;
-			dev_kfree_skb(skb, FREE_WRITE);
-			return 0;
-		}
-		new_skb->free = 1;
-
-		/*
-		 * Reserve space for our header and the lower device header
-		 */
-		skb_reserve(new_skb, max_headroom);
-
-		/*
-		 * Copy the old packet to the new buffer.
-		 * Note that new_skb->h.iph will be our (tunnel driver's) header
-		 * and new_skb->ip_hdr is the IP header of the old packet.
-		 */
-		new_skb->ip_hdr = (struct iphdr *) skb_put(new_skb, skb->len);
-		new_skb->dev = skb->dev;
-		memcpy(new_skb->ip_hdr, skb->data, skb->len);
-		memset(new_skb->proto_priv, 0, sizeof(skb->proto_priv));
-
-		/* Tack on our header */
-		new_skb->h.iph = (struct iphdr *) skb_push(new_skb, tunnel_hlen);
-
-		/* Free the old packet, we no longer need it */
-		dev_kfree_skb(skb, FREE_WRITE);
-		skb = new_skb;
-	}
-
-	/*
-	 *	Push down and install the IPIP header.
-	 */
-	 
-	iph 			=	skb->h.iph;
-	iph->version		= 	4;
-	iph->tos		=	skb->ip_hdr->tos;
-	iph->ttl		=	skb->ip_hdr->ttl;
-	iph->frag_off		=	0;
-	iph->daddr		=	target;
-	iph->saddr		=	tdev->pa_addr;
-	iph->protocol		=	IPPROTO_IPIP;
-	iph->ihl		=	5;
-	iph->tot_len		=	htons(skb->len);
-	iph->id			=	htons(ip_id_count++);	/* Race condition here? */
-	ip_send_check(iph);
-	skb->ip_hdr 		=	skb->h.iph;
-	skb->protocol		=	htons(ETH_P_IP);
-#ifdef TUNNEL_DEBUG
-	printk("New IP Header....\n");
-	print_ip(iph);
-#endif
-
-	/*
-	 *	Send the packet on its way!
-	 *	Note that dev_queue_xmit() will eventually free the skb.
-	 *	If ip_forward() made a copy, it will return 1 so we can free.
-	 */
-
-#ifdef CONFIG_IP_FORWARD
-	if (ip_forward(skb, dev, IPFWD_NOTTLDEC, target))
-#endif
-		kfree_skb(skb, FREE_WRITE);
-
-	/*
-	 *	Clean up:  We're done with the route and the packet
-	 */
-	 
-	ip_rt_put(rt);
- 
-#ifdef TUNNEL_DEBUG
-	printk("Packet sent through tunnel interface!\n");
-#endif
-/*printk(">");*/
-	/* Record statistics and return */
-	stats->tx_packets++;
-	dev->tbusy=0;
-	return 0;
-}
-
-static struct enet_statistics *tunnel_get_stats(struct device *dev)
-{
-	return((struct enet_statistics*) dev->priv);
-}
-
-/*
- *	Called when a new tunnel device is initialized.
- *	The new tunnel device structure is passed to us.
- */
- 
-int tunnel_init(struct device *dev)
-{
-	int i;
-
-	/* Oh, just say we're here, in case anyone cares */
-	static int tun_msg=0;
-	if(!tun_msg)
-	{
-		printk ( KERN_INFO "tunnel: version v0.2b2\n" );
-		tun_msg=1;
-	}
-
-	/* Add our tunnel functions to the device */
-	dev->open		= tunnel_open;
-	dev->stop		= tunnel_close;
-	dev->hard_start_xmit	= tunnel_xmit;
-	dev->get_stats		= tunnel_get_stats;
-	dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
-	if (dev->priv == NULL)
-		return -ENOMEM;
-	memset(dev->priv, 0, sizeof(struct enet_statistics));
-
-	/* Initialize the tunnel device structure */
-	for (i = 0; i < DEV_NUMBUFFS; i++)
-		skb_queue_head_init(&dev->buffs[i]);
-
-	dev->hard_header	= NULL;
-	dev->rebuild_header 	= NULL;
-	dev->set_mac_address 	= NULL;
-	dev->header_cache_bind 	= NULL;
-	dev->header_cache_update= NULL;
-
-	dev->type				= ARPHRD_TUNNEL;
-	dev->hard_header_len 	= TUNL_HLEN;
-	dev->mtu		= 1500-tunnel_hlen; 	/* eth_mtu */
-	dev->addr_len		= 0;		/* Is this only for ARP? */
-	dev->tx_queue_len	= 2;		/* Small queue */
-	memset(dev->broadcast,0xFF, ETH_ALEN);
-
-	/* New-style flags. */
-	dev->flags		= IFF_NOARP;	/* Don't use ARP on this device */
-						/* No broadcasting through a tunnel */
-	dev->family		= AF_INET;
-	dev->pa_addr		= 0;
-	dev->pa_brdaddr 	= 0;
-	dev->pa_mask		= 0;
-	dev->pa_alen		= 4;
-
-	/* We're done.  Have I forgotten anything? */
-	return 0;
-}
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-/*  Module specific interface                                      */
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-#ifdef MODULE
-
-static int tunnel_probe(struct device *dev)
-{
-	tunnel_init(dev);
-	return 0;
-}
-
-static struct device dev_tunnel = 
-{
-	"tunl0\0   ", 
-	0, 0, 0, 0,
- 	0x0, 0,
- 	0, 0, 0, NULL, tunnel_probe 
- };
-
-int init_module(void)
-{
-	/* Find a name for this unit */
-	int ct= 1;
-	
-	while(dev_get(dev_tunnel.name)!=NULL && ct<100)
-	{
-		sprintf(dev_tunnel.name,"tunl%d",ct);
-		ct++;
-	}
-	
-#ifdef TUNNEL_DEBUG
-	printk("tunnel: registering device %s\n", dev_tunnel.name);
-#endif
-	if (register_netdev(&dev_tunnel) != 0)
-		return -EIO;
-	return 0;
-}
-
-void cleanup_module(void)
-{
-	unregister_netdev(&dev_tunnel);
-	kfree_s(dev_tunnel.priv,sizeof(struct enet_statistics));
-	dev_tunnel.priv=NULL;
-}
-#endif /* MODULE */
-
diff -u --recursive --new-file v2.1.14/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v2.1.14/linux/drivers/net/plip.c	Fri Aug  9 13:33:52 1996
+++ linux/drivers/net/plip.c	Thu Dec 12 16:51:10 1996
@@ -87,7 +87,7 @@
 */
 
 #include <linux/module.h>
-
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
@@ -143,8 +143,7 @@
 static void plip_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
 /* Functions for DEV methods */
-static int plip_rebuild_header(void *buff, struct device *dev,
-			       unsigned long raddr, struct sk_buff *skb);
+static int plip_rebuild_header(struct sk_buff *skb);
 static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
 static int plip_open(struct device *dev);
 static int plip_close(struct device *dev);
@@ -209,8 +208,7 @@
 	enum plip_connection_state connection;
 	unsigned short timeout_count;
 	char is_deferred;
-	int (*orig_rebuild_header)(void *eth, struct device *dev,
-				   unsigned long raddr, struct sk_buff *skb);
+	int (*orig_rebuild_header)(struct sk_buff *skb);
 };
 
 /* Entry point of PLIP driver.
@@ -415,7 +413,6 @@
 	}
 	rcv->state = PLIP_PK_DONE;
 	if (rcv->skb) {
-		rcv->skb->free = 1;
 		kfree_skb(rcv->skb, FREE_READ);
 		rcv->skb = NULL;
 	}
@@ -857,17 +854,21 @@
 
 /* We don't need to send arp, for plip is point-to-point. */
 static int
-plip_rebuild_header(void *buff, struct device *dev, unsigned long dst,
-		    struct sk_buff *skb)
+plip_rebuild_header(struct sk_buff *skb)
 {
+	struct device *dev = skb->dev;
 	struct net_local *nl = (struct net_local *)dev->priv;
-	struct ethhdr *eth = (struct ethhdr *)buff;
+	struct ethhdr *eth = (struct ethhdr *)skb->data;
 	int i;
 
 	if ((dev->flags & IFF_NOARP)==0)
-		return nl->orig_rebuild_header(buff, dev, dst, skb);
+		return nl->orig_rebuild_header(skb);
 
-	if (eth->h_proto != htons(ETH_P_IP)) {
+	if (eth->h_proto != __constant_htons(ETH_P_IP)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		&& eth->h_proto != __constant_htons(ETH_P_IPV6)
+#endif
+	) {
 		printk("plip_rebuild_header: Don't know how to resolve type %d addresses?\n", (int)eth->h_proto);
 		memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
 		return 0;
@@ -875,7 +876,15 @@
 
 	for (i=0; i < ETH_ALEN - sizeof(u32); i++)
 		eth->h_dest[i] = 0xfc;
+#if 0
 	*(u32 *)(eth->h_dest+i) = dst;
+#else
+	/* Do not want to include net/route.h here.
+	 * In any case, it is TOP of silliness to emulate
+	 * hardware addresses on PtP link. --ANK
+	 */
+	*(u32 *)(eth->h_dest+i) = 0;
+#endif
 	return 0;
 }
 
@@ -1000,7 +1009,6 @@
 	}
 	rcv->state = PLIP_PK_DONE;
 	if (rcv->skb) {
-		rcv->skb->free = 1;
 		kfree_skb(rcv->skb, FREE_READ);
 		rcv->skb = NULL;
 	}
diff -u --recursive --new-file v2.1.14/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.14/linux/drivers/net/ppp.c	Sun Nov 10 20:12:11 1996
+++ linux/drivers/net/ppp.c	Thu Dec 12 16:51:10 1996
@@ -180,10 +180,7 @@
 static int ppp_dev_close (struct device *);
 static int ppp_dev_xmit (sk_buff *, struct device *);
 static struct enet_statistics *ppp_dev_stats (struct device *);
-static int ppp_dev_header (sk_buff *, struct device *, __u16,
-			   void *, void *, unsigned int);
-static int ppp_dev_rebuild (void *eth, struct device *dev,
-                            unsigned long raddr, struct sk_buff *skb);
+
 /*
  * TTY callbacks
  */
@@ -389,8 +386,6 @@
 {
 	int    indx;
 
-	dev->hard_header      = ppp_dev_header;
-	dev->rebuild_header   = ppp_dev_rebuild;
 	dev->hard_header_len  = PPP_HARD_HDR_LEN;
 
 	/* device INFO */
@@ -1209,7 +1204,6 @@
 /*
  * Tag the frame and kick it to the proper receive routine
  */
-	skb->free = 1;
 	ppp->ddinfo.recv_idle = jiffies;
 	netif_rx (skb);
 	return 1;
@@ -3167,20 +3161,6 @@
 	if (ppp->flags & SC_DEBUG)
 		printk (KERN_INFO "ppp_dev_stats called");
 	return &ppp_stats;
-}
-
-static int ppp_dev_header (sk_buff *skb, struct device *dev,
-			   __u16 type, void *daddr,
-			   void *saddr, unsigned int len)
-{
-	return (0);
-}
-
-static int
-ppp_dev_rebuild (void *eth, struct device *dev,
-                 unsigned long raddr, struct sk_buff *skb)
-{
-	return (0);
 }
 
 /*************************************************************
diff -u --recursive --new-file v2.1.14/linux/drivers/net/shaper.c linux/drivers/net/shaper.c
--- v2.1.14/linux/drivers/net/shaper.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/shaper.c	Thu Dec 12 16:51:10 1996
@@ -0,0 +1,695 @@
+/*
+ *			Simple traffic shaper for Linux NET3.
+ *
+ *	(c) Copyright 1996 Alan Cox <alan@cymru.net>, All Rights Reserved.
+ *				http://www.cymru.net
+ *
+ *	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.
+ *	
+ *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
+ *	warranty for any of this software. This material is provided 
+ *	"AS-IS" and at no charge.	
+ *
+ *	
+ *	Algorithm:
+ *
+ *	Queue Frame:
+ *		Compute time length of frame at regulated speed
+ *		Add frame to queue at appropriate point
+ *		Adjust time length computation for followup frames
+ *		Any frame that falls outside of its boundaries is freed
+ *
+ *	We work to the following constants
+ *
+ *		SHAPER_QLEN	Maximum queued frames
+ *		SHAPER_LATENCY	Bounding latency on a frame. Leaving this latency
+ *				window drops the frame. This stops us queueing 
+ *				frames for a long time and confusing a remote
+ *				host.
+ *		SHAPER_MAXSLIP	Maximum time a priority frame may jump forward.
+ *				That bounds the penalty we will inflict on low
+ *				priority traffic.
+ *		SHAPER_BURST	Time range we call "now" in order to reduce
+ *				system load. The more we make this the burstier
+ *				the behaviour, the better local performance you
+ *				get through packet clustering on routers and the
+ *				worse the remote end gets to judge rtts.
+ *
+ *	This is designed to handle lower speed links ( < 200K/second or so). We
+ *	run off a 100-150Hz base clock typically. This gives us a resolution at
+ *	200Kbit/second of about 2Kbit or 256 bytes. Above that our timer
+ *	resolution may start to cause much more burstiness in the traffic. We
+ *	could avoid a lot of that by calling kick_shaper() at the end of the 
+ *	tied device transmissions. If you run above about 100K second you 
+ *	may need to tune the supposed speed rate for the right values.
+ *
+ *	BUGS:
+ *		Downing the interface under the shaper before the shaper
+ *		will render your machine defunct. Don't for now shape over
+ *		PPP or SLIP therefore!
+ *		This will be fixed in BETA4
+ */
+ 
+ 
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <net/dst.h>
+#include "shaper.h"
+
+int sh_debug;		/* Debug flag */
+
+#define SHAPER_BANNER	"CymruNet Traffic Shaper BETA 0.03 for Linux 2.1\n"
+
+/*
+ *	Locking
+ */
+ 
+static int shaper_lock(struct shaper *sh)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	/*
+	 *	Lock in an interrupt may fail
+	 */
+	if(sh->locked && intr_count)
+	{
+		restore_flags(flags);
+		return 0;
+	}
+	while(sh->locked)
+		sleep_on(&sh->wait_queue);
+	sh->locked=1;
+	restore_flags(flags);
+	return 1;
+}
+
+static void shaper_kick(struct shaper *sh);
+
+static void shaper_unlock(struct shaper *sh)
+{
+	sh->locked=0;
+	wake_up(&sh->wait_queue);
+	shaper_kick(sh);
+}
+
+/*
+ *	Compute clocks on a buffer
+ */
+  
+static int shaper_clocks(struct shaper *shaper, struct sk_buff *skb)
+{
+ 	int t=skb->len/shaper->bytespertick;
+ 	return t;
+}
+
+/*
+ *	Set the speed of a shaper. We compute this in bytes per tick since
+ *	thats how the machine wants to run. Quoted input is in bits per second
+ *	as is traditional (note not BAUD). We assume 8 bit bytes. 
+ */
+  
+static void shaper_setspeed(struct shaper *shaper, int bitspersec)
+{
+	shaper->bytespertick=(bitspersec/HZ)/8;
+	if(!shaper->bytespertick)
+		shaper->bytespertick++;
+}
+
+/*
+ *	Throw a frame at a shaper.
+ */
+  
+static int shaper_qframe(struct shaper *shaper, struct sk_buff *skb)
+{
+ 	struct sk_buff *ptr;
+ 	
+ 	/*
+ 	 *	Get ready to work on this shaper. Lock may fail if its
+ 	 *	an interrupt and locked.
+ 	 */
+ 	 
+ 	if(!shaper_lock(shaper))
+ 		return -1;
+ 	ptr=shaper->sendq.prev;
+ 	
+ 	/*
+ 	 *	Set up our packet details
+ 	 */
+ 	 
+ 	skb->shapelatency=0;
+ 	skb->shapeclock=shaper->recovery;
+ 	if(skb->shapeclock<jiffies)
+ 		skb->shapeclock=jiffies;
+ 	skb->priority=0;	/* short term bug fix */
+ 	skb->shapestamp=jiffies;
+ 	
+ 	/*
+ 	 *	Time slots for this packet.
+ 	 */
+ 	 
+ 	skb->shapelen= shaper_clocks(shaper,skb);
+ 	
+#ifdef SHAPER_COMPLEX /* and broken.. */
+
+ 	while(ptr && ptr!=(struct sk_buff *)&shaper->sendq)
+ 	{
+ 		if(ptr->pri<skb->pri 
+ 			&& jiffies - ptr->shapeclock < SHAPER_MAXSLIP)
+ 		{
+ 			struct sk_buff *tmp=ptr->prev;
+
+ 			/*
+ 			 *	It goes before us therefore we slip the length
+ 			 *	of the new frame.
+ 			 */
+
+ 			ptr->shapeclock+=skb->shapelen;
+ 			ptr->shapelatency+=skb->shapelen;
+
+ 			/*
+ 			 *	The packet may have slipped so far back it
+ 			 *	fell off.
+ 			 */
+ 			if(ptr->shapelatency > SHAPER_LATENCY)
+ 			{
+ 				skb_unlink(ptr);
+ 				dev_kfree_skb(ptr, FREE_WRITE);
+ 			}
+ 			ptr=tmp;
+ 		}
+ 		else
+ 			break;
+ 	}
+ 	if(ptr==NULL || ptr==(struct sk_buff *)&shaper->sendq)
+ 		skb_queue_head(&shaper->sendq,skb);
+ 	else
+ 	{
+ 		struct sk_buff *tmp;
+ 		/*
+ 		 *	Set the packet clock out time according to the
+ 		 *	frames ahead. Im sure a bit of thought could drop
+ 		 *	this loop.
+ 		 */
+ 		for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && tmp!=ptr; tmp=tmp->next)
+ 			skb->shapeclock+=tmp->shapelen;
+ 		skb_append(ptr,skb);
+ 	}
+#else
+	{
+		struct sk_buff *tmp;
+		/*
+		 *	Up our shape clock by the time pending on the queue
+		 *	(Should keep this in the shaper as a variable..)
+		 */
+		for(tmp=skb_peek(&shaper->sendq); tmp!=NULL && 
+			tmp!=(struct sk_buff *)&shaper->sendq; tmp=tmp->next)
+			skb->shapeclock+=tmp->shapelen;
+		/*
+		 *	Queue over time. Spill packet.
+		 */
+		if(skb->shapeclock-jiffies > SHAPER_LATENCY)
+			dev_kfree_skb(skb, FREE_WRITE);
+		else
+			skb_queue_tail(&shaper->sendq, skb);
+	}
+#endif 	
+ 	if(sh_debug)
+ 		printk("Frame queued.\n");
+ 	if(skb_queue_len(&shaper->sendq)>SHAPER_QLEN)
+ 	{
+ 		ptr=skb_dequeue(&shaper->sendq);
+ 		dev_kfree_skb(ptr, FREE_WRITE);
+ 	}
+ 	shaper_unlock(shaper);
+ 	shaper_kick(shaper);
+ 	return 0;
+}
+
+/*
+ *	Transmit from a shaper
+ */
+ 
+static void shaper_queue_xmit(struct shaper *shaper, struct sk_buff *skb)
+{
+	struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+	if(sh_debug)
+		printk("Kick frame on %p\n",newskb);
+	if(newskb)
+	{
+		newskb->dev=shaper->dev;
+		newskb->arp=1;
+		newskb->priority=2;
+		if(sh_debug)
+			printk("Kick new frame to %s, %d\n",
+				shaper->dev->name,newskb->priority);
+		dev_queue_xmit(newskb);
+		if(sh_debug)
+			printk("Kicked new frame out.\n");
+		dev_kfree_skb(skb, FREE_WRITE);
+	}
+}
+
+/*
+ *	Timer handler for shaping clock
+ */
+ 
+static void shaper_timer(unsigned long data)
+{
+	struct shaper *sh=(struct shaper *)data;
+	shaper_kick(sh);
+}
+
+/*
+ *	Kick a shaper queue and try and do something sensible with the 
+ *	queue. 
+ */
+
+static void shaper_kick(struct shaper *shaper)
+{
+	struct sk_buff *skb;
+	unsigned long flags;
+	
+	save_flags(flags);
+	cli();
+
+	del_timer(&shaper->timer);
+
+	/*
+	 *	Shaper unlock will kick
+	 */
+	 
+	if(shaper->locked)
+	{	
+		if(sh_debug)
+			printk("Shaper locked.\n");
+		shaper->timer.expires=jiffies+1;
+		add_timer(&shaper->timer);
+		restore_flags(flags);
+		return;
+	}
+
+		
+	/*
+	 *	Walk the list (may be empty)
+	 */
+	 
+	while((skb=skb_peek(&shaper->sendq))!=NULL)
+	{
+		/*
+		 *	Each packet due to go out by now (within an error
+		 *	of SHAPER_BURST) gets kicked onto the link 
+		 */
+		 
+		if(sh_debug)
+			printk("Clock = %d, jiffies = %ld\n", skb->shapeclock, jiffies);
+		if(skb->shapeclock <= jiffies + SHAPER_BURST)
+		{
+			/*
+			 *	Pull the frame and get interrupts back on.
+			 */
+			 
+			skb_unlink(skb);
+			shaper->recovery=jiffies+skb->shapelen;
+			restore_flags(flags);
+
+			/*
+			 *	Pass on to the physical target device via
+			 *	our low level packet thrower.
+			 */
+			
+			skb->shapepend=0;
+			shaper_queue_xmit(shaper, skb);	/* Fire */
+			cli();
+		}
+		else
+			break;
+	}
+
+	/*
+	 *	Next kick.
+	 */
+	 
+	if(skb!=NULL)
+	{
+		del_timer(&shaper->timer);
+		shaper->timer.expires=skb->shapeclock;
+		add_timer(&shaper->timer);
+	}
+		
+	/*
+	 *	Interrupts on, mission complete
+	 */
+		
+	restore_flags(flags);
+}
+
+
+/*
+ *	Flush the shaper queues on a closedown
+ */
+ 
+static void shaper_flush(struct shaper *shaper)
+{
+	struct sk_buff *skb;
+	while((skb=skb_dequeue(&shaper->sendq))!=NULL)
+		dev_kfree_skb(skb, FREE_WRITE);
+}
+
+/*
+ *	Bring the interface up. We just disallow this until a 
+ *	bind.
+ */
+
+static int shaper_open(struct device *dev)
+{
+	struct shaper *shaper=dev->priv;
+	
+	/*
+	 *	Can't open until attached.
+	 */
+	 
+	if(shaper->dev==NULL)
+		return -ENODEV;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Closing a shaper flushes the queues.
+ */
+ 
+static int shaper_close(struct device *dev)
+{
+	struct shaper *shaper=dev->priv;
+	shaper_flush(shaper);
+	del_timer(&shaper->timer);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+/*
+ *	Revectored calls. We alter the parameters and call the functions
+ *	for our attached device. This enables us to bandwidth allocate after
+ *	ARP and other resolutions and not before.
+ */
+
+
+static int shaper_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct shaper *sh=dev->priv;
+	return shaper_qframe(sh, skb);
+}
+
+static struct enet_statistics *shaper_get_stats(struct device *dev)
+{
+	return NULL;
+}
+
+static int shaper_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+	struct shaper *sh=dev->priv;
+	if(sh_debug)
+		printk("Shaper header\n");
+	return sh->hard_header(skb,sh->dev,type,daddr,saddr,len);
+}
+
+static int shaper_rebuild_header(struct sk_buff *skb)
+{
+	struct shaper *sh=skb->dev->priv;
+	if(sh_debug)
+		printk("Shaper rebuild header\n");
+	return sh->rebuild_header(skb);
+}
+
+static int shaper_cache(struct dst_entry *dst, struct dst_entry *neigh, struct hh_cache *hh)
+{
+	struct shaper *sh=dst->dev->priv;
+	struct device *tmp;
+	int ret;
+	if(sh_debug)
+		printk("Shaper header cache bind\n");
+	tmp=dst->dev;
+	dst->dev=sh->dev;
+	ret=sh->hard_header_cache(dst,neigh,hh);
+	dst->dev=tmp;
+	return ret;
+}
+
+static void shaper_cache_update(struct hh_cache *hh, struct device *dev,
+	unsigned char *haddr)
+{
+	struct shaper *sh=dev->priv;
+	if(sh_debug)
+		printk("Shaper cache update\n");
+	sh->header_cache_update(hh, sh->dev, haddr);
+}
+
+static int shaper_attach(struct device *shdev, struct shaper *sh, struct device *dev)
+{
+	sh->dev = dev;
+	sh->hard_start_xmit=dev->hard_start_xmit;
+	sh->get_stats=dev->get_stats;
+	if(dev->hard_header)
+	{
+		sh->hard_header=dev->hard_header;
+		shdev->hard_header = shaper_header;
+	}
+	else
+		shdev->hard_header = NULL;
+		
+	if(dev->rebuild_header)
+	{
+		sh->rebuild_header	= dev->rebuild_header;
+		shdev->rebuild_header	= shaper_rebuild_header;
+	}
+	else
+		shdev->rebuild_header	= NULL;
+	
+	if(dev->hard_header_cache)
+	{
+		sh->hard_header_cache	= dev->hard_header_cache;
+		shdev->hard_header_cache= shaper_cache;
+	}
+	else
+	{
+		shdev->hard_header_cache= NULL;
+	}
+			
+	if(dev->header_cache_update)
+	{
+		sh->header_cache_update	= dev->header_cache_update;
+		shdev->header_cache_update = shaper_cache_update;
+	}
+	else
+		shdev->header_cache_update= NULL;
+	
+	shdev->hard_header_len=dev->hard_header_len;
+	shdev->type=dev->type;
+	shdev->addr_len=dev->addr_len;
+	shdev->mtu=dev->mtu;
+	return 0;
+}
+
+static int shaper_ioctl(struct device *dev,  struct ifreq *ifr, int cmd)
+{
+	struct shaperconf *ss= (struct shaperconf *)&ifr->ifr_data;
+	struct shaper *sh=dev->priv;
+	struct device *them=dev_get(ss->ss_name);
+	switch(ss->ss_cmd)
+	{
+		case SHAPER_SET_DEV:
+			if(them==NULL)
+				return -ENODEV;
+			if(sh->dev)
+				return -EBUSY;
+			return shaper_attach(dev,dev->priv, them);
+		case SHAPER_SET_SPEED:
+			shaper_setspeed(sh,ss->ss_speed);
+			return 0;
+		default:
+			return -EINVAL;
+	}
+}
+
+static struct shaper *shaper_alloc(struct device *dev)
+{
+	struct shaper *sh=kmalloc(sizeof(struct shaper), GFP_KERNEL);
+	if(sh==NULL)
+		return NULL;
+	memset(sh,0,sizeof(*sh));
+	skb_queue_head_init(&sh->sendq);
+	init_timer(&sh->timer);
+	sh->timer.function=shaper_timer;
+	sh->timer.data=(unsigned long)sh;
+	return sh;
+}
+
+/*
+ *	Add a shaper device to the system
+ */
+ 
+int shaper_probe(struct device *dev)
+{
+	int i;
+	
+	/*
+	 *	Set up the shaper.
+	 */
+	
+	dev->priv = shaper_alloc(dev);
+	if(dev->priv==NULL)
+		return -ENOMEM;
+		
+	dev->open		= shaper_open;
+	dev->stop		= shaper_close;
+	dev->hard_start_xmit 	= shaper_start_xmit;
+	dev->get_stats 		= shaper_get_stats;
+	dev->set_multicast_list = NULL;
+	
+	/*
+	 *	Intialise the packet queues
+	 */
+	 
+	for(i=0;i<DEV_NUMBUFFS;i++)
+		skb_queue_head_init(&dev->buffs[i]);
+	
+	/*
+	 *	Handlers for when we attach to a device.
+	 */
+
+	dev->hard_header 	= shaper_header;
+	dev->rebuild_header 	= shaper_rebuild_header;
+	dev->hard_header_cache	= shaper_cache;
+	dev->header_cache_update= shaper_cache_update;
+	dev->do_ioctl		= shaper_ioctl;
+	dev->hard_header_len	= 0;
+	dev->type		= ARPHRD_ETHER;	/* initially */
+	dev->set_mac_address	= NULL;
+	dev->mtu		= 1500;
+	dev->addr_len		= 0;
+	dev->tx_queue_len	= 10;
+	dev->flags		= 0;
+	dev->family		= AF_INET;
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr		= 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= 4;
+		
+	/*
+	 *	Shaper is ok
+	 */	
+	 
+	return 0;
+}
+ 
+#ifdef MODULE
+
+static char devicename[9];
+
+static struct device dev_shape = 
+{
+	devicename,
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+int init_module(void)
+{
+	int i;
+	for(i=0;i<99;i++)
+	{
+		sprintf(devicename,"shaper%d",i);
+		if(dev_get(devicename)==NULL)
+			break;
+	}
+	if(i==100)
+		return -ENFILE;
+	
+	printk(SHAPER_BANNER);	
+	if (register_netdev(&dev_shape) != 0)
+		return -EIO;
+	printk("Traffic shaper initialised.\n");
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	/*
+	 *	No need to check MOD_IN_USE, as sys_delete_module() checks.
+	 *	To be unloadable we must be closed and detached so we don't
+	 *	need to flush things.
+	 */
+	 
+	unregister_netdev(&dev_shape);
+
+	/*
+	 *	Free up the private structure, or leak memory :-) 
+	 */
+	 
+	kfree(dev_shape.priv);
+	dev_shape.priv = NULL;
+}
+
+#else
+
+static struct device dev_sh0 = 
+{
+	"shaper0",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+
+static struct device dev_sh1 = 
+{
+	"shaper1",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+
+static struct device dev_sh2 = 
+{
+	"shaper2",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+static struct device dev_sh3 = 
+{
+	"shaper3",
+	0, 0, 0, 0,
+	0, 0,
+	0, 0, 0, NULL, shaper_probe 
+};
+
+void shaper_init(void)
+{
+	register_netdev(&dev_sh0);
+	register_netdev(&dev_sh1);
+	register_netdev(&dev_sh2);
+	register_netdev(&dev_sh3);
+}
+
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.14/linux/drivers/net/shaper.h linux/drivers/net/shaper.h
--- v2.1.14/linux/drivers/net/shaper.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/shaper.h	Thu Dec 12 16:51:10 1996
@@ -0,0 +1,61 @@
+#ifndef __LINUX_SHAPER_H
+#define __LINUX_SHAPER_H
+
+#ifdef __KERNEL__
+
+#define SHAPER_QLEN	10
+/*
+ *	This is a bit speed dependant (read it shouldnt be a constant!)
+ *
+ *	5 is about right for 28.8 upwards. Below that double for every
+ *	halving of speed or so. - ie about 20 for 9600 baud.
+ */
+#define SHAPER_LATENCY	(5*HZ)
+#define SHAPER_MAXSLIP	2
+#define SHAPER_BURST	(HZ/50)		/* Good for >128K then */
+
+struct shaper
+{
+	struct sk_buff_head sendq;
+	__u32 bytespertick;
+	__u32 shapelatency;
+	__u32 shapeclock;
+	__u32 recovery;		/* Time we can next clock a packet out on
+				   an empty queue */
+	char locked;
+	struct device *dev;
+	int  (*hard_start_xmit) (struct sk_buff *skb,
+		struct device *dev);
+	int  (*hard_header) (struct sk_buff *skb,
+		struct device *dev,
+		unsigned short type,
+		void *daddr,
+		void *saddr,
+		unsigned len);
+	int  (*rebuild_header)(struct sk_buff *skb);
+	int (*hard_header_cache)(struct dst_entry *dst, struct dst_entry *neigh,
+		struct hh_cache *hh);
+	void (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char *  haddr);
+	struct enet_statistics* (*get_stats)(struct device *dev);
+	struct wait_queue *wait_queue;
+	struct timer_list timer;
+};
+
+#endif
+
+#define SHAPER_SET_DEV		0x0001
+#define SHAPER_SET_SPEED	0x0002
+
+struct shaperconf
+{
+	__u16	ss_cmd;
+	union
+	{
+		char 	ssu_name[14];
+		__u32	ssu_speed;
+	} ss_u;
+#define ss_speed ss_u.ssu_speed
+#define ss_name ss_u.ssu_name
+};
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.14/linux/drivers/net/slip.c	Tue Nov 12 15:56:07 1996
+++ linux/drivers/net/slip.c	Thu Dec 12 16:51:10 1996
@@ -517,24 +517,6 @@
 
 /* Return the frame type ID.  This is normally IP but maybe be AX.25. */
 
-/* Fill in the MAC-level header. Not used by SLIP. */
-static int
-sl_header(struct sk_buff *skb, struct device *dev, unsigned short type,
-	  void *daddr, void *saddr, unsigned len)
-{
-	return 0;
-}
-
-
-/* Rebuild the MAC-level header.  Not used by SLIP. */
-static int
-sl_rebuild_header(void *buff, struct device *dev, unsigned long raddr,
-		  struct sk_buff *skb)
-{
-	return 0;
-}
-
-
 /* Open the low-level part of the SLIP channel. Easy! */
 static int
 sl_open(struct device *dev)
@@ -1180,20 +1162,18 @@
 	dev->hard_start_xmit	= sl_xmit;
 	dev->open		= sl_open_dev;
 	dev->stop		= sl_close;
-	dev->hard_header	= sl_header;
 	dev->get_stats	        = sl_get_stats;
 	dev->hard_header_len	= 0;
 	dev->addr_len		= 0;
 	dev->type		= ARPHRD_SLIP + SL_MODE_DEFAULT;
 	dev->tx_queue_len	= 10;
-	dev->rebuild_header	= sl_rebuild_header;
 
 	for (i = 0; i < DEV_NUMBUFFS; i++)  {
 		skb_queue_head_init(&dev->buffs[i]);
 	}
 
 	/* New-style flags. */
-	dev->flags		= 0;
+	dev->flags		= IFF_NOARP|IFF_MULTICAST;
 	dev->family		= AF_INET;
 	dev->pa_addr		= 0;
 	dev->pa_brdaddr	        = 0;
diff -u --recursive --new-file v2.1.14/linux/drivers/net/smc-mca.c linux/drivers/net/smc-mca.c
--- v2.1.14/linux/drivers/net/smc-mca.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/smc-mca.c	Thu Dec 12 16:51:10 1996
@@ -0,0 +1,382 @@
+/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/*
+    Most of this driver, except for ultramca_probe is nearly
+    verbatim from smc-ultra.c by Donald Becker. The rest is 
+    written and copyright 1996 by David Weis, weisd3458@uni.edu
+
+    This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
+
+    This driver uses the cards in the 8390-compatible, shared memory mode.
+    Most of the run-time complexity is handled by the generic code in
+    8390.c.  The code in this file is responsible for
+
+    This driver enables the shared memory only when doing the actual data
+    transfers to avoid a bug in early version of the card that corrupted
+    data transferred by a AHA1542.
+
+    This driver does not support the programmed-I/O data transfer mode of
+    the EtherEZ.  That support (if available) is smc-ez.c.  Nor does it
+    use the non-8390-compatible "Altego" mode. (No support currently planned.)
+
+    Changelog:
+
+    Paul Gortmaker  : multiple card support for module users.
+    David Weis      : Micro Channel-ized it.
+
+*/
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+#include "smc-mca.h"
+#include <linux/mca.h>
+
+int ultramca_probe(struct device *dev);
+
+static int ultramca_open(struct device *dev);
+static void ultramca_reset_8390(struct device *dev);
+static void ultramca_get_8390_hdr(struct device *dev,
+                                  struct e8390_pkt_hdr *hdr, 
+                                  int ring_page);
+static void ultramca_block_input(struct device *dev, int count,
+                                 struct sk_buff *skb, 
+                                 int ring_offset);
+static void ultramca_block_output(struct device *dev, int count,
+                                  const unsigned char *buf,
+                                  const start_page);
+static int ultramca_close_card(struct device *dev);
+
+#define START_PG        0x00    /* First page of TX buffer */
+
+#define ULTRA_CMDREG 0      /* Offset to ASIC command register. */
+#define ULTRA_RESET  0x80   /* Board reset, in ULTRA_CMDREG. */
+#define ULTRA_MEMENB 0x40   /* Enable the shared memory. */
+#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+#define EN0_ERWCNT      0x08  /* Early receive warning count. */
+
+
+int ultramca_probe(struct device *dev)
+{
+	unsigned short ioaddr;
+	unsigned char reg4, num_pages;
+	char slot;
+	unsigned char pos2, pos3, pos4, pos5;
+	int i;
+
+	if( (slot=mca_find_adapter(0x61c8,0)) != MCA_NOTFOUND) 
+	{
+#ifndef MODULE
+		mca_set_adapter_name( slot, "SMC Elite/A (8013EP/A)" );
+#endif
+	} 
+	else if( (slot=mca_find_adapter(0x61c9,0)) != MCA_NOTFOUND) 
+	{
+#ifndef MODULE
+		mca_set_adapter_name( slot, "SMC Elite10T/A (8013WP/A)" );
+#endif
+	} 
+	else
+		return -ENODEV;
+
+	pos2 = mca_read_stored_pos(slot, 2);     /* IO range */
+	pos3 = mca_read_stored_pos(slot, 3);     /* shared mem */
+	pos4 = mca_read_stored_pos(slot, 4);     /* bios base */
+	pos5 = mca_read_stored_pos(slot, 5);     /* irq and media */
+
+	dev->base_addr = ioaddr = addr_table[pos2 >> 4].base_addr;
+	dev->irq = irq_table[(pos5 & ~IRQ_MASK) >> 2].irq;
+
+	dev->mem_start = 0;
+	num_pages = 40;
+	for (i = 0; i < 15; i++) 
+	{
+		if (mem_table[i].mem_index == (pos3 & ~MEM_MASK)) 
+		{
+			dev->mem_start = mem_table[i].mem_start;
+			num_pages = mem_table[i].num_pages;
+		}
+	}
+
+	if (dev->mem_start == 0)      /* sanity check, shouldn't happen */
+		return -ENODEV;
+
+	reg4 = inb(ioaddr + 4) & 0x7f;
+	outb(reg4, ioaddr + 4);
+
+	printk("%s: SMC Ultra MCA at %#3x,", dev->name, ioaddr);
+
+	for (i = 0; i < 6; i++)
+		printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+	/*
+	 *	Switch from the station address to the alternate register set and
+	 *	read the useful registers there. 
+	 */
+
+	outb(0x80 | reg4, ioaddr + 4);
+
+	/*
+	 *	Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot. 
+	 */
+
+	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+  
+	/*
+	 *	Switch back to the station address register set so that the MS-DOS driver
+	 *	can find the card after a warm boot. 
+	 */
+
+	outb(reg4, ioaddr + 4);
+
+	/*
+	 *	Allocate dev->priv and fill in 8390 specific dev fields. 
+	 */
+
+	if (ethdev_init(dev)) 
+	{
+		printk (", no memory for dev->priv.\n");
+		return -ENOMEM;
+	}
+ 
+	/*
+	 *	OK, we are certain this is going to work.  Setup the device. 
+	 */
+
+	request_region(ioaddr, ULTRA_IO_EXTENT, "smc-mca");
+
+	/*
+	 *	The 8390 isn't at the base address, so fake the offset 
+	 */
+
+	dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+
+	ei_status.name = "SMC Ultra MCA";
+	ei_status.word16 = 1;
+	ei_status.tx_start_page = START_PG;
+	ei_status.rx_start_page = START_PG + TX_PAGES;
+	ei_status.stop_page = num_pages;
+
+	dev->rmem_start = dev->mem_start + TX_PAGES*256;
+	dev->mem_end = dev->rmem_end = 
+	dev->mem_start + (ei_status.stop_page - START_PG)*256;
+
+	printk(", IRQ %d memory %#lx-%#lx.\n", dev->irq, dev->mem_start, dev->mem_end-1);
+
+	ei_status.reset_8390 = &ultramca_reset_8390;
+	ei_status.block_input = &ultramca_block_input;
+	ei_status.block_output = &ultramca_block_output;
+	ei_status.get_8390_hdr = &ultramca_get_8390_hdr;
+	dev->open = &ultramca_open;
+	dev->stop = &ultramca_close_card;
+	NS8390_init(dev, 0);
+
+	return 0;
+}
+
+static int ultramca_open(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL))
+	return -EAGAIN;
+
+	outb(ULTRA_MEMENB, ioaddr); /* Enable memory */
+	outb(0x80, ioaddr + 5);     /* ??? */
+	outb(0x01, ioaddr + 6);     /* Enable interrupts and memory. */
+	outb(0x04, ioaddr + 5);     /* ??? */
+
+	/*
+	 *	Set the early receive warning level in window 0 high enough not
+	 *	to receive ERW interrupts. 
+	 */
+
+	/*
+	 *	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+	 *	outb(0xff, dev->base_addr + EN0_ERWCNT);
+	 */
+
+	ei_open(dev);
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void ultramca_reset_8390(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+	outb(ULTRA_RESET, ioaddr);
+	if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
+	ei_status.txing = 0;
+
+	outb(0x80, ioaddr + 5);     /* ??? */
+	outb(0x01, ioaddr + 6);     /* Enable interrupts and memory. */
+
+	if (ei_debug > 1)
+		printk("reset done\n");
+	return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+   we don't need to be concerned with ring wrap as the header will be at
+   the start of a page, so we optimize accordingly. */
+
+static void ultramca_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+	unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8);
+
+#ifdef notdef
+	/* Officially this is what we are doing, but the readl() is faster */
+	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+	((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+   complication is when the ring buffer wraps. */
+
+static void ultramca_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+	unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG<<8);
+
+	if (xfer_start + count > dev->rmem_end) 
+	{
+        	/* We must wrap the input move. */
+		int semi_count = dev->rmem_end - xfer_start;
+		memcpy_fromio(skb->data, xfer_start, semi_count);
+		count -= semi_count;
+		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+	}
+	else
+	{
+		/* Packet is in one chunk -- we can copy + cksum. */
+		eth_io_copy_and_sum(skb, xfer_start, count, 0);
+	}
+
+}
+
+static void ultramca_block_output(struct device *dev, int count, const unsigned char *buf,
+                int start_page)
+{
+	unsigned long shmem = dev->mem_start + ((start_page - START_PG)<<8);
+
+	memcpy_toio(shmem, buf, count);
+}
+
+static int ultramca_close_card(struct device *dev)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (ei_debug > 1)
+		printk("%s: Shutting down ethercard.\n", dev->name);
+
+	outb(0x00, ioaddr + 6);     /* Disable interrupts. */
+	free_irq(dev->irq, NULL);
+	irq2dev_map[dev->irq] = 0;
+
+	NS8390_init(dev, 0);
+	/* We should someday disable shared memory and change to 8-bit mode
+       "just in case"... */
+
+	MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+
+#ifdef MODULE
+#undef MODULE        /* don't want to bother now! */
+
+#define MAX_ULTRAMCA_CARDS  4   /* Max number of Ultra cards per module */
+#define NAMELEN     8   /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, };
+
+static struct device dev_ultra[MAX_ULTRA_CARDS] = 
+{
+	{
+		NULL,       /* assign a chunk of namelist[] below */
+	        0, 0, 0, 0,
+	        0, 0,
+	        0, 0, 0, NULL, NULL
+	},
+};
+
+static int io[MAX_ULTRA_CARDS] = { 0, };
+static int irq[MAX_ULTRA_CARDS]  = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+
+int init_module(void)
+{
+	int this_dev, found = 0;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) 
+	{
+		struct device *dev = &dev_ultra[this_dev];
+		dev->name = namelist+(NAMELEN*this_dev);
+		dev->irq = irq[this_dev];
+		dev->base_addr = io[this_dev];
+		dev->init = ultra_probe;
+		if (io[this_dev] == 0)  
+		{	
+			if (this_dev != 0) 
+				break; /* only autoprobe 1st one */
+			printk(KERN_NOTICE "smc-mca.c: Presently autoprobing (not recommended) for a single card.\n");
+		}
+		if (register_netdev(dev) != 0) 
+		{
+			printk(KERN_WARNING "smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+			if (found != 0) 
+				return 0;   /* Got at least one. */
+			return -ENXIO;
+		}
+		found++;
+	}
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	int this_dev;
+
+	for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) 
+	{
+		struct device *dev = &dev_ultra[this_dev];
+        	if (dev->priv != NULL) 
+        	{
+			/* NB: ultra_close_card() does free_irq + irq2dev */
+			int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+			kfree(dev->priv);
+			dev->priv = NULL;
+			release_region(ioaddr, ULTRA_IO_EXTENT);
+			unregister_netdev(dev);
+		}
+	}
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-mca.c"
+ *  version-control: t
+ *  kept-new-versions: 5
+ *  c-indent-level: 8
+ *  tab-width: 8
+ * End:
+ */
diff -u --recursive --new-file v2.1.14/linux/drivers/net/smc-mca.h linux/drivers/net/smc-mca.h
--- v2.1.14/linux/drivers/net/smc-mca.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/smc-mca.h	Thu Dec 12 16:51:10 1996
@@ -0,0 +1,62 @@
+/* 
+   djweis weisd3458@uni.edu 
+   most of this file was taken from ps2esdi.h
+*/
+
+struct {
+  unsigned int base_addr;
+} addr_table[] = {
+    { 0x0800 },
+    { 0x1800 },
+    { 0x2800 },
+    { 0x3800 },
+    { 0x4800 },
+    { 0x5800 },
+    { 0x6800 },
+    { 0x7800 },
+    { 0x8800 },
+    { 0x9800 },
+    { 0xa800 },
+    { 0xb800 },
+    { 0xc800 },
+    { 0xd800 },
+    { 0xe800 },
+    { 0xf800 }
+};
+
+#define MEM_MASK 64
+struct {
+  unsigned char mem_index;
+  unsigned long mem_start;
+  unsigned char num_pages;
+} mem_table[] = {
+    { 16, 0x0c0000, 40 },
+    { 18, 0x0c4000, 40 },
+    { 20, 0x0c8000, 40 },
+    { 22, 0x0cc000, 40 },
+    { 24, 0x0d0000, 40 },
+    { 26, 0x0d4000, 40 },
+    { 28, 0x0d8000, 40 },
+    { 30, 0x0dc000, 40 },
+    {144, 0xfc0000, 40 },
+    {148, 0xfc8000, 40 },
+    {154, 0xfd0000, 40 },
+    {156, 0xfd8000, 40 },
+    {  0, 0x0c0000, 20 },
+    {  1, 0x0c2000, 20 },
+    {  2, 0x0c4000, 20 },
+    {  3, 0x0c6000, 20 }
+};
+
+#define IRQ_MASK 243
+struct {
+   unsigned char irq;
+} irq_table[] = {
+   {  3 }, 
+   {  4 },
+   { 10 },
+   { 14 }
+};
+
+ 
+   
diff -u --recursive --new-file v2.1.14/linux/drivers/net/tunnel.c linux/drivers/net/tunnel.c
--- v2.1.14/linux/drivers/net/tunnel.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/tunnel.c	Thu Dec 12 16:51:10 1996
@@ -0,0 +1,313 @@
+/* tunnel.c: an IP tunnel driver
+
+	The purpose of this driver is to provide an IP tunnel through
+	which you can tunnel network traffic transparently across subnets.
+
+	This was written by looking at Nick Holloway's dummy driver
+	Thanks for the great code!
+
+		-Sam Lantinga	(slouken@cs.ucdavis.edu)  02/01/95
+		
+	Minor tweaks:
+		Cleaned up the code a little and added some pre-1.3.0 tweaks.
+		dev->hard_header/hard_header_len changed to use no headers.
+		Comments/bracketing tweaked.
+		Made the tunnels use dev->name not tunnel: when error reporting.
+		Added tx_dropped stat
+		
+		-Alan Cox	(Alan.Cox@linux.org) 21 March 95
+
+	Reworked:
+		Changed to tunnel to destination gateway in addition to the
+			tunnel's pointopoint address
+		Almost completely rewritten
+		Note:  There is currently no firewall or ICMP handling done.
+
+		-Sam Lantinga	(slouken@cs.ucdavis.edu) 02/13/96
+		
+*/
+
+/* Things I wish I had known when writing the tunnel driver:
+
+	When the tunnel_xmit() function is called, the skb contains the
+	packet to be sent (plus a great deal of extra info), and dev
+	contains the tunnel device that _we_ are.
+
+	When we are passed a packet, we are expected to fill in the
+	source address with our source IP address.
+
+	What is the proper way to allocate, copy and free a buffer?
+	After you allocate it, it is a "0 length" chunk of memory
+	starting at zero.  If you want to add headers to the buffer
+	later, you'll have to call "skb_reserve(skb, amount)" with
+	the amount of memory you want reserved.  Then, you call
+	"skb_put(skb, amount)" with the amount of space you want in
+	the buffer.  skb_put() returns a pointer to the top (#0) of
+	that buffer.  skb->len is set to the amount of space you have
+	"allocated" with skb_put().  You can then write up to skb->len
+	bytes to that buffer.  If you need more, you can call skb_put()
+	again with the additional amount of space you need.  You can
+	find out how much more space you can allocate by calling 
+	"skb_tailroom(skb)".
+	Now, to add header space, call "skb_push(skb, header_len)".
+	This creates space at the beginning of the buffer and returns
+	a pointer to this new space.  If later you need to strip a
+	header from a buffer, call "skb_pull(skb, header_len)".
+	skb_headroom() will return how much space is left at the top
+	of the buffer (before the main data).  Remember, this headroom
+	space must be reserved before the skb_put() function is called.
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <net/ip.h>
+#include <linux/if_arp.h>
+
+/*#define TUNNEL_DEBUG*/
+
+/* 
+ *	Our header is a simple IP packet with no options
+ */
+ 
+#define tunnel_hlen	sizeof(struct iphdr)
+
+/*
+ *	Okay, this needs to be high enough that we can fit a "standard"
+ *	ethernet header and an IP tunnel header into the outgoing packet.
+ *	[36 bytes]
+ */
+ 
+#define TUNL_HLEN	(((ETH_HLEN+15)&~15)+tunnel_hlen)
+
+
+static int tunnel_open(struct device *dev)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int tunnel_close(struct device *dev)
+{
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+#ifdef TUNNEL_DEBUG
+void print_ip(struct iphdr *ip)
+{
+	unsigned char *ipaddr;
+
+	printk("IP packet:\n");
+	printk("--- header len = %d\n", ip->ihl*4);
+	printk("--- ip version: %d\n", ip->version);
+	printk("--- ip protocol: %d\n", ip->protocol);
+	ipaddr=(unsigned char *)&ip->saddr;
+	printk("--- source address: %u.%u.%u.%u\n", 
+			*ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
+	ipaddr=(unsigned char *)&ip->daddr;
+	printk("--- destination address: %u.%u.%u.%u\n", 
+			*ipaddr, *(ipaddr+1), *(ipaddr+2), *(ipaddr+3));
+	printk("--- total packet len: %d\n", ntohs(ip->tot_len));
+}
+#endif
+
+/*
+ *	This function assumes it is being called from dev_queue_xmit()
+ *	and that skb is filled properly by that function.
+ */
+
+static int tunnel_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct enet_statistics *stats;		/* This device's statistics */
+	struct rtable *rt;     			/* Route to the other host */
+	struct hh_cache *hh;
+	struct device *tdev;			/* Device to other host */
+	struct iphdr  *iph;			/* Our new IP header */
+	int    max_headroom;			/* The extra header space needed */
+
+	stats = (struct enet_statistics *)dev->priv;
+  
+	/*
+	 *  First things first.  Look up the destination address in the 
+	 *  routing tables
+	 */
+	iph = skb->nh.iph;
+
+	if (ip_route_output(&rt, dev->pa_dstaddr, dev->pa_addr, RT_TOS(iph->tos), NULL)) {
+		/* No route to host */
+		printk ( KERN_INFO "%s: Can't reach target gateway!\n", dev->name);
+		stats->tx_errors++;
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+	tdev = rt->u.dst.dev;
+	hh = rt->u.dst.hh;
+
+	if (tdev->type == ARPHRD_TUNNEL) { 
+		/* Tunnel to tunnel?  -- I don't think so. */
+		printk ( KERN_INFO "%s: Packet targetted at myself!\n" , dev->name);
+		ip_rt_put(rt);
+		stats->tx_errors++;
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
+	}
+
+	skb->h.ipiph = skb->nh.iph;
+
+	/*
+	 * Okay, now see if we can stuff it in the buffer as-is.
+	 */
+	max_headroom = (((tdev->hard_header_len+15)&~15)+tunnel_hlen);
+
+	if (skb_headroom(skb) < max_headroom || skb->users != 1) {
+		struct sk_buff *new_skb = skb_realloc_headroom(skb, max_headroom);
+		if (!new_skb) {
+			ip_rt_put(rt);
+  			stats->tx_dropped++;
+			dev_kfree_skb(skb, FREE_WRITE);
+			return 0;
+		}
+		dev_kfree_skb(skb, FREE_WRITE);
+		skb = new_skb;
+	}
+
+	skb->nh.iph = (struct iphdr *) skb_push(skb, tunnel_hlen);
+	dst_release(skb->dst);
+	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+	dst_release(skb->dst);
+	skb->dst = &rt->u.dst;
+
+	/*
+	 *	Push down and install the IPIP header.
+	 */
+	 
+	iph 			=	skb->nh.iph;
+	iph->version		= 	4;
+	iph->tos		=	skb->h.ipiph->tos;
+	iph->ttl		=	skb->h.ipiph->ttl;
+	iph->frag_off		=	0;
+	iph->daddr		=	dev->pa_dstaddr;
+	iph->saddr		=	dev->pa_addr;
+	iph->protocol		=	IPPROTO_IPIP;
+	iph->ihl		=	5;
+	iph->tot_len		=	htons(skb->len);
+	iph->id			=	htons(ip_id_count++);	/* Race condition here? */
+	ip_send_check(iph);
+
+	ip_send(skb);
+
+	/* Record statistics and return */
+	stats->tx_packets++;
+	return 0;
+}
+
+static struct enet_statistics *tunnel_get_stats(struct device *dev)
+{
+	return((struct enet_statistics*) dev->priv);
+}
+
+/*
+ *	Called when a new tunnel device is initialized.
+ *	The new tunnel device structure is passed to us.
+ */
+ 
+int tunnel_init(struct device *dev)
+{
+	int i;
+
+	/* Oh, just say we're here, in case anyone cares */
+	static int tun_msg=0;
+	if(!tun_msg)
+	{
+		printk ( KERN_INFO "tunnel: version v0.2b2\n" );
+		tun_msg=1;
+	}
+
+	/* Add our tunnel functions to the device */
+	dev->open		= tunnel_open;
+	dev->stop		= tunnel_close;
+	dev->hard_start_xmit	= tunnel_xmit;
+	dev->get_stats		= tunnel_get_stats;
+	dev->priv = kmalloc(sizeof(struct enet_statistics), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	memset(dev->priv, 0, sizeof(struct enet_statistics));
+
+	/* Initialize the tunnel device structure */
+	for (i = 0; i < DEV_NUMBUFFS; i++)
+		skb_queue_head_init(&dev->buffs[i]);
+
+	dev->hard_header	= NULL;
+	dev->rebuild_header 	= NULL;
+	dev->set_mac_address 	= NULL;
+	dev->hard_header_cache 	= NULL;
+	dev->header_cache_update= NULL;
+
+	dev->type				= ARPHRD_TUNNEL;
+	dev->hard_header_len 	= TUNL_HLEN;
+	dev->mtu		= 1500-tunnel_hlen; 	/* eth_mtu */
+	dev->addr_len		= 0;		/* Is this only for ARP? */
+	dev->tx_queue_len	= 2;		/* Small queue */
+	memset(dev->broadcast,0xFF, ETH_ALEN);
+
+	/* New-style flags. */
+	dev->flags		= IFF_NOARP;	/* Don't use ARP on this device */
+						/* No broadcasting through a tunnel */
+	dev->family		= AF_INET;
+	dev->pa_addr		= 0;
+	dev->pa_brdaddr 	= 0;
+	dev->pa_mask		= 0;
+	dev->pa_alen		= 4;
+
+	/* We're done.  Have I forgotten anything? */
+	return 0;
+}
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+/*  Module specific interface                                      */
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#ifdef MODULE
+
+static int tunnel_probe(struct device *dev)
+{
+	tunnel_init(dev);
+	return 0;
+}
+
+static struct device dev_tunnel = 
+{
+	"tunl0\0   ", 
+	0, 0, 0, 0,
+ 	0x0, 0,
+ 	0, 0, 0, NULL, tunnel_probe 
+ };
+
+int init_module(void)
+{
+	/* Find a name for this unit */
+	int ct= 1;
+	
+	while(dev_get(dev_tunnel.name)!=NULL && ct<100)
+	{
+		sprintf(dev_tunnel.name,"tunl%d",ct);
+		ct++;
+	}
+	
+#ifdef TUNNEL_DEBUG
+	printk("tunnel: registering device %s\n", dev_tunnel.name);
+#endif
+	if (register_netdev(&dev_tunnel) != 0)
+		return -EIO;
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unregister_netdev(&dev_tunnel);
+	kfree_s(dev_tunnel.priv,sizeof(struct enet_statistics));
+	dev_tunnel.priv=NULL;
+}
+#endif /* MODULE */
+
diff -u --recursive --new-file v2.1.14/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.1.14/linux/drivers/net/wavelan.c	Thu Dec 12 17:02:43 1996
+++ linux/drivers/net/wavelan.c	Tue Dec  3 09:24:16 1996
@@ -1,2490 +1,4213 @@
 /*
+ *	Wavelan ISA driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyrigth follow (see also end of this file).
+ * See wavelan.p.h for details.
+ */
+
+/*
  * AT&T GIS (nee NCR) WaveLAN card:
  *	An Ethernet-like radio transceiver
  *	controlled by an Intel 82586 coprocessor.
  */
 
-#include	<linux/module.h>
-
-#include	<linux/kernel.h>
-#include	<linux/sched.h>
-#include	<linux/types.h>
-#include	<linux/fcntl.h>
-#include	<linux/interrupt.h>
-#include	<linux/stat.h>
-#include	<linux/ptrace.h>
-#include	<linux/ioport.h>
-#include	<linux/in.h>
-#include	<linux/string.h>
-#include	<linux/delay.h>
-#include	<asm/system.h>
-#include	<asm/bitops.h>
-#include	<asm/io.h>
-#include	<asm/dma.h>
-#include	<linux/errno.h>
-#include	<linux/netdevice.h>
-#include	<linux/etherdevice.h>
-#include	<linux/skbuff.h>
-#include	<linux/malloc.h>
-#include	<linux/timer.h>
-#include	<linux/proc_fs.h>
-#define	STRUCT_CHECK	1
-#include	"i82586.h"
-#include	"wavelan.h"
-
-#ifndef WAVELAN_DEBUG
-#define WAVELAN_DEBUG			0
-#endif	/* WAVELAN_DEBUG */
-
-#define	WATCHDOG_JIFFIES		512	/* TODO: express in HZ. */
-#define	ENABLE_FULL_PROMISCUOUS		0x10000
-
-#define	nels(a)				(sizeof(a) / sizeof(a[0]))
-
-typedef struct device		device;
-typedef struct enet_statistics	en_stats;
-typedef struct net_local	net_local;
-typedef struct timer_list	timer_list;
-
-struct net_local
-{
-	en_stats	stats;
-	unsigned int	tx_n_in_use;
-	unsigned char	nwid[2];
-	unsigned short	hacr;
-	unsigned short	rx_head;
-	unsigned short	rx_last;
-	unsigned short	tx_first_free;
-	unsigned short	tx_first_in_use;
-	unsigned int	nresets;
-	unsigned int	correct_nwid;
-	unsigned int	wrong_nwid;
-	unsigned int	promiscuous;
-	unsigned int	full_promiscuous;
-	timer_list	watchdog;
-	device		*dev;
-	net_local	*prev;
-	net_local	*next;
-};
-
-extern int		wavelan_probe(device *);	/* See Space.c */
-
-static const char	*version	= "wavelan.c:v9 96/11/17\n";
-
-/*
- * Entry point forward declarations.
- */
-static int		wavelan_probe1(device *, unsigned short);
-static int		wavelan_open(device *);
-static int		wavelan_send_packet(struct sk_buff *, device *);
-static void		wavelan_interrupt(int, void *, struct pt_regs *);
-static int		wavelan_close(device *);
-static en_stats		*wavelan_get_stats(device *);
-static void		wavelan_set_multicast_list(device *);
-static int		wavelan_get_info(char*, char**, off_t, int, int);
-
-/*
- * Other forward declarations.
- */
-static void		wavelan_cu_show_one(device *, net_local *, int, unsigned short);
-static void		wavelan_cu_start(device *);
-static void		wavelan_ru_start(device *);
-static void		wavelan_watchdog(unsigned long);
-#if	0
-static void		wavelan_psa_show(psa_t *);
-static void		wavelan_mmc_show(unsigned short);
-#endif	/* 0 */
-static void		wavelan_scb_show(unsigned short);
-static void		wavelan_ru_show(device *);
-static void		wavelan_cu_show(device *);
-static void		wavelan_dev_show(device *);
-static void		wavelan_local_show(device *);
+#include "wavelan.p.h"		/* Private header */
 
-static unsigned int	wavelan_debug	= WAVELAN_DEBUG;
-static net_local	*first_wavelan	= (net_local *)0;
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (wavelan modem or i82586)
+ */
 
-static
-unsigned long
-wavelan_splhi(void)
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for disabling interrupts.
+ */
+static inline unsigned long
+wv_splhi(void)
 {
-	unsigned long	flags;
+  unsigned long flags;
 
-	save_flags(flags);
-	cli();
-
-	return flags;
-}
+  save_flags(flags);
+  cli();
 
-static
-void
-wavelan_splx(unsigned long flags)
-{
-	restore_flags(flags);
+  return(flags);
 }
 
-static
-unsigned short
-hasr_read(unsigned short ioaddr)
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts.
+ */
+static inline void
+wv_splx(unsigned long	flags)
 {
-	return inw(HASR(ioaddr));
+  restore_flags(flags);
 }
 
-static
-void
-hacr_write(unsigned short ioaddr, int hacr)
+/*------------------------------------------------------------------*/
+/*
+ * Translate irq number to PSA irq parameter
+ */
+static u_char
+wv_irq_to_psa(int	irq)
 {
-	outw(hacr, HACR(ioaddr));
-}
+  if(irq < 0 || irq >= NELS(irqvals))
+    return 0;
 
-static
-void
-hacr_write_slow(unsigned short ioaddr, int hacr)
-{
-	hacr_write(ioaddr, hacr);
-	/* delay might only be needed sometimes */
-	udelay(1000);
+  return irqvals[irq];
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Set the channel attention bit.
+ * Translate PSA irq parameter to irq number 
  */
-static
-void
-set_chan_attn(unsigned short ioaddr, unsigned short current_hacr)
+static int
+wv_psa_to_irq(u_char	irqval)
 {
-	hacr_write(ioaddr, current_hacr | HACR_CA);
+  int	irq;
+
+  for(irq = 0; irq < NELS(irqvals); irq++)
+    if(irqvals[irq] == irqval)
+      return irq;
+
+  return -1;
 }
 
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
 /*
- * Reset, and then set host adaptor into default mode.
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
  */
-static
-void
-wavelan_reset(unsigned short ioaddr)
+static char *
+wv_struct_check(void)
 {
-	hacr_write_slow(ioaddr, HACR_RESET);
-	hacr_write(ioaddr, HACR_DEFAULT);
-}
+#define	SC(t,s,n)	if (sizeof(t) != s) return(n);
 
-static
-void
-wavelan_16_off(unsigned short ioaddr, unsigned short hacr)
-{
-	hacr &= ~HACR_16BITS;
+  SC(psa_t, PSA_SIZE, "psa_t");
+  SC(mmw_t, MMW_SIZE, "mmw_t");
+  SC(mmr_t, MMR_SIZE, "mmr_t");
+  SC(ha_t, HA_SIZE, "ha_t");
 
-	hacr_write(ioaddr, hacr);
-}
+#undef	SC
 
-static
-void
-wavelan_16_on(unsigned short ioaddr, unsigned short hacr)
-{
-	hacr |= HACR_16BITS;
+  return((char *) NULL);
+} /* wv_structuct_check */
+#endif	/* STRUCT_CHECK */
 
-	hacr_write(ioaddr, hacr);
-}
+/********************* HOST ADAPTER SUBROUTINES *********************/
+/*
+ * Usefull subroutines to manage the wavelan ISA interface
+ *
+ * One major difference with the Pcmcia hardware (exept the port mapping)
+ * is that we have to keep the state of the Host Control Register
+ * because of the interrupt enable & bus size flags.
+ */
 
-static
-void
-wavelan_ints_off(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u_short
+hasr_read(u_short	ioaddr)
 {
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned long	x;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
+  return(inw(HASR(ioaddr)));
+} /* hasr_read */
 
-	x = wavelan_splhi();
-
-	lp->hacr &= ~HACR_INTRON;
-	hacr_write(ioaddr, lp->hacr);
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register.
+ */
+static inline void
+hacr_write(u_short	ioaddr,
+	   u_short	hacr)
+{
+  outw(hacr, HACR(ioaddr));
+} /* hacr_write */
 
-	wavelan_splx(x);
-}
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void
+hacr_write_slow(u_short	ioaddr,
+		u_short	hacr)
+{
+  hacr_write(ioaddr, hacr);
+  /* delay might only be needed sometimes */
+  udelay(1000L);
+} /* hacr_write_slow */
 
-static
-void
-wavelan_ints_on(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Set the channel attention bit.
+ */
+static inline void
+set_chan_attn(u_short	ioaddr,
+	      u_short	hacr)
 {
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned long	x;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
+  hacr_write(ioaddr, hacr | HACR_CA);
+} /* set_chan_attn */
 
-	x = wavelan_splhi();
+/*------------------------------------------------------------------*/
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static inline void
+wv_hacr_reset(u_short	ioaddr)
+{
+  hacr_write_slow(ioaddr, HACR_RESET);
+  hacr_write(ioaddr, HACR_DEFAULT);
+} /* wv_hacr_reset */
 
-	lp->hacr |= HACR_INTRON;
-	hacr_write(ioaddr, lp->hacr);
+/*------------------------------------------------------------------*/
+/*
+ * Set the i/o transfer over the ISA bus to 8 bits mode
+ */
+static inline void
+wv_16_off(u_short	ioaddr,
+	  u_short	hacr)
+{
+  hacr &= ~HACR_16BITS;
+  hacr_write(ioaddr, hacr);
+} /* wv_16_off */
 
-	wavelan_splx(x);
-}
+/*------------------------------------------------------------------*/
+/*
+ * Set the i/o transfer over the ISA bus to 8 bits mode
+ */
+static inline void
+wv_16_on(u_short	ioaddr,
+	 u_short	hacr)
+{
+  hacr |= HACR_16BITS;
+  hacr_write(ioaddr, hacr);
+} /* wv_16_on */
 
+/*------------------------------------------------------------------*/
 /*
- * Read bytes from the PSA.
+ * Disable interrupts on the wavelan hardware
  */
-static
-void
-psa_read(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+static inline void
+wv_ints_off(device *	dev)
 {
-	wavelan_16_off(ioaddr, hacr);
+  net_local *	lp = (net_local *)dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  u_long	x;
 
-	while (n-- > 0)
-	{
-		outw(o, PIOR2(ioaddr));
-		o++;
-		*b++ = inb(PIOP2(ioaddr));
-	}
+  x = wv_splhi();
 
-	wavelan_16_on(ioaddr, hacr);
-}
+  lp->hacr &= ~HACR_INTRON;
+  hacr_write(ioaddr, lp->hacr);
 
-#if	defined(IRQ_SET_WORKS)
+  wv_splx(x);
+} /* wv_ints_off */
+
+/*------------------------------------------------------------------*/
 /*
- * Write bytes to the PSA.
+ * Enable interrupts on the wavelan hardware
  */
-static
-void
-psa_write(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+static inline void
+wv_ints_on(device *	dev)
 {
-	wavelan_16_off(ioaddr, hacr);
+  net_local *	lp = (net_local *)dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  u_long	x;
 
-	while (n-- > 0)
-	{
-		outw(o, PIOR2(ioaddr));
-		o++;
-		outb(*b, PIOP2(ioaddr));
-		b++;
-	}
+  x = wv_splhi();
 
-	wavelan_16_on(ioaddr, hacr);
-}
-#endif	/* defined(IRQ_SET_WORKS) */
+  lp->hacr |= HACR_INTRON;
+  hacr_write(ioaddr, lp->hacr);
+
+  wv_splx(x);
+} /* wv_ints_on */
 
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
 /*
- * Read bytes from the on-board RAM.
+ * Usefull subroutines to manage the modem of the wavelan
  */
-static
-void
-obram_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
-{
-	n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
 
-	outw(o, PIOR1(ioaddr));
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
+/*
+ * Read bytes from the PSA.
+ */
+static void
+psa_read(u_short	ioaddr,
+	 u_short	hacr,
+	 int		o,	/* offset in PSA */
+	 u_char *	b,	/* buffer to fill */
+	 int		n)	/* size to read */
+{
+  wv_16_off(ioaddr, hacr);
+
+  while(n-- > 0)
+    {
+      outw(o, PIOR2(ioaddr));
+      o++;
+      *b++ = inb(PIOP2(ioaddr));
+    }
 
-	insw(PIOP1(ioaddr), (unsigned short *)b, n);
-}
+  wv_16_on(ioaddr, hacr);
+} /* psa_read */
 
+/*------------------------------------------------------------------*/
 /*
- * Write bytes to the on-board RAM.
+ * Write the Paramter Storage Area to the WaveLAN card's memory
  */
-static
-void
-obram_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
-{
-	n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+static void
+psa_write(u_short	ioaddr,
+	  u_short	hacr,
+	  int		o,	/* Offset in psa */
+	  u_char *	b,	/* Buffer in memory */
+	  int		n)	/* Length of buffer */
+{
+  int	count = 0;
+
+  wv_16_off(ioaddr, hacr);
+
+  while(n-- > 0)
+    {
+      outw(o, PIOR2(ioaddr));
+      o++;
+
+      outb(*b, PIOP2(ioaddr));
+      b++;
+
+      /* Wait for the memory to finish its write cycle */
+      count = 0;
+      while((count++ < 100) &&
+	    (hasr_read(ioaddr) & HASR_PSA_BUSY))
+	udelay(1000);
+    }
 
-	outw(o, PIOR1(ioaddr));
+  wv_16_on(ioaddr, hacr);
+} /* psa_write */
 
-	outsw(PIOP1(ioaddr), (unsigned short *)b, n);
-}
+#ifdef PSA_CRC
+/*------------------------------------------------------------------*/
+/*
+ * Calculate the PSA CRC (not tested yet)
+ * As the Wavelan drivers don't use the CRC, I won't use it either...
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
+ */
+static u_short
+psa_crc(u_short *	psa,	/* The PSA */
+	int		size)	/* Number of short for CRC */
+{
+  int		byte_cnt;	/* Loop on the PSA */
+  u_short	crc_bytes = 0;	/* Data in the PSA */
+  int		bit_cnt;	/* Loop on the bits of the short */
+
+  for(byte_cnt = 0; byte_cnt <= size; byte_cnt++ )
+    {
+      crc_bytes ^= psa[byte_cnt];	/* Its an xor */
+
+      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
+	{
+	  if(crc_bytes & 0x0001)
+	    crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+	  else
+	    crc_bytes >>= 1 ;
+        }
+    }
+
+  return crc_bytes;
+} /* psa_crc */
+#endif	/* PSA_CRC */
 
+/*------------------------------------------------------------------*/
 /*
- * Read bytes from the MMC.
+ * Write 1 byte to the MMC.
  */
-static
-void
-mmc_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+static inline void
+mmc_out(u_short		ioaddr,
+	u_short		o,
+	u_char		d)
 {
-	while (n-- > 0)
-	{
-		while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
-			;
-
-		outw(o << 1, MMCR(ioaddr));
-		o++;
-
-		while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
-			;
+  /* Wait for MMC to go idle */
+  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+    ;
 
-		*b++ = (unsigned char)(inw(MMCR(ioaddr)) >> 8);
-	}
+  outw((u_short) (((u_short) d << 8) | (o << 1) | 1),
+       MMCR(ioaddr));
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Write bytes to the MMC.
+ * Routine to write bytes to the Modem Management Controller.
+ * We start by the end because it is the way it should be !
  */
-static
-void
-mmc_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+static inline void
+mmc_write(u_short	ioaddr,
+	  u_char	o,
+	  u_char *	b,
+	  int		n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0 )
+    mmc_out(ioaddr, --o, *(--b));
+} /* mmc_write */
+
+/*------------------------------------------------------------------*/
+/*
+ * Read 1 byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory...
+ */
+static inline u_char
+mmc_in(u_short	ioaddr,
+       u_short	o)
 {
-	while (n-- > 0)
-	{
-		while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
-			;
+  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+    ;
+  outw(o << 1, MMCR(ioaddr));
 
-		outw((unsigned short)(((unsigned short)*b << 8) | (o << 1) | 1), MMCR(ioaddr));
-		b++;
-		o++;
-	}
+  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+    ;
+  return (u_char) (inw(MMCR(ioaddr)) >> 8);
 }
 
-static int	irqvals[]	=
-{
-	   0,    0,    0, 0x01,
-	0x02, 0x04,    0, 0x08,
-	   0,    0, 0x10, 0x20,
-	0x40,    0,    0, 0x80,
-};
+/*------------------------------------------------------------------*/
+/*
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start by the end because it is the way it should be !
+ */
+static inline void
+mmc_read(u_short	ioaddr,
+	 u_char		o,
+	 u_char *	b,
+	 int		n)
+{
+  o += n;
+  b += n;
+
+  while(n-- > 0)
+    *(--b) = mmc_in(ioaddr, --o);
+} /* mmc_read */
 
-#if	defined(IRQ_SET_WORKS)
-static
-int
-wavelan_unmap_irq(int irq, unsigned char *irqval)
+/*------------------------------------------------------------------*/
+/*
+ * Wait for the frequency EEprom to complete a command...
+ * I hope this one will be optimally inlined...
+ */
+static inline void
+fee_wait(u_short	ioaddr,	/* i/o port of the card */
+	 int		delay,	/* Base delay to wait for */
+	 int		number)	/* Number of time to wait */
 {
-	if (irq < 0 || irq >= nels(irqvals) || irqvals[irq] == 0)
-		return -1;
-	
-	*irqval = (unsigned char)irqvals[irq];
+  int		count = 0;	/* Wait only a limited time */
 
-	return 0;
+  while((count++ < number) &&
+	(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
+    udelay(delay);
 }
-#endif	/* defined(IRQ_SET_WORKS) */
 
+/*------------------------------------------------------------------*/
 /*
- * Map values from the irq parameter register to irq numbers.
+ * Read bytes from the Frequency EEprom (frequency select cards).
  */
-static
-int
-wavelan_map_irq(unsigned char irqval)
+static void
+fee_read(u_short	ioaddr,	/* i/o port of the card */
+	 u_short	o,	/* destination offset */
+	 u_short *	b,	/* data buffer */
+	 int		n)	/* number of registers */
 {
-	int	irq;
+  b += n;		/* Position at the end of the area */
 
-	for (irq = 0; irq < nels(irqvals); irq++)
-	{
-		if (irqvals[irq] == (int)irqval)
-			return irq;
-	}
+  /* Write the address */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
 
-	return -1;
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the read command */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
+
+      /* Wait until EEprom is ready (should be quick !) */
+      fee_wait(ioaddr, 10, 100);
+
+      /* Read the value */
+      *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) |
+	      mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+    }
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Initialize the Modem Management Controller.
+ * Write bytes from the Frequency EEprom (frequency select cards).
+ * This is a bit complicated, because the frequency eeprom has to
+ * be unprotected and the write enabled.
+ * Jean II
  */
-static
-void
-wavelan_mmc_init(device *dev, psa_t *psa)
+static void
+fee_write(u_short	ioaddr,	/* i/o port of the card */
+	  u_short	o,	/* destination offset */
+	  u_short *	b,	/* data buffer */
+	  int		n)	/* number of registers */
 {
-	unsigned short	ioaddr;
-	net_local	*lp;
-	mmw_t		m;
-	int		configured;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-	memset(&m, 0x00, sizeof(m));
+  b += n;		/* Position at the end of the area */
 
-	/*
-	 *	configured = psa->psa_conf_status & 1;
-	 *
-	 * For now we use the persistent PSA
-	 * information as little as possible, thereby
-	 * allowing us to return to the same known state
-	 * during a hardware reset.
-	 */
-	configured = 0;
-	
-	/*
-	 * Set default modem control parameters.
-	 * See NCR document 407-0024326 Rev. A.
-	 */
-	m.mmw_jabber_enable = 0x01;
-	m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
-	m.mmw_ifs = 0x20;
-	m.mmw_mod_delay = 0x04;
-	m.mmw_jam_time = 0x38;
+#ifdef EEPROM_IS_PROTECTED	/* disabled */
+#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
+  /* Ask to read the protected register */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
 
-	m.mmw_encr_enable = 0;
-	m.mmw_des_io_invert = 0;
-	m.mmw_freeze = 0;
-	m.mmw_decay_prm = 0;
-	m.mmw_decay_updat_prm = 0;
+  fee_wait(ioaddr, 10, 100);
 
-	if (configured)
-	{
-		/*
-		 * Use configuration defaults from parameter storage area.
-		 */
-		if (psa->psa_undefined & 1)
-			m.mmw_loopt_sel = 0x00;
-		else
-			m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
+  /* Read the protected register */
+  printk("Protected 2 : %02X-%02X\n",
+	 mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)),
+	 mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+#endif	/* DOESNT_SEEM_TO_WORK */
 
-		m.mmw_thr_pre_set = psa->psa_thr_pre_set & 0x3F;
-		m.mmw_quality_thr = psa->psa_quality_thr & 0x0F;
-	}
-	else
-	{
-		if (lp->promiscuous && lp->full_promiscuous)
-			m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
-		else
-			m.mmw_loopt_sel = 0x00;
+  /* Enable protected register */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
 
-		/*
-		 * 0x04 for AT,
-		 * 0x01 for MCA.
-		 */
-		if (psa->psa_comp_number & 1)
-			m.mmw_thr_pre_set = 0x01;
-		else
-			m.mmw_thr_pre_set = 0x04;
+  fee_wait(ioaddr, 10, 100);
 
-		m.mmw_quality_thr = 0x03;
-	}
+  /* Unprotect area */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK	/* disabled */
+  /* Or use : */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif	/* DOESNT_SEEM_TO_WORK */
 
-	m.mmw_netw_id_l = lp->nwid[1];
-	m.mmw_netw_id_h = lp->nwid[0];
+  fee_wait(ioaddr, 10, 100);
+#endif	/* EEPROM_IS_PROTECTED */
 
-	mmc_write(ioaddr, 0, (unsigned char *)&m, sizeof(m));
-}
+  /* Write enable */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
 
-static
-void
-wavelan_ack(device *dev)
-{
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned short	scb_cs;
-	int		i;
+  fee_wait(ioaddr, 10, 100);
 
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
+  /* Write the EEprom address */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
 
-	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
-	scb_cs &= SCB_ST_INT;
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the value */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
 
-	if (scb_cs == 0)
-		return;
+      /* Write the write command */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
 
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
+      udelay(10000);
+      fee_wait(ioaddr, 10, 100);
+    }
 
-	set_chan_attn(ioaddr, lp->hacr);
+  /* Write disable */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
 
-	for (i = 1000; i > 0; i--)
-	{
-		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-		if (scb_cs == 0)
-			break;
+  fee_wait(ioaddr, 10, 100);
 
-		udelay(1000);
-	}
+#ifdef EEPROM_IS_PROTECTED	/* disabled */
+  /* Reprotect EEprom */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
 
-	if (i <= 0)
-		printk("%s: wavelan_ack(): board not accepting command.\n", dev->name);
+  fee_wait(ioaddr, 10, 100);
+#endif	/* EEPROM_IS_PROTECTED */
 }
 
+/************************ I82586 SUBROUTINES *************************/
 /*
- * Set channel attention bit and busy wait until command has
- * completed, then acknowledge the command completion.
+ * Usefull subroutines to manage the Ethernet controler
  */
-static
-int
-wavelan_synchronous_cmd(device *dev, const char *str)
-{
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned short	scb_cmd;
-	ach_t		cb;
-	int		i;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	for (i = 64; i > 0; i--)
-	{
-		obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
-		if (cb.ac_status & AC_SFLD_C)
-			break;
-
-		udelay(1000);
-	}
-
-	if (i <= 0 || !(cb.ac_status & AC_SFLD_OK))
-	{
-		printk("%s: %s failed; status = 0x%x\n", dev->name, str, cb.ac_status);
-		wavelan_scb_show(ioaddr);
-		return -1;
-	}
 
-	wavelan_ack(dev);
-
-	return 0;
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the on-board RAM.
+ * Why inlining this function make it fail ???
+ */
+static /*inline*/ void
+obram_read(u_short	ioaddr,
+	   u_short	o,
+	   u_char *	b,
+	   int		n)
+{
+  outw(o, PIOR1(ioaddr));
+  insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
 }
 
-static
-int
-wavelan_hardware_reset(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes to the on-board RAM.
+ */
+static inline void
+obram_write(u_short	ioaddr,
+	    u_short	o,
+	    u_char *	b,
+	    int		n)
 {
-	unsigned short	ioaddr;
-	psa_t		psa;
-	net_local	*lp;
-	scp_t		scp;
-	iscp_t		iscp;
-	scb_t		scb;
-	ach_t		cb;
-	int		i;
-	ac_cfg_t	cfg;
-	ac_ias_t	ias;
-
-	if (wavelan_debug > 0)
-		printk("%s: ->wavelan_hardware_reset(dev=0x%x)\n", dev->name, (unsigned int)dev);
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	lp->nresets++;
+  outw(o, PIOR1(ioaddr));
+  outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
 
-	wavelan_reset(ioaddr);
-	lp->hacr = HACR_DEFAULT;
+/*------------------------------------------------------------------*/
+/*
+ * Acknowledge the reading of the status issued by the i82586
+ */
+static void
+wv_ack(device *		dev)
+{
+  net_local *	lp = (net_local *)dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  u_short	scb_cs;
+  int		i;
 
-	/*
-	 * Clear the onboard RAM.
-	 */
-	{
-		unsigned char	zeroes[512];
+  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+	     (unsigned char *) &scb_cs, sizeof(scb_cs));
+  scb_cs &= SCB_ST_INT;
 
-		memset(&zeroes[0], 0x00, sizeof(zeroes));
+  if(scb_cs == 0)
+    return;
 
-		for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
-			obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
-	}
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+	      (unsigned char *) &scb_cs, sizeof(scb_cs));
 
-	psa_read(ioaddr, lp->hacr, 0, (unsigned char *)&psa, sizeof(psa));
+  set_chan_attn(ioaddr, lp->hacr);
 
-	wavelan_mmc_init(dev, &psa);
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+      if(scb_cs == 0)
+	break;
 
-	/*
-	 * Construct the command unit structures:
-	 * scp, iscp, scb, cb.
-	 */
-	memset(&scp, 0x00, sizeof(scp));
-	scp.scp_sysbus = SCP_SY_16BBUS;
-	scp.scp_iscpl = OFFSET_ISCP;
-	obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp));
+      udelay(10);
+    }
+  udelay(100);
 
-	memset(&iscp, 0x00, sizeof(iscp));
-	iscp.iscp_busy = 1;
-	iscp.iscp_offset = OFFSET_SCB;
-	obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+#ifdef DEBUG_CONFIG_ERROR
+  if(i <= 0)
+    printk(KERN_INFO "%s: wv_ack(): board not accepting command.\n",
+	   dev->name);
+#endif
+}
 
-	memset(&scb, 0x00, sizeof(scb));
-	scb.scb_command = SCB_CMD_RESET;
-	scb.scb_cbl_offset = OFFSET_CU;
-	scb.scb_rfa_offset = OFFSET_RU;
-	obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+/*------------------------------------------------------------------*/
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge the command completion.
+ */
+static inline int
+wv_synchronous_cmd(device *	dev,
+		   const char *	str)
+{
+  net_local *	lp = (net_local *)dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  u_short	scb_cmd;
+  ach_t		cb;
+  int		i;
+
+  scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+	      (unsigned char *) &scb_cmd, sizeof(scb_cmd));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  for (i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+      if (cb.ac_status & AC_SFLD_C)
+	break;
+
+      udelay(10);
+    }
+  udelay(100);
+
+  if(i <= 0 || !(cb.ac_status & AC_SFLD_OK))
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      printk(KERN_INFO "%s: %s failed; status = 0x%x\n",
+	     dev->name, str, cb.ac_status);
+#endif
+#ifdef DEBUG_I82586_SHOW
+      wv_scb_show(ioaddr);
+#endif
+      return -1;
+    }
 
-	set_chan_attn(ioaddr, lp->hacr);
+  /* Ack the status */
+  wv_ack(dev);
 
-	for (i = 1000; i > 0; i--)
-	{
-		obram_read(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+  return 0;
+}
 
-		if (iscp.iscp_busy == (unsigned short)0)
-			break;
+/*------------------------------------------------------------------*/
+/*
+ * Configuration commands completion interrupt.
+ * Check if done, and if ok...
+ */
+static inline int
+wv_config_complete(device *	dev,
+		   u_short	ioaddr,
+		   net_local *	lp)
+{
+  unsigned short	mcs_addr;
+  unsigned short	status;
+  int			ret;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name);
+#endif
+
+  mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t)
+    + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t);
+
+  /* Read the status of the last command (set mc list) */
+  obram_read(ioaddr, acoff(mcs_addr, ac_status), (unsigned char *)&status, sizeof(status));
+
+  /* If not completed -> exit */
+  if((status & AC_SFLD_C) == 0)
+    ret = 0;		/* Not ready to be scrapped */
+  else
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      unsigned short	cfg_addr;
+      unsigned short	ias_addr;
+
+      /* Check mc_config command */
+      if(status & AC_SFLD_OK != 0)
+	printk(KERN_INFO "wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
+	       dev->name, str, status);
+
+      /* check ia-config command */
+      ias_addr = mcs_addr - sizeof(ac_ias_t);
+      obram_read(ioaddr, acoff(ias_addr, ac_status), (unsigned char *)&status, sizeof(status));
+      if(status & AC_SFLD_OK != 0)
+	printk(KERN_INFO "wv_config_complete(): set_MAC_address; status = 0x%x\n",
+	       dev->name, str, status);
+
+      /* Check config command */
+      cfg_addr = ias_addr - sizeof(ac_cfg_t);
+      obram_read(ioaddr, acoff(cfg_addr, ac_status), (unsigned char *)&status, sizeof(status));
+      if(status & AC_SFLD_OK != 0)
+	printk(KERN_INFO "wv_config_complete(): configure; status = 0x%x\n",
+	       dev->name, str, status);
+#endif	/* DEBUG_CONFIG_ERROR */
+
+      ret = 1;		/* Ready to be scrapped */
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name, ret);
+#endif
+  return ret;
+}
 
-		udelay(1000);
-	}
+/*------------------------------------------------------------------*/
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ */
+static int
+wv_complete(device *	dev,
+	    u_short	ioaddr,
+	    net_local *	lp)
+{
+  int	nreaped = 0;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name);
+#endif
+
+  /* Loop on all the transmit buffers */
+  while(lp->tx_first_in_use != I82586NULL)
+    {
+      unsigned short	tx_status;
+
+      /* Read the first transmit buffer */
+      obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status));
+
+      /* Hack for reconfiguration... */
+      if(tx_status == 0xFFFF)
+	if(!wv_config_complete(dev, ioaddr, lp))
+	  break;	/* Not completed */
+
+      /* If not completed -> exit */
+      if((tx_status & AC_SFLD_C) == 0)
+	break;
+
+      /* We now remove this buffer */
+      nreaped++;
+      --lp->tx_n_in_use;
 
-	if (i <= 0)
-	{
-		printk("%s: wavelan_hardware_reset(): iscp_busy timeout.\n", dev->name);
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-		return -1;
-	}
+/*
+if (lp->tx_n_in_use > 0)
+	printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
 
-	for (i = 15; i > 0; i--)
+      /* Was it the last one ? */
+      if(lp->tx_n_in_use <= 0)
+	lp->tx_first_in_use = I82586NULL;
+      else
 	{
-		obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+	  /* Next one in the chain */
+	  lp->tx_first_in_use += TXBLOCKZ;
+	  if(lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+	    lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ;
+	}
+
+      /* Hack for reconfiguration... */
+      if(tx_status == 0xFFFF)
+	continue;
+
+      /* Now, check status of the finished command */
+      if(tx_status & AC_SFLD_OK)
+	{
+	  int	ncollisions;
+
+	  lp->stats.tx_packets++;
+	  ncollisions = tx_status & AC_SFLD_MAXCOL;
+	  lp->stats.collisions += ncollisions;
+#ifdef DEBUG_INTERRUPT_INFO
+	  if(ncollisions > 0)
+	    printk(KERN_DEBUG "%s: wv_complete(): tx completed after %d collisions.\n",
+		   dev->name, ncollisions);
+#endif
+	}
+      else
+	{
+	  lp->stats.tx_errors++;
+#ifndef IGNORE_NORMAL_XMIT_ERRS
+	  if(tx_status & AC_SFLD_S10)
+	    {
+	      lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+	      printk(KERN_INFO "%s: wv_complete(): tx error: no CS.\n",
+		     dev->name);
+#endif
+	    }
+#endif	/* IGNORE_NORMAL_XMIT_ERRS */
+	  if(tx_status & AC_SFLD_S9)
+	    {
+	      lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+	      printk(KERN_INFO "%s: wv_complete(): tx error: lost CTS.\n",
+		     dev->name);
+#endif
+	    }
+	  if(tx_status & AC_SFLD_S8)
+	    {
+	      lp->stats.tx_fifo_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+	      printk(KERN_INFO "%s: wv_complete(): tx error: slow DMA.\n",
+		     dev->name);
+#endif
+	    }
+#ifndef IGNORE_NORMAL_XMIT_ERRS
+	  if(tx_status & AC_SFLD_S6)
+	    {
+	      lp->stats.tx_heartbeat_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+	      printk(KERN_INFO "%s: wv_complete(): tx error: heart beat.\n",
+		     dev->name);
+#endif
+	    }
+	  if(tx_status & AC_SFLD_S5)
+	    {
+	      lp->stats.tx_aborted_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+	      printk(KERN_INFO "%s: wv_complete(): tx error: too many collisions.\n",
+		     dev->name);
+#endif
+	    }
+#endif	/* IGNORE_NORMAL_XMIT_ERRS */
+	}
+
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "%s: wv_complete(): tx completed, tx_status 0x%04x\n",
+	     dev->name, tx_status);
+#endif
+    }
+
+#ifdef DEBUG_INTERRUPT_INFO
+  if(nreaped > 1)
+    printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n", dev->name, nreaped);
+#endif
+
+  /*
+   * Inform upper layers.
+   */
+  if(lp->tx_n_in_use < NTXBLOCKS - 1)
+    {
+      dev->tbusy = 0;
+      mark_bh(NET_BH);
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name);
+#endif
+  return nreaped;
+}
 
-		if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
-			break;
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82586, or at least ask for it...
+ * Because wv_82586_config use a transmission buffer, we must do it
+ * when we are sure that there is one left, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option...), so you may experience
+ * some delay sometime...
+ */
+static inline void
+wv_82586_reconfig(device *	dev)
+{
+  net_local *	lp = (net_local *)dev->priv;
 
-		udelay(1000);
-	}
+  /* Check if we can do it now ! */
+  if(!(dev->start) || (set_bit(0, (void *)&dev->tbusy) != 0))
+    {
+      lp->reconfig_82586 = 1;
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_82586_reconfig(): delayed (busy = %ld, start = %d)\n",
+	     dev->name, dev->tbusy, dev->start);
+#endif
+    }
+  else
+    wv_82586_config(dev);
+}
 
-	if (i <= 0)
-	{
-		printk("%s: wavelan_hardware_reset(): status: expected 0x%02x, got 0x%02x.\n", dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-		return -1;
-	}
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routines are used in the code to show debug informations.
+ * Most of the time, it dump the content of hardware structures...
+ */
 
-	wavelan_ack(dev);
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted contents of the Parameter Storage Area.
+ */
+static void
+wv_psa_show(psa_t *	p)
+{
+  printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
+  printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+	 p->psa_io_base_addr_1,
+	 p->psa_io_base_addr_2,
+	 p->psa_io_base_addr_3,
+	 p->psa_io_base_addr_4);
+  printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+	 p->psa_rem_boot_addr_1,
+	 p->psa_rem_boot_addr_2,
+	 p->psa_rem_boot_addr_3);
+  printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+  printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	 p->psa_unused0[0],
+	 p->psa_unused0[1],
+	 p->psa_unused0[2],
+	 p->psa_unused0[3],
+	 p->psa_unused0[4],
+	 p->psa_unused0[5],
+	 p->psa_unused0[6]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	 p->psa_univ_mac_addr[0],
+	 p->psa_univ_mac_addr[1],
+	 p->psa_univ_mac_addr[2],
+	 p->psa_univ_mac_addr[3],
+	 p->psa_univ_mac_addr[4],
+	 p->psa_univ_mac_addr[5]);
+  printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+	 p->psa_local_mac_addr[0],
+	 p->psa_local_mac_addr[1],
+	 p->psa_local_mac_addr[2],
+	 p->psa_local_mac_addr[3],
+	 p->psa_local_mac_addr[4],
+	 p->psa_local_mac_addr[5]);
+  printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
+  printk("psa_comp_number: %d, ", p->psa_comp_number);
+  printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+  printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+	 p->psa_feature_select);
+  printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+  printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+  printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+  printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]);
+  printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+  printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select);
+  printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+	 p->psa_encryption_key[0],
+	 p->psa_encryption_key[1],
+	 p->psa_encryption_key[2],
+	 p->psa_encryption_key[3],
+	 p->psa_encryption_key[4],
+	 p->psa_encryption_key[5],
+	 p->psa_encryption_key[6],
+	 p->psa_encryption_key[7]);
+  printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+  printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+	 p->psa_call_code[0]);
+  printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	 p->psa_call_code[0],
+	 p->psa_call_code[1],
+	 p->psa_call_code[2],
+	 p->psa_call_code[3],
+	 p->psa_call_code[4],
+	 p->psa_call_code[5],
+	 p->psa_call_code[6],
+	 p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+	 p->psa_reserved[0],
+	 p->psa_reserved[1],
+	 p->psa_reserved[2],
+	 p->psa_reserved[3]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+  printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+  printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+} /* wv_psa_show */
+#endif	/* DEBUG_PSA_SHOW */
 
-	memset(&cb, 0x00, sizeof(cb));
-	cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
-	cb.ac_link = OFFSET_CU;
-	obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function need to be completed...
+ */
+static void
+wv_mmc_show(device *	dev)
+{
+  u_short	ioaddr = dev->base_addr;
+  net_local *	lp = (net_local *)dev->priv;
+  mmr_t		m;
+
+  /* Basic check */
+  if(hasr_read(ioaddr) & HASR_NO_CLK)
+    {
+      printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n",
+	     dev->name);
+      return;
+    }
+
+  /* Read the mmc */
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+  mmc_read(ioaddr, 0, (u_char *)&m, sizeof(m));
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+  /* Don't forget to update statistics */
+  lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+
+  printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+	 m.mmr_unused0[0],
+	 m.mmr_unused0[1],
+	 m.mmr_unused0[2],
+	 m.mmr_unused0[3],
+	 m.mmr_unused0[4],
+	 m.mmr_unused0[5],
+	 m.mmr_unused0[6],
+	 m.mmr_unused0[7]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n",
+	 m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+	 m.mmr_unused1[0],
+	 m.mmr_unused1[1],
+	 m.mmr_unused1[2],
+	 m.mmr_unused1[3],
+	 m.mmr_unused1[4]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+	 m.mmr_dce_status,
+	 (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"",
+	 (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+	 "loop test indicated," : "",
+	 (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "",
+	 (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+	 "jabber timer expired," : "");
+  printk(KERN_DEBUG "Dsp ID: %02X\n",
+	 m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+	 m.mmr_unused2[0],
+	 m.mmr_unused2[1]);
+#endif	/* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+	 (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+	 (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+  printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+	 m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+	 (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below");
+  printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+	 m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+	 (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg");
+  printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL,
+	 (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update");
+  printk("sgnl_qual: 0x%x [%s]\n",
+	 m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+	 (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif	/* DEBUG_SHOW_UNUSED */
+} /* wv_mmc_show */
+#endif	/* DEBUG_MMC_SHOW */
 
-	if (wavelan_synchronous_cmd(dev, "diag()") == -1)
-	{
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-		return -1;
-	}
+#ifdef DEBUG_I82586_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the last block of the i82586 memory
+ */
+static void
+wv_scb_show(unsigned short	ioaddr)
+{
+  scb_t		scb;
 
-	obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
-	if (cb.ac_status & AC_SFLD_FAIL)
-	{
-		printk("%s: wavelan_hardware_reset(): i82586 Self Test failed.\n", dev->name);
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-		return -1;
-	}
+  obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));   
 
-	memset(&cfg, 0x00, sizeof(cfg));
+  printk(KERN_DEBUG "##### wavelan system control block: #####\n");
 
-#if	0
-	/*
-	 * The default board configuration.
-	 */
-	cfg.fifolim_bytecnt 	= 0x080c;
-	cfg.addrlen_mode  	= 0x2600;
-	cfg.linprio_interframe	= 0x7820;	/* IFS=120, ACS=2 */
-	cfg.slot_time      	= 0xf00c;	/* slottime=12    */
-	cfg.hardware	     	= 0x0008;	/* tx even w/o CD */
-	cfg.min_frame_len   	= 0x0040;
-#endif	/* 0 */
+  printk(KERN_DEBUG "status: ");
+  printk("stat 0x%x[%s%s%s%s] ",
+	 (scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12,
+	 (scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "",
+	 (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+	 (scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "",
+	 (scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : "");
+  printk("cus 0x%x[%s%s%s] ",
+	 (scb.scb_status & SCB_ST_CUS) >> 8,
+	 ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "",
+	 ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "",
+	 ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : "");
+  printk("rus 0x%x[%s%s%s%s]\n",
+	 (scb.scb_status & SCB_ST_RUS) >> 4,
+	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "",
+	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "",
+	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "",
+	 ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : "");
+
+  printk(KERN_DEBUG "command: ");
+  printk("ack 0x%x[%s%s%s%s] ",
+	 (scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+	 (scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+	 (scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+	 (scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
+	 (scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : "");
+  printk("cuc 0x%x[%s%s%s%s%s] ",
+	 (scb.scb_command & SCB_CMD_CUC) >> 8,
+	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "",
+	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
+	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "",
+	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "",
+	 ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : "");
+  printk("ruc 0x%x[%s%s%s%s%s]\n",
+	 (scb.scb_command & SCB_CMD_RUC) >> 4,
+	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "",
+	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
+	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "",
+	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "",
+	 ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : "");
+
+  printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset);
+  printk("rfa_offset 0x%x\n", scb.scb_rfa_offset);
+
+  printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs);
+  printk("alnerrs %d ", scb.scb_alnerrs);
+  printk("rscerrs %d ", scb.scb_rscerrs);
+  printk("ovrnerrs %d\n", scb.scb_ovrnerrs);
+}
 
-	/*
-	 * For Linux we invert AC_CFG_ALOC(..) so as to conform
-	 * to the way that net packets reach us from above.
-	 * (See also ac_tx_t.)
-	 */
-	cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
-	cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
- 	cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
-			AC_CFG_SRDY(0);
- 	cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
-			AC_CFG_ILPBCK(0) |
-			AC_CFG_PRELEN(AC_CFG_PLEN_2) |
-			AC_CFG_ALOC(1) |
-			AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
-	cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
-			AC_CFG_ACR(0) |
-			AC_CFG_LINPRIO(0);
-	cfg.cfg_ifs = 32;
-	cfg.cfg_slotl = 0;
-	cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
-			AC_CFG_SLTTMHI(2);
-	cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
-			AC_CFG_BTSTF(0) |
-			AC_CFG_CRC16(0) |
-			AC_CFG_NCRC(0) |
-			AC_CFG_TNCRS(1) |
-			AC_CFG_MANCH(0) |
-			AC_CFG_BCDIS(0) |
-			AC_CFG_PRM(lp->promiscuous);
-	cfg.cfg_byte15 = AC_CFG_ICDS(0) |
-			AC_CFG_CDTF(0) |
-			AC_CFG_ICSS(0) |
-			AC_CFG_CSTF(0);
+/*------------------------------------------------------------------*/
 /*
-	cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
-*/
-	cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
-
-	cfg.cfg_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_configure);
-	cfg.cfg_h.ac_link = OFFSET_CU;
-	obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cfg, sizeof(cfg));
-
-	if (wavelan_synchronous_cmd(dev, "reset()-configure") == -1)
-	{
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-
-		return -1;
-	}
+ * Print the formatted status of the i82586's receive unit.
+ */
+static void
+wv_ru_show(device *	dev)
+{
+  /* net_local *lp = (net_local *) dev->priv; */
 
-	memset(&ias, 0x00, sizeof(ias));
-	ias.ias_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_ia_setup);
-	ias.ias_h.ac_link = OFFSET_CU;
-	memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr));
-	obram_write(ioaddr, OFFSET_CU, (unsigned char *)&ias, sizeof(ias));
+  printk(KERN_DEBUG "##### wavelan i82586 receiver unit status: #####\n");
+  printk(KERN_DEBUG "ru:");
+  /*
+   * Not implemented yet...
+   */
+  printk("\n");
+} /* wv_ru_show */
 
-	if (wavelan_synchronous_cmd(dev, "reset()-address") == -1)
-	{
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+/*------------------------------------------------------------------*/
+/*
+ * Display info about one control block of the i82586 memory
+ */
+static void
+wv_cu_show_one(device *		dev,
+	       net_local *	lp,
+	       int		i,
+	       u_short		p)
+{
+  unsigned short	ioaddr;
+  ac_tx_t		actx;
 
-		return -1;
-	}
+  ioaddr = dev->base_addr;
 
-	wavelan_ints_on(dev);
+  printk("%d: 0x%x:", i, p);
 
-	if (wavelan_debug > 4)
-		wavelan_scb_show(ioaddr);
+  obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx));
+  printk(" status=0x%x,", actx.tx_h.ac_status);
+  printk(" command=0x%x,", actx.tx_h.ac_command);
 
-	wavelan_ru_start(dev);
-	wavelan_cu_start(dev);
+  /*
+  {
+    tbd_t	tbd;
 
-	if (wavelan_debug > 0)
-		printk("%s: <-wavelan_hardware_reset(): 0\n", dev->name);
+    obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
+    printk(" tbd_status=0x%x,", tbd.tbd_status);
+  }
+  */
 
-	return 0;
+  printk("|");
 }
 
-#if	STRUCT_CHECK == 1
-
-static
-const char	*
-wavelan_struct_check(void)
+/*------------------------------------------------------------------*/
+/*
+ * Print status of the command unit of the i82586
+ */
+static void
+wv_cu_show(device *	dev)
 {
-#define	SC(t,s,n)	if (sizeof(t) != s) return n
-	SC(psa_t, PSA_SIZE, "psa_t");
-	SC(mmw_t, MMW_SIZE, "mmw_t");
-	SC(mmr_t, MMR_SIZE, "mmr_t");
-	SC(ha_t, HA_SIZE, "ha_t");
-#undef	SC
-
-	return (char *)0;
+  net_local *	lp = (net_local *)dev->priv;
+  unsigned int	i;
+  u_short	p;
+
+  printk(KERN_DEBUG "##### wavelan i82586 command unit status: #####\n");
+
+  printk(KERN_DEBUG);
+  for(i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
+    {
+      wv_cu_show_one(dev, lp, i, p);
+
+      p += TXBLOCKZ;
+      if(p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+	p -= NTXBLOCKS * TXBLOCKZ;
+    }
+  printk("\n");
 }
+#endif	/* DEBUG_I82586_SHOW */
 
-#endif	/* STRUCT_CHECK == 1 */
-
+#ifdef DEBUG_DEVICE_SHOW
+/*------------------------------------------------------------------*/
 /*
- * Check for a network adaptor of this type.
- * Return '0' iff one exists.
- * (There seem to be different interpretations of
- * the initial value of dev->base_addr.
- * We follow the example in drivers/net/ne.c.)
+ * Print the formatted status of the WaveLAN PCMCIA device driver.
  */
-int
-wavelan_probe(device *dev)
+static void
+wv_dev_show(device *	dev)
 {
-	int			i;
-	int			r;
-	short			base_addr;
-	static unsigned short	iobase[]	=
-	{
-#if	0
-		Leave out 0x3C0 for now -- seems to clash
-		with some video controllers.
-		Leave out the others too -- we will always
-		use 0x390 and leave 0x300 for the Ethernet device.
-		0x300, 0x390, 0x3E0, 0x3C0,
-#endif	/* 0 */
-		0x390,
-	};
-	static struct proc_dir_entry	pe	=
-	{
-		PROC_NET_WAVELAN, 7, "wavelan",
-		S_IFREG | S_IRUGO, 1, 0, 0,
-		0, &proc_net_inode_operations,
-		wavelan_get_info
-	};
-
-	if (wavelan_debug > 0)
-		printk("%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", dev->name, (unsigned int)dev, (unsigned int)dev->base_addr);
+  printk(KERN_DEBUG "dev:");
+  printk(" start=%d,", dev->start);
+  printk(" tbusy=%ld,", dev->tbusy);
+  printk(" interrupt=%d,", dev->interrupt);
+  printk(" trans_start=%ld,", dev->trans_start);
+  printk(" flags=0x%x,", dev->flags);
+  printk("\n");
+} /* wv_dev_show */
 
-#if	STRUCT_CHECK == 1
-	if (wavelan_struct_check() != (char *)0)
-	{
-		printk("%s: structure/compiler botch: \"%s\"\n", dev->name, wavelan_struct_check());
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver's
+ * private information.
+ */
+static void
+wv_local_show(device *	dev)
+{
+  net_local *lp;
 
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+  lp = (net_local *)dev->priv;
 
-		return ENODEV;
-	}
-#endif	/* STRUCT_CHECK == 1 */
+  printk(KERN_DEBUG "local:");
+  printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
+  printk(" hacr=0x%x,", lp->hacr);
+  printk(" rx_head=0x%x,", lp->rx_head);
+  printk(" rx_last=0x%x,", lp->rx_last);
+  printk(" tx_first_free=0x%x,", lp->tx_first_free);
+  printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
+  printk("\n");
+} /* wv_local_show */
+#endif	/* DEBUG_DEVICE_SHOW */
 
-	base_addr = dev->base_addr;
+#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
+/*------------------------------------------------------------------*/
+/*
+ * Dump packet header (and content if necessary) on the screen
+ */
+static inline void
+wv_packet_info(u_char *		p,		/* Packet to dump */
+	       int		length,		/* Length of the packet */
+	       char *		msg1,		/* Name of the device */
+	       char *		msg2)		/* Name of the function */
+{
+#ifndef DEBUG_PACKET_DUMP
+  printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
+	 msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
+  printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
+	 msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]);
+
+#else	/* DEBUG_PACKET_DUMP */
+  int		i;
+  int		maxi;
+
+  printk(KERN_DEBUG "%s: %s(): len=%d, data=\"", msg1, msg2, length);
+
+  if((maxi = length) > DEBUG_PACKET_DUMP)
+    maxi = DEBUG_PACKET_DUMP;
+  for(i = 0; i < maxi; i++)
+    if(p[i] >= ' ' && p[i] <= '~')
+      printk(" %c", p[i]);
+    else
+      printk("%02X", p[i]);
+  if(maxi < length)
+    printk("..");
+  printk("\"\n");
+  printk(KERN_DEBUG "\n");
+#endif	/* DEBUG_PACKET_DUMP */
+}
+#endif	/* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
 
-	if (base_addr < 0)
-	{
-		/*
-		 * Don't probe at all.
-		 */
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_probe(): ENXIO\n", dev->name);
-		return ENXIO;
-	}
-
-	if (base_addr > 0x100)
-	{
-		/*
-		 * Check a single specified location.
-		 */
-		r = wavelan_probe1(dev, base_addr);
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_probe(): %d\n", dev->name, r);
-		if (r == 0)
-			proc_net_register(&pe);
-		return r;
-	}
-
-	for (i = 0; i < nels(iobase); i++)
-	{
-		if (check_region(iobase[i], sizeof(ha_t)))
-			continue;
-
-		if (wavelan_probe1(dev, iobase[i]) == 0)
-		{
-			if (wavelan_debug > 0)
-				printk("%s: <-wavelan_probe(): 0\n", dev->name);
-			proc_net_register(&pe);
-			return 0;
-		}
-	}
-
-	if (wavelan_debug > 0)
-		printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
-
-	return ENODEV;
-}
-
-static
-int
-wavelan_probe1(device *dev, unsigned short ioaddr)
+/*------------------------------------------------------------------*/
+/*
+ * This is the information which is displayed by the driver at startup
+ * There  is a lot of flag to configure it at your will...
+ */
+static inline void
+wv_init_info(device *	dev)
 {
-	psa_t		psa;
-	int		irq;
-	int		i;
-	net_local	*lp;
-	int		enable_full_promiscuous;
-
-	if (wavelan_debug > 0)
-		printk("%s: ->wavelan_probe1(dev=0x%x, ioaddr=0x%x)\n", dev->name, (unsigned int)dev, ioaddr);
-
-	wavelan_reset(ioaddr);
-
-	psa_read(ioaddr, HACR_DEFAULT, 0, (unsigned char *)&psa, sizeof(psa));
-
-	/*
-	 * Check the first three octets of the MAC address
-	 * for the manufacturer's code.
-	 */ 
-	if
-	(
-		psa.psa_univ_mac_addr[0] != SA_ADDR0
-		||
-		psa.psa_univ_mac_addr[1] != SA_ADDR1
-		||
-		(
-			psa.psa_univ_mac_addr[2] != SA_ADDR2
-			&&
-			psa.psa_univ_mac_addr[2] != SA_ALT_ADDR2
-		)
-	)
-	{
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_probe1(): ENODEV\n", dev->name);
-		return ENODEV;
-	}
-
-	printk("%s: WaveLAN at %#x,", dev->name, ioaddr);
-
-	if (dev->irq != 0)
-	{
-		printk("[WARNING: explicit IRQ value %d ignored: using PSA value instead]", dev->irq);
-#if	defined(IRQ_SET_WORKS)
-Leave this out until I can get it to work -- BJ.
-		if (wavelan_unmap_irq(dev->irq, &psa.psa_int_req_no) == -1)
-		{
-			printk(" could not wavelan_unmap_irq(%d, ..) -- ignored.\n", dev->irq);
-			dev->irq = 0;
-		}
-		else
-		{
-			psa_write(ioaddr, HACR_DEFAULT, (char *)&psa.psa_int_req_no - (char *)&psa, (unsigned char *)&psa.psa_int_req_no, sizeof(psa.psa_int_req_no));
-			wavelan_reset(ioaddr);
-		}
-#endif	/* defined(IRQ_SET_WORKS) */
-	}
-
-	if ((irq = wavelan_map_irq(psa.psa_int_req_no)) == -1)
-	{
-		printk(" could not wavelan_map_irq(%d).\n", psa.psa_int_req_no);
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_probe1(): EAGAIN\n", dev->name);
-		return EAGAIN;
-	}
-
-	dev->irq = irq;
-
-	request_region(ioaddr, sizeof(ha_t), "wavelan");
-	dev->base_addr = ioaddr;
-
-	/*
-	 * The third numeric argument to LILO's
-	 * `ether=' control line arrives here as `dev->mem_start'.
-	 *
-	 * If bit 16 of dev->mem_start is non-zero we enable
-	 * full promiscuity.
-	 *
-	 * If either of the least significant two bytes of
-	 * dev->mem_start are non-zero we use them instead
-	 * of the PSA NWID.
-	 */
-	enable_full_promiscuous = (dev->mem_start & ENABLE_FULL_PROMISCUOUS) == ENABLE_FULL_PROMISCUOUS;
-	dev->mem_start &= ~ENABLE_FULL_PROMISCUOUS;
-
-	if (dev->mem_start != 0)
-	{
-		psa.psa_nwid[0] = (dev->mem_start >> 8) & 0xFF;
-		psa.psa_nwid[1] = (dev->mem_start >> 0) & 0xFF;
-	}
-
-	dev->mem_start = 0x0000;
-	dev->mem_end = 0x0000;
-	dev->if_port = 0;
-
-	memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
-
-	for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-		printk("%s%02x", (i == 0) ? " " : ":", dev->dev_addr[i]);
-
-	printk(", IRQ %d", dev->irq);
-	if (enable_full_promiscuous)
-		printk(", promisc");
-	printk(", nwid 0x%02x%02x", psa.psa_nwid[0], psa.psa_nwid[1]);
-
-	printk(", PC");
-	switch (psa.psa_comp_number)
+  short		ioaddr = dev->base_addr;
+  net_local *	lp = (net_local *)dev->priv;
+  psa_t		psa;
+  int		i;
+
+  /* Read the parameter storage area */
+  psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef DEBUG_PSA_SHOW
+  wv_psa_show(&psa);
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+  wv_cu_show(dev);
+#endif
+
+#ifdef DEBUG_BASIC_SHOW
+  /* Now, let's go for the basic stuff */
+  printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr);
+  for(i = 0; i < WAVELAN_ADDR_SIZE; i++)
+    printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
+  printk(", IRQ %d", dev->irq);
+
+  /* Print current network id */
+  if(psa.psa_nwid_select)
+    printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]);
+  else
+    printk(", nwid off");
+
+  /* If 2.00 card */
+  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      unsigned short	freq;
+
+      /* Ask the EEprom to read the frequency from the first area */
+      fee_read(ioaddr, 0x00 /* 1st area - frequency... */,
+	       &freq, 1);
+
+      /* Print frequency */
+      printk(", 2.00, %ld", (freq >> 6) + 2400L);
+
+      /* Hack !!! */
+      if(freq & 0x20)
+	printk(".5");
+    }
+  else
+    {
+      printk(", PC");
+      switch(psa.psa_comp_number)
 	{
 	case PSA_COMP_PC_AT_915:
 	case PSA_COMP_PC_AT_2400:
-		printk("-AT");
-		break;
-
+	  printk("-AT");
+	  break;
 	case PSA_COMP_PC_MC_915:
 	case PSA_COMP_PC_MC_2400:
-		printk("-MC");
-		break;
-
+	  printk("-MC");
+	  break;
 	case PSA_COMP_PCMCIA_915:
-		printk("MCIA");
-		break;
-
+	  printk("MCIA");
+	  break;
 	default:
-		printk("???");
-		break;
+	  printk("???");
 	}
-
-	printk(", ");
-	switch (psa.psa_subband)
+      printk(", ");
+      switch (psa.psa_subband)
 	{
 	case PSA_SUBBAND_915:
-		printk("915");
-		break;
-
+	  printk("915");
+	  break;
 	case PSA_SUBBAND_2425:
-		printk("2425");
-		break;
-
+	  printk("2425");
+	  break;
 	case PSA_SUBBAND_2460:
-		printk("2460");
-		break;
-
+	  printk("2460");
+	  break;
 	case PSA_SUBBAND_2484:
-		printk("2484");
-		break;
-
+	  printk("2484");
+	  break;
 	case PSA_SUBBAND_2430_5:
-		printk("2430.5");
-		break;
-
+	  printk("2430.5");
+	  break;
 	default:
-		printk("???");
-		break;
+	  printk("???");
 	}
-	printk(" MHz");
-
-	printk("\n");
+    }
 
-	if (wavelan_debug > 0)
-		printk(version);
+  printk(" MHz\n");
+#endif	/* DEBUG_BASIC_SHOW */
 
-	dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
-	if (dev->priv == NULL)
-		return -ENOMEM;
-	memset(dev->priv, 0x00, sizeof(net_local));
-	lp = (net_local *)dev->priv;
+#ifdef DEBUG_VERSION_SHOW
+  /* Print version information */
+  printk(KERN_NOTICE "%s", version);
+#endif
+} /* wv_init_info */
 
-	if (first_wavelan == (net_local *)0)
-	{
-		first_wavelan = lp;
-		lp->prev = lp;
-		lp->next = lp;
-	}
-	else
-	{
-		lp->prev = first_wavelan->prev;
-		lp->next = first_wavelan;
-		first_wavelan->prev->next = lp;
-		first_wavelan->prev = lp;
-	}
-	lp->dev = dev;
-
-	lp->hacr = HACR_DEFAULT;
+/********************* IOCTL, STATS & RECONFIG *********************/
+/*
+ * We found here routines that are called by Linux on differents
+ * occasions after the configuration and not for transmitting data
+ * These may be called when the user use ifconfig, /proc/net/dev
+ * or wireless extensions
+ */
 
-	lp->full_promiscuous = enable_full_promiscuous;
-	lp->nwid[0] = psa.psa_nwid[0];
-	lp->nwid[1] = psa.psa_nwid[1];
+/*------------------------------------------------------------------*/
+/*
+ * Get the current ethernet statistics. This may be called with the
+ * card open or closed.
+ * Used when the user read /proc/net/dev
+ */
+static en_stats	*
+wavelan_get_stats(device *	dev)
+{
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
+#endif
 
-	lp->watchdog.function = wavelan_watchdog;
-	lp->watchdog.data = (unsigned long)dev;
+  return(&((net_local *) dev->priv)->stats);
+}
 
-	dev->open = wavelan_open;
-	dev->stop = wavelan_close;
-	dev->hard_start_xmit = wavelan_send_packet;
-	dev->get_stats = wavelan_get_stats;
-	dev->set_multicast_list = &wavelan_set_multicast_list;
+/*------------------------------------------------------------------*/
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets,
+ *			and do best-effort filtering.
+ */
+static void
+wavelan_set_multicast_list(device *	dev)
+{
+  net_local *	lp = (net_local *) dev->priv;
 
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name);
+#endif
+
+#ifdef DEBUG_IOCTL_INFO
+  printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
+	 dev->name, dev->flags, dev->mc_count);
+#endif
+
+  /* If we ask for promiscuous mode,
+   * or all multicast addresses (we don't have that !)
+   * or too much multicast addresses for the hardware filter */
+  if((dev->flags & IFF_PROMISC) ||
+     (dev->flags & IFF_ALLMULTI) ||
+     (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES))
+    {
+      /*
+       * Enable promiscuous mode: receive all packets.
+       */
+      if(!lp->promiscuous)
+	{
+	  lp->promiscuous = 1;
+	  lp->mc_count = 0;
+
+	  wv_82586_reconfig(dev);
+
+	  /* Tell the kernel that we are doing a really bad job... */
+	  dev->flags |= IFF_PROMISC;
+	}
+    }
+  else
+    /* If there is some multicast addresses to send */
+    if(dev->mc_list != (struct dev_mc_list *) NULL)
+      {
 	/*
-	 * Fill in the fields of the device structure
-	 * with ethernet-generic values.
+	 * Disable promiscuous mode, but receive all packets
+	 * in multicast list
 	 */
-	ether_setup(dev);
-
-	dev->flags &= ~IFF_MULTICAST;		/* Not yet supported */
-	
-	dev->mtu = WAVELAN_MTU;
-
-	if (wavelan_debug > 0)
-		printk("%s: <-wavelan_probe1(): 0\n", dev->name);
-
-	return 0;
+#ifdef MULTICAST_AVOID
+	if(lp->promiscuous ||
+	   (dev->mc_count != lp->mc_count))
+#endif
+	  {
+	    lp->promiscuous = 0;
+	    lp->mc_count = dev->mc_count;
+
+	    wv_82586_reconfig(dev);
+	  }
+      }
+    else
+      {
+	/*
+	 * Switch to normal mode: disable promiscuous mode and 
+	 * clear the multicast list.
+	 */
+	if(lp->promiscuous || lp->mc_count == 0)
+	  {
+	    lp->promiscuous = 0;
+	    lp->mc_count = 0;
+
+	    wv_82586_reconfig(dev);
+	  }
+      }
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name);
+#endif
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Construct the fd and rbd structures.
- * Start the receive unit.
+ * This function doesn't exist...
  */
-static
-void
-wavelan_ru_start(device *dev)
+static int
+wavelan_set_mac_address(device *	dev,
+			void *		addr)
 {
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned short	scb_cs;
-	fd_t		fd;
-	rbd_t		rbd;
-	unsigned short	rx;
-	unsigned short	rx_next;
-	int		i;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
-	if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
-		return;
-
-	lp->rx_head = OFFSET_RU;
-
-	for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next)
-	{
-		rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
-
-		fd.fd_status = 0;
-		fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
-		fd.fd_link_offset = rx_next;
-		fd.fd_rbd_offset = rx + sizeof(fd);
-		obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd));
-
-		rbd.rbd_status = 0;
-		rbd.rbd_next_rbd_offset = I82586NULL;
-		rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
-		rbd.rbd_bufh = 0;
-		rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
-		obram_write(ioaddr, rx + sizeof(fd), (unsigned char *)&rbd, sizeof(rbd));
-
-		lp->rx_last = rx;
-	}
-
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), (unsigned char *)&lp->rx_head, sizeof(lp->rx_head));
-
-	scb_cs = SCB_CMD_RUC_GO;
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-
-	set_chan_attn(ioaddr, lp->hacr);
+  struct sockaddr *	mac = addr;
 
-	for (i = 1000; i > 0; i--)
-	{
-		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-		if (scb_cs == 0)
-			break;
+  /* Copy the address */
+  memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
 
-		udelay(1000);
-	}
+  /* Reconfig the beast */
+  wv_82586_reconfig(dev);
 
-	if (i <= 0)
-		printk("%s: wavelan_ru_start(): board not accepting command.\n", dev->name);
+  return 0;
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Initialise the transmit blocks.
- * Start the command unit executing the NOP
- * self-loop of the first transmit block.
+ * Frequency setting (for hardware able of it)
+ * It's a bit complicated and you don't really want to look into it...
+ * (called in wavelan_ioctl)
  */
-static
-void
-wavelan_cu_start(device *dev)
-{
-	unsigned short	ioaddr;
-	net_local	*lp;
-	int		i;
-	unsigned short	txblock;
-	unsigned short	first_nop;
-	unsigned short	scb_cs;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	lp->tx_first_free = OFFSET_CU;
-	lp->tx_first_in_use = I82586NULL;
-
-	for
-	(
-		i = 0, txblock = OFFSET_CU;
-		i < NTXBLOCKS;
-		i++, txblock += TXBLOCKZ
-	)
-	{
-		ac_tx_t		tx;
-		ac_nop_t	nop;
-		tbd_t		tbd;
-		unsigned short	tx_addr;
-		unsigned short	nop_addr;
-		unsigned short	tbd_addr;
-		unsigned short	buf_addr;
-
-		tx_addr = txblock;
-		nop_addr = tx_addr + sizeof(tx);
-		tbd_addr = nop_addr + sizeof(nop);
-		buf_addr = tbd_addr + sizeof(tbd);
-
-		tx.tx_h.ac_status = 0;
-		tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
-		tx.tx_h.ac_link = nop_addr;
-		tx.tx_tbd_offset = tbd_addr;
-		obram_write(ioaddr, tx_addr, (unsigned char *)&tx, sizeof(tx));
-
-		nop.nop_h.ac_status = 0;
-		nop.nop_h.ac_command = acmd_nop;
-		nop.nop_h.ac_link = nop_addr;
-		obram_write(ioaddr, nop_addr, (unsigned char *)&nop, sizeof(nop));
-
-		tbd.tbd_status = TBD_STATUS_EOF;
-		tbd.tbd_next_bd_offset = I82586NULL;
-		tbd.tbd_bufl = buf_addr;
-		tbd.tbd_bufh = 0;
-		obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
-	}
-
-	first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), (unsigned char *)&first_nop, sizeof(first_nop));
-
-	scb_cs = SCB_CMD_CUC_GO;
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	for (i = 1000; i > 0; i--)
-	{
-		obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-		if (scb_cs == 0)
-			break;
-
-		udelay(1000);
-	}
-
-	if (i <= 0)
-		printk("%s: wavelan_cu_start(): board not accepting command.\n", dev->name);
-
-	lp->tx_n_in_use = 0;
-	dev->tbusy = 0;
+static inline int
+wv_set_frequency(u_short	ioaddr,	/* i/o port of the card */
+		 float		frequency)
+{
+  u_short	table[10];	/* Authorized frequency table */
+  const int	BAND_NUM = 10;	/* Number of bands */
+  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz */
+#ifdef DEBUG_IOCTL_INFO
+  int		i;
+#endif
+
+  /* Read the frequency table */
+  fee_read(ioaddr, 0x71 /* frequency table */,
+	   table, 10);
+
+#ifdef DEBUG_IOCTL_INFO
+  printk(KERN_DEBUG "Frequency table :");
+  for(i = 0; i < 10; i++)
+    {
+      printk(" %04X",
+	     table[i]);
+    }
+  printk("\n");
+#endif
+
+  /* Setting by frequency */
+  /* Theoritically, you may set any frequency between
+   * the two limits with a 0.5 MHz precision. In practice,
+   * I don't want you to have trouble with local
+   * regulations... */
+  if((frequency >= 2.412e9) && (frequency <= 2.487e9))
+    {
+      /* Warning : rounding problems... */
+      freq = (((long) ((frequency / 1e5) + .4)) - 24000L) / 5;
+
+      /* Look in the table if the frequency is allowed */
+      if(!(table[9 - ((freq - 24) / 16)] &
+	   (1 << ((freq - 24) % 16))))
+	return -EINVAL;		/* not allowed */
+    }
+
+  /* Setting by channel (same as wfreqsel) */
+  /* Warning : each channel is 22MHz wide, so some of the channels
+   * will interfere... */
+  if((frequency >= 0.0) && (frequency < (float) BAND_NUM))
+    {
+      /* frequency in 1/4 of MHz (as read in the offset register) */
+      short	bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, 0xD0, 0xF0, 0xF8, 0x150 };
+
+      /* Get frequency offset */
+      freq = bands[((int) frequency)] >> 1;
+
+      /* Look in the table if the frequency is allowed */
+      if(!(table[9 - ((freq - 24) / 16)] &
+	   (1 << ((freq - 24) % 16))))
+	return -EINVAL;		/* not allowed */
+    }
+
+  /* If we get a usable frequency */
+  if(freq != 0L)
+    {
+      unsigned short	area[16];
+      unsigned short	dac[2];
+      unsigned short	area_verify[16];
+      unsigned short	dac_verify[2];
+      /* Corresponding gain (in the power adjust value table)
+       * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
+       * & WCIN062D.DOC, page 6.2.9 */
+      unsigned short	power_limit[] = { 40, 80, 120, 160, 0 };
+      int		power_band = 0;		/* Selected band */
+      unsigned short	power_adjust;		/* Correct value */
+
+      /* Search for the gain */
+      power_band = 0;
+      while((freq > power_limit[power_band]) &&
+	    (power_limit[++power_band] != 0))
+	;
+
+      /* Read the first area */
+      fee_read(ioaddr, 0x00,
+	       area, 16);
+
+      /* Read the DAC */
+      fee_read(ioaddr, 0x60,
+	       dac, 2);
+
+      /* Read the new power adjust value */
+      fee_read(ioaddr, 0x6B - (power_band >> 1),
+	       &power_adjust, 1);
+      if(power_band & 0x1)
+	power_adjust >>= 8;
+      else
+	power_adjust &= 0xFF;
+
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
+	{
+	  printk(" %04X",
+		 area[i]);
+	}
+      printk("\n");
+
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+	     dac[0], dac[1]);
+#endif
+
+      /* Frequency offset (for info only...) */
+      area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
+
+      /* Receiver Principle main divider coefficient */
+      area[3] = (freq >> 1) + 2400L - 352L;
+      area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+      /* Transmitter Main divider coefficient */
+      area[13] = (freq >> 1) + 2400L;
+      area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
+
+      /* Others part of the area are flags, bit streams or unused... */
+
+      /* Set the value in the DAC */
+      dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
+      dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
+
+      /* Write the first area */
+      fee_write(ioaddr, 0x00,
+		area, 16);
+
+      /* Write the DAC */
+      fee_write(ioaddr, 0x60,
+		dac, 2);
+
+      /* We now should verify here that the EEprom writting was ok */
+
+      /* ReRead the first area */
+      fee_read(ioaddr, 0x00,
+	       area_verify, 16);
+
+      /* ReRead the DAC */
+      fee_read(ioaddr, 0x60,
+	       dac_verify, 2);
+
+      /* Compare */
+      if(memcmp(area, area_verify, 16 * 2) ||
+	 memcmp(dac, dac_verify, 2 * 2))
+	{
+#ifdef DEBUG_IOCTL_ERROR
+	  printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
+#endif
+	  return -EOPNOTSUPP;
+	}
+
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F);
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+      /* Wait until the download is finished */
+      fee_wait(ioaddr, 100, 100);
+
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61);
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+	      MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
+
+      /* Wait until the download is finished */
+      fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_IOCTL_INFO
+      /* Verification of what we have done... */
+
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
+	{
+	  printk(" %04X",
+		 area_verify[i]);
+	}
+      printk("\n");
+
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+	     dac_verify[0], dac_verify[1]);
+#endif
+
+      return 0;
+    }
+  else
+    return -EINVAL;		/* Bah, never get there... */
 }
 
-static
-int
-wavelan_open(device *dev)
-{
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned long	x;
-	int		r;
-
-	if (wavelan_debug > 0)
-		printk("%s: ->wavelan_open(dev=0x%x)\n", dev->name, (unsigned int)dev);
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	if (dev->irq == 0)
-	{
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_open(): -ENXIO\n", dev->name);
-		return -ENXIO;
-	}
-
-	if
-	(
-		irq2dev_map[dev->irq] != (device *)0
-		/* This is always true, but avoid the false IRQ. */
-		||
-		(irq2dev_map[dev->irq] = dev) == (device *)0
-		||
-		request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", NULL) != 0
-	)
-	{
-		irq2dev_map[dev->irq] = (device *)0;
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_open(): -EAGAIN\n", dev->name);
-		return -EAGAIN;
-	}
-
-	x = wavelan_splhi();
-	if ((r = wavelan_hardware_reset(dev)) != -1)
-	{
-		dev->interrupt = 0;
-		dev->start = 1;
-	}
-	wavelan_splx(x);
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
 
-	if (r == -1)
-	{
-		free_irq(dev->irq, NULL);
-		irq2dev_map[dev->irq] = (device *)0;
-		if (wavelan_debug > 0)
-			printk("%s: <-wavelan_open(): -EAGAIN(2)\n", dev->name);
-		return -EAGAIN;
-	}
-
-	MOD_INC_USE_COUNT;
-
-	if (wavelan_debug > 0)
-		printk("%s: <-wavelan_open(): 0\n", dev->name);
+/*------------------------------------------------------------------*/
+/*
+ * Frequency setting (for hardware able of it)
+ * It's a bit complicated and you don't really want to look into it...
+ * (called in wavelan_ioctl)
+ */
+static inline int
+wv_frequency_list(u_short	ioaddr,	/* i/o port of the card */
+		  float *	list,	/* List of frequency to fill */
+		  int		max)	/* Maximum number of frequencies */
+{
+  u_short	table[10];	/* Authorized frequency table */
+  long		freq = 0L;	/* offset to 2.4 GHz in .5 MHz + 12 MHz */
+  int		i;		/* index in the table */
+
+  /* Read the frequency table */
+  fee_read(ioaddr, 0x71 /* frequency table */,
+	   table, 10);
+
+  /* Look all frequencies */
+  i = 0;
+  for(freq = 0; freq < 150; freq++)
+    /* Look in the table if the frequency is allowed */
+    if(table[9 - (freq / 16)] & (1 << (freq % 16)))
+      {
+	/* put in the list */
+	list[i++] = (((freq + 24) * 5) + 24000L) * 1e5;
+
+	/* Check number */
+	if(i >= max)
+	  return(i);
+      }
 
-	return 0;
+  return(i);
 }
 
-static
-void
-hardware_send_packet(device *dev, void *buf, short length)
-{
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned short	txblock;
-	unsigned short	txpred;
-	unsigned short	tx_addr;
-	unsigned short	nop_addr;
-	unsigned short	tbd_addr;
-	unsigned short	buf_addr;
-	ac_tx_t		tx;
-	ac_nop_t	nop;
-	tbd_t		tbd;
-	unsigned long	x;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	x = wavelan_splhi();
-
-	txblock = lp->tx_first_free;
-	txpred = txblock - TXBLOCKZ;
-	if (txpred < OFFSET_CU)
-		txpred += NTXBLOCKS * TXBLOCKZ;
-	lp->tx_first_free += TXBLOCKZ;
-	if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-		lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
-
+#ifdef WIRELESS_SPY
+/*------------------------------------------------------------------*/
 /*
-if (lp->tx_n_in_use > 0)
-	printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
-*/
-
-	lp->tx_n_in_use++;
-
-	tx_addr = txblock;
-	nop_addr = tx_addr + sizeof(tx);
-	tbd_addr = nop_addr + sizeof(nop);
-	buf_addr = tbd_addr + sizeof(tbd);
-
-	/*
-	 * Transmit command.
-	 */
-	tx.tx_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), (unsigned char *)&tx.tx_h.ac_status, sizeof(tx.tx_h.ac_status));
-
-	/*
-	 * NOP command.
-	 */
-	nop.nop_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
-	nop.nop_h.ac_link = nop_addr;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
-
-	/*
-	 * Transmit buffer descriptor. 
-	 */
-	tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & length);
-	tbd.tbd_next_bd_offset = I82586NULL;
-	tbd.tbd_bufl = buf_addr;
-	tbd.tbd_bufh = 0;
-	obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
-
-	/*
-	 * Data.
-	 */
-	obram_write(ioaddr, buf_addr, buf, length);
-
-	/*
-	 * Overwrite the predecessor NOP link
-	 * so that it points to this txblock.
-	 */
-	nop_addr = txpred + sizeof(tx);
-	nop.nop_h.ac_status = 0;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
-	nop.nop_h.ac_link = txblock;
-	obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
-
-	if (lp->tx_first_in_use == I82586NULL)
-		lp->tx_first_in_use = txblock;
-
-	if (lp->tx_n_in_use < NTXBLOCKS - 1)
-		dev->tbusy = 0;
-
-	dev->trans_start = jiffies;
-
-	if (lp->watchdog.next == (timer_list *)0)
-		wavelan_watchdog((unsigned long)dev);
-
-	wavelan_splx(x);
-
-	if (wavelan_debug > 4)
-	{
-		unsigned char	*a;
-
-		a = (unsigned char *)buf;
-
-		printk
-		(
-			"%s: tx: dest %02x:%02x:%02x:%02x:%02x:%02x, length %d, tbd.tbd_bufl 0x%x.\n",
-			dev->name,
-			a[0], a[1], a[2], a[3], a[4], a[5],
-			length,
-			buf_addr
-		);
-	}
+ * Gather wireless spy statistics : for each packet, compare the source
+ * address with out list, and if match, get the stats...
+ * Sorry, but this function really need wireless extensions...
+ */
+static inline void
+wl_spy_gather(device *	dev,
+	      u_char *	mac,		/* MAC address */
+	      u_char *	stats)		/* Statistics to gather */
+{
+  net_local *	lp = (net_local *) dev->priv;
+  int		i;
+
+  /* Look all addresses */
+  for(i = 0; i < lp->spy_number; i++)
+    /* If match */
+    if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE))
+      {
+	/* Update statistics */
+	lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
+	lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
+	lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
+	lp->spy_stat[i].updated = 0x7;
+      }
 }
+#endif	/* WIRELESS_SPY */
 
-static
-int
-wavelan_send_packet(struct sk_buff *skb, device *dev)
+#ifdef HISTOGRAM
+/*------------------------------------------------------------------*/
+/*
+ * This function calculate an histogram on the signal level.
+ * As the noise is quite constant, it's like doing it on the SNR.
+ * We have defined a set of interval (lp->his_range), and each time
+ * the level goes in that interval, we increment the count (lp->his_sum).
+ * With this histogram you may detect if one wavelan is really weak,
+ * or you may also calculate the mean and standard deviation of the level...
+ */
+static inline void
+wl_his_gather(device *	dev,
+	      u_char *	stats)		/* Statistics to gather */
 {
-	unsigned short	ioaddr;
-
-	ioaddr = dev->base_addr;
-
-	if (dev->tbusy)
-	{
-		/*
-		 * If we get here, some higher level
-		 * has decided we are broken.
-		 */
-		int	tickssofar;
-
-		tickssofar = jiffies - dev->trans_start;
-
-		/*
-		 * But for the moment, we will rely on wavelan_watchdog()
-		 * instead as it allows finer control over exactly when we
-		 * make the determination of failure.
-		 *
-		if (tickssofar < 5)
-		 */
-			return 1;
-
-		wavelan_scb_show(ioaddr);
-		wavelan_ru_show(dev);
-		wavelan_cu_show(dev);
-		wavelan_dev_show(dev);
-		wavelan_local_show(dev);
-
-		printk("%s: transmit timed out -- resetting board.\n", dev->name);
-
-		(void)wavelan_hardware_reset(dev);
-	}
+  net_local *	lp = (net_local *) dev->priv;
+  u_char	level = stats[0] & MMR_SIGNAL_LVL;
+  int		i;
 
-	/*
-	 * If some higher layer thinks we've missed
-	 * a tx-done interrupt we are passed NULL.
-	 * Caution: dev_tint() handles the cli()/sti() itself.
-	 */
-	if (skb == (struct sk_buff *)0)
-	{
-		dev_tint(dev);
-		return 0;
-	}
+  /* Find the correct interval */
+  i = 0;
+  while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++]))
+    ;
 
-	/*
-	 * Block a timer-based transmit from overlapping.
-	 */
-	if (set_bit(0, (void *)&dev->tbusy) == 0)
-	{
-		short		length;
-		unsigned char	*buf;
-
-		length = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
-		buf = skb->data;
-
-		hardware_send_packet(dev, buf, length);
-	}
-	else
-		printk("%s: Transmitter access conflict.\n", dev->name);
-
-	dev_kfree_skb(skb, FREE_WRITE);
+  /* Increment interval counter */
+  (lp->his_sum[i])++;
+}
+#endif	/* HISTOGRAM */
 
-	return 0;
+/*------------------------------------------------------------------*/
+/*
+ * Perform ioctl : config & info stuff
+ * This is here that are treated the wireless extensions (iwconfig)
+ */
+static int
+wavelan_ioctl(struct device *	dev,	/* Device on wich the ioctl apply */
+	      struct ifreq *	rq,	/* Data passed */
+	      int		cmd)	/* Ioctl number */
+{
+  unsigned short	ioaddr = dev->base_addr;
+  net_local *		lp = (net_local *)dev->priv;	/* lp is not unused */
+  struct iwreq *	wrq = (struct iwreq *) rq;
+  psa_t			psa;
+  mm_t			m;
+  unsigned long		x;
+  int			ret = 0;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd);
+#endif
+
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+
+  /* Look what is the request */
+  switch(cmd)
+    {
+      /* --------------- WIRELESS EXTENSIONS --------------- */
+
+    case SIOCGIWNAME:
+      strcpy(wrq->u.name, "Wavelan");
+      break;
+
+    case SIOCSIWNWID:
+      /* Set NWID in wavelan */
+      if(wrq->u.nwid.on)
+	{
+	  /* Set NWID in psa */
+	  psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+	  psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
+	  psa.psa_nwid_select = 0x01;
+	  psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
+		    (unsigned char *)psa.psa_nwid, 3);
+
+	  /* Set NWID in mmc */
+	  m.w.mmw_netw_id_l = wrq->u.nwid.nwid & 0xFF;
+	  m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+	  mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m,
+		    (unsigned char *)&m.w.mmw_netw_id_l, 2);
+	  m.w.mmw_loopt_sel = 0x00;
+	  mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
+		    (unsigned char *)&m.w.mmw_loopt_sel, 1);
+	}
+      else
+	{
+	  /* Disable nwid in the psa */
+	  psa.psa_nwid_select = 0x00;
+	  psa_write(ioaddr, lp->hacr,
+		    (char *)&psa.psa_nwid_select - (char *)&psa,
+		    (unsigned char *)&psa.psa_nwid_select, 1);
+
+	  /* Disable nwid in the mmc (no check) */
+	  m.w.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+	  mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
+		    (unsigned char *)&m.w.mmw_loopt_sel, 1);
+	}
+      break;
+
+    case SIOCGIWNWID:
+      /* Read the NWID */
+      psa_read(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
+	       (unsigned char *)psa.psa_nwid, 3);
+      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.on = psa.psa_nwid_select;
+      break;
+
+    case SIOCSIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+      if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+	ret = wv_set_frequency(ioaddr, wrq->u.freq);
+      else
+	ret = -EOPNOTSUPP;
+      break;
+
+    case SIOCGIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+       * (does it work for everybody ??? - especially old cards...) */
+      if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+	   (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+	{
+	  unsigned short	freq;
+
+	  /* Ask the EEprom to read the frequency from the first area */
+	  fee_read(ioaddr, 0x00 /* 1st area - frequency... */,
+		   &freq, 1);
+	  wrq->u.freq = ((freq >> 5) * 5 + 24000L) * 1e5;
+	}
+      else
+	{
+	  float	bands[] = { 915e6, 2.425e9, 2.46e9, 2.484e9, 2.4305e9 };
+
+	  psa_read(ioaddr, lp->hacr, (char *)&psa.psa_subband - (char *)&psa,
+		   (unsigned char *)&psa.psa_subband, 1);
+
+	  if(psa.psa_subband <= 4)
+	    wrq->u.freq = bands[psa.psa_subband];
+	  else
+	    ret = -EOPNOTSUPP;
+	}
+      break;
+
+    case SIOCGIWRANGE:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+	{
+	  struct iw_range	range;
+
+	  /* Verify the user buffer */
+	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+			    sizeof(struct iw_range));
+	  if(ret)
+	    break;
+
+	  /* Set the length (useless : its constant...) */
+	  wrq->u.data.length = sizeof(struct iw_range);
+
+	  /* Set information in the range struct */
+	  range.throughput = 1.6 * 1024 * 1024;	/* don't argue on this ! */
+	  range.min_nwid = 0x0000;
+	  range.max_nwid = 0xFFFF;
+
+	  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+	  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+	       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+	    {
+	      range.num_channels = 10;
+	      range.num_frequency = wv_frequency_list(ioaddr, range.freq,
+						      IW_MAX_FREQUENCIES);
+	    }
+	  else
+	    range.num_channels = range.num_frequency = 0;
+
+	  range.max_qual.qual = MMR_SGNL_QUAL;
+	  range.max_qual.level = MMR_SIGNAL_LVL;
+	  range.max_qual.noise = MMR_SILENCE_LVL;
+
+	  /* Copy structure to the user buffer */
+	  copy_to_user(wrq->u.data.pointer, &range,
+		       sizeof(struct iw_range));
+	}
+      break;
+
+    case SIOCGIWPRIV:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+	{
+	  struct iw_priv_args	priv[] =
+	  {	/* cmd,		set_args,	get_args,	name */
+	    { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
+	    { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
+	    { SIOCSIPLTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setlevelthr" },
+	    { SIOCGIPLTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getlevelthr" },
+
+	    { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,	0, "sethisto" },
+	    { SIOCGIPHISTO, 0,	    IW_PRIV_TYPE_INT | 16, "gethisto" },
+	  };
+
+	  /* Verify the user buffer */
+	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+			    sizeof(priv));
+	  if(ret)
+	    break;
+
+	  /* Set the number of ioctl available */
+	  wrq->u.data.length = 6;
+
+	  /* Copy structure to the user buffer */
+	  copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+		       sizeof(priv));
+	}
+      break;
+
+#ifdef WIRELESS_SPY
+    case SIOCSIWSPY:
+      /* Set the spy list */
+
+      /* Check the number of addresses */
+      if(wrq->u.data.length > IW_MAX_SPY)
+	{
+	  ret = -E2BIG;
+	  break;
+	}
+      lp->spy_number = wrq->u.data.length;
+
+      /* If there is some addresses to copy */
+      if(lp->spy_number > 0)
+	{
+	  struct sockaddr	address[IW_MAX_SPY];
+	  int			i;
+
+	  /* Verify where the user has set his addresses */
+	  ret = verify_area(VERIFY_READ, wrq->u.data.pointer,
+			    sizeof(struct sockaddr) * lp->spy_number);
+	  if(ret)
+	    break;
+	  /* Copy addresses to the driver */
+	  copy_from_user(address, wrq->u.data.pointer,
+			 sizeof(struct sockaddr) * lp->spy_number);
+
+	  /* Copy addresses to the lp structure */
+	  for(i = 0; i < lp->spy_number; i++)
+	    {
+	      memcpy(lp->spy_address[i], address[i].sa_data,
+		     WAVELAN_ADDR_SIZE);
+	    }
+
+	  /* Reset structure... */
+	  memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY);
+
+#ifdef DEBUG_IOCTL_INFO
+	  printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n");
+	  for(i = 0; i < wrq->u.data.length; i++)
+	    printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X \n",
+		   lp->spy_address[i][0],
+		   lp->spy_address[i][1],
+		   lp->spy_address[i][2],
+		   lp->spy_address[i][3],
+		   lp->spy_address[i][4],
+		   lp->spy_address[i][5]);
+#endif	/* DEBUG_IOCTL_INFO */
+	}
+
+      break;
+
+    case SIOCGIWSPY:
+      /* Get the spy list and spy stats */
+
+      /* Set the number of addresses */
+      wrq->u.data.length = lp->spy_number;
+
+      /* If the user want to have the addresses back... */
+      if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+	{
+	  struct sockaddr	address[IW_MAX_SPY];
+	  int			i;
+
+	  /* Verify the user buffer */
+	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+			    (sizeof(iw_qual) + sizeof(struct sockaddr))
+			    * IW_MAX_SPY);
+	  if(ret)
+	    break;
+
+	  /* Copy addresses from the lp structure */
+	  for(i = 0; i < lp->spy_number; i++)
+	    {
+	      memcpy(address[i].sa_data, lp->spy_address[i],
+		     WAVELAN_ADDR_SIZE);
+	      address[i].sa_family = AF_UNIX;
+	    }
+
+	  /* Copy addresses to the user buffer */
+	  copy_to_user(wrq->u.data.pointer, address,
+		       sizeof(struct sockaddr) * lp->spy_number);
+
+	  /* Copy stats to the user buffer (just after) */
+	  copy_to_user(wrq->u.data.pointer +
+		       (sizeof(struct sockaddr) * lp->spy_number),
+		       lp->spy_stat, sizeof(iw_qual) * lp->spy_number);
+
+	  /* Reset updated flags */
+	  for(i = 0; i < lp->spy_number; i++)
+	    lp->spy_stat[i].updated = 0x0;
+	}	/* if(pointer != NULL) */
+
+      break;
+#endif	/* WIRELESS_SPY */
+
+      /* ------------------ PRIVATE IOCTL ------------------ */
+
+    case SIOCSIPQTHR:
+      if(!suser())
+	return -EPERM;
+      psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
+	       (unsigned char *)&psa.psa_quality_thr, 1);
+      mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
+      break;
+
+    case SIOCGIPQTHR:
+      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
+	       (unsigned char *)&psa.psa_quality_thr, 1);
+      *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
+      break;
+
+    case SIOCSIPLTHR:
+      if(!suser())
+	return -EPERM;
+      psa.psa_thr_pre_set = *(wrq->u.name) & 0x3F;
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+	       (unsigned char *)&psa.psa_thr_pre_set, 1);
+      mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
+      break;
+
+    case SIOCGIPLTHR:
+      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+	       (unsigned char *)&psa.psa_thr_pre_set, 1);
+      *(wrq->u.name) = psa.psa_thr_pre_set & 0x3F;
+      break;
+
+#ifdef HISTOGRAM
+    case SIOCSIPHISTO:
+      /* Verif if the user is root */
+      if(!suser())
+	return -EPERM;
+
+      /* Check the number of intervals */
+      if(wrq->u.data.length > 16)
+	{
+	  ret = -E2BIG;
+	  break;
+	}
+      lp->his_number = wrq->u.data.length;
+
+      /* If there is some addresses to copy */
+      if(lp->his_number > 0)
+	{
+	  /* Verify where the user has set his addresses */
+	  ret = verify_area(VERIFY_READ, wrq->u.data.pointer,
+			    sizeof(char) * lp->his_number);
+	  if(ret)
+	    break;
+	  /* Copy interval ranges to the driver */
+	  copy_from_user(lp->his_range, wrq->u.data.pointer,
+			 sizeof(char) * lp->his_number);
+
+	  /* Reset structure... */
+	  memset(lp->his_sum, 0x00, sizeof(long) * 16);
+	}
+      break;
+
+    case SIOCGIPHISTO:
+      /* Set the number of intervals */
+      wrq->u.data.length = lp->his_number;
+
+      /* Give back the distribution statistics */
+      if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+	{
+	  /* Verify the user buffer */
+	  ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+			    sizeof(long) * 16);
+	  if(ret)
+	    break;
+
+	  /* Copy data to the user buffer */
+	  copy_to_user(wrq->u.data.pointer, lp->his_sum,
+		       sizeof(long) * lp->his_number);
+	}	/* if(pointer != NULL) */
+      break;
+#endif	/* HISTOGRAM */
+
+      /* ------------------- OTHER IOCTL ------------------- */
+
+    default:
+      ret = -EOPNOTSUPP;
+    }
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
+#endif
+  return ret;
 }
 
-#if	0
-static
-int
-addrcmp(unsigned char *a0, unsigned char *a1)
+/*------------------------------------------------------------------*/
+/*
+ * Get wireless statistics
+ * Called by /proc/net/wireless...
+ */
+static iw_stats *
+wavelan_get_wireless_stats(device *	dev)
 {
-	int	i;
+  unsigned short	ioaddr = dev->base_addr;
+  net_local *		lp = (net_local *) dev->priv;
+  mmr_t			m;
+  iw_stats *		wstats;
+  unsigned long		x;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
+#endif
+
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+
+  if(lp == (net_local *) NULL)
+    return (iw_stats *) NULL;
+  wstats = &lp->wstats;
+
+  /* Get data from the mmc */
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+
+  mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
+  mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2);
+  mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4);
+
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+  /* Copy data to wireless stuff */
+  wstats->status = m.mmr_dce_status;
+  wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
+  wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
+  wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
+  wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) |
+			  ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) |
+			  ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
+  wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+  wstats->discard.crypt = 0L;
+  wstats->discard.misc = 0L;
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
+#endif
+  return &lp->wstats;
+}
+#endif	/* WIRELESS_EXT */
 
-	for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-	{
-		if (a0[i] != a1[i])
-			return a0[i] - a1[i];
-	}
+/************************* PACKET RECEPTION *************************/
+/*
+ * This part deal with receiving the packets.
+ * The interrupt handler get an interrupt when a packet has been
+ * successfully received and called this part...
+ */
 
-	return 0;
+/*------------------------------------------------------------------*/
+/*
+ * This routine does the actual copy of data (including the ethernet
+ * header structure) from the WaveLAN card to an sk_buff chain that
+ * will be passed up to the network interface layer. NOTE: We
+ * currently don't handle trailer protocols (neither does the rest of
+ * the network interface), so if that is needed, it will (at least in
+ * part) be added here.  The contents of the receive ring buffer are
+ * copied to a message chain that is then passed to the kernel.
+ *
+ * Note: if any errors occur, the packet is "dropped on the floor"
+ * (called by wv_packet_rcv())
+ */
+static inline void
+wv_packet_read(device *		dev,
+	       u_short		buf_off,
+	       int		sksize)
+{
+  net_local *		lp = (net_local *) dev->priv;
+  u_short		ioaddr = dev->base_addr;
+  struct sk_buff *	skb;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
+	 dev->name, fd_p, sksize);
+#endif
+
+  /* Allocate buffer for the data */
+  if((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
+	     dev->name, sksize);
+#endif
+      lp->stats.rx_dropped++;
+      return;
+    }
+
+  skb->dev = dev;
+
+  /* Copy the packet to the buffer */
+  obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
+  skb->protocol=eth_type_trans(skb, dev);
+
+#ifdef DEBUG_RX_INFO
+  wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+#endif	/* DEBUG_RX_INFO */
+
+  /* Statistics gathering & stuff associated.
+   * It seem a bit messy with all the define, but it's really simple... */
+#if defined(WIRELESS_SPY) || defined(HISTOGRAM)
+  if(
+#ifdef WIRELESS_SPY
+     (lp->spy_number > 0) ||
+#endif	/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+     (lp->his_number > 0) ||
+#endif	/* HISTOGRAM */
+     0)
+    {
+      u_char	stats[3];	/* Signal level, Noise level, Signal quality */
+
+      /* read signal level, silence level and signal quality bytes */
+      /* Note : in the Pcmcia hardware, these are part of the frame. It seem
+       * that for the ISA hardware, it's nowhere to be found in the frame,
+       * so I'm oblige to do this (it has side effect on /proc/net/wireless)
+       * Any idea ? */
+      mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+      mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3);
+      mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef DEBUG_RX_INFO
+      printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
+	     dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F);
+#endif
+
+      /* Spying stuff */
+#ifdef WIRELESS_SPY
+      wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
+#endif	/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+      wl_his_gather(dev, stats);
+#endif	/* HISTOGRAM */
+    }
+#endif	/* defined(WIRELESS_SPY) || defined(HISTOGRAM) */
+
+  /*
+   * Hand the packet to the Network Module
+   */
+  netif_rx(skb);
+
+  lp->stats.rx_packets++;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
+#endif
 }
-#endif	/* 0 */
 
+/*------------------------------------------------------------------*/
 /*
  * Transfer as many packets as we can
  * from the device RAM.
  * Called by the interrupt handler.
  */
-static
-void
-wavelan_receive(device *dev)
+static inline void
+wv_receive(device *	dev)
 {
-	unsigned short	ioaddr;
-	net_local	*lp;
-	int		nreaped;
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-	nreaped = 0;
-
-	for (;;)
-	{
-		fd_t		fd;
-		rbd_t		rbd;
-		ushort		pkt_len;
-		int		sksize;
-		struct sk_buff	*skb;
-
-		obram_read(ioaddr, lp->rx_head, (unsigned char *)&fd, sizeof(fd));
-
-		if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
-			break;
-
-		nreaped++;
-
-		if
-		(
-			(fd.fd_status & (FD_STATUS_B | FD_STATUS_OK))
-			!=
-			(FD_STATUS_B | FD_STATUS_OK)
-		)
-		{
-			/*
-			 * Not sure about this one -- it does not seem
-			 * to be an error so we will keep quiet about it.
-			if ((fd.fd_status & FD_STATUS_B) != FD_STATUS_B)
-				printk("%s: frame not consumed by RU.\n", dev->name);
-			 */
-
-			if ((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK)
-				printk("%s: frame not received successfully.\n", dev->name);
-		}
-
-		if ((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 | FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11)) != 0)
-		{
-			lp->stats.rx_errors++;
-
-			if ((fd.fd_status & FD_STATUS_S6) != 0)
-				printk("%s: no EOF flag.\n", dev->name);
-
-			if ((fd.fd_status & FD_STATUS_S7) != 0)
-			{
-				lp->stats.rx_length_errors++;
-				printk("%s: frame too short.\n", dev->name);
-			}
-
-			if ((fd.fd_status & FD_STATUS_S8) != 0)
-			{
-				lp->stats.rx_over_errors++;
-				printk("%s: rx DMA overrun.\n", dev->name);
-			}
-
-			if ((fd.fd_status & FD_STATUS_S9) != 0)
-			{
-				lp->stats.rx_fifo_errors++;
-				printk("%s: ran out of resources.\n", dev->name);
-			}
-
-			if ((fd.fd_status & FD_STATUS_S10) != 0)
-			{
-				lp->stats.rx_frame_errors++;
-				printk("%s: alignment error.\n", dev->name);
-			}
-
-			if ((fd.fd_status & FD_STATUS_S11) != 0)
-			{
-				lp->stats.rx_crc_errors++;
-				printk("%s: CRC error.\n", dev->name);
-			}
-		}
-
-		if (fd.fd_rbd_offset == I82586NULL)
-			printk("%s: frame has no data.\n", dev->name);
-		else
-		{
-			obram_read(ioaddr, fd.fd_rbd_offset, (unsigned char *)&rbd, sizeof(rbd));
-
-			if ((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF)
-				printk("%s: missing EOF flag.\n", dev->name);
-
-			if ((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F)
-				printk("%s: missing F flag.\n", dev->name);
-
-			pkt_len = rbd.rbd_status & RBD_STATUS_ACNT;
-
-#if	0
-			{
-				unsigned char		addr[WAVELAN_ADDR_SIZE];
-				int			i;
-				static unsigned char	toweraddr[WAVELAN_ADDR_SIZE]	=
-				{
-					0x08, 0x00, 0x0e, 0x20, 0x3e, 0xd3,
-				};
-
-				obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
-				if
-				(
-					/*
-					addrcmp(&addr[0], &dev->dev_addr[0]) != 0
-					&&
-					*/
-					addrcmp(&addr[0], toweraddr) != 0
-				)
-				{
-					printk("%s: foreign MAC source addr=", dev->name);
-					for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-						printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
-					printk("\n");
-				}
-			}
-#endif	/* 0 */
-
-			if (wavelan_debug > 5)
-			{
-				unsigned char	addr[WAVELAN_ADDR_SIZE];
-				unsigned short	ltype;
-				int		i;
-
-#if	0
-				printk("%s: fd_dest=", dev->name);
-				for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-					printk("%s%02x", (i == 0) ? "" : ":", fd.fd_dest[i]);
-				printk("\n");
-
-				printk("%s: fd_src=", dev->name);
-				for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-					printk("%s%02x", (i == 0) ? "" : ":", fd.fd_src[i]);
-				printk("\n");
-				printk("%s: fd_length=%d\n", dev->name, fd.fd_length);
-#endif	/* 0 */
-
-				obram_read(ioaddr, rbd.rbd_bufl, &addr[0], sizeof(addr));
-				printk("%s: dest=", dev->name);
-				for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-					printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
-				printk("\n");
-
-				obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
-				printk("%s: src=", dev->name);
-				for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-					printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
-				printk("\n");
-
-				obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr) * 2, (unsigned char *)&ltype, sizeof(ltype));
-				printk("%s: ntohs(length/type)=0x%04x\n", dev->name, ntohs(ltype));
-			}
-
-			sksize = pkt_len;
-
-			if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *)0)
-			{
-				printk("%s: could not alloc_skb(%d, GFP_ATOMIC).\n", dev->name, sksize);
-				lp->stats.rx_dropped++;
-			}
-			else
-			{
-				skb->dev = dev;
-
-				obram_read(ioaddr, rbd.rbd_bufl, skb_put(skb,pkt_len), pkt_len);
-
-				if (wavelan_debug > 5)
-				{
-					int	i;
-					int	maxi;
-
-					printk("%s: pkt_len=%d, data=\"", dev->name, pkt_len);
-
-					if ((maxi = pkt_len) > 16)
-						maxi = 16;
-				
-					for (i = 0; i < maxi; i++)
-					{
-						unsigned char	c;
-
-						c = skb->data[i];
-						if (c >= ' ' && c <= '~')
-							printk(" %c", skb->data[i]);
-						else
-							printk("%02x", skb->data[i]);
-					}
-
-					if (maxi < pkt_len)
-						printk("..");
-				
-					printk("\"\n\n");
-				}
-			
-				skb->protocol=eth_type_trans(skb,dev);
-				netif_rx(skb);
-
-				lp->stats.rx_packets++;
-			}
-		}
-
-		fd.fd_status = 0;
-		obram_write(ioaddr, fdoff(lp->rx_head, fd_status), (unsigned char *)&fd.fd_status, sizeof(fd.fd_status));
-
-		fd.fd_command = FD_COMMAND_EL;
-		obram_write(ioaddr, fdoff(lp->rx_head, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
-
-		fd.fd_command = 0;
-		obram_write(ioaddr, fdoff(lp->rx_last, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
-
-		lp->rx_last = lp->rx_head;
-		lp->rx_head = fd.fd_link_offset;
-	}
-
-/*
-	if (nreaped > 1)
-		printk("r%d", nreaped);
-*/
+  u_short	ioaddr = dev->base_addr;
+  net_local *	lp = (net_local *)dev->priv;
+  int		nreaped = 0;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name);
+#endif
+
+  /* Loop on each received packet */
+  for(;;)
+    {
+      fd_t		fd;
+      rbd_t		rbd;
+      ushort		pkt_len;
+
+      obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, sizeof(fd));
+
+      /* If the current frame is not complete, we have reach the end... */
+      if((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
+	break;		/* This is how we exit the loop */
+
+      nreaped++;
+
+      /* Check if frame correctly received */
+      if((fd.fd_status & (FD_STATUS_B | FD_STATUS_OK)) !=
+	 (FD_STATUS_B | FD_STATUS_OK))
+	{
+	  /*
+	   * Not sure about this one -- it does not seem
+	   * to be an error so we will keep quiet about it.
+	   */
+#ifndef IGNORE_NORMAL_XMIT_ERRS
+#ifdef DEBUG_RX_ERROR
+	  if((fd.fd_status & FD_STATUS_B) != FD_STATUS_B)
+	    printk(KERN_INFO "%s: wv_receive(): frame not consumed by RU.\n",
+		   dev->name);
+#endif
+#endif	/* IGNORE_NORMAL_XMIT_ERRS */
+
+#ifdef DEBUG_RX_ERROR
+	  if((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK)
+	    printk(KERN_INFO "%s: wv_receive(): frame not received successfully.\n",
+		   dev->name);
+#endif
+	}
+
+      /* Check is there was problems in the frame processing */
+      if((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 |
+			  FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11))
+	 != 0)
+	{
+	  lp->stats.rx_errors++;
+
+#ifdef DEBUG_RX_ERROR
+	  if((fd.fd_status & FD_STATUS_S6) != 0)
+	    printk(KERN_INFO "%s: wv_receive(): no EOF flag.\n", dev->name);
+#endif
+
+	  if((fd.fd_status & FD_STATUS_S7) != 0)
+	    {
+	      lp->stats.rx_length_errors++;
+#ifdef DEBUG_RX_ERROR
+	      printk(KERN_INFO "%s: wv_receive(): frame too short.\n",
+		     dev->name);
+#endif
+	    }
+
+	  if((fd.fd_status & FD_STATUS_S8) != 0)
+	    {
+	      lp->stats.rx_over_errors++;
+#ifdef DEBUG_RX_ERROR
+	      printk(KERN_INFO "%s: wv_receive(): rx DMA overrun.\n",
+		     dev->name);
+#endif
+	    }
+
+	  if((fd.fd_status & FD_STATUS_S9) != 0)
+	    {
+	      lp->stats.rx_fifo_errors++;
+#ifdef DEBUG_RX_ERROR
+	      printk(KERN_INFO "%s: wv_receive(): ran out of resources.\n",
+		     dev->name);
+#endif
+	    }
+
+	  if((fd.fd_status & FD_STATUS_S10) != 0)
+	    {
+	      lp->stats.rx_frame_errors++;
+#ifdef DEBUG_RX_ERROR
+	      printk(KERN_INFO "%s: wv_receive(): alignment error.\n",
+		     dev->name);
+#endif
+	    }
+
+	  if((fd.fd_status & FD_STATUS_S11) != 0)
+	    {
+	      lp->stats.rx_crc_errors++;
+#ifdef DEBUG_RX_ERROR
+	      printk(KERN_INFO "%s: wv_receive(): CRC error.\n", dev->name);
+#endif
+	    }
+	}
+
+      /* Check if frame contain a pointer to the data */
+      if(fd.fd_rbd_offset == I82586NULL)
+#ifdef DEBUG_RX_ERROR
+	printk(KERN_INFO "%s: wv_receive(): frame has no data.\n", dev->name);
+#endif
+      else
+	{
+	  obram_read(ioaddr, fd.fd_rbd_offset,
+		     (unsigned char *) &rbd, sizeof(rbd));
+
+#ifdef DEBUG_RX_ERROR
+	  if((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF)
+	    printk(KERN_INFO "%s: wv_receive(): missing EOF flag.\n",
+		   dev->name);
+
+	  if((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F)
+	    printk(KERN_INFO "%s: wv_receive(): missing F flag.\n",
+		   dev->name);
+#endif
+
+	  pkt_len = rbd.rbd_status & RBD_STATUS_ACNT;
+
+	  /* Read the packet and transmit to Linux */
+	  wv_packet_read(dev, rbd.rbd_bufl, pkt_len);
+	}	/* if frame has data */
+
+      fd.fd_status = 0;
+      obram_write(ioaddr, fdoff(lp->rx_head, fd_status),
+		  (unsigned char *) &fd.fd_status, sizeof(fd.fd_status));
+
+      fd.fd_command = FD_COMMAND_EL;
+      obram_write(ioaddr, fdoff(lp->rx_head, fd_command),
+		  (unsigned char *) &fd.fd_command, sizeof(fd.fd_command));
+
+      fd.fd_command = 0;
+      obram_write(ioaddr, fdoff(lp->rx_last, fd_command),
+		  (unsigned char *) &fd.fd_command, sizeof(fd.fd_command));
+
+      lp->rx_last = lp->rx_head;
+      lp->rx_head = fd.fd_link_offset;
+    }	/* for(;;) -> loop on all frames */
+
+#ifdef DEBUG_RX_INFO
+  if(nreaped > 1)
+    printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n", dev->name, nreaped);
+#endif
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name);
+#endif
 }
 
+/*********************** PACKET TRANSMISSION ***********************/
 /*
- * Command completion interrupt.
- * Reclaim as many freed tx buffers as we can.
+ * This part deal with sending packet through the wavelan
+ *
  */
-static
-int
-wavelan_complete(device *dev, unsigned short ioaddr, net_local *lp)
-{
-	int	nreaped;
-
-	nreaped = 0;
-
-	for (;;)
-	{
-		unsigned short	tx_status;
-
-		if (lp->tx_first_in_use == I82586NULL)
-			break;
 
-		obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status));
-
-		if ((tx_status & AC_SFLD_C) == 0)
-			break;
-
-		nreaped++;
-
-		--lp->tx_n_in_use;
+/*------------------------------------------------------------------*/
+/*
+ * This routine fills in the appropriate registers and memory
+ * locations on the WaveLAN card and starts the card off on
+ * the transmit.
+ *
+ * The principle :
+ * Each block contain a transmit command, a nop command,
+ * a transmit block descriptor and a buffer.
+ * The CU read the transmit block which point to the tbd,
+ * read the tbd and the the content of the buffer.
+ * When it has finish with it, it goes to the next command
+ * which in our case is the nop. The nop point on itself,
+ * so the CU stop here.
+ * When we add the next block, we modify the previous nop
+ * to make it point on the new tx command.
+ * Simple, isn't it ?
+ *
+ * (called in wavelan_packet_xmit())
+ */
+static inline void
+wv_packet_write(device *	dev,
+		void *	buf,
+		short	length)
+{
+  net_local *		lp = (net_local *) dev->priv;
+  u_short		ioaddr = dev->base_addr;
+  unsigned short	txblock;
+  unsigned short	txpred;
+  unsigned short	tx_addr;
+  unsigned short	nop_addr;
+  unsigned short	tbd_addr;
+  unsigned short	buf_addr;
+  ac_tx_t		tx;
+  ac_nop_t		nop;
+  tbd_t			tbd;
+  int			clen = length;
+  unsigned long		x;
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
+#endif
+
+  /* Check if we need some padding */
+  if(clen < ETH_ZLEN)
+    clen = ETH_ZLEN;
+
+  x = wv_splhi();
+
+  /* Calculate addresses of next block and previous block */
+  txblock = lp->tx_first_free;
+  txpred = txblock - TXBLOCKZ;
+  if(txpred < OFFSET_CU)
+    txpred += NTXBLOCKS * TXBLOCKZ;
+  lp->tx_first_free += TXBLOCKZ;
+  if(lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+    lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
 
 /*
 if (lp->tx_n_in_use > 0)
 	printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
 */
 
-		if (lp->tx_n_in_use <= 0)
-			lp->tx_first_in_use = I82586NULL;
-		else
-		{
-			lp->tx_first_in_use += TXBLOCKZ;
-			if (lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-				lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ;
-		}
-
-		if (tx_status & AC_SFLD_OK)
-		{
-			int	ncollisions;
-
-			lp->stats.tx_packets++;
-			ncollisions = tx_status & AC_SFLD_MAXCOL;
-			lp->stats.collisions += ncollisions;
-			/*
-			if (ncollisions > 0)
-				printk("%s: tx completed after %d collisions.\n", dev->name, ncollisions);
-			*/
-		}
-		else
-		{
-			lp->stats.tx_errors++;
-			if (tx_status & AC_SFLD_S10)
-			{
-				lp->stats.tx_carrier_errors++;
-				if (wavelan_debug > 0)
-					printk("%s:     tx error: no CS.\n", dev->name);
-			}
-			if (tx_status & AC_SFLD_S9)
-			{
-				lp->stats.tx_carrier_errors++;
-				printk("%s:     tx error: lost CTS.\n", dev->name);
-			}
-			if (tx_status & AC_SFLD_S8)
-			{
-				lp->stats.tx_fifo_errors++;
-				printk("%s:     tx error: slow DMA.\n", dev->name);
-			}
-			if (tx_status & AC_SFLD_S6)
-			{
-				lp->stats.tx_heartbeat_errors++;
-				if (wavelan_debug > 0)
-					printk("%s:     tx error: heart beat.\n", dev->name);
-			}
-			if (tx_status & AC_SFLD_S5)
-			{
-				lp->stats.tx_aborted_errors++;
-				if (wavelan_debug > 0)
-					printk("%s:     tx error: too many collisions.\n", dev->name);
-			}
-		}
-
-		if (wavelan_debug > 5)
-			printk("%s:     tx completed, tx_status 0x%04x.\n", dev->name, tx_status);
-	}
-
-/*
-	if (nreaped > 1)
-		printk("c%d", nreaped);
-*/
-
-	/*
-	 * Inform upper layers.
-	 */
-	if (lp->tx_n_in_use < NTXBLOCKS - 1)
-	{
-		dev->tbusy = 0;
-		mark_bh(NET_BH);
-	}
+  lp->tx_n_in_use++;
 
-	return nreaped;
+  /* Calculate addresses of the differents part of the block */
+  tx_addr = txblock;
+  nop_addr = tx_addr + sizeof(tx);
+  tbd_addr = nop_addr + sizeof(nop);
+  buf_addr = tbd_addr + sizeof(tbd);
+
+  /*
+   * Transmit command.
+   */
+  tx.tx_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+	      (unsigned char *) &tx.tx_h.ac_status,
+	      sizeof(tx.tx_h.ac_status));
+
+  /*
+   * NOP command.
+   */
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+	      (unsigned char *) &nop.nop_h.ac_status,
+	      sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = nop_addr;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+	      (unsigned char *) &nop.nop_h.ac_link,
+	      sizeof(nop.nop_h.ac_link));
+
+  /*
+   * Transmit buffer descriptor. 
+   */
+  tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen);
+  tbd.tbd_next_bd_offset = I82586NULL;
+  tbd.tbd_bufl = buf_addr;
+  tbd.tbd_bufh = 0;
+  obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+
+  /*
+   * Data.
+   */
+  obram_write(ioaddr, buf_addr, buf, clen);
+
+  /*
+   * Overwrite the predecessor NOP link
+   * so that it points to this txblock.
+   */
+  nop_addr = txpred + sizeof(tx);
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+	      (unsigned char *)&nop.nop_h.ac_status,
+	      sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = txblock;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+	      (unsigned char *) &nop.nop_h.ac_link,
+	      sizeof(nop.nop_h.ac_link));
+
+  /* If watchdog not already active, activate it... */
+  if(lp->watchdog.prev == (timer_list *) NULL)
+    {
+      /* set timer to expire in WATCHDOG_JIFFIES */
+      lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+      add_timer(&lp->watchdog);
+    }
+
+  if(lp->tx_first_in_use == I82586NULL)
+    lp->tx_first_in_use = txblock;
+
+  if(lp->tx_n_in_use < NTXBLOCKS - 1)
+    dev->tbusy = 0;
+
+  wv_splx(x);
+
+#ifdef DEBUG_TX_INFO
+  wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
+#endif	/* DEBUG_TX_INFO */
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
+#endif
 }
 
-static
-void
-wavelan_watchdog(unsigned long a)
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called when we want to send a packet (NET3 callback)
+ * In this routine, we check if the the harware is ready to accept
+ * the packet. We also prevent reentrance. Then, we call the function
+ * to send the packet...
+ */
+static int
+wavelan_packet_xmit(struct sk_buff *	skb,
+		    device *		dev)
 {
-	device		*dev;
-	net_local	*lp;
-	unsigned short	ioaddr;
-	unsigned long	x;
-	unsigned int	nreaped;
-
-	x = wavelan_splhi();
-
-	dev = (device *)a;
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	if (lp->tx_n_in_use <= 0)
-	{
-		wavelan_splx(x);
-		return;
-	}
-
-	lp->watchdog.expires = jiffies+WATCHDOG_JIFFIES;
-	add_timer(&lp->watchdog);
+  net_local *	lp = (net_local *)dev->priv;
 
-	if (jiffies - dev->trans_start < WATCHDOG_JIFFIES)
-	{
-		wavelan_splx(x);
-		return;
-	}
-
-	nreaped = wavelan_complete(dev, ioaddr, lp);
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+	 (unsigned) skb);
+#endif
+
+  /* This flag indicate that the hardware can't perform a transmission.
+   * Theoritically, NET3 check it before sending a packet to the driver,
+   * but in fact it never do that and pool continuously.
+   * As the watchdog will abort too long transmissions, we are quite safe...
+   */
+  if(dev->tbusy)
+    return 1;
+
+  /*
+   * If some higher layer thinks we've missed
+   * a tx-done interrupt we are passed NULL.
+   * Caution: dev_tint() handles the cli()/sti() itself.
+   */
+  if(skb == (struct sk_buff *)0)
+    {
+#ifdef DEBUG_TX_ERROR
+      printk(KERN_INFO "%s: wavelan_packet_xmit(): skb == NULL\n", dev->name);
+#endif
+      dev_tint(dev);
+      return 0;
+    }
+
+  /*
+   * Block a timer-based transmit from overlapping.
+   * In other words, prevent reentering this routine.
+   */
+  if(set_bit(0, (void *)&dev->tbusy) != 0)
+#ifdef DEBUG_TX_ERROR
+    printk(KERN_INFO "%s: Transmitter access conflict.\n", dev->name);
+#endif
+  else
+    {
+      /* If somebody has asked to reconfigure the controler, we can do it now */
+      if(lp->reconfig_82586)
+	{
+	  wv_82586_config(dev);
+	  if(dev->tbusy)
+	    return 1;
+	}
+
+#ifdef DEBUG_TX_ERROR
+      if(skb->next)
+	printk(KERN_INFO "skb has next\n");
+#endif
+
+      wv_packet_write(dev, skb->data, skb->len);
+    }
+
+  dev_kfree_skb(skb, FREE_WRITE);
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+#endif
+  return 0;
+}
 
-	printk("%s: warning: wavelan_watchdog(): %d reaped, %d remain.\n", dev->name, nreaped, lp->tx_n_in_use);
-	/*
-	wavelan_scb_show(ioaddr);
-	wavelan_ru_show(dev);
-	wavelan_cu_show(dev);
-	wavelan_dev_show(dev);
-	wavelan_local_show(dev);
-	*/
+/********************** HARDWARE CONFIGURATION **********************/
+/*
+ * This part do the real job of starting and configuring the hardware.
+ */
 
-	wavelan_splx(x);
+/*------------------------------------------------------------------*/
+/*
+ * Routine to initialize the Modem Management Controller.
+ * (called by wv_hw_reset())
+ */
+static inline int
+wv_mmc_init(device *	dev)
+{
+  u_short	ioaddr = dev->base_addr;
+  net_local *	lp = (net_local *)dev->priv;
+  psa_t		psa;
+  mmw_t		m;
+  int		configured;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
+#endif
+
+  /* Read the parameter storage area */
+  psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef USE_PSA_CONFIG
+  configured = psa.psa_conf_status & 1;
+#else
+  configured = 0;
+#endif
+
+  /* Is the PSA is not configured */
+  if(!configured)
+    {
+      /* User will be able to configure NWID after (with iwconfig) */
+      psa.psa_nwid[0] = 0;
+      psa.psa_nwid[1] = 0;
+
+      /* As NWID is not set : no NWID checking */
+      psa.psa_nwid_select = 0;
+
+      /* Set to standard values
+       * 0x04 for AT,
+       * 0x01 for MCA,
+       * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
+       */
+      if (psa.psa_comp_number & 1)
+	psa.psa_thr_pre_set = 0x01;
+      else
+	psa.psa_thr_pre_set = 0x04;
+      psa.psa_quality_thr = 0x03;
+
+#ifdef USE_PSA_CONFIG
+      /* Write the psa */
+      psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
+		(unsigned char *)psa.psa_nwid, 3);
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+		(unsigned char *)&psa.psa_thr_pre_set, 1);
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
+		(unsigned char *)&psa.psa_quality_thr, 1);
+#endif
+    }
+
+  /* Zero the mmc structure */
+  memset(&m, 0x00, sizeof(m));
+
+  /* Copy PSA info to the mmc */
+  m.mmw_netw_id_l = psa.psa_nwid[1];
+  m.mmw_netw_id_h = psa.psa_nwid[0];
+  
+  if(psa.psa_nwid_select & 1)
+    m.mmw_loopt_sel = 0x00;
+  else
+    m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+
+  m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
+  m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
+
+  /* Missing : encryption stuff... */
+
+  /*
+   * Set default modem control parameters.
+   * See NCR document 407-0024326 Rev. A.
+   */
+  m.mmw_jabber_enable = 0x01;
+  m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+  m.mmw_ifs = 0x20;
+  m.mmw_mod_delay = 0x04;
+  m.mmw_jam_time = 0x38;
+
+  m.mmw_encr_enable = 0;
+  m.mmw_des_io_invert = 0;
+  m.mmw_freeze = 0;
+  m.mmw_decay_prm = 0;
+  m.mmw_decay_updat_prm = 0;
+
+  /* Write all info to mmc */
+  mmc_write(ioaddr, 0, (u_char *)&m, sizeof(m));
+
+  /* The following code start the modem of the 2.00 frequency
+   * selectable cards at power on. It's not strictly needed for the
+   * following boots...
+   * The original patch was by Joe Finney for the PCMCIA driver, but
+   * I've cleaned it a bit and add documentation.
+   * Thanks to Loeke Brederveld from Lucent for the info.
+   */
+
+  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+   * (does it work for everybody ??? - especially old cards...) */
+  /* Note : WFREQSEL verify that it is able to read from EEprom
+   * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
+   * is 0xA (Xilinx version) or 0xB (Ariadne version).
+   * My test is more crude but do work... */
+  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      m.mmw_fee_addr = 0x0F;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(ioaddr, (char *)&m.mmw_fee_ctrl - (char *)&m,
+		(unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+      fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_CONFIG_INFO
+      /* The frequency was in the last word downloaded... */
+      mmc_read(ioaddr, (char *)&m.mmw_fee_data_l - (char *)&m,
+	       (unsigned char *)&m.mmw_fee_data_l, 2);
+
+      /* Print some info for the user */
+      printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
+	     dev->name,
+	     ((m.mmw_fee_data_h << 4) |
+	      (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
+#endif
+
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      m.mmw_fee_addr = 0x61;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(ioaddr, (char *)&m.mmw_fee_ctrl - (char *)&m,
+		(unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+    }	/* if 2.00 card */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
+#endif
+  return 0;
 }
 
-static
-void
-wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+/*------------------------------------------------------------------*/
+/*
+ * Construct the fd and rbd structures.
+ * Start the receive unit.
+ * (called by wv_hw_reset())
+ */
+static inline int
+wv_ru_start(device *	dev)
 {
-	device		*dev;
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned short	hasr;
-	unsigned short	status;
-	unsigned short	ack_cmd;
-
-	if ((dev = (device *)(irq2dev_map[irq])) == (device *)0)
-	{
-		printk("wavelan_interrupt(): irq %d for unknown device.\n", irq);
-		return;
-	}
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	dev->interrupt = 1;
-
-	if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR)
-	{
-		unsigned char	dce_status;
-
-		/*
-		 * Interrupt from the modem management controller.
-		 * This will clear it -- ignored for now.
-		 */
-		mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status));
-		if (wavelan_debug > 0)
-			printk("%s: warning: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", dev->name, dce_status);
-	}
-
-	if ((hasr & HASR_82586_INTR) == 0)
-	{
-		dev->interrupt = 0;
-		if (wavelan_debug > 0)
-			printk("%s: warning: wavelan_interrupt() but (hasr & HASR_82586_INTR) == 0.\n", dev->name);
-		return;
-	}
-
-	obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&status, sizeof(status));
-
-	/*
-	 * Acknowledge the interrupt(s).
-	 */
-	ack_cmd = status & SCB_ST_INT;
-
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&ack_cmd, sizeof(ack_cmd));
-
-	set_chan_attn(ioaddr, lp->hacr);
-
-	if (wavelan_debug > 5)
-		printk("%s: interrupt, status 0x%04x.\n", dev->name, status);
-
-	if ((status & SCB_ST_CX) == SCB_ST_CX)
-	{
-		/*
-		 * Command completed.
-		 */
-		if (wavelan_debug > 5)
-			printk("%s: command completed.\n", dev->name);
-		(void)wavelan_complete(dev, ioaddr, lp);
-	}
-
-	if ((status & SCB_ST_FR) == SCB_ST_FR)
-	{
-		/*
-		 * Frame received.
-		 */
-		if (wavelan_debug > 5)
-			printk("%s: received packet.\n", dev->name);
-		wavelan_receive(dev);
-	}
-
-	if
-	(
-		(status & SCB_ST_CNA) == SCB_ST_CNA
-		||
-		(((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start)
-	)
-	{
-		printk("%s: warning: CU inactive -- restarting.\n", dev->name);
-
-		(void)wavelan_hardware_reset(dev);
-	}
-
-	if
-	(
-		(status & SCB_ST_RNR) == SCB_ST_RNR
-		||
-		(((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start)
-	)
-	{
-		printk("%s: warning: RU not ready -- restarting.\n", dev->name);
-
-		(void)wavelan_hardware_reset(dev);
-	}
-
-	dev->interrupt = 0;
+  net_local *	lp = (net_local *) dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  u_short	scb_cs;
+  fd_t		fd;
+  rbd_t		rbd;
+  u_short	rx;
+  u_short	rx_next;
+  int		i;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
+#endif
+
+  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+  if((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
+    return 0;
+
+  lp->rx_head = OFFSET_RU;
+
+  for(i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next)
+    {
+      rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
+
+      fd.fd_status = 0;
+      fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
+      fd.fd_link_offset = rx_next;
+      fd.fd_rbd_offset = rx + sizeof(fd);
+      obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd));
+
+      rbd.rbd_status = 0;
+      rbd.rbd_next_rbd_offset = I82586NULL;
+      rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
+      rbd.rbd_bufh = 0;
+      rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
+      obram_write(ioaddr, rx + sizeof(fd),
+		  (unsigned char *) &rbd, sizeof(rbd));
+
+      lp->rx_last = rx;
+    }
+
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset),
+	      (unsigned char *) &lp->rx_head, sizeof(lp->rx_head));
+
+  scb_cs = SCB_CMD_RUC_GO;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+	      (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+		 (unsigned char *) &scb_cs, sizeof(scb_cs));
+      if (scb_cs == 0)
+	break;
+
+      udelay(10);
+    }
+
+  if(i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wavelan_ru_start(): board not accepting command.\n",
+	     dev->name);
+#endif
+      return -1;
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
+#endif
+  return 0;
 }
 
-static
-int
-wavelan_close(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Initialise the transmit blocks.
+ * Start the command unit executing the NOP
+ * self-loop of the first transmit block.
+ *
+ * Here, we create the list of send buffer used to transmit packets
+ * between the PC and the command unit. For each buffer, we create a
+ * buffer descriptor (pointing on the buffer), a transmit command
+ * (pointing to the buffer descriptor) and a nop command.
+ * The transmit command is linked to the nop, and the nop to itself.
+ * When we will have finish to execute the transmit command, we will
+ * then loop on the nop. By releasing the nop link to a new command,
+ * we may send another buffer.
+ *
+ * (called by wv_hw_reset())
+ */
+static inline int
+wv_cu_start(device *	dev)
 {
-	unsigned short	ioaddr;
-	net_local	*lp;
-	unsigned short	scb_cmd;
-
-	if (wavelan_debug > 0)
-		printk("%s: ->wavelan_close(dev=0x%x)\n", dev->name, (unsigned int)dev);
-
-	ioaddr = dev->base_addr;
-	lp = (net_local *)dev->priv;
-
-	dev->tbusy = 1;
-	dev->start = 0;
-
-	/*
-	 * Flush the Tx and disable Rx.
-	 */
-	scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
-	obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
-	set_chan_attn(ioaddr, lp->hacr);
-
-	wavelan_ints_off(dev);
-
-	free_irq(dev->irq, NULL);
-	irq2dev_map[dev->irq] = (device *)0;
-
-	/*
-	 * Release the ioport-region.
-	 */
-	release_region(ioaddr, sizeof(ha_t));
-
-	MOD_DEC_USE_COUNT;
-
-	if (wavelan_debug > 0)
-		printk("%s: <-wavelan_close(): 0\n", dev->name);
-
-	return 0;
+  net_local *	lp = (net_local *) dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  int		i;
+  u_short	txblock;
+  u_short	first_nop;
+  u_short	scb_cs;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name);
+#endif
+
+  lp->tx_first_free = OFFSET_CU;
+  lp->tx_first_in_use = I82586NULL;
+
+  for(i = 0, txblock = OFFSET_CU;
+      i < NTXBLOCKS;
+      i++, txblock += TXBLOCKZ)
+    {
+      ac_tx_t		tx;
+      ac_nop_t		nop;
+      tbd_t		tbd;
+      unsigned short	tx_addr;
+      unsigned short	nop_addr;
+      unsigned short	tbd_addr;
+      unsigned short	buf_addr;
+
+      tx_addr = txblock;
+      nop_addr = tx_addr + sizeof(tx);
+      tbd_addr = nop_addr + sizeof(nop);
+      buf_addr = tbd_addr + sizeof(tbd);
+
+      tx.tx_h.ac_status = 0;
+      tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
+      tx.tx_h.ac_link = nop_addr;
+      tx.tx_tbd_offset = tbd_addr;
+      obram_write(ioaddr, tx_addr, (unsigned char *) &tx, sizeof(tx));
+
+      nop.nop_h.ac_status = 0;
+      nop.nop_h.ac_command = acmd_nop;
+      nop.nop_h.ac_link = nop_addr;
+      obram_write(ioaddr, nop_addr, (unsigned char *) &nop, sizeof(nop));
+
+      tbd.tbd_status = TBD_STATUS_EOF;
+      tbd.tbd_next_bd_offset = I82586NULL;
+      tbd.tbd_bufl = buf_addr;
+      tbd.tbd_bufh = 0;
+      obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd));
+    }
+
+  first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset),
+	      (unsigned char *) &first_nop, sizeof(first_nop));
+
+  scb_cs = SCB_CMD_CUC_GO;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+	      (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+		 (unsigned char *) &scb_cs, sizeof(scb_cs));
+      if (scb_cs == 0)
+	break;
+
+      udelay(10);
+    }
+
+  if(i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wavelan_cu_start(): board not accepting command.\n",
+	     dev->name);
+#endif
+      return -1;
+    }
+
+  lp->tx_n_in_use = 0;
+  dev->tbusy = 0;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name);
+#endif
+  return 0;
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Get the current statistics.
- * This may be called with the card open or closed.
+ * This routine does a standard config of the WaveLAN controler (i82586).
+ *
+ * It initialise the scp, iscp and scb structure
+ * The two first are only pointer to the next.
+ * The last one is used for basic configuration and for basic
+ * communication (interrupt status)
+ *
+ * (called by wv_hw_reset())
  */
-static
-en_stats	*
-wavelan_get_stats(device *dev)
+static inline int
+wv_82586_start(device *	dev)
 {
-	net_local	*lp;
-
-	lp = (net_local *)dev->priv;
-
-	return &lp->stats;
+  net_local *	lp = (net_local *) dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  scp_t		scp;		/* system configuration pointer */
+  iscp_t	iscp;		/* intermediate scp */
+  scb_t		scb;		/* system control block */
+  ach_t		cb;		/* Action command header */
+  u_char	zeroes[512];
+  int		i;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name);
+#endif
+
+  /*
+   * Clear the onboard RAM.
+   */
+  memset(&zeroes[0], 0x00, sizeof(zeroes));
+  for(i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
+    obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
+
+  /*
+   * Construct the command unit structures:
+   * scp, iscp, scb, cb.
+   */
+  memset(&scp, 0x00, sizeof(scp));
+  scp.scp_sysbus = SCP_SY_16BBUS;
+  scp.scp_iscpl = OFFSET_ISCP;
+  obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp));
+
+  memset(&iscp, 0x00, sizeof(iscp));
+  iscp.iscp_busy = 1;
+  iscp.iscp_offset = OFFSET_SCB;
+  obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+  /* Our first command is to reset the i82586 */
+  memset(&scb, 0x00, sizeof(scb));
+  scb.scb_command = SCB_CMD_RESET;
+  scb.scb_cbl_offset = OFFSET_CU;
+  scb.scb_rfa_offset = OFFSET_RU;
+  obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  /* Wait for command to finish */
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, sizeof(iscp));
+
+      if(iscp.iscp_busy == (unsigned short) 0)
+	break;
+
+      udelay(10);
+    }
+
+  if(i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wv_82586_start(): iscp_busy timeout.\n",
+	     dev->name);
+#endif
+      return -1;
+    }
+
+  /* Check command completion */
+  for(i = 15; i > 0; i--)
+    {
+      obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, sizeof(scb));
+
+      if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
+	break;
+
+      udelay(10);
+    }
+
+  if (i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n",
+	     dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
+#endif
+      return -1;
+    }
+
+  wv_ack(dev);
+
+  /* Set the action command header */
+  memset(&cb, 0x00, sizeof(cb));
+  cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
+  cb.ac_link = OFFSET_CU;
+  obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+
+  if(wv_synchronous_cmd(dev, "diag()") == -1)
+    return -1;
+
+  obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+  if(cb.ac_status & AC_SFLD_FAIL)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wv_82586_start(): i82586 Self Test failed.\n",
+	     dev->name);
+#endif
+      return -1;
+    }
+
+#ifdef DEBUG_I82586_SHOW
+  wv_scb_show(ioaddr);
+#endif
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name);
+#endif
+  return 0;
 }
 
-static
-void
-wavelan_set_multicast_list(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard config of the WaveLAN controler (i82586).
+ *
+ * This routine is a violent hack. We use the first free transmit block
+ * to make our configuration. In the buffer area, we create the three
+ * configure command (linked). We make the previous nop point to the
+ * beggining of the buffer instead of the tx command. After, we go as
+ * usual to the nop command...
+ * Note that only the last command (mc_set) will generate an interrupt...
+ *
+ * (called by wv_hw_reset(), wv_82586_reconfig())
+ */
+static void
+wv_82586_config(device *	dev)
 {
-	net_local	*lp;
-	unsigned long	x;
+  net_local *		lp = (net_local *) dev->priv;
+  u_short		ioaddr = dev->base_addr;
+  unsigned short	txblock;
+  unsigned short	txpred;
+  unsigned short	tx_addr;
+  unsigned short	nop_addr;
+  unsigned short	tbd_addr;
+  unsigned short	cfg_addr;
+  unsigned short	ias_addr;
+  unsigned short	mcs_addr;
+  ac_tx_t		tx;
+  ac_nop_t		nop;
+  ac_cfg_t		cfg;		/* Configure action */
+  ac_ias_t		ias;		/* IA-setup action */
+  ac_mcs_t		mcs;		/* Multicast setup */
+  struct dev_mc_list *	dmi;
+  unsigned long		x;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name);
+#endif
+
+  x = wv_splhi();
+
+  /* Calculate addresses of next block and previous block */
+  txblock = lp->tx_first_free;
+  txpred = txblock - TXBLOCKZ;
+  if(txpred < OFFSET_CU)
+    txpred += NTXBLOCKS * TXBLOCKZ;
+  lp->tx_first_free += TXBLOCKZ;
+  if(lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+    lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+  lp->tx_n_in_use++;
+
+  /* Calculate addresses of the differents part of the block */
+  tx_addr = txblock;
+  nop_addr = tx_addr + sizeof(tx);
+  tbd_addr = nop_addr + sizeof(nop);
+  cfg_addr = tbd_addr + sizeof(tbd_t);	/* beggining of the buffer */
+  ias_addr = cfg_addr + sizeof(cfg);
+  mcs_addr = ias_addr + sizeof(ias);
+
+  /*
+   * Transmit command.
+   */
+  tx.tx_h.ac_status = 0xFFFF;	/* Fake completion value */
+  obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+	      (unsigned char *) &tx.tx_h.ac_status,
+	      sizeof(tx.tx_h.ac_status));
+
+  /*
+   * NOP command.
+   */
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+	      (unsigned char *) &nop.nop_h.ac_status,
+	      sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = nop_addr;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+	      (unsigned char *) &nop.nop_h.ac_link,
+	      sizeof(nop.nop_h.ac_link));
 
-	if (wavelan_debug > 0)
-		printk("%s: ->wavelan_set_multicast_list(dev=%p)", dev->name, dev);
+  /* Create a configure action */
+  memset(&cfg, 0x00, sizeof(cfg));
 
-	lp = (net_local *)dev->priv;
+#if	0
+  /*
+   * The default board configuration.
+   */
+  cfg.fifolim_bytecnt 	= 0x080c;
+  cfg.addrlen_mode  	= 0x2600;
+  cfg.linprio_interframe	= 0x7820;	/* IFS=120, ACS=2 */
+  cfg.slot_time      	= 0xf00c;	/* slottime=12    */
+  cfg.hardware	     	= 0x0008;	/* tx even w/o CD */
+  cfg.min_frame_len   	= 0x0040;
+#endif	/* 0 */
 
-	if(dev->flags&IFF_PROMISC)
-	{
-		/*
-		 * Promiscuous mode: receive all packets.
-		 */
-		lp->promiscuous = 1;
-		x = wavelan_splhi();
-		(void)wavelan_hardware_reset(dev);
-		wavelan_splx(x);
-	}
-#if MULTICAST_IS_ADDED	
-	else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
-	{
-			
-	
-	}
-#endif	
-	else	
-	{
-		/*
-		 * Normal mode: disable promiscuous mode,
-		 * clear multicast list.
-		 */
-		lp->promiscuous = 0;
-		x = wavelan_splhi();
-		(void)wavelan_hardware_reset(dev);
-		wavelan_splx(x);
-	}
+  /*
+   * For Linux we invert AC_CFG_ALOC(..) so as to conform
+   * to the way that net packets reach us from above.
+   * (See also ac_tx_t.)
+   */
+  cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
+  cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
+  cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
+		  AC_CFG_SRDY(0);
+  cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
+		  AC_CFG_ILPBCK(0) |
+		  AC_CFG_PRELEN(AC_CFG_PLEN_2) |
+		  AC_CFG_ALOC(1) |
+		  AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
+  cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
+		   AC_CFG_ACR(0) |
+		   AC_CFG_LINPRIO(0);
+  cfg.cfg_ifs = 32;
+  cfg.cfg_slotl = 0;
+  cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
+		   AC_CFG_SLTTMHI(2);
+  cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
+		   AC_CFG_BTSTF(0) |
+		   AC_CFG_CRC16(0) |
+		   AC_CFG_NCRC(0) |
+		   AC_CFG_TNCRS(1) |
+		   AC_CFG_MANCH(0) |
+		   AC_CFG_BCDIS(0) |
+		   AC_CFG_PRM(lp->promiscuous);
+  cfg.cfg_byte15 = AC_CFG_ICDS(0) |
+		   AC_CFG_CDTF(0) |
+		   AC_CFG_ICSS(0) |
+		   AC_CFG_CSTF(0);
+/*
+  cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
+*/
+  cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
 
-	if (wavelan_debug > 0)
-		printk("%s: <-wavelan_set_multicast_list()\n", dev->name);
+  cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure);
+  cfg.cfg_h.ac_link = ias_addr;
+  obram_write(ioaddr, cfg_addr, (unsigned char *)&cfg, sizeof(cfg));
+
+  /* Setup the MAC address */
+  memset(&ias, 0x00, sizeof(ias));
+  ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup);
+  ias.ias_h.ac_link = mcs_addr;
+  memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr));
+  obram_write(ioaddr, ias_addr, (unsigned char *)&ias, sizeof(ias));
+
+  /* Initialize adapter's ethernet multicast addresses */
+  memset(&mcs, 0x00, sizeof(mcs));
+  mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup);
+  mcs.mcs_h.ac_link = nop_addr;
+  mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count;
+  obram_write(ioaddr, mcs_addr, (unsigned char *)&mcs, sizeof(mcs));
+
+  /* If any address to set */
+  if(lp->mc_count)
+    {
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+	outsw(PIOP1(ioaddr), (u_short *) dmi->dmi_addr,
+	      WAVELAN_ADDR_SIZE >> 1);
+
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_82586_config(): set %d multicast addresses:\n",
+	     dev->name, lp->mc_count);
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+	printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
+	       dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] );
+#endif
+    }
+
+  /*
+   * Overwrite the predecessor NOP link
+   * so that it points to the configure action.
+   */
+  nop_addr = txpred + sizeof(tx);
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+	      (unsigned char *)&nop.nop_h.ac_status,
+	      sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = cfg_addr;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+	      (unsigned char *) &nop.nop_h.ac_link,
+	      sizeof(nop.nop_h.ac_link));
+
+  /* If watchdog not already active, activate it... */
+  if(lp->watchdog.prev == (timer_list *) NULL)
+    {
+      /* set timer to expire in WATCHDOG_JIFFIES */
+      lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+      add_timer(&lp->watchdog);
+    }
+
+  lp->reconfig_82586 = 0;
+
+  if(lp->tx_first_in_use == I82586NULL)
+    lp->tx_first_in_use = txblock;
+
+  if(lp->tx_n_in_use < NTXBLOCKS - 1)
+    dev->tbusy = 0;
+
+  wv_splx(x);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name);
+#endif
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Extra WaveLAN-specific device data.
- * "cat /proc/net/wavelan" -- see fs/proc/net.c.
+ * This routine stop gracefully the WaveLAN controler (i82586).
+ * (called by wavelan_close())
  */
-static
-int
-sprintf_stats(char *buffer, device *dev)
+static inline void
+wv_82586_stop(device *	dev)
 {
-	net_local	*lp;
-	unsigned char	v;
-	mmr_t		m;
-
-	lp = (net_local *)dev->priv;
-
-	if (lp == (net_local *)0)
-		return sprintf(buffer, "%6s: No statistics available.\n", dev->name);
-
-	v = (unsigned char)1;
-	mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
-
-	mmc_read(dev->base_addr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, sizeof(m.mmr_dce_status));
-	mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_h), &m.mmr_correct_nwid_h, sizeof(m.mmr_correct_nwid_h));
-	mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_l), &m.mmr_correct_nwid_l, sizeof(m.mmr_correct_nwid_l));
-	mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_h), &m.mmr_wrong_nwid_h, sizeof(m.mmr_wrong_nwid_h));
-	mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, sizeof(m.mmr_wrong_nwid_l));
-	mmc_read(dev->base_addr, mmroff(0, mmr_signal_lvl), &m.mmr_signal_lvl, sizeof(m.mmr_signal_lvl));
-	mmc_read(dev->base_addr, mmroff(0, mmr_silence_lvl), &m.mmr_silence_lvl, sizeof(m.mmr_silence_lvl));
-	mmc_read(dev->base_addr, mmroff(0, mmr_sgnl_qual), &m.mmr_sgnl_qual, sizeof(m.mmr_sgnl_qual));
-
-	v = (unsigned char)0;
-	mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
-
-	lp->correct_nwid += (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l;
-	lp->wrong_nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-
-	return sprintf
-	(
-		buffer,
-		"%6s:   %02x %08x %08x   %02x   %02x   %02x   %02x    %u\n",
-		dev->name,
-		m.mmr_dce_status,
-		lp->correct_nwid,
-		lp->wrong_nwid,
-		m.mmr_signal_lvl,
-		m.mmr_silence_lvl,
-		m.mmr_sgnl_qual,
-		lp->tx_n_in_use,
-		lp->nresets
-	);
+  net_local *	lp = (net_local *) dev->priv;
+  u_short	ioaddr = dev->base_addr;
+  u_short	scb_cmd;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name);
+#endif
+
+  /* Suspend both command unit and receive unit */
+  scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+	      (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+  set_chan_attn(ioaddr, lp->hacr);
+
+  /* No more interrupts */
+  wv_ints_off(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name);
+#endif
 }
 
+/*------------------------------------------------------------------*/
+/*
+ * Totally reset the wavelan and restart it.
+ * Performs the following actions:
+ *	1. A power reset (reset DMA)
+ *	2. Initialize the radio modem (using wv_mmc_init)
+ *	3. Reset & Configure LAN controller (using wv_82586_start)
+ *	4. Start the LAN controller's command unit
+ *	5. Start the LAN controller's receive unit
+ */
 static int
-wavelan_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+wv_hw_reset(device *	dev)
 {
-	int		len;
-	off_t		begin;
-	off_t		pos;
-	int		size;
-	unsigned long	x;
+  net_local *	lp = (net_local *)dev->priv;
+  u_short	ioaddr = dev->base_addr;
 
-	len = 0;
-	begin = 0;
-	pos = 0;
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
+	 (unsigned int)dev);
+#endif
 
-	size = sprintf(buffer, "%s", "Iface |  dce    +nwid    -nwid  lvl slnc qual ntxq nrst\n");
+  /* If watchdog was activated, kill it ! */
+  if(lp->watchdog.prev != (timer_list *) NULL)
+    del_timer(&lp->watchdog);
 
-	pos += size;
-	len += size;
-	
-	x = wavelan_splhi();
+  /* Increase the number of resets done */
+  lp->nresets++;
 
-	if (first_wavelan != (net_local *)0)
-	{
-		net_local	*lp;
-
-		lp = first_wavelan;
-		do
-		{
-			size = sprintf_stats(buffer + len, lp->dev);
+  wv_hacr_reset(ioaddr);
+  lp->hacr = HACR_DEFAULT;
 
-			len += size;
-			pos = begin + len;
-					
-			if (pos < offset)
-			{
-				len = 0;
-				begin = pos;
-			}
+  if((wv_mmc_init(dev) < 0) ||
+     (wv_82586_start(dev) < 0))
+    return -1;
 
-			if (pos > offset + length)
-				break;
-		}
-			while ((lp = lp->next) != first_wavelan);
-	}
+  /* Enable the card to send interrupts */
+  wv_ints_on(dev);
 
-	wavelan_splx(x);
+  /* Start card functions */
+  if((wv_ru_start(dev) < 0) ||
+     (wv_cu_start(dev) < 0))
+    return -1;
 
-	*start = buffer + (offset - begin);	/* Start of wanted data */
-	len -= (offset - begin);		/* Start slop */
-	if (len > length)
-		len = length;			/* Ending slop */
+  /* Finish configuration */
+  wv_82586_config(dev);
 
-	return len;
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
+#endif
+  return 0;
 }
 
-#if	defined(MODULE)
-static char		devicename[9]		= { 0, };
-static struct device	dev_wavelan		=
-{
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, wavelan_probe
-};
-
-static int io = 0x390; /* Default from above.. */
-static int irq = 0;
-
-int
-init_module(void)
+/*------------------------------------------------------------------*/
+/*
+ * Check if there is a wavelan at the specific base address.
+ * As a side effect, it read the MAC address.
+ * (called in wavelan_probe() and init_module())
+ */
+static int
+wv_check_ioaddr(u_short		ioaddr,
+		u_char *	mac)
 {
-	dev_wavelan.base_addr = io;
-	dev_wavelan.irq       = irq;
-	if (register_netdev(&dev_wavelan) != 0)
-		return -EIO;
+  int		i;		/* Loop counter */
 
-	return 0;
+  /* Check if the base address if available */
+  if(check_region(ioaddr, sizeof(ha_t)))
+    return  EADDRINUSE;		/* ioaddr already used... */
+
+  /* Reset host interface */
+  wv_hacr_reset(ioaddr);
+
+  /* Read the MAC address from the parameter storage area */
+  psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr),
+	   mac, 6);
+
+  /*
+   * Check the first three octets of the addr for the manufacturer's code.
+   * Note: If you can't find your wavelan card, you've got a
+   * non-NCR/AT&T/Lucent ISA cards, see wavelan.p.h for detail on
+   * how to configure your card...
+   */
+  for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
+    if((mac[0] == MAC_ADDRESSES[i][0]) &&
+       (mac[1] == MAC_ADDRESSES[i][1]) &&
+       (mac[2] == MAC_ADDRESSES[i][2]))
+      return 0;
+
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_WARNING "Wavelan (0x%3X) : Your MAC address might be : %02X:%02X:%02X...\n",
+	 ioaddr, mac[0], mac[1], mac[2]);
+#endif
+    return ENODEV;
 }
 
-void
-cleanup_module(void)
-{
-	proc_net_unregister(PROC_NET_WAVELAN);
-	unregister_netdev(&dev_wavelan);
-	kfree_s(dev_wavelan.priv, sizeof(struct net_local));
-	dev_wavelan.priv = NULL;
+/************************ INTERRUPT HANDLING ************************/
+
+/*
+ * This function is the interrupt handler for the WaveLAN card. This
+ * routine will be called whenever: 
+ */
+static void
+wavelan_interrupt(int			irq,
+		  void *		dev_id,
+		  struct pt_regs *	regs)
+{
+  device *	dev;
+  u_short	ioaddr;
+  net_local *	lp;
+  u_short	hasr;
+  u_short	status;
+  u_short	ack_cmd;
+
+  if((dev = (device *) (irq2dev_map[irq])) == (device *) NULL)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
+	     irq);
+#endif
+      return;
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
+#endif
+
+  lp = (net_local *) dev->priv;
+  ioaddr = dev->base_addr;
+
+  /* Prevent reentrance. What should we do here ? */
+#ifdef DEBUG_INTERRUPT_ERROR
+  if(dev->interrupt)
+    printk(KERN_INFO "%s: wavelan_interrupt(): Re-entering the interrupt handler.\n",
+	   dev->name);
+#endif
+  dev->interrupt = 1;
+
+  if((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR)
+    {
+      u_char	dce_status;
+
+      /*
+       * Interrupt from the modem management controller.
+       * This will clear it -- ignored for now.
+       */
+      mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status));
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n",
+	     dev->name, dce_status);
+#endif
+    }
+
+  if((hasr & HASR_82586_INTR) == 0)
+    {
+      dev->interrupt = 0;
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): interrupt not coming from i82586\n",
+	     dev->name);
+#endif
+      return;
+    }
+
+  /* Read interrupt data */
+  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+	     (unsigned char *) &status, sizeof(status));
+
+  /*
+   * Acknowledge the interrupt(s).
+   */
+  ack_cmd = status & SCB_ST_INT;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+	      (unsigned char *) &ack_cmd, sizeof(ack_cmd));
+  set_chan_attn(ioaddr, lp->hacr);
+
+#ifdef DEBUG_INTERRUPT_INFO
+  printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n",
+	 dev->name, status);
+#endif
+
+  /* Command completed. */
+  if((status & SCB_ST_CX) == SCB_ST_CX)
+    {
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "%s: wavelan_interrupt(): command completed.\n",
+	     dev->name);
+#endif
+      wv_complete(dev, ioaddr, lp);
+
+      /* If watchdog was activated, kill it ! */
+      if(lp->watchdog.prev != (timer_list *) NULL)
+	del_timer(&lp->watchdog);
+      if(lp->tx_n_in_use > 0)
+	{
+	  /* set timer to expire in WATCHDOG_JIFFIES */
+	  lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+	  add_timer(&lp->watchdog);
+	}
+    }
+
+  /* Frame received. */
+  if((status & SCB_ST_FR) == SCB_ST_FR)
+    {
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "%s: wavelan_interrupt(): received packet.\n",
+	     dev->name);
+#endif
+      wv_receive(dev);
+    }
+
+  /* Check the state of the command unit */
+  if(((status & SCB_ST_CNA) == SCB_ST_CNA) ||
+     (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start))
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): CU inactive -- restarting\n",
+	     dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+
+  /* Check the state of the command unit */
+  if(((status & SCB_ST_RNR) == SCB_ST_RNR) ||
+     (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start))
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): RU not ready -- restarting\n",
+	     dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+
+  dev->interrupt = 0;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
 }
-#endif	/* defined(MODULE) */
 
-static
-void
-wavelan_cu_show_one(device *dev, net_local *lp, int i, unsigned short p)
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog : when we start a transmission, we set a timer in the
+ * kernel.  If the transmission complete, this timer is disabled. If
+ * it expire, it try to unlock the hardware.
+ *
+ * Note : this watchdog doesn't work on the same principle as the
+ * watchdog in the previous version of the ISA driver. I make it this
+ * way because the overhead of add_timer() and del_timer() is nothing
+ * and that it avoid calling the watchdog, saving some CPU...
+ */
+static void
+wavelan_watchdog(u_long		a)
 {
-	unsigned short	ioaddr;
-	ac_tx_t		actx;
-
-	ioaddr = dev->base_addr;
+  device *		dev;
+  net_local *		lp;
+  unsigned short	ioaddr;
+  unsigned long		x;
+  unsigned int		nreaped;
+
+  dev = (device *) a;
+  ioaddr = dev->base_addr;
+  lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
+#endif
+
+#ifdef DEBUG_INTERRUPT_ERROR
+  printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
+	 dev->name);
+#endif
+
+  x = wv_splhi();
+
+  dev = (device *) a;
+  ioaddr = dev->base_addr;
+  lp = (net_local *) dev->priv;
+
+  if(lp->tx_n_in_use <= 0)
+    {
+      wv_splx(x);
+      return;
+    }
+
+  nreaped = wv_complete(dev, ioaddr, lp);
+
+#ifdef DEBUG_INTERRUPT_INFO
+  printk(KERN_DEBUG "%s: wavelan_watchdog(): %d reaped, %d remain.\n",
+	 dev->name, nreaped, lp->tx_n_in_use);
+#endif
+
+#ifdef DEBUG_PSA_SHOW
+  {
+    psa_t		psa;
+    psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+    wv_psa_show(&psa);
+  }
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+  wv_cu_show(dev);
+#endif
+
+  /* If no buffer has been freed */
+  if(nreaped == 0)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_watchdog(): cleanup failed, trying reset\n",
+	     dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+  else
+    /* Re-set watchodog for next transmission */
+    if(lp->tx_n_in_use > 0)
+      {
+	/* set timer to expire in WATCHDOG_JIFFIES */
+	lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+	add_timer(&lp->watchdog);
+      }
 
-	printk("%d: 0x%x:", i, p);
+  wv_splx(x);
 
-	obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx));
-	printk(" status=0x%x,", actx.tx_h.ac_status);
-	printk(" command=0x%x,", actx.tx_h.ac_command);
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
+#endif
+}
 
+/********************* CONFIGURATION CALLBACKS *********************/
 /*
-	{
-		tbd_t	tbd;
-
-		obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
-		printk(" tbd_status=0x%x,", tbd.tbd_status);
-	}
-*/
-
-	printk("|");
-}
+ * Here are the functions called by the linux networking (NET3) for
+ * initialization, configuration and deinstallations of the Wavelan
+ * ISA Hardware.
+ */
 
-#if	0
-static
-void
-wavelan_psa_show(psa_t *p)
+/*------------------------------------------------------------------*/
+/*
+ * Configure and start up the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "open" the device.
+ */
+static int
+wavelan_open(device *	dev)
 {
-	printk("psa:");
-
-	printk("psa_io_base_addr_1: 0x%02x,", p->psa_io_base_addr_1);
-	printk("psa_io_base_addr_2: 0x%02x,", p->psa_io_base_addr_2);
-	printk("psa_io_base_addr_3: 0x%02x,", p->psa_io_base_addr_3);
-	printk("psa_io_base_addr_4: 0x%02x,", p->psa_io_base_addr_4);
-	printk("psa_rem_boot_addr_1: 0x%02x,", p->psa_rem_boot_addr_1);
-	printk("psa_rem_boot_addr_2: 0x%02x,", p->psa_rem_boot_addr_2);
-	printk("psa_rem_boot_addr_3: 0x%02x,", p->psa_rem_boot_addr_3);
-	printk("psa_holi_params: 0x%02x,", p->psa_holi_params);
-	printk("psa_int_req_no: %d,", p->psa_int_req_no);
-	printk
-	(
-		"psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
-		p->psa_univ_mac_addr[0],
-		p->psa_univ_mac_addr[1],
-		p->psa_univ_mac_addr[2],
-		p->psa_univ_mac_addr[3],
-		p->psa_univ_mac_addr[4],
-		p->psa_univ_mac_addr[5]
-	);
-	printk
-	(
-		"psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
-		p->psa_local_mac_addr[0],
-		p->psa_local_mac_addr[1],
-		p->psa_local_mac_addr[2],
-		p->psa_local_mac_addr[3],
-		p->psa_local_mac_addr[4],
-		p->psa_local_mac_addr[5]
-	);
-	printk("psa_univ_local_sel: %d,", p->psa_univ_local_sel);
-	printk("psa_comp_number: %d,", p->psa_comp_number);
-	printk("psa_thr_pre_set: 0x%02x,", p->psa_thr_pre_set);
-	printk("psa_feature_select/decay_prm: 0x%02x,", p->psa_feature_select);
-	printk("psa_subband/decay_update_prm: %d,", p->psa_subband);
-	printk("psa_quality_thr: 0x%02x,", p->psa_quality_thr);
-	printk("psa_mod_delay: 0x%02x,", p->psa_mod_delay);
-	printk("psa_nwid: 0x%02x%02x,", p->psa_nwid[0], p->psa_nwid[1]);
-	printk("psa_undefined: %d,", p->psa_undefined);
-	printk("psa_encryption_select: %d,", p->psa_encryption_select);
-	printk
-	(
-		"psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,",
-		p->psa_encryption_key[0],
-		p->psa_encryption_key[1],
-		p->psa_encryption_key[2],
-		p->psa_encryption_key[3],
-		p->psa_encryption_key[4],
-		p->psa_encryption_key[5],
-		p->psa_encryption_key[6],
-		p->psa_encryption_key[7]
-	);
-	printk("psa_databus_width: %d,", p->psa_databus_width);
-	printk("psa_call_code/auto_squelch: 0x%02x,", p->psa_call_code);
-	printk("psa_no_of_retries: %d,", p->psa_no_of_retries);
-	printk("psa_acr: %d,", p->psa_acr);
-	printk("psa_dump_count: %d,", p->psa_dump_count);
-	printk("psa_nwid_prefix: 0x%02x,", p->psa_nwid_prefix);
-	printk("psa_conf_status: %d,", p->psa_conf_status);
-	printk("psa_crc: 0x%02x%02x,", p->psa_crc[0], p->psa_crc[1]);
-	printk("psa_crc_status: 0x%02x,", p->psa_crc_status);
+  u_long	x;
 
-	printk("\n");
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
+	 (unsigned int) dev);
+#endif
+
+  /* Check irq */
+  if(dev->irq == 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wavelan_open(): no irq\n", dev->name);
+#endif
+      return -ENXIO;
+    }
+
+  if((irq2dev_map[dev->irq] != (device *) NULL) ||
+     /* This is always true, but avoid the false IRQ. */
+     ((irq2dev_map[dev->irq] = dev) == (device *) NULL) ||
+     (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", NULL) != 0))
+    {
+      irq2dev_map[dev->irq] = (device *) NULL;
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wavelan_open(): invalid irq\n", dev->name);
+#endif
+      return -EAGAIN;
+    }
+
+  x = wv_splhi();
+  if(wv_hw_reset(dev) != -1)
+    {
+      dev->interrupt = 0;
+      dev->start = 1;
+    }
+  else
+    {
+      free_irq(dev->irq, NULL);
+      irq2dev_map[dev->irq] = (device *) NULL;
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wavelan_open(): impossible to start the card\n",
+	     dev->name);
+#endif
+      return -EAGAIN;
+    }
+  wv_splx(x);
+
+  MOD_INC_USE_COUNT;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
+#endif
+  return 0;
 }
 
-static
-void
-wavelan_mmc_show(unsigned short ioaddr)
+/*------------------------------------------------------------------*/
+/*
+ * Shutdown the WaveLAN ISA card.
+ * Called by NET3 when it "close" the device.
+ */
+static int
+wavelan_close(device *	dev)
 {
-	mmr_t	m;
+  net_local *	lp = (net_local *)dev->priv;
 
-	mmc_read(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
+	 (unsigned int) dev);
+#endif
 
-	printk("mmr:");
-	printk(" des_status: 0x%x", m.mmr_des_status);
-	printk(" des_avail: 0x%x", m.mmr_des_avail);
-	printk(" des_io_invert: 0x%x", m.mmr_des_io_invert);
-	printk
-	(
-		" dce_status: 0x%x[%s%s%s%s]",
-		m.mmr_dce_status & 0x0F,
-		(m.mmr_dce_status & MMR_DCE_STATUS_ENERG_DET) ? "energy detected," : "",
-		(m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? "loop test indicated," : "",
-		(m.mmr_dce_status & MMR_DCE_STATUS_XMTITR_IND) ? "transmitter on," : "",
-		(m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? "jabber timer expired," : ""
-	);
-	printk(" correct_nwid: %d", m.mmr_correct_nwid_h << 8 | m.mmr_correct_nwid_l);
-	printk(" wrong_nwid: %d", (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
-	printk(" thr_pre_set: 0x%x", m.mmr_thr_pre_set);
-	printk(" signal_lvl: %d", m.mmr_signal_lvl);
-	printk(" silence_lvl: %d", m.mmr_silence_lvl);
-	printk(" sgnl_qual: 0x%x", m.mmr_sgnl_qual);
-	printk(" netw_id_l: %x", m.mmr_netw_id_l);
+  /* Not do the job twice... */
+  if(dev->start == 0)
+    return 0;
 
-	printk("\n");
-}
-#endif	/* 0 */
+  dev->tbusy = 1;
+  dev->start = 0;
 
-static
-void
-wavelan_scb_show(unsigned short ioaddr)
-{
-	scb_t	scb;
+  /* If watchdog was activated, kill it ! */
+  if(lp->watchdog.prev != (timer_list *) NULL)
+    del_timer(&lp->watchdog);
 
-	obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));   
+  /*
+   * Flush the Tx and disable Rx.
+   */
+  wv_82586_stop(dev);
 
-	printk("scb:");
+  free_irq(dev->irq, NULL);
+  irq2dev_map[dev->irq] = (device *) NULL;
 
-	printk(" status:");
-	printk
-	(
-		" stat 0x%x[%s%s%s%s]",
-		(scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12,
-		(scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "",
-		(scb.scb_status & SCB_ST_FR) ? "frame received," : "",
-		(scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "",
-		(scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : ""
-	);
-	printk
-	(
-		" cus 0x%x[%s%s%s]",
-		(scb.scb_status & SCB_ST_CUS) >> 8,
-		((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "",
-		((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "",
-		((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : ""
-	);
-	printk
-	(
-		" rus 0x%x[%s%s%s%s]",
-		(scb.scb_status & SCB_ST_RUS) >> 4,
-		((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "",
-		((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "",
-		((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "",
-		((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : ""
-	);
-
-	printk(" command:");
-	printk
-	(
-		" ack 0x%x[%s%s%s%s]",
-		(scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
-		(scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
-		(scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
-		(scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
-		(scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""
-	);
-	printk
-	(
-		" cuc 0x%x[%s%s%s%s%s]",
-		(scb.scb_command & SCB_CMD_CUC) >> 8,
-		((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "",
-		((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
-		((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "",
-		((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "",
-		((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : ""
-	);
-	printk
-	(
-		" ruc 0x%x[%s%s%s%s%s]",
-		(scb.scb_command & SCB_CMD_RUC) >> 4,
-		((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "",
-		((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
-		((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "",
-		((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "",
-		((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : ""
-	);
-
-	printk(" cbl_offset 0x%x", scb.scb_cbl_offset);
-	printk(" rfa_offset 0x%x", scb.scb_rfa_offset);
-
-	printk(" crcerrs %d", scb.scb_crcerrs);
-	printk(" alnerrs %d", scb.scb_alnerrs);
-	printk(" rscerrs %d", scb.scb_rscerrs);
-	printk(" ovrnerrs %d", scb.scb_ovrnerrs);
+  MOD_DEC_USE_COUNT;
 
-	printk("\n");
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
+#endif
+  return 0;
 }
 
-static
-void
-wavelan_ru_show(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Probe an i/o address, and if the wavelan is there configure the
+ * device structure
+ * (called by wavelan_probe() & via init_module())
+ */
+static int
+wavelan_config(device *	dev)
 {
-	net_local	*lp;
-
-	lp = (net_local *)dev->priv;
-
-	printk("ru:");
-	/*
-	 * Not implemented yet...
-	 */
-	printk("\n");
+  u_short	ioaddr = dev->base_addr;
+  u_char	irq_mask;
+  int		irq;
+  net_local *	lp;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%x)\n", dev->name,
+	 (unsigned int)dev, ioaddr);
+#endif
+
+  /* Check irq arg on command line */
+  if(dev->irq != 0)
+    {
+      irq_mask = wv_irq_to_psa(dev->irq);
+
+      if(irq_mask == 0)
+	{
+#ifdef DEBUG_CONFIG_ERROR
+	  printk(KERN_WARNING "%s: wavelan_config(): invalid irq %d -- ignored.\n",
+		 dev->name, dev->irq);
+#endif
+	  dev->irq = 0;
+	}
+      else
+	{
+#ifdef DEBUG_CONFIG_INFO
+	  printk(KERN_DEBUG "%s: wavelan_config(): changing irq to %d\n",
+		 dev->name, dev->irq);
+#endif
+	  psa_write(ioaddr, HACR_DEFAULT,
+		    psaoff(0, psa_int_req_no), &irq_mask, 1);
+	  wv_hacr_reset(ioaddr);
+	}
+    }
+
+  psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), &irq_mask, 1);
+  if((irq = wv_psa_to_irq(irq_mask)) == -1)
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      printk(KERN_INFO "%s: wavelan_config(): could not wavelan_map_irq(%d).\n",
+	     dev->name, irq_mask);
+#endif
+      return EAGAIN;
+    }
+
+  dev->irq = irq;
+
+  request_region(ioaddr, sizeof(ha_t), "wavelan");
+
+  dev->mem_start = 0x0000;
+  dev->mem_end = 0x0000;
+  dev->if_port = 0;
+
+  /* Initialize device structures */
+  dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
+  if(dev->priv == NULL)
+    return -ENOMEM;
+  memset(dev->priv, 0x00, sizeof(net_local));
+  lp = (net_local *)dev->priv;
+
+  /* Back link to the device structure */
+  lp->dev = dev;
+  /* Add the device at the beggining of the linked list */
+  lp->next = wavelan_list;
+  wavelan_list = lp;
+
+  lp->hacr = HACR_DEFAULT;
+
+  lp->watchdog.function = wavelan_watchdog;
+  lp->watchdog.data = (unsigned long) dev;
+  lp->promiscuous = 0;
+  lp->mc_count = 0;
+
+  /*
+   * Fill in the fields of the device structure
+   * with ethernet-generic values.
+   */
+  ether_setup(dev);
+
+  dev->open = wavelan_open;
+  dev->stop = wavelan_close;
+  dev->hard_start_xmit = wavelan_packet_xmit;
+  dev->get_stats = wavelan_get_stats;
+  dev->set_multicast_list = &wavelan_set_multicast_list;
+  dev->set_mac_address = &wavelan_set_mac_address;
+
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+  dev->do_ioctl = wavelan_ioctl;
+  dev->get_wireless_stats = wavelan_get_wireless_stats;
+#endif
+
+  dev->mtu = WAVELAN_MTU;
+
+  /* Display nice info */
+  wv_init_info(dev);
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name);
+#endif
+  return 0;
 }
 
-static
-void
-wavelan_cu_show(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Check for a network adaptor of this type.
+ * Return '0' iff one exists.
+ * (There seem to be different interpretations of
+ * the initial value of dev->base_addr.
+ * We follow the example in drivers/net/ne.c.)
+ * (called in "Space.c")
+ */
+extern int
+wavelan_probe(device *	dev)
 {
-	net_local	*lp;
-	unsigned int	i;
-	unsigned short	p;
-
-	lp = (net_local *)dev->priv;
-
-	printk("cu:");
-	printk("\n");
+  short		base_addr;
+  mac_addr	mac;		/* Mac address (check wavelan existence) */
+  int		i;
+  int		r;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n",
+	 dev->name, (unsigned int)dev, (unsigned int)dev->base_addr);
+#endif
+
+#ifdef	STRUCT_CHECK
+  if (wv_struct_check() != (char *) NULL)
+    {
+      printk(KERN_WARNING "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n",
+	     dev->name, wv_struct_check());
+      return ENODEV;
+    }
+#endif	/* STRUCT_CHECK */
+
+  /* Check the value of the command line parameter for base address */
+  base_addr = dev->base_addr;
+
+  /* Don't probe at all. */
+  if(base_addr < 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wavelan_probe(): invalid base address\n",
+	     dev->name);
+#endif
+      return ENXIO;
+    }
+
+  /* Check a single specified location. */
+  if(base_addr > 0x100)
+    {
+      /* Check if the is something at this base address */
+      if((r = wv_check_ioaddr(base_addr, mac)) == 0)
+	{
+	  memcpy(dev->dev_addr, mac, 6);	/* Copy mac address */
+	  r = wavelan_config(dev);
+	}
+
+#ifdef DEBUG_CONFIG_INFO
+      if(r != 0)
+	printk(KERN_DEBUG "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n",
+	       dev->name, base_addr);
+#endif
+
+#ifdef DEBUG_CALLBACK_TRACE
+      printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
+#endif
+      return r;
+    }
+
+  /* Scan all possible address of the wavelan hardware */
+  for(i = 0; i < NELS(iobase); i++)
+    {
+      /* Check if the is something at this base address */
+      if(wv_check_ioaddr(iobase[i], mac) == 0)
+	{
+	  dev->base_addr = iobase[i];		/* Copy base address */
+	  memcpy(dev->dev_addr, mac, 6);	/* Copy mac address */
+	  if(wavelan_config(dev) == 0)
+	    {
+#ifdef DEBUG_CALLBACK_TRACE
+	      printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
+#endif
+	      return 0;
+	    }
+	}
+    }
+
+  /* We may have touch base_addr : another driver may not like it... */
+  dev->base_addr = base_addr;
+
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n",
+	 dev->name);
+#endif
 
-	for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
-	{
-		wavelan_cu_show_one(dev, lp, i, p);
-
-		p += TXBLOCKZ;
-		if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-			p -= NTXBLOCKS * TXBLOCKZ;
-	}
+  return ENODEV;
 }
 
-static
-void
-wavelan_dev_show(device *dev)
+/****************************** MODULE ******************************/
+/*
+ * Module entry point : insertion & removal
+ */
+
+#ifdef	MODULE
+/*------------------------------------------------------------------*/
+/*
+ * Insertion of the module...
+ * I'm now quite proud of the multi-device support...
+ */
+int
+init_module(void)
 {
-	printk("dev:");
-	printk(" start=%d,", dev->start);
-	printk(" tbusy=%ld,", dev->tbusy);
-	printk(" interrupt=%d,", dev->interrupt);
-	printk(" trans_start=%ld,", dev->trans_start);
-	printk(" flags=0x%x,", dev->flags);
-	printk("\n");
+  mac_addr	mac;		/* Mac address (check wavelan existence) */
+  int		ret = 0;
+  int		i;
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> init_module()\n");
+#endif
+
+  /* If probing is asked */
+  if(io[0] == 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "wavelan init_module(): doing device probing (bad !)\n");
+      printk(KERN_WARNING "Specify base addresses while loading module to correct the problem\n");
+#endif
+
+      /* Copy the basic set of address to be probed */
+      for(i = 0; i < NELS(iobase); i++)
+	io[i] = iobase[i];
+    }
+
+
+  /* Loop on all possible base addresses */
+  i = -1;
+  while((io[++i] != 0) && (i < NELS(io)))
+    {
+      /* Check if the is something at this base address */
+      if(wv_check_ioaddr(io[i], mac) == 0)
+	{
+	  device *	dev;
+
+	  /* Create device and set basics args */
+	  dev = kmalloc(sizeof(struct device), GFP_KERNEL);
+	  memset(dev, 0x00, sizeof(struct device));
+	  dev->name = name[i];
+	  dev->base_addr = io[i];
+	  dev->irq = irq[i];
+	  dev->init = &wavelan_config;
+	  memcpy(dev->dev_addr, mac, 6);	/* Copy mac address */
+
+	  /* Try to create the device */
+	  if(register_netdev(dev) != 0)
+	    {
+	      /* DeAllocate everything */
+	      /* Note : if dev->priv is mallocated, there is no way to fail */
+	      kfree_s(dev, sizeof(struct device));
+	      ret = -EIO;
+	    }
+	}	/* If there is something at the address */
+    }		/* Loop on all addresses */
+
+#ifdef DEBUG_CONFIG_ERRORS
+  if(wavelan_list == (net_local *) NULL)
+    printk(KERN_WARNING "wavelan init_module(): No device found\n");
+#endif
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- init_module()\n");
+#endif
+  return ret;
 }
 
-static
+/*------------------------------------------------------------------*/
+/*
+ * Removal of the module
+ */
 void
-wavelan_local_show(device *dev)
+cleanup_module(void)
 {
-	net_local	*lp;
-
-	lp = (net_local *)dev->priv;
-
-	printk("local:");
-	printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
-	printk(" hacr=0x%x,", lp->hacr);
-	printk(" rx_head=0x%x,", lp->rx_head);
-	printk(" rx_last=0x%x,", lp->rx_last);
-	printk(" tx_first_free=0x%x,", lp->tx_first_free);
-	printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
-	printk("\n");
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> cleanup_module()\n");
+#endif
+
+  /* Loop on all devices and release them */
+  while(wavelan_list != (net_local *) NULL)
+    {
+      device *	dev = wavelan_list->dev;
+
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: cleanup_module(): removing device at 0x%x\n",
+	     dev->name, (unsigned int) dev);
+#endif
+
+      /* Release the ioport-region. */
+      release_region(dev->base_addr, sizeof(ha_t));
+
+      /* Remove definitely the device */
+      unregister_netdev(dev);
+
+      /* Unlink the device */
+      wavelan_list = wavelan_list->next;
+
+      /* Free pieces */
+      kfree_s(dev->priv, sizeof(struct net_local));
+      kfree_s(dev, sizeof(struct device));
+    }
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- cleanup_module()\n");
+#endif
 }
+#endif	/* MODULE */
 
 /*
  * This software may only be used and distributed
diff -u --recursive --new-file v2.1.14/linux/drivers/net/wavelan.h linux/drivers/net/wavelan.h
--- v2.1.14/linux/drivers/net/wavelan.h	Thu Dec 12 17:02:43 1996
+++ linux/drivers/net/wavelan.h	Tue Dec  3 09:24:15 1996
@@ -1,160 +1,43 @@
-#define WAVELAN_ADDR_SIZE	6	/* Size of a MAC address */
-#define SA_ADDR0		0x08	/* First octet of WaveLAN MAC addresses */
-#define SA_ADDR1		0x00	/* Second octet of WaveLAN MAC addresses */
-#define SA_ADDR2		0x0E	/* Third octet of WaveLAN MAC addresses */
-#define SA_ALT_ADDR2		0x6A	/* Alternate third octet of WaveLAN MAC addresses */
-#define WAVELAN_MTU		1500	/* Maximum size of WaveLAN packet */
-
 /*
- * Parameter Storage Area (PSA).
+ *	Wavelan ISA driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyrigth follow. See wavelan.p.h for details.
+ *
+ * This file contain the declarations of the Wavelan hardware. Note that
+ * the Wavelan ISA include a i82586 controler (see definitions in
+ * file i82586.h).
+ *
+ * The main difference between the ISA hardware and the pcmcia one is
+ * the Ethernet Controler (i82586 instead of i82593).
+ * The i82586 allow multiple transmit buffers. The PSA need to be accessed
+ * through the host interface.
  */
-typedef struct psa_t	psa_t;
-struct psa_t
-{
-	unsigned char	psa_io_base_addr_1;	/* Base address 1 ??? */
-	unsigned char	psa_io_base_addr_2;	/* Base address 2 */
-	unsigned char	psa_io_base_addr_3;	/* Base address 3 */
-	unsigned char	psa_io_base_addr_4;	/* Base address 4 */
-	unsigned char	psa_rem_boot_addr_1;	/* Remote Boot Address 1 */
-	unsigned char	psa_rem_boot_addr_2;	/* Remote Boot Address 2 */
-	unsigned char	psa_rem_boot_addr_3;	/* Remote Boot Address 3 */
-	unsigned char	psa_holi_params;	/* HOst Lan Interface (HOLI) Parameters */
-	unsigned char	psa_int_req_no;		/* Interrupt Request Line */
-	unsigned char	psa_unused0[7];		/* unused */
-	unsigned char	psa_univ_mac_addr[WAVELAN_ADDR_SIZE];	/* Universal (factory) MAC Address */
-	unsigned char	psa_local_mac_addr[WAVELAN_ADDR_SIZE];	/* Local MAC Address */
-	unsigned char	psa_univ_local_sel;	/* Universal Local Selection */
-#define		PSA_UNIVERSAL	0		/* Universal (factory) */
-#define		PSA_LOCAL	1		/* Local */
-	unsigned char	psa_comp_number;	/* Compatibility Number: */
-#define		PSA_COMP_PC_AT_915	0 	/* PC-AT 915 MHz	*/
-#define		PSA_COMP_PC_MC_915	1 	/* PC-MC 915 MHz	*/
-#define		PSA_COMP_PC_AT_2400	2 	/* PC-AT 2.4 GHz	*/
-#define		PSA_COMP_PC_MC_2400	3 	/* PC-MC 2.4 GHz	*/
-#define		PSA_COMP_PCMCIA_915	4 	/* PCMCIA 915 MHz	*/
-	unsigned char	psa_thr_pre_set;	/* Modem Threshold Preset */
-	unsigned char	psa_feature_select;	/* ??? */
-#if	0
-	<alias for above>
-	unsigned char	psa_decay_prm;		/* Modem Decay */
-#endif	/* 0 */
-	unsigned char	psa_subband;		/* Subband	*/
-#define		PSA_SUBBAND_915		0	/* 915 MHz	*/
-#define		PSA_SUBBAND_2425	1	/* 2425 MHz	*/
-#define		PSA_SUBBAND_2460	2	/* 2460 MHz	*/
-#define		PSA_SUBBAND_2484	3	/* 2484 MHz	*/
-#define		PSA_SUBBAND_2430_5	4	/* 2430.5 MHz	*/
-#if	0
-	<alias for above>
-	unsigned char	psa_decay_updat_prm;	/* Modem Decay Update ??? */
-#endif	/* 0 */
-	unsigned char	psa_quality_thr;	/* Modem Quality Threshold */
-	unsigned char	psa_mod_delay;		/* Modem Delay ??? */
-	unsigned char	psa_nwid[2];		/* Network ID */
-	unsigned char	psa_undefined;		/* undefined */
-	unsigned char	psa_encryption_select;	/* Encryption On Off */
-	unsigned char	psa_encryption_key[8];	/* Encryption Key */
-	unsigned char	psa_databus_width;	/* 8/16 bit bus width */
-	unsigned char	psa_call_code;		/* ??? */
-#if	0
-	<alias for above>
-	unsigned char	psa_auto_squelch;	/* Automatic Squelch level On off ??? */
-#endif	/* 0 */
-	unsigned char	psa_no_of_retries;	/* LAN Cont. No of retries */
-	unsigned char	psa_acr;		/* LAN Cont. ACR */
-	unsigned char	psa_dump_count;		/* number of Dump Commands in TFB */
-	unsigned char	psa_unused1[4];		/* unused */
-	unsigned char	psa_nwid_prefix;	/* ??? */
-	unsigned char	psa_unused2[3];		/* unused */
-	unsigned char	psa_conf_status;	/* Card Configuration Status */
-	unsigned char	psa_crc[2];		/* CRC over PSA */
-	unsigned char	psa_crc_status;		/* CRC Valid Flag */
-};
-#if	STRUCT_CHECK == 1
-#define	PSA_SIZE	64
-#endif	/* STRUCT_CHECK == 1 */
 
-/*
- * Modem Management Controller (MMC) write structure.
+#ifndef _WAVELAN_H
+#define	_WAVELAN_H
+
+/* The detection of the wavelan card is made by reading the MAC address
+ * from the card and checking it. If you have a non AT&T product (OEM,
+ * like DEC RoamAbout, or Digital Ocean, Epson, ...), you must modify this
+ * part to accomodate your hardware...
  */
-typedef struct mmw_t	mmw_t;
-struct mmw_t
+const char	MAC_ADDRESSES[][3] =
 {
-	unsigned char	mmw_encr_key[8];	/* encryption key */
-	unsigned char	mmw_encr_enable;	/* enable/disable encryption */
-	unsigned char	mmw_unused0[1];		/* unused */
-	unsigned char	mmw_des_io_invert;	/* ??? */
-	unsigned char	mmw_unused1[5];		/* unused */
-	unsigned char	mmw_loopt_sel;		/* looptest selection */
-#define		MMW_LOOPT_SEL_UNDEFINED	0x40	/* undefined */
-#define		MMW_LOOPT_SEL_INT	0x20	/* activate Attention Request */
-#define		MMW_LOOPT_SEL_LS	0x10	/* looptest without collision avoidance */
-#define		MMW_LOOPT_SEL_LT3A	0x08	/* looptest 3a */
-#define		MMW_LOOPT_SEL_LT3B	0x04	/* looptest 3b */
-#define		MMW_LOOPT_SEL_LT3C	0x02	/* looptest 3c */
-#define		MMW_LOOPT_SEL_LT3D	0x01	/* looptest 3d */
-	unsigned char	mmw_jabber_enable;	/* jabber timer enable */
-	unsigned char	mmw_freeze;		/* freeze / unfreeze signal level */
-	unsigned char	mmw_anten_sel;		/* antenna selection */
-#define		MMW_ANTEN_SEL_SEL	0x01	/* direct antenna selection */
-#define		MMW_ANTEN_SEL_ALG_EN	0x02	/* antenna selection algorithm enable */
-	unsigned char	mmw_ifs;		/* inter frame spacing */
-	unsigned char	mmw_mod_delay;	 	/* modem delay */
-	unsigned char	mmw_jam_time;		/* jamming time */
-	unsigned char	mmw_unused2[1];		/* unused */
-	unsigned char	mmw_thr_pre_set;	/* level threshold preset */
-	unsigned char	mmw_decay_prm;		/* decay parameters */
-	unsigned char	mmw_decay_updat_prm;	/* decay update parameters */
-	unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */
-	unsigned char	mmw_netw_id_l;		/* NWID low order byte */
-	unsigned char	mmw_netw_id_h;		/* NWID high order byte */
-};
-#if	STRUCT_CHECK == 1
-#define	MMW_SIZE	30
-#endif	/* STRUCT_CHECK == 1 */
+  { 0x08, 0x00, 0x0E },		/* AT&T Wavelan (standard) */
+  { 0x08, 0x00, 0x6A },		/* AT&T Wavelan (alternate) */
+  /* Add your card here */
+};
 
-#define	mmwoff(p,f) 	(unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+#define WAVELAN_ADDR_SIZE	6	/* Size of a MAC address */
 
-/*
- * Modem Management Controller (MMC) read structure.
- */
-typedef struct mmr_t	mmr_t;
-struct mmr_t
-{
-	unsigned char	mmr_unused0[8];		/* unused */
-	unsigned char	mmr_des_status;		/* encryption status */
-	unsigned char	mmr_des_avail;		/* encryption available (0x55 read) */
-	unsigned char	mmr_des_io_invert;	/* des I/O invert register */
-	unsigned char	mmr_unused1[5];		/* unused */
-	unsigned char	mmr_dce_status;		/* DCE status */
-#define		MMR_DCE_STATUS_ENERG_DET	0x01	/* energy detected */
-#define		MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */
-#define		MMR_DCE_STATUS_XMTITR_IND	0x04	/* transmitter on */
-#define		MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */
-	unsigned char	mmr_unused2[3];		/* unused */
-	unsigned char	mmr_correct_nwid_l;	/* no. of correct NWID's rxd (low) */
-	unsigned char	mmr_correct_nwid_h;	/* no. of correct NWID's rxd (high) */
-	unsigned char	mmr_wrong_nwid_l;	/* count of wrong NWID's received (low) */
-	unsigned char	mmr_wrong_nwid_h;	/* count of wrong NWID's received (high) */
-	unsigned char	mmr_thr_pre_set;	/* level threshold preset */
-	unsigned char	mmr_signal_lvl;		/* signal level */
-	unsigned char	mmr_silence_lvl;	/* silence level */
-	unsigned char	mmr_sgnl_qual;		/* signal quality */
-#define		MMR_SGNL_QUAL_0		0x01	/* signal quality 0 */
-#define		MMR_SGNL_QUAL_1		0x02	/* signal quality 1 */
-#define		MMR_SGNL_QUAL_2		0x04	/* signal quality 2 */
-#define		MMR_SGNL_QUAL_3		0x08	/* signal quality 3 */
-#define		MMR_SGNL_QUAL_S_A	0x80	/* currently selected antenna */
-	unsigned char	mmr_netw_id_l;		/* NWID low order byte ??? */
-	unsigned char	mmr_unused3[1];		/* unused */
-};
-#if	STRUCT_CHECK == 1
-#define	MMR_SIZE	30
-#endif	/* STRUCT_CHECK == 1 */
+#define WAVELAN_MTU		1500	/* Maximum size of WaveLAN packet */
 
-#define	MMR_LEVEL_MASK	0x3F
+#define	MAXDATAZ		(WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
 
-#define	mmroff(p,f) 	(unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+/*************************** PC INTERFACE ****************************/
 
 /*
  * Host Adaptor structure.
@@ -195,9 +78,8 @@
 	unsigned short	ha_pior2;	/* Program I/O Address Register Port 2 */
 	unsigned short	ha_piop2;	/* Program I/O Port 2 */
 };
-#if	STRUCT_CHECK == 1
+
 #define HA_SIZE		16
-#endif	/* STRUCT_CHECK == 1 */
 
 #define	hoff(p,f) 	(unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
 #define	HACR(p)		hoff(p, ha_command)
@@ -227,14 +109,14 @@
 #define	HACR_DEFAULT		(HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
 #define	HACR_INTRON		(HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
 
-#define	MAXDATAZ		(WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
+/************************** MEMORY LAYOUT **************************/
 
 /*
  * Onboard 64k RAM layout.
  * (Offsets from 0x0000.)
  */
-#define OFFSET_RU		0x0000
-#define OFFSET_CU		0x8000
+#define OFFSET_RU		0x0000		/* 75 % memory */
+#define OFFSET_CU		0xC000		/* 25 % memory */
 #define OFFSET_SCB		(OFFSET_ISCP - sizeof(scb_t))
 #define OFFSET_ISCP		(OFFSET_SCP - sizeof(iscp_t))
 #define OFFSET_SCP		I82586_SCP_ADDR
@@ -244,6 +126,217 @@
 
 #define	NRXBLOCKS		((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
 #define	NTXBLOCKS		((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
+
+/********************** PARAMETER STORAGE AREA **********************/
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t	psa_t;
+struct psa_t
+{
+  unsigned char	psa_io_base_addr_1;	/* [0x00] Base address 1 ??? */
+  unsigned char	psa_io_base_addr_2;	/* [0x01] Base address 2 */
+  unsigned char	psa_io_base_addr_3;	/* [0x02] Base address 3 */
+  unsigned char	psa_io_base_addr_4;	/* [0x03] Base address 4 */
+  unsigned char	psa_rem_boot_addr_1;	/* [0x04] Remote Boot Address 1 */
+  unsigned char	psa_rem_boot_addr_2;	/* [0x05] Remote Boot Address 2 */
+  unsigned char	psa_rem_boot_addr_3;	/* [0x06] Remote Boot Address 3 */
+  unsigned char	psa_holi_params;	/* [0x07] HOst Lan Interface (HOLI) Parameters */
+  unsigned char	psa_int_req_no;		/* [0x08] Interrupt Request Line */
+  unsigned char	psa_unused0[7];		/* [0x09-0x0F] unused */
+
+  unsigned char	psa_univ_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x10-0x15] Universal (factory) MAC Address */
+  unsigned char	psa_local_mac_addr[WAVELAN_ADDR_SIZE];	/* [0x16-1B] Local MAC Address */
+  unsigned char	psa_univ_local_sel;	/* [0x1C] Universal Local Selection */
+#define		PSA_UNIVERSAL	0		/* Universal (factory) */
+#define		PSA_LOCAL	1		/* Local */
+  unsigned char	psa_comp_number;	/* [0x1D] Compatability Number: */
+#define		PSA_COMP_PC_AT_915	0 	/* PC-AT 915 MHz	*/
+#define		PSA_COMP_PC_MC_915	1 	/* PC-MC 915 MHz	*/
+#define		PSA_COMP_PC_AT_2400	2 	/* PC-AT 2.4 GHz	*/
+#define		PSA_COMP_PC_MC_2400	3 	/* PC-MC 2.4 GHz	*/
+#define		PSA_COMP_PCMCIA_915	4 	/* PCMCIA 915 MHz or 2.0 */
+  unsigned char	psa_thr_pre_set;	/* [0x1E] Modem Threshold Preset */
+  unsigned char	psa_feature_select;	/* [0x1F] Call code required (1=on) */
+#define		PSA_FEATURE_CALL_CODE	0x01 	/* Call code required (Japan) */
+  unsigned char	psa_subband;		/* [0x20] Subband	*/
+#define		PSA_SUBBAND_915		0	/* 915 MHz or 2.0 */
+#define		PSA_SUBBAND_2425	1	/* 2425 MHz	*/
+#define		PSA_SUBBAND_2460	2	/* 2460 MHz	*/
+#define		PSA_SUBBAND_2484	3	/* 2484 MHz	*/
+#define		PSA_SUBBAND_2430_5	4	/* 2430.5 MHz	*/
+  unsigned char	psa_quality_thr;	/* [0x21] Modem Quality Threshold */
+  unsigned char	psa_mod_delay;		/* [0x22] Modem Delay ??? (reserved) */
+  unsigned char	psa_nwid[2];		/* [0x23-0x24] Network ID */
+  unsigned char	psa_nwid_select;	/* [0x25] Network ID Select On Off */
+  unsigned char	psa_encryption_select;	/* [0x26] Encryption On Off */
+  unsigned char	psa_encryption_key[8];	/* [0x27-0x2E] Encryption Key */
+  unsigned char	psa_databus_width;	/* [0x2F] AT bus width select 8/16 */
+  unsigned char	psa_call_code[8];	/* [0x30-0x37] (Japan) Call Code */
+  unsigned char	psa_nwid_prefix[2];	/* [0x38-0x39] Roaming domain */
+  unsigned char	psa_reserved[2];	/* [0x3A-0x3B] Reserved - fixed 00 */
+  unsigned char	psa_conf_status;	/* [0x3C] Conf Status, bit 0=1:config*/
+  unsigned char	psa_crc[2];		/* [0x3D] CRC-16 over PSA */
+  unsigned char	psa_crc_status;		/* [0x3F] CRC Valid Flag */
+};
+
+#define	PSA_SIZE	64
+
+/* Calculate offset of a field in the above structure
+ * Warning : only even addresses are used */
+#define	psaoff(p,f) 	((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
+
+/******************** MODEM MANAGEMENT INTERFACE ********************/
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t	mmw_t;
+struct mmw_t
+{
+  unsigned char	mmw_encr_key[8];	/* encryption key */
+  unsigned char	mmw_encr_enable;	/* enable/disable encryption */
+#define	MMW_ENCR_ENABLE_MODE	0x02	/* Mode of security option */
+#define	MMW_ENCR_ENABLE_EN	0x01	/* Enable security option */
+  unsigned char	mmw_unused0[1];		/* unused */
+  unsigned char	mmw_des_io_invert;	/* Encryption option */
+#define	MMW_DES_IO_INVERT_RES	0x0F	/* Reserved */
+#define	MMW_DES_IO_INVERT_CTRL	0xF0	/* Control ??? (set to 0) */
+  unsigned char	mmw_unused1[5];		/* unused */
+  unsigned char	mmw_loopt_sel;		/* looptest selection */
+#define	MMW_LOOPT_SEL_DIS_NWID	0x40	/* disable NWID filtering */
+#define	MMW_LOOPT_SEL_INT	0x20	/* activate Attention Request */
+#define	MMW_LOOPT_SEL_LS	0x10	/* looptest w/o collision avoidance */
+#define MMW_LOOPT_SEL_LT3A	0x08	/* looptest 3a */
+#define	MMW_LOOPT_SEL_LT3B	0x04	/* looptest 3b */
+#define	MMW_LOOPT_SEL_LT3C	0x02	/* looptest 3c */
+#define	MMW_LOOPT_SEL_LT3D	0x01	/* looptest 3d */
+  unsigned char	mmw_jabber_enable;	/* jabber timer enable */
+  /* Abort transmissions > 200 ms */
+  unsigned char	mmw_freeze;		/* freeze / unfreeeze signal level */
+  /* 0 : signal level & qual updated for every new message, 1 : frozen */
+  unsigned char	mmw_anten_sel;		/* antenna selection */
+#define MMW_ANTEN_SEL_SEL	0x01	/* direct antenna selection */
+#define	MMW_ANTEN_SEL_ALG_EN	0x02	/* antenna selection algo. enable */
+  unsigned char	mmw_ifs;		/* inter frame spacing */
+  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
+  unsigned char	mmw_mod_delay;	 	/* modem delay (synchro) */
+  unsigned char	mmw_jam_time;		/* jamming time (after collision) */
+  unsigned char	mmw_unused2[1];		/* unused */
+  unsigned char	mmw_thr_pre_set;	/* level threshold preset */
+  /* Discard all packet with signal < this value (0) */
+  unsigned char	mmw_decay_prm;		/* decay parameters */
+  unsigned char	mmw_decay_updat_prm;	/* decay update parameterz */
+  unsigned char	mmw_quality_thr;	/* quality (z-quotient) threshold */
+  /* Discard all packet with quality < this value (3) */
+  unsigned char	mmw_netw_id_l;		/* NWID low order byte */
+  unsigned char	mmw_netw_id_h;		/* NWID high order byte */
+  /* Network ID or Domain : create virtual net on the air */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char	mmw_mode_select;	/* for analog tests (set to 0) */
+  unsigned char	mmw_unused3[1];		/* unused */
+  unsigned char	mmw_fee_ctrl;		/* frequency eeprom control */
+#define	MMW_FEE_CTRL_PRE	0x10	/* Enable protected instructions */
+#define	MMW_FEE_CTRL_DWLD	0x08	/* Download eeprom to mmc */
+#define	MMW_FEE_CTRL_CMD	0x07	/* EEprom commands : */
+#define	MMW_FEE_CTRL_READ	0x06	/* Read */
+#define	MMW_FEE_CTRL_WREN	0x04	/* Write enable */
+#define	MMW_FEE_CTRL_WRITE	0x05	/* Write data to address */
+#define	MMW_FEE_CTRL_WRALL	0x04	/* Write data to all addresses */
+#define	MMW_FEE_CTRL_WDS	0x04	/* Write disable */
+#define	MMW_FEE_CTRL_PRREAD	0x16	/* Read addr from protect register */
+#define	MMW_FEE_CTRL_PREN	0x14	/* Protect register enable */
+#define	MMW_FEE_CTRL_PRCLEAR	0x17	/* Unprotect all registers */
+#define	MMW_FEE_CTRL_PRWRITE	0x15	/* Write addr in protect register */
+#define	MMW_FEE_CTRL_PRDS	0x14	/* Protect register disable */
+  /* Never issue this command (PRDS) : it's irreversible !!! */
+
+  unsigned char	mmw_fee_addr;		/* EEprom address */
+#define	MMW_FEE_ADDR_CHANNEL	0xF0	/* Select the channel */
+#define	MMW_FEE_ADDR_OFFSET	0x0F	/* Offset in channel data */
+#define	MMW_FEE_ADDR_EN		0xC0	/* FEE_CTRL enable operations */
+#define	MMW_FEE_ADDR_DS		0x00	/* FEE_CTRL disable operations */
+#define	MMW_FEE_ADDR_ALL	0x40	/* FEE_CTRL all operations */
+#define	MMW_FEE_ADDR_CLEAR	0xFF	/* FEE_CTRL clear operations */
+
+  unsigned char	mmw_fee_data_l;		/* Write data to EEprom */
+  unsigned char	mmw_fee_data_h;		/* high octet */
+  unsigned char	mmw_ext_ant;		/* Setting for external antenna */
+#define	MMW_EXT_ANT_EXTANT	0x01	/* Select external antenna */
+#define	MMW_EXT_ANT_POL		0x02	/* Polarity of the antenna */
+#define	MMW_EXT_ANT_INTERNAL	0x00	/* Internal antenna */
+#define	MMW_EXT_ANT_EXTERNAL	0x03	/* External antenna */
+#define	MMW_EXT_ANT_IQ_TEST	0x1C	/* IQ test pattern (set to 0) */
+};
+
+#define	MMW_SIZE	37
+
+#define	mmwoff(p,f) 	(unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t	mmr_t;
+struct mmr_t
+{
+  unsigned char	mmr_unused0[8];		/* unused */
+  unsigned char	mmr_des_status;		/* encryption status */
+  unsigned char	mmr_des_avail;		/* encryption available (0x55 read) */
+#define	MMR_DES_AVAIL_DES	0x55		/* DES available */
+#define	MMR_DES_AVAIL_AES	0x33		/* AES (AT&T) available */
+  unsigned char	mmr_des_io_invert;	/* des I/O invert register */
+  unsigned char	mmr_unused1[5];		/* unused */
+  unsigned char	mmr_dce_status;		/* DCE status */
+#define	MMR_DCE_STATUS_RX_BUSY		0x01	/* receiver busy */
+#define	MMR_DCE_STATUS_LOOPT_IND	0x02	/* loop test indicated */
+#define	MMR_DCE_STATUS_TX_BUSY		0x04	/* transmitter on */
+#define	MMR_DCE_STATUS_JBR_EXPIRED	0x08	/* jabber timer expired */
+  unsigned char	mmr_dsp_id;		/* DSP id (AA = Daedalus rev A) */
+  unsigned char	mmr_unused2[2];		/* unused */
+  unsigned char	mmr_correct_nwid_l;	/* # of correct NWID's rxd (low) */
+  unsigned char	mmr_correct_nwid_h;	/* # of correct NWID's rxd (high) */
+  /* Warning : Read high order octet first !!! */
+  unsigned char	mmr_wrong_nwid_l;	/* # of wrong NWID's rxd (low) */
+  unsigned char	mmr_wrong_nwid_h;	/* # of wrong NWID's rxd (high) */
+  unsigned char	mmr_thr_pre_set;	/* level threshold preset */
+#define	MMR_THR_PRE_SET		0x3F		/* level threshold preset */
+#define	MMR_THR_PRE_SET_CUR	0x80		/* Current signal above it */
+  unsigned char	mmr_signal_lvl;		/* signal level */
+#define	MMR_SIGNAL_LVL		0x3F		/* signal level */
+#define	MMR_SIGNAL_LVL_VALID	0x80		/* Updated since last read */
+  unsigned char	mmr_silence_lvl;	/* silence level (noise) */
+#define	MMR_SILENCE_LVL		0x3F		/* silence level */
+#define	MMR_SILENCE_LVL_VALID	0x80		/* Updated since last read */
+  unsigned char	mmr_sgnl_qual;		/* signal quality */
+#define	MMR_SGNL_QUAL		0x0F		/* signal quality */
+#define	MMR_SGNL_QUAL_ANT	0x80		/* current antenna used */
+  unsigned char	mmr_netw_id_l;		/* NWID low order byte ??? */
+  unsigned char	mmr_unused3[3];		/* unused */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char	mmr_fee_status;		/* Status of frequency eeprom */
+#define	MMR_FEE_STATUS_ID	0xF0		/* Modem revision id */
+#define	MMR_FEE_STATUS_DWLD	0x08		/* Download in progress */
+#define	MMR_FEE_STATUS_BUSY	0x04		/* EEprom busy */
+  unsigned char	mmr_unused4[1];		/* unused */
+  unsigned char	mmr_fee_data_l;		/* Read data from eeprom (low) */
+  unsigned char	mmr_fee_data_h;		/* Read data from eeprom (high) */
+};
+
+#define	MMR_SIZE	36
+
+#define	mmroff(p,f) 	(unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/* Make the two above structures one */
+typedef union mm_t
+{
+  struct mmw_t	w;	/* Write to the mmc */
+  struct mmr_t	r;	/* Read from the mmc */
+} mm_t;
+
+#endif /* _WAVELAN_H */
 
 /*
  * This software may only be used and distributed
diff -u --recursive --new-file v2.1.14/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h
--- v2.1.14/linux/drivers/net/wavelan.p.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/wavelan.p.h	Tue Dec  3 09:24:16 1996
@@ -0,0 +1,613 @@
+/*
+ *	Wavelan ISA driver
+ *
+ *		Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ *
+ * This file contain all definition and declarations necessary for the
+ * wavelan isa driver. This file is a private header, so it should
+ * be included only on wavelan.c !!!
+ */
+
+#ifndef WAVELAN_P_H
+#define WAVELAN_P_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * This driver provide a Linux interface to the Wavelan ISA hardware
+ * The Wavelan is a product of Lucent ("http://wavelan.netland.nl/").
+ * This division was formerly part of NCR and then AT&T.
+ * Wavelan are also distributed by DEC (RoamAbout), Digital Ocean and
+ * Aironet (Arlan). If you have one of those product, you will need to
+ * make some changes below...
+ *
+ * This driver is still a beta software. A lot of bugs have been corrected,
+ * a lot of functionalities are implemented, the whole appear pretty stable,
+ * but there is still some area of improvement (encryption, performance...).
+ *
+ * To know how to use this driver, read the NET3 HOWTO.
+ * If you want to exploit the many other fonctionalities, look comments
+ * in the code...
+ *
+ * This driver is the result of the effort of many peoples (see below).
+ */
+
+/* ------------------------ SPECIFIC NOTES ------------------------ */
+/*
+ * MAC address and hardware detection :
+ * ----------------------------------
+ *	The detection code of the wavelan chech that the first 3
+ *	octets of the MAC address fit the company code. This type of
+ *	detection work well for AT&T cards (because the AT&T code is
+ *	hardcoded in wavelan.h), but of course will fail for other
+ *	manufacturer.
+ *
+ *	If you are sure that your card is derived from the wavelan,
+ *	here is the way to configure it :
+ *	1) Get your MAC address
+ *		a) With your card utilities (wfreqsel, instconf, ...)
+ *		b) With the driver :
+ *			o compile the kernel with DEBUG_CONFIG_INFO enabled
+ *			o Boot and look the card messages
+ *	2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
+ *	3) Compile & verify
+ *	4) Send me the MAC code - I will include it in the next version...
+ *
+ * "CU Inactive" message at boot up :
+ * -----------------------------------
+ *	It seem that there is some weird timings problems with the
+ *	Intel microcontroler. In fact, this message is triggered by a
+ *	bad reading of the on board ram the first time we read the
+ *	control block. If you ignore this message, all is ok (but in
+ *	fact, currently, it reset the wavelan hardware).
+ *
+ *	To get rid of that problem, there is two solution. The first
+ *	is to add a dummy read of the scb at the end of
+ *	wv_82586_config. The second is to add the timers
+ *	wv_synchronous_cmd and wv_ack (the udelay just after the
+ *	waiting loops - seem that the controler is not totally ready
+ *	when it say it is !).
+ *
+ *	In the current code, I use the second solution (to be
+ *	consistent with the original solution of Bruce Janson).
+ */
+
+/* --------------------- WIRELESS EXTENSIONS --------------------- */
+/*
+ * This driver is the first one to support "wireless extensions".
+ * This set of extensions provide you some way to control the wireless
+ * caracteristics of the hardware in a standard way and support for
+ * applications for taking advantage of it (like Mobile IP).
+ *
+ * By default, these wireless extensions are disabled, because they
+ * need a patch to the Linux Kernel. This simple patch may be found
+ * with the driver + some utilities to access those wireless
+ * extensions (iwconfig...). Hopefully, those wireless extensions will
+ * make their way in the kernel someday.
+ *
+ * You also will need to enable the CONFIG_NET_RADIO in the kernel
+ * configuration to enable the wireless extensions.
+ */
+
+/* ---------------------------- FILES ---------------------------- */
+/*
+ * wavelan.c :		The actual code for the driver - C functions
+ *
+ * wavelan.p.h :	Private header : local types / vars for the driver
+ *
+ * wavelan.h :		Description of the hardware interface & structs
+ *
+ * i82586.h :		Description if the Ethernet controler
+ */
+
+/* --------------------------- HISTORY --------------------------- */
+/*
+ * (Made with information in drivers headers. It may not be accurate,
+ * and I garantee nothing except my best effort...)
+ *
+ * The history of the Wavelan drivers is as complicated as history of
+ * the Wavelan itself (NCR -> AT&T -> Lucent).
+ *
+ * All started with Anders Klemets <klemets@paul.rutgers.edu>,
+ * writting a Wavelan ISA driver for the MACH microkernel. Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modify this for the Pcmcia hardware.
+ * 
+ * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
+ * and add specific Pcmcia support (there is currently no equivalent
+ * of the PCMCIA package under BSD...).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to freeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * correctly 2.00 cards (2.4 GHz with frequency selection).
+ * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his
+ * Pcmcia package (+ bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patchs to the Pcmcia driver. After, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards. Then, I've done the same to the Pcmcia driver + some
+ * reorganisation. Finally, I came back to the ISA driver to
+ * upgrade it at the same level as the Pcmcia one and reorganise
+ * the code
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed informations on the Wavelan hardware.
+ *
+ * Yongguang Zhang <ygz@isl.hrl.hac.com> send me a patch for enabling
+ * multicast in the old pcmcia driver. I tried to do the same (with
+ * some minor changes) in this driver, but without any luck (I don't
+ * know how to enable multicast in the chip...).
+ */
+
+/* The original copyrights and litteratures mention others names and
+ * credits. I don't know what there part in this development was...
+ */
+
+/* By the way : for the copyright & legal stuff :
+ * Almost everybody wrote code under GNU or BSD license (or alike),
+ * and want that their original copyright remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody want to take responsibility for anything, except the fame...
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ *	Ajay Bakre (bakre@paul.rutgers.edu),
+ *	Donald Becker (becker@cesdis.gsfc.nasa.gov),
+ *	Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ *	Anders Klemets (klemets@it.kth.se),
+ *	Vladimir V. Kolpakov (w@stier.koenig.ru),
+ *	Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
+ *	Pauline Middelink (middelin@polyware.iaf.nl),
+ *	Robert Morris (rtm@das.harvard.edu),
+ *	Jean Tourrilhes (jt@hplb.hpl.hp.com),
+ *	Girish Welling (welling@paul.rutgers.edu),
+ *	Yongguang Zhang <ygz@isl.hrl.hac.com>...
+ *
+ * Thanks go also to:
+ *	James Ashton (jaa101@syseng.anu.edu.au),
+ *	Alan Cox (iialan@iiit.swan.ac.uk),
+ *	Allan Creighton (allanc@cs.usyd.edu.au),
+ *	Matthew Geier (matthew@cs.usyd.edu.au),
+ *	Remo di Giovanni (remo@cs.usyd.edu.au),
+ *	Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
+ *	Vipul Gupta (vgupta@cs.binghamton.edu),
+ *	Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ *	Tim Nicholson (tim@cs.usyd.edu.au),
+ *	Ian Parkin (ian@cs.usyd.edu.au),
+ *	John Rosenberg (johnr@cs.usyd.edu.au),
+ *	George Rossi (george@phm.gov.au),
+ *	Arthur Scott (arthur@cs.usyd.edu.au),
+ *	Peter Storey,
+ * for their assistance and advice.
+ *
+ * Additional Credits:
+ *
+ * My developpement has been done under Linux 2.0.x (Debian 1.1) with
+ *	an HP Vectra XP/60.
+ *
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present :
+ *
+ * Changes mades in first release :
+ * ------------------------------
+ *	- Reorganisation of the code, function name change
+ *	- Creation of private header (wavelan.p.h)
+ *	- Reorganised debug messages
+ *	- More comments, history, ...
+ *	- mmc_init : configure the PSA if not done
+ *	- mmc_init : correct default value of level threshold for pcmcia
+ *	- mmc_init : 2.00 detection better code for 2.00 init
+ *	- better info at startup
+ *	- irq setting (note : this setting is permanent...)
+ *	- Watchdog : change strategy (+ solve module removal problems)
+ *	- add wireless extensions (ioctl & get_wireless_stats)
+ *	  get/set nwid/frequency on fly, info for /proc/net/wireless
+ *	- More wireless extension : SETSPY and GETSPY
+ *	- Make wireless extensions optional
+ *	- Private ioctl to set/get quality & level threshold, histogram
+ *	- Remove /proc/net/wavelan
+ *	- Supress useless stuff from lp (net_local)
+ *	- kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
+ *	- Add message level (debug stuff in /var/adm/debug & errors not
+ *	  displayed at console and still in /var/adm/messages)
+ *	- multi device support
+ *	- Start fixing the probe (init code)
+ *	- More inlines
+ *	- man page
+ *	- Lot of others minor details & cleanups
+ *
+ * Changes made in second release :
+ * ------------------------------
+ *	- Cleanup init code (probe & module init)
+ *	- Better multi device support (module)
+ *	- name assignement (module)
+ *
+ * Changes made in third release :
+ * -----------------------------
+ *	- Be more conservative on timers
+ *	- Preliminary support for multicast (I still lack some details...)
+ *
+ * Changes made in fourth release :
+ * ----------------------------
+ *	- multicast (revisited and finished)
+ *	- Avoid reset in set_multicast_list (a really big hack)
+ *	  if somebody could apply this code for other i82586 based driver...
+ *	- Share on board memory 75% RU / 25% CU (instead of 50/50)
+ *
+ * Changes made in this release :
+ * ----------------------------
+ *	- Change the detection code for multi manufacturer code support
+ *
+ * Wishes & dreams :
+ * ---------------
+ *	- encryption stuff
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include	<linux/module.h>
+
+#include	<linux/kernel.h>
+#include	<linux/sched.h>
+#include	<linux/types.h>
+#include	<linux/fcntl.h>
+#include	<linux/interrupt.h>
+#include	<linux/stat.h>
+#include	<linux/ptrace.h>
+#include	<linux/ioport.h>
+#include	<linux/in.h>
+#include	<linux/string.h>
+#include	<linux/delay.h>
+#include	<asm/system.h>
+#include	<asm/bitops.h>
+#include	<asm/io.h>
+#include	<asm/dma.h>
+#include	<asm/uaccess.h>
+#include	<linux/errno.h>
+#include	<linux/netdevice.h>
+#include	<linux/etherdevice.h>
+#include	<linux/skbuff.h>
+#include	<linux/malloc.h>
+#include	<linux/timer.h>
+
+/*#include <linux/wireless.h>*/		/* Wireless extensions */
+
+/* Wavelan declarations */
+#include	"i82586.h"
+#include	"wavelan.h"
+
+/****************************** DEBUG ******************************/
+
+#undef DEBUG_MODULE_TRACE	/* Module insertion/removal */
+#undef DEBUG_CALLBACK_TRACE	/* Calls made by Linux */
+#undef DEBUG_INTERRUPT_TRACE	/* Calls to handler */
+#undef DEBUG_INTERRUPT_INFO	/* type of interrupt & so on */
+#define DEBUG_INTERRUPT_ERROR	/* problems */
+#undef DEBUG_CONFIG_TRACE	/* Trace the config functions */
+#undef DEBUG_CONFIG_INFO	/* What's going on... */
+#define DEBUG_CONFIG_ERRORS	/* Errors on configuration */
+#undef DEBUG_TX_TRACE		/* Transmission calls */
+#undef DEBUG_TX_INFO		/* Header of the transmited packet */
+#define DEBUG_TX_ERROR		/* unexpected conditions */
+#undef DEBUG_RX_TRACE		/* Transmission calls */
+#undef DEBUG_RX_INFO		/* Header of the transmited packet */
+#define DEBUG_RX_ERROR		/* unexpected conditions */
+#undef DEBUG_PACKET_DUMP	16	/* Dump packet on the screen */
+#undef DEBUG_IOCTL_TRACE	/* Misc call by Linux */
+#undef DEBUG_IOCTL_INFO		/* Various debug info */
+#define DEBUG_IOCTL_ERROR	/* What's going wrong */
+#define DEBUG_BASIC_SHOW	/* Show basic startup info */
+#undef DEBUG_VERSION_SHOW	/* Print version info */
+#undef DEBUG_PSA_SHOW		/* Dump psa to screen */
+#undef DEBUG_MMC_SHOW		/* Dump mmc to screen */
+#undef DEBUG_SHOW_UNUSED	/* Show also unused fields */
+#undef DEBUG_I82586_SHOW	/* Show i82586 status */
+#undef DEBUG_DEVICE_SHOW	/* Show device parameters */
+
+/* Options : */
+#define USE_PSA_CONFIG		/* Use info from the PSA */
+#define IGNORE_NORMAL_XMIT_ERRS	/* Don't bother with normal conditions */
+#undef STRUCT_CHECK		/* Verify padding of structures */
+#undef PSA_CRC			/* Check CRC in PSA */
+#undef OLDIES			/* Old code (to redo) */
+#undef RECORD_SNR		/* To redo */
+#undef EEPROM_IS_PROTECTED	/* Doesn't seem to be necessary */
+#define MULTICAST_AVOID		/* Avoid extra multicast (I'm sceptical) */
+
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+/* Warning : these stuff will slow down the driver... */
+#define WIRELESS_SPY		/* Enable spying addresses */
+#undef HISTOGRAM		/* Enable histogram of sig level... */
+#endif
+
+/************************ CONSTANTS & MACROS ************************/
+
+#ifdef DEBUG_VERSION_SHOW
+static const char	*version	= "wavelan.c : v12 (wireless extensions) 1/12/96\n";
+#endif
+
+/* Watchdog temporisation */
+#define	WATCHDOG_JIFFIES	32	/* TODO: express in HZ. */
+
+/* Macro to get the number of elements in an array */
+#define	NELS(a)				(sizeof(a) / sizeof(a[0]))
+
+/* ------------------------ PRIVATE IOCTL ------------------------ */
+
+#define SIOCSIPQTHR	SIOCDEVPRIVATE		/* Set quality threshold */
+#define SIOCGIPQTHR	SIOCDEVPRIVATE + 1	/* Get quality threshold */
+#define SIOCSIPLTHR	SIOCDEVPRIVATE + 2	/* Set level threshold */
+#define SIOCGIPLTHR	SIOCDEVPRIVATE + 3	/* Get level threshold */
+
+#define SIOCSIPHISTO	SIOCDEVPRIVATE + 6	/* Set histogram ranges */
+#define SIOCGIPHISTO	SIOCDEVPRIVATE + 7	/* Get histogram values */
+
+/****************************** TYPES ******************************/
+
+/* Shortcuts */
+typedef struct device		device;
+typedef struct enet_statistics	en_stats;
+typedef struct iw_statistics	iw_stats;
+typedef struct iw_quality	iw_qual;
+typedef struct net_local	net_local;
+typedef struct timer_list	timer_list;
+
+/* Basic types */
+typedef u_char		mac_addr[WAVELAN_ADDR_SIZE];	/* Hardware address */
+
+/*
+ * Static specific data for the interface.
+ *
+ * For each network interface, Linux keep data in two structure. "device"
+ * keep the generic data (same format for everybody) and "net_local" keep
+ * the additional specific data.
+ * Note that some of this specific data is in fact generic (en_stats, for
+ * example).
+ */
+struct net_local
+{
+  net_local *	next;		/* Linked list of the devices */
+  device *	dev;		/* Reverse link... */
+  en_stats	stats;		/* Ethernet interface statistics */
+  int		nresets;	/* Number of hw resets */
+  u_char	reconfig_82586;	/* Need to reconfigure the controler */
+  u_char	promiscuous;	/* Promiscuous mode */
+  int		mc_count;	/* Number of multicast addresses */
+  timer_list	watchdog;	/* To avoid blocking state */
+  u_short	hacr;		/* Current host interface state */
+
+  int		tx_n_in_use;
+  u_short	rx_head;
+  u_short	rx_last;
+  u_short	tx_first_free;
+  u_short	tx_first_in_use;
+
+#ifdef WIRELESS_EXT
+  iw_stats	wstats;		/* Wireless specific stats */
+#endif
+
+#ifdef WIRELESS_SPY
+  int		spy_number;		/* Number of addresses to spy */
+  mac_addr	spy_address[IW_MAX_SPY];	/* The addresses to spy */
+  iw_qual	spy_stat[IW_MAX_SPY];		/* Statistics gathered */
+#endif	/* WIRELESS_SPY */
+#ifdef HISTOGRAM
+  int		his_number;		/* Number of intervals */
+  u_char	his_range[16];		/* Boundaries of interval ]n-1; n] */
+  u_long	his_sum[16];		/* Sum in interval */
+#endif	/* HISTOGRAM */
+};
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- MISC SUBROUTINES ------------------------ */
+static inline unsigned long	/* flags */
+	wv_splhi(void);		/* Disable interrupts */
+static inline void
+	wv_splx(unsigned long);	/* ReEnable interrupts : flags */
+static u_char
+	wv_irq_to_psa(int);
+static int
+	wv_psa_to_irq(u_char);
+/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
+static inline u_short		/* data */
+	hasr_read(u_short);	/* Read the host interface : base address */
+static inline void
+	hacr_write(u_short,	/* Write to host interface : base address */
+		   u_short),	/* data */
+	hacr_write_slow(u_short,
+		   u_short),
+	set_chan_attn(u_short,	/* ioaddr */
+		      u_short),	/* hacr */
+	wv_hacr_reset(u_short),	/* ioaddr */
+	wv_16_off(u_short,	/* ioaddr */
+		  u_short),	/* hacr */
+	wv_16_on(u_short,	/* ioaddr */
+		 u_short),	/* hacr */
+	wv_ints_off(device *),
+	wv_ints_on(device *);
+/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
+static void
+	psa_read(u_short,	/* Read the Parameter Storage Area */
+		 u_short,	/* hacr */
+		 int,		/* offset in PSA */
+		 u_char *,	/* buffer to fill */
+		 int),		/* size to read */
+	psa_write(u_short, 	/* Write to the PSA */
+		  u_short,	/* hacr */
+		  int,		/* Offset in psa */
+		  u_char *,	/* Buffer in memory */
+		  int);		/* Length of buffer */
+static inline void
+	mmc_out(u_short,	/* Write 1 byte to the Modem Manag Control */
+		u_short,
+		u_char),
+	mmc_write(u_short,	/* Write n bytes to the MMC */
+		  u_char,
+		  u_char *,
+		  int);
+static inline u_char		/* Read 1 byte from the MMC */
+	mmc_in(u_short,
+	       u_short);
+static inline void
+	mmc_read(u_short,	/* Read n bytes from the MMC */
+		 u_char,
+		 u_char *,
+		 int),
+	fee_wait(u_short,	/* Wait for frequency EEprom : base address */
+		 int,		/* Base delay to wait for */
+		 int);		/* Number of time to wait */
+static void
+	fee_read(u_short,	/* Read the frequency EEprom : base address */
+		 u_short,	/* destination offset */
+		 u_short *,	/* data buffer */
+		 int),		/* number of registers */
+	fee_write(u_short,	/* Write to frequency EEprom : base address */
+		  u_short,	/* destination offset */
+		  u_short *,	/* data buffer */
+		  int);		/* number of registers */
+/* ---------------------- I82586 SUBROUTINES ----------------------- */
+static /*inline*/ void
+	obram_read(u_short,	/* ioaddr */
+		   u_short,	/* o */
+		   u_char *,	/* b */
+		   int);	/* n */
+static inline void
+	obram_write(u_short,	/* ioaddr */
+		    u_short,	/* o */
+		    u_char *,	/* b */
+		    int);	/* n */
+static void
+	wv_ack(device *);
+static inline int
+	wv_synchronous_cmd(device *,
+			   const char *),
+	wv_config_complete(device *,
+			   u_short,
+			   net_local *);
+static int
+	wv_complete(device *,
+		    u_short,
+		    net_local *);
+static inline void
+	wv_82586_reconfig(device *);
+/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
+#ifdef DEBUG_I82586_SHOW
+static void
+	wv_scb_show(unsigned short);
+#endif
+static inline void
+	wv_init_info(device *);	/* display startup info */
+/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
+static en_stats	*
+	wavelan_get_stats(device *);	/* Give stats /proc/net/dev */
+static void
+	wavelan_set_multicast_list(device *);
+/* ----------------------- PACKET RECEPTION ----------------------- */
+static inline void
+	wv_packet_read(device *,	/* Read a packet from a frame */
+		       u_short,
+		       int),
+	wv_receive(device *);	/* Read all packets waiting */
+/* --------------------- PACKET TRANSMISSION --------------------- */
+static inline void
+	wv_packet_write(device *,	/* Write a packet to the Tx buffer */
+			void *,
+			short);
+static int
+	wavelan_packet_xmit(struct sk_buff *,	/* Send a packet */
+			    device *);
+/* -------------------- HARDWARE CONFIGURATION -------------------- */
+static inline int
+	wv_mmc_init(device *),		/* Initialize the modem */
+	wv_ru_start(device *),		/* Start the i82586 receiver unit */
+	wv_cu_start(device *),		/* Start the i82586 command unit */
+	wv_82586_start(device *);	/* Start the i82586 */
+static void
+	wv_82586_config(device *);	/* Configure the i82586 */
+static inline void
+	wv_82586_stop(device *);
+static int
+	wv_hw_reset(device *),		/* Reset the wavelan hardware */
+	wv_check_ioaddr(u_short,	/* ioaddr */
+			u_char *);	/* mac address (read) */
+/* ---------------------- INTERRUPT HANDLING ---------------------- */
+static void
+	wavelan_interrupt(int,		/* Interrupt handler */
+			  void *,
+			  struct pt_regs *);
+static void
+	wavelan_watchdog(u_long);	/* Transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+	wavelan_open(device *),		/* Open the device */
+	wavelan_close(device *),	/* Close the device */
+	wavelan_config(device *);	/* Configure one device */
+extern int
+	wavelan_probe(device *);	/* See Space.c */
+
+/**************************** VARIABLES ****************************/
+
+/*
+ * This is the root of the linked list of wavelan drivers
+ * It is use to verify that we don't reuse the same base address
+ * for two differents drivers and to make the cleanup when
+ * removing the module.
+ */
+static net_local *	wavelan_list	= (net_local *) NULL;
+
+/*
+ * This table is used to translate the psa value to irq number
+ * and vice versa...
+ */
+static u_char	irqvals[]	=
+{
+	   0,    0,    0, 0x01,
+	0x02, 0x04,    0, 0x08,
+	   0,    0, 0x10, 0x20,
+	0x40,    0,    0, 0x80,
+};
+
+/*
+ * Table of the available i/o address (base address) for wavelan
+ */
+static unsigned short	iobase[]	=
+{
+#if	0
+  /* Leave out 0x3C0 for now -- seems to clash with some video
+   * controllers.
+   * Leave out the others too -- we will always use 0x390 and leave
+   * 0x300 for the Ethernet device.
+   * Jean II : 0x3E0 is really fine as well...
+   */
+  0x300, 0x390, 0x3E0, 0x3C0
+#endif	/* 0 */
+  0x390, 0x3E0
+};
+
+#ifdef	MODULE
+/* Name of the devices (memory allocation) */
+static char	devname[4][IFNAMSIZ] = { "", "", "", "" };
+
+/* Parameters set by insmod */
+static int	io[4]	= { 0, 0, 0, 0 };
+static int	irq[4]	= { 0, 0, 0, 0 };
+static char *	name[4] = { devname[0], devname[1], devname[2], devname[3] };
+#endif	/* MODULE */
+
+#endif	/* WAVELAN_P_H */
diff -u --recursive --new-file v2.1.14/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.14/linux/drivers/pci/pci.c	Fri Nov 22 18:28:18 1996
+++ linux/drivers/pci/pci.c	Thu Dec 12 16:51:10 1996
@@ -61,6 +61,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),
@@ -117,6 +118,7 @@
 	DEVICE( BUSLOGIC,	BUSLOGIC_MULTIMASTER,    "MultiMaster"),
 	DEVICE( BUSLOGIC,	BUSLOGIC_FLASHPOINT,     "FlashPoint"),
 	DEVICE( OAK,		OAK_OTI107,	"OTI107"),
+	DEVICE( WINBOND2,	WINBOND2_89C940,"NE2000-PCI"),
 	DEVICE( PROMISE,	PROMISE_5300,	"DC5030"),
 	DEVICE( N9,		N9_I128,	"Imagine 128"),
 	DEVICE( N9,		N9_I128_2,	"Imagine 128v2"),
@@ -197,6 +199,7 @@
 	DEVICE( ZEITNET,	ZEITNET_1225,	"1225"),
 	DEVICE( SPECIALIX,	SPECIALIX_XIO,	"XIO/SIO host"),
 	DEVICE( SPECIALIX,	SPECIALIX_RIO,	"RIO host"),
+	DEVICE( COMPEX,		COMPEX_RL2000,	"ReadyLink 2000"),
 	DEVICE( RP,             RP8OCTA,        "RocketPort 8 Oct"),
 	DEVICE( RP,             RP8INTF,        "RocketPort 8 Intf"),
 	DEVICE( RP,             RP16INTF,       "RocketPort 16 Intf"),
@@ -479,6 +482,7 @@
 	      case PCI_VENDOR_ID_SGS:		return "SGS Thomson";
 	      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_PROMISE:	return "Promise Technology";
 	      case PCI_VENDOR_ID_N9:		return "Number Nine";
 	      case PCI_VENDOR_ID_UMC:		return "UMC";
@@ -516,6 +520,7 @@
 	      case PCI_VENDOR_ID_TOSHIBA:	return "Toshiba";
 	      case PCI_VENDOR_ID_ZEITNET:	return "ZeitNet";
 	      case PCI_VENDOR_ID_SPECIALIX:	return "Specialix";
+	      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_SYMPHONY:	return "Symphony";
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.14/linux/drivers/scsi/Config.in	Fri Nov 22 18:28:18 1996
+++ linux/drivers/scsi/Config.in	Thu Dec 12 16:51:10 1996
@@ -30,6 +30,9 @@
 dep_tristate 'EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
 dep_tristate 'EATA ISA/EISA/PCI (DPT and generic EATA/DMA-compliant boards) support' CONFIG_SCSI_EATA $CONFIG_SCSI
+  if [ "$CONFIG_SCSI_EATA" != "n" ]; then
+    bool '  enable tagged command queueing' CONFIG_SCSI_EATA_TAGGED_QUEUE
+  fi
 dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
 dep_tristate 'Generic NCR5380/53c400 SCSI support' CONFIG_SCSI_GENERIC_NCR5380 $CONFIG_SCSI
 if [ "$CONFIG_SCSI_GENERIC_NCR5380" != "n" ]; then
@@ -63,6 +66,9 @@
     bool '  disable scsi parity checking' CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
     bool '  force synchronous negotiation' CONFIG_SCSI_NCR53C8XX_FORCE_SYNC_NEGO
   fi
+fi
+if [ "$CONFIG_MCA" = "y" ]; then
+  bool 'IBMMCA SCSI support' CONFIG_SCSI_IBMMCA
 fi
 dep_tristate 'IOMEGA Parallel Port ZIP drive SCSI support' CONFIG_SCSI_PPA $CONFIG_SCSI
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.14/linux/drivers/scsi/Makefile	Fri Nov 15 23:49:08 1996
+++ linux/drivers/scsi/Makefile	Thu Dec 12 16:51:10 1996
@@ -307,6 +307,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_IBMMCA),y)
+L_OBJS += ibmmca.o
+else
+  ifeq ($(CONFIG_SCSI_IBMMCA),m)
+  M_OBJS += ibmmca.o
+  endif
+endif
+
 ifeq ($(CONFIG_SCSI_T128),y)
 L_OBJS += t128.o
 else
@@ -344,6 +352,14 @@
 else
   ifeq ($(CONFIG_SCSI_NCR53C406A),m)
   M_OBJS += NCR53c406a.o
+  endif
+endif
+
+ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
+L_OBJS += ide-scsi.o
+else
+  ifeq ($(CONFIG_BLK_DEV_IDESCSI),m)
+  M_OBJS += ide-scsi.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/README.AM53C974 linux/drivers/scsi/README.AM53C974
--- v2.1.14/linux/drivers/scsi/README.AM53C974	Fri Apr 12 09:49:40 1996
+++ linux/drivers/scsi/README.AM53C974	Thu Dec 12 16:51:10 1996
@@ -50,38 +50,42 @@
   - Automatic scanning of io_port and irq implemented; no need for BIOS32 
     anymore
   - Improved configuration (now via LILO parameter string)
-  - Cleanup of probing and initialisation code
+  - Cleanup of probing and initialization code
   - Improved sync. negotiation (can be setup individually for every device)
   - Improved/ debugged code for reception of ext. messages
 0.3 -> 0.4:
-  - Improved PCI probing and initialisation code
+  - Improved PCI probing and initialization code
   - Compatibility changes for Linux 1.3.x
 0.4 -> 0.5:
   - Compatibility changes for Linux 1.3.42
 
+
 Bugs & Todo
 -----------
  - Add proc info function
  - Support SCSI-2 tagged queuing
  - Finalize abort code 
 
+
 Features
 --------
-This driver supports asynchronous and synchronous SCSI-I and SCSI-II devices.
-It is capable of transfer rate and synchronous negotiation (see below).
-The driver supports scatter-gather. Transfers are DMA based, but do not 
-(yet) make use of the AM53/79C974 MDL mode.
-Max. transfer rate is 10MHz (whatever this is in real life). The transfer
-rate is negotiated with each device (see dmesg output).
-The AM53/79C974 has a 96-byte DMA FIFO to the PCI bus and a 16-byte SCSI
-FIFO. It provides active negation and glitch suppression functions.
+This driver supports asynchronous and synchronous SCSI-I and SCSI-II
+devices.  It is capable of transfer rate and synchronous negotiation
+(see below).  The driver supports scatter-gather.  Transfers are DMA
+based, but do not (yet) make use of the AM53/79C974 MDL mode.
+Max. transfer rate is 10MHz (whatever this is in real life).  The
+transfer rate is negotiated with each device (see dmesg output).  The
+AM53/79C974 has a 96-byte DMA FIFO to the PCI bus and a 16-byte SCSI
+FIFO.  It provides active negation and glitch suppression functions.
 Burst DMA transfer rate is 132 MBytes/sec.
 
 
 Configuration
 -------------
-The following communication characteristics can be set individually  for every
-SCSI device on the bus:
+
+The following communication characteristics can be set individually
+for every SCSI device on the bus:
+
   - enable/disable sync. negotiation
   - transfer rate
   - asynchronous or synchronous communication
@@ -97,23 +101,29 @@
   - 5.0 MHz transfer rate
   - asynchronous mode
   - zero offset
-The parameters can be modified by passing a string with the following syntax to 
-the kernel: AM53C974=host-scsi-id,target-scsi-id,max-rate,max-offset
-The parameters will be used by the driver as negotiation basis.
-The range of the rate parameter is 3 to 10 MHz.
-The range of the sync. offset parameter is 0 to 15 bytes. A value of 0 denotes 
-asynchronous comm. mode.
-If the target cannot cope with the specified transfer rate, sync. mode or sync.
-offset, the negotiation result will differ from the specified values.
-The negotiation result is printed out at the end of the negotiation process
-(to read it, use the dmesg program or the appropriate syslog).
-The parameter strings (blank separated) can be passed to the kernel at the
-LILO prompt, or as part of the LILO configuration file.
-
-For example, the string "AM53C974=7,2,8,15" would be interpreted as follows:
- "For communication between the controller with SCSI-ID 7 and the device with
- SCSI-ID 2 a transfer rate of 8MHz in synchronous mode with max. 15 bytes offset
- should be negotiated".
+
+The parameters can be modified by passing a string with the following
+syntax to the kernel:
+
+	AM53C974=host-scsi-id,target-scsi-id,max-rate,max-offset
+
+The parameters will be used by the driver as negotiation basis.  The
+range of the rate parameter is 3 to 10 MHz.  The range of the
+sync. offset parameter is 0 to 15 bytes. A value of 0 denotes
+asynchronous comm. mode.  If the target cannot cope with the specified
+transfer rate, sync. mode or sync.  offset, the negotiation result
+will differ from the specified values.  The negotiation result is
+printed out at the end of the negotiation process (to read it, use the
+dmesg program or the appropriate syslog).  The parameter strings
+(blank separated) can be passed to the kernel at the LILO prompt, or
+as part of the LILO configuration file.
+
+For example, the string "AM53C974=7,2,8,15" would be interpreted as
+follows:
+
+For communication between the controller with SCSI-ID 7 and the
+device with SCSI-ID 2 a transfer rate of 8MHz in synchronous mode with
+max. 15 bytes offset should be negotiated.
 
 As an example, here my LILO configuration file:
   boot = /dev/sda
@@ -135,21 +145,22 @@
     label = setup
 
 The same parameters at the LILO prompt:
+
   LILO boot: linux AM53C974=7,0,10,0 AM53C974=7,1,10,0 AM53C974=7,2,10,15 AM53C974=7,4,10,0 AM53C974=7,5,10,0
 
-You can override parameters specified in the LILO configuration file by 
-parameters specified on the LILO command line.
+You can override parameters specified in the LILO configuration file
+by parameters specified on the LILO command line.
 
 
 BIOS usage
 ----------
 Version 0.4 of the driver will use the BIOS, if available. Otherwise
-it will try its internal PCI scan and access routines.
-The driver assumes that the controller's SCSI-ID (usually 7) has been
-correctly loaded by the BIOS into the controller's register during
-system boot. If the driver detects that the controller's SCSI ID is not 
-'7' it will print out a warning. If this happens to you please correct
-setting of the controller's SCSI-ID. If it is wrong, then edit the 
+it will try its internal PCI scan and access routines.  The driver
+assumes that the controller's SCSI-ID (usually 7) has been correctly
+loaded by the BIOS into the controller's register during system
+boot. If the driver detects that the controller's SCSI ID is not '7'
+it will print out a warning. If this happens to you please correct
+setting of the controller's SCSI-ID. If it is wrong, then edit the
 AM53C974_SCSI_ID definition in file AM53C974.h accordingly.
 
 
@@ -168,38 +179,41 @@
    Use of this device with AM53C974 driver version 0.2 caused the kernel to
    hang during Linux boot. If you encounter the problem, don't enable sync.
    negotiation with the CD-ROM, i.e. simply don't specify comm. parameters 
-   for this device on the LILO commandline or configuration file. 
+   for this device on the LILO command line or configuration file. 
    The driver will thus use its default for the CD-ROM, which is 5MHz 
-   transfer rate asynch and no sync. negotiation.
+   transfer rate async and no sync. negotiation.
  - Some disks cause problems.
 
 
 What to do if there is a SCSI problem possibly related to the driver
 --------------------------------------------------------------------
-Read Klaus Liedl's WWW page (http://www-c724.uibk.ac.at/XL/).
-In case this does not help:
-Send me a complete description of the problem, including your SCSI 
-configuration plus as much debugging information as possible. 
-Don't wait until I ask you for this information. To enable the 
-generation of debugging output, remove the comments from the following 
-definitions in the AM53C974.h file:
+
+Read Klaus Liedl's WWW page (http://www-c724.uibk.ac.at/XL/).  In case
+this does not help: Send me a complete description of the problem,
+including your SCSI configuration plus as much debugging information
+as possible.  Don't wait until I ask you for this information. To
+enable the generation of debugging output, remove the comments from
+the following definitions in the AM53C974.h file:
+
     AM53C974_DEBUG
     AM53C974_DEBUG_MSG
     AM53C974_DEBUG_KEYWAIT
     AM53C974_DEBUG_INFO
     AM53C974_DEBUG_INTR
-With these definitions enabled, the driver will enter single-step mode during 
-Linux boot. Use the spacebar for stepping.
-Take note of at least the last 10 printout sections (marked by dashes) before 
-the crash/hangup or whatever happens and send me all of this information via 
-email. If the system can boot, use the syslogd daemon to record the debugging 
-output. Maybe you can use the ramdisk for this purpose too (if necessary, kindly
-ask K. Liedl (Klaus.Liedl@uibk.ac.at) for support, he knows how to do it -- 
-I never tried). Stay in email contact with me. Be aware that the following 
-weeks/months could be the worst of your life.
-Note: If single-stepping takes up too much time, you can try to let the driver 
-catch the problem by pressing the 'r' key. The driver will automatically enter
-single-step mode if it has detected something weird.
+
+With these definitions enabled, the driver will enter single-step mode
+during Linux boot. Use the spacebar for stepping.  Take note of at
+least the last 10 printout sections (marked by dashes) before the
+crash/hangup or whatever happens and send me all of this information
+via email. If the system can boot, use the syslogd daemon to record
+the debugging output. Maybe you can use the ramdisk for this purpose
+too (if necessary, kindly ask K. Liedl (Klaus.Liedl@uibk.ac.at) for
+support, he knows how to do it -- I never tried). Stay in email
+contact with me. Be aware that the following weeks/months could be the
+worst of your life.  Note: If single-stepping takes up too much time,
+you can try to let the driver catch the problem by pressing the 'r'
+key. The driver will automatically enter single-step mode if it has
+detected something weird.
 
 
 Author's Contact Address
@@ -217,6 +231,7 @@
 - Volunteer wanted for further maintenance of this driver software. I
   don't have the time anymore to do serious support as some of you will know.
 
+
 Literature
 ----------
  - AMD AM53C974 PC-SCSI Technical Manual, publication #18624B
@@ -229,4 +244,3 @@
 ---------
  - Drew Eckhardt, Robin Cutshaw, K. Liedl, Robert J. Pappas, A. Grenier, 
    Mark Stockton, David C. Niemi, Ben Craft, and many others who have helped
-
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/README.dtc3x80 linux/drivers/scsi/README.dtc3x80
--- v2.1.14/linux/drivers/scsi/README.dtc3x80	Fri Apr 12 09:49:40 1996
+++ linux/drivers/scsi/README.dtc3x80	Thu Dec 12 16:51:10 1996
@@ -6,15 +6,17 @@
 Data Technology Corp---a division of Qume.
 
 The 3280 has a standard floppy interface.
+
 The 3180 does not.  Otherwise, they are identical.
+
 The DTC3x80 does not support DMA but it does have Pseudo-DMA which is
 supported by the driver.
+
 It's DTC406 scsi chip is supposedly compatible with the NCR 53C400.
-It is memory mapped, uses an IRQ, but no dma or io-port.  There is 
+It is memory mapped, uses an IRQ, but no dma or io-port.  There is
 internal DMA, between SCSI bus and an on-chip 128-byte buffer.  Double
-buffering is done automagically by the chip.
-Data is transferred between the on-chip buffer and CPU/RAM via
-memory moves.
+buffering is done automagically by the chip.  Data is transferred
+between the on-chip buffer and CPU/RAM via memory moves.
 
 The driver detects the possible memory addresses (jumper selectable):
 	CC00, DC00, C800, and D800
@@ -24,21 +26,20 @@
 Information can be obtained from /proc/scsi/dtc3c80/N.
 
 Note on interrupts:
+
 The documentation says that it can be set to interrupt whenever the
-on-chip buffer needs CPU attention.  I couldn't get this to work.
-So the driver polls for data-ready in the pseudo-DMA transfer routine.
+on-chip buffer needs CPU attention.  I couldn't get this to work.  So
+the driver polls for data-ready in the pseudo-DMA transfer routine.
 The interrupt support routines in the NCR3280.c core modules handle
-scsi disconnect/reconnect, and this (mostly) works.
-However.....
-I have tested it with 4 totally different hard drives (both SCSI-1 
-and SCSI-2), and one CDROM drive.
-Interrupts works great for all but one specific hard drive.  For this one,
-the driver will eventually hang in the transfer state.
-I have tested with: "dd bs=4k count=2k of=/dev/null if=/dev/sdb".  It
-reads ok for a while, then hangs.  After beating my head against this for a
-couple of weeks, getting nowhere, I give up.
-So.....This driver does NOT use interrupts, even if you have the card 
-jumpered to an IRQ.  Probably nobody will ever care.
-Nor will I when the $380 2.5GB IDE drives hit the market in early 1996!
+scsi disconnect/reconnect, and this (mostly) works.  However.....  I
+have tested it with 4 totally different hard drives (both SCSI-1 and
+SCSI-2), and one CDROM drive.  Interrupts works great for all but one
+specific hard drive.  For this one, the driver will eventually hang in
+the transfer state.  I have tested with: "dd bs=4k count=2k
+of=/dev/null if=/dev/sdb".  It reads ok for a while, then hangs.
+After beating my head against this for a couple of weeks, getting
+nowhere, I give up.  So.....This driver does NOT use interrupts, even
+if you have the card jumpered to an IRQ.  Probably nobody will ever
+care.
 
 
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/README.ncr53c8xx linux/drivers/scsi/README.ncr53c8xx
--- v2.1.14/linux/drivers/scsi/README.ncr53c8xx	Tue Nov 12 15:56:11 1996
+++ linux/drivers/scsi/README.ncr53c8xx	Thu Dec 12 16:51:11 1996
@@ -43,19 +43,21 @@
           Wolfgang Stanglmeier        <wolf@cologne.de>
           Stefan Esser                <se@mi.Uni-Koeln.de>
 
-You can find technical information about the NCR 8xx family in the PCI-HOWTO 
-written by Michael Will and in the SCSI-HOWTO written by Drew Eckhardt.
+You can find technical information about the NCR 8xx family in the
+PCI-HOWTO written by Michael Will and in the SCSI-HOWTO written by
+Drew Eckhardt.
 
 Information about new chips is available at SYMBIOS web server:
-          http://www.symbios.com
 
-This short documentation only describes the features of the NCR53C8XX driver, 
-configuration parameters and control commands available through the proc SCSI
-file system read / write operations.
+          http://www.symbios.com/
+
+This short documentation only describes the features of the NCR53C8XX
+driver, configuration parameters and control commands available
+through the proc SCSI file system read / write operations.
 
 This driver has been tested OK with linux/i386 and Linux/Alpha.
 
-I am not a native speaker of English and there are probably lots of 
+I am not a native speaker of English and there are probably lots of
 mistakes in this README file. Any help will be welcome.
 
 
@@ -69,8 +71,8 @@
 	SCSI parity checking
 	Master parity checking
 
-"Wide negotiation" is supported for chips that allow it.
-The following table shows some characteristics of NCR 8xx family chips:
+"Wide negotiation" is supported for chips that allow it.  The
+following table shows some characteristics of NCR 8xx family chips:
 
        On board                        Supported by    Tested with
 Chip   SDMS BIOS   Wide   Ultra SCSI   the driver      the driver
@@ -100,51 +102,52 @@
 
 4. Memory mapped I/O versus normal I/O
 
-Memory mapped I/O has less latency than normal I/O.
-Since linux-1.3.x, memory mapped I/O is used rather than normal I/O.
-Memory mapped I/O seems to work fine on most hardware configurations, but some 
-poorly designed motherboards may break this feature.
+Memory mapped I/O has less latency than normal I/O.  Since
+linux-1.3.x, memory mapped I/O is used rather than normal I/O.  Memory
+mapped I/O seems to work fine on most hardware configurations, but
+some poorly designed motherboards may break this feature.
 
-The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the 
+The configuration option CONFIG_SCSI_NCR53C8XX_IOMAPPED forces the
 driver to use normal I/O in all cases.
 
 
 5. Tagged command queueing
 
-Some SCSI devices do not properly support tagged command queuing.
-A safe configuration is to not enable tagged command queuing support at 
-boot-up, and to enable support of it with the control command "settags" 
-described further in this text.
-
-Once you are sure that all your devices properly support tagged command queuing,
-you can enable it by default with the CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE 
-configuration option.
-
-The maximum number of simultaneous tagged commands queued to a device is 
-currently set to 4 by default. It is defined in the file ncr53c8xx.h by 
-SCSI_NCR_MAX_TAGS. This value is suitable for most scsi disks.
-With large scsi disks (> 2GB, cache > 512KB average seek time < 10 ms),  
-8 tagged commands may give better performance.
-
-In some special conditions, some scsi disk firmwares may return a QUEUE FULL 
-status for a scsi command. This behaviour is managed by the driver by the 
-following heuristic:
+Some SCSI devices do not properly support tagged command queuing.  A
+safe configuration is to not enable tagged command queuing support at
+boot-up, and to enable support of it with the control command
+"settags" described further in this text.
+
+Once you are sure that all your devices properly support tagged
+command queuing, you can enable it by default with the
+CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE configuration option.
+
+The maximum number of simultaneous tagged commands queued to a device
+is currently set to 4 by default. It is defined in the file
+ncr53c8xx.h by SCSI_NCR_MAX_TAGS. This value is suitable for most SCSI
+disks.  With large SCSI disks (> 2GB, cache > 512KB average seek time
+< 10 ms), 8 tagged commands may give better performance.
+
+In some special conditions, some SCSI disk firmwares may return a
+QUEUE FULL status for a SCSI command. This behaviour is managed by the
+driver by the following heuristic:
 
 - Each time a QUEUE FULL status is returned, tagged command queueing is 
   temporarily disabled. 
 
-- Every 100 successfully completed scsi commands, if allowed by the current 
-  limit, the maximum number of queueable commands is incremented and tagged 
-  command queueing is reenabled.
+- Every 100 successfully completed SCSI commands, if allowed by the
+  current limit, the maximum number of queueable commands is
+  incremented and tagged command queueing is reenabled.
 
 
 6. Parity checking
 
-The driver supports SCSI parity checking and PCI bus master parity checking.
-These features must be enabled in order to ensure safe data transfers.
-However, some flawed devices or mother boards will have problems with 
-parity. You can disable parity by choosing first "CONFIG_EXPERIMENTAL".
-Then, "make config" will allow to set the following configuration options:
+The driver supports SCSI parity checking and PCI bus master parity
+checking.  These features must be enabled in order to ensure safe data
+transfers.  However, some flawed devices or mother boards will have
+problems with parity. You can disable parity by choosing first
+"CONFIG_EXPERIMENTAL".  Then, "make config" will allow to set the
+following configuration options:
 
    CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK  (disable SCSI parity checking)
    CONFIG_SCSI_NCR53C8XX_DISABLE_MPARITY_CHECK (disable master parity checking)
@@ -154,16 +157,19 @@
 
 Profiling information is available through the proc SCSI file system.
 The device associated with a host has the following pathname:
+
           /proc/scsi/ncr53c8xx/N     (N=0,1,2 ....)
 
 Generally, only 1 board is used on hardware configuration, and that device is:
           /proc/scsi/ncr53c8xx/0
 
-However, if the driver has been made as module, the number of the hosts is 
-incremented each time the driver is loaded.
+However, if the driver has been made as module, the number of the
+hosts is incremented each time the driver is loaded.
 
 In order to display profiling information, just enter:
+
          cat /proc/scsi/ncr53c8xx/0
+
 and you will get something like the following text:
 
 -------------------------------------------------------
@@ -184,7 +190,7 @@
   ms_post      = 1320
 -------------------------------------------------------
 
-General information is easy to understand. The device ID and the 
+General information is easy to understand. The device ID and the
 revision ID identify the SCSI chip as follows:
 
 Chip    Device id     Revision Id
@@ -198,13 +204,15 @@
 875       0xf
 
 The profiling information is updated upon completion of SCSI commands.
-A data structure is allocated and zeroed when the host adapter is 
-attached. So, if the driver is a module, the profile counters are cleared each 
-time the driver is loaded.
-The "clearprof" command allows you to clear these counters at any time.
+A data structure is allocated and zeroed when the host adapter is
+attached. So, if the driver is a module, the profile counters are
+cleared each time the driver is loaded.  The "clearprof" command
+allows you to clear these counters at any time.
 
 The following counters are available:
-("num" prefix means "number of", "ms" means milli-seconds)
+
+("num" prefix means "number of",
+"ms" means milli-seconds)
 
 num_trans
 	Number of completed commands
@@ -247,36 +255,38 @@
 	(time from SCSI status get to command completion call)
 	Example above: 1.32 seconds spent for post processing
 
-Due to the 1/100 second tick of the system clock, "ms_post" time may be 
-wrong.
+Due to the 1/100 second tick of the system clock, "ms_post" time may
+be wrong.
 
-In the example above, we got 18038 interrupts "on the fly" and only 1673 script 
-breaks probably due to disconnections inside a segment of the scatter list. 
-This is an excellent result due to the fact that the driver tries to use small 
-data segments (512) for the scatter list. The CPU load of this rescatter process 
-is acceptable. Unlike other SCSI processors, NCR53C8XX controllers do not need  
-large data chunks in order to get better performance, and it seems that it 
-is just the opposite.
-The scatter/gather algorithm of the middle SCSI driver is not optimal for 
-NCR SCSI processors and should be tunable according to host type.
-
-You can tune the "wished" segment size for the scatterlist by changing the 
-following "define" in the file ncr53c8xx.h.
-Use only power of 2 greater than 512 (1024, 2048 or 4096).
+In the example above, we got 18038 interrupts "on the fly" and only
+1673 script breaks probably due to disconnections inside a segment of
+the scatter list.  This is an excellent result due to the fact that
+the driver tries to use small data segments (512) for the scatter
+list. The CPU load of this rescatter process is acceptable. Unlike
+other SCSI processors, NCR53C8XX controllers do not need large data
+chunks in order to get better performance, and it seems that it is
+just the opposite.  The scatter/gather algorithm of the middle SCSI
+driver is not optimal for NCR SCSI processors and should be tunable
+according to host type.
+
+You can tune the "wished" segment size for the scatterlist by changing
+the following "define" in the file ncr53c8xx.h.  Use only power of 2
+greater than 512 (1024, 2048 or 4096).
 
 SCSI_NCR_SEGMENT_SIZE		(default: 512)
 
 
 8. Control commands
 
-Control commands can be sent to the driver with write operations to the 
-proc SCSI file system. The generic command syntax is the following:
+Control commands can be sent to the driver with write operations to
+the proc SCSI file system. The generic command syntax is the
+following:
 
       echo "<verb> <parameters>" >/proc/scsi/ncr53c8xx/0
       (assumes controller number is 0)
 
-Using "all" for "<target>" parameter with the commands below will apply to 
-all targets of the scsi chain (except the controller).
+Using "all" for "<target>" parameter with the commands below will
+apply to all targets of the SCSI chain (except the controller).
 
 Available commands:
 
@@ -339,8 +349,8 @@
 
     clearprof
 
-    The profile counters are automatically cleared when the amount of data 
-    transfered reaches 1000 GB in order to avoid overflow.
+    The profile counters are automatically cleared when the amount of
+    data transfered reaches 1000 GB in order to avoid overflow.
     The "clearprof" command allows you to clear these counters at any time.
 
 
@@ -351,21 +361,22 @@
     target:    target number
 
     For the moment, only one flag is available:
+
         no_sync:   not allow target to disconnect.
 
     Do not specify any flag in order to reset the flag. For example:
     - setflag 4
       will reset no_sync flag for target 4, so will allow it disconnections.
     - setflag all
-      will allow disconnection for all devices on the scsi bus.
+      will allow disconnection for all devices on the SCSI bus.
 
 9. Configuration parameters
 
-If the firmware of all your devices is perfect enough, all the features 
-supported by the driver can be enabled at start-up.
-However, if only one has a flaw for some SCSI feature, you can disable the 
-support by the driver of this feature at linux start-up and enable this 
-feature after boot-up only for devices that support it safely.
+If the firmware of all your devices is perfect enough, all the
+features supported by the driver can be enabled at start-up.  However,
+if only one has a flaw for some SCSI feature, you can disable the
+support by the driver of this feature at linux start-up and enable
+this feature after boot-up only for devices that support it safely.
 
 CONFIG_SCSI_NCR53C8XX_IOMAPPED       (default answer: n)
     Answer "y" if you suspect your mother board to not allow memory mapped I/O.
@@ -398,9 +409,9 @@
 
 10. Some constants and flags of the ncr53c8xx.h header files
 
-Some of these are defined from the configuration parameters.
-To change other "defines", you must edit the header file.
-Do that only if you know what you are doing.
+Some of these are defined from the configuration parameters.  To
+change other "defines", you must edit the header file.  Do that only
+if you know what you are doing.
 
 SCSI_NCR_TRUST_BIOS_SETTING	(default: not defined)
 	If defined, the driver will preserve features bits from  
@@ -510,11 +521,11 @@
 
 12. Installation procedure
 
-This install script has been tested with linux-1.2.13 and linux-2.0.22 to 
-2.0.25. It will probably work with linux 2.0.X (X>25).
+This install script has been tested with linux-1.2.13 and linux-2.0.22
+to 2.0.25. It will probably work with linux 2.0.X (X>25).
 
-This procedure copies the new driver files to the kernel tree and applies 
-a patch to some files of the kernel tree.
+This procedure copies the new driver files to the kernel tree and
+applies a patch to some files of the kernel tree.
 
 	If your linux directory is at the standard location
 	"/usr/src/linux", just enter:
@@ -534,14 +545,15 @@
 
 13. Control commands under linux-1.2.13
 
-Profiling data and control commands using the proc SCSI file system are not 
-available for linux-1.2.13.
-The only control command available is "scsitag" which allows you to enable
-tagged command queuing support after linux boot-up.
+Profiling data and control commands using the proc SCSI file system
+are not available for linux-1.2.13.  The only control command
+available is "scsitag" which allows you to enable tagged command
+queuing support after linux boot-up.
 
 Tagged command queueing is disabled by default at system startup.
 
 You can enable tagged queue per device with the following command:
+
 	scsitag device_name (ex: scsitag /dev/sda)
 
 Use "cc -o scsitag scsitag.c" to create the "scsitag" executable.
@@ -551,63 +563,67 @@
 
 14.1 Tagged commands with Iomega Jaz device
 
-I have not tried this device, however it has been reported to me the following:
-This device is capable of Tagged command queuing. However while spinning up,
-it rejects Tagged commands. This behaviour is conforms to 6.8.2 of SCSI-2 
-specifications. The current behaviour of the driver in that situation is not 
-satisfying. So do not enable Tagged command queuing for devices that are able 
-to spin down.
-The other problem that may appear is timeouts. The only way to avoid timeouts 
-seems to edit linux/drivers/scsi/sd.c and to increase the current timeout 
-values.
+I have not tried this device, however it has been reported to me the
+following: This device is capable of Tagged command queuing. However
+while spinning up, it rejects Tagged commands. This behaviour is
+conforms to 6.8.2 of SCSI-2 specifications. The current behaviour of
+the driver in that situation is not satisfying. So do not enable
+Tagged command queuing for devices that are able to spin down.  The
+other problem that may appear is timeouts. The only way to avoid
+timeouts seems to edit linux/drivers/scsi/sd.c and to increase the
+current timeout values.
+
+
+15. SCSI problem troubleshooting
+
+Most SCSI problems are due to a non conformant SCSI bus or to buggy
+devices.  If infortunately you have SCSI problems, you can check the
+following things:
 
-
-15. SCSI problems solving
-
-Most scsi problems are due to a non conformant scsi bus or to buggy devices.
-If infortunately you have scsi problems, you can check the following things:
-
-- scsi bus cables
-- terminations at both end of the scsi chain
+- SCSI bus cables
+- terminations at both end of the SCSI chain
 - linux syslog messages (some of them may help you)
 
-If you donnot find the source of problems, you can configure the driver with 
-no feature enabled.
+If you donnot find the source of problems, you can configure the
+driver with no features enabled.
 
 - only asynchronous data transfers
 - tagged commands disabled
 - disconnections not allowed
 
-Now, if your scsi bus is ok, your system have every chance to work with this 
-safe configuration but performances will not be optimal.
+Now, if your SCSI bus is ok, your system have every chance to work
+with this safe configuration but performances will not be optimal.
 
-If it still fails, then you can send your problem description to appropriate 
-mailing lists or news-groups.
-Send me a copy in order to be sure I will receive it.
-Obviously, a bug in the driver code is possible.
+If it still fails, then you can send your problem description to
+appropriate mailing lists or news-groups.  Send me a copy in order to
+be sure I will receive it.  Obviously, a bug in the driver code is
+possible.
 
      My email address: Gerard Roudier <groudier@club-internet.fr>
 
-Allowing disconnections is important if you use severall devices on your 
-scsi bus but often causes problems with buggy devices.
-Synchronous data transfers increases throughput of fast devices as hard disks.
-Good scsi hard disks with a large cache gain advantage of tagged commands 
-queuing.
+Allowing disconnections is important if you use several devices on
+your SCSI bus but often causes problems with buggy devices.
+Synchronous data transfers increases throughput of fast devices like
+hard disks.  Good SCSI hard disks with a large cache gain advantage of
+tagged commands queuing.
 
-Try to enable one feature at a time with control commands. For example:
+Try to enable one feature at a time with control commands.  For example:
 
 - echo "setsync all 25" >/proc/scsi/ncr53c8xx/0
   Will enable fast synchronous data transfer negotiation for all targets.
 
 - echo "setflag 3" >/proc/scsi/ncr53c8xx/0
   Will reset flags (no_sync) for target 3, and so will allow it to disconnect 
-  the scsi bus.
+  the SCSI Bus.
 
 - echo "settags 3 4" >/proc/scsi/ncr53c8xx/0
   Will enable tagged command queuing for target 3 if that device supports it.
 
 Once you have found the device and the feature that cause problems, just 
-donnot enable the feature for that device only.
+disable that feature for that device.
 
 ===============================================================================
 End of NCR53C8XX driver README file
+
+
+
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/README.qlogicfas linux/drivers/scsi/README.qlogicfas
--- v2.1.14/linux/drivers/scsi/README.qlogicfas	Sat May 18 11:58:33 1996
+++ linux/drivers/scsi/README.qlogicfas	Thu Dec 12 16:54:14 1996
@@ -1,6 +1,4 @@
 
-RANDOM NOTES ON THE QLOGICFAS SCSI DRIVER
-
 This driver supports the Qlogic FASXXX family of chips.  This driver
 only works with the ISA, VLB, and PCMCIA versions of the Qlogic
 FastSCSI!  cards as well as any other card based on the FASXX chip
@@ -9,39 +7,45 @@
 This driver does NOT support the PCI version.  Support for these PCI
 Qlogic boards:
 
-	IQ-PCI
-	IQ-PCI-10
-	IQ-PCI-D
+	* IQ-PCI
+	* IQ-PCI-10
+	* IQ-PCI-D
+
+is provided by the qlogicisp.c driver.  Check README.qlogicisp for
+details.
 
-is provided by the qlogicisp.c driver.   Check README.qlogicisp for details.
+Nor does it support the PCI-Basic, which is supported by the
+'am53c974' driver.
 
 PCMCIA SUPPORT
 
-This currently only works if the card is enabled first from DOS.  This means
-you will have to load your socket and card services, and QL41DOS.SYS and
-QL40ENBL.SYS.  These are a minimum, but loading the rest of the modules
-won't interfere with the operation.  The next thing to do is load the kernel
-without resetting the hardware, which can be a simple ctrl-alt-delete with
-a boot floppy, or by using loadlin with the kernel image accessible from
-DOS.  If you are using the Linux PCMCIA driver, you will have to adjust
-it or otherwise stop it from configuring the card.
+This currently only works if the card is enabled first from DOS.  This
+means you will have to load your socket and card services, and
+QL41DOS.SYS and QL40ENBL.SYS.  These are a minimum, but loading the
+rest of the modules won't interfere with the operation.  The next
+thing to do is load the kernel without resetting the hardware, which
+can be a simple ctrl-alt-delete with a boot floppy, or by using
+loadlin with the kernel image accessible from DOS.  If you are using
+the Linux PCMCIA driver, you will have to adjust it or otherwise stop
+it from configuring the card.
 
-I am working with the PCMCIA group to make it more flexible, but that may
-take a while.
+I am working with the PCMCIA group to make it more flexible, but that
+may take a while.
 
 ALL CARDS
 
-The top of the qlogic.c file has a number of defines that controls 
+The top of the qlogic.c file has a number of defines that controls
 configuration.  As shipped, it provides a balance between speed and
 function.  If there are any problems, try setting SLOW_CABLE to 1, and
-then try changing USE_IRQ and TURBO_PDMA to zero.  If you are familiar 
+then try changing USE_IRQ and TURBO_PDMA to zero.  If you are familiar
 with SCSI, there are other settings which can tune the bus.
 
-It may be a good idea to enable RESET_AT_START, especially if the devices
-may not have been just powered up, or if you are restarting after a crash,
-since they may be busy trying to complete the last command or something.
-It comes up faster if this is set to zero, and if you have reliable
-hardware and connections it may be more useful to not reset things.
+It may be a good idea to enable RESET_AT_START, especially if the
+devices may not have been just powered up, or if you are restarting
+after a crash, since they may be busy trying to complete the last
+command or something.  It comes up faster if this is set to zero, and
+if you have reliable hardware and connections it may be more useful to
+not reset things.
 
 SOME TROUBLESHOOTING TIPS
 
@@ -53,21 +57,23 @@
 
 IMPORTANT
 
-The best way to test if your cables, termination, etc. are good is to copy
-a very big file (e.g. a doublespace container file, or a very large executable
-or archive).  It should be at least 5 megabytes, but you can do multiple tests
-on smaller files.  Then do a COMP to verify that the file copied properly.
-(Turn off all caching when doing these tests, otherwise you will test your
-RAM and not the files).  Then do 10 COMPs, comparing the same file on the
-SCSI hard drive, i.e. "COMP realbig.doc realbig.doc".  Then do it after the
-computer gets warm.
-
-I noticed my system which seems to work 100% would fail this test if the
-computer was left on for a few hours.  It was worse with longer cables, and
-more devices on the SCSI bus.  What seems to happen is that it gets a false
-ACK causing an extra byte to be inserted into the stream (and this is not
-detected).  This can be caused by bad termination (the ACK can be reflected),
-or by noise when the chips work less well because of the heat, or when cables
-get too long for the speed.
+The best way to test if your cables, termination, etc. are good is to
+copy a very big file (e.g. a doublespace container file, or a very
+large executable or archive).  It should be at least 5 megabytes, but
+you can do multiple tests on smaller files.  Then do a COMP to verify
+that the file copied properly.  (Turn off all caching when doing these
+tests, otherwise you will test your RAM and not the files).  Then do
+10 COMPs, comparing the same file on the SCSI hard drive, i.e. "COMP
+realbig.doc realbig.doc".  Then do it after the computer gets warm.
+
+I noticed my system which seems to work 100% would fail this test if
+the computer was left on for a few hours.  It was worse with longer
+cables, and more devices on the SCSI bus.  What seems to happen is
+that it gets a false ACK causing an extra byte to be inserted into the
+stream (and this is not detected).  This can be caused by bad
+termination (the ACK can be reflected), or by noise when the chips
+work less well because of the heat, or when cables get too long for
+the speed.
 
-Remember, if it doesn't work under DOS, it probably won't work under Linux.
+Remember, if it doesn't work under DOS, it probably won't work under
+Linux.
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/README.qlogicisp linux/drivers/scsi/README.qlogicisp
--- v2.1.14/linux/drivers/scsi/README.qlogicisp	Mon May 20 08:38:41 1996
+++ linux/drivers/scsi/README.qlogicisp	Thu Dec 12 16:54:19 1996
@@ -1,22 +1,26 @@
-Notes for the QLogic ISP1020 PCI SCSI Driver revision 0.6.
+Notes for the QLogic ISP1020 PCI SCSI Driver:
 
-This software should be considered ***BETA***.
+This driver works well in practice, but does not support disconnect/
+reconnect, which makes using it with tape drives impractical.
 
-Be sure to include PCI BIOS support when rebuilding the kernel.
+It should work for most host adaptors with the ISP1020 chip.  The
+QLogic Corporation produces several PCI SCSI adapters which should
+work:
 
-The QLogic Corporation produces several PCI SCSI adapters:
+	* IQ-PCI
+	* IQ-PCI-10
+	* IQ-PCI-D
 
-	PCI-basic
-	IQ-PCI
-	IQ-PCI-10
-	IQ-PCI-D
+This driver may work with boards containing the ISP1020A or ISP1040A
+chips, but that has not been tested.
 
-This driver should work for all these adapters, except for the PCI-basic which
-does not use the ISP1020 chip.  If you have the QLogic PCI-basic there is a
-an am53c974 driver that supports your adapter.
+This driver will NOT work with:
 
-Much thanks to QLogic's tech support for providing the latest ISP1020 firmware,
-and for taking the time to review my code.
+	* ISA or VL Bus Qlogic cards (they use the 'qlogicfas' driver)
+	* PCI-basic (it uses the 'am53c974' driver)
+
+Much thanks to QLogic's tech support for providing the latest ISP1020
+firmware, and for taking the time to review my code.
 
 Erik Moe
 ehm@cris.com
@@ -24,3 +28,7 @@
 Revised:
 Michael A. Griffith
 grif@cs.ucr.edu
+
+
+
+
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/eata.c linux/drivers/scsi/eata.c
--- v2.1.14/linux/drivers/scsi/eata.c	Thu Dec 12 17:02:44 1996
+++ linux/drivers/scsi/eata.c	Wed Dec 11 16:26:38 1996
@@ -1,6 +1,9 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *       3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
+ *          Added support for tagged commands and queue depth adjustment.
+ *
  *      22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
  *          When CONFIG_PCI is defined, BIOS32 is used to include in the
  *          list of i/o ports to be probed all the PCI SCSI controllers.
@@ -218,6 +221,7 @@
 #define MAX_LARGE_SGLIST 252
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
+#define MAX_TAGGED_CMD_PER_LUN 16
 
 #define SKIP 1
 #define FALSE 0
@@ -427,6 +431,63 @@
 static int do_trace = FALSE;
 static int setup_done = FALSE;
 
+#if defined (CONFIG_SCSI_EATA_TAGGED_QUEUE)
+static int tagged_commands = TRUE;
+#else
+static int tagged_commands = FALSE;
+#endif
+
+static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
+   Scsi_Device *dev;
+   int j, ntag = 0, nuntag = 0, tqd, utqd; 
+   unsigned long flags;
+
+   save_flags(flags);
+   cli();
+
+   j = ((struct hostdata *) host->hostdata)->board_number;
+
+   for(dev = devlist; dev; dev = dev->next) {
+
+      if (dev->host != host) continue;
+
+      if (dev->tagged_supported) ntag++;
+      else                       nuntag++;
+      }
+
+   utqd = MAX_CMD_PER_LUN;
+
+   tqd = (host->can_queue - utqd * nuntag) / (ntag + 1);
+
+   if (tqd > MAX_TAGGED_CMD_PER_LUN) tqd = MAX_TAGGED_CMD_PER_LUN;
+
+   if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
+
+   for(dev = devlist; dev; dev = dev->next) {
+      char *tag_suffix = "";
+
+      if (dev->host != host) continue;
+
+      if (dev->tagged_supported) dev->queue_depth = tqd;
+      else                       dev->queue_depth = utqd;
+
+      if (tagged_commands && dev->tagged_supported) {
+         dev->tagged_queue = 1;
+         dev->current_tag = 1;
+         }
+
+      if (dev->tagged_supported && dev->tagged_queue) tag_suffix = ", tagged";
+      else if (dev->tagged_supported) tag_suffix = ", untagged";
+
+      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s.\n",
+             BN(j), host->host_no, dev->channel, dev->id, dev->lun,
+             dev->queue_depth, tag_suffix);
+      }
+
+   restore_flags(flags);
+   return;
+}
+
 static inline int wait_on_busy(unsigned int iobase) {
    unsigned int loop = MAXLOOP;
 
@@ -472,7 +533,7 @@
    unsigned char irq, dma_channel, subversion, i;
    unsigned char protocol_rev;
    struct eata_info info;
-   char *bus_type;
+   char *bus_type, dma_name[16];
 
    /* Allowed DMA channels for ISA (0 indicates reserved) */
    unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
@@ -590,6 +651,7 @@
    sh[j]->this_id = (ushort) info.host_addr[3];
    sh[j]->can_queue = (ushort) ntohs(info.queue_size);
    sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+   sh[j]->select_queue_depths = select_queue_depths;
 
    /* Register the I/O space that we use */
    request_region(sh[j]->io_port, sh[j]->n_io_port, driver_name);
@@ -650,6 +712,9 @@
    else if (subversion == ESA) bus_type = "EISA";
    else bus_type = "ISA";
 
+   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "NO DMA");
+   else                       sprintf(dma_name, "DMA %u", dma_channel);
+
    for (i = 0; i < sh[j]->can_queue; i++)
       if (! ((&HD(j)->cp[i])->sglist = kmalloc(
             sh[j]->sg_tablesize * sizeof(struct sg_list), 
@@ -659,10 +724,10 @@
          return FALSE;
          }
       
-   printk("%s: rev. 2.0%c, %s, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
-	  "Mbox %d, CmdLun %d.\n", BN(j), HD(j)->protocol_rev, bus_type,
-	   sh[j]->io_port, sh[j]->irq, sh[j]->dma_channel,
-	   sh[j]->sg_tablesize, sh[j]->can_queue, sh[j]->cmd_per_lun);
+   printk("%s: rev. 2.0%c, %s, PORT 0x%03x, IRQ %u, %s, SG %d, "\
+	  "Mbox %d, TC %d.\n", BN(j), HD(j)->protocol_rev, bus_type,
+          sh[j]->io_port, sh[j]->irq, dma_name, sh[j]->sg_tablesize,
+          sh[j]->can_queue, tagged_commands);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -874,6 +939,17 @@
    cpp->SCpnt = SCpnt;
    cpp->sense_addr = V2DEV(SCpnt->sense_buffer); 
    cpp->sense_len = sizeof SCpnt->sense_buffer;
+   
+   if (SCpnt->device->tagged_queue) {
+
+      if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] || 
+                            HD(j)->target_to[SCpnt->target][SCpnt->channel])
+         cpp->mess[0] = ORDERED_QUEUE_TAG;
+      else
+         cpp->mess[0] = SIMPLE_QUEUE_TAG;
+
+      cpp->mess[1] = SCpnt->device->current_tag++;
+      }
 
    if (SCpnt->use_sg) {
       cpp->sg = TRUE;
@@ -1262,8 +1338,8 @@
 #else
 	    if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
 		(spp->adapter_status != ASOK && 
-		 spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
-		do_trace)
+		spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+		do_trace || msg_byte(spp->target_status))
 #endif
 	       printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
 		      " target %d.%d:%d, pid %ld, count %d.\n",
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/eata.h linux/drivers/scsi/eata.h
--- v2.1.14/linux/drivers/scsi/eata.h	Thu Dec 12 17:02:44 1996
+++ linux/drivers/scsi/eata.h	Wed Dec 11 16:26:38 1996
@@ -12,7 +12,7 @@
 int eata2x_abort(Scsi_Cmnd *);
 int eata2x_reset(Scsi_Cmnd *, unsigned int);
 
-#define EATA_VERSION "2.30.00"
+#define EATA_VERSION "2.40.00"
 
 
 #define EATA {                                                 \
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.1.14/linux/drivers/scsi/hosts.c	Fri Nov  1 17:13:18 1996
+++ linux/drivers/scsi/hosts.c	Thu Dec 12 16:54:19 1996
@@ -145,6 +145,10 @@
 #include "wd7000.h"
 #endif
 
+#ifdef CONFIG_SCSI_IBMMCA
+#include "ibmmca.h"
+#endif
+
 #ifdef CONFIG_SCSI_EATA
 #include "eata.h"
 #endif
@@ -165,6 +169,10 @@
 #include "esp.h"
 #endif
 
+#ifdef CONFIG_BLK_DEV_IDESCSI
+#include "ide-scsi.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -291,6 +299,9 @@
 #ifdef CONFIG_SCSI_7000FASST
     WD7000,
 #endif
+#ifdef CONFIG_SCSI_IBMMCA
+    IBMMCA,
+#endif
 #ifdef CONFIG_SCSI_EATA
     EATA,
 #endif
@@ -302,6 +313,9 @@
 #endif
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
+#endif
+#ifdef CONFIG_BLK_DEV_IDESCSI
+    IDESCSI,
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/ibmmca.c linux/drivers/scsi/ibmmca.c
--- v2.1.14/linux/drivers/scsi/ibmmca.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/ibmmca.c	Thu Dec 12 16:54:19 1996
@@ -0,0 +1,949 @@
+/*
+ * Low Level Driver for the IBM Microchannel SCSI Subsystem
+ *
+ * Copyright (c) 1995 Strom Systems, Inc. under the terms of the GNU 
+ * General Public License. Written by Martin Kolinek, December 1995.
+ */
+
+/* Update history:
+   Jan 15 1996:  First public release.
+
+   Jan 23 1996:  Scrapped code which reassigned scsi devices to logical
+   device numbers. Instead, the existing assignment (created
+   when the machine is powered-up or rebooted) is used. 
+   A side effect is that the upper layer of Linux SCSI 
+   device driver gets bogus scsi ids (this is benign), 
+   and also the hard disks are ordered under Linux the 
+   same way as they are under dos (i.e., C: disk is sda, 
+   D: disk is sdb, etc.).
+
+   I think that the CD-ROM is now detected only if a CD is 
+   inside CD_ROM while Linux boots. This can be fixed later,
+   once the driver works on all types of PS/2's.
+
+   Feb 7 1996:   Modified biosparam function. Fixed the CD-ROM detection. 
+   For now, devices other than harddisk and CD_ROM are 
+   ignored. Temporarily modified abort() function 
+   to behave like reset(). 
+
+   Mar 31 1996:  The integrated scsi subsystem is correctly found
+   in PS/2 models 56,57, but not in model 76. Therefore
+   the ibmmca_scsi_setup() function has been added today.
+   This function allows the user to force detection of
+   scsi subsystem. The kernel option has format
+   ibmmcascsi=n
+   where n is the scsi_id (pun) of the subsystem. Most
+   likely, n is 7. 
+
+   Aug 21 1996:  Modified the code which maps ldns to (pun,0).  It was
+   insufficient for those of us with CD-ROM changers.
+   - Chris Beauregard
+ */
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/mca.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "ibmmca.h"
+
+/*--------------------------------------------------------------------*/
+
+/*
+   Driver Description
+
+   (A) Subsystem Detection
+   This is done in find_adapter() function and is easy, since
+   the information about MCA integrated subsystems and plug-in 
+   adapters is readily available in structure *mca_info.
+
+   In case you have more than one SCSI subsystem, only one can be used,
+   and its I/O port addresses should be standard 0x3540-7. To use more
+   than one adapter, the sharing of interrupt 14 would have to be 
+   supported in Linux (it is not at present, but could be, since MCA 
+   interrupts are level-triggered).
+
+   (B) Physical Units, Logical Units, and Logical Devices
+   There can be up to 56 devices on SCSI bus (besides the adapter):
+   there are up to 7 "physical units" (each identified by physical unit 
+   number or pun, also called the scsi id, this is the number you select
+   with hardware jumpers), and each physical unit can have up to 8 
+   "logical units" (each identified by logical unit number, or lun, 
+   between 0 and 7). 
+
+   Typically the adapter has pun=7, so puns of other physical units
+   are between 0 and 6. Almost all physical units have only one   
+   logical unit, with lun=0. A CD-ROM jukebox would be an example of 
+   a physical unit with more than one logical unit.
+
+   The embedded microprocessor of IBM SCSI subsystem hides the complex
+   two-dimensional (pun,lun) organization from the operating system.
+   When the machine is powered-up (or rebooted, I am not sure), the 
+   embedded microprocessor checks, on it own, all 56 possible (pun,lun) 
+   combinations, and first 15 devices found are assigned into a 
+   one-dimensional array of so-called "logical devices", identified by 
+   "logical device numbers" or ldn. The last ldn=15 is reserved for 
+   the subsystem itself. 
+
+   One consequence of information hiding is that the real (pun,lun)    
+   numbers are also hidden. Therefore this driver takes the following
+   approach: It checks the ldn's (0 to 6) to find out which ldn's
+   have devices assigned. This is done by function check_devices() and
+   device_exists(). The interrupt handler has a special paragraph of code
+   (see local_checking_phase_flag) to assist in the checking. Assume, for
+   example, that three logical devices were found assigned at ldn 0, 1, 2.
+   These are presented to the upper layer of Linux SCSI driver
+   as devices with bogus (pun, lun) equal to (0,0), (1,0), (2,0). 
+   On the other hand, if the upper layer issues a command to device
+   say (4,0), this driver returns DID_NO_CONNECT error.
+
+   That last paragraph is no longer correct, but is left for
+   historical purposes.  It limited the number of devices to 7, far
+   fewer than the 15 that it could use.  Now it just maps
+   ldn -> (ldn/8,ldn%8).  We end up with a real mishmash of puns
+   and luns, but it all seems to work. - Chris Beaurgard
+
+   (C) Regular Processing 
+   Only three functions get involved: ibmmca_queuecommand(), issue_cmd(),
+   and interrupt_handler().
+
+   The upper layer issues a scsi command by calling function 
+   ibmmca_queuecommand(). This function fills a "subsystem control block"
+   (scb) and calls a local function issue_cmd(), which writes a scb 
+   command into subsystem I/O ports. Once the scb command is carried out, 
+   interrupt_handler() is invoked.
+
+   (D) Abort, Reset.
+   These are implemented with busy waiting for interrupt to arrive.
+   The abort does not worked well for me, so I instead call the 
+   ibmmca_reset() from the ibmmca_abort() function.
+
+   (E) Disk Geometry
+   The ibmmca_biosparams() function should return same disk geometry 
+   as bios. This is needed for fdisk, etc. The returned geometry is 
+   certainly correct for disk smaller than 1 gigabyte, but I am not 
+   100% sure that it is correct for larger disks.
+
+   (F) Kernel Boot Option 
+   The function ibmmca_scsi_setup() is called if option ibmmcascsi=n 
+   is passed to the kernel. See file linux/init/main.c for details.
+ */
+
+/*--------------------------------------------------------------------*/
+
+/* Here are the values and structures specific for the subsystem. 
+ * The source of information is "Update for the PS/2 Hardware 
+ * Interface Technical Reference, Common Interfaces", September 1991, 
+ * part number 04G3281, available in the U.S. for $21.75 at 
+ * 1-800-IBM-PCTB, elsewhere call your local friendly IBM 
+ * representative.
+ * In addition to SCSI subsystem, this update contains fairly detailed 
+ * (at hardware register level) sections on diskette  controller,
+ * keyboard controller, serial port controller, VGA, and XGA.
+ */
+
+/*addresses of hardware registers on the subsystem */
+#define IM_CMD_REG   0x3540	/*Command Interface, (4 bytes long) */
+#define IM_ATTN_REG  0x3544	/*Attention (1 byte) */
+#define IM_CTR_REG   0x3545	/*Basic Control (1 byte) */
+#define IM_INTR_REG  0x3546	/*Interrupt Status (1 byte, read only) */
+#define IM_STAT_REG  0x3547	/*Basic Status (1 byte, read only) */
+
+/*requests going into the upper nibble of the Attention register */
+/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
+#define IM_IMM_CMD   0x10	/*immediate command */
+#define IM_SCB       0x30	/*Subsystem Control Block command */
+#define IM_LONG_SCB  0x40	/*long Subsystem Control Block command */
+#define IM_EOI       0xe0	/*end-of-interrupt request */
+
+/*values for bits 7,1,0 of Basic Control reg. (bits 6-2 reserved) */
+#define IM_HW_RESET     0x80	/*hardware reset */
+#define IM_ENABLE_DMA   0x02	/*enable subsystem's busmaster DMA */
+#define IM_ENABLE_INTR  0x01	/*enable interrupts to the system */
+
+/*to interpret the upper nibble of Interrupt Status register */
+/*note: the lower nibble specifies the device(0-14), or subsystem(15) */
+#define IM_SCB_CMD_COMPLETED               0x10
+#define IM_SCB_CMD_COMPLETED_WITH_RETRIES  0x50
+#define IM_ADAPTER_HW_FAILURE              0x70
+#define IM_IMMEDIATE_CMD_COMPLETED         0xa0
+#define IM_CMD_COMPLETED_WITH_FAILURE      0xc0
+#define IM_CMD_ERROR                       0xe0
+#define IM_SOFTWARE_SEQUENCING_ERROR       0xf0
+
+/*to interpret bits 3-0 of Basic Status register (bits 7-4 reserved) */
+#define IM_CMD_REG_FULL   0x08
+#define IM_CMD_REG_EMPTY  0x04
+#define IM_INTR_REQUEST   0x02
+#define IM_BUSY           0x01
+
+/*immediate commands (word written into low 2 bytes of command reg) */
+#define IM_RESET_IMM_CMD        0x0400
+#define IM_FORMAT_PREP_IMM_CMD  0x0417
+#define IM_FEATURE_CTR_IMM_CMD  0x040c
+#define IM_DMA_PACING_IMM_CMD   0x040d
+#define IM_ASSIGN_IMM_CMD       0x040e
+#define IM_ABORT_IMM_CMD        0x040f
+
+/*SCB (Subsystem Control Block) structure */
+struct im_scb
+  {
+    unsigned short command;	/*command word (read, etc.) */
+    unsigned short enable;	/*enable word, modifies cmd */
+    union
+      {
+	unsigned long log_blk_adr;	/*block address on SCSI device */
+	unsigned char scsi_cmd_length;	/*6,10,12, for other scsi cmd */
+      }
+    u1;
+    unsigned long sys_buf_adr;	/*physical system memory adr */
+    unsigned long sys_buf_length;	/*size of sys mem buffer */
+    unsigned long tsb_adr;	/*Termination Status Block adr */
+    unsigned long scb_chain_adr;	/*optional SCB chain address */
+    union
+      {
+	struct
+	  {
+	    unsigned short count;	/*block count, on SCSI device */
+	    unsigned short length;	/*block length, on SCSI device */
+	  }
+	blk;
+	unsigned char scsi_command[12];		/*other scsi command */
+      }
+    u2;
+  };
+
+/*structure scatter-gather element (for list of system memory areas) */
+struct im_sge
+  {
+    void *address;
+    unsigned long byte_length;
+  };
+
+/*values for SCB command word */
+#define IM_NO_SYNCHRONOUS      0x0040	/*flag for any command */
+#define IM_NO_DISCONNECT       0x0080	/*flag for any command */
+#define IM_READ_DATA_CMD       0x1c01
+#define IM_WRITE_DATA_CMD      0x1c02
+#define IM_READ_VERIFY_CMD     0x1c03
+#define IM_WRITE_VERIFY_CMD    0x1c04
+#define IM_REQUEST_SENSE_CMD   0x1c08
+#define IM_READ_CAPACITY_CMD   0x1c09
+#define IM_DEVICE_INQUIRY_CMD  0x1c0b
+#define IM_OTHER_SCSI_CMD_CMD  0x241f
+
+/*values to set bits in the enable word of SCB */
+#define IM_READ_CONTROL              0x8000
+#define IM_REPORT_TSB_ONLY_ON_ERROR  0x4000
+#define IM_RETRY_ENABLE              0x2000
+#define IM_POINTER_TO_LIST           0x1000
+#define IM_SUPRESS_EXCEPTION_SHORT   0x0400
+#define IM_CHAIN_ON_NO_ERROR         0x0001
+
+/*TSB (Termination Status Block) structure */
+struct im_tsb
+  {
+    unsigned short end_status;
+    unsigned short reserved1;
+    unsigned long residual_byte_count;
+    unsigned long sg_list_element_adr;
+    unsigned short status_length;
+    unsigned char dev_status;
+    unsigned char cmd_status;
+    unsigned char dev_error;
+    unsigned char cmd_error;
+    unsigned short reserved2;
+    unsigned short reserved3;
+    unsigned short low_of_last_scb_adr;
+    unsigned short high_of_last_scb_adr;
+  };
+
+/*subsystem uses interrupt request level 14 */
+#define IM_IRQ  14
+
+/*PS2 disk led is turned on/off by bits 6,7 of system control port */
+#define PS2_SYS_CTR  0x92
+#define PS2_DISK_LED_ON()   outb(inb(PS2_SYS_CTR) | 0xc0, PS2_SYS_CTR)
+#define PS2_DISK_LED_OFF()  outb(inb(PS2_SYS_CTR) & 0x3f, PS2_SYS_CTR)
+
+/*--------------------------------------------------------------------*/
+
+/*list of supported subsystems */
+struct subsys_list_struct
+  {
+    unsigned short mca_id;
+    char *description;
+  };
+struct subsys_list_struct subsys_list[] =
+{
+  {0x8efc, "IBM Fast SCSI-2 Adapter"},
+  {0x8efd, "IBM 7568 Industrial Computer SCSI Adapter w/cache"},
+  {0x8ef8, "IBM Expansion Unit SCSI Controller"},
+  {0x8eff, "IBM SCSI Adapter w/Cache"},
+  {0x8efe, "IBM SCSI Adapter"},
+};
+
+/*for /proc filesystem */
+struct proc_dir_entry proc_scsi_ibmmca =
+{
+  PROC_SCSI_IBMMCA, 6, "ibmmca",
+  S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*max number of logical devices (can be up to 15) */
+#define MAX_LOG_DEV  15
+
+/*local data for a logical device */
+struct logical_device
+  {
+    struct im_scb scb;
+    struct im_tsb tsb;
+    struct im_sge sge[16];
+    Scsi_Cmnd *cmd;
+    int is_disk;
+    int block_length;
+  };
+static struct logical_device ld[MAX_LOG_DEV];
+
+/*if this is nonzero, ibmmcascsi option has been passed to the kernel */
+static int setup_called = 0;
+
+/*scsi id (physical unit number) of subsystem (set during detect) */
+static int subsystem_pun;
+
+/*array to convert (pun, lun) into logical device number */
+static unsigned char get_ldn[8][8];
+
+/*counter of concurrent disk read/writes, to turn on/off disk led */
+static int disk_rw_in_progress;
+
+/*used only when checking logical devices */
+static int local_checking_phase_flag;
+static int got_interrupt;
+static int stat_result;
+
+/*reset status and its values (used only when doing reset) */
+static int reset_status;
+#define IM_RESET_NOT_IN_PROGRESS   0
+#define IM_RESET_IN_PROGRESS       1
+#define IM_RESET_FINISHED_OK       2
+#define IM_RESET_FINISHED_FAIL     3
+
+/*local functions */
+static void interrupt_handler (int irq, void *dev_id,
+			       struct pt_regs *regs);
+static void issue_cmd (unsigned long cmd_reg, unsigned char attn_reg);
+static void internal_done (Scsi_Cmnd * cmd);
+static int find_subsystem (void);
+static void check_devices (void);
+static int device_exists (int ldn, int *is_disk, int *block_length);
+
+/*--------------------------------------------------------------------*/
+
+static void 
+interrupt_handler (int irq, void *dev_id,
+		   struct pt_regs *regs)
+{
+  /*get command result and logical device */
+  unsigned int intr_reg = inb (IM_INTR_REG);
+  unsigned int cmd_result = intr_reg & 0xf0;
+  unsigned int ldn = intr_reg & 0x0f;
+
+  if (!inb (IM_STAT_REG) & IM_INTR_REQUEST)
+    {
+      printk ("ibmmca SCSI: spurious/shared interrupt?\n");
+      return;
+    }
+
+  /*must wait for attention reg not busy, then send EOI to subsystem */
+  while (1)
+    {
+      cli ();
+      if (!(inb (IM_STAT_REG) & IM_BUSY))
+	break;
+      sti ();
+    }
+  outb (IM_EOI | ldn, IM_ATTN_REG);
+  sti ();
+
+  /*these should never happen (hw fails, or a local programming bug) */
+  if (cmd_result == IM_ADAPTER_HW_FAILURE)
+    panic ("IBM MCA SCSI: subsystem hardware failure.\n");
+  if (cmd_result == IM_CMD_ERROR)
+    panic ("IBM MCA SCSI: command error.\n");
+  if (cmd_result == IM_SOFTWARE_SEQUENCING_ERROR)
+    panic ("IBM MCA SCSI: software sequencing error.\n");
+
+  /*only for local checking phase */
+  if (local_checking_phase_flag)
+    {
+      stat_result = cmd_result;
+      got_interrupt = 1;
+      return;
+    }
+
+  /*handling of commands coming from upper level of scsi driver */
+  else
+    {
+      Scsi_Cmnd *cmd;
+
+      /*verify ldn, and may handle rare reset immediate command */
+      if (ldn >= MAX_LOG_DEV)
+	{
+	  if (ldn == 0xf && reset_status == IM_RESET_IN_PROGRESS)
+	    {
+	      if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
+		{
+		  reset_status = IM_RESET_FINISHED_FAIL;
+		}
+	      else
+		{
+		  /*reset disk led counter, turn of disk led */
+		  disk_rw_in_progress = 0;
+		  PS2_DISK_LED_OFF ();
+		  reset_status = IM_RESET_FINISHED_OK;
+		}
+	      return;
+	    }
+	  else
+	    panic ("IBM MCA SCSI: invalid logical device number.\n");
+	}
+
+      /*if no command structure, just return, else clear cmd */
+      cmd = ld[ldn].cmd;
+      if (!cmd)
+	return;
+      ld[ldn].cmd = 0;
+
+      /* printk("cmd=%02x ireg=%02x ds=%02x cs=%02x de=%02x ce=%02x\n", 
+         cmd->cmnd[0], intr_reg, 
+         ld[ldn].tsb.dev_status, ld[ldn].tsb.cmd_status, 
+         ld[ldn].tsb.dev_error, ld[ldn].tsb.cmd_error); */
+
+      /*if this is end of disk read/write, may turn off PS/2 disk led */
+      if (ld[ldn].is_disk)
+	{
+	  switch (cmd->cmnd[0])
+	    {
+	    case READ_6:
+	    case WRITE_6:
+	    case READ_10:
+	    case WRITE_10:
+	      if (--disk_rw_in_progress == 0)
+		PS2_DISK_LED_OFF ();
+	    }
+	}
+
+      /*write device status into cmd->result, and call done function */
+      if (cmd_result == IM_CMD_COMPLETED_WITH_FAILURE)
+	cmd->result = ld[ldn].tsb.dev_status & 0x1e;
+      else
+	cmd->result = 0;
+      (cmd->scsi_done) (cmd);
+    }
+}
+
+/*--------------------------------------------------------------------*/
+
+static void 
+issue_cmd (unsigned long cmd_reg, unsigned char attn_reg)
+{
+  /*must wait for attention reg not busy */
+  while (1)
+    {
+      cli ();
+      if (!(inb (IM_STAT_REG) & IM_BUSY))
+	break;
+      sti ();
+    }
+
+  /*write registers and enable system interrupts */
+  outl (cmd_reg, IM_CMD_REG);
+  outb (attn_reg, IM_ATTN_REG);
+  sti ();
+}
+
+/*--------------------------------------------------------------------*/
+
+static void 
+internal_done (Scsi_Cmnd * cmd)
+{
+  cmd->SCp.Status++;
+}
+
+/*--------------------------------------------------------------------*/
+
+static int
+ibmmca_getinfo (char *buf, int slot, void *dev)
+{
+  int len = 0;
+
+  len += sprintf (buf + len, "Subsystem PUN: %d\n", subsystem_pun);
+  len += sprintf (buf + len, "Detected at boot: %s\n",
+		  setup_called ? "No" : "Yes");
+  return len;
+}
+
+static int 
+find_subsystem (void)
+{
+  int j, list_size, slot;
+  unsigned char pos2, pos3;
+
+  /*if this is not MCA machine, return "nothing found" */
+  if (!MCA_bus)
+    return 0;
+
+  /*if ibmmcascsi setup option was passed to kernel, return "found" */
+  if (setup_called)
+    {
+      printk ("IBM MCA SCSI: forced detection, scsi id=%d.\n",
+	      subsystem_pun);
+      return 1;
+    }
+
+  /*first look for the SCSI integrated on the motherboard */
+  pos2 = mca_read_stored_pos (MCA_INTEGSCSI, 2);
+  if ((pos2 & 1) == 0)
+    {
+      pos3 = mca_read_stored_pos (MCA_INTEGSCSI, 3);
+      subsystem_pun = (pos3 & 0xe0) >> 5;
+      printk ("IBM MCA SCSI: integrated SCSI found, scsi id=%d.\n",
+	      subsystem_pun);
+      mca_set_adapter_name (MCA_INTEGSCSI, "PS/2 Integrated SCSI");
+      mca_set_adapter_procfn (MCA_INTEGSCSI, (MCA_ProcFn) ibmmca_getinfo,
+			      NULL);
+      return 1;
+    }
+
+  list_size = sizeof (subsys_list) / sizeof (struct subsys_list_struct);
+  for (j = 0; j < list_size; j += 1)
+    {
+      if ((slot = mca_find_adapter (subsys_list[j].mca_id, 0)) != MCA_NOTFOUND)
+	{
+	  pos3 = mca_read_stored_pos (slot, 3);
+	  subsystem_pun = (pos3 & 0xe0) >> 5;
+	  printk ("IBM MCA SCSI: %s found in slot %d, scsi id=%d.\n",
+		  subsys_list[j].description, slot + 1, subsystem_pun);
+
+	  mca_set_adapter_name (slot, subsys_list[j].description);
+	  mca_set_adapter_procfn (slot, (MCA_ProcFn) ibmmca_getinfo,
+				  NULL);
+
+	  return 1;
+	}
+    }
+
+  /*return "nothing found" */
+  return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+static void 
+check_devices (void)
+{
+  int is_disk, block_length;
+  int ldn;
+  int num_ldn = 0;
+
+  /*check ldn's from 0 to MAX_LOG_DEV to find which devices exist */
+  for (ldn = 0; ldn < MAX_LOG_DEV; ldn++)
+    {
+      if (device_exists (ldn, &is_disk, &block_length))
+	{
+	  printk ("IBM MCA SCSI: logical device found at ldn=%d.\n", ldn);
+	  ld[ldn].is_disk = is_disk;
+	  ld[ldn].block_length = block_length;
+	  get_ldn[num_ldn / 8][num_ldn % 8] = ldn;
+	  num_ldn++;
+	}
+    }
+
+  return;
+}
+
+/*--------------------------------------------------------------------*/
+
+static int 
+device_exists (int ldn, int *is_disk, int *block_length)
+{
+  struct im_scb scb;
+  struct im_tsb tsb;
+  unsigned char buf[256];
+  int retries;
+
+  for (retries = 0; retries < 3; retries++)
+    {
+      /*fill scb with inquiry command */
+      scb.command = IM_DEVICE_INQUIRY_CMD;
+      scb.enable = IM_READ_CONTROL | IM_SUPRESS_EXCEPTION_SHORT;
+      /* I think this virt_to_bus is needed.. ??? AC */
+      scb.sys_buf_adr = virt_to_bus((unsigned long) buf);
+      scb.sys_buf_length = 255;
+      scb.tsb_adr = virt_to_bus((unsigned long) &tsb);
+
+      /*issue scb to passed ldn, and busy wait for interrupt */
+      got_interrupt = 0;
+      issue_cmd (virt_to_bus((unsigned long)) &scb, IM_SCB | ldn);
+      while (!got_interrupt)
+	barrier ();
+
+      /*if command succesful, break */
+      if (stat_result == IM_SCB_CMD_COMPLETED)
+	break;
+    }
+
+  /*if all three retries failed, return "no device at this ldn" */
+  if (retries >= 3)
+    return 0;
+
+  /*if device is CD_ROM, assume block size 2048 and return */
+  if (buf[0] == TYPE_ROM)
+    {
+      *is_disk = 0;
+      *block_length = 2048;
+      return 1;
+    }
+
+  /*if device is disk, use "read capacity" to find its block size */
+  if (buf[0] == TYPE_DISK)
+    {
+      *is_disk = 1;
+
+      for (retries = 0; retries < 3; retries++)
+	{
+	  /*fill scb with read capacity command */
+	  scb.command = IM_READ_CAPACITY_CMD;
+	  scb.enable = IM_READ_CONTROL;
+	  scb.sys_buf_adr = virt_to_bus((unsigned long) buf);
+	  scb.sys_buf_length = 8;
+	  scb.tsb_adr = virt_to_bus((unsigned long) &tsb);
+
+	  /*issue scb to passed ldn, and busy wait for interrupt */
+	  got_interrupt = 0;
+	  issue_cmd (virt_to_bus((unsigned long) &scb), IM_SCB | ldn);
+	  while (!got_interrupt)
+	    barrier ();
+
+	  /*if got capacity, get block length and return one device found */
+	  if (stat_result == IM_SCB_CMD_COMPLETED)
+	    {
+	      *block_length = buf[7] + (buf[6] << 8) + (buf[5] << 16) + (buf[4] << 24);
+	      return 1;
+	    }
+	}
+
+      /*if all three retries failed, return "no device at this ldn" */
+      if (retries >= 3)
+	return 0;
+    }
+
+  /*for now, ignore tape and other devices - return 0 */
+  return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+void 
+ibmmca_scsi_setup (char *str, int *ints)
+{
+  /*verify that one value (between 0 and 7) was specified */
+  if (setup_called++ || ints[0] != 1 || ints[1] < 0 || ints[1] >= 8)
+    {
+      printk ("IBM MCA SCSI: usage: ibmmcascsi=<ADAPTER_SCSI_ID>\n");
+      return;
+    }
+  subsystem_pun = ints[1];
+}
+
+/*--------------------------------------------------------------------*/
+
+int 
+ibmmca_detect (Scsi_Host_Template * template)
+{
+  /*initialize local data */
+  memset (ld, 0, sizeof ld);
+  memset (get_ldn, 0xff, sizeof get_ldn);
+  disk_rw_in_progress = 0;
+  reset_status = IM_RESET_NOT_IN_PROGRESS;
+
+  /*search for the subsystem, return 0 if not found */
+  if (!find_subsystem ())
+    return 0;
+
+  /*get interrupt request level */
+  if (request_irq (IM_IRQ, interrupt_handler, SA_SHIRQ, "ibmmca", 0))
+    {
+      printk ("IBM MCA SCSI: Unable to get IRQ %d.\n", IM_IRQ);
+      return 0;
+    }
+
+  /*check which logical devices exist */
+  local_checking_phase_flag = 1;
+  check_devices ();
+  local_checking_phase_flag = 0;
+
+  /*if got here, one ibm mca subsystem has been detected */
+  return 1;
+}
+
+/*--------------------------------------------------------------------*/
+
+int 
+ibmmca_command (Scsi_Cmnd * cmd)
+{
+  ibmmca_queuecommand (cmd, internal_done);
+  cmd->SCp.Status = 0;
+  while (!cmd->SCp.Status)
+    barrier ();
+  return cmd->result;
+}
+
+/*--------------------------------------------------------------------*/
+
+int 
+ibmmca_queuecommand (Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+  unsigned int ldn;
+  unsigned int scsi_cmd;
+  struct im_scb *scb;
+
+  /*if (target,lun) unassigned, return error */
+  ldn = get_ldn[cmd->target][cmd->lun];
+  if (ldn >= MAX_LOG_DEV)
+    {
+      cmd->result = DID_NO_CONNECT << 16;
+      done (cmd);
+      return 0;
+    }
+
+  /*verify there is no command already in progress for this log dev */
+  if (ld[ldn].cmd)
+    panic ("IBM MCA SCSI: cmd already in progress for this ldn.\n");
+
+  /*save done in cmd, and save cmd for the interrupt handler */
+  cmd->scsi_done = done;
+  ld[ldn].cmd = cmd;
+
+  /*fill scb information independent of the scsi command */
+  scb = &(ld[ldn].scb);
+  scb->enable = IM_REPORT_TSB_ONLY_ON_ERROR;
+  scb->tsb_adr = virt_to_bus((unsigned long) &(ld[ldn].tsb));
+  if (cmd->use_sg)
+    {
+      int i = cmd->use_sg;
+      struct scatterlist *sl = (struct scatterlist *) cmd->request_buffer;
+      if (i > 16)
+	panic ("IBM MCA SCSI: scatter-gather list too long.\n");
+      while (--i >= 0)
+	{
+	  ld[ldn].sge[i].address = virt_to_bus(sl[i].address);
+	  ld[ldn].sge[i].byte_length = sl[i].length;
+	}
+      scb->enable |= IM_POINTER_TO_LIST;
+      scb->sys_buf_adr = virt_to_bus((unsigned long) &(ld[ldn].sge[0]));
+      scb->sys_buf_length = cmd->use_sg * sizeof (struct im_sge);
+    }
+  else
+    {
+      scb->sys_buf_adr = virt_to_bus((unsigned long) cmd->request_buffer);
+      scb->sys_buf_length = cmd->request_bufflen;
+    }
+
+  /*fill scb information dependent on scsi command */
+  scsi_cmd = cmd->cmnd[0];
+  /* printk("issue scsi cmd=%02x to ldn=%d\n", scsi_cmd, ldn); */
+  switch (scsi_cmd)
+    {
+    case READ_6:
+    case WRITE_6:
+    case READ_10:
+    case WRITE_10:
+      if (scsi_cmd == READ_6 || scsi_cmd == READ_10)
+	{
+	  scb->command = IM_READ_DATA_CMD;
+	  scb->enable |= IM_READ_CONTROL;
+	}
+      else
+	{
+	  scb->command = IM_WRITE_DATA_CMD;
+	}
+      if (scsi_cmd == READ_6 || scsi_cmd == WRITE_6)
+	{
+	  scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[3]) << 0) |
+	    (((unsigned) cmd->cmnd[2]) << 8) |
+	    ((((unsigned) cmd->cmnd[1]) & 0x1f) << 16);
+	  scb->u2.blk.count = (unsigned) cmd->cmnd[4];
+	}
+      else
+	{
+	  scb->u1.log_blk_adr = (((unsigned) cmd->cmnd[5]) << 0) |
+	    (((unsigned) cmd->cmnd[4]) << 8) |
+	    (((unsigned) cmd->cmnd[3]) << 16) |
+	    (((unsigned) cmd->cmnd[2]) << 24);
+	  scb->u2.blk.count = (((unsigned) cmd->cmnd[8]) << 0) |
+	    (((unsigned) cmd->cmnd[7]) << 8);
+	}
+      scb->u2.blk.length = ld[ldn].block_length;
+      if (ld[ldn].is_disk)
+	{
+	  if (++disk_rw_in_progress == 1)
+	    PS2_DISK_LED_ON ();
+	}
+      break;
+
+    case INQUIRY:
+      scb->command = IM_DEVICE_INQUIRY_CMD;
+      scb->enable |= IM_READ_CONTROL |
+	IM_SUPRESS_EXCEPTION_SHORT;
+      break;
+
+    case READ_CAPACITY:
+      scb->command = IM_READ_CAPACITY_CMD;
+      scb->enable |= IM_READ_CONTROL;
+      /*the length of system memory buffer must be exactly 8 bytes */
+      if (scb->sys_buf_length >= 8)
+	scb->sys_buf_length = 8;
+      break;
+
+    case REQUEST_SENSE:
+      scb->command = IM_REQUEST_SENSE_CMD;
+      scb->enable |= IM_READ_CONTROL;
+      break;
+
+    default:
+      scb->command = IM_OTHER_SCSI_CMD_CMD;
+      scb->enable |= IM_READ_CONTROL |
+	IM_SUPRESS_EXCEPTION_SHORT;
+      scb->u1.scsi_cmd_length = cmd->cmd_len;
+      memcpy (scb->u2.scsi_command, cmd->cmnd, cmd->cmd_len);
+      break;
+    }
+
+  /*issue scb command, and return */
+  issue_cmd (virt_to_bus((unsigned long) scb), IM_SCB | ldn);
+  return 0;
+}
+
+/*--------------------------------------------------------------------*/
+
+int 
+ibmmca_abort (Scsi_Cmnd * cmd)
+{
+/*do a reset instead, since abort does not work well for me at present */
+  return ibmmca_reset (cmd);
+
+#if 0
+  unsigned int ldn;
+  void (*saved_done) (Scsi_Cmnd *);
+
+  /*get logical device number, and disable system interrupts */
+  printk ("IBM MCA SCSI: sending abort to device id=%d lun=%d.\n",
+	  cmd->target, cmd->lun);
+  ldn = get_ldn[cmd->target][cmd->lun];
+  cli ();
+
+  /*if cmd for this ldn has already finished, no need to abort */
+  if (!ld[ldn].cmd)
+    {
+      sti ();
+      return SCSI_ABORT_NOT_RUNNING;
+    }
+
+  /* Clear ld.cmd, save done function, install internal done, 
+   * send abort immediate command (this enables sys. interrupts), 
+   * and wait until the interrupt arrives. 
+   */
+  ld[ldn].cmd = 0;
+  saved_done = cmd->scsi_done;
+  cmd->scsi_done = internal_done;
+  cmd->SCp.Status = 0;
+  issue_cmd (IM_ABORT_IMM_CMD, IM_IMM_CMD | ldn);
+  while (!cmd->SCp.Status)
+    barrier ();
+
+  /*if abort went well, call saved done, then return success or error */
+  if (cmd->result == 0)
+    {
+      cmd->result |= DID_ABORT << 16;
+      saved_done (cmd);
+      return SCSI_ABORT_SUCCESS;
+    }
+  else
+    return SCSI_ABORT_ERROR;
+#endif
+}
+
+/*--------------------------------------------------------------------*/
+
+int 
+ibmmca_reset (Scsi_Cmnd * cmd)
+{
+  /*issue reset immediate command to subsystem, and wait for interrupt */
+  printk ("IBM MCA SCSI: resetting all devices.\n");
+  cli ();
+  reset_status = IM_RESET_IN_PROGRESS;
+  issue_cmd (IM_RESET_IMM_CMD, IM_IMM_CMD | 0xf);
+  while (reset_status == IM_RESET_IN_PROGRESS)
+    barrier ();
+
+  /*if reset failed, just return error */
+  if (reset_status == IM_RESET_FINISHED_FAIL)
+    return SCSI_RESET_ERROR;
+
+  /*so reset finished ok - call outstanding done's, and return success */
+  printk ("IBM MCA SCSI: reset finished well.\n");
+  {
+    int i;
+    for (i = 0; i < MAX_LOG_DEV; i++)
+      {
+	Scsi_Cmnd *cmd = ld[i].cmd;
+	if (cmd && cmd->scsi_done)
+	  {
+	    ld[i].cmd = 0;
+	    cmd->result = DID_RESET;
+	    (cmd->scsi_done) (cmd);
+	  }
+      }
+  }
+  return SCSI_RESET_SUCCESS;
+}
+
+/*--------------------------------------------------------------------*/
+
+int 
+ibmmca_biosparam (Disk * disk, kdev_t dev, int *info)
+{
+  info[0] = 64;
+  info[1] = 32;
+  info[2] = disk->capacity / (info[0] * info[1]);
+  if (info[2] >= 1024)
+    {
+      info[0] = 128;
+      info[1] = 63;
+      info[2] = disk->capacity / (info[0] * info[1]);
+      if (info[2] >= 1024)
+	{
+	  info[0] = 255;
+	  info[1] = 63;
+	  info[2] = disk->capacity / (info[0] * info[1]);
+	  if (info[2] >= 1024)
+	    info[2] = 1023;
+	}
+    }
+  return 0;
+}
+
+/*--------------------------------------------------------------------*/
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/ibmmca.h linux/drivers/scsi/ibmmca.h
--- v2.1.14/linux/drivers/scsi/ibmmca.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/ibmmca.h	Thu Dec 12 16:54:19 1996
@@ -0,0 +1,45 @@
+
+#ifndef _IBMMCA_H
+#define _IBMMCA_H
+
+/* 
+ * Low Level Driver for the IBM Microchannel SCSI Subsystem
+ */
+
+/*services provided to the higher level of Linux SCSI driver */
+int ibmmca_detect (Scsi_Host_Template *);
+int ibmmca_command (Scsi_Cmnd *);
+int ibmmca_queuecommand (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
+int ibmmca_abort (Scsi_Cmnd *);
+int ibmmca_reset (Scsi_Cmnd *);
+int ibmmca_biosparam (Disk *, kdev_t, int *);
+
+/*structure for /proc filesystem */
+extern struct proc_dir_entry proc_scsi_ibmmca;
+
+/*initialization for Scsi_host_template type */
+#define IBMMCA {                                      \
+          NULL,                 /*next*/              \
+          NULL,                 /*usage_count*/       \
+          &proc_scsi_ibmmca,    /*proc_dir*/          \
+          NULL,                 /*proc info fn*/      \
+          "IBMMCA",             /*name*/              \
+          ibmmca_detect,        /*detect fn*/         \
+          NULL,                 /*release fn*/        \
+          NULL,                 /*info fn*/           \
+          ibmmca_command,       /*command fn*/        \
+          ibmmca_queuecommand,  /*queuecommand fn*/   \
+          ibmmca_abort,         /*abort fn*/          \
+          ibmmca_reset,         /*reset fn*/          \
+          NULL,                 /*slave_attach fn*/   \
+          ibmmca_biosparam,     /*bios fn*/           \
+          16,                   /*can_queue*/         \
+          7,                    /*set by detect*/     \
+          16,                   /*sg_tablesize*/      \
+          1,                    /*cmd_per_lun*/       \
+          0,                    /*present*/           \
+          0,                    /*unchecked_isa_dma*/ \
+          ENABLE_CLUSTERING     /*use_clustering*/    \
+        }
+
+#endif /* _IBMMCA_H */
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- v2.1.14/linux/drivers/scsi/ide-scsi.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/ide-scsi.c	Thu Dec 12 15:48:15 1996
@@ -0,0 +1,492 @@
+/*
+ * linux/drivers/scsi/ide-scsi.c	Version 0.1 - ALPHA	Dec   3, 1996
+ *
+ * Copyright (C) 1996 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * Emulation of a SCSI host adapter for IDE ATAPI devices.
+ *
+ * With this driver, one can use the Linux SCSI drivers instead of the
+ * native IDE ATAPI drivers.
+ *
+ * Ver 0.1   Dec  3 96   Initial version.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "../block/ide.h"
+
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "ide-scsi.h"
+
+#define IDESCSI_DEBUG_LOG		0
+
+typedef struct idescsi_pc_s {
+	u8 c[12];				/* Actual packet bytes */
+	int request_transfer;			/* Bytes to transfer */
+	int actually_transferred;		/* Bytes actually transferred */
+	int buffer_size;			/* Size of our data buffer */
+	struct request *rq;			/* The corresponding request */
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	Scsi_Cmnd *scsi_cmd;			/* SCSI command */
+	void (*done)(Scsi_Cmnd *);		/* Scsi completion routine */
+} idescsi_pc_t;
+
+typedef struct {
+	ide_drive_t *drive;
+	idescsi_pc_t *pc;			/* Current packet command */
+	unsigned int flags;			/* Status/Action flags */
+} idescsi_scsi_t;
+
+#define IDESCSI_DRQ_INTERRUPT		0	/* DRQ interrupt device */
+#define IDESCSI_PC_RQ			90
+
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned check		:1;	/* Error occurred */
+		unsigned idx		:1;	/* Reserved */
+		unsigned corr		:1;	/* Correctable error occurred */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned dsc		:1;	/* Media access command finished */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
+		unsigned bsy		:1;	/* The device has access to the command block */
+	} b;
+} idescsi_status_reg_t;
+
+typedef union {
+	unsigned all			:16;
+	struct {
+		unsigned low		:8;	/* LSB */
+		unsigned high		:8;	/* MSB */
+	} b;
+} idescsi_bcount_reg_t;
+
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned reserved	:6;	/* Reserved */
+	} b;
+} idescsi_ireason_reg_t;
+
+static void idescsi_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		IN_BYTE (IDE_DATA_REG);
+}
+
+static void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	idescsi_scsi_t *scsi = drive->driver_data;
+	struct request *rq = hwgroup->rq;
+	idescsi_pc_t *pc = (idescsi_pc_t *) rq->buffer;
+
+	if (rq->cmd != IDESCSI_PC_RQ) {
+		ide_end_request (uptodate, hwgroup);
+		return;
+	}
+	ide_end_drive_cmd (drive, 0, 0);
+	if (rq->errors >= ERROR_MAX) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = DID_ERROR << 16;
+	} else if (rq->errors) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+	} else {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: success for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = DID_OK << 16;
+	}
+	pc->done(pc->scsi_cmd);
+	kfree(pc); kfree(rq);
+	scsi->pc = NULL;
+}
+
+static void idescsi_pc_intr (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->driver_data;
+	idescsi_status_reg_t status;
+	idescsi_bcount_reg_t bcount;
+	idescsi_ireason_reg_t ireason;
+	idescsi_pc_t *pc=scsi->pc;
+	struct request *rq = pc->rq;
+	unsigned int temp;
+
+#if IDESCSI_DEBUG_LOG
+	printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n");
+#endif /* IDESCSI_DEBUG_LOG */	
+
+	status.all = GET_STAT();					/* Clear the interrupt */
+
+	if (!status.b.drq) {						/* No more interrupts */
+#if IDESCSI_DEBUG_LOG
+		printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDESCSI_DEBUG_LOG */
+		ide_sti();
+		if (status.b.check)
+			rq->errors++;
+		idescsi_end_request (1, HWGROUP(drive));
+		return;
+	}
+	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);
+       	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+	if (ireason.b.cod) {
+		printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n");
+		ide_do_reset (drive);
+		return;
+	}
+	if (ireason.b.io) {
+		temp = pc->actually_transferred + bcount.all;
+		if ( temp > pc->request_transfer) {
+			if (temp > pc->buffer_size) {
+				printk (KERN_ERR "ide-scsi: The scsi wants to send us more data than expected - discarding data\n");
+				idescsi_discard_data (drive,bcount.all);
+				ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);
+				return;
+			}
+#if IDESCSI_DEBUG_LOG
+			printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n");
+#endif /* IDESCSI_DEBUG_LOG */
+		}
+	}
+	if (ireason.b.io)
+		atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+	else
+		atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+	pc->actually_transferred+=bcount.all;				/* Update the current position */
+	pc->current_position+=bcount.all;
+
+	ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);		/* And set the interrupt handler again */
+}
+
+static void idescsi_transfer_pc (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->driver_data;
+	idescsi_ireason_reg_t ireason;
+
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return;
+	}
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+	if (!ireason.b.cod || ireason.b.io) {
+		printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n");
+		ide_do_reset (drive);
+		return;
+	}
+	ide_set_handler (drive, &idescsi_pc_intr, WAIT_CMD);	/* Set the interrupt routine */
+	atapi_output_bytes (drive, scsi->pc->c, 12);		/* Send the actual packet */
+}
+
+/*
+ *	Issue a packet command
+ */
+static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	idescsi_scsi_t *scsi = drive->driver_data;
+	idescsi_bcount_reg_t bcount;
+
+	scsi->pc=pc;							/* Set the current packet command */
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;
+	bcount.all = IDE_MIN (pc->request_transfer, 63 * 1024);		/* Request to transfer the entire buffer at once */
+
+	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE (0,IDE_FEATURE_REG);
+	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+	if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
+		ide_set_handler (drive, &idescsi_transfer_pc, WAIT_CMD);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+		idescsi_transfer_pc (drive);
+	}
+}
+
+/*
+ *	idescsi_do_request is our request handling function.
+ */
+static void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+#if IDESCSI_DEBUG_LOG
+	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDESCSI_DEBUG_LOG */
+
+	if (rq->cmd == IDESCSI_PC_RQ) {
+		idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer);
+		return;
+	}
+	printk (KERN_ERR "ide-scsi: %s: unsupported command in request queue (%x)\n", drive->name, rq->cmd);
+	idescsi_end_request (0,HWGROUP (drive));
+}
+
+static int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static ide_drive_t *idescsi_drives[MAX_HWIFS * MAX_DRIVES];
+static int idescsi_initialized = 0;
+
+/*
+ *	Driver initialization.
+ */
+static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi, int id)
+{
+	DRIVER(drive)->busy++;
+	idescsi_drives[id] = drive;
+	drive->driver_data = scsi;
+	drive->ready_stat = 0;
+	memset (scsi, 0, sizeof (idescsi_scsi_t));
+	scsi->drive = drive;
+	if (drive->id && (drive->id->config & 0x0060) == 0x20)
+		set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+}
+
+static int idescsi_cleanup (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->driver_data;
+
+	if (ide_unregister_subdriver (drive))
+		return 1;
+	drive->driver_data = NULL;
+	kfree (scsi);
+	return 0;
+}
+
+int idescsi_init (void);
+static ide_module_t idescsi_module = {
+	IDE_DRIVER_MODULE,
+	idescsi_init,
+	NULL
+};
+
+/*
+ *	IDE subdriver functions, registered with ide.c
+ */
+static ide_driver_t idescsi_driver = {
+	ide_scsi,		/* media */
+	0,			/* busy */
+	0,			/* supports_dma */
+	0,			/* supports_dsc_overlap */
+	idescsi_cleanup,	/* cleanup */
+	idescsi_do_request,	/* do_request */
+	idescsi_end_request,	/* end_request */
+	NULL,			/* ioctl */
+	idescsi_open,		/* open */
+	idescsi_ide_release,	/* release */
+	NULL,			/* media_change */
+	NULL,			/* pre_reset */
+	NULL,			/* capacity */
+	NULL			/* special */
+};
+
+static struct proc_dir_entry idescsi_proc_dir = {PROC_SCSI_IDESCSI, 8, "ide-scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+
+/*
+ *	idescsi_init will register the driver for each scsi.
+ */
+int idescsi_init (void)
+{
+	ide_drive_t *drive;
+	idescsi_scsi_t *scsi;
+	byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255};
+	int i, failed, id;
+
+	if (idescsi_initialized)
+		return 0;
+	idescsi_initialized = 1;
+	for (i = 0; i < MAX_HWIFS * MAX_DRIVES; i++)
+		idescsi_drives[i] = NULL;
+	MOD_INC_USE_COUNT;
+	for (i = 0; media[i] != 255; i++) {
+		failed = 0;
+		while ((drive = ide_scan_devices (media[i], NULL, failed++)) != NULL) {
+			if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) {
+				printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name);
+				continue;
+			}
+			if (ide_register_subdriver (drive, &idescsi_driver, IDE_SUBDRIVER_VERSION)) {
+				printk (KERN_ERR "ide-scsi: %s: Failed to register the driver with ide.c\n", drive->name);
+				kfree (scsi);
+				continue;
+			}
+			for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+			idescsi_setup (drive, scsi, id);
+			failed--;
+		}
+	}
+	ide_register_module(&idescsi_module);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int idescsi_detect (Scsi_Host_Template *host_template)
+{
+	struct Scsi_Host *host;
+	int id;
+
+	host_template->proc_dir = &idescsi_proc_dir;
+	host = scsi_register(host_template, 0);
+	for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+	host->max_id = id;
+	host->can_queue = host->cmd_per_lun * id;
+	return 1;
+}
+
+int idescsi_release (struct Scsi_Host *host)
+{
+	ide_drive_t *drive;
+	int id;
+
+	for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) {
+		drive = idescsi_drives[id];
+		if (drive)
+			DRIVER(drive)->busy--;
+	}
+	return 0;
+}
+
+const char *idescsi_info (struct Scsi_Host *host)
+{
+	return "SCSI host adapter emulation for IDE ATAPI devices";
+}
+
+/*
+ *	Most of the SCSI commands are supported directly by ATAPI devices.
+ *	idescsi_transform_pc handles the few exceptions.
+ */
+static inline void idescsi_transform_pc (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	if (drive->media == ide_cdrom) {
+		if (pc->c[0] == READ_6) {
+			pc->c[8] = pc->c[4];
+			pc->c[5] = pc->c[3];
+			pc->c[4] = pc->c[2];
+			pc->c[3] = pc->c[1] & 0x1f;
+			pc->c[2] = 0;
+			pc->c[1] &= 0xe0;
+			pc->c[0] = READ_10;
+		}
+	}
+}
+
+int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+	ide_drive_t *drive = idescsi_drives[cmd->target];
+	struct request *rq = NULL;
+	idescsi_pc_t *pc = NULL;
+
+#if IDESCSI_DEBUG_LOG
+	printk ("idescsi_queue called, serial = %lu, cmd[0] = %x, id = %d\n", cmd->serial_number, cmd->cmnd[0], cmd->target);
+#endif	/* IDESCSI_DEBUG_LOG */
+
+	if (!drive) {
+		printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target);
+		goto abort;
+	}
+	pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+	rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+	if (rq == NULL || pc == NULL) {
+		printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name);
+		goto abort;
+	}
+
+	memset (pc->c, 0, 12);
+	pc->rq = rq;
+	memcpy (pc->c, cmd->cmnd, cmd->cmd_len);
+	pc->buffer = cmd->request_buffer;
+	pc->request_transfer = pc->buffer_size = cmd->request_bufflen;
+	pc->scsi_cmd = cmd;
+	pc->done = done;
+	idescsi_transform_pc (drive, pc);
+
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->cmd = IDESCSI_PC_RQ;
+	(void) ide_do_drive_cmd (drive, rq, ide_end);
+	return 0;
+abort:
+	if (pc) kfree (pc);
+	if (rq) kfree (rq);
+	cmd->result = DID_ERROR << 16;
+	done(cmd);
+	return 0;
+}
+
+int idescsi_abort (Scsi_Cmnd *cmd)
+{
+	return SCSI_ABORT_SNOOZE;
+}
+
+int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags)
+{
+	return SCSI_RESET_PUNT;
+}
+
+#ifdef MODULE
+Scsi_Host_Template idescsi_template = IDESCSI;
+
+int init_module (void)
+{
+	idescsi_init ();
+	idescsi_template.usage_count = &mod_use_count_;
+	scsi_register_module (MODULE_SCSI_HA, &idescsi_template);
+	return 0;
+}
+
+void cleanup_module (void)
+{
+	ide_drive_t *drive;
+	byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255};
+	int i, failed;
+
+	scsi_unregister_module (MODULE_SCSI_HA, &idescsi_template);
+	for (i = 0; media[i] != 255; i++) {
+		failed = 0;
+		while ((drive = ide_scan_devices (media[i], &idescsi_driver, failed++)) != NULL)
+			if (idescsi_cleanup (drive)) {
+				printk ("%s: cleanup_module() called while still busy\n", drive->name);
+				failed++;
+			}
+	}
+	ide_unregister_module(&idescsi_module);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/ide-scsi.h linux/drivers/scsi/ide-scsi.h
--- v2.1.14/linux/drivers/scsi/ide-scsi.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/ide-scsi.h	Wed Dec 11 16:45:55 1996
@@ -0,0 +1,41 @@
+/*
+ * linux/drivers/scsi/ide-scsi.h
+ *
+ * Copyright (C) 1996 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+#ifndef IDESCSI_H
+#define IDESCSI_H
+
+extern int idescsi_detect (Scsi_Host_Template *host_template);
+extern int idescsi_release (struct Scsi_Host *host);
+extern const char *idescsi_info (struct Scsi_Host *host);
+extern int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int idescsi_abort (Scsi_Cmnd *cmd);
+extern int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags);
+
+#define IDESCSI								\
+{	NULL,			/* next		*/			\
+	NULL,			/* usage_count	*/			\
+	NULL,			/* proc_dir	*/			\
+	NULL,			/* proc_info	*/			\
+	"idescsi",		/* name		*/			\
+	idescsi_detect,		/* detect	*/			\
+	idescsi_release,	/* release	*/			\
+	idescsi_info,		/* info		*/			\
+	NULL,			/* command	*/			\
+	idescsi_queue,		/* queuecommand */			\
+	idescsi_abort,		/* abort	*/			\
+	idescsi_reset,		/* reset	*/			\
+	NULL,			/* slave_attach	*/			\
+	NULL,			/* bios_param	*/			\
+	10,			/* can_queue	*/			\
+	-1,			/* this_id	*/			\
+	SG_NONE,		/* sg_tablesize	*/			\
+	5,			/* cmd_per_lun	*/			\
+	0,			/* present	*/			\
+	0,			/* isa_dma	*/			\
+	DISABLE_CLUSTERING	/* clustering	*/			\
+}
+
+#endif /* IDESCSI_H */
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/ncr53c8xx.h linux/drivers/scsi/ncr53c8xx.h
--- v2.1.14/linux/drivers/scsi/ncr53c8xx.h	Tue Nov 12 15:56:11 1996
+++ linux/drivers/scsi/ncr53c8xx.h	Sun Dec  1 21:22:01 1996
@@ -237,11 +237,7 @@
 
 #if defined(HOSTS_C) || defined(MODULE)
 
-#if	LINUX_VERSION_CODE >= LinuxVersionCode(1,3,98)
 #include <scsi/scsicam.h>
-#else
-#include <linux/scsicam.h>
-#endif
 
 int ncr53c8xx_abort(Scsi_Cmnd *);
 int ncr53c8xx_detect(Scsi_Host_Template *tpnt);
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v2.1.14/linux/drivers/scsi/scsi_ioctl.c	Tue Oct 29 19:58:13 1996
+++ linux/drivers/scsi/scsi_ioctl.c	Wed Dec 11 16:50:17 1996
@@ -186,11 +186,12 @@
     /*
      * The structure that we are passed should look like:
      *
-     * struct sdata{
+     * struct sdata {
      *	unsigned int inlen;
      *	unsigned int outlen;
      *	unsigned char  cmd[];  # However many bytes are used for cmd.
      *	unsigned char  data[];
+     * };
      */
     get_user(inlen, &sic->inlen);
     get_user(outlen, &sic->outlen);
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v2.1.14/linux/drivers/scsi/seagate.c	Thu Dec 12 17:02:44 1996
+++ linux/drivers/scsi/seagate.c	Wed Dec 11 16:57:49 1996
@@ -1,21 +1,34 @@
 /*
- *	seagate.c Copyright (C) 1992, 1993 Drew Eckhardt 
- *	low level scsi driver for ST01/ST02, Future Domain TMC-885, 
- *	TMC-950  by
+ *    seagate.c Copyright (C) 1992, 1993 Drew Eckhardt
+ *      low level scsi driver for ST01/ST02, Future Domain TMC-885,
+ *      TMC-950  by
  *
- *		Drew Eckhardt 
+ *              Drew Eckhardt
  *
- *	<drew@colorado.edu>
+ *      <drew@colorado.edu>
  *
- * 	Note : TMC-880 boards don't work because they have two bits in 
- *		the status register flipped, I'll fix this "RSN"
+ *      Note : TMC-880 boards don't work because they have two bits in
+ *              the status register flipped, I'll fix this "RSN"
  *
  *      This card does all the I/O via memory mapped I/O, so there is no need
  *      to check or allocate a region of the I/O address space.
  */
 
+/* Modified 1996 to use new read{b,w,l}, write{b,w,l}, and phys_to_virt
+   macros. This meant redefining st0x_cr_sr and st0x_dr, as well as
+   replacing the "DATA = foo;" and "CONTROL = foo;" structures with 
+   WRITE_DATA(foo) and WRITE_CONTROL(foo) macros.
+
+   Replaced assembler routines with C. There's probably a performance hit,
+   but I only have a cdrom and can't tell. Define SEAGATE_USE_ASM if you
+   want the old assembler code.
+
+   Look for the string "SJT" for details.
+
+ */
+
 /*
- * Configuration : 
+ * Configuration :
  * To use without BIOS -DOVERRIDE=base_address -DCONTROLLER=FD or SEAGATE
  * -DIRQ will override the default of 5.
  * Note: You can now set these options from the kernel's "command line".
@@ -29,21 +42,21 @@
  *
  * will configure the driver for a TMC-8xx style controller using IRQ 15
  * with a base address of 0xC8000.
- * 
+ *
  * -DFAST or -DFAST32 will use blind transfers where possible
  *
- * -DARBITRATE will cause the host adapter to arbitrate for the 
- *	bus for better SCSI-II compatibility, rather than just 
- *	waiting for BUS FREE and then doing its thing.  Should
- *	let us do one command per Lun when I integrate my 
- *	reorganization changes into the distribution sources.
- *
- * -DSLOW_HANDSHAKE will allow compatibility with broken devices that don't 
- *	handshake fast enough (ie, some CD ROM's) for the Seagate
- * 	code.
+ * -DARBITRATE will cause the host adapter to arbitrate for the
+ *      bus for better SCSI-II compatibility, rather than just
+ *      waiting for BUS FREE and then doing its thing.  Should
+ *      let us do one command per Lun when I integrate my
+ *      reorganization changes into the distribution sources.
+ *
+ * -DSLOW_HANDSHAKE will allow compatibility with broken devices that don't
+ *      handshake fast enough (ie, some CD ROM's) for the Seagate
+ *      code.
  *
- * -DSLOW_RATE=x, x some number will let you specify a default 
- *	transfer rate if handshaking isn't working correctly.
+ * -DSLOW_RATE=x, x some number will let you specify a default
+ *      transfer rate if handshaking isn't working correctly.
  */
 
 #include <linux/module.h>
@@ -62,13 +75,16 @@
 #include "seagate.h"
 #include "constants.h"
 #include<linux/stat.h>
+#include <asm/uaccess.h>
+#include "sd.h"
+#include <scsi/scsi_ioctl.h>
 
-struct proc_dir_entry proc_scsi_seagate = {
-    PROC_SCSI_SEAGATE, 7, "seagate",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+struct proc_dir_entry proc_scsi_seagate =
+{
+  PROC_SCSI_SEAGATE, 7, "seagate",
+  S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-
 #ifndef IRQ
 #define IRQ 5
 #endif
@@ -85,113 +101,111 @@
 #define SLOW_RATE 50
 #endif
 
-
 #if defined(LINKED)
-#undef LINKED		/* Linked commands are currently broken ! */
+#undef LINKED                           /* Linked commands are currently
+                                           broken! */
 #endif
 
-static int internal_command(unsigned char target, unsigned char lun,
-			    const void *cmnd,
-			 void *buff, int bufflen, int reselect);
-
-static int incommand;			/*
-						set if arbitration has finished and we are 
-						in some command phase.
-					*/
-
-static unsigned int base_address = 0;	/*
-						Where the card ROM starts,
-						used to calculate memory mapped
-						register location.
-					*/
-#ifdef notyet
-static volatile int abort_confirm = 0;
-#endif
-
-static unsigned int st0x_cr_sr;		/*
-						control register write,
-						status register read.
-						256 bytes in length.
-
-						Read is status of SCSI BUS,
-						as per STAT masks.
-
-					*/
+static int internal_command (unsigned char target, unsigned char lun,
+                             const void *cmnd,
+                             void *buff, int bufflen, int reselect);
 
+static int incommand;                   /* set if arbitration has finished
+                                           and we are in some command phase. */
 
-static unsigned int st0x_dr;		/*
-						data register, read write
-						256 bytes in length.
-					*/
-
+static unsigned int base_address = 0;   /* Where the card ROM starts, used to 
+                                           calculate memory mapped register
+                                           location.  */
+#ifdef notyet
+static volatile int abort_confirm = 0;
 
-static volatile int st0x_aborted=0;	/* 
-						set when we are aborted, ie by a time out, etc.
-					*/
+#endif
 
-static unsigned char controller_type = 0; /* set to SEAGATE for ST0x boards or FD for TMC-8xx boards */
+static unsigned long st0x_cr_sr;        /* control register write, status
+                                           register read.  256 bytes in
+                                           length.
+                                           Read is status of SCSI BUS, as per 
+                                           STAT masks.  */
+
+static unsigned long st0x_dr;           /* data register, read write 256
+                                           bytes in length.  */
+
+static volatile int st0x_aborted = 0;   /* set when we are aborted, ie by a
+                                           time out, etc.  */
+
+static unsigned char controller_type = 0;       /* set to SEAGATE for ST0x
+                                                   boards or FD for TMC-8xx
+                                                   boards */
 static unsigned char irq = IRQ;
-			
-#define retcode(result) (((result) << 16) | (message << 8) | status) 			
+
+#define retcode(result) (((result) << 16) | (message << 8) | status)
 #define STATUS (readb(st0x_cr_sr))
-#define CONTROL STATUS 
 #define DATA (readb(st0x_dr))
+/* SJT: Start. */
+#define WRITE_CONTROL(d) writeb((d), st0x_cr_sr)
+#define WRITE_DATA(d) writeb((d), st0x_dr)
+/* SJT: End. */
 
-void st0x_setup (char *str, int *ints) {
-    controller_type = SEAGATE;
-    base_address = ints[1];
-    irq = ints[2];
+void st0x_setup (char *str, int *ints)
+{
+  controller_type = SEAGATE;
+  base_address = ints[1];
+  irq = ints[2];
 }
 
-void tmc8xx_setup (char *str, int *ints) {
-    controller_type = FD;
-    base_address = ints[1];
-    irq = ints[2];
+void tmc8xx_setup (char *str, int *ints)
+{
+  controller_type = FD;
+  base_address = ints[1];
+  irq = ints[2];
 }
-    
 
-#ifndef OVERRIDE		
-static unsigned int seagate_bases[] = {
-	0xc8000, 0xca000, 0xcc000,
-	0xce000, 0xdc000, 0xde000
+#ifndef OVERRIDE
+static unsigned int seagate_bases[] =
+{
+  0xc8000, 0xca000, 0xcc000,
+  0xce000, 0xdc000, 0xde000
 };
 
-typedef struct {
-	const unsigned char *signature ;
-	unsigned offset;
-	unsigned length;
-	unsigned char type;
-} Signature;
-	
-static const Signature signatures[] = {
+typedef struct
+{
+  const unsigned char *signature;
+  unsigned offset;
+  unsigned length;
+  unsigned char type;
+}
+Signature;
+
+static const Signature signatures[] =
+{
 #ifdef CONFIG_SCSI_SEAGATE
-{"ST01 v1.7  (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
-{"SCSI BIOS 2.00  (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
+  {"ST01 v1.7  (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
+  {"SCSI BIOS 2.00  (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
 
 /*
- * The following two lines are NOT mistakes.  One detects ROM revision 
- * 3.0.0, the other 3.2.  Since seagate has only one type of SCSI adapter, 
+ * The following two lines are NOT mistakes.  One detects ROM revision
+ * 3.0.0, the other 3.2.  Since seagate has only one type of SCSI adapter,
  * and this is not going to change, the "SEAGATE" and "SCSI" together
  * are probably "good enough"
  */
 
-{"SEAGATE SCSI BIOS ",16, 17, SEAGATE},
-{"SEAGATE SCSI BIOS ",17, 17, SEAGATE},
+  {"SEAGATE SCSI BIOS ", 16, 17, SEAGATE},
+  {"SEAGATE SCSI BIOS ", 17, 17, SEAGATE},
 
 /*
  * However, future domain makes several incompatible SCSI boards, so specific
  * signatures must be used.
  */
 
-{"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 46, FD},
-{"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
-{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD},
-{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
-{"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
-{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92",   5, 44, FD},
-{"IBM F1 BIOS V1.1004/30/92",			   5, 25, FD},
-{"FUTURE DOMAIN TMC-950",                        5, 21, FD},
-#endif /* CONFIG_SCSI_SEAGATE */
+  {"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 46, FD},
+  {"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
+  {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90", 5, 47, FD},
+  {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90", 5, 47, FD},
+  {"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
+  {"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+  {"IBM F1 BIOS V1.1004/30/92", 5, 25, FD},
+  {"FUTURE DOMAIN TMC-950", 5, 21, FD},
+#endif                                  /* CONFIG_SCSI_SEAGATE */
 }
 ;
 
@@ -203,44 +217,45 @@
  */
 
 static int hostno = -1;
-static void seagate_reconnect_intr(int, void *, struct pt_regs *);
+static void seagate_reconnect_intr (int, void *, struct pt_regs *);
 
 #ifdef FAST
 static int fast = 1;
-#endif 
+
+#endif
 
 #ifdef SLOW_HANDSHAKE
-/* 
- * Support for broken devices : 
- * The Seagate board has a handshaking problem.  Namely, a lack 
- * thereof for slow devices.  You can blast 600K/second through 
- * it if you are polling for each byte, more if you do a blind 
- * transfer.  In the first case, with a fast device, REQ will 
- * transition high-low or high-low-high before your loop restarts 
- * and you'll have no problems.  In the second case, the board 
- * will insert wait states for up to 13.2 usecs for REQ to 
+/*
+ * Support for broken devices :
+ * The Seagate board has a handshaking problem.  Namely, a lack
+ * thereof for slow devices.  You can blast 600K/second through
+ * it if you are polling for each byte, more if you do a blind
+ * transfer.  In the first case, with a fast device, REQ will
+ * transition high-low or high-low-high before your loop restarts
+ * and you'll have no problems.  In the second case, the board
+ * will insert wait states for up to 13.2 usecs for REQ to
  * transition low->high, and everything will work.
  *
- * However, there's nothing in the state machine that says 
+ * However, there's nothing in the state machine that says
  * you *HAVE* to see a high-low-high set of transitions before
  * sending the next byte, and slow things like the Trantor CD ROMS
  * will break because of this.
- * 
- * So, we need to slow things down, which isn't as simple as it 
+ *
+ * So, we need to slow things down, which isn't as simple as it
  * seems.  We can't slow things down period, because then people
- * who don't recompile their kernels will shoot me for ruining 
+ * who don't recompile their kernels will shoot me for ruining
  * their performance.  We need to do it on a case per case basis.
  *
- * The best for performance will be to, only for borken devices 
+ * The best for performance will be to, only for borken devices
  * (this is stored on a per-target basis in the scsi_devices array)
- * 
- * Wait for a low->high transition before continuing with that 
- * transfer.  If we timeout, continue anyways.  We don't need 
- * a long timeout, because REQ should only be asserted until the 
+ *
+ * Wait for a low->high transition before continuing with that
+ * transfer.  If we timeout, continue anyways.  We don't need
+ * a long timeout, because REQ should only be asserted until the
  * corresponding ACK is received and processed.
  *
- * Note that we can't use the system timer for this, because of 
- * resolution, and we *really* can't use the timer chip since 
+ * Note that we can't use the system timer for this, because of
+ * resolution, and we *really* can't use the timer chip since
  * gettimeofday() and the beeper routines use that.  So,
  * the best thing for us to do will be to calibrate a timing
  * loop in the initialization code using the timer chip before
@@ -248,192 +263,206 @@
  */
 
 static int borken_calibration = 0;
-static void borken_init (void) {
+static void borken_init (void)
+{
   register int count = 0, start = jiffies + 1, stop = start + 25;
 
-  while (jiffies < start);
-  for (;jiffies < stop; ++count);
+  while (jiffies < start) ;
+  for (; jiffies < stop; ++count) ;
 
-/* 
- * Ok, we now have a count for .25 seconds.  Convert to a 
+/*
+ * Ok, we now have a count for .25 seconds.  Convert to a
  * count per second and divide by transfer rate in K.
  */
 
-  borken_calibration =  (count * 4) / (SLOW_RATE*1024);
+  borken_calibration = (count * 4) / (SLOW_RATE * 1024);
 
   if (borken_calibration < 1)
-  	borken_calibration = 1;
+    borken_calibration = 1;
 #if (DEBUG & DEBUG_BORKEN)
-  printk("scsi%d : borken calibrated to %dK/sec, %d cycles per transfer\n", 
-	hostno, BORKEN_RATE, borken_calibration);
+  printk ("scsi%d : borken calibrated to %dK/sec, %d cycles per transfer\n",
+          hostno, BORKEN_RATE, borken_calibration);
 #endif
 }
 
-static inline void borken_wait(void) {
+static inline void borken_wait (void)
+{
   register int count;
-  for (count = borken_calibration; count && (STATUS & STAT_REQ); 
-  	--count);
-#if (DEBUG & DEBUG_BORKEN) 
+
+  for (count = borken_calibration; count && (STATUS & STAT_REQ);
+       --count) ;
+#if (DEBUG & DEBUG_BORKEN)
   if (count)
-  	printk("scsi%d : borken timeout\n", hostno);
-#endif 
+    printk ("scsi%d : borken timeout\n", hostno);
+#endif
 }
 
 #endif /* def SLOW_HANDSHAKE */
 
 int seagate_st0x_detect (Scsi_Host_Template * tpnt)
-	{
-     struct Scsi_Host *instance;
+{
+  struct Scsi_Host *instance;
+
 #ifndef OVERRIDE
-	int i,j;
-#endif 
+  int i, j;
 
-     tpnt->proc_dir = &proc_scsi_seagate;
+#endif
+
+  tpnt->proc_dir = &proc_scsi_seagate;
 /*
- *	First, we try for the manual override.
+ *    First, we try for the manual override.
  */
-#ifdef DEBUG 
-	printk("Autodetecting ST0x / TMC-8xx\n");
+#ifdef DEBUG
+  printk ("Autodetecting ST0x / TMC-8xx\n");
 #endif
-	
-	if (hostno != -1)
-		{
-		printk ("ERROR : seagate_st0x_detect() called twice.\n");
-		return 0;
-		}
 
-      /* If the user specified the controller type from the command line,
-	 controller_type will be non-zero, so don't try to detect one */
+  if (hostno != -1)
+  {
+    printk ("ERROR : seagate_st0x_detect() called twice.\n");
+    return 0;
+  }
+
+/* If the user specified the controller type from the command line,
+   controller_type will be non-zero, so don't try to detect one */
 
-	if (!controller_type) {
+  if (!controller_type)
+  {
 #ifdef OVERRIDE
-	base_address = OVERRIDE;
+    base_address = OVERRIDE;
 
 /* CONTROLLER is used to override controller (SEAGATE or FD). PM: 07/01/93 */
 #ifdef CONTROLLER
-	controller_type = CONTROLLER;
+    controller_type = CONTROLLER;
 #else
 #error Please use -DCONTROLLER=SEAGATE or -DCONTROLLER=FD to override controller type
 #endif /* CONTROLLER */
 #ifdef DEBUG
-	printk("Base address overridden to %x, controller type is %s\n",
-		base_address,controller_type == SEAGATE ? "SEAGATE" : "FD");
-#endif 
-#else /* OVERRIDE */	
-/*
- *	To detect this card, we simply look for the signature
- *	from the BIOS version notice in all the possible locations
- *	of the ROM's.  This has a nice side effect of not trashing
- * 	any register locations that might be used by something else.
+    printk ("Base address overridden to %x, controller type is %s\n",
+            base_address, controller_type == SEAGATE ? "SEAGATE" : "FD");
+#endif
+#else /* OVERRIDE */
+/*
+ *    To detect this card, we simply look for the signature
+ *      from the BIOS version notice in all the possible locations
+ *      of the ROM's.  This has a nice side effect of not trashing
+ *      any register locations that might be used by something else.
  *
  * XXX - note that we probably should be probing the address
  * space for the on-board RAM instead.
  */
 
-	for (i = 0; i < (sizeof (seagate_bases) / sizeof (unsigned int)); ++i)
-		for (j = 0; !base_address && j < NUM_SIGNATURES; ++j)
-		if (check_signature(seagate_bases[i] + signatures[j].offset,
-		    signatures[j].signature, signatures[j].length)) {
-			base_address = seagate_bases[i];
-			controller_type = signatures[j].type;
-		}
+    for (i = 0; i < (sizeof (seagate_bases) / sizeof (unsigned int)); ++i)
+
+      for (j = 0; !base_address && j < NUM_SIGNATURES; ++j)
+        if (check_signature (seagate_bases[i] + signatures[j].offset,
+                             signatures[j].signature, signatures[j].length))
+        {
+          base_address = seagate_bases[i];
+          controller_type = signatures[j].type;
+        }
 #endif /* OVERRIDE */
-	} /* (! controller_type) */
- 
-	tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
-	tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
-
-	if (base_address)
-		{
-		st0x_cr_sr = base_address + (controller_type == SEAGATE ? 0x1a00 : 0x1c00); 
-		st0x_dr = st0x_cr_sr + 0x200;
+  }    /* (! controller_type) */
+
+  tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
+  tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
+
+  if (base_address)
+  {
+    st0x_cr_sr = base_address + (controller_type == SEAGATE ? 0x1a00 : 0x1c00);
+    st0x_dr = st0x_cr_sr + 0x200;
 #ifdef DEBUG
-		printk("%s detected. Base address = %x, cr = %x, dr = %x\n", tpnt->name, base_address, st0x_cr_sr, st0x_dr);
+    printk ("%s detected. Base address = %x, cr = %x, dr = %x\n",
+            tpnt->name, base_address, st0x_cr_sr, st0x_dr);
 #endif
 /*
- *	At all times, we will use IRQ 5.  Should also check for IRQ3 if we 
- * 	loose our first interrupt.
+ *    At all times, we will use IRQ 5.  Should also check for IRQ3 if we
+ *      loose our first interrupt.
  */
-		instance = scsi_register(tpnt, 0);
-		hostno = instance->host_no;
-		if (request_irq((int) irq, seagate_reconnect_intr, SA_INTERRUPT,
-		   (controller_type == SEAGATE) ? "seagate" : "tmc-8xx", NULL)) {
-			printk("scsi%d : unable to allocate IRQ%d\n",
-				hostno, (int) irq);
-			return 0;
-		}
-		instance->irq = irq;
-		instance->io_port = base_address;
+    instance = scsi_register (tpnt, 0);
+    hostno = instance->host_no;
+    if (request_irq ((int) irq, seagate_reconnect_intr, SA_INTERRUPT,
+                (controller_type == SEAGATE) ? "seagate" : "tmc-8xx", NULL))
+    {
+      printk ("scsi%d : unable to allocate IRQ%d\n", hostno, (int) irq);
+      return 0;
+    }
+    instance->irq = irq;
+    instance->io_port = base_address;
 #ifdef SLOW_HANDSHAKE
-		borken_init();
+    borken_init ();
 #endif
-		
-		printk("%s options:"
+
+    printk ("%s options:"
 #ifdef ARBITRATE
-		" ARBITRATE"
+            " ARBITRATE"
 #endif
 #ifdef SLOW_HANDSHAKE
-		" SLOW_HANDSHAKE"
+            " SLOW_HANDSHAKE"
 #endif
 #ifdef FAST
 #ifdef FAST32
-		" FAST32"
+            " FAST32"
 #else
-		" FAST"
+            " FAST"
 #endif
 #endif
 #ifdef LINKED
-		" LINKED"
+            " LINKED"
 #endif
-	      "\n", tpnt->name);
-		return 1;
-		}
-	else
-		{
+            "\n", tpnt->name);
+    return 1;
+  }
+  else
+  {
 #ifdef DEBUG
-		printk("ST0x / TMC-8xx not detected.\n");
+    printk ("ST0x / TMC-8xx not detected.\n");
 #endif
-		return 0;
-		}
-	}
-	 
-const char *seagate_st0x_info(struct Scsi_Host * shpnt) {
-      static char buffer[64];
-	sprintf(buffer, "%s at irq %d, address 0x%05X", 
-		(controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR,
-		irq, base_address);
-	return buffer;
+    return 0;
+  }
 }
 
-int seagate_st0x_proc_info(char *buffer, char **start, off_t offset,
-                               int length, int hostno, int inout)
+const char *seagate_st0x_info (struct Scsi_Host *shpnt)
 {
-       const char *info = seagate_st0x_info(NULL);
-       int len;
-       int pos;
-       int begin;
-
-       if (inout) return(-ENOSYS);
-
-       begin = 0;
-       strcpy(buffer,info);
-       strcat(buffer,"\n");
-
-       pos = len = strlen(buffer);
-
-       if (pos<offset) {
-               len = 0;
-               begin = pos;
-               }
-
-       *start = buffer + (offset - begin);
-       len -= (offset - begin);
-       if ( len > length ) len = length;
-       return(len);
+  static char buffer[64];
+
+  sprintf (buffer, "%s at irq %d, address 0x%05X",
+           (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR,
+           irq, base_address);
+  return buffer;
+}
+
+int seagate_st0x_proc_info (char *buffer, char **start, off_t offset,
+                            int length, int hostno, int inout)
+{
+  const char *info = seagate_st0x_info (NULL);
+  int len;
+  int pos;
+  int begin;
+
+  if (inout)
+    return (-ENOSYS);
+
+  begin = 0;
+  strcpy (buffer, info);
+  strcat (buffer, "\n");
+
+  pos = len = strlen (buffer);
+
+  if (pos < offset)
+  {
+    len = 0;
+    begin = pos;
+  }
+
+  *start = buffer + (offset - begin);
+  len -= (offset - begin);
+  if (len > length)
+    len = length;
+  return (len);
 }
 
 /*
- * These are our saved pointers for the outstanding command that is 
+ * These are our saved pointers for the outstanding command that is
  * waiting for a reconnect
  */
 
@@ -445,39 +474,39 @@
 
 #ifdef LINKED
 
-/* 
- * linked_connected indicates whether or not we are currently connected to 
+/*
+ * linked_connected indicates whether or not we are currently connected to
  * linked_target, linked_lun and in an INFORMATION TRANSFER phase,
  * using linked commands.
  */
 
 static int linked_connected = 0;
 static unsigned char linked_target, linked_lun;
-#endif
 
+#endif
 
-static void (*done_fn)(Scsi_Cmnd *) = NULL;
-static Scsi_Cmnd * SCint = NULL;
+static void (*done_fn) (Scsi_Cmnd *) = NULL;
+static Scsi_Cmnd *SCint = NULL;
 
 /*
  * These control whether or not disconnect / reconnect will be attempted,
  * or are being attempted.
  */
 
-#define NO_RECONNECT 	0
-#define RECONNECT_NOW 	1
-#define CAN_RECONNECT	2
+#define NO_RECONNECT    0
+#define RECONNECT_NOW   1
+#define CAN_RECONNECT   2
 
 #ifdef LINKED
 
 /*
  * LINKED_RIGHT indicates that we are currently connected to the correct target
- * for this command, LINKED_WRONG indicates that we are connected to the wrong 
+ * for this command, LINKED_WRONG indicates that we are connected to the wrong
  * target.  Note that these imply CAN_RECONNECT.
  */
 
-#define LINKED_RIGHT 	3
-#define LINKED_WRONG	4
+#define LINKED_RIGHT    3
+#define LINKED_WRONG    4
 #endif
 
 /*
@@ -487,55 +516,60 @@
 static int should_reconnect = 0;
 
 /*
- * The seagate_reconnect_intr routine is called when a target reselects the 
- * host adapter.  This occurs on the interrupt triggered by the target 
+ * The seagate_reconnect_intr routine is called when a target reselects the
+ * host adapter.  This occurs on the interrupt triggered by the target
  * asserting SEL.
  */
 
-static void seagate_reconnect_intr(int irq, void *dev_id, struct pt_regs *regs)
-	{
-	int temp;
-	Scsi_Cmnd * SCtmp;
+static void seagate_reconnect_intr (int irq, void *dev_id, 
+                                    struct pt_regs *regs)
+{
+  int temp;
+  Scsi_Cmnd *SCtmp;
 
-/* enable all other interrupts. */	
-	sti();
+/* enable all other interrupts. */
+  sti ();
 #if (DEBUG & PHASE_RESELECT)
-	printk("scsi%d : seagate_reconnect_intr() called\n", hostno);
+  printk ("scsi%d : seagate_reconnect_intr() called\n", hostno);
 #endif
 
-	if (!should_reconnect)
-	    printk("scsi%d: unexpected interrupt.\n", hostno);
-	else {
-		 should_reconnect = 0;
+  if (!should_reconnect)
+    printk ("scsi%d: unexpected interrupt.\n", hostno);
+  else
+  {
+    should_reconnect = 0;
 
 #if (DEBUG & PHASE_RESELECT)
-		printk("scsi%d : internal_command("
-		       "%d, %08x, %08x, %d, RECONNECT_NOW\n", hostno, 
-			current_target, current_data, current_bufflen);
-#endif
-	
-		temp =  internal_command (current_target, current_lun,
-			current_cmnd, current_data, current_bufflen,
-			RECONNECT_NOW);
+    printk ("scsi%d : internal_command("
+            "%d, %08x, %08x, %d, RECONNECT_NOW\n", hostno,
+            current_target, current_data, current_bufflen);
+#endif
 
-		if (msg_byte(temp) != DISCONNECT) {
-			if (done_fn) {
+    temp = internal_command (current_target, current_lun, current_cmnd,
+                             current_data, current_bufflen, RECONNECT_NOW);
+
+    if (msg_byte (temp) != DISCONNECT)
+    {
+      if (done_fn)
+      {
 #if (DEBUG & PHASE_RESELECT)
-				printk("scsi%d : done_fn(%d,%08x)", hostno, 
-				hostno, temp);
+        printk ("scsi%d : done_fn(%d,%08x)", hostno,
+                hostno, temp);
 #endif
-				if(!SCint) panic("SCint == NULL in seagate");
-				SCtmp = SCint;
-				SCint = NULL;
-				SCtmp->result = temp;
-				done_fn (SCtmp);
-			} else
-				printk("done_fn() not defined.\n");
-			}
-		}
-	} 
+        if (!SCint)
+          panic ("SCint == NULL in seagate");
+        SCtmp = SCint;
+        SCint = NULL;
+        SCtmp->result = temp;
+        done_fn (SCtmp);
+      }
+      else
+        printk ("done_fn() not defined.\n");
+    }
+  }
+}
 
-/* 
+/*
  * The seagate_st0x_queue_command() function provides a queued interface
  * to the seagate SCSI driver.  Basically, it just passes control onto the
  * seagate_command() function, after fixing it so that the done_fn()
@@ -548,290 +582,303 @@
 
 static int recursion_depth = 0;
 
-int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt,  void (*done)(Scsi_Cmnd *))
-	{
-	int result, reconnect;
-	Scsi_Cmnd * SCtmp;
-
-	done_fn = done;
-	current_target = SCpnt->target;
-	current_lun = SCpnt->lun;
-	(const void *) current_cmnd = SCpnt->cmnd;
-	current_data = (unsigned char *) SCpnt->request_buffer;
-	current_bufflen = SCpnt->request_bufflen;
-	SCint = SCpnt;
-	if(recursion_depth) {
-	  return 0;
-	};
-	recursion_depth++;
-	do{
+int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
+{
+  int result, reconnect;
+  Scsi_Cmnd *SCtmp;
+
+  done_fn = done;
+  current_target = SCpnt->target;
+  current_lun = SCpnt->lun;
+  (const void *) current_cmnd = SCpnt->cmnd;
+  current_data = (unsigned char *) SCpnt->request_buffer;
+  current_bufflen = SCpnt->request_bufflen;
+  SCint = SCpnt;
+  if (recursion_depth)
+  {
+    return 0;
+  };
+  recursion_depth++;
+  do
+  {
 #ifdef LINKED
 /*
  * Set linked command bit in control field of SCSI command.
  */
 
-	  current_cmnd[SCpnt->cmd_len] |= 0x01;
-	  if (linked_connected) {
-#if (DEBUG & DEBUG_LINKED) 
-	    printk("scsi%d : using linked commands, current I_T_L nexus is ",
-	      hostno);
-#endif
-	    if ((linked_target == current_target) && 
-	      (linked_lun == current_lun)) {
-#if (DEBUG & DEBUG_LINKED) 
-	    printk("correct\n");
-#endif
-	      reconnect = LINKED_RIGHT;
-	    } else {
-#if (DEBUG & DEBUG_LINKED) 
-	    printk("incorrect\n");
-#endif
-	      reconnect = LINKED_WRONG;
-	    }
-	  } else 
+    current_cmnd[SCpnt->cmd_len] |= 0x01;
+    if (linked_connected)
+    {
+#if (DEBUG & DEBUG_LINKED)
+      printk ("scsi%d : using linked commands, current I_T_L nexus is ",
+              hostno);
+#endif
+      if ((linked_target == current_target) && (linked_lun == current_lun))
+      {
+#if (DEBUG & DEBUG_LINKED)
+        printk ("correct\n");
+#endif
+        reconnect = LINKED_RIGHT;
+      }
+      else
+      {
+#if (DEBUG & DEBUG_LINKED)
+        printk ("incorrect\n");
+#endif
+        reconnect = LINKED_WRONG;
+      }
+    }
+    else
 #endif /* LINKED */
-	    reconnect = CAN_RECONNECT;
-
-
+      reconnect = CAN_RECONNECT;
 
+    result = internal_command (SCint->target, SCint->lun, SCint->cmnd,
+                               SCint->request_buffer, SCint->request_bufflen,
+                               reconnect);
+    if (msg_byte (result) == DISCONNECT)
+      break;
+    SCtmp = SCint;
+    SCint = NULL;
+    SCtmp->result = result;
+    done_fn (SCtmp);
+  }
+  while (SCint);
+  recursion_depth--;
+  return 0;
+}
 
+int seagate_st0x_command (Scsi_Cmnd * SCpnt)
+{
+  return internal_command (SCpnt->target, SCpnt->lun, SCpnt->cmnd,
+                           SCpnt->request_buffer, SCpnt->request_bufflen,
+                           (int) NO_RECONNECT);
+}
 
-	  result = internal_command (SCint->target, SCint->lun, SCint->cmnd, SCint->request_buffer,
-				     SCint->request_bufflen, 
-				     reconnect);
-	  if (msg_byte(result) == DISCONNECT)  break;
-	  SCtmp = SCint;
-	  SCint = NULL;
-	  SCtmp->result = result;
-	  done_fn (SCtmp);
-	} while(SCint);
-	recursion_depth--;
-	return 0;
-      }
+static int internal_command (unsigned char target, unsigned char lun, 
+                             const void *cmnd, void *buff, int bufflen, 
+                             int reselect)
+{
+  int len = 0;
+  unsigned char *data = NULL;
+  struct scatterlist *buffer = NULL;
+  int nobuffs = 0;
+  int clock;
+  int temp;
 
-int seagate_st0x_command (Scsi_Cmnd * SCpnt) {
-	return internal_command (SCpnt->target, SCpnt->lun, SCpnt->cmnd, SCpnt->request_buffer,
-				 SCpnt->request_bufflen, 
-				 (int) NO_RECONNECT);
-}
-	
-static int internal_command(unsigned char target, unsigned char lun, const void *cmnd,
-			 void *buff, int bufflen, int reselect) {
-	int len = 0;
-	unsigned char *data = NULL;	
-	struct scatterlist *buffer = NULL;
-	int nobuffs = 0;
-	int clock;			
-	int temp;
 #ifdef SLOW_HANDSHAKE
-	int borken;	/* Does the current target require Very Slow I/O ? */
+  int borken;                           /* Does the current target require
+                                           Very Slow I/O ?  */
 #endif
 
+#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT)
+  int transfered = 0;
 
-#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT) 
-	int transfered = 0;
 #endif
 
 #if (((DEBUG & PHASE_ETC) == PHASE_ETC) || (DEBUG & PRINT_COMMAND) || \
-	(DEBUG & PHASE_EXIT))	
-	int i;
+     (DEBUG & PHASE_EXIT))
+  int i;
+
 #endif
 
 #if ((DEBUG & PHASE_ETC) == PHASE_ETC)
-	int phase=0, newphase;
+  int phase = 0, newphase;
+
 #endif
 
-	int done = 0;
-	unsigned char status = 0;	
-	unsigned char message = 0;
-	register unsigned char status_read;
+  int done = 0;
+  unsigned char status = 0;
+  unsigned char message = 0;
+  register unsigned char status_read;
 
-	unsigned transfersize = 0, underflow = 0;
+  unsigned transfersize = 0, underflow = 0;
 
-	incommand = 0;
-	st0x_aborted = 0;
+  incommand = 0;
+  st0x_aborted = 0;
 
 #ifdef SLOW_HANDSHAKE
-	borken = (int) SCint->device->borken;
+  borken = (int) SCint->device->borken;
 #endif
 
 #if (DEBUG & PRINT_COMMAND)
-	printk ("scsi%d : target = %d, command = ", hostno, target);
-	print_command((unsigned char *) cmnd);
-	printk("\n");
+  printk ("scsi%d : target = %d, command = ", hostno, target);
+  print_command ((unsigned char *) cmnd);
+  printk ("\n");
 #endif
 
 #if (DEBUG & PHASE_RESELECT)
-	switch (reselect) {
-	case RECONNECT_NOW :
-		printk("scsi%d : reconnecting\n", hostno);
-		break;
+  switch (reselect)
+  {
+    case RECONNECT_NOW:
+      printk ("scsi%d : reconnecting\n", hostno);
+      break;
 #ifdef LINKED
-	case LINKED_RIGHT : 
-		printk("scsi%d : connected, can reconnect\n", hostno);
-		break;
-	case LINKED_WRONG :
-		printk("scsi%d : connected to wrong target, can reconnect\n",
-			hostno);
-		break;		
-#endif
-	case CAN_RECONNECT :
-		printk("scsi%d : allowed to reconnect\n", hostno);
-		break;
-	default :
-		printk("scsi%d : not allowed to reconnect\n", hostno);
-	}
-#endif
-	
-
-	if (target == (controller_type == SEAGATE ? 7 : 6))
-		return DID_BAD_TARGET;
-
-/*
- *	We work it differently depending on if this is is "the first time,"
- *	or a reconnect.  If this is a reselect phase, then SEL will 
- *	be asserted, and we must skip selection / arbitration phases.
+    case LINKED_RIGHT:
+      printk ("scsi%d : connected, can reconnect\n", hostno);
+      break;
+    case LINKED_WRONG:
+      printk ("scsi%d : connected to wrong target, can reconnect\n", hostno);
+      break;
+#endif
+    case CAN_RECONNECT:
+      printk ("scsi%d : allowed to reconnect\n", hostno);
+      break;
+    default:
+      printk ("scsi%d : not allowed to reconnect\n", hostno);
+  }
+#endif
+
+  if (target == (controller_type == SEAGATE ? 7 : 6))
+    return DID_BAD_TARGET;
+
+/*
+ *    We work it differently depending on if this is is "the first time,"
+ *      or a reconnect.  If this is a reselect phase, then SEL will
+ *      be asserted, and we must skip selection / arbitration phases.
  */
 
-	switch (reselect) {
-	case RECONNECT_NOW:
+  switch (reselect)
+  {
+    case RECONNECT_NOW:
 #if (DEBUG & PHASE_RESELECT)
-		printk("scsi%d : phase RESELECT \n", hostno);
+      printk ("scsi%d : phase RESELECT \n", hostno);
 #endif
 
 /*
- *	At this point, we should find the logical or of our ID and the original
- *	target's ID on the BUS, with BSY, SEL, and I/O signals asserted.
+ *    At this point, we should find the logical or of our ID and the original
+ *      target's ID on the BUS, with BSY, SEL, and I/O signals asserted.
  *
- *	After ARBITRATION phase is completed, only SEL, BSY, and the 
- *	target ID are asserted.  A valid initiator ID is not on the bus
- *	until IO is asserted, so we must wait for that.
+ *      After ARBITRATION phase is completed, only SEL, BSY, and the
+ *      target ID are asserted.  A valid initiator ID is not on the bus
+ *      until IO is asserted, so we must wait for that.
  */
-		clock = jiffies + 10;
-		for (;;) {
-			temp = STATUS;
-			if ((temp & STAT_IO) && !(temp & STAT_BSY))
-				break;
+      clock = jiffies + 10;
+      for (;;)
+      {
+        temp = STATUS;
+        if ((temp & STAT_IO) && !(temp & STAT_BSY))
+          break;
 
-			if (jiffies > clock) {
+        if (jiffies > clock)
+        {
 #if (DEBUG & PHASE_RESELECT)
-				printk("scsi%d : RESELECT timed out while waiting for IO .\n",
-					hostno);
+          printk ("scsi%d : RESELECT timed out while waiting for IO .\n",
+                  hostno);
 #endif
-				return (DID_BAD_INTR << 16);
-			}
-		}
-
-/* 
- * 	After I/O is asserted by the target, we can read our ID and its
- *	ID off of the BUS.
- */
- 
-		if (!((temp = DATA) & (controller_type == SEAGATE ? 0x80 : 0x40)))
-			{
+          return (DID_BAD_INTR << 16);
+        }
+      }
+
+/*
+ *    After I/O is asserted by the target, we can read our ID and its
+ *      ID off of the BUS.
+ */
+
+      if (!((temp = DATA) & (controller_type == SEAGATE ? 0x80 : 0x40)))
+      {
 #if (DEBUG & PHASE_RESELECT)
-			printk("scsi%d : detected reconnect request to different target.\n" 
-			       "\tData bus = %d\n", hostno, temp);
+        printk ("scsi%d : detected reconnect request to different target.\n"
+                "\tData bus = %d\n", hostno, temp);
 #endif
-			return (DID_BAD_INTR << 16);
-			}
+        return (DID_BAD_INTR << 16);
+      }
 
-		if (!(temp & (1 << current_target)))
-			{
-			printk("scsi%d : Unexpected reselect interrupt.  Data bus = %d\n",
-				hostno, temp);
-			return (DID_BAD_INTR << 16);
-			}
-
-		buffer=current_buffer;	
-		cmnd=current_cmnd;      /* WDE add */
-		data=current_data;      /* WDE add */
-		len=current_bufflen;    /* WDE add */
-		nobuffs=current_nobuffs;
+      if (!(temp & (1 << current_target)))
+      {
+        printk ("scsi%d : Unexpected reselect interrupt.  Data bus = %d\n",
+                hostno, temp);
+        return (DID_BAD_INTR << 16);
+      }
+
+      buffer = current_buffer;
+      cmnd = current_cmnd;              /* WDE add */
+      data = current_data;              /* WDE add */
+      len = current_bufflen;            /* WDE add */
+      nobuffs = current_nobuffs;
 
 /*
- * 	We have determined that we have been selected.  At this point, 
- *	we must respond to the reselection by asserting BSY ourselves
+ *    We have determined that we have been selected.  At this point,
+ *      we must respond to the reselection by asserting BSY ourselves
  */
 
 #if 1
-		CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+      WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
 #else
-		CONTROL = (BASE_CMD | CMD_BSY);
+      WRITE_CONTROL (BASE_CMD | CMD_BSY);
 #endif
 
 /*
- *	The target will drop SEL, and raise BSY, at which time we must drop
- *	BSY.
+ *    The target will drop SEL, and raise BSY, at which time we must drop
+ *      BSY.
  */
 
-		for (clock = jiffies + 10; (jiffies < clock) &&  (STATUS & STAT_SEL););
+      for (clock = jiffies + 10; (jiffies < clock) && (STATUS & STAT_SEL);) ;
 
-		if (jiffies >= clock)
-			{ 
-			CONTROL = (BASE_CMD | CMD_INTR);
+      if (jiffies >= clock)
+      {
+        WRITE_CONTROL (BASE_CMD | CMD_INTR);
 #if (DEBUG & PHASE_RESELECT)
-			printk("scsi%d : RESELECT timed out while waiting for SEL.\n",
-				hostno);
+        printk ("scsi%d : RESELECT timed out while waiting for SEL.\n", 
+                hostno);
 #endif
-			return (DID_BAD_INTR << 16);				 
-			}
+        return (DID_BAD_INTR << 16);
+      }
 
-		CONTROL = BASE_CMD;
+      WRITE_CONTROL (BASE_CMD);
 
 /*
- *	At this point, we have connected with the target and can get 
- *	on with our lives.
- */	 
-		break;
-	case CAN_RECONNECT:
+ *    At this point, we have connected with the target and can get
+ *      on with our lives.
+ */
+      break;
+    case CAN_RECONNECT:
 
 #ifdef LINKED
 /*
  * This is a bletcherous hack, just as bad as the Unix #! interpreter stuff.
  * If it turns out we are using the wrong I_T_L nexus, the easiest way to deal
- * with it is to go into our INFORMATION TRANSFER PHASE code, send a ABORT 
+ * with it is to go into our INFORMATION TRANSFER PHASE code, send a ABORT
  * message on MESSAGE OUT phase, and then loop back to here.
  */
-  
-connect_loop :
+
+    connect_loop:
 
 #endif
 
 #if (DEBUG & PHASE_BUS_FREE)
-		printk ("scsi%d : phase = BUS FREE \n", hostno);
+      printk ("scsi%d : phase = BUS FREE \n", hostno);
 #endif
 
 /*
- *	BUS FREE PHASE
+ *    BUS FREE PHASE
  *
- * 	On entry, we make sure that the BUS is in a BUS FREE
- *	phase, by insuring that both BSY and SEL are low for
- *	at least one bus settle delay.  Several reads help
- *	eliminate wire glitch.
+ *      On entry, we make sure that the BUS is in a BUS FREE
+ *      phase, by insuring that both BSY and SEL are low for
+ *      at least one bus settle delay.  Several reads help
+ *      eliminate wire glitch.
  */
 
-		clock = jiffies + ST0X_BUS_FREE_DELAY;	
+      clock = jiffies + ST0X_BUS_FREE_DELAY;
 
-#if !defined (ARBITRATE) 
-		while (((STATUS |  STATUS | STATUS) & 
-			 (STAT_BSY | STAT_SEL)) && 
-			 (!st0x_aborted) && (jiffies < clock));
+#if !defined (ARBITRATE)
+      while (((STATUS | STATUS | STATUS) &
+              (STAT_BSY | STAT_SEL)) &&
+             (!st0x_aborted) && (jiffies < clock)) ;
 
-		if (jiffies > clock)
-			return retcode(DID_BUS_BUSY);
-		else if (st0x_aborted)
-			return retcode(st0x_aborted);
+      if (jiffies > clock)
+        return retcode (DID_BUS_BUSY);
+      else if (st0x_aborted)
+        return retcode (st0x_aborted);
 #endif
 
 #if (DEBUG & PHASE_SELECTION)
-		printk("scsi%d : phase = SELECTION\n", hostno);
+      printk ("scsi%d : phase = SELECTION\n", hostno);
 #endif
 
-		clock = jiffies + ST0X_SELECTION_DELAY;
+      clock = jiffies + ST0X_SELECTION_DELAY;
 
 /*
- * Arbitration/selection procedure : 
+ * Arbitration/selection procedure :
  * 1.  Disable drivers
  * 2.  Write HOST adapter address bit
  * 3.  Set start arbitration.
@@ -840,823 +887,860 @@
  * 5.  OR our ID and targets on bus.
  * 6.  Enable SCSI drivers and asserted SEL and ATTN
  */
-		
-#if defined(ARBITRATE)	
-	cli();
-	CONTROL = 0;
-	DATA = (controller_type == SEAGATE) ? 0x80 : 0x40;
-	CONTROL = CMD_START_ARB; 
-	sti();
-	while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) &&
-		(jiffies < clock) && !st0x_aborted);
 
-	if (!(status_read & STAT_ARB_CMPL)) {
+#if defined(ARBITRATE)
+      cli ();
+      WRITE_CONTROL (0);
+      WRITE_DATA ((controller_type == SEAGATE) ? 0x80 : 0x40);
+      WRITE_CONTROL (CMD_START_ARB);
+      sti ();
+      while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) &&
+             (jiffies < clock) && !st0x_aborted) ;
+
+      if (!(status_read & STAT_ARB_CMPL))
+      {
 #if (DEBUG & PHASE_SELECTION)
-		if (status_read & STAT_SEL) 
-			printk("scsi%d : arbitration lost\n", hostno);
-		else
-			printk("scsi%d : arbitration timeout.\n", hostno);
-#endif
-		CONTROL = BASE_CMD;
-		return retcode(DID_NO_CONNECT);
-	};
+        if (status_read & STAT_SEL)
+          printk ("scsi%d : arbitration lost\n", hostno);
+        else
+          printk ("scsi%d : arbitration timeout.\n", hostno);
+#endif
+        WRITE_CONTROL (BASE_CMD);
+        return retcode (DID_NO_CONNECT);
+      };
 
 #if (DEBUG & PHASE_SELECTION)
-	printk("scsi%d : arbitration complete\n", hostno);
+      printk ("scsi%d : arbitration complete\n", hostno);
 #endif
 #endif
 
-
 /*
- *	When the SCSI device decides that we're gawking at it, it will 
- *	respond by asserting BUSY on the bus.
+ *    When the SCSI device decides that we're gawking at it, it will
+ *    respond by asserting BUSY on the bus.
  *
- * 	Note : the Seagate ST-01/02 product manual says that we should 
- * 	twiddle the DATA register before the control register.  However,
- *	this does not work reliably so we do it the other way around.
+ *    Note : the Seagate ST-01/02 product manual says that we should
+ *    twiddle the DATA register before the control register.    However,
+ *    this does not work reliably so we do it the other way around.
  *
- *	Probably could be a problem with arbitration too, we really should
- *	try this with a SCSI protocol or logic analyzer to see what is 
- *	going on.
+ *    Probably could be a problem with arbitration too, we really should
+ *    try this with a SCSI protocol or logic analyzer to see what is
+ *    going on.
  */
-	cli();
-	DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40));
-	CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | 
-		(reselect ? CMD_ATTN : 0);
-	sti();
-		while (!((status_read = STATUS) & STAT_BSY) && 
-			(jiffies < clock) && !st0x_aborted)
-
+      cli ();
+      WRITE_DATA ((unsigned char) ((1 << target) |
+                               (controller_type == SEAGATE ? 0x80 : 0x40)));
+      WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+                     (reselect ? CMD_ATTN : 0));
+      sti ();
+      while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock)
+             && !st0x_aborted)
 #if 0 && (DEBUG & PHASE_SELECTION)
-		{
-		temp = clock - jiffies;
+      {
+        temp = clock - jiffies;
 
-		if (!(jiffies % 5))
-			printk("seagate_st0x_timeout : %d            \r",temp);
-	
-		}
-		printk("Done.                                             \n");
-		printk("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n", 
-			hostno, status_read, temp, st0x_aborted);
+        if (!(jiffies % 5))
+          printk ("seagate_st0x_timeout : %d            \r", temp);
+      }
+      printk ("Done.                                             \n");
+      printk ("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n",
+              hostno, status_read, temp, st0x_aborted);
 #else
-		;
+        ;
 #endif
-	
 
-		if ((jiffies >= clock)  && !(status_read & STAT_BSY))
-			{
+      if ((jiffies >= clock) && !(status_read & STAT_BSY))
+      {
 #if (DEBUG & PHASE_SELECTION)
-			printk ("scsi%d : NO CONNECT with target %d, status = %x \n", 
-				hostno, target, STATUS);
+        printk ("scsi%d : NO CONNECT with target %d, status = %x \n",
+                hostno, target, STATUS);
 #endif
-			return retcode(DID_NO_CONNECT);
-			}
+        return retcode (DID_NO_CONNECT);
+      }
 
 /*
- *	If we have been aborted, and we have a command in progress, IE the 
- *	target still has BSY asserted, then we will reset the bus, and 
- * 	notify the midlevel driver to expect sense.
- */
-
-		if (st0x_aborted) {
-			CONTROL = BASE_CMD;
-			if (STATUS & STAT_BSY) {
-				printk("scsi%d : BST asserted after we've been aborted.\n",
-					hostno);
-				seagate_st0x_reset(NULL, 0);
-				return retcode(DID_RESET);
-			}
-			return retcode(st0x_aborted);
-		}	
+ *    If we have been aborted, and we have a command in progress, IE the
+ *      target still has BSY asserted, then we will reset the bus, and
+ *      notify the midlevel driver to expect sense.
+ */
+
+      if (st0x_aborted)
+      {
+        WRITE_CONTROL (BASE_CMD);
+        if (STATUS & STAT_BSY)
+        {
+          printk ("scsi%d : BST asserted after we've been aborted.\n",
+                  hostno);
+          seagate_st0x_reset (NULL, 0);
+          return retcode (DID_RESET);
+        }
+        return retcode (st0x_aborted);
+      }
 
 /* Establish current pointers.  Take into account scatter / gather */
 
-	if ((nobuffs = SCint->use_sg)) {
+      if ((nobuffs = SCint->use_sg))
+      {
 #if (DEBUG & DEBUG_SG)
-	{
-	int i;
-	printk("scsi%d : scatter gather requested, using %d buffers.\n",
-		hostno, nobuffs);
-	for (i = 0; i < nobuffs; ++i)
-		printk("scsi%d : buffer %d address = %08x length = %d\n",
-			hostno, i, buffer[i].address, buffer[i].length);
-	}
-#endif
-		
-		buffer = (struct scatterlist *) SCint->buffer;
-		len = buffer->length;
-		data = (unsigned char *) buffer->address;
-	} else {
+        {
+          int i;
+
+          printk ("scsi%d : scatter gather requested, using %d buffers.\n",
+                  hostno, nobuffs);
+          for (i = 0; i < nobuffs; ++i)
+            printk ("scsi%d : buffer %d address = %08x length = %d\n",
+                    hostno, i, buffer[i].address, buffer[i].length);
+        }
+#endif
+
+        buffer = (struct scatterlist *) SCint->buffer;
+        len = buffer->length;
+        data = (unsigned char *) buffer->address;
+      }
+      else
+      {
 #if (DEBUG & DEBUG_SG)
-	printk("scsi%d : scatter gather not requested.\n", hostno);
+        printk ("scsi%d : scatter gather not requested.\n", hostno);
 #endif
-		buffer = NULL;
-		len = SCint->request_bufflen;
-		data = (unsigned char *) SCint->request_buffer;
-	}
+        buffer = NULL;
+        len = SCint->request_bufflen;
+        data = (unsigned char *) SCint->request_buffer;
+      }
 
 #if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT))
-	printk("scsi%d : len = %d\n", hostno, len);
+      printk ("scsi%d : len = %d\n", hostno, len);
 #endif
 
-		break;
+      break;
 #ifdef LINKED
-	case LINKED_RIGHT:
-	    	break;
-	case LINKED_WRONG:
-		break;
+    case LINKED_RIGHT:
+      break;
+    case LINKED_WRONG:
+      break;
 #endif
-	}
+  }                                     /* end of switch(reselect) */
 
 /*
- * 	There are several conditions under which we wish to send a message : 
- *	1.  When we are allowing disconnect / reconnect, and need to establish
- *	    the I_T_L nexus via an IDENTIFY with the DiscPriv bit set.
+ *    There are several conditions under which we wish to send a message :
+ *      1.  When we are allowing disconnect / reconnect, and need to establish
+ *          the I_T_L nexus via an IDENTIFY with the DiscPriv bit set.
  *
- *	2.  When we are doing linked commands, are have the wrong I_T_L nexus
- *	    established and want to send an ABORT message.
+ *      2.  When we are doing linked commands, are have the wrong I_T_L nexus
+ *          established and want to send an ABORT message.
  */
 
-	
-	CONTROL = BASE_CMD | CMD_DRVR_ENABLE | 
-		(((reselect == CAN_RECONNECT)
-#ifdef LINKED 
-		|| (reselect == LINKED_WRONG)
-#endif 
-		)  ? CMD_ATTN : 0) ;
-	
+/* GCC does not like an ifdef inside a macro, so do it the hard way. */
+#ifdef LINKED
+  WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE |
+                 (((reselect == CAN_RECONNECT)
+                   || (reselect == LINKED_WRONG)
+                  )? CMD_ATTN : 0));
+#else
+  WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE |
+                 (((reselect == CAN_RECONNECT)
+                  )? CMD_ATTN : 0));
+#endif
+
 /*
- * 	INFORMATION TRANSFER PHASE
+ *    INFORMATION TRANSFER PHASE
+ *
+ *      The nasty looking read / write inline assembler loops we use for
+ *      DATAIN and DATAOUT phases are approximately 4-5 times as fast as
+ *      the 'C' versions - since we're moving 1024 bytes of data, this
+ *      really adds up.
+ *
+ *      SJT: The nasty-looking assembler is gone, so it's slower.
  *
- *	The nasty looking read / write inline assembler loops we use for 
- *	DATAIN and DATAOUT phases are approximately 4-5 times as fast as 
- *	the 'C' versions - since we're moving 1024 bytes of data, this
- *	really adds up.
  */
 
 #if ((DEBUG & PHASE_ETC) == PHASE_ETC)
-	printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno);
-#endif  
-
-	incommand = 1;
-	transfersize = SCint->transfersize;
-	underflow = SCint->underflow;
+  printk ("scsi%d : phase = INFORMATION TRANSFER\n", hostno);
+#endif
 
+  incommand = 1;
+  transfersize = SCint->transfersize;
+  underflow = SCint->underflow;
 
 /*
- * 	Now, we poll the device for status information,
- *	and handle any requests it makes.  Note that since we are unsure of 
- *	how much data will be flowing across the system, etc and cannot 
- *	make reasonable timeouts, that we will instead have the midlevel
- * 	driver handle any timeouts that occur in this phase.
+ *    Now, we poll the device for status information,
+ *      and handle any requests it makes.  Note that since we are unsure of
+ *      how much data will be flowing across the system, etc and cannot
+ *      make reasonable timeouts, that we will instead have the midlevel
+ *      driver handle any timeouts that occur in this phase.
  */
 
-	while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done) 
-		{
+  while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done)
+  {
 #ifdef PARITY
-		if (status_read & STAT_PARITY)
-			{
-			printk("scsi%d : got parity error\n", hostno);
-			st0x_aborted = DID_PARITY;
-			}	
+    if (status_read & STAT_PARITY)
+    {
+      printk ("scsi%d : got parity error\n", hostno);
+      st0x_aborted = DID_PARITY;
+    }
 #endif
 
-		if (status_read & STAT_REQ)
-			{
+    if (status_read & STAT_REQ)
+    {
 #if ((DEBUG & PHASE_ETC) == PHASE_ETC)
-			if ((newphase = (status_read & REQ_MASK)) != phase)
-				{
-				phase = newphase;
-				switch (phase)
-				{
-				case REQ_DATAOUT: 
-					printk("scsi%d : phase = DATA OUT\n",
-						hostno); 
-					break;
-				case REQ_DATAIN : 
-					printk("scsi%d : phase = DATA IN\n",
-						hostno); 
-					break;
-				case REQ_CMDOUT : 
-					printk("scsi%d : phase = COMMAND OUT\n",
-						hostno); 
-					break;
-				case REQ_STATIN :
-					 printk("scsi%d : phase = STATUS IN\n",
-						hostno); 
-					break;
-				case REQ_MSGOUT :
-					printk("scsi%d : phase = MESSAGE OUT\n",
-						hostno); 
-					break;
-				case REQ_MSGIN :
-					printk("scsi%d : phase = MESSAGE IN\n",
-						hostno);
-					break;
-				default : 
-					printk("scsi%d : phase = UNKNOWN\n",
-						hostno); 
-					st0x_aborted = DID_ERROR; 
-				}	
-				}
-#endif
-		switch (status_read & REQ_MASK)
-		{			
-		case REQ_DATAOUT : 
+      if ((newphase = (status_read & REQ_MASK)) != phase)
+      {
+        phase = newphase;
+        switch (phase)
+        {
+          case REQ_DATAOUT:
+            printk ("scsi%d : phase = DATA OUT\n", hostno);
+            break;
+          case REQ_DATAIN:
+            printk ("scsi%d : phase = DATA IN\n", hostno);
+            break;
+          case REQ_CMDOUT:
+            printk ("scsi%d : phase = COMMAND OUT\n", hostno);
+            break;
+          case REQ_STATIN:
+            printk ("scsi%d : phase = STATUS IN\n", hostno);
+            break;
+          case REQ_MSGOUT:
+            printk ("scsi%d : phase = MESSAGE OUT\n", hostno);
+            break;
+          case REQ_MSGIN:
+            printk ("scsi%d : phase = MESSAGE IN\n", hostno);
+            break;
+          default:
+            printk ("scsi%d : phase = UNKNOWN\n", hostno);
+            st0x_aborted = DID_ERROR;
+        }
+      }
+#endif
+      switch (status_read & REQ_MASK)
+      {
+        case REQ_DATAOUT:
 /*
  * If we are in fast mode, then we simply splat the data out
  * in word-sized chunks as fast as we can.
  */
 
-#ifdef FAST 
-if (!len) {
-#if 0 
-	printk("scsi%d: underflow to target %d lun %d \n", 
-		hostno, target, lun);
-	st0x_aborted = DID_ERROR;
-	fast = 0;
+#ifdef FAST
+          if (!len)
+          {
+#if 0
+            printk ("scsi%d: underflow to target %d lun %d \n", hostno,
+                    target, lun);
+            st0x_aborted = DID_ERROR;
+            fast = 0;
 #endif
-	break;
-}
+            break;
+          }
 
-if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+          if (fast && transfersize && !(len % transfersize)
+              && (len >= transfersize)
 #ifdef FAST32
-	&& !(transfersize % 4)
+              && !(transfersize % 4)
 #endif
-	) {
-#if (DEBUG & DEBUG_FAST) 
-	printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
-	       "         len = %d, data = %08x\n", hostno, SCint->underflow, 
-	       SCint->transfersize, len, data);
+            )
+          {
+#if (DEBUG & DEBUG_FAST)
+            printk ("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+                    "         len = %d, data = %08x\n",
+                  hostno, SCint->underflow, SCint->transfersize, len, data);
 #endif
 
-#warning This no longer works: rewrite in C and use readbwl/writebwl
-	__asm__("
-	cld;
-"
+/* SJT: Start. Fast Write */
+#ifdef SEAGATE_USE_ASM
+            __asm__(
+                "cld\n\t"
 #ifdef FAST32
-"	shr $2, %%ecx;
-1:	lodsl;
-	movl %%eax, (%%edi);
-"
+                "shr $2, %%ecx\n\t"
+                "1:\t"
+                "lodsl\n\t"
+                "movl %%eax, (%%edi)\n\t"
 #else
-"1:	lodsb;
-	movb %%al, (%%edi);
-"
-#endif
-"	loop 1b;" : :
-	/* input */
-	"D" (st0x_dr), "S" (data), "c" (SCint->transfersize) :
-	/* clobbered */
-	"eax", "ecx", "esi" );
-
-	len -= transfersize;
-	data += transfersize;
-
+                "1:\t"
+                "lodsb\n\t"
+                "movb %%al, (%%edi)\n\t"
+#endif
+                "loop 1b;"
+/* output */    :
+/* input */     : "D" (phys_to_virt(st0x_dr)), "S" (data), "c" (SCint->transfersize) 
+/* clobbered */ : "eax", "ecx", "esi" );
+#else /* SEAGATE_USE_ASM */
+            {
+#ifdef FAST32
+              unsigned int *iop = phys_to_virt (st0x_dr);
+              const unsigned int *dp = (unsigned int *) data;
+              int xferlen = transfersize >> 2;
+#else
+              unsigned char *iop = phys_to_virt (st0x_dr);
+              const unsigned char *dp = data;
+              int xferlen = transfersize;
+#endif
+              for (; xferlen; --xferlen)
+                *iop = *dp++;
+            }
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End */
+            len -= transfersize;
+            data += transfersize;
 #if (DEBUG & DEBUG_FAST)
-	printk("scsi%d : FAST transfer complete len = %d data = %08x\n", 
-		hostno, len, data);
+            printk ("scsi%d : FAST transfer complete len = %d data = %08x\n",
+                    hostno, len, data);
 #endif
-
-
-} else 
-#endif
-
-{
+          }
+          else
+#endif /* ifdef FAST */
+          {
 /*
- * 	We loop as long as we are in a data out phase, there is data to send, 
- *	and BSY is still active.
+ *    We loop as long as we are in a data out phase, there is data to send,
+ *      and BSY is still active.
  */
-#warning This no longer works: rewrite in C and use readbwl/writebwl
-		__asm__ (
 
+/* SJT: Start. Slow Write. */
+#ifdef SEAGATE_USE_ASM
 /*
-	Local variables : 
-	len = ecx
-	data = esi
-	st0x_cr_sr = ebx
-	st0x_dr =  edi
-
-	Test for any data here at all.
-*/
-	"\torl %%ecx, %%ecx
-	jz 2f
-
-	cld
-
-	movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%ebx
-	movl " SYMBOL_NAME_STR(st0x_dr) ", %%edi
-	
-1:	movb (%%ebx), %%al\n"
-/*
-	Test for BSY
-*/
-
-	"\ttest $1, %%al
-	jz 2f\n"
-
-/*
-	Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0.
+ *      We loop as long as we are in a data out phase, there is data to send, 
+ *      and BSY is still active.
+ */
+/* Local variables : len = ecx , data = esi, 
+                     st0x_cr_sr = ebx, st0x_dr =  edi
 */
-	"\ttest $0xe, %%al
-	jnz 2f	\n"
-/*
-	Test for REQ
-*/	
-	"\ttest $0x10, %%al
-	jz 1b
-	lodsb
-	movb %%al, (%%edi) 
-	loop 1b
-
-2: 
-									":
-/* output */
-"=S" (data), "=c" (len) :
-/* input */
-"0" (data), "1" (len) :
-/* clobbered */
-"eax", "ebx", "edi"); 
-}
-
-			if (!len && nobuffs) {
-				--nobuffs;
-				++buffer;
-				len = buffer->length;
-				data = (unsigned char *) buffer->address;
+            __asm__ (
+            /* Test for any data here at all. */
+                    "orl %%ecx, %%ecx\n\t"
+                    "jz 2f\n\t"
+                    "cld\n\t"
+/*                    "movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%ebx\n\t"  */
+/*                    "movl " SYMBOL_NAME_STR(st0x_dr) ", %%edi\n\t"  */
+                "1:\t"
+                    "movb (%%ebx), %%al\n\t"
+            /* Test for BSY */
+                    "test $1, %%al\n\t"
+                    "jz 2f\n\t"
+            /* Test for data out phase - STATUS & REQ_MASK should be 
+               REQ_DATAOUT, which is 0. */
+                    "test $0xe, %%al\n\t"
+                    "jnz 2f\n\t"
+            /* Test for REQ */      
+                    "test $0x10, %%al\n\t"
+                    "jz 1b\n\t"
+                    "lodsb\n\t"
+                    "movb %%al, (%%edi)\n\t"
+                    "loop 1b\n\t"
+                "2:\n"
+/* output */    : "=S" (data), "=c" (len) 
+/* input */     : "0" (data), "1" (len), "b" (phys_to_virt(st0x_cr_sr)), "D" (phys_to_virt(st0x_dr)) 
+/* clobbered */ : "eax", "ebx", "edi"); 
+#else /* SEAGATE_USE_ASM */
+            while (len)
+            {
+              unsigned char stat;
+
+              stat = STATUS;
+              if (!(stat & STAT_BSY) || ((stat & REQ_MASK) != REQ_DATAOUT))
+                break;
+              if (stat & STAT_REQ)
+              {
+                WRITE_DATA (*data++);
+                --len;
+              }
+            }
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End. */
+          }
+
+          if (!len && nobuffs)
+          {
+            --nobuffs;
+            ++buffer;
+            len = buffer->length;
+            data = (unsigned char *) buffer->address;
 #if (DEBUG & DEBUG_SG)
-	printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
-		hostno, len, data);
+            printk ("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+                    hostno, len, data);
 #endif
-			}
-			break;
+          }
+          break;
 
-		case REQ_DATAIN : 
+        case REQ_DATAIN:
 #ifdef SLOW_HANDSHAKE
-	if (borken) {
+          if (borken)
+          {
 #if (DEBUG & (PHASE_DATAIN))
-		transfered += len;
+            transfered += len;
 #endif
-		for (; len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN |
-			STAT_REQ); --len) {
-				*data++ = DATA;
-				borken_wait();
-}
+            for (;
+                 len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN |
+                                                             STAT_REQ)
+                 ; --len)
+            {
+              *data++ = DATA;
+              borken_wait ();
+            }
 #if (DEBUG & (PHASE_DATAIN))
-		transfered -= len;
+            transfered -= len;
 #endif
-	} else
+          }
+          else
 #endif
 #ifdef FAST
-if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+            if (fast && transfersize && !(len % transfersize) &&
+                (len >= transfersize)
 #ifdef FAST32
-	&& !(transfersize % 4)
+                && !(transfersize % 4)
 #endif
-	) {
-#if (DEBUG & DEBUG_FAST) 
-	printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
-	       "         len = %d, data = %08x\n", hostno, SCint->underflow, 
-	       SCint->transfersize, len, data);
-#endif
-#warning This no longer works: rewrite in C and use readbwl/writebwl
-	__asm__("
-	cld;
-"
+            )
+          {
+#if (DEBUG & DEBUG_FAST)
+            printk ("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+                    "         len = %d, data = %08x\n",
+                  hostno, SCint->underflow, SCint->transfersize, len, data);
+#endif
+/* SJT: Start. Fast Read */
+#ifdef SEAGATE_USE_ASM
+            __asm__(
+                    "cld\n\t"
 #ifdef FAST32
-"	shr $2, %%ecx;
-1:	movl (%%esi), %%eax;
-	stosl;
-"
+                    "shr $2, %%ecx\n\t"
+                "1:\t"
+                    "movl (%%esi), %%eax\n\t"
+                    "stosl\n\t"
 #else
-"1:	movb (%%esi), %%al;
-	stosb;
-"
-#endif
-
-"	loop 1b;" : :
-	/* input */
-	"S" (st0x_dr), "D" (data), "c" (SCint->transfersize) :
-	/* clobbered */
-	"eax", "ecx", "edi");
-
-	len -= transfersize;
-	data += transfersize;
-
+                "1:\t"
+                    "movb (%%esi), %%al\n\t"
+                    "stosb\n\t"
+#endif
+                    "loop 1b\n\t"
+/* output */        : 
+/* input */         : "S" (phys_to_virt(st0x_dr)), "D" (data), "c" (SCint->transfersize) 
+/* clobbered */     : "eax", "ecx", "edi");
+#else /* SEAGATE_USE_ASM */
+            {
+#ifdef FAST32
+              const unsigned int *iop = phys_to_virt (st0x_dr);
+              unsigned int *dp = (unsigned int *) data;
+              int xferlen = len >> 2;
+#else
+              const unsigned char *iop = phys_to_virt (st0x_dr);
+              unsigned char *dp = data;
+              int xferlen = len;
+#endif
+              for (; xferlen; --xferlen)
+                *dp++ = *iop;
+            }
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End */
+            len -= transfersize;
+            data += transfersize;
 #if (DEBUG & PHASE_DATAIN)
-	printk("scsi%d: transfered += %d\n", hostno, transfersize);
-	transfered += transfersize;
+            printk ("scsi%d: transfered += %d\n", hostno, transfersize);
+            transfered += transfersize;
 #endif
 
 #if (DEBUG & DEBUG_FAST)
-	printk("scsi%d : FAST transfer complete len = %d data = %08x\n", 
-		hostno, len, data);
+            printk ("scsi%d : FAST transfer complete len = %d data = %08x\n",
+                    hostno, len, data);
 #endif
-
-} else
+          }
+          else
 #endif
-{
+          {
 
 #if (DEBUG & PHASE_DATAIN)
-	printk("scsi%d: transfered += %d\n", hostno, len);
-	transfered += len;	/* Assume we'll transfer it all, then
-				   subtract what we *didn't* transfer */
-#endif
-	
-/*
- * 	We loop as long as we are in a data in phase, there is room to read, 
- * 	and BSY is still active
- */
- 
-#warning This no longer works: rewrite in C and use readbwl/writebwl
-			__asm__ (
-/*
-	Local variables : 
-	ecx = len
-	edi = data
-	esi = st0x_cr_sr
-	ebx = st0x_dr
-
-	Test for room to read
-*/
-	"\torl %%ecx, %%ecx
-	jz 2f
-
-	cld
-	movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%esi
-	movl " SYMBOL_NAME_STR(st0x_dr) ", %%ebx
-
-1:	movb (%%esi), %%al\n"
-/*
-	Test for BSY
-*/
-
-	"\ttest $1, %%al 
-	jz 2f\n"
+            printk ("scsi%d: transfered += %d\n", hostno, len);
+            transfered += len;          /* Assume we'll transfer it all, then
+                                           subtract what we *didn't* transfer */
+#endif
 
 /*
-	Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN, = STAT_IO, which is 4.
-*/
-	"\tmovb $0xe, %%ah	
-	andb %%al, %%ah
-	cmpb $0x04, %%ah
-	jne 2f\n"
-		
-/*
-	Test for REQ
-*/	
-	"\ttest $0x10, %%al
-	jz 1b
-
-	movb (%%ebx), %%al	
-	stosb	
-	loop 1b\n"
-
-"2:\n"
-									:
-/* output */
-"=D" (data), "=c" (len) :
-/* input */
-"0" (data), "1" (len) :
-/* clobbered */
-"eax","ebx", "esi"); 
-
+ *    We loop as long as we are in a data in phase, there is room to read,
+ *      and BSY is still active
+ */
+
+/* SJT: Start. */
+#ifdef SEAGATE_USE_ASM
+/*
+ *      We loop as long as we are in a data in phase, there is room to read, 
+ *      and BSY is still active
+ */
+            /* Local variables : ecx = len, edi = data
+                                 esi = st0x_cr_sr, ebx = st0x_dr */
+            __asm__ (
+            /* Test for room to read */
+                "orl %%ecx, %%ecx\n\t"
+                "jz 2f\n\t"
+                "cld\n\t"
+/*                "movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%esi\n\t"  */
+/*                "movl " SYMBOL_NAME_STR(st0x_dr) ", %%ebx\n\t"  */
+            "1:\t"
+                "movb (%%esi), %%al\n\t"
+            /* Test for BSY */
+                "test $1, %%al\n\t"
+                "jz 2f\n\t"
+            /* Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN, 
+               = STAT_IO, which is 4. */
+                "movb $0xe, %%ah\n\t"      
+                "andb %%al, %%ah\n\t"
+                "cmpb $0x04, %%ah\n\t"
+                "jne 2f\n\t"
+            /* Test for REQ */      
+                "test $0x10, %%al\n\t"
+                "jz 1b\n\t"
+                "movb (%%ebx), %%al\n\t"      
+                "stosb\n\t"   
+                "loop 1b\n\t"
+            "2:\n"
+/* output */    : "=D" (data), "=c" (len) 
+/* input */     : "0" (data), "1" (len), "S" (phys_to_virt(st0x_cr_sr)), "b" (phys_to_virt(st0x_dr)) 
+/* clobbered */ : "eax","ebx", "esi"); 
+#else /* SEAGATE_USE_ASM */
+            while (len)
+            {
+              unsigned char stat;
+
+              stat = STATUS;
+              if (!(stat & STAT_BSY) || ((stat & REQ_MASK) != REQ_DATAIN))
+                break;
+              if (stat & STAT_REQ)
+              {
+                *data++ = DATA;
+                --len;
+              }
+            }
+#endif /* SEAGATE_USE_ASM */
+/* SJT: End. */
 #if (DEBUG & PHASE_DATAIN)
-	printk("scsi%d: transfered -= %d\n", hostno, len);
-	transfered -= len;		/* Since we assumed all of Len got 
-					 * transfered, correct our mistake */
-#endif
-}
-	
-			if (!len && nobuffs) {
-				--nobuffs;
-				++buffer;
-				len = buffer->length;
-				data = (unsigned char *) buffer->address;
+            printk ("scsi%d: transfered -= %d\n", hostno, len);
+            transfered -= len;          /* Since we assumed all of Len got  *
+                                           transfered, correct our mistake */
+#endif
+          }
+
+          if (!len && nobuffs)
+          {
+            --nobuffs;
+            ++buffer;
+            len = buffer->length;
+            data = (unsigned char *) buffer->address;
 #if (DEBUG & DEBUG_SG)
-	printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
-		hostno, len, data);
+            printk ("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+                    hostno, len, data);
 #endif
-			}
+          }
 
-			break;
+          break;
 
-		case REQ_CMDOUT : 
-			while (((status_read = STATUS) & STAT_BSY) && 
-			       ((status_read & REQ_MASK) == REQ_CMDOUT))
-				if (status_read & STAT_REQ) {
-					DATA = *(const unsigned char *) cmnd;
-					cmnd = 1+(const unsigned char *) cmnd;
+        case REQ_CMDOUT:
+          while (((status_read = STATUS) & STAT_BSY) &&
+                 ((status_read & REQ_MASK) == REQ_CMDOUT))
+            if (status_read & STAT_REQ)
+            {
+              WRITE_DATA (*(const unsigned char *) cmnd);
+              cmnd = 1 + (const unsigned char *) cmnd;
 #ifdef SLOW_HANDSHAKE
-					if (borken) 
-						borken_wait();
+              if (borken)
+                borken_wait ();
 #endif
-				}
-			break;
-	
-		case REQ_STATIN : 
-			status = DATA;
-			break;
-				
-		case REQ_MSGOUT : 
+            }
+          break;
+
+        case REQ_STATIN:
+          status = DATA;
+          break;
+
+        case REQ_MSGOUT:
 /*
- *	We can only have sent a MSG OUT if we requested to do this 
- *	by raising ATTN.  So, we must drop ATTN.
+ *    We can only have sent a MSG OUT if we requested to do this
+ *      by raising ATTN.  So, we must drop ATTN.
  */
 
-			CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+          WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE);
 /*
- * 	If we are reconnecting, then we must send an IDENTIFY message in 
- *	 response  to MSGOUT.
+ *    If we are reconnecting, then we must send an IDENTIFY message in
+ *       response  to MSGOUT.
  */
-			switch (reselect) {
-			case CAN_RECONNECT:
-				DATA = IDENTIFY(1, lun);
+          switch (reselect)
+          {
+            case CAN_RECONNECT:
+              WRITE_DATA (IDENTIFY (1, lun));
 
-#if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT)) 
-				printk("scsi%d : sent IDENTIFY message.\n", hostno);
+#if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT))
+              printk ("scsi%d : sent IDENTIFY message.\n", hostno);
 #endif
-				break;
+              break;
 #ifdef LINKED
-			case LINKED_WRONG:
-				DATA = ABORT;
-				linked_connected = 0;
-				reselect = CAN_RECONNECT;
-				goto connect_loop;
+            case LINKED_WRONG:
+              WRITE_DATA (ABORT);
+              linked_connected = 0;
+              reselect = CAN_RECONNECT;
+              goto connect_loop;
 #if (DEBUG & (PHASE_MSGOUT | DEBUG_LINKED))
-				printk("scsi%d : sent ABORT message to cancel incorrect I_T_L nexus.\n", hostno);
+              printk ("scsi%d : sent ABORT message to cancel incorrect I_T_L nexus.\n", hostno);
 #endif
 #endif /* LINKED */
-#if (DEBUG & DEBUG_LINKED) 
-	    printk("correct\n");
+#if (DEBUG & DEBUG_LINKED)
+              printk ("correct\n");
 #endif
-			default:
-				DATA = NOP;
-				printk("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target);
-			}
-			break;
-					
-		case REQ_MSGIN : 
-			switch (message = DATA) {
-			case DISCONNECT :
-				should_reconnect = 1;
-				current_data = data;    /* WDE add */
-				current_buffer = buffer;
-				current_bufflen = len;  /* WDE add */
-				current_nobuffs = nobuffs;
+            default:
+              WRITE_DATA (NOP);
+              printk ("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target);
+          }
+          break;
+
+        case REQ_MSGIN:
+          switch (message = DATA)
+          {
+            case DISCONNECT:
+              should_reconnect = 1;
+              current_data = data;      /* WDE add */
+              current_buffer = buffer;
+              current_bufflen = len;    /* WDE add */
+              current_nobuffs = nobuffs;
 #ifdef LINKED
-				linked_connected = 0;
+              linked_connected = 0;
 #endif
-				done=1;
+              done = 1;
 #if (DEBUG & (PHASE_RESELECT | PHASE_MSGIN))
-				printk("scsi%d : disconnected.\n", hostno);
+              printk ("scsi%d : disconnected.\n", hostno);
 #endif
-				break;
+              break;
 
 #ifdef LINKED
-			case LINKED_CMD_COMPLETE:
-			case LINKED_FLG_CMD_COMPLETE:
+            case LINKED_CMD_COMPLETE:
+            case LINKED_FLG_CMD_COMPLETE:
 #endif
-			case COMMAND_COMPLETE :
+            case COMMAND_COMPLETE:
 /*
- * Note : we should check for underflow here.   
+ * Note : we should check for underflow here.
  */
-#if (DEBUG & PHASE_MSGIN)	
-				printk("scsi%d : command complete.\n", hostno);
+#if (DEBUG & PHASE_MSGIN)
+              printk ("scsi%d : command complete.\n", hostno);
 #endif
-				done = 1;
-				break;
-			case ABORT :
+              done = 1;
+              break;
+            case ABORT:
 #if (DEBUG & PHASE_MSGIN)
-				printk("scsi%d : abort message.\n", hostno);
+              printk ("scsi%d : abort message.\n", hostno);
 #endif
-				done=1;
-				break;
-			case SAVE_POINTERS :
-				current_buffer = buffer;
-				current_bufflen = len;  /* WDE add */
-				current_data = data;	/* WDE mod */
-				current_nobuffs = nobuffs;
+              done = 1;
+              break;
+            case SAVE_POINTERS:
+              current_buffer = buffer;
+              current_bufflen = len;    /* WDE add */
+              current_data = data;      /* WDE mod */
+              current_nobuffs = nobuffs;
 #if (DEBUG & PHASE_MSGIN)
-				printk("scsi%d : pointers saved.\n", hostno);
-#endif 
-				break;
-			case RESTORE_POINTERS:
-				buffer=current_buffer;
-				cmnd=current_cmnd;
-				data=current_data;	/* WDE mod */
-				len=current_bufflen;
-				nobuffs=current_nobuffs;
+              printk ("scsi%d : pointers saved.\n", hostno);
+#endif
+              break;
+            case RESTORE_POINTERS:
+              buffer = current_buffer;
+              cmnd = current_cmnd;
+              data = current_data;      /* WDE mod */
+              len = current_bufflen;
+              nobuffs = current_nobuffs;
 #if (DEBUG & PHASE_MSGIN)
-				printk("scsi%d : pointers restored.\n", hostno);
+              printk ("scsi%d : pointers restored.\n", hostno);
 #endif
-				break;
-			default:
+              break;
+            default:
 
 /*
- * 	IDENTIFY distinguishes itself from the other messages by setting the
- *	high byte.
- * 	
- *	Note : we need to handle at least one outstanding command per LUN,
- *	and need to hash the SCSI command for that I_T_L nexus based on the 
- *	known ID (at this point) and LUN.
+ *    IDENTIFY distinguishes itself from the other messages by setting the
+ *      high byte.
+ *
+ *      Note : we need to handle at least one outstanding command per LUN,
+ *      and need to hash the SCSI command for that I_T_L nexus based on the
+ *      known ID (at this point) and LUN.
  */
 
-				if (message & 0x80) {
+              if (message & 0x80)
+              {
 #if (DEBUG & PHASE_MSGIN)
-					printk("scsi%d : IDENTIFY message received from id %d, lun %d.\n",
-						hostno, target, message & 7);
+                printk ("scsi%d : IDENTIFY message received from id %d, lun %d.\n",
+                        hostno, target, message & 7);
 #endif
-				} else {
+              }
+              else
+              {
 
 /*
- *      We should go into a MESSAGE OUT phase, and send  a MESSAGE_REJECT 
- * 	if we run into a message that we don't like.  The seagate driver 
- * 	needs some serious restructuring first though.
+ *      We should go into a MESSAGE OUT phase, and send  a MESSAGE_REJECT
+ *      if we run into a message that we don't like.  The seagate driver
+ *      needs some serious restructuring first though.
  */
 
 #if (DEBUG & PHASE_MSGIN)
-					printk("scsi%d : unknown message %d from target %d.\n",
-						hostno,  message,   target);
-#endif	
-				}
-			}
-			break;
-
-		default : 
-			printk("scsi%d : unknown phase.\n", hostno); 
-			st0x_aborted = DID_ERROR; 
-		}	
+                printk ("scsi%d : unknown message %d from target %d.\n",
+                        hostno, message, target);
+#endif
+              }
+          }
+          break;
+
+        default:
+          printk ("scsi%d : unknown phase.\n", hostno);
+          st0x_aborted = DID_ERROR;
+      }                                 /* end of switch (status_read &
+                                           REQ_MASK) */
 
 #ifdef SLOW_HANDSHAKE
 /*
- * I really don't care to deal with borken devices in each single 
+ * I really don't care to deal with borken devices in each single
  * byte transfer case (ie, message in, message out, status), so
  * I'll do the wait here if necessary.
  */
-		if (borken)
-			borken_wait();
+      if (borken)
+        borken_wait ();
 #endif
- 
-		} /* if ends */
-		} /* while ends */
+
+    }                                   /* if(status_read & STAT_REQ) ends */
+  }                                     /* while(((status_read = STATUS)...)
+                                           ends */
 
 #if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT))
-	printk("scsi%d : Transfered %d bytes\n", hostno, transfered);
+  printk ("scsi%d : Transfered %d bytes\n", hostno, transfered);
 #endif
 
 #if (DEBUG & PHASE_EXIT)
-#if 0		/* Doesn't work for scatter / gather */
-	printk("Buffer : \n");
-	for (i = 0; i < 20; ++i) 
-		printk ("%02x  ", ((unsigned char *) data)[i]);	/* WDE mod */
-	printk("\n");
-#endif
-	printk("scsi%d : status = ", hostno);
-	print_status(status);
-	printk("message = %02x\n", message);
+#if 0                                   /* Doesn't work for scatter/gather */
+  printk ("Buffer : \n");
+  for (i = 0; i < 20; ++i)
+    printk ("%02x  ", ((unsigned char *) data)[i]);     /* WDE mod */
+  printk ("\n");
+#endif
+  printk ("scsi%d : status = ", hostno);
+  print_status (status);
+  printk ("message = %02x\n", message);
 #endif
 
-
 /* We shouldn't reach this until *after* BSY has been deasserted */
 #ifdef notyet
-	if (st0x_aborted) {
-		if (STATUS & STAT_BSY) {	
-			seagate_st0x_reset(NULL);
-			st0x_aborted = DID_RESET;
-		} 
-		abort_confirm = 1;
-	} 
+  if (st0x_aborted)
+  {
+    if (STATUS & STAT_BSY)
+    {
+      seagate_st0x_reset (NULL);
+      st0x_aborted = DID_RESET;
+    }
+    abort_confirm = 1;
+  }
 #endif
 
 #ifdef LINKED
-else {
+  else
+  {
 /*
- * Fix the message byte so that unsuspecting high level drivers don't 
- * puke when they see a LINKED COMMAND message in place of the COMMAND 
- * COMPLETE they may be expecting.  Shouldn't be necessary, but it's 
- * better to be on the safe side. 
+ * Fix the message byte so that unsuspecting high level drivers don't
+ * puke when they see a LINKED COMMAND message in place of the COMMAND
+ * COMPLETE they may be expecting.  Shouldn't be necessary, but it's
+ * better to be on the safe side.
  *
- * A non LINKED* message byte will indicate that the command completed, 
+ * A non LINKED* message byte will indicate that the command completed,
  * and we are now disconnected.
  */
 
-		switch (message) {
-		case LINKED_CMD_COMPLETE :
-		case LINKED_FLG_CMD_COMPLETE : 
-			message = COMMAND_COMPLETE;
-			linked_target = current_target;
-			linked_lun = current_lun;
-			linked_connected = 1;
+    switch (message)
+    {
+      case LINKED_CMD_COMPLETE:
+      case LINKED_FLG_CMD_COMPLETE:
+        message = COMMAND_COMPLETE;
+        linked_target = current_target;
+        linked_lun = current_lun;
+        linked_connected = 1;
 #if (DEBUG & DEBUG_LINKED)
-			printk("scsi%d : keeping I_T_L nexus established for linked command.\n", 
-				hostno);
+        printk ("scsi%d : keeping I_T_L nexus established for linked command.\n",
+                hostno);
 #endif
+    /* We also will need to adjust status to accommodate intermediate
+       conditions. */
+        if ((status == INTERMEDIATE_GOOD) ||
+            (status == INTERMEDIATE_C_GOOD))
+          status = GOOD;
+
+        break;
 /*
- * We also will need to adjust status to accommodate intermediate conditions.
- */
-			if ((status == INTERMEDIATE_GOOD) ||
-				(status == INTERMEDIATE_C_GOOD))
-				status = GOOD;
-			
-			break;
-/*
- * We should also handle what are "normal" termination messages 
- * here (ABORT, BUS_DEVICE_RESET?, and COMMAND_COMPLETE individually, 
+ * We should also handle what are "normal" termination messages
+ * here (ABORT, BUS_DEVICE_RESET?, and COMMAND_COMPLETE individually,
  * and flake if things aren't right.
  */
-
-		default :
+      default:
 #if (DEBUG & DEBUG_LINKED)
-			printk("scsi%d : closing I_T_L nexus.\n", hostno);
+        printk ("scsi%d : closing I_T_L nexus.\n", hostno);
 #endif
-			linked_connected = 0;
-		}
-	}
+        linked_connected = 0;
+    }
+  }
 #endif /* LINKED */
 
-
-
-
-	if (should_reconnect) {
+  if (should_reconnect)
+  {
 #if (DEBUG & PHASE_RESELECT)
-		printk("scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n",
-			hostno);
+    printk ("scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n",
+            hostno);
 #endif
-		CONTROL = BASE_CMD | CMD_INTR ;
-	} else 
-		CONTROL = BASE_CMD;
+    WRITE_CONTROL (BASE_CMD | CMD_INTR);
+  }
+  else
+    WRITE_CONTROL (BASE_CMD);
 
-	return retcode (st0x_aborted);
-	}
+  return retcode (st0x_aborted);
+}                                       /* end of internal_command */
 
 int seagate_st0x_abort (Scsi_Cmnd * SCpnt)
-	{
-	  st0x_aborted = DID_ABORT;
-	  
-	  return SCSI_ABORT_PENDING;
-	}
+{
+  st0x_aborted = DID_ABORT;
+  return SCSI_ABORT_PENDING;
+}
 
 /*
-	the seagate_st0x_reset function resets the SCSI bus
-*/
-	
+   the seagate_st0x_reset function resets the SCSI bus */
+
 int seagate_st0x_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags)
-	{
-	unsigned clock;
-	/*
-		No timeouts - this command is going to fail because 
-		it was reset.
-	*/
+{
+  unsigned clock;
 
+/* No timeouts - this command is going to fail because it was reset. */
 #ifdef DEBUG
-	printk("In seagate_st0x_reset()\n");
+  printk ("In seagate_st0x_reset()\n");
 #endif
 
+/* assert  RESET signal on SCSI bus.  */
+  WRITE_CONTROL (BASE_CMD | CMD_RST);
+  clock = jiffies + 2;
 
-	/* assert  RESET signal on SCSI bus.  */
-		
-	CONTROL = BASE_CMD  | CMD_RST;
-	clock=jiffies+2;
-
-	
-	/* Wait.  */
-	
-	while (jiffies < clock);
+/* Wait.  */
+  while (jiffies < clock) ;
 
-	CONTROL = BASE_CMD;
-	
-	st0x_aborted = DID_RESET;
+  WRITE_CONTROL (BASE_CMD);
+  st0x_aborted = DID_RESET;
 
 #ifdef DEBUG
-	printk("SCSI bus reset.\n");
+  printk ("SCSI bus reset.\n");
 #endif
-	return SCSI_RESET_WAKEUP;
-	}
+  return SCSI_RESET_WAKEUP;
+}
 
-#include <asm/uaccess.h>
-#include "sd.h"
-#include <scsi/scsi_ioctl.h>
 
-int seagate_st0x_biosparam(Disk * disk, kdev_t dev, int* ip) {
-  unsigned char buf[256 + sizeof (Scsi_Ioctl_Command)], cmd[6], *data, *page;
+int seagate_st0x_biosparam (Disk * disk, kdev_t dev, int *ip)
+{
+  unsigned char buf[256 + sizeof (Scsi_Ioctl_Command)], 
+                cmd[6], *data, *page;
   Scsi_Ioctl_Command *sic = (Scsi_Ioctl_Command *) buf;
   int result, formatted_sectors, total_sectors;
   int cylinders, heads, sectors;
   int capacity;
 
 /*
- * Only SCSI-I CCS drives and later implement the necessary mode sense 
- * pages.  
+ * Only SCSI-I CCS drives and later implement the necessary mode sense
+ * pages.
  */
 
-  if (disk->device->scsi_level < 2) 
-	return -1;
+  if (disk->device->scsi_level < 2)
+    return -1;
 
   data = sic->data;
 
   cmd[0] = MODE_SENSE;
   cmd[1] = (disk->device->lun << 5) & 0xe5;
-  cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */
+  cmd[2] = 0x04;                        /* Read page 4, rigid disk geometry
+                                           page current values */
   cmd[3] = 0;
   cmd[4] = 255;
   cmd[5] = 0;
@@ -1665,88 +1749,90 @@
  * We are transferring 0 bytes in the out direction, and expect to get back
  * 24 bytes for each mode page.
  */
-
   sic->inlen = 0;
   sic->outlen = 256;
 
   memcpy (data, cmd, 6);
 
-  if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, sic))) {
+  if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND,
+                                    sic)))
+  {
 /*
- * The mode page lies beyond the MODE SENSE header, with length 4, and 
+ * The mode page lies beyond the MODE SENSE header, with length 4, and
  * the BLOCK DESCRIPTOR, with length header[3].
  */
-
     page = data + 4 + data[3];
     heads = (int) page[5];
     cylinders = (page[2] << 16) | (page[3] << 8) | page[4];
 
-    cmd[2] = 0x03; /* Read page 3, format page current values */
+    cmd[2] = 0x03;                      /* Read page 3, format page current
+                                           values */
     memcpy (data, cmd, 6);
 
-    if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, sic))) {
+    if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND,
+                                      sic)))
+    {
       page = data + 4 + data[3];
-      sectors = (page[10] << 8) | page[11];	
-
-	
+      sectors = (page[10] << 8) | page[11];
 /*
- * Get the total number of formatted sectors from the block descriptor, 
- * so we can tell how many are being used for alternates.  
+ * Get the total number of formatted sectors from the block descriptor,
+ * so we can tell how many are being used for alternates.
  */
-
-      formatted_sectors = (data[4 + 1] << 16) | (data[4 + 2] << 8) |
-	data[4 + 3] ;
+      formatted_sectors = (data[4 + 1] << 16) | (data[4 + 2] << 8) 
+                          | data[4 + 3];
 
       total_sectors = (heads * cylinders * sectors);
 
 /*
- * Adjust the real geometry by subtracting 
+ * Adjust the real geometry by subtracting
  * (spare sectors / (heads * tracks)) cylinders from the number of cylinders.
  *
  * It appears that the CE cylinder CAN be a partial cylinder.
  */
 
-     
-printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n",
-    hostno, heads, cylinders, sectors, total_sectors, formatted_sectors);
+      printk ("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n",
+              hostno, heads, cylinders, sectors, total_sectors,
+              formatted_sectors);
 
       if (!heads || !sectors || !cylinders)
-	result = -1;
+        result = -1;
       else
-	cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
+        cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
 
 /*
- * Now, we need to do a sanity check on the geometry to see if it is 
- * BIOS compatible.  The maximum BIOS geometry is 1024 cylinders * 
- * 256 heads * 64 sectors. 
- */
-
-      if ((cylinders > 1024) || (sectors > 64)) {
-	/* The Seagate's seem to have some mapping
-	 * Multiple heads * sectors * cyl to get capacity
-	 * Then start rounding down. */
-	capacity = heads * sectors * cylinders;
-	sectors = 17;	/* Old MFM Drives use this, so does the Seagate */
-	heads = 2;
-	capacity = capacity / sectors;
-	while (cylinders > 1024)
-	{
-		heads *= 2;	/* For some reason, they go in multiples */
-		cylinders = capacity / heads;
-	}
+ * Now, we need to do a sanity check on the geometry to see if it is
+ * BIOS compatible.  The maximum BIOS geometry is 1024 cylinders *
+ * 256 heads * 64 sectors.
+ */
+
+      if ((cylinders > 1024) || (sectors > 64))
+      {
+        /* The Seagate's seem to have some mapping.  Multiply
+           heads*sectors*cyl to get capacity.  Then start rounding down.
+         */
+        capacity = heads * sectors * cylinders;         
+      
+        /* Old MFM Drives use this, so does the Seagate */
+        sectors = 17;
+        heads = 2;
+        capacity = capacity / sectors;
+        while (cylinders > 1024)
+        {
+          heads *= 2;                   /* For some reason, they go in
+                                           multiples */
+          cylinders = capacity / heads;
+        }
       }
       ip[0] = heads;
       ip[1] = sectors;
       ip[2] = cylinders;
-
-/* 
+/*
  * There should be an alternate mapping for things the seagate doesn't
  * understand, but I couldn't say what it is with reasonable certainty.
  */
-
-      }
     }
-    
+  }
+
   return result;
 }
 
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v2.1.14/linux/drivers/scsi/sr.c	Sat Nov 30 12:03:08 1996
+++ linux/drivers/scsi/sr.c	Thu Dec 12 16:54:19 1996
@@ -315,7 +315,7 @@
 		}
 
 		if (SCpnt->sense_buffer[2] == NOT_READY) {
-			printk("CD-ROM not ready.  Make sure you have a disc in the drive.\n");
+			printk(KERN_INFO "CD-ROM not ready.  Make sure you have a disc in the drive.\n");
 			SCpnt = end_scsi_request(SCpnt, 0, this_count);
 			requeue_sr_request(SCpnt); /* Do next request */
 			return;
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v2.1.14/linux/drivers/scsi/sr_ioctl.c	Thu Dec 12 17:02:44 1996
+++ linux/drivers/scsi/sr_ioctl.c	Thu Dec 12 16:54:19 1996
@@ -63,7 +63,7 @@
 	    printk("Disc change detected.\n");
 	    break;
 	case NOT_READY: /* This happens if there is no disc in drive */
-	    printk("CDROM not ready.  Make sure there is a disc in the drive.\n");
+	    printk(KERN_INFO "CDROM not ready.  Make sure there is a disc in the drive.\n");
 	    break;
 	case ILLEGAL_REQUEST:
 	    printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/u14-34f.c linux/drivers/scsi/u14-34f.c
--- v2.1.14/linux/drivers/scsi/u14-34f.c	Thu Dec 12 17:02:44 1996
+++ linux/drivers/scsi/u14-34f.c	Wed Dec 11 16:26:38 1996
@@ -1,7 +1,10 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
- *      21 Nov 1996 rev. 2.30 for linux 2.1.11 and 2.0.25
+ *       3 Dec 1996 rev. 2.40 for linux 2.1.14 and 2.0.27
+ *          Added queue depth adjustment.
+ *
+ *      22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
  *          The list of i/o ports to be probed can be overwritten by the
  *          "u14-34f=port0, port1,...." boot command line option.
  *          Scatter/gather lists are now allocated by a number of kmalloc
@@ -112,8 +115,8 @@
  *
  *  Here a sample configuration using two U14F boards:
  *
- U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, Mbox 16, CmdLun 2, C1.
- U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, Mbox 16, CmdLun 2, C1.
+ U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, Mbox 16, UC 1.
+ U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, Mbox 16, UC 1.
  *
  *  The boot controller must have its BIOS enabled, while other boards can
  *  have their BIOS disabled, or enabled to an higher address.
@@ -239,6 +242,7 @@
 #define MAX_SAFE_SGLIST 16
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
+#define MAX_TAGGED_CMD_PER_LUN 8
 
 #define FALSE 0
 #define TRUE 1
@@ -291,8 +295,8 @@
    unsigned char lun: 3;                /* SCSI logical unit number */
    unsigned int data_address PACKED;    /* transfer data pointer */
    unsigned int data_len PACKED;        /* length in bytes */
-   unsigned int command_link PACKED;    /* for linking command chains */
-   unsigned char scsi_command_link_id;  /* identifies command in chain */
+   unsigned int link_address PACKED;    /* for linking command chains */
+   unsigned char link_id;               /* identifies command in chain */
    unsigned char use_sg;                /* (if sg is set) 8 bytes per list */
    unsigned char sense_len;
    unsigned char scsi_cdbs_len;         /* 6, 10, or 12 */
@@ -341,12 +345,14 @@
 #define HD(board) ((struct hostdata *) &sh[board]->hostdata)
 #define BN(board) (HD(board)->board_name)
 
-#if defined(__BIG_ENDIAN)
-#define H2DEV(x) ((unsigned long)( \
+#define SWAP_BYTE(x) ((unsigned long)( \
 	(((unsigned long)(x) & 0x000000ffU) << 24) | \
 	(((unsigned long)(x) & 0x0000ff00U) <<  8) | \
 	(((unsigned long)(x) & 0x00ff0000U) >>  8) | \
 	(((unsigned long)(x) & 0xff000000U) >> 24)))
+
+#if defined(__BIG_ENDIAN)
+#define H2DEV(x) SWAP_BYTE(x)
 #else
 #define H2DEV(x) (x)
 #endif
@@ -359,6 +365,52 @@
 static int do_trace = FALSE;
 static int setup_done = FALSE;
 
+static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
+   Scsi_Device *dev;
+   int j, ntag = 0, nuntag = 0, tqd, utqd; 
+   unsigned long flags;
+
+   save_flags(flags);
+   cli();
+
+   j = ((struct hostdata *) host->hostdata)->board_number;
+
+   for(dev = devlist; dev; dev = dev->next) {
+
+      if (dev->host != host) continue;
+
+      if (dev->tagged_supported) ntag++;
+      else                       nuntag++;
+      }
+
+   utqd = MAX_CMD_PER_LUN;
+
+   tqd = (host->can_queue - utqd * nuntag) / (ntag + 1);
+
+   if (tqd > MAX_TAGGED_CMD_PER_LUN) tqd = MAX_TAGGED_CMD_PER_LUN;
+
+   if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
+
+   for(dev = devlist; dev; dev = dev->next) {
+      char *tag_suffix = "";
+
+      if (dev->host != host) continue;
+
+      if (dev->tagged_supported) dev->queue_depth = tqd;
+      else                       dev->queue_depth = utqd;
+
+      if (dev->tagged_supported && dev->tagged_queue) tag_suffix = ", tagged";
+      else if (dev->tagged_supported) tag_suffix = ", untagged";
+
+      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s.\n",
+             BN(j), host->host_no, dev->channel, dev->id, dev->lun,
+             dev->queue_depth, tag_suffix);
+      }
+
+   restore_flags(flags);
+   return;
+}
+
 static inline int wait_on_busy(unsigned int iobase) {
    unsigned int loop = MAXLOOP;
 
@@ -415,6 +467,7 @@
                               Scsi_Host_Template *tpnt) {
    unsigned char irq, dma_channel, subversion, i;
    unsigned char in_byte;
+   char dma_name[16];
 
    /* Allowed BIOS base addresses (NULL indicates reserved) */
    void *bios_segment_table[8] = { 
@@ -508,6 +561,7 @@
    sh[j]->this_id = config_2.ha_scsi_id;
    sh[j]->can_queue = MAX_MAILBOXES;
    sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+   sh[j]->select_queue_depths = select_queue_depths;
 
 #if defined(DEBUG_DETECT)
    {
@@ -575,6 +629,9 @@
 	 }
       }
 
+   if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "NO DMA");
+   else                       sprintf(dma_name, "DMA %u", dma_channel);
+
    for (i = 0; i < sh[j]->can_queue; i++)
       if (! ((&HD(j)->cp[i])->sglist = kmalloc(
             sh[j]->sg_tablesize * sizeof(struct sg_list), 
@@ -584,11 +641,10 @@
          return FALSE;
          }
       
-   printk("%s: PORT 0x%03x, BIOS 0x%05x, IRQ %u, DMA %u, SG %d, "\
-	  "Mbox %d, CmdLun %d, C%d.\n", BN(j), sh[j]->io_port, 
-	  (int)sh[j]->base, sh[j]->irq, sh[j]->dma_channel,
-          sh[j]->sg_tablesize, sh[j]->can_queue, sh[j]->cmd_per_lun,
-	  sh[j]->hostt->use_clustering);
+   printk("%s: PORT 0x%03x, BIOS 0x%05x, IRQ %u, %s, SG %d, "\
+	  "Mbox %d, UC %d.\n", BN(j), sh[j]->io_port,
+          (int)sh[j]->base, sh[j]->irq, dma_name, sh[j]->sg_tablesize,
+          sh[j]->can_queue, sh[j]->hostt->use_clustering);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -1135,7 +1191,7 @@
 	 if ((spp->adapter_status != ASOK && HD(j)->iocount >  1000) ||
 	     (spp->adapter_status != ASOK && 
 	      spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
-	     do_trace)
+              do_trace || msg_byte(spp->target_status))
 #endif
 	    printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
 		   " target %d.%d:%d, pid %ld, count %d.\n",
diff -u --recursive --new-file v2.1.14/linux/drivers/scsi/u14-34f.h linux/drivers/scsi/u14-34f.h
--- v2.1.14/linux/drivers/scsi/u14-34f.h	Thu Dec 12 17:02:44 1996
+++ linux/drivers/scsi/u14-34f.h	Wed Dec 11 16:26:38 1996
@@ -11,7 +11,7 @@
 int u14_34f_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "2.30.00"
+#define U14_34F_VERSION "2.40.00"
 
 #define ULTRASTOR_14_34F {                                            \
 		NULL, /* Ptr for modules */                           \
diff -u --recursive --new-file v2.1.14/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.14/linux/fs/buffer.c	Fri Nov 15 23:49:09 1996
+++ linux/fs/buffer.c	Thu Dec 12 16:54:19 1996
@@ -17,6 +17,7 @@
  */
  
 /* Some bdflush() changes for the dynamic ramdisk - Paul Gortmaker, 12/94 */
+/* Start bdflush() with kernel_thread not syscall - Paul Gortmaker, 12/95 */
 
 #include <linux/sched.h>
 #include <linux/kernel.h>
diff -u --recursive --new-file v2.1.14/linux/fs/ext2/file.c linux/fs/ext2/file.c
--- v2.1.14/linux/fs/ext2/file.c	Tue Nov 12 15:56:11 1996
+++ linux/fs/ext2/file.c	Sun Dec  8 21:22:48 1996
@@ -120,7 +120,7 @@
 
 	/* was any of the uid bits set? */
 	mode &= inode->i_mode;
-	if (mode && suser()) {
+	if (mode && !suser()) {
 		inode->i_mode &= ~mode;
 		inode->i_dirt = 1;
 	}
diff -u --recursive --new-file v2.1.14/linux/fs/namei.c linux/fs/namei.c
--- v2.1.14/linux/fs/namei.c	Tue Oct 29 19:58:43 1996
+++ linux/fs/namei.c	Thu Dec 12 16:54:19 1996
@@ -48,25 +48,42 @@
 	return retval;
 }
 
+/*
+ * This is a single page for faster getname.
+ *   If the page is available when entering getname, use it.
+ *   If the page is not available, call __get_free_page instead.
+ * This works even though do_getname can block (think about it).
+ * -- Michael Chastain, based on idea of Linus Torvalds, 1 Dec 1996.
+ */
+static unsigned long name_page_cache = 0;
+
 int getname(const char * filename, char **result)
 {
 	unsigned long page;
 	int retval;
 
-	page  = __get_free_page(GFP_KERNEL);
-	retval = -ENOMEM;
-	if (page) {
-		*result = (char *)page;
-		retval = do_getname(filename, (char *) page);
-		if (retval < 0)
-			free_page(page);
+	page = name_page_cache;
+	name_page_cache = 0;
+	if (!page) {
+		page = __get_free_page(GFP_KERNEL);
+		if (!page)
+			return -ENOMEM;
 	}
+
+	retval = do_getname(filename, (char *) page);
+	if (retval < 0)
+		putname( (char *) page );
+	else
+		*result = (char *) page;
 	return retval;
 }
 
 void putname(char * name)
 {
-	free_page((unsigned long) name);
+	if (name_page_cache == 0)
+		name_page_cache = (unsigned long) name;
+	else
+		free_page((unsigned long) name);
 }
 
 /*
diff -u --recursive --new-file v2.1.14/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c
--- v2.1.14/linux/fs/ncpfs/sock.c	Tue Oct 29 19:58:43 1996
+++ linux/fs/ncpfs/sock.c	Thu Dec 12 16:54:19 1996
@@ -18,6 +18,7 @@
 #include <linux/net.h>
 #include <linux/mm.h>
 #include <linux/netdevice.h>
+#include <net/scm.h>
 #include <linux/ipx.h>
 
 #include <linux/ncp.h>
@@ -29,43 +30,62 @@
 #define _S(nr) (1<<((nr)-1))
 static int _recvfrom(struct socket *sock, unsigned char *ubuf,
 		     int size, int noblock, unsigned flags,
-		     struct sockaddr_ipx *sa, int *addr_len)
+		     struct sockaddr_ipx *sa)
 {
         struct iovec iov;
         struct msghdr msg;
+	struct scm_cookie scm;
+
+	memset(&scm, 0, sizeof(scm));
 
         iov.iov_base = ubuf;
         iov.iov_len  = size;
 
         msg.msg_name      = (void *)sa;
         msg.msg_namelen   = 0;
-        if (addr_len)
-                msg.msg_namelen = *addr_len;
+        if (sa)
+                msg.msg_namelen = sizeof(struct sockaddr_ipx);
         msg.msg_control = NULL;
         msg.msg_iov       = &iov;
         msg.msg_iovlen    = 1;
+	if (noblock) {
+		flags |= MSG_DONTWAIT;
+	}
 
-        return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
+        return sock->ops->recvmsg(sock, &msg, size, flags, &scm);
 }
 
 static int _sendto(struct socket *sock, const void *buff,
-		   int len, int nonblock, unsigned flags,
-		   struct sockaddr_ipx *sa, int addr_len)
+		   int len, int noblock, unsigned flags,
+		   struct sockaddr_ipx *sa)
 
 {
         struct iovec iov;
         struct msghdr msg;
+	struct scm_cookie scm;
+	int err;
 
         iov.iov_base = (void *)buff;
         iov.iov_len  = len;
 
         msg.msg_name      = (void *)sa;
-        msg.msg_namelen   = addr_len;
+        msg.msg_namelen   = sizeof(struct sockaddr_ipx);
         msg.msg_control = NULL;
         msg.msg_iov       = &iov;
         msg.msg_iovlen    = 1;
 
-        return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+	if (noblock) {
+		flags |= MSG_DONTWAIT;
+	}
+
+	msg.msg_flags = flags;
+
+	err = scm_send(sock, &msg, &scm);
+	if (err < 0)
+		return err;
+        err = sock->ops->sendmsg(sock, &msg, len, &scm);
+	scm_destroy(&scm);
+	return err;
 }
 
 
@@ -78,7 +98,6 @@
 	{
 		unsigned char packet_buf[2];
 		struct sockaddr_ipx sender;
-		int addr_len = sizeof(struct sockaddr_ipx);
 		int result;
 		unsigned short fs;
 
@@ -86,7 +105,7 @@
 		set_fs(get_ds());
 
 		result = _recvfrom(sock, (void *)packet_buf, 2, 1, 0,
-				   &sender, &addr_len);
+				   &sender);
 
 		if (   (result != 2)
 		    || (packet_buf[1] != '?')
@@ -111,7 +130,7 @@
 
 			packet_buf[1] = 'Y';
 			result = _sendto(sock, (void *)packet_buf, 2, 1, 0,
-					 &sender, sizeof(sender));
+					 &sender);
 			DDPRINTK("send result: %d\n", result);
 		}
 		set_fs(fs);
@@ -145,7 +164,7 @@
                 return -EINVAL;
         }
 
-        sk   = (struct sock *)(sock->data);
+        sk   = sock->sk;
 
         if (sk == NULL)
 	{
@@ -196,7 +215,7 @@
                 return -EINVAL;
         }
 
-        sk = (struct sock *)(sock->data);
+        sk = sock->sk;
 
         if (sk == NULL)
 	{
@@ -237,7 +256,6 @@
 	{
 		unsigned char packet_buf[2];
 		struct sockaddr_ipx sender;
-		int addr_len = sizeof(struct sockaddr_ipx);
 		int result;
 		unsigned short fs;
 
@@ -245,7 +263,7 @@
 		set_fs(get_ds());
 
 		result = _recvfrom(sock, (void *)packet_buf, 2, 1, 0,
-				   &sender, &addr_len);
+				   &sender);
 
 		DPRINTK("ncpfs: got message of size %d from:\n", result);
 		DPRINTK("ncpfs: %08lX:%02X%02X%02X%02X%02X%02X:%04X,"
@@ -288,7 +306,7 @@
                 return -EINVAL;
         }
 
-        sk = (struct sock *)(sock->data);
+        sk = sock->sk;
 
         if (sk == NULL)
 	{
@@ -333,7 +351,6 @@
 	int acknowledge_seen;
 	char *server_name;
 	int n;
-	int addrlen;
 	unsigned long old_mask;
 
 	/* We have to check the result, so store the complete header */
@@ -391,8 +408,7 @@
 			 request.function);
 
 		result = _sendto(sock, (void *) start, size, 0, 0,
-				 &(server->m.serv_addr),
-				 sizeof(server->m.serv_addr));
+				 &(server->m.serv_addr));
 		if (result < 0)
 		{
 			printk("ncp_rpc_call: send error = %d\n", result);
@@ -455,14 +471,12 @@
 		else if (wait_table.nr)
 			remove_wait_queue(entry.wait_address, &entry.wait);
 		current->state = TASK_RUNNING;
-		addrlen = 0;
 
 		/* Get the header from the next packet using a peek, so keep it
 		 * on the recv queue.  If it is wrong, it will be some reply
 		 * we don't now need, so discard it */
 		result = _recvfrom(sock, (void *)&reply,
-				   sizeof(reply), 1, MSG_PEEK,
-				   NULL, &addrlen);
+				   sizeof(reply), 1, MSG_PEEK, NULL);
 		if (result < 0)
 		{
 			if (result == -EAGAIN)
@@ -488,7 +502,7 @@
 			/* Throw away the packet */
 			DPRINTK("ncp_rpc_call: got positive acknowledge\n");
 			_recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0,
-				  NULL, &addrlen);
+				  NULL);
 			n = 0;
 			timeout = max_timeout;
 			acknowledge_seen = 1;
@@ -518,8 +532,7 @@
 		 * we have xid mismatch, so discard the packet and start
 		 * again.  What a hack! but I can't call recvfrom with
 		 * a null buffer yet. */
-		_recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0, NULL,
-			  &addrlen);
+		_recvfrom(sock, (void *)&reply, sizeof(reply), 1, 0, NULL);
 
 		DPRINTK("ncp_rpc_call: reply mismatch\n");
 		goto re_select;
@@ -529,7 +542,7 @@
 	 * return it
 	 */
 	result = _recvfrom(sock, (void *)start, server->packet_size,
-			   1, 0, NULL, &addrlen);
+			   1, 0, NULL);
 	if (result < 0)
 	{
 		printk("NCP: notice message: result=%d\n", result);
@@ -538,7 +551,7 @@
 	{
 		printk("NCP: just caught a too small read memory size..., "
 		       "email to NET channel\n");
-		printk("NCP: result=%d,addrlen=%d\n", result, addrlen);
+		printk("NCP: result=%d\n", result);
 		result = -EIO;
 	}
 
diff -u --recursive --new-file v2.1.14/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c
--- v2.1.14/linux/fs/nfs/rpcsock.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/nfs/rpcsock.c	Thu Dec 12 16:54:19 1996
@@ -116,10 +116,12 @@
 	msg.msg_name	= sap;
 	msg.msg_namelen = salen;
 	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = 0;
 
 	oldfs = get_fs();
 	set_fs(get_ds());
-	result = sock->ops->sendmsg(sock, &msg, len, 0, 0);
+	result = sock_sendmsg(sock, &msg, len);
 	set_fs(oldfs);
 
 	dprintk("RPC: rpc_sendmsg(iov %p, len %d) = %d\n", iov, len, result);
@@ -136,17 +138,19 @@
 	struct sockaddr	sa;
 	struct msghdr	msg;
 	unsigned long	oldfs;
-	int		result, alen;
+	int		result;
 
 	msg.msg_iov	= iov;
 	msg.msg_iovlen	= nr;
 	msg.msg_name	= &sa;
 	msg.msg_namelen = sizeof(sa);
 	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_flags = 0;
 
 	oldfs = get_fs();
 	set_fs(get_ds());
-	result = sock->ops->recvmsg(sock, &msg, len, 1, flags, &alen);
+	result = sock_recvmsg(sock, &msg, len, flags|MSG_DONTWAIT);
 	set_fs(oldfs);
 
 	dprintk("RPC: rpc_recvmsg(iov %p, len %d) = %d\n", iov, len, result);
@@ -302,7 +306,7 @@
 rpc_send(struct rpc_sock *rsock, struct rpc_wait *slot)
 {
 	struct rpc_ioreq *req = slot->w_req;
-	struct iovec	iov[UIO_MAXIOV];
+	struct iovec	iov[UIO_FASTIOV];
 
 	if (rsock->shutdown)
 		return -EIO;
@@ -336,7 +340,7 @@
 {
 	struct rpc_wait	*rovr;
 	struct rpc_ioreq *req;
-	struct iovec	iov[UIO_MAXIOV];
+	struct iovec	iov[UIO_FASTIOV];
 	u32		xid;
 	int		safe, result;
 
@@ -537,7 +541,7 @@
 		printk(KERN_WARNING "RPC: only UDP sockets supported\n");
 		return NULL;
 	}
-	sk = (struct sock *) sock->data;
+	sk = sock->sk;
 
 	if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL)
 		return NULL;
diff -u --recursive --new-file v2.1.14/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.14/linux/fs/proc/root.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/proc/root.c	Thu Dec 12 16:54:19 1996
@@ -139,6 +139,16 @@
 	NULL, &proc_root, NULL
 };
 
+#ifdef CONFIG_MCA
+struct proc_dir_entry proc_mca = {
+	PROC_MCA, 3, "mca",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,
+	0, &proc_dir_inode_operations,
+	NULL, NULL,
+	NULL, &proc_root, NULL
+};
+#endif
+
 struct proc_dir_entry proc_sys_root = {
 	PROC_SYS, 3, "sys",			/* inode, name */
 	S_IFDIR | S_IRUGO | S_IXUGO, 2, 0, 0,	/* mode, nlink, uid, gid */
@@ -379,6 +389,9 @@
 	proc_register(&proc_root, &proc_net);
 	proc_register(&proc_root, &proc_scsi);
 	proc_register(&proc_root, &proc_sys_root);
+#ifdef CONFIG_MCA
+	proc_register(&proc_root, &proc_mca);
+#endif
 
 #ifdef CONFIG_DEBUG_MALLOC
 	proc_register(&proc_root, &proc_root_malloc);
diff -u --recursive --new-file v2.1.14/linux/fs/read_write.c linux/fs/read_write.c
--- v2.1.14/linux/fs/read_write.c	Tue Oct 29 19:58:44 1996
+++ linux/fs/read_write.c	Thu Dec 12 16:54:19 1996
@@ -13,6 +13,7 @@
 #include <linux/file.h>
 #include <linux/mm.h>
 #include <linux/uio.h>
+#include <linux/malloc.h>
 
 #include <asm/uaccess.h>
 
@@ -166,41 +167,14 @@
 	return error;
 }
 
-static long sock_readv_writev(int type, struct inode * inode, struct file * file,
-	const struct iovec * iov, long count, long size)
-{
-	struct msghdr msg;
-	struct socket *sock;
-
-	sock = &inode->u.socket_i;
-	if (!sock->ops)
-		return -EOPNOTSUPP;
-	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
-	msg.msg_control = NULL;
-	msg.msg_iov = (struct iovec *) iov;
-	msg.msg_iovlen = count;
-
-	/* read() does a VERIFY_WRITE */
-	if (type == VERIFY_WRITE) {
-		if (!sock->ops->recvmsg)
-			return -EOPNOTSUPP;
-		return sock->ops->recvmsg(sock, &msg, size,
-			(file->f_flags & O_NONBLOCK), 0, NULL);
-	}
-	if (!sock->ops->sendmsg)
-		return -EOPNOTSUPP;
-	return sock->ops->sendmsg(sock, &msg, size,
-		(file->f_flags & O_NONBLOCK), 0);
-}
-
 typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long);
 
 static long do_readv_writev(int type, struct inode * inode, struct file * file,
 	const struct iovec * vector, unsigned long count)
 {
 	unsigned long tot_len;
-	struct iovec iov[UIO_MAXIOV];
+	struct iovec iovstack[UIO_FASTIOV];
+	struct iovec *iov=iovstack;
 	long retval, i;
 	IO_fn_t fn;
 
@@ -212,27 +186,46 @@
 		return 0;
 	if (count > UIO_MAXIOV)
 		return -EINVAL;
-	if (copy_from_user(iov, vector, count*sizeof(*vector)))
+	if (count > UIO_FASTIOV) {
+		iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL);
+		if (!iov)
+			return -ENOMEM;
+	}
+	if (copy_from_user(iov, vector, count*sizeof(*vector))) {
+		if (iov != iovstack)
+			kfree(iov);
 		return -EFAULT;
+	}
 	tot_len = 0;
 	for (i = 0 ; i < count ; i++)
 		tot_len += iov[i].iov_len;
 
 	retval = locks_verify_area(type == VERIFY_READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
 				   inode, file, file->f_pos, tot_len);
-	if (retval)
+	if (retval) {
+		if (iov != iovstack)
+			kfree(iov);
 		return retval;
+	}
 
 	/*
 	 * Then do the actual IO.  Note that sockets need to be handled
 	 * specially as they have atomicity guarantees and can handle
 	 * iovec's natively
 	 */
-	if (inode->i_sock)
-		return sock_readv_writev(type, inode, file, iov, count, tot_len);
+	if (inode->i_sock) {
+		int err;
+		err = sock_readv_writev(type, inode, file, iov, count, tot_len);
+		if (iov != iovstack)
+			kfree(iov);
+		return err;
+	}
 
-	if (!file->f_op)
+	if (!file->f_op) {
+		if (iov != iovstack)
+			kfree(iov);
 		return -EINVAL;
+	}
 	/* VERIFY_WRITE actually means a read, as we write to user space */
 	fn = file->f_op->read;
 	if (type == VERIFY_READ)
@@ -257,6 +250,8 @@
 		if (nr != len)
 			break;
 	}
+	if (iov != iovstack)
+		kfree(iov);
 	return retval;
 }
 
diff -u --recursive --new-file v2.1.14/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- v2.1.14/linux/fs/smbfs/sock.c	Thu Dec 12 17:02:46 1996
+++ linux/fs/smbfs/sock.c	Thu Dec 12 16:54:19 1996
@@ -15,6 +15,7 @@
 #include <linux/net.h>
 #include <linux/mm.h>
 #include <linux/netdevice.h>
+#include <net/scm.h>
 #include <net/ip.h>
 
 #include <linux/smb.h>
@@ -26,31 +27,41 @@
 
 static int
 _recvfrom(struct socket *sock, unsigned char *ubuf, int size,
-	  int noblock, unsigned flags, struct sockaddr_in *sa, int *addr_len)
+	  int noblock, unsigned flags, struct sockaddr_in *sa)
 {
 	struct iovec iov;
 	struct msghdr msg;
+	struct scm_cookie scm;
 
+	memset(&scm, 0, sizeof(scm));
+	
 	iov.iov_base = ubuf;
 	iov.iov_len = size;
 
 	msg.msg_name = (void *) sa;
 	msg.msg_namelen = 0;
-	if (addr_len)
-		msg.msg_namelen = *addr_len;
+	if (sa)
+		msg.msg_namelen = sizeof(struct sockaddr_in);
 	msg.msg_control = NULL;
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
 
-	return sock->ops->recvmsg(sock, &msg, size, noblock, flags, addr_len);
+	if (noblock) {
+		flags |= MSG_DONTWAIT;
+	}
+
+	return sock->ops->recvmsg(sock, &msg, size, flags, &scm);
 }
 
 static int
 _send(struct socket *sock, const void *buff, int len,
-      int nonblock, unsigned flags)
+      int noblock, unsigned flags)
 {
 	struct iovec iov;
 	struct msghdr msg;
+	struct scm_cookie scm;
+	int err;
+	
 
 	iov.iov_base = (void *) buff;
 	iov.iov_len = len;
@@ -61,7 +72,19 @@
 	msg.msg_iov = &iov;
 	msg.msg_iovlen = 1;
 
-	return sock->ops->sendmsg(sock, &msg, len, nonblock, flags);
+	if (noblock) {
+		flags |= MSG_DONTWAIT;
+	}
+
+	msg.msg_flags = flags;
+
+	err = scm_send(sock, &msg, &scm);
+        if (err < 0)
+                return err;
+	
+	err = sock->ops->sendmsg(sock, &msg, len, &scm);
+	scm_destroy(&scm);
+	return err;
 }
 
 static void
@@ -79,13 +102,13 @@
 		set_fs(get_ds());
 
 		result = _recvfrom(sock, (void *) peek_buf, 1, 1,
-				   MSG_PEEK, NULL, NULL);
+				   MSG_PEEK, NULL);
 
 		while ((result != -EAGAIN) && (peek_buf[0] == 0x85))
 		{
 			/* got SESSION KEEP ALIVE */
 			result = _recvfrom(sock, (void *) peek_buf,
-					   4, 1, 0, NULL, NULL);
+					   4, 1, 0, NULL);
 
 			DDPRINTK("smb_data_callback:"
 				 " got SESSION KEEP ALIVE\n");
@@ -95,8 +118,7 @@
 				break;
 			}
 			result = _recvfrom(sock, (void *) peek_buf,
-					   1, 1, MSG_PEEK,
-					   NULL, NULL);
+					   1, 1, MSG_PEEK, NULL);
 		}
 		set_fs(fs);
 
@@ -132,7 +154,7 @@
 		server->data_ready = NULL;
 		return -EINVAL;
 	}
-	sk = (struct sock *) (sock->data);
+	sk = sock->sk;
 
 	if (sk == NULL)
 	{
@@ -178,7 +200,7 @@
 		printk("smb_dont_catch_keepalive: did not get SOCK_STREAM\n");
 		return -EINVAL;
 	}
-	sk = (struct sock *) (sock->data);
+	sk = sock->sk;
 
 	if (sk == NULL)
 	{
@@ -239,8 +261,7 @@
 	{
 		result = _recvfrom(sock,
 				   (void *) (target + already_read),
-				   length - already_read, 0, 0,
-				   NULL, NULL);
+				   length - already_read, 0, 0, NULL);
 
 		if (result < 0)
 		{
@@ -480,6 +501,8 @@
 	return result;
 }
 
+extern struct net_proto_family inet_family_ops;
+
 int
 smb_release(struct smb_server *server)
 {
@@ -498,8 +521,8 @@
 	   is nothing behind it, so I set it to SS_UNCONNECTED. */
 	sock->state = SS_UNCONNECTED;
 
-	result = sock->ops->create(sock, 0);
-	DPRINTK("smb_release: sock->ops->create = %d\n", result);
+	result = inet_family_ops.create(sock, 0);
+	DPRINTK("smb_release: inet_create = %d\n", result);
 	return result;
 }
 
@@ -588,6 +611,8 @@
 		int lparam, unsigned char *param)
 {
 	struct socket *sock = server_sock(server);
+	struct scm_cookie scm;
+	int err;
 
 	/* I know the following is very ugly, but I want to build the
 	   smb packet as efficiently as possible. */
@@ -647,7 +672,13 @@
 	msg.msg_iov = iov;
 	msg.msg_iovlen = 4;
 
-	return sock->ops->sendmsg(sock, &msg, packet_length, 0, 0);
+	err = scm_send(sock, &msg, &scm);
+        if (err < 0)
+                return err;
+	
+	err = sock->ops->sendmsg(sock, &msg, packet_length, &scm);
+	scm_destroy(&scm);
+	return err;
 }
 
 /*
diff -u --recursive --new-file v2.1.14/linux/include/asm-alpha/smp.h linux/include/asm-alpha/smp.h
--- v2.1.14/linux/include/asm-alpha/smp.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-alpha/smp.h	Sun Dec  1 21:39:49 1996
@@ -0,0 +1,6 @@
+#ifndef __ASM_SMP_H
+#define __ASM_SMP_H
+
+/* We'll get here eventually.. */
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- v2.1.14/linux/include/asm-i386/irq.h	Wed Oct  9 08:55:22 1996
+++ linux/include/asm-i386/irq.h	Thu Dec 12 16:54:19 1996
@@ -22,8 +22,6 @@
 #define __STR(x) #x
 #define STR(x) __STR(x)
 
-#define GET_CURRENT \
-	"movl " SYMBOL_NAME_STR(current_set) ",%ebx\n\t"
 
 #define SAVE_ALL \
 	"cld\n\t" \
@@ -143,7 +141,11 @@
 	"andb $0x0F,%al\n\t"
 
 #define GET_CURRENT \
-	"movl " SYMBOL_NAME_STR(current_set) "(,%eax,4),%ebx\n\t"
+	"movl "SYMBOL_NAME_STR(apic_reg)", %ebx\n\t" \
+	"movl 32(%ebx), %ebx\n\t" \
+	"shrl $22,%ebx\n\t" \
+	"andl $0x3C,%ebx\n\t" \
+	"movl " SYMBOL_NAME_STR(current_set) "(,%ebx),%ebx\n\t"
 	
 #define	ENTER_KERNEL \
 	"pushl %eax\n\t" \
@@ -151,7 +153,6 @@
 	"pushfl\n\t" \
 	"cli\n\t" \
 	GET_PROCESSOR_ID \
-	GET_CURRENT \
 	"btsl $" STR(SMP_FROM_INT) ","SYMBOL_NAME_STR(smp_proc_in_lock)"(,%eax,4)\n\t" \
 	"1: " \
 	"lock\n\t" \
@@ -220,6 +221,7 @@
 	UNBLK_##chip(mask) \
 	"decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
 	"incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
+	GET_CURRENT \
 	"jmp ret_from_sys_call\n" \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
@@ -267,6 +269,7 @@
 	UNBLK_##chip(mask) \
 	"decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
 	"incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
+	GET_CURRENT \
 	"jmp ret_from_sys_call\n");
 
 	
@@ -298,6 +301,7 @@
 	"btrl $" STR(SMP_FROM_INT) ","SYMBOL_NAME_STR(smp_proc_in_lock)"(,%eax,4)\n\t" \
 	"decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
 	"incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
+	GET_CURRENT \
 	"jmp ret_from_sys_call\n" \
 "\n"__ALIGN_STR"\n" \
 SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
@@ -334,8 +338,12 @@
 	"cli\n\t" \
 	"decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
 	"incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
+	GET_CURRENT \
 	"jmp ret_from_sys_call\n");
 #else
+
+#define GET_CURRENT \
+	"movl " SYMBOL_NAME_STR(current_set) ",%ebx\n\t"
 	
 #define BUILD_IRQ(chip,nr,mask) \
 asmlinkage void IRQ_NAME(nr); \
diff -u --recursive --new-file v2.1.14/linux/include/asm-i386/ldt.h linux/include/asm-i386/ldt.h
--- v2.1.14/linux/include/asm-i386/ldt.h	Tue Nov 21 08:34:54 1995
+++ linux/include/asm-i386/ldt.h	Wed Dec 11 16:41:23 1996
@@ -20,6 +20,7 @@
 	unsigned int  read_exec_only:1;
 	unsigned int  limit_in_pages:1;
 	unsigned int  seg_not_present:1;
+	unsigned int  useable:1;
 };
 
 #define MODIFY_LDT_CONTENTS_DATA	0
diff -u --recursive --new-file v2.1.14/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.14/linux/include/asm-i386/processor.h	Fri Nov  1 17:13:19 1996
+++ linux/include/asm-i386/processor.h	Thu Dec 12 16:54:19 1996
@@ -29,10 +29,15 @@
 
 /*
  * Bus types (default is ISA, but people can check others with these..)
- * MCA_bus hardcoded to 0 for now.
  */
 extern int EISA_bus;
-#define MCA_bus 0
+extern int MCA_bus;
+
+/* from system description table in BIOS.  Mostly for MCA use, but
+others may find it useful. */
+extern unsigned int machine_id;
+extern unsigned int machine_submodel_id;
+extern unsigned int BIOS_revision;
 
 /*
  * User space process size: 3GB. This is hardcoded into a few places,
diff -u --recursive --new-file v2.1.14/linux/include/asm-i386/socket.h linux/include/asm-i386/socket.h
--- v2.1.14/linux/include/asm-i386/socket.h	Tue Nov 19 15:53:56 1996
+++ linux/include/asm-i386/socket.h	Thu Dec 12 16:54:20 1996
@@ -21,10 +21,11 @@
 #define SO_LINGER	13
 #define SO_BSDCOMPAT	14
 /* To add :#define SO_REUSEPORT 15 */
-#define SO_RCVLOWAT	16
-#define SO_SNDLOWAT	17
-#define SO_RCVTIMEO	18
-#define SO_SNDTIMEO	19
-
+#define SO_PASSCRED	16
+#define SO_PEERCRED	17
+#define SO_RCVLOWAT	18
+#define SO_SNDLOWAT	19
+#define SO_RCVTIMEO	20
+#define SO_SNDTIMEO	21
 
 #endif /* _ASM_SOCKET_H */
diff -u --recursive --new-file v2.1.14/linux/include/asm-i386/vm86.h linux/include/asm-i386/vm86.h
--- v2.1.14/linux/include/asm-i386/vm86.h	Sat Oct 19 10:07:32 1996
+++ linux/include/asm-i386/vm86.h	Wed Dec 11 16:41:23 1996
@@ -43,12 +43,26 @@
 #define VM86_STI	3	/* sti/popf/iret instruction enabled virtual interrupts */
 
 /*
- * This is the stack-layout when we have done a "SAVE_ALL" from vm86
- * mode - the main change is that the old segment descriptors aren't
- * useful any more and are forced to be zero by the kernel (and the
- * hardware when a trap occurs), and the real segment descriptors are
- * at the end of the structure. Look at ptrace.h to see the "normal"
- * setup.
+ * Additional return values when invoking new vm86()
+ */
+#define VM86_PICRETURN	4	/* return due to pending PIC request */
+#define VM86_TRAP	6	/* return due to DOS-debugger request */
+
+/*
+ * function codes when invoking new vm86()
+ */
+#define VM86_PLUS_INSTALL_CHECK	0
+#define VM86_ENTER		1
+#define VM86_ENTER_NO_BYPASS	2
+#define	VM86_REQUEST_IRQ	3
+#define VM86_FREE_IRQ		4
+#define VM86_GET_IRQ_BITS	5
+#define VM86_GET_AND_RESET_IRQ	6
+
+/*
+ * This is the stack-layout seen by the user space programm when we have
+ * done a translation of "SAVE_ALL" from vm86 mode. The real kernel layout
+ * is 'kernel_vm86_regs' (see below).
  */
 
 struct vm86_regs {
@@ -64,6 +78,8 @@
 	long eax;
 	long __null_ds;
 	long __null_es;
+	long __null_fs;
+	long __null_gs;
 	long orig_eax;
 	long eip;
 	unsigned short cs, __csh;
@@ -97,11 +113,96 @@
  */
 #define VM86_SCREEN_BITMAP	0x0001
 
+struct vm86plus_info_struct {
+	unsigned long force_return_for_pic:1;
+	unsigned long vm86dbg_active:1;       /* for debugger */
+	unsigned long vm86dbg_TFpendig:1;     /* for debugger */
+	unsigned long unused:28;
+	unsigned long is_vm86pus:1;	      /* for vm86 internal use */
+	unsigned char vm86dbg_intxxtab[32];   /* for debugger */
+};
+
+struct vm86plus_struct {
+	struct vm86_regs regs;
+	unsigned long flags;
+	unsigned long screen_bitmap;
+	unsigned long cpu_type;
+	struct revectored_struct int_revectored;
+	struct revectored_struct int21_revectored;
+	struct vm86plus_info_struct vm86plus;
+};
+
 #ifdef __KERNEL__
+/*
+ * This is the (kernel) stack-layout when we have done a "SAVE_ALL" from vm86
+ * mode - the main change is that the old segment descriptors aren't
+ * useful any more and are forced to be zero by the kernel (and the
+ * hardware when a trap occurs), and the real segment descriptors are
+ * at the end of the structure. Look at ptrace.h to see the "normal"
+ * setup. For user space layout see 'struct vm86_regs' above.
+ */
+
+struct kernel_vm86_regs {
+/*
+ * normal regs, with special meaning for the segment descriptors..
+ */
+	long ebx;
+	long ecx;
+	long edx;
+	long esi;
+	long edi;
+	long ebp;
+	long eax;
+	long __null_ds;
+	long __null_es;
+	long orig_eax;
+	long eip;
+	unsigned short cs, __csh;
+	long eflags;
+	long esp;
+	unsigned short ss, __ssh;
+/*
+ * these are specific to v86 mode:
+ */
+	unsigned short es, __esh;
+	unsigned short ds, __dsh;
+	unsigned short fs, __fsh;
+	unsigned short gs, __gsh;
+};
+
+struct kernel_vm86_struct {
+	struct kernel_vm86_regs regs;
+/*
+ * the below part remains on the kernel stack while we are in VM86 mode.
+ * 'tss.esp0' then contains the address of VM86_TSS_ESP0 below, and when we
+ * get forced back from VM86, the CPU and "SAVE_ALL" will restore the above
+ * 'struct kernel_vm86_regs' with the then actual values.
+ * Therefore, pt_regs in fact points to a complete 'kernel_vm86_struct'
+ * in kernelspace, hence we need not reget the data from userspace.
+ */
+#define VM86_TSS_ESP0 flags
+	unsigned long flags;
+	unsigned long screen_bitmap;
+	unsigned long cpu_type;
+	struct revectored_struct int_revectored;
+	struct revectored_struct int21_revectored;
+	struct vm86plus_info_struct vm86plus;
+	struct pt_regs *regs32;   /* here we save the pointer to the old regs */
+/*
+ * The below is not part of the structure, but the stack layout continues
+ * this way. In front of 'return-eip' may be some data, depending on
+ * compilation, so we don't rely on this and save the pointer to 'oldregs'
+ * in 'regs32' above.
+ * However, with GCC-2.7.2 and the the current CFLAGS you see exactly this:
+
+	long return-eip;        from call to vm86()
+	struct pt_regs oldregs;  user space registers as saved by syscall
+ */
+};
 
-void handle_vm86_fault(struct vm86_regs *, long);
-void handle_vm86_debug(struct vm86_regs *, long);
+void handle_vm86_fault(struct kernel_vm86_regs *, long);
+int handle_vm86_trap(struct kernel_vm86_regs *, long, int);
 
-#endif
+#endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v2.1.14/linux/include/linux/atalk.h linux/include/linux/atalk.h
--- v2.1.14/linux/include/linux/atalk.h	Tue Nov 19 15:53:56 1996
+++ linux/include/linux/atalk.h	Thu Dec 12 16:54:20 1996
@@ -125,8 +125,6 @@
 	__u8	pa_dst_node		__attribute__ ((packed));	
 };
 
-typedef struct sock	atalk_socket;
-
 #define AARP_EXPIRY_TIME	(5*60*HZ)	/* Not specified - how long till we drop a resolved entry */
 #define AARP_HASH_SIZE		16		/* Size of hash table */
 #define AARP_TICK_TIME		(HZ/5)		/* Fast retransmission timer when resolving */
diff -u --recursive --new-file v2.1.14/linux/include/linux/blk.h linux/include/linux/blk.h
--- v2.1.14/linux/include/linux/blk.h	Wed Oct 16 10:48:29 1996
+++ linux/include/linux/blk.h	Thu Dec 12 17:33:06 1996
@@ -105,6 +105,9 @@
 void initrd_init(void);
 
 #endif
+#ifdef CONFIG_BLK_DEV_PS2
+extern int ps2esdi_init(void);
+#endif
 
 #define RO_IOCTLS(dev,where) \
   case BLKROSET: { int __val;  if (!suser()) return -EACCES; \
@@ -202,6 +205,14 @@
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+#elif (MAJOR_NR == PS2ESDI_MAJOR)
+
+#define DEVICE_NAME "PS/2 ESDI"
+#define DEVICE_REQUEST do_ps2esdi_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
 #elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
 
 #define DEVICE_NAME "CDU31A"
@@ -412,7 +423,8 @@
 	add_blkdev_randomness(MAJOR(req->rq_dev));
 #endif
 #ifdef IDE_DRIVER
-	blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
+	hwgroup->drive->queue = req->next;
+	blk_dev[MAJOR(req->rq_dev)].current_request = NULL;
 	hwgroup->rq = NULL;
 #else
 	DEVICE_OFF(req->rq_dev);
diff -u --recursive --new-file v2.1.14/linux/include/linux/blkdev.h linux/include/linux/blkdev.h
--- v2.1.14/linux/include/linux/blkdev.h	Mon Sep 30 11:19:00 1996
+++ linux/include/linux/blkdev.h	Thu Dec 12 17:33:06 1996
@@ -33,9 +33,14 @@
 	struct request * next;
 };
 
+typedef void (request_fn_proc) (void);
+typedef struct request ** (queue_proc) (kdev_t dev);
+
 struct blk_dev_struct {
-	void (*request_fn)(void);
-	struct request * current_request;
+	request_fn_proc		*request_fn;
+	queue_proc		*queue;
+	void			*data;
+	struct request		*current_request;
 	struct request   plug;
 	struct tq_struct plug_tq;
 };
diff -u --recursive --new-file v2.1.14/linux/include/linux/etherdevice.h linux/include/linux/etherdevice.h
--- v2.1.14/linux/include/linux/etherdevice.h	Fri Apr 12 10:55:09 1996
+++ linux/include/linux/etherdevice.h	Thu Dec 12 16:54:20 1996
@@ -31,12 +31,12 @@
 extern int		eth_header(struct sk_buff *skb, struct device *dev,
 				   unsigned short type, void *daddr,
 				   void *saddr, unsigned len);
-extern int		eth_rebuild_header(void *buff, struct device *dev,
-			unsigned long dst, struct sk_buff *skb);
+extern int		eth_rebuild_header(struct sk_buff *skb);
 extern unsigned short	eth_type_trans(struct sk_buff *skb, struct device *dev);
-extern void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
-				  unsigned short htype, __u32 daddr);
-extern void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
+extern void		eth_header_cache_update(struct hh_cache *hh, struct device *dev,
+						unsigned char * haddr);
+extern int		eth_header_cache(struct dst_entry *dst, struct dst_entry *neigh,
+					 struct hh_cache *hh);
 extern void		eth_copy_and_sum(struct sk_buff *dest,
 				unsigned char *src, int length, int base);
 extern struct device	* init_etherdev(struct device *, int);
diff -u --recursive --new-file v2.1.14/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.1.14/linux/include/linux/hdreg.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/hdreg.h	Thu Dec 12 17:33:06 1996
@@ -91,6 +91,7 @@
 #define HDIO_GET_32BIT 		0x0309	/* get current io_32bit setting */
 #define HDIO_GET_NOWERR		0x030a	/* get ignore-write-error flag */
 #define HDIO_GET_DMA		0x030b	/* get use-dma flag */
+#define HDIO_GET_NICE		0x030c	/* get nice flags */
 #define HDIO_DRIVE_CMD		0x031f	/* execute a special drive command */
 
 /* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
@@ -102,6 +103,7 @@
 #define HDIO_SET_DMA		0x0326	/* change use-dma flag */
 #define HDIO_SET_PIO_MODE	0x0327	/* reconfig interface to new speed */
 #define HDIO_SCAN_HWIF		0x0328	/* register and (re)scan interface */
+#define HDIO_SET_NICE		0x0329	/* set nice flags */
 
 /* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
 struct hd_driveid {
@@ -153,6 +155,17 @@
 	/* unsigned short vendor7  [32];*/	/* vendor unique (words 128-159) */
 	/* unsigned short reservedyy[96];*/	/* reserved (words 160-255) */
 };
+
+/*
+ * IDE "nice" flags. These are used on a per drive basis to determine
+ * when to be nice and give more bandwidth to the other devices which
+ * share the same IDE bus.
+ */
+#define IDE_NICE_DSC_OVERLAP	(0)	/* per the DSC overlap protocol */
+#define IDE_NICE_ATAPI_OVERLAP	(1)	/* not supported yet */
+#define IDE_NICE_0		(2)	/* when sure that it won't affect us */
+#define IDE_NICE_1		(3)	/* when probably won't affect us much */
+#define IDE_NICE_2		(4)	/* when we know it's on our expense */
 
 #ifdef __KERNEL__
 /*
diff -u --recursive --new-file v2.1.14/linux/include/linux/icmp.h linux/include/linux/icmp.h
--- v2.1.14/linux/include/linux/icmp.h	Sat Oct  5 16:58:36 1996
+++ linux/include/linux/icmp.h	Thu Dec 12 16:54:20 1996
@@ -72,6 +72,10 @@
 		__u16	sequence;
 	} echo;
 	__u32	gateway;
+	struct {
+		__u16	__unused;
+		__u16	mtu;
+	} frag;
   } un;
 };
 
diff -u --recursive --new-file v2.1.14/linux/include/linux/icmpv6.h linux/include/linux/icmpv6.h
--- v2.1.14/linux/include/linux/icmpv6.h	Thu Dec 12 17:02:46 1996
+++ linux/include/linux/icmpv6.h	Thu Dec 12 17:35:21 1996
@@ -134,7 +134,7 @@
 						    __u32 info, 
 						    struct device *dev);
 
-extern void				icmpv6_init(struct proto_ops *ops);
+extern void				icmpv6_init(struct net_proto_family *ops);
 extern int				icmpv6_err_convert(int type, int code,
 							   int *err);
 #endif
diff -u --recursive --new-file v2.1.14/linux/include/linux/if.h linux/include/linux/if.h
--- v2.1.14/linux/include/linux/if.h	Thu Dec 12 17:02:46 1996
+++ linux/include/linux/if.h	Thu Dec 12 16:54:20 1996
@@ -32,7 +32,6 @@
 #define	IFF_RUNNING	0x40		/* resources allocated		*/
 #define	IFF_NOARP	0x80		/* no ARP protocol		*/
 #define	IFF_PROMISC	0x100		/* receive all packets		*/
-/* Not supported */
 #define	IFF_ALLMULTI	0x200		/* receive all multicast packets*/
 
 #define IFF_MASTER	0x400		/* master of a load balancer 	*/
diff -u --recursive --new-file v2.1.14/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v2.1.14/linux/include/linux/if_arp.h	Tue Nov 19 15:53:57 1996
+++ linux/include/linux/if_arp.h	Thu Dec 12 17:35:19 1996
@@ -87,6 +87,8 @@
 #define	ATF_USETRAILERS	0x10		/* has requested trailers	*/
 #define ATF_NETMASK     0x20            /* want to use a netmask (only
 					   for proxy entries) */
+#define ATF_DONTPUB	0x40		/* don't answer this addresses	*/
+#define ATF_MAGIC	0x80		/* automatically added entry	*/
 
 /*
  *	This structure defines an ethernet arp header.
diff -u --recursive --new-file v2.1.14/linux/include/linux/igmp.h linux/include/linux/igmp.h
--- v2.1.14/linux/include/linux/igmp.h	Tue Nov 19 15:53:57 1996
+++ linux/include/linux/igmp.h	Thu Dec 12 16:54:20 1996
@@ -36,7 +36,7 @@
 #define IGMP_HOST_MEMBERSHIP_REPORT	0x12	/* Ditto */
 #define IGMP_DVMRP			0x13	/* DVMRP routing */
 #define IGMP_PIM			0x14	/* PIM routing */
-#define IGMP_TRACE			0x15	/* CISCO trace */
+#define IGMP_TRACE			0x15
 #define IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16	/* New version of 0x11 */
 #define IGMP_HOST_LEAVE_MESSAGE 	0x17	/* An extra BSD seems to send */
 
@@ -91,9 +91,9 @@
 	unsigned long multiaddr;
 	struct ip_mc_list *next;
 	struct timer_list timer;
-	short tm_running;
-	short reporter;
 	int users;
+	char tm_running;
+	char reporter;
 };
 
 struct ip_router_info
@@ -108,8 +108,7 @@
 extern struct ip_mc_list *ip_mc_head;
 
 
-extern int igmp_rcv(struct sk_buff *, struct device *, struct options *, __u32, unsigned short,
-	__u32, int , struct inet_protocol *);
+extern int igmp_rcv(struct sk_buff *, unsigned short);
 extern void ip_mc_drop_device(struct device *dev);
 extern int ip_mc_join_group(struct sock *sk, struct device *dev, unsigned long addr);
 extern int ip_mc_leave_group(struct sock *sk, struct device *dev,unsigned long addr);
diff -u --recursive --new-file v2.1.14/linux/include/linux/in.h linux/include/linux/in.h
--- v2.1.14/linux/include/linux/in.h	Tue Nov 19 15:53:57 1996
+++ linux/include/linux/in.h	Thu Dec 12 17:33:06 1996
@@ -45,6 +45,40 @@
 	__u32	s_addr;
 };
 
+#define IP_TOS		1
+#define IP_TTL		2
+#define IP_HDRINCL	3
+#define IP_OPTIONS	4
+#define IP_LOCALADDR	5
+#define IP_RECVOPTS	6
+#define IP_RETOPTS	7
+#define IP_LOCALDEV	8
+#define IP_RECVDSTADDR  9
+#define IP_PMTUDISC	10
+#define IP_RECVERR	11
+
+/* BSD compatibility */
+#define IP_RECVRETOPTS	IP_RETOPTS
+
+/* IP_PMTUDISC values */
+#define IP_PMTUDISC_WANT		0	/* Use per route hints	*/
+#define IP_PMTUDISC_DONT		1	/* Never send DF frames */
+#define IP_PMTUDISC_DO			2	/* Always DF		*/
+
+#define IP_MULTICAST_IF			32
+#define IP_MULTICAST_TTL 		33
+#define IP_MULTICAST_LOOP 		34
+#define IP_ADD_MEMBERSHIP		35
+#define IP_DROP_MEMBERSHIP		36
+#define IP_MULTICAST_IFN		37
+#define IP_ADD_MEMBERSHIPN		38
+#define IP_DROP_MEMBERSHIPN		39
+
+/* These need to appear somewhere around here */
+#define IP_DEFAULT_MULTICAST_TTL        1
+#define IP_DEFAULT_MULTICAST_LOOP       1
+#define IP_MAX_MEMBERSHIPS              20
+
 /* Request struct for multicast socket ops */
 
 struct ip_mreq 
@@ -53,6 +87,16 @@
 	struct in_addr imr_interface;	/* local IP address of interface */
 };
 
+struct ip_mreqn
+{
+	struct in_addr	imr_multiaddr;		/* IP multicast address of group */
+	struct in_addr	imr_address;		/* local IP address of interface */
+#if 1
+	char		imr_interface[16];
+#else
+	int		imr_ifindex;		/* Interface index */
+#endif
+};
 
 /* Structure describing an Internet (IP) socket address. */
 #define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
@@ -94,8 +138,8 @@
 #define	IN_MULTICAST(a)		IN_CLASSD(a)
 #define IN_MULTICAST_NET	0xF0000000
 
-#define	IN_EXPERIMENTAL(a)	((((long int) (a)) & 0xe0000000) == 0xe0000000)
-#define	IN_BADCLASS(a)		((((long int) (a)) & 0xf0000000) == 0xf0000000)
+#define	IN_EXPERIMENTAL(a)	((((long int) (a)) & 0xf0000000) == 0xf0000000)
+#define	IN_BADCLASS(a)		IN_EXPERIMENTAL((a))
 
 /* Address to accept any incoming messages. */
 #define	INADDR_ANY		((unsigned long int) 0x00000000)
@@ -114,9 +158,10 @@
 #define	IN_LOOPBACK(a)		((((long int) (a)) & 0xff000000) == 0x7f000000)
 
 /* Defines for Multicast INADDR */
-#define INADDR_UNSPEC_GROUP   	0xe0000000      /* 224.0.0.0   */
-#define INADDR_ALLHOSTS_GROUP 	0xe0000001      /* 224.0.0.1   */
-#define INADDR_MAX_LOCAL_GROUP  0xe00000ff      /* 224.0.0.255 */
+#define INADDR_UNSPEC_GROUP   	0xe0000000U	/* 224.0.0.0   */
+#define INADDR_ALLHOSTS_GROUP 	0xe0000001U	/* 224.0.0.1   */
+#define INADDR_ALLRTRS_GROUP    0xe0000002U	/* 224.0.0.2 */
+#define INADDR_MAX_LOCAL_GROUP  0xe00000ffU	/* 224.0.0.255 */
 
 
 /* <asm/byteorder.h> contains the htonl type stuff.. */
@@ -126,6 +171,9 @@
 /* Some random defines to make it easier in the kernel.. */
 #define LOOPBACK(x)	(((x) & htonl(0xff000000)) == htonl(0x7f000000))
 #define MULTICAST(x)	(((x) & htonl(0xf0000000)) == htonl(0xe0000000))
+#define BADCLASS(x)	(((x) & htonl(0xf0000000)) == htonl(0xf0000000))
+#define ZERONET(x)	(((x) & htonl(0xff000000)) == htonl(0x00000000))
+#define LOCAL_MCAST(x)	(((x) & htonl(0xFFFFFF00)) == htonl(0xE0000000))
 
 #endif
 
diff -u --recursive --new-file v2.1.14/linux/include/linux/interrupt.h linux/include/linux/interrupt.h
--- v2.1.14/linux/include/linux/interrupt.h	Fri Nov  1 17:13:19 1996
+++ linux/include/linux/interrupt.h	Wed Dec 11 17:16:07 1996
@@ -33,6 +33,7 @@
 	DIGI_BH,
 	SERIAL_BH,
 	RISCOM8_BH,
+	ESP_BH,
 	NET_BH,
 	IMMEDIATE_BH,
 	KEYBOARD_BH,
diff -u --recursive --new-file v2.1.14/linux/include/linux/ip.h linux/include/linux/ip.h
--- v2.1.14/linux/include/linux/ip.h	Mon Sep 30 11:21:33 1996
+++ linux/include/linux/ip.h	Thu Dec 12 17:38:39 1996
@@ -18,17 +18,54 @@
 #define _LINUX_IP_H
 #include <asm/byteorder.h>
 
-#define IPOPT_END	0
-#define IPOPT_NOOP	1
-#define IPOPT_SEC	130
-#define IPOPT_LSRR	131
-#define IPOPT_SSRR	137
-#define IPOPT_RR	7
-#define IPOPT_SID	136
-#define IPOPT_TIMESTAMP	68
+/* SOL_IP socket options */
 
+#define IPTOS_TOS_MASK		0x1E
+#define IPTOS_TOS(tos)		((tos)&IPTOS_TOS_MASK)
+#define	IPTOS_LOWDELAY		0x10
+#define	IPTOS_THROUGHPUT	0x08
+#define	IPTOS_RELIABILITY	0x04
+#define	IPTOS_MINCOST		0x02
+
+#define IPTOS_PREC_MASK		0xE0
+#define IPTOS_PREC(tos)		((tos)&IPTOS_PREC_MASK)
+#define IPTOS_PREC_NETCONTROL           0xe0
+#define IPTOS_PREC_INTERNETCONTROL      0xc0
+#define IPTOS_PREC_CRITIC_ECP           0xa0
+#define IPTOS_PREC_FLASHOVERRIDE        0x80
+#define IPTOS_PREC_FLASH                0x60
+#define IPTOS_PREC_IMMEDIATE            0x40
+#define IPTOS_PREC_PRIORITY             0x20
+#define IPTOS_PREC_ROUTINE              0x00
+
+
+/* IP options */
+#define IPOPT_COPY		0x80
+#define IPOPT_CLASS_MASK	0x60
+#define IPOPT_NUMBER_MASK	0x1f
+
+#define	IPOPT_COPIED(o)		((o)&IPOPT_COPY)
+#define	IPOPT_CLASS(o)		((o)&IPOPT_CLASS_MASK)
+#define	IPOPT_NUMBER(o)		((o)&IPOPT_NUMBER_MASK)
+
+#define	IPOPT_CONTROL		0x00
+#define	IPOPT_RESERVED1		0x20
+#define	IPOPT_MEASUREMENT	0x40
+#define	IPOPT_RESERVED2		0x60
+
+#define IPOPT_END	(0 |IPOPT_CONTROL)
+#define IPOPT_NOOP	(1 |IPOPT_CONTROL)
+#define IPOPT_SEC	(2 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_LSRR	(3 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_TIMESTAMP	(4 |IPOPT_MEASUREMENT)
+#define IPOPT_RR	(7 |IPOPT_CONTROL)
+#define IPOPT_SID	(8 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_SSRR	(9 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_RA	(20|IPOPT_CONTROL|IPOPT_COPY)
 
+#define IPVERSION	4
 #define MAXTTL		255
+#define IPDEFTTL	64
 
 struct timestamp {
 	__u8	len;
@@ -67,7 +104,7 @@
 #define	IPOPT_TS_TSANDADDR	1		/* timestamps and addresses */
 #define	IPOPT_TS_PRESPEC	2		/* specified modules only */
 
-struct options {
+struct ip_options {
   __u32		faddr;				/* Saved first hop address */
   unsigned char	optlen;
   unsigned char srr;
@@ -81,9 +118,9 @@
                 rr_needaddr:1,			/* Need to record addr of outgoing dev	*/
                 ts_needtime:1,			/* Need to record timestamp		*/
                 ts_needaddr:1;			/* Need to record addr of outgoing dev  */
+  unsigned char router_alert;
   unsigned char __pad1;
   unsigned char __pad2;
-  unsigned char __pad3;
   unsigned char __data[0];
 };
 
diff -u --recursive --new-file v2.1.14/linux/include/linux/major.h linux/include/linux/major.h
--- v2.1.14/linux/include/linux/major.h	Tue Nov 19 15:53:57 1996
+++ linux/include/linux/major.h	Thu Dec 12 16:54:20 1996
@@ -62,6 +62,7 @@
 #define IDE2_MAJOR	33
 #define IDE3_MAJOR	34
 #define NETLINK_MAJOR	36
+#define PS2ESDI_MAJOR	36
 #define IDETAPE_MAJOR	37
 #define Z2RAM_MAJOR	37
 #define RISCOM8_NORMAL_MAJOR 48
diff -u --recursive --new-file v2.1.14/linux/include/linux/mca.h linux/include/linux/mca.h
--- v2.1.14/linux/include/linux/mca.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/mca.h	Thu Dec 12 16:54:20 1996
@@ -0,0 +1,91 @@
+/*
+ * Header for Microchannel Architecture Bus 
+ * Written by Martin Kolinek, February 1996
+*/
+
+#ifndef _LINUX_MCA_H
+#define _LINUX_MCA_H
+
+/* The detection of MCA bus is done in the real mode (using BIOS).
+ * The information is exported to the protected code, where this 
+ * variable is set to one in case MCA bus was detected.
+*/
+extern int  MCA_bus;
+
+/* maximal number of MCA slots - actually, some machines have less, but
+they all have sufficient number of POS registers to cover 8. */
+#define MCA_MAX_SLOT_NR  8
+
+/* MCA_NOTFOUND is an error condition.  The other two indicate
+	motherboard POS registers contain the adapter.  They might be
+	returned by the mca_find_adapter() function, and can be used as
+	arguments to mca_read_stored_pos().  I'm not going to allow direct
+	access to the motherboard registers until we run across an adapter
+	that requires it.  We don't know enough about them to know if it's
+	safe.
+
+	See Documentation/mca.txt or one of the existing drivers for
+	more information.
+*/
+#define MCA_NOTFOUND	-1
+#define MCA_INTEGSCSI	(MCA_MAX_SLOT_NR)
+#define MCA_INTEGVIDEO	(MCA_MAX_SLOT_NR+1)
+
+/* max number of adapters, including both slots and various integrated
+things. */
+#define MCA_NUMADAPTERS (MCA_MAX_SLOT_NR+2)
+
+/* returns the slot of the first enabled adapter matching id.  User can
+specify a starting slot beyond zero, to deal with detecting multiple
+devices.  Returns MCA_NOTFOUND if id not found.  Also checks the
+integrated adapters. */
+extern int mca_find_adapter( int id, int start );
+
+/* adapter state info - returns 0 if no */
+extern int mca_isadapter( int slot );
+extern int mca_isenabled( int slot );
+
+/* gets a byte out of POS register (stored in memory) */
+extern unsigned char mca_read_stored_pos( int slot, int reg );
+
+/*
+	This can be expanded later.  Right now, it gives us a way of
+	getting meaningful information into the MCA_info structure,
+	so we can have a more interesting /proc/mca.
+*/
+extern void mca_set_adapter_name( int slot, char* name );
+extern char* mca_get_adapter_name( int slot );
+
+/*
+	This sets up an information callback for /proc/mca/slot?.  The
+	function is called with the buffer, slot, and device pointer (or
+	some equally informative context information, or nothing, if you
+	prefer), and is expected to put useful information into the
+	buffer.  The adapter name, id, and POS registers get printed
+	before this is called though, so don't do it again.
+
+	This should be called with a NULL procfn when a module
+	unregisters, thus preventing kernel crashes and other such
+	nastiness.
+*/
+typedef int (*MCA_ProcFn)( char* buf, int slot, void* dev );
+extern void mca_set_adapter_procfn( int slot, MCA_ProcFn, void* dev );
+
+/* These routines actually mess with the hardware POS registers.  They
+temporarily disable the device (and interrupts), so make sure you know
+what you're doing if you use them.  Furthermore, writing to a POS may
+result in two devices trying to share a resource, which in turn can
+result in multiple devices sharing memory spaces, IRQs, or even trashing
+hardware.  YOU HAVE BEEN WARNED.
+
+You can only access slots with this.  Motherboard registers are off
+limits.
+*/
+
+/* read a byte from the specified POS register. */
+extern unsigned char mca_read_pos( int slot, int reg );
+
+/* write a byte to the specified POS register. */
+extern void mca_write_pos( int slot, int reg, unsigned char byte );
+
+#endif /* _LINUX_MCA_H */
diff -u --recursive --new-file v2.1.14/linux/include/linux/mroute.h linux/include/linux/mroute.h
--- v2.1.14/linux/include/linux/mroute.h	Mon Sep 30 11:25:18 1996
+++ linux/include/linux/mroute.h	Thu Dec 12 17:39:24 1996
@@ -122,12 +122,16 @@
 extern int ipmr_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern void mroute_close(struct sock *sk);
 extern void ipmr_forward(struct sk_buff *skb, int is_frag);
+extern int ip_mr_find_tunnel(__u32, __u32);
 
 
 struct vif_device
 {
-	struct device 	*dev;			/* Device we are using */
-	struct route 	*rt_cache;		/* Tunnel route cache */
+	union
+	{
+		struct device 	*dev;		/* Device we are using */
+		struct rtable	*rt;		/* Route for tunnel    */
+	} u;
 	unsigned long	bytes_in,bytes_out;
 	unsigned long	pkt_in,pkt_out;		/* Statistics 			*/
 	unsigned long	rate_limit;		/* Traffic shaping (NI) 	*/
@@ -146,6 +150,12 @@
 	int mfc_flags;				/* Flags on line		*/
 	struct sk_buff_head mfc_unresolved;	/* Unresolved buffers		*/
 	int mfc_queuelen;			/* Unresolved buffer counter	*/
+	unsigned mfc_last_assert;
+	int mfc_minvif;
+	int mfc_maxvif;
+	unsigned long mfc_bytes;
+	unsigned long mfc_pkt;
+	unsigned long mfc_wrong_if;
 	unsigned char mfc_ttls[MAXVIFS];	/* TTL thresholds		*/
 };
 
@@ -162,6 +172,9 @@
 #endif		
 
 #endif
+
+
+#define MFC_ASSERT_THRESH (3*HZ)		/* Maximal freq. of asserts */
 
 /*
  *	Pseudo messages used by mrouted
diff -u --recursive --new-file v2.1.14/linux/include/linux/net.h linux/include/linux/net.h
--- v2.1.14/linux/include/linux/net.h	Sat Nov 30 12:03:11 1996
+++ linux/include/linux/net.h	Thu Dec 12 16:54:20 1996
@@ -57,38 +57,31 @@
 #define SO_NOSPACE	(1<<18)		/* no space to write		*/
 
 #ifdef __KERNEL__
-/*
- * Internal representation of a socket. not all the fields are used by
- * all configurations:
- *
- *		server			client
- * conn		client connected to	server connected to
- * iconn	list of clients		-unused-
- *		 awaiting connections
- * wait		sleep for clients,	sleep for connection,
- *		sleep for i/o		sleep for i/o
- */
-struct socket {
-  short			type;		/* SOCK_STREAM, ...		*/
-  socket_state		state;
-  long			flags;
-  struct proto_ops	*ops;		/* protocols do most everything	*/
-  void			*data;		/* protocol data		*/
-  struct socket		*conn;		/* server socket connected to	*/
-  struct socket		*iconn;		/* incomplete client conn.s	*/
-  struct socket		*next;
-  struct wait_queue	**wait;		/* ptr to place to wait on	*/
-  struct inode		*inode;
-  struct fasync_struct  *fasync_list;	/* Asynchronous wake up list	*/
-  struct file		*file;		/* File back pointer for gc	*/
+
+struct socket
+{
+	socket_state		state;
+
+	unsigned long		flags;
+	struct proto_ops	*ops;
+	struct inode		*inode;
+	struct fasync_struct	*fasync_list;	/* Asynchronous wake up list	*/
+	struct file		*file;		/* File back pointer for gc	*/
+	struct sock		*sk;
+	struct wait_queue	*wait;
+
+	short			type;
+	unsigned char		passcred;
+	unsigned char		tli;
 };
 
 #define SOCK_INODE(S)	((S)->inode)
 
+struct scm_cookie;
+
 struct proto_ops {
   int	family;
 
-  int	(*create)	(struct socket *sock, int protocol);
   int	(*dup)		(struct socket *newsock, struct socket *oldsock);
   int	(*release)	(struct socket *sock, struct socket *peer);
   int	(*bind)		(struct socket *sock, struct sockaddr *umyaddr,
@@ -112,8 +105,13 @@
 			 char *optval, int *optlen);
   int	(*fcntl)	(struct socket *sock, unsigned int cmd,
 			 unsigned long arg);	
-  int   (*sendmsg)	(struct socket *sock, struct msghdr *m, int total_len, int nonblock, int flags);
-  int   (*recvmsg)	(struct socket *sock, struct msghdr *m, int total_len, int nonblock, int flags, int *addr_len);
+  int   (*sendmsg)	(struct socket *sock, struct msghdr *m, int total_len, struct scm_cookie *scm);
+  int   (*recvmsg)	(struct socket *sock, struct msghdr *m, int total_len, int flags, struct scm_cookie *scm);
+};
+
+struct net_proto_family {
+	int	family;
+	int	(*create)(struct socket *sock, int protocol);
 };
 
 struct net_proto {
@@ -121,10 +119,14 @@
 	void (*init_func)(struct net_proto *);	/* Bootstrap */
 };
 
-extern int	sock_wake_async(struct socket *sock, int how);
-extern int	sock_register(int family, struct proto_ops *ops);
+extern int	sock_wake_async(struct socket *sk, int how);
+extern int	sock_register(struct net_proto_family *fam);
 extern int	sock_unregister(int family);
 extern struct socket *sock_alloc(void);
-extern void	sock_release(struct socket *sock);
+extern void	sock_release(struct socket *);
+extern int   	sock_sendmsg(struct socket *, struct msghdr *m, int len);
+extern int	sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
+extern int	sock_readv_writev(int type, struct inode * inode, struct file * file,
+				  const struct iovec * iov, long count, long size);
 #endif /* __KERNEL__ */
 #endif	/* _LINUX_NET_H */
diff -u --recursive --new-file v2.1.14/linux/include/linux/netbeui.h linux/include/linux/netbeui.h
--- v2.1.14/linux/include/linux/netbeui.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/netbeui.h	Thu Dec 12 16:54:20 1996
@@ -0,0 +1,16 @@
+#ifndef _LINUX_NETBEUI_H
+#define _LINUX_NETBEUI_H
+
+#include <linux/if.h>
+
+#define NB_NAME_LEN	20	/* Set this properly from the full docs when
+				   I get them */
+				   
+struct sockaddr_netbeui
+{
+	sa_family	snb_family;
+	char		snb_name[NB_NAME_LEN];
+	char		snb_devhint[IFNAMSIZ];
+};
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.14/linux/include/linux/netdevice.h	Tue Nov 19 15:53:57 1996
+++ linux/include/linux/netdevice.h	Thu Dec 12 17:35:17 1996
@@ -55,6 +55,12 @@
 #define IS_INVBCAST	4		/* Wrong netmask bcast not for us (unused)*/
 #define IS_MULTICAST	5		/* Multicast IP address */
 
+/* NOTE: move to ipv4_device.h */
+
+#define IFF_IP_ADDR_OK	1
+#define IFF_IP_MASK_OK	2
+#define IFF_IP_BRD_OK	4
+
 #ifdef __KERNEL__
 
 #include <linux/skbuff.h>
@@ -74,10 +80,6 @@
 struct hh_cache
 {
 	struct hh_cache *hh_next;
-	void		*hh_arp;	/* Opaque pointer, used by
-					 * any address resolution module,
-					 * not only ARP.
-					 */
 	int		hh_refcnt;	/* number of users */
 	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
 	char		hh_uptodate;	/* hh_data is valid */
@@ -85,133 +87,154 @@
 };
 
 /*
- * The DEVICE structure.
- * Actually, this whole structure is a big mistake.  It mixes I/O
- * data with strictly "high-level" data, and it has to know about
- * almost every data structure used in the INET module.  
+ *	The DEVICE structure.
+ *	Actually, this whole structure is a big mistake.  It mixes I/O
+ *	data with strictly "high-level" data, and it has to know about
+ *	almost every data structure used in the INET module.
+ *
+ *	FIXME: cleanup struct device such that network protocol info
+ *	moves out.
  */
-struct device 
+
+struct device
 {
 
-  /*
-   * This is the first field of the "visible" part of this structure
-   * (i.e. as seen by users in the "Space.c" file).  It is the name
-   * the interface.
-   */
-  char			  *name;
-
-  /* I/O specific fields - FIXME: Merge these and struct ifmap into one */
-  unsigned long		  rmem_end;		/* shmem "recv" end	*/
-  unsigned long		  rmem_start;		/* shmem "recv" start	*/
-  unsigned long		  mem_end;		/* shared mem end	*/
-  unsigned long		  mem_start;		/* shared mem start	*/
-  unsigned long		  base_addr;		/* device I/O address	*/
-  unsigned char		  irq;			/* device IRQ number	*/
-
-  /* Low-level status flags. */
-  volatile unsigned char  start,		/* start an operation	*/
-                          interrupt;		/* interrupt arrived	*/
-  unsigned long		  tbusy;		/* transmitter busy must be long for bitops */
-
-  struct device		  *next;
-
-  /* The device initialization function. Called only once. */
-  int			  (*init)(struct device *dev);
-
-  /* Some hardware also needs these fields, but they are not part of the
-     usual set specified in Space.c. */
-  unsigned char		  if_port;		/* Selectable AUI, TP,..*/
-  unsigned char		  dma;			/* DMA channel		*/
-
-  struct enet_statistics* (*get_stats)(struct device *dev);
-
-  /*
-   * This marks the end of the "visible" part of the structure. All
-   * fields hereafter are internal to the system, and may change at
-   * will (read: may be cleaned up at will).
-   */
-
-  /* These may be needed for future network-power-down code. */
-  unsigned long		  trans_start;	/* Time (in jiffies) of last Tx	*/
-  unsigned long		  last_rx;	/* Time of last Rx		*/
-
-  unsigned short	  flags;	/* interface flags (a la BSD)	*/
-  unsigned short	  family;	/* address family ID (AF_INET)	*/
-  unsigned short	  metric;	/* routing metric (not used)	*/
-  unsigned short	  mtu;		/* interface MTU value		*/
-  unsigned short	  type;		/* interface hardware type	*/
-  unsigned short	  hard_header_len;	/* hardware hdr length	*/
-  void			  *priv;	/* pointer to private data	*/
-
-  /* Interface address info. */
-  unsigned char		  broadcast[MAX_ADDR_LEN];	/* hw bcast add	*/
-  unsigned char		  pad;				/* make dev_addr aligned to 8 bytes */
-  unsigned char		  dev_addr[MAX_ADDR_LEN];	/* hw address	*/
-  unsigned char		  addr_len;	/* hardware address length	*/
-#if 0
-  __u32			  pa_addr_arr[4];
-  __u16			  pa_prefix_len;
-#define pa_addr		  pa_addr_arr[3];
-#else
-  unsigned long		  pa_addr;	/* protocol address		*/
+	/*
+	 * This is the first field of the "visible" part of this structure
+	 * (i.e. as seen by users in the "Space.c" file).  It is the name
+	 * the interface.
+	 */
+	char			*name;
+
+	/*
+	 *	I/O specific fields
+	 *	FIXME: Merge these and struct ifmap into one
+	 */
+	unsigned long		rmem_end;	/* shmem "recv" end	*/
+	unsigned long		rmem_start;	/* shmem "recv" start	*/
+	unsigned long		mem_end;	/* shared mem end	*/
+	unsigned long		mem_start;	/* shared mem start	*/
+	unsigned long		base_addr;	/* device I/O address	*/
+	unsigned char		irq;		/* device IRQ number	*/
+	
+	/* Low-level status flags. */
+	volatile unsigned char	start,		/* start an operation	*/
+				interrupt;	/* interrupt arrived	*/
+	unsigned long		tbusy;		/* transmitter busy must be
+						   long for bitops	*/
+	
+	struct device		*next;
+	
+	/* The device initialization function. Called only once. */
+	int			(*init)(struct device *dev);
+
+	/* Interface index. Unique device identifier	*/
+	int			ifindex;
+	struct device		*next_up;
+
+	/*
+	 *	Some hardware also needs these fields, but they are not
+	 *	part of the usual set specified in Space.c.
+	 */
+	unsigned char		if_port;	/* Selectable AUI, TP,..*/
+	unsigned char		dma;		/* DMA channel		*/
+
+	struct enet_statistics* (*get_stats)(struct device *dev);
+#ifdef CONFIG_NET_RADIO
+	struct iw_statistics*	(*get_wireless_stats)(struct device *dev);
 #endif
-  unsigned long		  pa_brdaddr;	/* protocol broadcast addr	*/
-  unsigned long		  pa_dstaddr;	/* protocol P-P other side addr	*/
-  unsigned long		  pa_mask;	/* protocol netmask		*/
-  unsigned short	  pa_alen;	/* protocol address length	*/
 
-  struct dev_mc_list	 *mc_list;	/* Multicast mac addresses	*/
-  int			 mc_count;	/* Number of installed mcasts	*/
+	/*
+	 * This marks the end of the "visible" part of the structure. All
+	 * fields hereafter are internal to the system, and may change at
+	 * will (read: may be cleaned up at will).
+	 */
+
+	/* These may be needed for future network-power-down code. */
+	unsigned long		trans_start;	/* Time (in jiffies) of last Tx	*/
+	unsigned long		last_rx;	/* Time of last Rx	*/
+	
+	unsigned short		flags;	/* interface flags (a la BSD)	*/
+	unsigned short		family;	/* address family ID (AF_INET)	*/
+	unsigned short		metric;	/* routing metric (not used)	*/
+	unsigned short		mtu;	/* interface MTU value		*/
+	unsigned short		type;	/* interface hardware type	*/
+	unsigned short		hard_header_len;	/* hardware hdr length	*/
+	void			*priv;	/* pointer to private data	*/
+	
+	/* Interface address info. */
+	unsigned char		broadcast[MAX_ADDR_LEN];	/* hw bcast add	*/
+	unsigned char		pad;		/* make dev_addr aligned to 8 bytes */
+	unsigned char		dev_addr[MAX_ADDR_LEN];	/* hw address	*/
+	unsigned char		addr_len;	/* hardware address length	*/
+	unsigned long		pa_addr;	/* protocol address		*/
+
+	unsigned long		pa_brdaddr;	/* protocol broadcast addr	*/
+	unsigned long		pa_dstaddr;	/* protocol P-P other side addr	*/
+	unsigned long		pa_mask;	/* protocol netmask		*/
+	unsigned short		pa_alen;	/* protocol address length	*/
+
+	struct dev_mc_list	*mc_list;	/* Multicast mac addresses	*/
+	int			mc_count;	/* Number of installed mcasts	*/
   
-  struct ip_mc_list	 *ip_mc_list;	/* IP multicast filter chain    */
-  __u32			tx_queue_len;	/* Max frames per queue allowed */
+	struct ip_mc_list	*ip_mc_list;	/* IP multicast filter chain    */
+	unsigned		ip_flags;
+	__u8			hash;	
+	__u32			tx_queue_len;	/* Max frames per queue allowed */
     
-  /* For load balancing driver pair support */
+	/* For load balancing driver pair support */
   
-  unsigned long		   pkt_queue;	/* Packets queued */
-  struct device		  *slave;	/* Slave device */
-  struct net_alias_info		*alias_info;	/* main dev alias info */
-  struct net_alias		*my_alias;	/* alias devs */
+	unsigned long		pkt_queue;	/* Packets queued	*/
+	struct device		*slave;		/* Slave device		*/
+	struct net_alias_info	*alias_info;	/* main dev alias info	*/
+	struct net_alias	*my_alias;	/* alias devs		*/
   
-  /* Pointer to the interface buffers. */
-  struct sk_buff_head	  buffs[DEV_NUMBUFFS];
+	/* Pointer to the interface buffers.	*/
+	struct sk_buff_head	buffs[DEV_NUMBUFFS];
 
-  /* Pointers to interface service routines. */
-  int			  (*open)(struct device *dev);
-  int			  (*stop)(struct device *dev);
-  int			  (*hard_start_xmit) (struct sk_buff *skb,
-					      struct device *dev);
-  int			  (*hard_header) (struct sk_buff *skb,
-					  struct device *dev,
-					  unsigned short type,
-					  void *daddr,
-					  void *saddr,
-					  unsigned len);
-  int			  (*rebuild_header)(void *eth, struct device *dev,
-				unsigned long raddr, struct sk_buff *skb);
+	/* Pointers to interface service routines.	*/
+	int			(*open)(struct device *dev);
+	int			(*stop)(struct device *dev);
+	int			(*hard_start_xmit) (struct sk_buff *skb,
+						    struct device *dev);
+	int			(*hard_header) (struct sk_buff *skb,
+						struct device *dev,
+						unsigned short type,
+						void *daddr,
+						void *saddr,
+						unsigned len);
+	int			(*rebuild_header)(struct sk_buff *skb);
 #define HAVE_MULTICAST			 
-  void			  (*set_multicast_list)(struct device *dev);
+	void			(*set_multicast_list)(struct device *dev);
 #define HAVE_SET_MAC_ADDR  		 
-  int			  (*set_mac_address)(struct device *dev, void *addr);
+	int			(*set_mac_address)(struct device *dev,
+						   void *addr);
 #define HAVE_PRIVATE_IOCTL
-  int			  (*do_ioctl)(struct device *dev, struct ifreq *ifr, int cmd);
+	int			(*do_ioctl)(struct device *dev,
+					    struct ifreq *ifr, int cmd);
 #define HAVE_SET_CONFIG
-  int			  (*set_config)(struct device *dev, struct ifmap *map);
+	int			(*set_config)(struct device *dev,
+					      struct ifmap *map);
 #define HAVE_HEADER_CACHE
-  void			  (*header_cache_bind)(struct hh_cache **hhp, struct device *dev, unsigned short htype, __u32 daddr);
-  void			  (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char *  haddr);
+	int			(*hard_header_cache)(struct dst_entry *dst,
+						     struct dst_entry *neigh,
+						     struct hh_cache *hh);
+	void			(*header_cache_update)(struct hh_cache *hh,
+						       struct device *dev,
+						       unsigned char *  haddr);
 #define HAVE_CHANGE_MTU
-  int			  (*change_mtu)(struct device *dev, int new_mtu);            
+	int			(*change_mtu)(struct device *dev, int new_mtu);
+
 };
 
 
 struct packet_type {
-  unsigned short	type;	/* This is really htons(ether_type). */
-  struct device *	dev;
-  int			(*func) (struct sk_buff *, struct device *,
-				 struct packet_type *);
-  void			*data;
-  struct packet_type	*next;
+	unsigned short		type;	/* This is really htons(ether_type). */
+	struct device		*dev;
+	int			(*func) (struct sk_buff *, struct device *,
+					 struct packet_type *);
+	void			*data;
+	struct packet_type	*next;
 };
 
 
@@ -221,28 +244,27 @@
 /* Used by dev_rint */
 #define IN_SKBUFF	1
 
-extern volatile unsigned long in_bh;
-
 extern struct device	loopback_dev;
 extern struct device	*dev_base;
 extern struct packet_type *ptype_base[16];
 
+/* NOTE: move to INET specific header;
+   __ip_chk_addr is deprecated, do not use if it's possible.
+ */
+extern int		__ip_chk_addr(unsigned long addr);
+extern struct device 	*ip_dev_find(unsigned long addr, char *name);
+/* This is the wrong place but it'll do for the moment */
+extern void		ip_mc_allhost(struct device *dev);
+extern int		devinet_ioctl(unsigned int cmd, void *);
 
-extern int		ip_addr_match(unsigned long addr1, unsigned long addr2);
-extern int		ip_chk_addr(unsigned long addr);
-extern struct device	*ip_dev_bynet(unsigned long daddr, unsigned long mask);
-extern unsigned long	ip_my_addr(void);
-extern unsigned long	ip_get_mask(unsigned long addr);
-extern struct device 	*ip_dev_find(unsigned long addr);
-extern struct device    *dev_getbytype(unsigned short type);
-
+extern struct device    *dev_getbyhwaddr(unsigned short type, char *hwaddr);
 extern void		dev_add_pack(struct packet_type *pt);
 extern void		dev_remove_pack(struct packet_type *pt);
 extern struct device	*dev_get(const char *name);
 extern int		dev_open(struct device *dev);
 extern int		dev_close(struct device *dev);
-extern void		dev_queue_xmit(struct sk_buff *skb, struct device *dev,
-				       int pri);
+extern int		dev_queue_xmit(struct sk_buff *skb);
+extern void		dev_loopback_xmit(struct sk_buff *skb);
 				      
 #define HAVE_NETIF_RX 1
 extern void		netif_rx(struct sk_buff *skb);
@@ -255,29 +277,21 @@
 
 /* Locking protection for page faults during outputs to devices unloaded during the fault */
 
-extern int		dev_lockct;
+extern atomic_t		dev_lockct;
 
 /*
- *	These two don't currently need to be interrupt-safe
+ *	These two don't currently need to be atomic
  *	but they may do soon. Do it properly anyway.
  */
 
 extern __inline__ void  dev_lock_list(void)
 {
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	dev_lockct++;
-	restore_flags(flags);
+	atomic_inc(&dev_lockct);
 }
 
 extern __inline__ void  dev_unlock_list(void)
 {
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	dev_lockct--;
-	restore_flags(flags);
+	atomic_dec(&dev_lockct);
 }
 
 /*
@@ -291,6 +305,31 @@
 		schedule();
 }
 
+/* NOTE: about to be replaced with if_index */
+
+static __inline__ __u8 dev_hash_name(char *name)
+{
+	__u8 hash = 0;
+	__u8 *p;
+	for (p=name; *p; p++)
+		hash ^= *p;
+	return hash;
+}
+
+static __inline__ __u8 dev_hash_mc_name(char *name)
+{
+	int i;
+	__u8 hash = 0;
+	unsigned *p = (unsigned*)name;
+	for (i=0; i<MAX_ADDR_LEN/sizeof(unsigned); i++) {
+		unsigned h = p[i];
+		h ^= (h>>16);
+		h ^= (h>>8);
+		hash ^= h;
+	}
+	return hash;
+}
+
 
 /* These functions live elsewhere (drivers/net/net_init.c, but related) */
 
@@ -308,8 +347,7 @@
 extern void 		dev_mc_delete(struct device *dev, void *addr, int alen, int all);
 extern void		dev_mc_add(struct device *dev, void *addr, int alen, int newonly);
 extern void		dev_mc_discard(struct device *dev);
-/* This is the wrong place but it'll do for the moment */
-extern void		ip_mc_allhost(struct device *dev);
+
 #endif /* __KERNEL__ */
 
 #endif	/* _LINUX_DEV_H */
diff -u --recursive --new-file v2.1.14/linux/include/linux/netlink.h linux/include/linux/netlink.h
--- v2.1.14/linux/include/linux/netlink.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/netlink.h	Thu Dec 12 16:54:20 1996
@@ -0,0 +1,20 @@
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+struct nlmsghdr
+{
+	unsigned long	nlmsg_len;	/* Length of message including header */
+	unsigned long	nlmsg_type;	/* Message type */
+	unsigned long	nlmsg_seq;	/* Sequence number */
+	unsigned long	nlmsg_pid;	/* Sending process PID */
+	unsigned char	nlmsg_data[0];
+};
+
+#define NLMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) )
+
+#define NLMSG_ACK		0x01	/* int - error code */
+#define NLMSG_OVERRUN		0x02	/* unsigned long[2] - start and end
+					 * of lost message sequence numbers.
+					 */
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/include/linux/notifier.h linux/include/linux/notifier.h
--- v2.1.14/linux/include/linux/notifier.h	Thu Jul  4 13:38:52 1996
+++ linux/include/linux/notifier.h	Thu Dec 12 16:54:20 1996
@@ -92,5 +92,16 @@
 				   detected a hardware crash and restarted
 				   - we can use this eg to kick tcp sessions
 				   once done */
+#define NETDEV_CHANGE	0x0004	/* Notify device state change */
+
+#define SYS_DOWN	0x0001	/* Notify of system down */
+#define SYS_HALT	0x0002	/* Notify of system halt */
+
+/*
+ *	Publically visible notifier objects
+ */
+ 
+extern struct notifier_block *boot_notifier_list;
+ 
 #endif
 #endif
diff -u --recursive --new-file v2.1.14/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.1.14/linux/include/linux/pci.h	Fri Nov 22 18:28:21 1996
+++ linux/include/linux/pci.h	Thu Dec 12 16:54:20 1996
@@ -252,6 +252,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
@@ -349,6 +350,10 @@
 #define PCI_VENDOR_ID_OAK		0x104e
 #define PCI_DEVICE_ID_OAK_OTI107	0x0107
 
+/* 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_PROMISE		0x105a
 #define PCI_DEVICE_ID_PROMISE_5300	0x5300
 
@@ -504,6 +509,9 @@
 #define PCI_VENDOR_ID_SPECIALIX		0x11cb
 #define PCI_DEVICE_ID_SPECIALIX_XIO	0x4000
 #define PCI_DEVICE_ID_SPECIALIX_RIO	0x8000
+
+#define PCI_VENDOR_ID_COMPEX		0x11f6
+#define PCI_DEVICE_ID_COMPEX_RL2000	0x1401
 
 #define PCI_VENDOR_ID_RP               0x11fe
 #define PCI_DEVICE_ID_RP8OCTA          0x0001
diff -u --recursive --new-file v2.1.14/linux/include/linux/pcwd.h linux/include/linux/pcwd.h
--- v2.1.14/linux/include/linux/pcwd.h	Thu Aug  1 15:43:05 1996
+++ linux/include/linux/pcwd.h	Thu Jan  1 02:00:00 1970
@@ -1,12 +0,0 @@
-/*
- * Berkshire PC Watchdog Defines
- * For version 0.41 of the driver
- */
-
-#define	PCWD_IOCTL_BASE	'W'
-
-#define	PCWD_GETSTAT	_IOR(PCWD_IOCTL_BASE, 1, int)
-#define	PCWD_PING	_IOR(PCWD_IOCTL_BASE, 2, int)
-
-#define	PCWD_PREVRESET	0x01	/* System previously reset by card */
-#define	PCWD_TEMPSENSE	0x02	/* Temperature overheat sense */
diff -u --recursive --new-file v2.1.14/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.14/linux/include/linux/proc_fs.h	Tue Nov 12 15:56:14 1996
+++ linux/include/linux/proc_fs.h	Thu Dec 12 17:33:16 1996
@@ -21,6 +21,7 @@
 	PROC_VERSION,
 	PROC_CPUINFO,
 	PROC_PCI,
+	PROC_MCA,
 	PROC_SELF,	/* will change inode # */
 	PROC_NET,
         PROC_SCSI,
@@ -69,6 +70,9 @@
 	PROC_NET_UNIX = 128,
 	PROC_NET_ARP,
 	PROC_NET_ROUTE,
+	PROC_NET_RTCLASSES,
+	PROC_NET_RTLOCAL,
+	PROC_NET_RTRULES,
 	PROC_NET_DEV,
 	PROC_NET_RAW,
 	PROC_NET_TCP,
@@ -83,7 +87,7 @@
 	PROC_NET_IPFWOUT,
 	PROC_NET_IPACCT,
 	PROC_NET_IPMSQHST,
-	PROC_NET_WAVELAN,
+	PROC_NET_WIRELESS,
 	PROC_NET_IPX_INTERFACE,
 	PROC_NET_IPX_ROUTE,
 	PROC_NET_IPX,
@@ -104,6 +108,7 @@
 	PROC_NET_IP_MASQ_APP,
 	PROC_NET_RT6,
 	PROC_NET_RT6_STATS,
+	PROC_NET_NDISC,
 	PROC_NET_STRIP_STATUS,
 	PROC_NET_STRIP_TRACE,
 	PROC_NET_Z8530,
@@ -111,6 +116,7 @@
 	PROC_NET_RS_NEIGH,
 	PROC_NET_RS_ROUTES,
 	PROC_NET_RS,
+	PROC_NET_CL2LLC,
 	PROC_NET_LAST
 };
 
@@ -137,6 +143,7 @@
 	PROC_SCSI_NCR53C8XX,
 	PROC_SCSI_ULTRASTOR,
 	PROC_SCSI_7000FASST,
+	PROC_SCSI_IBMMCA,
 	PROC_SCSI_EATA2X,
 	PROC_SCSI_AM53C974,
 	PROC_SCSI_SSC,
@@ -147,12 +154,22 @@
 	PROC_SCSI_A2091,
 	PROC_SCSI_GVP11,
 	PROC_SCSI_ATARI,
+	PROC_SCSI_IDESCSI,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
 	PROC_SCSI_LAST = (PROC_SCSI_FILE + 16) /* won't ever see more than */
 };                                             /* 16 HBAs in one machine   */
 
+enum mca_directory_inos {
+	PROC_MCA_MACHINE = (PROC_SCSI_LAST+1),
+	PROC_MCA_REGISTERS,
+	PROC_MCA_VIDEO,
+	PROC_MCA_SCSI,
+	PROC_MCA_SLOT,	/* the 8 adapter slots */
+	PROC_MCA_LAST = (PROC_MCA_SLOT + 8)
+};
+
 /* Finally, the dynamically allocatable proc entries are reserved: */
 
 #define PROC_DYNAMIC_FIRST 4096
@@ -200,6 +217,7 @@
 extern struct proc_dir_entry proc_sys;
 extern struct proc_dir_entry proc_pid;
 extern struct proc_dir_entry proc_pid_fd;
+extern struct proc_dir_entry proc_mca;
 
 extern struct inode_operations proc_scsi_inode_operations;
 
diff -u --recursive --new-file v2.1.14/linux/include/linux/ps2esdi.h linux/include/linux/ps2esdi.h
--- v2.1.14/linux/include/linux/ps2esdi.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/ps2esdi.h	Thu Dec 12 16:54:20 1996
@@ -0,0 +1,98 @@
+#ifndef _PS2ESDI_H_
+#define _PS2ESDI_H_
+
+#define NRML_ESDI_ID 0xddff
+#define INTG_ESDI_ID 0xdf9f
+
+#define PRIMARY_IO_BASE 0x3510
+#define ALT_IO_BASE 0x3518
+
+#define ESDI_CMD_INT (io_base+0)
+#define ESDI_STT_INT (io_base+0)
+#define ESDI_CONTROL (io_base+2)
+#define ESDI_STATUS  (io_base+2)
+#define ESDI_ATTN    (io_base+3)
+#define ESDI_INTRPT  (io_base+3)
+
+#define STATUS_ENABLED    0x01
+#define STATUS_ALTERNATE  0x02
+#define STATUS_BUSY       0x10
+#define STATUS_STAT_AVAIL 0x08
+#define STATUS_INTR       0x01
+#define STATUS_RESET_FAIL 0xea
+#define STATUS_CMD_INF	  0x04
+
+#define CTRL_SOFT_RESET   0xe4
+#define CTRL_HARD_RESET   0x80
+#define CTRL_EOI          0xe2
+#define CTRL_ENABLE_DMA   0x02
+#define CTRL_ENABLE_INTR  0x01
+#define CTRL_DISABLE_INTR  0x00
+
+#define ATT_EOI 0x02
+
+/* bits of word 0 of configuration status block. more info see p.38 of tech ref */
+#define CONFIG_IS 0x10 /* Invalid Secondary */
+#define CONFIG_ZD 0x08 /* Zero Defect */
+#define CONFIG_SF 0x04 /* Skewed Format */
+#define CONFIG_FR 0x02 /* Removable */
+#define CONFIG_RT 0x01 /* Retries */
+
+#define PORT_SYS_A   0x92
+#define PORT_DMA_FN  0x18
+#define PORT_DMA_EX  0x1a
+
+#define ON (unsigned char)0x40
+#define OFF (unsigned char)~ON
+#define LITE_ON outb(inb(PORT_SYS_A) | ON,PORT_SYS_A)
+#define LITE_OFF outb((inb(PORT_SYS_A) & OFF),PORT_SYS_A)
+
+#define FAIL 0
+#define SUCCES 1
+
+#define INT_CMD_COMPLETE 0x01
+#define INT_CMD_ECC      0x03
+#define INT_CMD_RETRY    0x05
+#define INT_CMD_FORMAT   0x06
+#define INT_CMD_ECC_RETRY 0x07
+#define INT_CMD_WARNING  0x08
+#define INT_CMD_ABORT    0x09
+#define INT_RESET        0x0A
+#define INT_TRANSFER_REQ 0x0B
+#define INT_CMD_FAILED   0x0C
+#define INT_DMA_ERR      0x0D
+#define INT_CMD_BLK_ERR  0x0E
+#define INT_ATTN_ERROR   0x0F
+
+#define DMA_MASK_CHAN 0x90
+#define DMA_UNMASK_CHAN 0xA0
+#define DMA_WRITE_ADDR 0x20
+#define DMA_WRITE_TC 0x40
+#define DMA_WRITE_MODE 0x70
+
+#define CMD_GET_DEV_CONFIG 0x09
+#define CMD_READ 0x4601
+#define CMD_WRITE 0x4602
+#define DMA_READ_16 0x4C
+#define DMA_WRITE_16 0x44
+
+
+#define MB 1024*1024
+#define SECT_SIZE 512   
+
+#define ERROR 1
+#define OK 0
+
+#define HDIO_GETGEO 0x0301
+
+#define FALSE 0
+#define TRUE !FALSE
+
+struct ps2esdi_geometry {
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned short cylinders;
+	unsigned long start;
+};
+
+#endif /* _PS2ESDI_H_ */
diff -u --recursive --new-file v2.1.14/linux/include/linux/route.h linux/include/linux/route.h
--- v2.1.14/linux/include/linux/route.h	Thu Dec 12 17:02:46 1996
+++ linux/include/linux/route.h	Thu Dec 12 16:54:20 1996
@@ -26,32 +26,47 @@
 /* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
 struct rtentry 
 {
-	unsigned long	rt_hash;	/* hash key for lookups		*/
+	unsigned long	rt_pad1;
 	struct sockaddr	rt_dst;		/* target address		*/
 	struct sockaddr	rt_gateway;	/* gateway addr (RTF_GATEWAY)	*/
 	struct sockaddr	rt_genmask;	/* target network mask (IP)	*/
-	short		rt_flags;
-	short		rt_refcnt;
-	unsigned long	rt_use;
-	struct ifnet	*rt_ifp;
+	unsigned short	rt_flags;
+	short		rt_pad2;
+	unsigned long	rt_pad3;
+	unsigned char	rt_tos;
+	unsigned char	rt_class;
+	short		rt_pad4;
 	short		rt_metric;	/* +1 for binary compatibility!	*/
 	char		*rt_dev;	/* forcing the device at add	*/
-	unsigned long	rt_mss;		/* per route MTU/Window 	*/
+	unsigned long	rt_mtu;		/* per route MTU/Window 	*/
+#ifndef __KERNEL__
+#define rt_mss	rt_mtu			/* Compatibility :-(            */
+#endif
 	unsigned long	rt_window;	/* Window clamping 		*/
 	unsigned short	rt_irtt;	/* Initial RTT			*/
+	
 };
 
 
-#define	RTF_UP		0x0001		/* route usable		  	  */
-#define	RTF_GATEWAY	0x0002		/* destination is a gateway	  */
-#define	RTF_HOST	0x0004		/* host entry (net otherwise)	  */
-#define RTF_REINSTATE	0x0008		/* reinstate route after tmout	  */
-#define	RTF_DYNAMIC	0x0010		/* created dyn. (by redirect)	  */
-#define	RTF_MODIFIED	0x0020		/* modified dyn. (by redirect)	  */
-#define RTF_MSS		0x0040		/* specific MSS for this route	  */
-#define RTF_WINDOW	0x0080		/* per route window clamping	  */
-#define RTF_IRTT	0x0100		/* Initial round trip time	  */
-#define RTF_REJECT	0x0200		/* Reject route			  */
+#define	RTF_UP		0x0001		/* route usable		  	*/
+#define	RTF_GATEWAY	0x0002		/* destination is a gateway	*/
+
+#define	RTF_HOST	0x0004		/* host entry (net otherwise)	*/
+#define RTF_REINSTATE	0x0008		/* reinstate route after tmout	*/
+#define	RTF_DYNAMIC	0x0010		/* created dyn. (by redirect)	*/
+#define	RTF_MODIFIED	0x0020		/* modified dyn. (by redirect)	*/
+#define RTF_MTU		0x0040		/* specific MTU for this route	*/
+#define RTF_MSS		RTF_MTU		/* Compatibility :-(		*/
+#define RTF_WINDOW	0x0080		/* per route window clamping	*/
+#define RTF_IRTT	0x0100		/* Initial round trip time	*/
+#define RTF_REJECT	0x0200		/* Reject route			*/
+#define	RTF_STATIC	0x0400		/* Manually injected route	*/
+#define	RTF_XRESOLVE	0x0800		/* External resolver		*/
+#define RTF_NOFORWARD   0x1000		/* Forwarding inhibited		*/
+#define RTF_THROW	0x2000		/* Go to next class		*/
+#define RTF_NOPMTUDISC  0x4000		/* Do not send packets with DF	*/
+
+/* Bad idea. IPv6 should not use broken IPv4 interface */
 
 #define RTF_ADDRCONF	0x0800		/* announced on link prefix	  */
 #define RTF_INVALID	0x1000
@@ -59,27 +74,128 @@
 #define RTF_DEFAULT	0x4000		/* Route is a default route	  */
 #define RTF_NEXTHOP	0x8000		/* Non gateway route with nexthop */
 
+
+#define RTF_MAGIC	0x10000		/* Route added/deleted authomatically,
+					 * when interface changes its state.
+					 */
+
+#define RTCF_VALVE	0x00200000
+#define RTCF_MASQ	0x00400000
+#define RTCF_NAT	0x00800000
+#define RTCF_DOREDIRECT 0x01000000
+#define RTCF_LOG	0x02000000
+#define RTCF_DIRECTSRC	0x04000000
+
+#define RTF_LOCAL	0x80000000
+#define RTF_INTERFACE	0x40000000
+#define RTF_MULTICAST	0x20000000
+#define RTF_BROADCAST	0x10000000
+#define RTF_NAT		0x08000000
+
+#define RTF_ADDRCLASSMASK	0xF8000000
+#define RT_ADDRCLASS(flags)	((__u32)flags>>23)
+
+#define RT_TOS(tos)		((tos)&IPTOS_TOS_MASK)
+
+#define RT_LOCALADDR(flags)	((flags&RTF_ADDRCLASSMASK) == (RTF_LOCAL|RTF_INTERFACE))
+
+#define RT_CLASS_UNSPEC		0
+#define RT_CLASS_DEFAULT	253
+
+#define RT_CLASS_MAIN		254
+#define RT_CLASS_LOCAL		255
+#define RT_CLASS_MAX		255
+
+#ifdef _LINUX_IN_H 	/* hack to check that in.h included */
 /*
  *	This structure is passed from the kernel to user space by netlink
  *	routing/device announcements
  */
 
-struct netlink_rtinfo
+struct in_rtmsg
 {
-	unsigned long	rtmsg_type;
-	struct sockaddr rtmsg_dst;
-	struct sockaddr rtmsg_gateway;
-	struct sockaddr rtmsg_genmask;
-	short 		rtmsg_flags;
+	struct in_addr	rtmsg_prefix;
+	struct in_addr	rtmsg_gateway;
+	unsigned	rtmsg_flags;
+	unsigned long	rtmsg_mtu;
+	unsigned long	rtmsg_window;
+	unsigned short	rtmsg_rtt;
 	short		rtmsg_metric;
+	unsigned char	rtmsg_tos;
+	unsigned char	rtmsg_class;
+	unsigned char	rtmsg_prefixlen;
+	unsigned char	rtmsg_reserved;
 	char		rtmsg_device[16];
 };
 
-#define RTMSG_NEWROUTE		0x01
-#define RTMSG_DELROUTE		0x02
+
+struct in_ifmsg
+{
+	struct sockaddr ifmsg_lladdr;
+	struct in_addr	ifmsg_prefix;
+	struct in_addr	ifmsg_brd;
+	unsigned	ifmsg_flags;
+	unsigned long	ifmsg_mtu;
+	short		ifmsg_metric;
+	unsigned char	ifmsg_prefixlen;
+	unsigned char	ifmsg_reserved;
+	char		ifmsg_device[16];
+};
+
+enum rtrule_actions
+{
+	RTP_GO,
+	RTP_NAT,
+	RTP_DROP,
+	RTP_UNREACHABLE,
+	RTP_PROHIBIT,
+	RTP_MASQUERADE
+};
+
+#define RTRF_LOG		1	/* Log route creations		*/
+#define RTRF_VALVE		2	/* One-way route		*/
+
+struct in_rtrulemsg
+{
+	struct in_addr	rtrmsg_src;
+	struct in_addr	rtrmsg_dst;
+	struct in_addr	rtrmsg_srcmap;
+	unsigned char	rtrmsg_srclen;
+	unsigned char	rtrmsg_dstlen;
+	unsigned char	rtrmsg_tos;
+	unsigned char	rtrmsg_class;
+	char		rtrmsg_device[16];
+	unsigned char	rtrmsg_flags;
+	unsigned char	rtrmsg_action;
+	unsigned char	rtrmsg_preference;
+	unsigned char	rtrmsg_rtmsgs;
+	struct in_rtmsg rtrmsg_rtmsg[1];
+};
+
+struct in_rtctlmsg
+{
+        unsigned	rtcmsg_flags;
+        int		rtcmsg_delay;
+};
+
+#define RTCTL_ECHO	1	/* Echo route changes */
+#define RTCTL_FLUSH	2	/* Send flush updates */
+#define RTCTL_ACK	4	/* Send acks	      */
+#define RTCTL_DELAY	8	/* Set netlink delay  */
+#define RTCTL_OWNER	0x10	/* Set netlink reader */
+#endif
+
+#define RTMSG_ACK		NLMSG_ACK
+#define RTMSG_OVERRUN		NLMSG_OVERRUN
+
 #define RTMSG_NEWDEVICE		0x11
 #define RTMSG_DELDEVICE		0x12
+#define RTMSG_NEWROUTE		0x21
+#define RTMSG_DELROUTE		0x22
+#define RTMSG_NEWRULE		0x31
+#define RTMSG_DELRULE		0x32
+#define RTMSG_CONTROL		0x40
 
-#define RTMSG_AR_FAILED		0x21	/* Address Resolution failed	*/
+#define RTMSG_AR_FAILED		0x51	/* Address Resolution failed	*/
 #endif	/* _LINUX_ROUTE_H */
 
diff -u --recursive --new-file v2.1.14/linux/include/linux/rpcsock.h linux/include/linux/rpcsock.h
--- v2.1.14/linux/include/linux/rpcsock.h	Thu Jun  6 21:22:24 1996
+++ linux/include/linux/rpcsock.h	Thu Dec 12 16:54:20 1996
@@ -53,10 +53,10 @@
 	struct rpc_wait *	rq_slot;
 	struct sockaddr	*	rq_addr;
 	int			rq_alen;
-	struct iovec		rq_svec[UIO_MAXIOV];
+	struct iovec		rq_svec[UIO_FASTIOV];
 	unsigned int		rq_snr;
 	unsigned long		rq_slen;
-	struct iovec		rq_rvec[UIO_MAXIOV];
+	struct iovec		rq_rvec[UIO_FASTIOV];
 	unsigned int		rq_rnr;
 	unsigned long		rq_rlen;
 };
diff -u --recursive --new-file v2.1.14/linux/include/linux/serial.h linux/include/linux/serial.h
--- v2.1.14/linux/include/linux/serial.h	Sun Nov 10 20:12:14 1996
+++ linux/include/linux/serial.h	Sun Dec  8 21:21:15 1996
@@ -184,7 +184,6 @@
 	unsigned short		closing_wait2;
 	int			IER; 	/* Interrupt Enable Register */
 	int			MCR; 	/* Modem control register */
-	int			MCR_noint; /* MCR with interrupts off */
 	unsigned long		event;
 	unsigned long		last_active;
 	int			line;
diff -u --recursive --new-file v2.1.14/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v2.1.14/linux/include/linux/skbuff.h	Thu Dec 12 17:02:46 1996
+++ linux/include/linux/skbuff.h	Thu Dec 12 17:33:16 1996
@@ -44,7 +44,6 @@
 #endif
 };
 
-
 struct sk_buff 
 {
 	struct sk_buff	* next;			/* Next buffer in list 				*/
@@ -53,57 +52,61 @@
 #if CONFIG_SKB_CHECK
 	int		magic_debug_cookie;
 #endif
-	struct sk_buff	*link3;			/* Link for IP protocol level buffer chains 	*/
 	struct sock	*sk;			/* Socket we are owned by 			*/
 	unsigned long	when;			/* used to compute rtt's			*/
 	struct timeval	stamp;			/* Time we arrived				*/
 	struct device	*dev;			/* Device we arrived on/are leaving by		*/
-	union 
+
+	/* Transport layer header */
+	union
 	{
 		struct tcphdr	*th;
-		struct ethhdr	*eth;
-		struct iphdr	*iph;
 		struct udphdr	*uh;
+		struct icmphdr	*icmph;
+		struct igmphdr	*igmph;
+		struct iphdr	*ipiph;
+		struct spxhdr	*spxh;
 		unsigned char	*raw;
-		/* for passing file handles in a unix domain socket */
-		void *filp;
 	} h;
+
+	/* Network layer header */
+	union
+	{
+		struct iphdr	*iph;
+		struct ipv6hdr	*ipv6h;
+		struct arphdr	*arph;
+		struct ipxhdr	*ipxh;
+		unsigned char	*raw;
+	} nh;
   
+	/* Link layer header */
 	union 
 	{	
-		/* As yet incomplete physical layer views */
-	  	unsigned char 	*raw;
 	  	struct ethhdr	*ethernet;
+	  	unsigned char 	*raw;
 	} mac;
-  
-	struct iphdr	*ip_hdr;		/* For IPPROTO_RAW 				*/
+
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-	struct ipv6hdr	*ipv6_hdr;
-	
-	/* 
-	 *	It would be inefficient to store the nexthop address in every
-	 *	skb. Instead we store a pointer to the respective neighbour
-	 *	cache entry. This might make ndisc cache management harder.
+	/*
+	 *	Generic "neighbour" information
 	 */
-
 	struct neighbour *nexthop;
 #endif		
-	unsigned int 	len;			/* Length of actual data			*/
-	unsigned int	csum;			/* Checksum 					*/
-	__u32		saddr;			/* IP source address				*/
-	__u32		daddr;			/* IP target address				*/
-	__u32		raddr;			/* IP next hop address				*/
+	struct  dst_entry *dst;
+
 	__u32		seq;			/* TCP sequence number				*/
 	__u32		end_seq;		/* seq [+ fin] [+ syn] + datalen		*/
 	__u32		ack_seq;		/* TCP ack sequence number			*/
-	unsigned char	proto_priv[16];	        /* Protocol private data			*/
+	char    	cb[32];
+  
+	unsigned int 	len;			/* Length of actual data			*/
+	unsigned int	csum;			/* Checksum 					*/
 	volatile char 	acked,			/* Are we acked ?				*/
 			used,			/* Are we in use ?				*/
-			free,			/* How to free this buffer			*/
 			arp;			/* Has IP/ARP resolution finished		*/
 	unsigned char	tries,			/* Times tried					*/
-  			lock,			/* Are we locked ?				*/
-  			localroute,		/* Local routing asserted for this frame	*/
+  			inclone,		/* Inline clone					*/
+  			priority,
   			pkt_type,		/* Packet class					*/
   			pkt_bridged,		/* Tracker for bridging 			*/
   			ip_summed;		/* Driver fed us an IP checksum			*/
@@ -112,9 +115,9 @@
 #define PACKET_MULTICAST	2		/* To group					*/
 #define PACKET_OTHERHOST	3		/* To someone else 				*/
 #define PACKET_NDISC		17		/* Outgoing NDISC packet			*/
-	unsigned short	users;			/* User count - see datagram.c,tcp.c 		*/
+	atomic_t	users;			/* User count - see datagram.c,tcp.c 		*/
 	unsigned short	protocol;		/* Packet protocol from driver. 		*/
-	unsigned short	truesize;		/* Buffer size 					*/
+	unsigned int	truesize;		/* Buffer size 					*/
 
 	atomic_t	count;			/* reference count				*/
 	struct sk_buff	*data_skb;		/* Link to the actual data skb			*/
@@ -123,10 +126,16 @@
 	unsigned char	*tail;			/* Tail pointer					*/
 	unsigned char 	*end;			/* End pointer					*/
 	void 		(*destructor)(struct sk_buff *);	/* Destruct function		*/
-	__u16		redirport;		/* Redirect port				*/
-	__u16		inclone;		/* Inline clone	*/
 #define SKB_CLONE_ORIG		1
 #define SKB_CLONE_INLINE	2
+	
+#if defined(CONFIG_SHAPER) || defined(CONFIG_SHAPER_MODULE)
+	__u32		shapelatency;		/* Latency on frame */
+	__u32		shapeclock;		/* Time it should go out */
+	__u32		shapelen;		/* Frame length in clocks */
+	__u32		shapestamp;		/* Stamp for shaper    */
+	__u16		shapepend;		/* Pending */
+#endif
 };
 
 #ifdef CONFIG_SKB_LARGE
@@ -154,7 +163,7 @@
 #if 0
 extern void			print_skb(struct sk_buff *);
 #endif
-extern void			kfree_skb(struct sk_buff *skb, int rw);
+extern void			__kfree_skb(struct sk_buff *skb);
 extern void			skb_queue_head_init(struct sk_buff_head *list);
 extern void			skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
 extern void			skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
@@ -169,10 +178,8 @@
 extern void			kfree_skbmem(struct sk_buff *skb);
 extern struct sk_buff *		skb_clone(struct sk_buff *skb, int priority);
 extern struct sk_buff *		skb_copy(struct sk_buff *skb, int priority);
-extern void			skb_device_lock(struct sk_buff *skb);
-extern void			skb_device_unlock(struct sk_buff *skb);
-extern void			dev_kfree_skb(struct sk_buff *skb, int mode);
-extern int			skb_device_locked(struct sk_buff *skb);
+extern struct sk_buff *		skb_realloc_headroom(struct sk_buff *skb, int newheadroom);
+#define dev_kfree_skb(a, b)	kfree_skb((a), (b))
 extern unsigned char *		skb_put(struct sk_buff *skb, unsigned int len);
 extern unsigned char *		skb_push(struct sk_buff *skb, unsigned int len);
 extern unsigned char *		skb_pull(struct sk_buff *skb, unsigned int len);
@@ -186,6 +193,12 @@
 	return (list->next == (struct sk_buff *) list);
 }
 
+extern __inline__ void kfree_skb(struct sk_buff *skb, int rw)
+{
+	if (atomic_dec_and_test(&skb->users))
+		__kfree_skb(skb);
+}
+
 extern __inline__ struct sk_buff *skb_unshare(struct sk_buff *skb, int pri, int dir)
 {
 	struct sk_buff *nskb;
@@ -474,10 +487,47 @@
 	}
 }
 
+/* dev_tint can lock buffer at any moment,
+ * so that cli(), unlink it and sti(),
+ * now it is safe.
+ */
+
+extern __inline__ int skb_steal(struct sk_buff *skb)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	if (skb->next) {
+		skb_unlink(skb);
+		atomic_dec(&skb->users);
+	}
+	restore_flags(flags);
+	return 1;
+}
+
+extern __inline__ void __skb_steal(struct sk_buff *skb)
+{
+	if (skb->next) {
+		skb_unlink(skb);
+		atomic_dec(&skb->users);
+	}
+}
+
+extern __inline__ void skb_orphan(struct sk_buff *skb)
+{
+	if (skb->destructor)
+		skb->destructor(skb);
+	skb->destructor = NULL;
+	skb->sk = NULL;
+}
+
+
 #endif
 
+extern struct sk_buff *		skb_realloc_headroom(struct sk_buff *skb, int newheadroom);
 extern struct sk_buff *		skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
-extern int			datagram_select(struct sock *sk, int sel_type, select_table *wait);
+extern int			datagram_select(struct socket *sock, int sel_type, select_table *wait);
 extern int			skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
 extern int			skb_copy_datagram_iovec(struct sk_buff *from, int offset, struct iovec *to,int size);
 extern void			skb_free_datagram(struct sock * sk, struct sk_buff *skb);
diff -u --recursive --new-file v2.1.14/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.1.14/linux/include/linux/socket.h	Sat Nov 30 12:03:11 1996
+++ linux/include/linux/socket.h	Thu Dec 12 16:54:20 1996
@@ -4,6 +4,7 @@
 #include <asm/socket.h>			/* arch-dependent defines	*/
 #include <linux/sockios.h>		/* the SIOCxxx I/O controls	*/
 #include <linux/uio.h>			/* iovec support		*/
+#include <linux/types.h>		/* pid_t			*/
 
 typedef unsigned short	sa_family_t;
 
@@ -33,10 +34,10 @@
 	void	*	msg_name;	/* Socket name			*/
 	int		msg_namelen;	/* Length of name		*/
 	struct iovec *	msg_iov;	/* Data blocks			*/
-	size_t 		msg_iovlen;	/* Number of blocks		*/
+	size_t		msg_iovlen;	/* Number of blocks		*/
 	void 	*	msg_control;	/* Per protocol magic (eg BSD file descriptor passing) */
-	size_t		msg_controllen;	/* Length of rights list */
-	int		msg_flags;	/* 4.4 BSD item we dont use      */
+	size_t		msg_controllen;	/* Length of cmsg list */
+	unsigned	msg_flags;
 };
 
 /*
@@ -57,9 +58,14 @@
  *	Table 5-14 of POSIX 1003.1g
  */
 
-#define CMSG_DATA(cmsg)		cmsg->cmsg_data
+#define CMSG_DATA(cmsg)		(cmsg)->cmsg_data
 #define CMSG_NXTHDR(mhdr, cmsg) cmsg_nxthdr(mhdr, cmsg)
-#define CMSG_FIRST(mhdr)	((struct cmsghdr *) (mhdr)->msg_control)
+
+#define CMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) )
+
+#define	CMSG_FIRST(msg)	((msg)->msg_controllen >= sizeof(struct cmsghdr) ? \
+			 (struct cmsghdr *)(msg)->msg_control : \
+			 (struct cmsghdr *)NULL)
 
 extern __inline__ struct cmsghdr * cmsg_nxthdr(struct msghdr *mhdr,
 					       struct cmsghdr *cmsg)
@@ -70,16 +76,39 @@
 	{
 		return NULL;
 	}
-	ptr = ((unsigned char *) cmsg) +  cmsg->cmsg_len;
+	ptr = ((unsigned char *) cmsg) +  CMSG_ALIGN(cmsg->cmsg_len);
 	if (ptr >= (unsigned char *) mhdr->msg_control + mhdr->msg_controllen)
 		return NULL;
 
 	return (struct cmsghdr *) ptr;
 }
 
-/* Control Messages */
+#ifdef __KERNEL__
+
+#define	KCMSG_NXTHDR(msg, cmsg)	({ \
+	struct cmsghdr * __cmptr = (struct cmsghdr *)((unsigned char*)(cmsg) + CMSG_ALIGN(kcm.cmsg_len)); \
+	( (void *)(__cmptr + 1) <= (msg)->msg_control + (msg)->msg_controllen && \
+	  !copy_from_user(&kcm, __cmptr, sizeof(struct cmsghdr)) ? __cmptr : NULL); })
+
+#define	KCMSG_FIRSTHDR(msg)	((msg)->msg_control && (msg)->msg_controllen >= sizeof(struct cmsghdr) \
+				 && !copy_from_user(&kcm, (msg)->msg_control, sizeof(struct cmsghdr)) ? \
+				 (struct cmsghdr *)(msg)->msg_control : \
+				 (struct cmsghdr *)NULL)
+
+#endif
+
+/* "Socket"-level control message types: */
 
-#define SCM_RIGHTS		1
+#define	SCM_RIGHTS	0x01		/* rw: access rights (array of int) */
+#define SCM_CREDENTIALS 0x02		/* rw: struct ucred		*/
+#define SCM_CONNECT	0x03		/* rw: struct scm_connect	*/
+
+struct ucred
+{
+	pid_t	pid;
+	uid_t	uid;
+	gid_t	gid;
+};
 
 /* Socket types. */
 #define SOCK_STREAM	1		/* stream (connection) socket	*/
@@ -140,11 +169,22 @@
 #define MSG_OOB		1
 #define MSG_PEEK	2
 #define MSG_DONTROUTE	4
-#define MSG_CTRUNC	8	/*  We need to support this for BSD oddments */
-#define MSG_PROXY	16	/* Supply or ask second address. */
-#define MSG_EOR		32	/* End of record */
-#define MSG_TRUNC	64	/* Data was discarded before delivery */
-#define MSG_WAITALL	128	/* Wait for a full request */
+#define MSG_CTRUNC	8
+#define MSG_PROXY	0x10	/* Supply or ask second address. */
+#define MSG_TRUNC	0x20
+#define MSG_DONTWAIT	0x40	/* Nonblocking io		 */
+#define MSG_EOR         0x80	/* End of record */
+#define MSG_WAITALL	0x100	/* Wait for a full request */
+#define MSG_FIN         0x200
+#define MSG_SYN		0x400
+#define MSG_URG		0x800
+#define MSG_RST		0x1000
+
+#define MSG_CTLIGNORE   0x80000000
+
+#define MSG_EOF         MSG_FIN
+#define MSG_CTLFLAGS	(MSG_OOB|MSG_URG|MSG_FIN|MSG_SYN|MSG_RST)
+
 
 /* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
 #define SOL_IP		0
@@ -161,27 +201,6 @@
 #define SOL_TCP		6
 #define SOL_UDP		17
 
-/* IP options */
-#define IP_TOS		1
-#define	IPTOS_LOWDELAY		0x10
-#define	IPTOS_THROUGHPUT	0x08
-#define	IPTOS_RELIABILITY	0x04
-#define	IPTOS_MINCOST		0x02
-#define IP_TTL		2
-#define IP_HDRINCL	3
-#define IP_OPTIONS	4
-
-#define IP_MULTICAST_IF			32
-#define IP_MULTICAST_TTL 		33
-#define IP_MULTICAST_LOOP 		34
-#define IP_ADD_MEMBERSHIP		35
-#define IP_DROP_MEMBERSHIP		36
-
-/* These need to appear somewhere around here */
-#define IP_DEFAULT_MULTICAST_TTL        1
-#define IP_DEFAULT_MULTICAST_LOOP       1
-#define IP_MAX_MEMBERSHIPS              20
- 
 /* IPX options */
 #define IPX_TYPE	1
 
@@ -207,5 +226,6 @@
 extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len);
 extern int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen);
 extern int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr);
+extern void put_cmsg(struct msghdr*, int level, int type, int len, void *data);
 #endif
 #endif /* _LINUX_SOCKET_H */
diff -u --recursive --new-file v2.1.14/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v2.1.14/linux/include/linux/sockios.h	Thu Dec 12 17:02:46 1996
+++ linux/include/linux/sockios.h	Thu Dec 12 16:54:20 1996
@@ -23,6 +23,7 @@
 /* Routing table calls. */
 #define SIOCADDRT	0x890B		/* add routing table entry	*/
 #define SIOCDELRT	0x890C		/* delete routing table entry	*/
+#define SIOCRTMSG	0x890D		/* call to routing system	*/
 
 /* Socket configuration controls. */
 #define SIOCGIFNAME	0x8910		/* get iface name		*/
diff -u --recursive --new-file v2.1.14/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.14/linux/include/linux/sysctl.h	Fri Nov 22 18:28:21 1996
+++ linux/include/linux/sysctl.h	Thu Dec 12 16:54:20 1996
@@ -93,14 +93,31 @@
 /* /proc/sys/net/unix */
 
 /* /proc/sys/net/ipv4 */
-#define NET_IPV4_ARP_RES_TIME           1
-#define NET_IPV4_ARP_DEAD_RES_TIME      2
-#define NET_IPV4_ARP_MAX_TRIES          3
-#define NET_IPV4_ARP_TIMEOUT            4
-#define NET_IPV4_ARP_CHECK_INTERVAL     5
-#define NET_IPV4_ARP_CONFIRM_INTERVAL   6
-#define NET_IPV4_ARP_CONFIRM_TIMEOUT	7
-#define NET_IPV4_TCP_VEGAS_CONG_AVOID   8
+enum
+{
+	NET_IPV4_ARP_RES_TIME=1,
+	NET_IPV4_ARP_DEAD_RES_TIME,
+	NET_IPV4_ARP_MAX_TRIES,
+	NET_IPV4_ARP_TIMEOUT,
+	NET_IPV4_ARP_CHECK_INTERVAL,
+	NET_IPV4_ARP_CONFIRM_INTERVAL,
+	NET_IPV4_ARP_CONFIRM_TIMEOUT,
+	NET_IPV4_TCP_VEGAS_CONG_AVOID,
+	NET_IPV4_FORWARDING,
+	NET_IPV4_DEFAULT_TTL,
+	NET_IPV4_RFC1812_FILTER,
+	NET_IPV4_LOG_MARTIANS,
+	NET_IPV4_SOURCE_ROUTE,
+	NET_IPV4_ADDRMASK_AGENT,
+	NET_IPV4_BOOTP_AGENT,
+	NET_IPV4_BOOTP_RELAY,
+	NET_IPV4_FIB_MODEL,
+	NET_IPV4_NO_PMTU_DISC,
+	NET_IPV4_ACCEPT_REDIRECTS,
+	NET_IPV4_SECURE_REDIRECTS,
+	NET_IPV4_RFC1620_REDIRECTS,
+};
+
 
 /* /proc/sys/net/ipv6 */
 #define NET_IPV6_FORWARDING		1
diff -u --recursive --new-file v2.1.14/linux/include/linux/tpqic02.h linux/include/linux/tpqic02.h
--- v2.1.14/linux/include/linux/tpqic02.h	Mon Sep 30 11:21:09 1996
+++ linux/include/linux/tpqic02.h	Thu Dec 12 17:34:57 1996
@@ -12,7 +12,7 @@
 
 #include <linux/config.h>
 
-#if CONFIG_QIC02_TAPE
+#if defined(CONFIG_QIC02_TAPE) || defined (CONFIG_QIC02_TAPE_MODULE)
 
 /* need to have QIC02_TAPE_DRIVE and QIC02_TAPE_IFC expand to something */
 #include <linux/mtio.h>
diff -u --recursive --new-file v2.1.14/linux/include/linux/trdevice.h linux/include/linux/trdevice.h
--- v2.1.14/linux/include/linux/trdevice.h	Sun Mar 24 13:33:25 1996
+++ linux/include/linux/trdevice.h	Thu Dec 12 16:54:20 1996
@@ -31,8 +31,7 @@
 extern int		tr_header(struct sk_buff *skb, struct device *dev,
 				   unsigned short type, void *daddr,
 				   void *saddr, unsigned len);
-extern int		tr_rebuild_header(void *buff, struct device *dev,
-			unsigned long raddr, struct sk_buff *skb);
+extern int		tr_rebuild_header(struct sk_buff *skb);
 extern unsigned short	tr_type_trans(struct sk_buff *skb, struct device *dev);
 
 #endif
diff -u --recursive --new-file v2.1.14/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.1.14/linux/include/linux/tty.h	Sun Nov 10 20:12:15 1996
+++ linux/include/linux/tty.h	Thu Dec 12 17:33:06 1996
@@ -213,7 +213,7 @@
 	unsigned long flags;
 	int count;
 	struct winsize winsize;
-	unsigned char stopped:1, hw_stopped:1, packet:1;
+	unsigned char stopped:1, hw_stopped:1, flow_stopped:1, packet:1;
 	unsigned char ctrl_status;
 
 	struct tty_struct *link;
@@ -294,6 +294,7 @@
 extern int stl_init(void);
 extern int stli_init(void);
 extern int riscom8_init(void);
+extern int esp_init(void);
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
 			      const char *routine);
diff -u --recursive --new-file v2.1.14/linux/include/linux/uio.h linux/include/linux/uio.h
--- v2.1.14/linux/include/linux/uio.h	Tue Nov 19 15:53:58 1996
+++ linux/include/linux/uio.h	Thu Dec 12 16:54:20 1996
@@ -24,7 +24,12 @@
  *	UIO_MAXIOV shall be at least 16 1003.1g (5.4.1.1)
  */
  
+#define UIO_FASTIOV	8
+#define UIO_MAXIOV	1024
+#if 0
 #define UIO_MAXIOV	16	/* Maximum iovec's in one operation 
 				   16 matches BSD */
+                                /* Beg pardon: BSD has 1024 --ANK */
+#endif
 
 #endif
diff -u --recursive --new-file v2.1.14/linux/include/linux/watchdog.h linux/include/linux/watchdog.h
--- v2.1.14/linux/include/linux/watchdog.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/watchdog.h	Thu Dec 12 16:54:20 1996
@@ -0,0 +1,39 @@
+/*
+ *	Generic watchdog defines. Derived from..
+ *
+ * Berkshire PC Watchdog Defines
+ * by Ken Hollis <khollis@bitgate.com>
+ *
+ */
+
+#include <linux/ioctl.h>
+
+#define	WATCHDOG_IOCTL_BASE	'W'
+
+struct watchdog_info {
+	u32 options;		/* Options the card/driver supports */
+	u32 firmware_version;	/* Firmware version of the card */
+	u8  identity[32];	/* Identity of the board */
+};
+
+#define	WDIOC_GETSUPPORT	_IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)
+#define	WDIOC_GETSTATUS		_IOR(WATCHDOG_IOCTL_BASE, 1, int)
+#define	WDIOC_GETBOOTSTATUS	_IOR(WATCHDOG_IOCTL_BASE, 2, int)
+#define	WDIOC_GETTEMP		_IOR(WATCHDOG_IOCTL_BASE, 3, int)
+#define	WDIOC_SETOPTIONS	_IOR(WATCHDOG_IOCTL_BASE, 4, int)
+#define	WDIOC_KEEPALIVE		_IOR(WATCHDOG_IOCTL_BASE, 5, int)
+
+#define	WDIOF_UNKNOWN		-1	/* Unknown flag error */
+#define	WDIOS_UNKNOWN		-1	/* Unknown status error */
+
+#define	WDIOF_OVERHEAT		0x0001	/* Reset due to CPU overheat */
+#define	WDIOF_FANFAULT		0x0002	/* Fan failed */
+#define	WDIOF_EXTERN1		0x0004	/* External relay 1 */
+#define	WDIOF_EXTERN2		0x0008	/* External relay 2 */
+#define	WDIOF_POWERUNDER	0x0010	/* Power bad/power fault */
+#define	WDIOF_CARDRESET		0x0020	/* Card previously reset the CPU */
+#define WDIOF_POWEROVER		0x0040	/* Power over voltage */
+#define	WDIOF_KEEPALIVEPING	0x8000	/* Keep alive ping reply */
+
+#define	WDIOS_DISABLECARD	0x0001	/* Turn off the watchdog timer */
+#define	WDIOS_ENABLECARD	0x0002	/* Turn on the watchdog timer */
diff -u --recursive --new-file v2.1.14/linux/include/linux/wireless.h linux/include/linux/wireless.h
--- v2.1.14/linux/include/linux/wireless.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/wireless.h	Thu Dec 12 16:54:20 1996
@@ -0,0 +1,283 @@
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version :	2	30.10.96
+ *
+ * Authors :	Jean II - HPLB - MCD <jt@hplb.hpl.hp.com>
+ */
+
+#ifndef _LINUX_WIRELESS_H
+#define _LINUX_WIRELESS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Basically, the wireless extensions are for now a set of standard ioctl
+ * call + /proc/net/wireless
+ *
+ * The entry /proc/net/wireless give statistics and information on the
+ * driver.
+ * This is better than having each driver having its entry because
+ * its centralised and we may remove the driver module safely.
+ *
+ * Ioctl are used to configure the driver and issue commands.  This is
+ * better than command line options of insmod because we may want to
+ * change dynamically (while the driver is running) some parameters.
+ *
+ * The ioctl mechanimsm are copied from standard devices ioctl.
+ * We have the list of command plus a structure descibing the
+ * data exchanged...
+ * Note that to add these ioctl, I was obliged to modify :
+ *	net/core/dev.c (two place + add include)
+ *	net/ipv4/af_inet.c (one place + add include)
+ *
+ * /proc/net/wireless is a copy of /proc/net/dev.
+ * We have a structure for data passed from the driver to /proc/net/wireless
+ * Too add this, I've modified :
+ *	net/core/dev.c (two other places)
+ *	include/linux/netdevice.h (one place)
+ *	include/linux/proc_fs.h (one place)
+ *
+ * Do not add here things that are redundant with other mechanisms
+ * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
+ * wireless specific.
+ *
+ * These wireless extensions are not magic : each driver has to provide
+ * support for them...
+ *
+ * IMPORTANT NOTE : As everything in the kernel, this is very much a
+ * work in progress. Contact me if you have ideas of improvements...
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/types.h>		/* for "caddr_t" et al		*/
+#include <linux/socket.h>		/* for "struct sockaddr" et al	*/
+#include <linux/if.h>			/* for IFNAMSIZ and co... */
+
+/**************************** CONSTANTS ****************************/
+
+/* --------------------------- VERSION --------------------------- */
+/*
+ * This constant is used to know the availability of the wireless
+ * extensions and to know which version of wireless extensions it is
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
+ */
+#define WIRELESS_EXT	2
+
+/* -------------------------- IOCTL LIST -------------------------- */
+
+/* Basic operations */
+#define SIOCSIWNAME	0x8B00		/* Unused ??? */
+#define SIOCGIWNAME	0x8B01		/* get name */
+#define SIOCSIWNWID	0x8B02		/* set network id */
+#define SIOCGIWNWID	0x8B03		/* get network id */
+#define SIOCSIWFREQ	0x8B04		/* set channel/frequency */
+#define SIOCGIWFREQ	0x8B05		/* get channel/frequency */
+#define SIOCSIWENCODE	0x8B06		/* set encoding info */
+#define SIOCGIWENCODE	0x8B07		/* get encoding info */
+
+/* Informative stuff */
+#define SIOCSIWRANGE	0x8B0A		/* Unused ??? */
+#define SIOCGIWRANGE	0x8B0B		/* Get range of parameters */
+#define SIOCSIWPRIV	0x8B0C		/* Unused ??? */
+#define SIOCGIWPRIV	0x8B0D		/* get private ioctl interface info */
+
+/* Mobile IP support */
+#define SIOCSIWSPY	0x8B10		/* set spy addresses */
+#define SIOCGIWSPY	0x8B11		/* get spy info (quality of link) */
+
+/* ------------------------- IOCTL STUFF ------------------------- */
+
+/* The first and the last (range) */
+#define SIOCIWFIRST	0x8B00
+#define SIOCIWLAST	0x8B13
+
+/* Even : get (world access), odd : set (root access) */
+#define IW_IS_SET(cmd)	(!((cmd) & 0x1))
+#define IW_IS_GET(cmd)	((cmd) & 0x1)
+
+/* ------------------------- PRIVATE INFO ------------------------- */
+/*
+ * The following is used with SIOCGIWPRIV. It allow a driver to define
+ * the interface (name, type of data) for its private ioctl.
+ * Privates ioctl are SIOCDEVPRIVATE -> SIOCDEVPRIVATE + 0xF
+ */
+
+#define IW_PRIV_TYPE_MASK	0x7000	/* Type of arguments */
+#define IW_PRIV_TYPE_NONE	0x0000
+#define IW_PRIV_TYPE_BYTE	0x1000	/* Char as number */
+#define IW_PRIV_TYPE_CHAR	0x2000	/* Char as character */
+#define IW_PRIV_TYPE_INT	0x4000	/* 32 bits int */
+#define IW_PRIV_TYPE_FLOAT	0x5000
+
+#define IW_PRIV_SIZE_FIXED	0x0800	/* Variable or fixed nuber of args */
+
+#define IW_PRIV_SIZE_MASK	0x07FF	/* Max number of those args */
+
+/*
+ * Note : if the number of args is fixed and the size < 16 octets,
+ * instead of passing a pointer we will put args in the iwreq struct...
+ */
+
+/* ----------------------- OTHER CONSTANTS ----------------------- */
+
+/* Maximum frequencies in the range struct */
+#define IW_MAX_FREQUENCIES	16
+/* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+/* Maximum of address that you may set with SPY */
+#define IW_MAX_SPY		8
+
+/****************************** TYPES ******************************/
+
+/* --------------------------- SUBTYPES --------------------------- */
+
+/*
+ *	A frequency
+ */
+ 
+typedef struct 
+{
+	__u32	value;
+	__u16	scale;
+} wireless_freq_t;
+
+/*
+ *	Quality of the link
+ */
+
+struct	iw_quality
+{
+	__u8		qual;		/* link quality (SNR or better...) */
+	__u8		level;		/* signal level */
+	__u8		noise;		/* noise level */
+	__u8		updated;	/* Flags to know if updated */
+};
+
+/*
+ *	Packet discarded in the wireless adapter due to
+ *	"wireless" specific problems...
+ */
+ 
+struct	iw_discarded
+{
+	__u32		nwid;		/* Wrong nwid */
+	__u32		codec;		/* Unable to core/decode */
+	__u32		misc;		/* Others cases */
+};
+
+/* ------------------------ WIRELESS STATS ------------------------ */
+/*
+ * Wireless statistics (used for /proc/net/wireless)
+ */
+
+struct	iw_statistics
+{
+	__u8		status;		/* Status
+					 * - device dependant for now */
+
+	struct iw_quality	qual;		/* Quality of the link
+						 * (instant/mean/max) */
+	struct iw_discarded	discard;	/* Packet discarded counts */
+};
+
+/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * The structure to exchange data for ioctl.
+ * This structure is the same as 'struct ifreq', but (re)defined for
+ * convenience...
+ *
+ * Note that it should fit on the same memory footprint !
+ * You should check this when increasing the above structures (16 octets)
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ */
+struct	iwreq 
+{
+	union
+	{
+		char	ifrn_name[IFNAMSIZ];	/* if name, e.g. "en0" */
+	} ifr_ifrn;
+
+	/* Data part */
+	union
+	{
+		/* Config - generic */
+		char	name[IFNAMSIZ];
+		/* Name : used to verify the presence of  wireless extensions.
+		 * Name of the protocol/provider... */
+
+		struct		/* network id (or domain) : used to to */
+		{		/* create logical channels on the air */
+			__u32	nwid;		/* value */
+			__u8	on;		/* active/unactive nwid */
+		}	nwid;
+
+		wireless_freq_t	freq;	/* frequency or channel :
+					 * 0-1000 = channel
+					 * > 1000 = frequency in Hz */
+					 
+		struct		/* Encoding stuff */
+		{
+			__u8	method;		/* Algorithm number / off */
+			__u64	data;		/* Data used for algorithm */
+		}	encoder;
+
+		struct		/* For all data bigger than 16 octets */
+		{
+			caddr_t	pointer;	/* Pointer to the data
+						 * (in user space) */
+			__u16	length;		/* fields or byte size */
+			__u16	flags;		/* Unused */
+		}	data;
+	}	u;
+};
+
+/*	-------------------------- IOCTL DATA --------------------------	*/
+
+/*
+ *	For those ioctl which want to exchange mode data that what could
+ *	fit in the above structure...
+ */
+
+/*
+ *	Range of parameters
+ */
+
+struct	iw_range
+{
+	/* Informative stuff (to choose between different interface) */
+	__u32		throughput;	/* To give an idea... */
+
+	/* NWID (or domain id) */
+	__u32		min_nwid;	/* Minimal NWID we are able to set */
+	__u32		max_nwid;	/* Maximal NWID we are able to set */
+
+	/* Frequency */
+	__u16		num_channels;	/* Number of channels [0; num - 1] */
+	__u8		num_frequency;	/* Number of entry in the list */
+	wireless_freq_t	freq[IW_MAX_FREQUENCIES];	/* list */
+
+	/* Note : this frequency list doesn't need to fit channel numbers */
+
+	/* Encoder stuff */
+
+	/* Quality of link & SNR stuff */
+	struct iw_quality	max_qual;	/* Quality of the link */
+};
+
+/*
+ * Private ioctl interface information
+ */
+ 
+struct	iw_priv_args
+{
+	__u32		cmd;		/* Number of the ioctl to issue */
+	__u16		set_args;	/* Type and number of args */
+	__u16		get_args;	/* Type and number of args */
+	char		name[IFNAMSIZ];	/* Name of the extension */
+};
+
+#endif	/* _LINUX_WIRELESS_H */
diff -u --recursive --new-file v2.1.14/linux/include/net/af_unix.h linux/include/net/af_unix.h
--- v2.1.14/linux/include/net/af_unix.h	Mon May 13 07:17:23 1996
+++ linux/include/net/af_unix.h	Thu Dec 12 16:54:21 1996
@@ -7,8 +7,29 @@
 typedef struct sock unix_socket;
 extern void unix_gc(void);
 
-extern unix_socket *unix_socket_list;
+#define UNIX_HASH_SIZE	16
 
-#define UNIX_MAX_FD	8
+extern unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
+
+#define forall_unix_sockets(i, s) for (i=0; i<=UNIX_HASH_SIZE; i++) \
+                                    for (s=unix_socket_table[i]; s; s=s->next)
+
+struct unix_address
+{
+	int		refcnt;
+	int		len;
+	unsigned	hash;
+	struct sockaddr_un name[0];
+};
+
+struct unix_skb_parms
+{
+	struct ucred		creds;		/* Skb credentials	*/
+	struct scm_fp_list	*fp;		/* Passed files		*/
+	unsigned		attr;		/* Special attributes	*/
+};
+
+#define UNIXCB(skb) 	(*(struct unix_skb_parms*)&((skb)->cb))
+#define UNIXCREDS(skb)	(&UNIXCB((skb)).creds)
 
 #endif
diff -u --recursive --new-file v2.1.14/linux/include/net/arp.h linux/include/net/arp.h
--- v2.1.14/linux/include/net/arp.h	Tue Nov 14 16:03:57 1995
+++ linux/include/net/arp.h	Thu Dec 12 17:38:39 1996
@@ -2,16 +2,21 @@
 #ifndef _ARP_H
 #define _ARP_H
 
+#include <linux/if_arp.h>
+
 extern void	arp_init(void);
 extern int	arp_rcv(struct sk_buff *skb, struct device *dev,
 			struct packet_type *pt);
 extern int	arp_query(unsigned char *haddr, u32 paddr, struct device *dev);
-extern int	arp_find(unsigned char *haddr, u32 paddr,
-		struct device *dev, u32 saddr, struct sk_buff *skb);
+extern int	arp_find(unsigned char *haddr, struct sk_buff *skb);
+extern int	arp_find_1(unsigned char *haddr, struct dst_entry* dst, struct dst_entry *neigh);
 extern int	arp_ioctl(unsigned int cmd, void *arg);
 extern void     arp_send(int type, int ptype, u32 dest_ip, 
 			 struct device *dev, u32 src_ip, 
 			 unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th);
+extern int	arp_req_set(struct arpreq *r, struct device *dev);
+extern int	arp_req_delete(struct arpreq *r, struct device *dev);
 extern int	arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short type, __u32 daddr);
 extern int	arp_update_cache(struct hh_cache * hh);
+extern struct dst_entry *arp_find_neighbour(struct dst_entry *dst, int);
 #endif	/* _ARP_H */
diff -u --recursive --new-file v2.1.14/linux/include/net/ax25.h linux/include/net/ax25.h
--- v2.1.14/linux/include/net/ax25.h	Tue Nov 19 15:53:58 1996
+++ linux/include/net/ax25.h	Thu Dec 12 17:35:21 1996
@@ -205,11 +205,11 @@
 extern struct device *ax25rtr_get_dev(ax25_address *);
 extern int  ax25_encapsulate(struct sk_buff *, struct device *, unsigned short,
 	void *, void *, unsigned int);
-extern int  ax25_rebuild_header(unsigned char *, struct device *, unsigned long, struct sk_buff *);
+extern int  ax25_rebuild_header(struct sk_buff *);
 extern ax25_uid_assoc *ax25_uid_list;
 extern int  ax25_uid_policy;
 extern ax25_address *ax25_findbyuid(uid_t);
-extern void ax25_queue_xmit(struct sk_buff *, struct device *, int);
+extern void ax25_queue_xmit(struct sk_buff *);
 extern int  ax25_dev_is_dama_slave(struct device *);	/* dl1bke 951121 */
 
 #include <net/ax25call.h>
diff -u --recursive --new-file v2.1.14/linux/include/net/dst.h linux/include/net/dst.h
--- v2.1.14/linux/include/net/dst.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/dst.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,123 @@
+/*
+ * net/dst.h	Protocol independent destination cache definitions.
+ *
+ * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifndef _NET_DST_H
+#define _NET_DST_H
+
+/*
+ * 0 - no debugging messages
+ * 1 - rare events and bugs (default)
+ * 2 - trace mode.
+ */
+#ifdef  NO_ANK_FIX
+#define RT_CACHE_DEBUG		0
+#else
+#define RT_CACHE_DEBUG		1
+#endif
+
+#define DST_GC_MIN	(1*HZ)
+#define DST_GC_INC	(5*HZ)
+#define DST_GC_MAX	(120*HZ)
+
+struct sk_buff;
+
+struct dst_entry
+{
+	struct dst_entry        *next;
+	atomic_t		refcnt;
+	atomic_t		use;
+	struct device	        *dev;
+	char			obsolete;
+	char			priority;
+	char			__pad1, __pad2;
+	unsigned long		lastuse;
+	unsigned		window;
+	unsigned		pmtu;
+	unsigned		rtt;
+	int			error;
+
+	struct dst_entry	*neighbour;
+	struct hh_cache		*hh;
+
+	int			(*input)(struct sk_buff*);
+	int			(*output)(struct sk_buff*);
+
+	struct  dst_ops	        *ops;
+	
+	char			info[0];
+};
+
+
+struct dst_ops
+{
+	unsigned short		family;
+	struct dst_entry *	(*check)(struct dst_entry *);
+	struct dst_entry *	(*reroute)(struct dst_entry *);
+	void			(*destroy)(struct dst_entry *);
+};
+
+extern struct dst_entry * dst_garbage_list;
+extern atomic_t	dst_total;
+
+static __inline__
+struct dst_entry * dst_clone(struct dst_entry * dst)
+{
+	if (dst)
+		atomic_inc(&dst->refcnt);
+	return dst;
+}
+
+static __inline__
+void dst_release(struct dst_entry * dst)
+{
+	if (dst)
+		atomic_dec(&dst->refcnt);
+}
+
+static __inline__
+struct dst_entry * dst_check(struct dst_entry ** dst_p)
+{
+	struct dst_entry * dst = *dst_p;
+	if (dst && dst->obsolete)
+		dst = dst->ops->check(dst);
+	return (*dst_p = dst);
+}
+
+static __inline__
+struct dst_entry * dst_reroute(struct dst_entry ** dst_p)
+{
+	struct dst_entry * dst = *dst_p;
+	if (dst && dst->obsolete)
+		dst = dst->ops->reroute(dst);
+	return (*dst_p = dst);
+}
+
+static __inline__
+void dst_destroy(struct dst_entry * dst)
+{
+	if (dst->neighbour)
+		dst_release(dst->neighbour);
+	if (dst->ops->destroy)
+		dst->ops->destroy(dst);
+	kfree(dst);
+	atomic_dec(&dst_total);
+}
+
+extern void * dst_alloc(int size, struct dst_ops * ops);
+extern void __dst_free(struct dst_entry * dst);
+
+static __inline__
+void dst_free(struct dst_entry * dst)
+{
+	if (!dst->refcnt) {
+		dst_destroy(dst);
+		return;
+	}
+	__dst_free(dst);
+}
+
+#endif /* _NET_DST_H */
diff -u --recursive --new-file v2.1.14/linux/include/net/icmp.h linux/include/net/icmp.h
--- v2.1.14/linux/include/net/icmp.h	Mon Sep 30 11:24:39 1996
+++ linux/include/net/icmp.h	Thu Dec 12 17:38:39 1996
@@ -28,14 +28,10 @@
 extern struct icmp_mib icmp_statistics;
 
 extern void	icmp_send(struct sk_buff *skb_in,  int type, int code,
-			  unsigned long info, struct device *dev);
-extern int	icmp_rcv(struct sk_buff *skb1, struct device *dev,
-			 struct options *opt, __u32 daddr,
-			 unsigned short len, __u32 saddr,
-			 int redo, struct inet_protocol *protocol);
-extern int	icmp_ioctl(struct sock *sk, int cmd,
-			   unsigned long arg);
-extern void	icmp_init(struct proto_ops *ops);
+			  unsigned long info);
+extern int	icmp_rcv(struct sk_buff *skb, unsigned short len);
+extern int	icmp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+extern void	icmp_init(struct net_proto_family *ops);
 
 /* CONFIG_IP_TRANSPARENT_PROXY */
 extern int	icmp_chkaddr(struct sk_buff *skb);
diff -u --recursive --new-file v2.1.14/linux/include/net/inet_common.h linux/include/net/inet_common.h
--- v2.1.14/linux/include/net/inet_common.h	Sat Nov 30 12:03:11 1996
+++ linux/include/net/inet_common.h	Thu Dec 12 16:54:21 1996
@@ -1,7 +1,8 @@
 #ifndef _INET_COMMON_H
 #define _INET_COMMON_H
 
-extern struct proto_ops		inet_proto_ops;
+extern struct proto_ops		inet_stream_ops;
+extern struct proto_ops		inet_dgram_ops;
 extern struct sock *		tcp_sock_array[SOCK_ARRAY_SIZE];
 extern struct sock *		udp_sock_array[SOCK_ARRAY_SIZE];
 
@@ -15,19 +16,20 @@
 					      struct sock *sk);
 extern int			inet_release(struct socket *sock, 
 					     struct socket *peer);
-extern int			inet_connect(struct socket *sock, 
-					     struct sockaddr * uaddr,
-					     int addr_len, int flags);
+extern int			inet_stream_connect(struct socket *sock,
+						    struct sockaddr * uaddr,
+						    int addr_len, int flags);
+extern int			inet_dgram_connect(struct socket *sock, 
+						   struct sockaddr * uaddr,
+						   int addr_len, int flags);
 extern int			inet_accept(struct socket *sock, 
 					    struct socket *newsock, int flags);
 extern int			inet_recvmsg(struct socket *sock, 
 					     struct msghdr *ubuf, 
-					     int size, int noblock, 
-					     int flags, int *addr_len );
+					     int size, int flags, struct scm_cookie *scm);
 extern int			inet_sendmsg(struct socket *sock, 
 					     struct msghdr *msg, 
-					     int size, int noblock, 
-					     int flags);
+					     int size, struct scm_cookie *scm);
 extern int			inet_shutdown(struct socket *sock, int how);
 extern int			inet_select(struct socket *sock, int sel_type,
 					    select_table *wait);
diff -u --recursive --new-file v2.1.14/linux/include/net/ip.h linux/include/net/ip.h
--- v2.1.14/linux/include/net/ip.h	Fri Nov 15 23:49:10 1996
+++ linux/include/net/ip.h	Thu Dec 12 17:38:39 1996
@@ -33,6 +33,26 @@
 
 #include <net/sock.h>	/* struct sock */
 
+struct inet_skb_parm
+{
+	struct ip_options	opt;		/* Compiled IP options		*/
+	u16			redirport;	/* Redirect port		*/
+	unsigned char		flags;
+	char			vif;
+
+#define IPSKB_MASQUERADED	1
+#define IPSKB_TRANSLATED	2
+#define IPSKB_TUNNELED		4
+};
+
+struct ipcm_cookie
+{
+	u32			addr;
+	struct ip_options	*opt;
+};
+
+#define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb))
+
 /* IP flags. */
 #define IP_CE		0x8000		/* Flag: "Congestion"		*/
 #define IP_DF		0x4000		/* Flag: "Don't Fragment"	*/
@@ -41,13 +61,9 @@
 
 #define IP_FRAG_TIME	(30 * HZ)		/* fragment lifetime	*/
 
-#ifdef CONFIG_IP_MULTICAST
 extern void		ip_mc_dropsocket(struct sock *);
 extern void		ip_mc_dropdevice(struct device *dev);
 extern int		ip_mc_procinfo(char *, char **, off_t, int, int);
-#endif
-
-#include <net/ip_forward.h> 
 
 /* Describe an IP fragment. */
 struct ipfrag 
@@ -83,72 +99,119 @@
  *	Functions provided by ip.c
  */
 
-extern void		ip_print(const struct iphdr *ip);
 extern int		ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
-extern void		ip_route_check(__u32 daddr); 
-extern int 		ip_send(struct rtable *rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr);
-extern int 		ip_build_header(struct sk_buff *skb,
-					__u32 saddr,
-					__u32 daddr,
-					struct device **dev, int type,
-					struct options *opt, int len,
-					int tos,int ttl,struct rtable **rp);
+extern int		ip_build_pkt(struct sk_buff *skb, struct sock *sk,
+				     u32 saddr, u32 daddr,
+				     struct ip_options *opt);
+extern int 		ip_build_header(struct sk_buff *skb, struct sock *sk);
 extern int		ip_rcv(struct sk_buff *skb, struct device *dev,
 			       struct packet_type *pt);
-extern int		ip_options_echo(struct options * dopt, struct options * sopt,
-					__u32 daddr, __u32 saddr,
-					struct sk_buff * skb);
-extern int		ip_options_compile(struct options * opt, struct sk_buff * skb);
+extern int		ip_local_deliver(struct sk_buff *skb);
+extern int		ip_mr_input(struct sk_buff *skb);
+extern int		ip_output(struct sk_buff *skb);
+extern int		ip_mc_output(struct sk_buff *skb);
+#ifdef CONFIG_IP_ACCT
+extern int		ip_acct_output(struct sk_buff *skb);
+#else
+#define ip_acct_output	dev_queue_xmit
+#endif
+extern void		ip_fragment(struct sk_buff *skb, int, int (*out)(struct sk_buff*));
+extern struct sk_buff *	ip_reply(struct sk_buff *skb, int payload);
+extern int		ip_do_nat(struct sk_buff *skb);
 extern void		ip_send_check(struct iphdr *ip);
 extern int		ip_id_count;			  
-extern void		ip_queue_xmit(struct sock *sk,
-				      struct device *dev, struct sk_buff *skb,
-				      int free);
+extern void		ip_queue_xmit(struct sk_buff *skb);
 extern void		ip_init(void);
 extern int		ip_build_xmit(struct sock *sk,
 				      int getfrag (const void *,
-						   __u32,
 						   char *,
 						   unsigned int,
 						   unsigned int),
 				      const void *frag,
 				      unsigned short int length,
-				      __u32 daddr,
-				      __u32 saddr,
-				      struct options * opt,
-				      int flags,
-				      int type,
-				      int noblock);
+				      struct ipcm_cookie *ipc,
+				      struct rtable *rt,
+				      int flags);
+
+
+static __inline__
+void ip_send(struct sk_buff *skb)
+{
+	ip_ll_header(skb);
+
+	if (skb->len > skb->dev->mtu + skb->dev->hard_header_len)
+		ip_fragment(skb, 0, ip_acct_output);
+	else
+		ip_acct_output(skb);
+}
+
+static __inline__
+int ip_decrease_ttl(struct iphdr *iph)
+{
+	u16 check = iph->check;
+	check = ntohs(check) + 0x0100;
+	if ((check & 0xFF00) == 0)
+		check++;		/* carry overflow */
+	iph->check = htons(check);
+	return --iph->ttl;
+}
 
 extern struct ip_mib	ip_statistics;
 
+struct ipv4_config
+{
+	int	accept_redirects;
+	int	secure_redirects;
+	int	rfc1620_redirects;
+	int	rfc1812_filter;
+	int	addrmask_agent;
+	int	log_martians;
+	int	source_route;
+	int	multicast_route;
+	int	bootp_agent;
+	int	bootp_relay;
+	int	fib_model;
+	int	no_pmtu_disc;
+};
+
+extern struct ipv4_config ipv4_config;
+
+#define IS_ROUTER	(ip_statistics.IpForwarding == 1)
+
 /*
  *	Functions provided by ip_fragment.o
  */
  
-struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev);
-void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag);
+struct sk_buff *ip_defrag(struct sk_buff *skb);
 
 /*
  *	Functions provided by ip_forward.c
  */
  
-extern int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, __u32 target_addr);
+extern int ip_forward(struct sk_buff *skb);
+extern int ip_net_unreachable(struct sk_buff *skb);
  
 /*
  *	Functions provided by ip_options.c
  */
  
-extern void ip_options_build(struct sk_buff *skb, struct options *opt, __u32 daddr, __u32 saddr, int is_frag);
-extern int ip_options_echo(struct options *dopt, struct options *sopt, __u32 daddr, __u32 saddr, struct sk_buff *skb);
+extern void ip_options_build(struct sk_buff *skb, struct ip_options *opt, u32 daddr, u32 saddr, int is_frag);
+extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb);
 extern void ip_options_fragment(struct sk_buff *skb);
-extern int ip_options_compile(struct options *opt, struct sk_buff *skb);
+extern int ip_options_compile(struct ip_options *opt, struct sk_buff *skb);
+extern int ip_options_getfromuser(struct ip_options **optp, unsigned char *data, int optlen);
+extern void ip_options_undo(struct ip_options * opt);
+extern void ip_forward_options(struct sk_buff *skb);
+extern int ip_options_rcv_srr(struct sk_buff *skb);
 
 /*
  *	Functions provided by ip_sockglue.c
  */
 
-extern int 		ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
-extern int 		ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
-  
+extern void	ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb);
+extern int	ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc, struct device **devp);
+extern int	ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
+extern int	ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
+
+extern int		ipv4_backlog_rcv(struct sock *sk, struct sk_buff *skb);  
 #endif	/* _IP_H */
diff -u --recursive --new-file v2.1.14/linux/include/net/ip_fib.h linux/include/net/ip_fib.h
--- v2.1.14/linux/include/net/ip_fib.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/ip_fib.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,133 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET  is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Definitions for the Forwarding Information Base.
+ *
+ * Authors:	A.N.Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *		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.
+ */
+
+#ifndef _NET_IP_FIB_H
+#define _NET_IP_FIB_H
+
+
+struct fib_node
+{
+	struct fib_node		*fib_next;
+	u32			fib_key;
+	struct fib_info		*fib_info;
+	short			fib_metric;
+	u8			fib_tos;
+	u8			fib_flag;
+};
+
+#define FIBFLG_DOWN		1	/* Ignore this node	*/
+#define FIBFLG_THROW		2	/* Class lookup failed	*/
+#define FIBFLG_REJECT		4	/* Route lookup failed	*/
+
+#define MAGIC_METRIC		0x7FFF
+
+/*
+ * This structure contains data shared by many of routes.
+ */	
+
+struct fib_info
+{
+	struct fib_info		*fib_next;
+	struct fib_info		*fib_prev;
+	u32			fib_gateway;
+	struct device		*fib_dev;
+	int			fib_refcnt;
+	unsigned long		fib_window;
+	unsigned		fib_flags;
+	unsigned short		fib_mtu;
+	unsigned short		fib_irtt;
+};
+
+struct fib_zone
+{
+	struct fib_zone	*fz_next;
+	struct fib_node	**fz_hash;
+	int		fz_nent;
+	int		fz_divisor;
+	u32		fz_hashmask;
+	int		fz_logmask;
+	u32		fz_mask;
+};
+
+struct fib_class
+{
+	unsigned char	cl_id;
+	unsigned char	cl_auto;
+	struct fib_zone	*fib_zones[33];
+	struct fib_zone	*fib_zone_list;
+	int		cl_users;
+};
+
+struct fib_rule
+{
+	struct fib_rule *cl_next;
+	struct fib_class *cl_class;
+	u32		cl_src;
+	u32		cl_srcmask;
+	u32		cl_dst;
+	u32		cl_dstmask;
+	u32		cl_srcmap;
+	u8		cl_action;
+	u8		cl_flags;
+	u8		cl_tos;
+	u8		cl_preference;
+	struct device	*cl_dev;
+};
+
+struct fib_result
+{
+	struct fib_node *f;
+	struct fib_rule *fr;
+	int		 fm;
+};
+
+void ip_fib_init(void);
+unsigned ip_fib_chk_addr(u32 addr);
+int ip_fib_chk_default_gw(u32 addr, struct device*);
+
+int fib_lookup(struct fib_result *, u32 daddr, u32 src, u8 tos, struct device *devin,
+	       struct device *devout);
+
+static __inline__ struct fib_info *
+fib_lookup_info(u32 dst, u32 src, u8 tos, struct device *devin,
+		struct device *devout)
+{
+	struct fib_result res;
+	if (fib_lookup(&res, dst, src, tos, devin, devout) < 0)
+		return NULL;
+	return res.f->fib_info;
+}
+
+static __inline__ struct device * get_gw_dev(u32 gw, struct device *dev)
+{
+	struct fib_info * fi;
+
+	fi = fib_lookup_info(gw, 0, 1, &loopback_dev, dev);
+	if (fi)
+		return fi->fib_dev;
+	return NULL;
+}
+
+extern int		ip_rt_event(int event, struct device *dev);
+extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
+extern void		ip_rt_change_broadcast(struct device *, u32);
+extern void		ip_rt_change_dstaddr(struct device *, u32);
+extern void		ip_rt_change_netmask(struct device *, u32);
+extern void		ip_rt_multicast_event(struct device *dev);
+
+extern struct device *	ip_dev_find_tunnel(u32 daddr, u32 saddr);
+
+
+#endif  _NET_FIB_H
diff -u --recursive --new-file v2.1.14/linux/include/net/ip_forward.h linux/include/net/ip_forward.h
--- v2.1.14/linux/include/net/ip_forward.h	Fri Jul 19 08:24:05 1996
+++ linux/include/net/ip_forward.h	Thu Jan  1 02:00:00 1970
@@ -1,11 +0,0 @@
-#ifndef __NET_IP_FORWARD_H
-#define __NET_IP_FORWARD_H
-
-#define IPFWD_FRAGMENT		1
-#define IPFWD_LASTFRAG		2
-#define IPFWD_MASQUERADED	4
-#define IPFWD_MULTICASTING	8
-#define IPFWD_MULTITUNNEL	0x10
-#define IPFWD_NOTTLDEC		0x20
-
-#endif
diff -u --recursive --new-file v2.1.14/linux/include/net/ipip.h linux/include/net/ipip.h
--- v2.1.14/linux/include/net/ipip.h	Mon Sep 11 20:15:53 1995
+++ linux/include/net/ipip.h	Thu Dec 12 16:54:21 1996
@@ -1,4 +1,8 @@
-extern int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, 
-		__u32 daddr, unsigned short len, __u32 saddr,
-                                   int redo, struct inet_protocol *protocol);
+#ifndef __NET_IPIP_H
+#define __NET_IPIP_H 1
+
+extern void ipip_err(struct sk_buff *skb, unsigned char*);
+extern int ipip_rcv(struct sk_buff *skb, unsigned short len);
                                    
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/include/net/ipv6_route.h linux/include/net/ipv6_route.h
--- v2.1.14/linux/include/net/ipv6_route.h	Thu Dec 12 17:02:47 1996
+++ linux/include/net/ipv6_route.h	Thu Dec 12 16:54:21 1996
@@ -183,7 +183,7 @@
 	{
 		if (rt->rt_nexthop)
 		{
-			ndisc_dec_neigh(rt->rt_nexthop);
+			neighbour_unlock(rt->rt_nexthop);
 		}
 
 		if (rt->rt_flags & RTI_DCACHE)
diff -u --recursive --new-file v2.1.14/linux/include/net/ipx.h linux/include/net/ipx.h
--- v2.1.14/linux/include/net/ipx.h	Mon Sep 30 11:21:33 1996
+++ linux/include/net/ipx.h	Thu Dec 12 17:35:21 1996
@@ -43,9 +43,6 @@
 	ipx_address	ipx_source __attribute__ ((packed));
 } ipx_packet;
 
-
-typedef struct sock ipx_socket;
-
 #include <net/ipxcall.h>
 extern int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
 extern void ipxrtr_device_down(struct device *dev);
@@ -62,7 +59,7 @@
 
 	/* socket support */
 	unsigned short	if_sknum;
-	ipx_socket	*if_sklist;
+	struct sock	*if_sklist;
 
 	/* administrative overhead */
 	int		if_ipx_offset;
diff -u --recursive --new-file v2.1.14/linux/include/net/llc.h linux/include/net/llc.h
--- v2.1.14/linux/include/net/llc.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/llc.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,146 @@
+#include <linux/skbuff.h>
+
+#define LLC_MODULE
+
+typedef struct llc_struct llc;
+typedef struct llc_struct *llcptr;
+
+/*
+ *	LLC operations object.
+ */
+ 
+typedef struct
+{
+	void (* data_indication_ep) (llcptr llc, struct sk_buff *skb);
+	/* unit data returns 0 to keep the data 1 to free it */
+	int (* unit_data_indication_ep) (llcptr llc, int ll, char *xid_data);       
+	void (* connect_indication_ep) (llcptr llc);
+	void (* connect_confirm_ep) (llcptr llc);
+	void (* data_connect_indication_ep) (llcptr llc);
+	void (* data_connect_confirm_ep) (llcptr llc);
+	void (* disconnect_indication_ep) (llcptr llc);
+	void (* disconnect_confirm_ep) (llcptr llc);
+	void (* reset_confirm_ep) (llcptr llc, char lr);
+	void (* reset_indication_ep) (llcptr llc, char lr);
+#define LOCAL		0
+#define REMOTE		1
+	void (* xid_indication_ep) (llcptr llc, int ll, char *xid_data);
+	void (* test_indication_ep) (llcptr llc, int ll, char *test_data);
+	void (* report_status_ep) (llcptr llc, char status);
+#define FRMR_RECEIVED	0
+#define FRMR_SENT	1
+#define REMOTE_BUSY	2
+#define REMOTE_NOT_BUSY	3
+} llc_ops;
+
+/*
+ *	LLC private data area structure.
+ */
+
+struct llc_struct
+{ 
+	char eye[4];			/* To recognize llc area in dump */
+	int retry_count;		/* LLC link state variables */
+	unsigned char s_flag;
+	unsigned char p_flag;
+	unsigned char f_flag;
+	unsigned char data_flag;
+	unsigned char cause_flag;
+	unsigned char vs;		/* Send state variable */
+	unsigned char vr;		/* Receive state variable */
+	unsigned char remote_busy;
+	unsigned char state;		/* Current state of type2 llc procedure */
+	int n1;				/* Maximum number of bytes in I pdu 7.8.2 */
+	int n2;				/* Naximum number of retransmissions 7.8.2 */
+	unsigned char k;		/* Transmit window size 7.8.4, tw in IBM doc*/ 
+	unsigned char rw;		/* Receive window size */
+	struct 
+	{     				
+		/*
+		 *	FRMR_RSP info field structure: 5.4.2.3.5 p55
+		 */
+
+		unsigned char cntl1;
+		unsigned char cntl2;
+		unsigned char vs;
+		unsigned char vr_cr;
+		unsigned char xxyz;
+	} frmr_info_fld;
+
+	/*
+	 *	Timers in 7.8.1 page 78 
+	 */
+
+#define P_TIMER         0
+#define REJ_TIMER       1
+#define ACK_TIMER       2 
+#define BUSY_TIMER      3
+	unsigned long timer_expire_time[4];	
+	unsigned char timer_state[4];	/* The state of each timer */
+#define TIMER_IDLE      0
+#define TIMER_RUNNING   1
+#define TIMER_EXPIRED   2
+	unsigned long timer_interval[4]; 
+	struct timer_list tl[4];
+
+	/* 
+	 *	Client entry points, called by the LLC 
+	 */
+	 
+	llc_ops *ops;
+	
+	/*
+	 *	Mux and Demux variables
+	 */
+	 
+	char * client_data;		/* Pointer to clients context */
+	unsigned char local_sap;
+	unsigned char remote_sap ;
+	char remote_mac[MAX_ADDR_LEN];  /* MAC address of remote session partner */
+	int  remote_mac_len;		/* Actual length of mac address */
+	int  mac_offset;		/* Source mac offset in skb */ 
+	struct device *dev;		/* Device we are attached to */
+		     
+	unsigned char llc_mode;		/* See doc 7.1 on p70 */
+#define MODE_ADM 1
+#define MODE_ABM 2
+
+	struct sk_buff *rtq_front;	/* oldest skb in the re-transmit queue */
+	struct sk_buff *rtq_back;
+
+	struct sk_buff *atq_front;	/* oldest skb in the await-transmit queue */
+	struct sk_buff *atq_back; 
+      
+	unsigned char xid_count;
+	char * nextllc;			/* ptr to next llc struct in proto chain */
+};
+
+#define ADD_TO_RTQ(skb) llc_add_to_queue(skb, &lp->rtq_front, &lp->rtq_back) 
+#define ADD_TO_ATQ(skb) llc_add_to_queue(skb, &lp->atq_front, &lp->atq_back) 
+
+void 		llc_cancel_timers(llcptr lp);
+int		llc_decode_frametype(frameptr fr);
+llcptr 		llc_find(void);
+int		llc_free_acknowledged_skbs(llcptr lp, unsigned char ack);
+void		llc_handle_xid_indication( char *chsp, short int ll, char *xid_data);
+void		llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, char type);
+void		llc_add_to_queue(struct sk_buff *skb, struct sk_buff **f, struct sk_buff **b);
+void		llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type);
+struct sk_buff *llc_pull_from_atq(llcptr lp); 
+int 		llc_resend_ipdu(llcptr lp, unsigned char ack_nr, unsigned char type, char p);
+void 		llc_sendpdu(llcptr lp, char type, char pf, int data_len, char *pdu_data);
+void 		llc_sendipdu(llcptr lp, char type, char pf, struct sk_buff *skb);
+void		llc_start_timer(llcptr lp, int t);
+void		llc_stop_timer(llcptr lp, int t);
+void		llc_timer_expired(llcptr lp, int t);
+int		llc_validate_seq_nos(llcptr lp, frameptr fr);
+
+int		llc_data_request(llcptr lp, struct sk_buff *skb);
+void		llc_unit_data_request(llcptr lp, int ll, char * data);
+void		llc_disconnect_request(llcptr lp);
+void		llc_connect_request(llcptr lp);
+void		llc_xid_request(llcptr lp, char opt, int data_len, char *pdu_data);
+void		llc_test_request(llcptr lp, int data_len, char *pdu_data);
+
+int		register_cl2llc_client(llcptr llc, const char *device, llc_ops *ops, u8 *rmac, u8 ssap, u8 dsap);
+void		unregister_cl2llc_client(llcptr lp);
diff -u --recursive --new-file v2.1.14/linux/include/net/llc_frame.h linux/include/net/llc_frame.h
--- v2.1.14/linux/include/net/llc_frame.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/llc_frame.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,98 @@
+/* if_ether.h needed for definition of ETH_DATA_LEN and ETH_ALEN
+ */
+#include "linux/if_ether.h"
+
+/* frame layout based on par3.2 "LLC PDU format"
+ */
+typedef union {			/* pdu layout from pages 40 & 44 */
+    struct {			/* general header, all pdu types */
+	unsigned dsap : 8;	/* dest service access point */
+	unsigned ssap : 8;	/* source service access point */
+	unsigned f1 : 1;	/* I- U- or S- format id bits */
+	unsigned f2 : 1;
+	unsigned : 6;
+	unsigned : 8;
+   } pdu_hdr;
+   struct {
+        char dummy1[2];   	/* dsap + ssap */
+	char byte1;
+	char byte2;
+   } pdu_cntl;			/* unformatted control bytes */
+   struct {			/* header of an Information pdu */
+	unsigned char dummy2[2];
+	unsigned : 1;
+	unsigned ns : 7;
+	unsigned i_pflag : 1;   /* poll/final bit */
+	unsigned nr : 7;	/* N(R)  */	
+	unsigned char is_info[ ETH_DATA_LEN ];
+   }  i_hdr;
+   struct {			/* header of a Supervisory pdu */
+ 	unsigned char dummy3[2];
+	unsigned : 2;
+	unsigned ss : 2;	/* supervisory function bits */
+	unsigned : 4;
+	unsigned s_pflag : 1;   /* poll/final bit  */
+	unsigned nr : 7;	/* N(R)  */
+   } s_hdr;
+
+/* when accessing the P/F bit or the N(R) field there's no need to distinguish
+   I pdus from S pdus i_pflag and s_pflag / i_nr and s_nr map to the same
+   physical location.
+ */ 
+   struct {			/* header of an Unnumbered pdu */
+	unsigned char dummy4[2];
+	unsigned : 2;
+	unsigned mm1 : 2;	/* modifier function part1 */
+	unsigned u_pflag : 1;    /* P/F for U- pdus */
+	unsigned mm2 : 3;	/* modifier function part2 */
+	unsigned char u_info[ ETH_DATA_LEN-1];
+   } u_hdr;
+   struct {			/* mm field in an Unnumbered pdu */
+	unsigned char dummy5[2];
+	unsigned : 2;
+	unsigned mm : 6;	/* must be masked to get ridd of P/F !  */
+   } u_mm;
+   	 
+} frame_type, *frameptr;
+
+/* frame format test macros: */
+
+#define IS_UFRAME( fr ) ( ( (fr)->pdu_hdr.f1) & ( (fr)->pdu_hdr.f2) )
+
+#define IS_IFRAME( fr ) ( !( (fr)->pdu_hdr.f1) )
+
+#define IS_SFRAME( fr ) ( ( (fr)->pdu_hdr.f1) & !( (fr)->pdu_hdr.f2) )
+
+#define IS_RSP( fr ) ( fr->pdu_hdr.ssap & 0x01 )
+
+
+/* The transition table, the _encode tables and some tests in the
+   source code depend on the numeric order of these values.
+   Think twice before changing.
+ */
+
+/* frame names for TYPE 2 operation: */
+#define I_CMD		0
+#define RR_CMD		1
+#define RNR_CMD		2
+#define REJ_CMD		3
+#define DISC_CMD	4
+#define SABME_CMD	5
+#define I_RSP		6
+#define RR_RSP		7
+#define RNR_RSP		8
+#define REJ_RSP		9
+#define UA_RSP		10
+#define DM_RSP		11
+#define FRMR_RSP	12
+
+/* junk frame name: */
+#define BAD_FRAME	13
+#define NO_FRAME	13
+
+/* frame names for TYPE 1 operation: */
+#define UI_CMD		14
+#define XID_CMD		15
+#define TEST_CMD	16
+#define XID_RSP		17
+#define TEST_RSP	18
diff -u --recursive --new-file v2.1.14/linux/include/net/llc_name.h linux/include/net/llc_name.h
--- v2.1.14/linux/include/net/llc_name.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/llc_name.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,7 @@
+char *frame_names[] =
+    {"I_CMD","RR_CMD","RNR_CMD","REJ_CMD","DISC_CMD",
+    "SABME_CMD","I_RSP","RR_RSP","RNR_RSP","REJ_RSP",
+    "UA_RSP","DM_RSP","FRMR_RSP","BAD_FRAME","UI_CMD",
+    "XID_CMD","TEST_CMD","XID_RSP","TEST_RSP"
+};
+ 
diff -u --recursive --new-file v2.1.14/linux/include/net/llc_state.h linux/include/net/llc_state.h
--- v2.1.14/linux/include/net/llc_state.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/llc_state.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,4 @@
+char *state_names[] = { 
+	"ADM","CONN","RESET_WAIT","RESET_CHECK","SETUP",
+	"RESET","D_CONN","ERROR","NORMAL"
+};
diff -u --recursive --new-file v2.1.14/linux/include/net/llccall.h linux/include/net/llccall.h
--- v2.1.14/linux/include/net/llccall.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/llccall.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,3 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void llc_init(struct net_proto *pro);
+
diff -u --recursive --new-file v2.1.14/linux/include/net/ndisc.h linux/include/net/ndisc.h
--- v2.1.14/linux/include/net/ndisc.h	Sun Nov 10 20:12:16 1996
+++ linux/include/net/ndisc.h	Thu Dec 12 17:38:41 1996
@@ -22,12 +22,12 @@
 
 #define NDISC_QUEUE_LEN	3
 
-#define NCF_NOARP		0x01	/* no ARP needed on this device */
-#define NCF_SUBNET		0x02    /* NC entry for subnet		*/
-#define NCF_INVALID		0x04
-#define NCF_DELAY_EXPIRED	0x08	/* time to move to PROBE	*/
-#define NCF_ROUTER		0x10	/* neighbour is a router	*/
-#define NCF_HHVALID		0x20	/* Hardware header is valid	*/
+#define NCF_NOARP		0x0100	/* no ARP needed on this device */
+#define NCF_SUBNET		0x0200   /* NC entry for subnet		*/
+#define NCF_INVALID		0x0400
+#define NCF_DELAY_EXPIRED	0x0800	/* time to move to PROBE	*/
+#define NCF_ROUTER		0x1000	/* neighbour is a router	*/
+#define NCF_HHVALID		0x2000	/* Hardware header is valid	*/
 
 /*
  *	ICMP codes for neighbour discovery messages
@@ -64,37 +64,30 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/icmpv6.h>
+#include <net/neighbour.h>
 #include <asm/atomic.h>
 
 /*
  *	neighbour cache entry
  *	used by neighbour discovery module
- *	as similar functions of "struct hh_cache" used in ipv4
  */
-struct neighbour {
-	struct in6_addr		addr;		/* next hop addr */
-	__u8			len;		/* prefix len	 */
-	__u8			type;		/* {unicast, multicast} */
-			
-	struct device *		dev;
-
-	__u8			flags;
 
+struct nd_neigh {
+	struct neighbour	neigh;
+	struct in6_addr		ndn_addr;	/* next hop addr */
+
+	__u8			ndn_plen,	/* prefix len	 */
+				ndn_type,	/* {unicast, multicast} */
+				ndn_nud_state,
+				ndn_probes;
 	
-	__u8			hh_data[MAX_ADDR_LEN];	/* cached hdr	*/
-	__u8			*h_dest;		/* dest addr	*/
-
-	struct sk_buff_head	arp_queue;	/* packets waiting for ND to
-						   finish */
-	atomic_t		refcnt;
-	__u8			nud_state;
-	__u8			probes;
-	__u32			tstamp;		/* last reachable conf	*/
-
-	unsigned long		expires;	/* timer expires at	*/
+	unsigned long		ndn_expires;	/* timer expires at	*/
 
-	struct neighbour	*next; 		/* for hash chaining	*/
-	struct neighbour	*prev; 		/* for hash chaining	*/
+#define ndn_refcnt		neigh.refcnt
+#define ndn_tstamp		neigh.lastused
+#define ndn_dev			neigh.dev
+#define ndn_flags		neigh.flags
+#define ndn_ha			neigh.ha
 };
 
 struct nd_msg {
@@ -124,18 +117,16 @@
 	__u32	res_failed;		/* address resolution failures	*/
 };
 
-extern struct neighbour *	ndisc_get_neigh(struct device *dev, 
-						struct in6_addr *addr);
+extern struct neighbour *	ndisc_find_neigh(struct device *dev, 
+						 struct in6_addr *addr);
 
 extern void			ndisc_validate(struct neighbour *neigh);
 
-extern void			ndisc_init(struct proto_ops *ops);
+extern void			ndisc_init(struct net_proto_family *ops);
+extern struct neighbour*	ndisc_get_neigh(struct device *dev,
+						struct in6_addr *addr);
 extern void			ndisc_cleanup(void);
 
-extern int			ndisc_eth_resolv(unsigned char *,
-						 struct device *,
-						 struct sk_buff *);
-
 extern int			ndisc_rcv(struct sk_buff *skb,
 					  struct device *dev,
 					  struct in6_addr *saddr,
@@ -159,7 +150,9 @@
 extern int			(*ndisc_eth_hook) (unsigned char *,
 						   struct device *,
 						   struct sk_buff *);
-
+extern int			ndisc_eth_resolv(unsigned char *,
+						struct device *,
+						struct sk_buff *);
 extern void			ndisc_forwarding_on(void);
 extern void			ndisc_forwarding_off(void);
 
@@ -171,12 +164,6 @@
 
 extern unsigned long	nd_rand_seed;
 extern int		ipv6_random(void);
-
-
-static __inline__ void ndisc_dec_neigh(struct neighbour *neigh)
-{
-	atomic_dec(&neigh->refcnt);
-}
 
 #endif /* __KERNEL__ */
 
diff -u --recursive --new-file v2.1.14/linux/include/net/neighbour.h linux/include/net/neighbour.h
--- v2.1.14/linux/include/net/neighbour.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/neighbour.h	Thu Dec 12 17:38:41 1996
@@ -0,0 +1,174 @@
+#ifndef _NET_NEIGHBOUR_H
+#define _NET_NEIGHBOUR_H
+
+/*
+ *	Generic neighbour manipulation
+ *
+ *	authors:
+ *	Pedro Roque		<roque@di.fc.ul.pt>
+ */
+
+#ifdef __KERNEL__
+
+#include <asm/atomic.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+
+/*
+ *	flags
+ *
+ */
+#define NTF_COMPLETE	0x02
+#define NTF_PERMANENT	0x04
+
+struct neighbour {
+	struct neighbour	*next;
+	struct neighbour	*prev;
+	struct neigh_table	*tbl;
+	struct device		*dev;
+	unsigned long		lastused;
+	unsigned long		flags;
+	unsigned char		ha[MAX_ADDR_LEN];
+	atomic_t		refcnt;
+	struct neigh_ops	*ops;
+	struct sk_buff_head	arp_queue;
+	char			primary_key[0];
+};
+
+struct neigh_ops {
+	int			family;
+	unsigned int		(*hash)(void *primary_key);
+	int			(*resolve)(unsigned char *h_dest,
+					   struct device *dev,
+					   struct sk_buff *skb);
+	void			(*destructor)(struct neighbour *);
+};
+
+extern struct neighbour		*neigh_alloc(int size, int priority);
+
+/*
+ *	Neighbour references
+ *
+ *	When neighbour pointers are passed to "client" code the
+ *	reference count is increased. The count is 0 if the node
+ *	is only referenced by the corresponding table.
+ *
+ *	Nodes cannot be unlinked from the table if their
+ *	reference count != 0.
+ *
+ *	i.e. you can't reclaim a neighbour if it is being used by a
+ *	dst_cache or routing entry - hopefully those will react
+ *	to memory shortage and GC their unused entries
+ */
+
+
+static __inline__ void neighbour_unlock(struct neighbour *neigh)
+{
+	if (atomic_dec_and_test(&neigh->refcnt))
+		neigh->lastused = jiffies;
+}
+
+static __inline__ struct neighbour * neighbour_clone(struct neighbour *neigh)
+{
+	if (neigh)
+		atomic_inc(&neigh->refcnt);
+	return neigh;
+}
+
+#define NT_MASK_QUEUE	0x01
+#define NT_MASK_GC	0x02
+
+/*
+ *	neighbour table manipulation
+ */
+
+struct neigh_table {
+	int			tbl_size;	/* num. of hash	buckets	*/
+	int			tbl_entries;	/* entry count		*/
+	struct neighbour	**hash_buckets;
+	atomic_t		tbl_lock;
+	unsigned int		tbl_bh_mask;	/* bh mask		*/
+	struct neigh_ops	*neigh_ops;
+	struct neighbour	*request_queue; /* pending inserts	*/
+};
+
+extern void			neigh_table_init(struct neigh_table *tbl,
+						 struct neigh_ops *ops,
+						 int size);
+extern void			neigh_table_destroy(struct neigh_table *tbl);
+
+extern void			neigh_table_run_bh(struct neigh_table *tbl);
+
+extern void			neigh_table_ins(struct neigh_table *tbl,
+						struct neighbour *neigh);
+
+extern void			neigh_queue_ins(struct neigh_table *tbl,
+						struct neighbour *neigh);
+
+extern void			neigh_unlink(struct neighbour *neigh);
+
+extern struct neighbour *	neigh_lookup(struct neigh_table *tbl,
+					     void *pkey, int key_len,
+					     struct device *dev);
+
+static __inline__ void neigh_insert(struct neigh_table *tbl,
+				    struct neighbour *neigh)
+{
+	start_bh_atomic();
+	if (tbl->tbl_lock == 1)
+	{
+		neigh_table_ins(tbl, neigh);
+		end_bh_atomic();
+	}
+	else
+	{
+		end_bh_atomic();
+		tbl->tbl_bh_mask |= NT_MASK_QUEUE;
+		neigh_queue_ins(tbl, neigh);
+	}
+	
+}
+
+
+
+typedef int (*ntbl_examine_t) (struct neighbour *neigh, void *arg);
+
+/*
+ *	examine every element of a neighbour table.
+ *	For every neighbour the callback function will be called.
+ *
+ *	parameters:
+ *		max	:	max bucket index (<= tbl_size, 0 all)
+ *		filter	:	(neigh->flags & (~filter)) -> call func
+ *		args	:	opaque pointer
+ *
+ *	return values
+ *		0		nop
+ *		!0		unlink node from table and destroy it
+ */
+
+extern void			ntbl_walk_table(struct neigh_table *tbl,
+						ntbl_examine_t func,
+						unsigned long filter,
+						int max, void *args);
+
+static __inline__ void neigh_table_lock(struct neigh_table *tbl)
+{
+	atomic_inc(&tbl->tbl_lock);
+}
+
+extern void			neigh_tbl_run_bh(struct neigh_table *tbl);
+
+static __inline__ void neigh_table_unlock(struct neigh_table *tbl)
+{
+	start_bh_atomic();
+	if (atomic_dec_and_test(&tbl->tbl_lock) && tbl->tbl_bh_mask)
+		neigh_tbl_run_bh(tbl);
+	end_bh_atomic();
+}
+
+#endif
+#endif
+
+
diff -u --recursive --new-file v2.1.14/linux/include/net/netbeui.h linux/include/net/netbeui.h
--- v2.1.14/linux/include/net/netbeui.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/netbeui.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,76 @@
+/*
+ *	NetBEUI data structures
+ */
+ 
+#ifndef __NET_NETBEUI_H
+#define __NET_NETBEUI_H
+
+/*
+ *	Used to keep lists of netbeui sessions
+ */
+ 
+struct nb_ses
+{
+	struct nb_ses *next;
+	struct nb_nam *name;
+	struct nb_link *parent;	/* Owner link */
+	struct sock *sk;
+};
+
+/*
+ *	A netbeui link
+ */
+ 
+struct nb_link
+{
+	u8	mac[6];		/* Mac address of remote */
+	struct device *dev;	/* Device we heard him on */
+	struct llc *llc;	/* 802.2 link layer */
+	struct nb_ses *sessions;/* Netbeui sessions on this LLC link */
+	struct wait_queue *wait;/* Wait queue for this netbios LLC */
+};
+
+
+/*
+ *	Netbios name defence list
+ */
+
+struct nb_name
+{
+	struct nb_name *next;	/*	Chain 		*/
+	struct device *dev;	/*	Device 		*/
+	char name[NB_NAME_LEN];	/* 	Object Name	*/
+	int state;		/* 	Name State	*/
+#define NB_NAME_ACQUIRE		1	/* We are trying to get a name */
+#define NB_NAME_COLLIDE		2	/* Name collided - we failed */
+#define NB_OURS			3	/* We own the name	*/
+#define NB_NAME_OTHER		4	/* Name found - owned by other */
+	int ours;			/* We own this name */
+	int users;			/* Number of nb_ses's to this name */
+	struct timer_list	timer;	/* Our timer */
+	int timer_mode;			/* Timer mode */
+#define NB_TIMER_ACQUIRE	1	/* Expiry means we got our name */
+#define NB_TIMER_COLLIDE	2	/* Expire a collded record */
+#define NB_TIMER_DROP		3	/* Drop a learned record */	
+};
+
+
+/*
+ *	LLC link manager
+ */
+ 
+extern struct nb_link *netbeui_find_link(u8 macaddr);
+extern struct nb_link *netbeui_create_link(u8 macaddr);
+extern int netbeui_destroy_link(u8 macaddr);
+
+/*
+ *	Namespace manager
+ */
+ 
+extern struct nb_name *netbeui_find_name(char *name);
+extern struct nb_name *netbeui_add_name(char *name, int ours);
+extern struct nb_name *netbeui_lookup_name(char *name);
+extern int nb_delete_name(struct nb_name *name);
+
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/include/net/netbeuicall.h linux/include/net/netbeuicall.h
--- v2.1.14/linux/include/net/netbeuicall.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/netbeuicall.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void netbeui_proto_init(struct net_proto *pro);
diff -u --recursive --new-file v2.1.14/linux/include/net/netlink.h linux/include/net/netlink.h
--- v2.1.14/linux/include/net/netlink.h	Tue Nov 19 15:53:58 1996
+++ linux/include/net/netlink.h	Thu Dec 12 16:54:21 1996
@@ -2,14 +2,17 @@
 #define __NET_NETLINK_H
 
 #define NET_MAJOR 36		/* Major 18 is reserved for networking 						*/
+/* so....                                ^^ is this 36? */
+/* and the things below... */
 #define MAX_LINKS 16		/* 18,0 for route updates, 18,1 for SKIP, 18,2 debug tap 18,3 PPP reserved 	*/
 				/* 4-7 are psi0-psi3  8 is arpd 9 is ppp */
 				/* 10 is for IPSEC <John Ioannidis> */
 				/* 11 IPv6 route updates		*/
 				/* 12 is for firewall trapout	*/
+
 #define MAX_QBYTES 32768	/* Maximum bytes in the queue 							*/
 
-#include <linux/config.h>
+#include <linux/netlink.h>
 
 extern int netlink_attach(int unit, int (*function)(int,struct sk_buff *skb));
 extern int netlink_donothing(int, struct sk_buff *skb);
@@ -17,6 +20,44 @@
 extern int netlink_post(int unit, struct sk_buff *skb);
 extern int init_netlink(void);
 
+/*
+ *	skb should fit one page. This choice is good for headerless malloc.
+ */
+#define NLMSG_GOODSIZE (PAGE_SIZE - ((sizeof(struct sk_buff)+0xF)&~0xF))
+
+#define NLMSG_RECOVERY_TIMEO	(HZ/2)		/* If deleivery was failed,
+						   retry after */
+
+struct nlmsg_ctl
+{
+	struct timer_list nlmsg_timer;
+	struct sk_buff	*nlmsg_skb;		/* Partially built skb	*/
+	int		nlmsg_unit;
+	int		nlmsg_delay;		/* Time to delay skb send*/
+	int		nlmsg_maxsize;		/* Maximal message size  */
+	int		nlmsg_force;		/* post immediately	 */
+	unsigned long	nlmsg_overrun_start;	/* seqno starting lossage*/
+	unsigned long	nlmsg_overrun_end;	/* the last lost message */
+	char		nlmsg_overrun;		/* overrun flag		 */
+};
+
+void* nlmsg_send(struct nlmsg_ctl*, unsigned long type, int len,
+		 unsigned long seq, unsigned long pid);
+void nlmsg_transmit(struct nlmsg_ctl*);
+
+extern __inline__ void nlmsg_ack(struct nlmsg_ctl* ctl, unsigned long seq,
+				 unsigned long pid, int err)
+{
+	int *r;
+
+	start_bh_atomic();
+	r = nlmsg_send(ctl, NLMSG_ACK, sizeof(r), seq, pid);
+	if (r)
+		*r = err;
+	end_bh_atomic();
+}
+
+
 #define NETLINK_ROUTE		0	/* Routing/device hook				*/
 #define NETLINK_SKIP		1	/* Reserved for ENskip  			*/
 #define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
@@ -25,11 +66,8 @@
 #define NETLINK_ARPD		8
 #define NETLINK_IPSEC		10	/* IPSEC */
 #define NETLINK_ROUTE6		11	/* af_inet6 route comm channel */
-#define NETLINK_
+/* Wouldn't this suffice instead of the confusion at the top of
+   this file?  i.e. 3 is firewall or ppp... */
+/* #define MAX_LINKS		16 */
 
-#ifdef CONFIG_RTNETLINK
-extern void ip_netlink_msg(unsigned long, __u32, __u32, __u32, short, short, char *);
-#else
-#define ip_netlink_msg(a,b,c,d,e,f,g)
-#endif
 #endif
diff -u --recursive --new-file v2.1.14/linux/include/net/protocol.h linux/include/net/protocol.h
--- v2.1.14/linux/include/net/protocol.h	Tue Nov 19 15:53:58 1996
+++ linux/include/net/protocol.h	Thu Dec 12 17:35:21 1996
@@ -35,13 +35,8 @@
 
 /* This is used to register protocols. */
 struct inet_protocol {
-  int			(*handler)(struct sk_buff *skb, struct device *dev,
-				   struct options *opt, __u32 daddr,
-				   unsigned short len, __u32 saddr,
-				   int redo, struct inet_protocol *protocol);
-  void			(*err_handler)(int type, int code, unsigned char *buff,
-				       __u32 info, __u32 daddr, __u32 saddr,
-				       struct inet_protocol *protocol, int len);
+  int			(*handler)(struct sk_buff *skb, unsigned short len);
+  void			(*err_handler)(struct sk_buff *skb, unsigned char *dp);
   struct inet_protocol *next;
   unsigned char		protocol;
   unsigned char		copy:1;
diff -u --recursive --new-file v2.1.14/linux/include/net/raw.h linux/include/net/raw.h
--- v2.1.14/linux/include/net/raw.h	Mon Sep 11 20:15:53 1995
+++ linux/include/net/raw.h	Thu Dec 12 16:54:21 1996
@@ -21,14 +21,7 @@
 extern struct proto raw_prot;
 
 
-extern void	raw_err(int type, int code, unsigned char *header, __u32 daddr,
-			__u32 saddr, struct inet_protocol *protocol);
-extern int	raw_recvfrom(struct sock *sk, unsigned char *to,
-			int len, int noblock, unsigned flags,
-			struct sockaddr_in *sin, int *addr_len);
-extern int	raw_read(struct sock *sk, unsigned char *buff,
-			int len, int noblock, unsigned flags);
-extern int 	raw_rcv(struct sock *, struct sk_buff *, struct device *, 
-			__u32, __u32);
+extern int 	raw_err(struct sock *, struct sk_buff *);
+extern int 	raw_rcv(struct sock *, struct sk_buff *);
 
 #endif	/* _RAW_H */
diff -u --recursive --new-file v2.1.14/linux/include/net/route.h linux/include/net/route.h
--- v2.1.14/linux/include/net/route.h	Mon Sep 30 11:24:39 1996
+++ linux/include/net/route.h	Thu Dec 12 16:54:21 1996
@@ -14,9 +14,6 @@
  *		Alan Cox	:	Support for TCP parameters.
  *		Alexey Kuznetsov:	Major changes for new routing code.
  *
- *	FIXME:
- *		Make atomic ops more generic and hide them in asm/...
- *
  *		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
@@ -25,25 +22,12 @@
 #ifndef _ROUTE_H
 #define _ROUTE_H
 
-#include <linux/config.h>
+#include <net/ip_fib.h>
+#include <net/dst.h>
 
-/*
- * 0 - no debugging messages
- * 1 - rare events and bugs situations (default)
- * 2 - trace mode.
- */
-#define RT_CACHE_DEBUG		0
 
 #define RT_HASH_DIVISOR	    	256
-#define RT_CACHE_SIZE_MAX    	256
-
-#define RTZ_HASH_DIVISOR	256
-
-#if RT_CACHE_DEBUG >= 2
-#define RTZ_HASHING_LIMIT 0
-#else
-#define RTZ_HASHING_LIMIT 16
-#endif
+#define RT_CACHE_MAX_SIZE    	256
 
 /*
  * Maximal time to live for unused entry.
@@ -51,127 +35,140 @@
 #define RT_CACHE_TIMEOUT		(HZ*300)
 
 /*
+ * Cache invalidations can be delayed by:
+ */
+#define RT_FLUSH_DELAY (2*HZ)
+
+#define RT_REDIRECT_NUMBER		9
+#define RT_REDIRECT_LOAD		(HZ/50)	/* 20 msec */
+#define RT_REDIRECT_SILENCE		(RT_REDIRECT_LOAD<<(RT_REDIRECT_NUMBER+1))
+                                                /* 20sec */
+
+#define RT_ERROR_LOAD			(1*HZ)
+
+
+/*
  * Prevents LRU trashing, entries considered equivalent,
  * if the difference between last use times is less then this number.
  */
-#define RT_CACHE_BUBBLE_THRESHOLD	(HZ*5)
+#define RT_CACHE_BUBBLE_THRESHOLD	(5*HZ)
 
 #include <linux/route.h>
 
-#ifdef __KERNEL__
-#define RTF_LOCAL 0x8000
-#endif
-
 struct rtable 
 {
-	struct rtable		*rt_next;
-	__u32			rt_dst;
-	__u32			rt_src;
-	__u32			rt_gateway;
-	atomic_t		rt_refcnt;
-	atomic_t		rt_use;
-	unsigned long		rt_window;
-	atomic_t		rt_lastuse;
-	struct hh_cache		*rt_hh;
-	struct device		*rt_dev;
-	unsigned short		rt_flags;
-	unsigned short		rt_mtu;
-	unsigned short		rt_irtt;
-	unsigned char		rt_tos;
+	union
+	{
+		struct dst_entry	dst;
+		struct rtable		*rt_next;
+	} u;
+
+	unsigned		rt_flags;
+
+	u32			rt_dst;		/* Path destination	*/
+	u32			rt_src;		/* Path source		*/
+	struct device		*rt_src_dev;	/* Path source device	*/
+
+	/* Info on neighbour */
+	u32			rt_gateway;
+
+	/* Cache lookup keys */
+	struct
+	{
+		u32			dst;
+		u32			src;
+		struct device		*src_dev;
+		struct device		*dst_dev;
+		u8			tos;
+	} key;
+
+	/* Miscellaneous cached information */
+	u32			rt_spec_dst;	/* RFC1122 specific destination */
+	u32			rt_src_map;
+	u32			rt_dst_map;
+
+	/* ICMP statistics */
+	unsigned long		last_error;
+	unsigned long		errors;
 };
 
-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 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);
-extern int		ip_rt_new(struct rtentry *rt);
-extern int		ip_rt_kill(struct rtentry *rt);
-extern void		ip_rt_check_expire(void);
-extern void		ip_rt_advice(struct rtable **rp, int advice);
 
-extern void		ip_rt_run_bh(void);
-extern atomic_t	    	ip_rt_lock;
-extern unsigned		ip_rt_bh_mask;
-extern struct rtable 	*ip_rt_hash_table[RT_HASH_DIVISOR];
+#define RTF_IFBRD	(RTF_UP|RTF_MAGIC|RTF_LOCAL|RTF_BROADCAST)
+#define RTF_IFLOCAL	(RTF_UP|RTF_MAGIC|RTF_LOCAL|RTF_INTERFACE)
+#define RTF_IFPREFIX	(RTF_UP|RTF_MAGIC|RTF_INTERFACE)
 
-extern __inline__ void ip_rt_fast_lock(void)
-{
-	atomic_inc(&ip_rt_lock);
-}
+/*
+ *	Flags not visible at user level.
+ */
+#define RTF_INTERNAL	0xFFFF0000
 
-extern __inline__ void ip_rt_fast_unlock(void)
-{
-	atomic_dec(&ip_rt_lock);
-}
+/*
+ *	Flags saved in FIB.
+ */
+#define RTF_FIB		(RTF_UP|RTF_GATEWAY|RTF_REJECT|RTF_THROW|RTF_STATIC|\
+			 RTF_XRESOLVE|RTF_NOPMTUDISC|RTF_NOFORWARD|RTF_INTERNAL)
 
-extern __inline__ void ip_rt_unlock(void)
+extern void		ip_rt_init(void);
+extern void		ip_rt_redirect(u32 old_gw, u32 dst, u32 new_gw,
+				       u32 src, u8 tos, struct device *dev);
+extern void		ip_rt_check_expire(void);
+extern void		ip_rt_advice(struct rtable **rp, int advice);
+extern void		rt_cache_flush(int how);
+extern int		ip_route_output(struct rtable **, u32 dst, u32 src, u8 tos, struct device *devout);
+extern int		ip_route_output_dev(struct rtable **, u32 dst, u32 src, u8 tos, char *devname);
+extern int		ip_route_input(struct sk_buff*, u32 dst, u32 src, u8 tos, struct device *devin);
+extern unsigned short	ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu);
+extern void		ip_rt_send_redirect(struct sk_buff *skb);
+
+static __inline__ void ip_rt_put(struct rtable * rt)
 {
-	if (atomic_dec_and_test(&ip_rt_lock) && ip_rt_bh_mask)
-		ip_rt_run_bh();
+	if (rt)
+		dst_release(&rt->u.dst);
 }
 
-extern __inline__ unsigned ip_rt_hash_code(__u32 addr)
+static __inline__ char rt_tos2priority(u8 tos)
 {
-	unsigned tmp = addr + (addr>>16);
-	return (tmp + (tmp>>8)) & 0xFF;
+	if (tos & IPTOS_LOWDELAY)
+		return SOPRI_INTERACTIVE;
+	if (tos & (IPTOS_THROUGHPUT|IPTOS_MINCOST))
+		return SOPRI_BACKGROUND;
+	return SOPRI_NORMAL;
 }
 
 
-extern __inline__ void ip_rt_put(struct rtable * rt)
-#ifndef MODULE
+static __inline__ int ip_route_connect(struct rtable **rp, u32 dst, u32 src, u32 tos)
 {
-	if (rt)
-		atomic_dec(&rt->rt_refcnt);
+	int err;
+	err = ip_route_output(rp, dst, src, tos, NULL);
+	if (err || (dst && src))
+		return err;
+	dst = (*rp)->rt_dst;
+	src = (*rp)->rt_src;
+	ip_rt_put(*rp);
+	*rp = NULL;
+	return ip_route_output(rp, dst, src, tos, NULL);
 }
-#else
-;
-#endif
-
-#ifdef CONFIG_KERNELD
-extern struct rtable * ip_rt_route(__u32 daddr, int local);
-#else
-extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
-#ifndef MODULE
+
+static __inline__ void ip_ll_header(struct sk_buff *skb)
 {
-	struct rtable * rth;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct device *dev = rt->u.dst.dev;
+	struct hh_cache *hh = rt->u.dst.hh;
+	int hh_len = dev->hard_header_len;
 
-	ip_rt_fast_lock();
+	skb->dev = dev;
+	skb->arp = 1;
+	skb->protocol = htons(ETH_P_IP);
 
-	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
-	{
-		if (rth->rt_dst == daddr)
-		{
-			rth->rt_lastuse = jiffies;
-			atomic_inc(&rth->rt_use);
-			atomic_inc(&rth->rt_refcnt);
-			ip_rt_unlock();
-			return rth;
-		}
-	}
-	return ip_rt_slow_route (daddr, local);
+	if (hh) {
+		memcpy(skb_push(skb, hh_len), hh->hh_data, hh_len);
+		skb->arp = hh->hh_uptodate;
+	} else if (dev->hard_header &&
+		   dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, 0)<0)
+		skb->arp = 0;
+		
+	skb->mac.raw = skb->data;
 }
-#else
-;
-#endif
-#endif
-
-extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
-						       __u32 daddr, int local)
-{
-	struct rtable * rt = *rp;
-
-	if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
-	    || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
-	{
-		ip_rt_put(rt);
-		rt = ip_rt_route(daddr, local);
-		*rp = rt;
-	}
-	return rt;
-}	
 
 
 #endif	/* _ROUTE_H */
diff -u --recursive --new-file v2.1.14/linux/include/net/scm.h linux/include/net/scm.h
--- v2.1.14/linux/include/net/scm.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/scm.h	Thu Dec 12 16:54:21 1996
@@ -0,0 +1,69 @@
+#ifndef __LINUX_NET_SCM_H
+#define __LINUX_NET_SCM_H
+
+/* Well, we should have at least one descriptor open
+ * to accept passed FDs 8)
+ */
+#define SCM_MAX_FD	(OPEN_MAX-1)
+
+struct scm_fp_list
+{
+	int		count;
+	struct file	*fp[SCM_MAX_FD];
+};
+
+struct scm_cookie
+{
+	struct ucred		creds;		/* Skb credentials	*/
+	struct scm_fp_list	*fp;		/* Passed files		*/
+	unsigned long		seq;		/* Connection seqno	*/
+	struct socket 		*sock;		/* Passed socket	*/
+};
+
+extern void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm);
+extern int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm);
+extern void __scm_destroy(struct scm_cookie *scm);
+extern struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl);
+
+static __inline__ void scm_destroy(struct scm_cookie *scm)
+{
+	if (scm && scm->fp)
+		__scm_destroy(scm);
+}
+
+static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
+			       struct scm_cookie *scm)
+{
+	memset(scm, 0, sizeof(*scm));
+	scm->creds.uid = current->uid;
+	scm->creds.gid = current->gid;
+	scm->creds.pid = current->pid;
+	scm->sock      = sock;
+	if (!msg->msg_control)
+		return 0;
+	return __scm_send(sock, msg, scm);
+}
+
+static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
+				struct scm_cookie *scm, int flags)
+{
+	if (!msg->msg_control)
+	{
+		if (sock->passcred || scm->fp)
+			msg->msg_flags |= MSG_CTRUNC;
+		scm_destroy(scm);
+		return;
+	}
+
+	if (sock->passcred)
+		put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(scm->creds), &scm->creds);
+
+	if (!scm->fp)
+		return;
+	
+	scm_detach_fds(msg, scm);
+}
+
+
+#endif __LINUX_NET_SCM_H
+
diff -u --recursive --new-file v2.1.14/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.14/linux/include/net/sock.h	Thu Dec 12 17:02:47 1996
+++ linux/include/net/sock.h	Thu Dec 12 17:35:21 1996
@@ -33,7 +33,6 @@
 
 #include <linux/config.h>
 #include <linux/timer.h>
-#include <linux/ip.h>		/* struct options */
 #include <linux/in.h>		/* struct sockaddr_in */
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
@@ -87,9 +86,11 @@
 	int 			family;
 	char *			name;
 	int  			locks;
+	struct unix_address	*addr;
 	struct inode *		inode;
 	struct semaphore	readsem;
 	struct sock *		other;
+	struct sock **		list;
 	int 			marksweep;
 #define MARKED			1
 	int			inflight;
@@ -295,7 +296,6 @@
 
 struct sock 
 {
-	struct options		*opt;
 	atomic_t		wmem_alloc;
 	atomic_t		rmem_alloc;
 	unsigned long		allocation;		/* Allocation mode */
@@ -344,7 +344,8 @@
 
 	struct sk_buff_head	write_queue,
 				receive_queue,
-				out_of_order_queue;
+				out_of_order_queue,
+	                        error_queue;
 
 	unsigned short		family;
 	struct proto		*prot;
@@ -354,6 +355,8 @@
 	__u32			saddr;		/* Sending source */
 	__u32			rcv_saddr;	/* Bound address */
 
+	struct dst_entry	*dst_cache;
+
 	unsigned short		max_unacked;
 
 
@@ -409,6 +412,7 @@
 	int			sndbuf;
 	unsigned short		type;
 	unsigned char		localroute;	/* Route locally only */
+	struct ucred		peercred;
   
 /*
  *	This is where all the private (optional) areas that don't
@@ -449,19 +453,20 @@
  */
 	int			ip_ttl;			/* TTL setting */
 	int			ip_tos;			/* TOS */
+	unsigned	   	ip_cmsg_flags;
 	struct tcphdr		dummy_th;
 	struct timer_list	keepalive_timer;	/* TCP keepalive hack */
 	struct timer_list	retransmit_timer;	/* TCP retransmit timer */
 	struct timer_list	delack_timer;		/* TCP delayed ack timer */
 	int			ip_xmit_timeout;	/* Why the timeout is running */
-	struct rtable		*ip_route_cache;	/* Cached output route */
+	struct ip_options	*opt;
 	unsigned char		ip_hdrincl;		/* Include headers ? */
-#ifdef CONFIG_IP_MULTICAST  
-	int			ip_mc_ttl;		/* Multicasting TTL */
-	int			ip_mc_loop;		/* Loopback */
+	__u8			ip_mc_ttl;		/* Multicasting TTL */
+	__u8			ip_mc_loop;		/* Loopback */
+	__u8			ip_recverr;
+	__u8			ip_pmtudisc;
 	char			ip_mc_name[MAX_ADDR_LEN];/* Multicast device name */
 	struct ip_mc_socklist	*ip_mc_list;		/* Group array */
-#endif  
 
 /*
  *	This part is used for the timeout functions (timer.c). 
@@ -524,11 +529,11 @@
 	void			(*write_wakeup)(struct sock *sk);
 	void			(*read_wakeup)(struct sock *sk);
 
-	int			(*select)(struct sock *sk, int which,
-					select_table *wait);
+	int			(*select)(struct socket *sock, int which,
+					  select_table *wait);
 
 	int			(*ioctl)(struct sock *sk, int cmd,
-					unsigned long arg);
+					 unsigned long arg);
 	int			(*init)(struct sock *sk);
 	int			(*destroy)(struct sock *sk);
 	void			(*shutdown)(struct sock *sk, int how);
@@ -538,7 +543,7 @@
 					int optname, char *optval, 
 					int *option);  	 
 	int			(*sendmsg)(struct sock *sk, struct msghdr *msg,
-					int len, int noblock, int flags);
+					   int len);
 	int			(*recvmsg)(struct sock *sk, struct msghdr *msg,
 					int len, int noblock, int flags, 
 					int *addr_len);
@@ -651,6 +656,9 @@
 extern void			inet_put_sock(unsigned short, struct sock *); 
 extern struct sock		*get_sock(struct proto *, unsigned short,
 					  unsigned long, unsigned short,
+					  unsigned long);
+extern struct sock		*get_sock_proxy(struct proto *, unsigned short,
+					  unsigned long, unsigned short,
 					  unsigned long,
 					  unsigned long, unsigned short);
 extern struct sock		*get_sock_mcast(struct sock *, unsigned short,
@@ -665,18 +673,16 @@
 extern struct sk_buff		*sock_rmalloc(struct sock *sk,
 					      unsigned long size, int force,
 					      int priority);
-extern void			sock_wfree(struct sock *sk,
-					   struct sk_buff *skb);
-extern void			sock_rfree(struct sock *sk,
-					   struct sk_buff *skb);
+extern void			sock_wfree(struct sk_buff *skb);
+extern void			sock_rfree(struct sk_buff *skb);
 extern unsigned long		sock_rspace(struct sock *sk);
 extern unsigned long		sock_wspace(struct sock *sk);
 
-extern int			sock_setsockopt(struct sock *sk, int level,
+extern int			sock_setsockopt(struct socket *sock, int level,
 						int op, char *optval,
 						int optlen);
 
-extern int			sock_getsockopt(struct sock *sk, int level,
+extern int			sock_getsockopt(struct socket *sock, int level,
 						int op, char *optval, 
 						int *optlen);
 extern struct sk_buff 		*sock_alloc_send_skb(struct sock *skb,
@@ -694,12 +700,26 @@
  *	packet ever received.
  */
 
+extern __inline__ void skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
+{
+	skb->sk = sk;
+	skb->destructor = sock_wfree;
+	atomic_add(skb->truesize, &sk->wmem_alloc);
+}
+
+extern __inline__ void skb_set_owner_r(struct sk_buff *skb, struct sock *sk)
+{
+	skb->sk = sk;
+	skb->destructor = sock_rfree;
+	atomic_add(skb->truesize, &sk->rmem_alloc);
+}
+
+
 extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
 {
 	if (sk->rmem_alloc + skb->truesize >= sk->rcvbuf)
 		return -ENOMEM;
-	atomic_add(skb->truesize, &sk->rmem_alloc);
-	skb->sk=sk;
+	skb_set_owner_r(skb, sk);
 	skb_queue_tail(&sk->receive_queue,skb);
 	if (!sk->dead)
 		sk->data_ready(sk,skb->len);
@@ -710,14 +730,24 @@
 {
 	if (sk->rmem_alloc + skb->truesize >= sk->rcvbuf)
 		return -ENOMEM;
-	atomic_add(skb->truesize, &sk->rmem_alloc);
-	skb->sk=sk;
+	skb_set_owner_r(skb, sk);
 	__skb_queue_tail(&sk->receive_queue,skb);
 	if (!sk->dead)
 		sk->data_ready(sk,skb->len);
 	return 0;
 }
 
+extern __inline__ int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb)
+{
+	if (sk->rmem_alloc + skb->truesize >= sk->rcvbuf)
+		return -ENOMEM;
+	skb_set_owner_r(skb, sk);
+	__skb_queue_tail(&sk->error_queue,skb);
+	if (!sk->dead)
+		sk->data_ready(sk,skb->len);
+	return 0;
+}
+
 /*
  *	Recover an error report and clear atomically
  */
@@ -728,6 +758,7 @@
 	return -err;
 }
 
+
 /* 
  *	Declarations from timer.c 
  */
@@ -743,6 +774,10 @@
  *	Enable debug/info messages 
  */
 
+#if 0
 #define NETDEBUG(x)	do { } while (0)
+#else
+#define NETDEBUG(x)	do { x; } while (0)
+#endif
 
 #endif	/* _SOCK_H */
diff -u --recursive --new-file v2.1.14/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.14/linux/include/net/tcp.h	Sat Nov 30 12:03:12 1996
+++ linux/include/net/tcp.h	Thu Dec 12 17:38:39 1996
@@ -142,7 +142,7 @@
 	struct open_request	req;
 	__u32			loc_addr;
 	__u32			rmt_addr;
-	struct options		*opt;
+	struct ip_options	*opt;
 };
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
@@ -164,10 +164,7 @@
 	int			(*build_net_header)	(struct sock *sk, 
 							 struct sk_buff *skb);
 
-	void			(*queue_xmit)		(struct sock *sk, 
-							 struct device *dev,
-							 struct sk_buff *skb, 
-							 int free);
+	void			(*queue_xmit)		(struct sk_buff *skb);
 
 	void			(*send_check)		(struct sock *sk,
 							 struct tcphdr *th,
@@ -208,6 +205,8 @@
 	void			(*addr2sockaddr)	(struct sock *sk,
 							 struct sockaddr *);
 
+	void			(*send_reset)		(struct sk_buff *skb);
+
 	int sockaddr_len;
 };
 
@@ -237,25 +236,17 @@
 extern struct proto tcp_prot;
 extern struct tcp_mib tcp_statistics;
 
-extern void			tcp_v4_err(int type, int code,
-					   unsigned char *header, __u32 info,
-					   __u32 daddr, __u32 saddr,
-					   struct inet_protocol *protocol,
-					   int len);
+extern void			tcp_v4_err(struct sk_buff *skb,
+					   unsigned char *);
 
 extern void			tcp_shutdown (struct sock *sk, int how);
 
-extern int			tcp_v4_rcv(struct sk_buff *skb, 
-					   struct device *dev,
-					   struct options *opt, __u32 daddr,
-					   unsigned short len, __u32 saddr, 
-					   int redo,
-					   struct inet_protocol *protocol);
+extern int			tcp_v4_rcv(struct sk_buff *skb,
+					   unsigned short len);
 
 extern int			tcp_do_sendmsg(struct sock *sk, 
 					       int iovlen, struct iovec *iov,
-					       int len, int nonblock, 
-					       int flags);
+					       int len, int flags);
 
 extern int			tcp_ioctl(struct sock *sk, 
 					  int cmd, 
@@ -266,7 +257,7 @@
 						      struct tcphdr *th,
 						      void *opt, __u16 len);
 
-extern void			tcp_rcv_established(struct sock *sk, 
+extern int			tcp_rcv_established(struct sock *sk, 
 						    struct sk_buff *skb,
 						    struct tcphdr *th, 
 						    __u16 len);
@@ -274,7 +265,7 @@
 extern void			tcp_close(struct sock *sk, 
 					  unsigned long timeout);
 extern struct sock *		tcp_accept(struct sock *sk, int flags);
-extern int			tcp_select(struct sock *sk, int sel_type, 
+extern int			tcp_select(struct socket *sock, int sel_type, 
 					   select_table *wait);
 extern int			tcp_getsockopt(struct sock *sk, int level, 
 					       int optname, char *optval, 
diff -u --recursive --new-file v2.1.14/linux/include/net/udp.h linux/include/net/udp.h
--- v2.1.14/linux/include/net/udp.h	Sat Nov 30 12:03:12 1996
+++ linux/include/net/udp.h	Thu Dec 12 16:54:21 1996
@@ -31,21 +31,13 @@
 extern struct proto udp_prot;
 
 
-extern void	udp_err(int type, int code, unsigned char *header,
-			__u32 info, __u32 daddr, __u32 saddr,
-			struct inet_protocol *protocol, int len);
-extern void	udp_send_check(struct udphdr *uh, __u32 saddr, 
-			__u32 daddr, int len, struct sock *sk);
+extern void	udp_err(struct sk_buff *, unsigned char *);
 extern int	udp_connect(struct sock *sk,
 			    struct sockaddr *usin, int addr_len);
 
-extern int	udp_sendmsg(struct sock *sk, struct msghdr *msg,
-			    int len, int noblock, int flags);
+extern int	udp_sendmsg(struct sock *sk, struct msghdr *msg, int len);
 
-extern int	udp_rcv(struct sk_buff *skb, struct device *dev,
-			struct options *opt, __u32 daddr,
-			unsigned short len, __u32 saddr, int redo,
-			struct inet_protocol *protocol);
+extern int	udp_rcv(struct sk_buff *skb, unsigned short len);
 extern int	udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
 extern void	udp_cache_zap(void);	/* Remove udp last socket cache */
 
diff -u --recursive --new-file v2.1.14/linux/init/main.c linux/init/main.c
--- v2.1.14/linux/init/main.c	Thu Dec 12 17:02:47 1996
+++ linux/init/main.c	Thu Dec 12 16:54:21 1996
@@ -66,9 +66,13 @@
 extern long console_init(long, long);
 extern long kmalloc_init(long,long);
 extern void sock_init(void);
-extern long pci_init(long, long);
+extern unsigned long pci_init(unsigned long, unsigned long);
+#ifdef CONFIG_MCA
+extern long mca_init(long, long);
+#endif
 extern void sysctl_init(void);
 
+extern void smp_setup(char *str, int *ints);
 extern void no_scroll(char *str, int *ints);
 extern void swap_setup(char *str, int *ints);
 extern void buff_setup(char *str, int *ints);
@@ -95,6 +99,9 @@
 extern void eata2x_setup(char *str, int *ints);
 extern void u14_34f_setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
+#ifdef CONFIG_SCSI_IBMMCA
+extern void ibmmca_scsi_setup(char *str, int *ints);
+#endif
 extern void in2000_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
 extern void wd7000_setup(char *str, int *ints);
@@ -105,6 +112,10 @@
 #ifdef CONFIG_CDU31A
 extern void cdu31a_setup(char *str, int *ints);
 #endif CONFIG_CDU31A
+#ifdef CONFIG_BLK_DEV_PS2
+extern void ed_setup(char *str, int *ints);
+extern void tp720_setup(char *str, int *ints);
+#endif CONFIG_BLK_DEV_PS2
 #ifdef CONFIG_MCD
 extern void mcd_setup(char *str, int *ints);
 #endif CONFIG_MCD
@@ -255,6 +266,10 @@
 } bootsetups[] = {
 	{ "reserve=", reserve_setup },
 	{ "profile=", profile_setup },
+#ifdef __SMP__
+	{ "nosmp", smp_setup },
+	{ "maxcpus=", smp_setup },
+#endif
 #ifdef CONFIG_BLK_DEV_RAM
 	{ "ramdisk_start=", ramdisk_start_setup },
 	{ "load_ramdisk=", load_ramdisk },
@@ -348,12 +363,19 @@
 #ifdef CONFIG_SCSI_PPA
         { "ppa=", ppa_setup },
 #endif
+#ifdef CONFIG_SCSI_IBMMCA
+        { "ibmmcascsi=", ibmmca_scsi_setup },
+#endif
 #ifdef CONFIG_BLK_DEV_XD
 	{ "xd=", xd_setup },
 #endif
 #ifdef CONFIG_BLK_DEV_FD
 	{ "floppy=", floppy_setup },
 #endif
+#ifdef CONFIG_BLK_DEV_PS2
+	{ "ed=", ed_setup },
+	{ "tp720", tp720_setup },
+#endif
 #ifdef CONFIG_CDU31A
 	{ "cdu31a=", cdu31a_setup },
 #endif CONFIG_CDU31A
@@ -817,6 +839,9 @@
 	memory_start = console_init(memory_start,memory_end);
 #ifdef CONFIG_PCI
 	memory_start = pci_init(memory_start,memory_end);
+#endif
+#ifdef CONFIG_MCA
+	memory_start = mca_init(memory_start,memory_end);
 #endif
 	memory_start = kmalloc_init(memory_start,memory_end);
 	sti();
diff -u --recursive --new-file v2.1.14/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.14/linux/kernel/ksyms.c	Tue Nov 12 15:56:15 1996
+++ linux/kernel/ksyms.c	Thu Dec 12 16:54:21 1996
@@ -105,10 +105,12 @@
 	X(pcibios_read_config_byte),
 	X(pcibios_read_config_word),
 	X(pcibios_read_config_dword),
-    	X(pcibios_strerror),
 	X(pcibios_write_config_byte),
 	X(pcibios_write_config_word),
 	X(pcibios_write_config_dword),
+    	X(pcibios_strerror),
+	X(pci_strvendor),
+	X(pci_strdev),
 #endif
 
 	/* process memory management */
diff -u --recursive --new-file v2.1.14/linux/kernel/panic.c linux/kernel/panic.c
--- v2.1.14/linux/kernel/panic.c	Tue Nov 19 15:53:59 1996
+++ linux/kernel/panic.c	Thu Dec 12 16:54:21 1996
@@ -53,6 +53,11 @@
 		printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout);
 		for(i = 0; i < (panic_timeout*1000); i++)
 			udelay(1000);
+		/*
+		 *	Should we run the reboot notifier. For the moment Im
+		 *	choosing not too. It might crash, be corrupt or do
+		 *	more harm than good for other reasons.
+		 */
 		hard_reset_now();
 	}
 	for(;;);
diff -u --recursive --new-file v2.1.14/linux/kernel/sys.c linux/kernel/sys.c
--- v2.1.14/linux/kernel/sys.c	Fri Nov 22 18:28:22 1996
+++ linux/kernel/sys.c	Thu Dec 12 16:54:21 1996
@@ -24,6 +24,7 @@
 #if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
 #include <linux/apm_bios.h>
 #endif
+#include <linux/notifier.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -31,8 +32,16 @@
 /*
  * this indicates whether you can reboot with ctrl-alt-del: the default is yes
  */
+
 int C_A_D = 1;
 
+/*
+ *	List of functions to call at shutdown. This is used to stop any
+ *	idling DMA operations and the like. 
+ */
+ 
+struct notifier_block *boot_notifier_list=NULL;
+
 extern void adjust_clock(void);
 
 asmlinkage int sys_ni_syscall(void)
@@ -187,7 +196,10 @@
 	if (magic != 0xfee1dead || magic_too != 672274793)
 		return -EINVAL;
 	if (flag == 0x01234567)
+	{
+		notifier_call_chain(&boot_notifier_list, SYS_DOWN, NULL);
 		hard_reset_now();
+	}
 	else if (flag == 0x89ABCDEF)
 		C_A_D = 1;
 	else if (!flag)
@@ -198,6 +210,7 @@
 #if defined(CONFIG_APM) && defined(CONFIG_APM_POWER_OFF)
 		apm_set_power_state(APM_STATE_OFF);
 #endif
+		notifier_call_chain(&boot_notifier_list, SYS_HALT, NULL);
 		do_exit(0);
 	} else
 		return -EINVAL;
@@ -212,7 +225,10 @@
 void ctrl_alt_del(void)
 {
 	if (C_A_D)
+	{
+		notifier_call_chain(&boot_notifier_list, SYS_DOWN, NULL);
 		hard_reset_now();
+	}
 	else
 		kill_proc(1, SIGINT, 1);
 }
diff -u --recursive --new-file v2.1.14/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.14/linux/kernel/sysctl.c	Fri Nov 22 18:28:22 1996
+++ linux/kernel/sysctl.c	Thu Dec 12 16:54:21 1996
@@ -140,7 +140,7 @@
 #ifdef CONFIG_ROOT_NFS
 	{KERN_NFSRNAME, "nfs-root-name", nfs_root_name, NFS_ROOT_NAME_LEN,
 	 0644, NULL, &proc_dostring, &sysctl_string },
-	{KERN_NFSRNAME, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
+	{KERN_NFSRADDRS, "nfs-root-addrs", nfs_root_addrs, NFS_ROOT_ADDRS_LEN,
 	 0644, NULL, &proc_dostring, &sysctl_string },
 #endif
 #ifdef CONFIG_BINFMT_JAVA
diff -u --recursive --new-file v2.1.14/linux/net/802/Makefile linux/net/802/Makefile
--- v2.1.14/linux/net/802/Makefile	Tue Nov 19 15:53:59 1996
+++ linux/net/802/Makefile	Thu Dec 12 16:54:22 1996
@@ -10,6 +10,13 @@
 O_TARGET := 802.o
 O_OBJS	  = p8023.o sysctl_net_802.o
 
+ifeq ($(CONFIG_LLC),y)
+SUB_DIRS += transit
+O_OBJS += llc_macinit.o llc_sendpdu.o llc_utility.o cl2llc.o
+
+
+endif
+
 ifdef CONFIG_TR
 O_OBJS += tr.o
 endif
@@ -29,6 +36,9 @@
 endif
 
 include $(TOPDIR)/Rules.make
+
+cl2llc.c: cl2llc.pre
+	sed -f ./pseudo/opcd2num.sed cl2llc.pre >cl2llc.c
 
 tar:
 		tar -cvf /dev/f1 .
diff -u --recursive --new-file v2.1.14/linux/net/802/TODO linux/net/802/TODO
--- v2.1.14/linux/net/802/TODO	Thu Jan  1 02:00:00 1970
+++ linux/net/802/TODO	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,29 @@
+Remaining Problems:
+
+1. Serialization of access to variables in the llc structure
+by mac_data_indicate(), timer expired functions, and data_request() .
+There is not serialization of any kind right now.
+While testing, I have not seen any problems that stem from this lack of
+serialization, but it wories me...
+
+2. The code is currently able to handle one connection only,
+there is more work in register_cl2llc_client() to make a chain
+of llc structures and in mac_data_indicate() to find back
+the llc structure addressed by an incomming frame.
+According to IEEE, connections are identified by (remote mac + local mac
++ dsap + ssap). dsap and ssap do not seem important: existing applications
+always use the same dsap/ssap. Its probably sufficient to index on 
+the remote mac only. 
+ 
+3. There is no test to see if the transmit window is full in data_request()
+as described in the doc p73, "7.5.1 Sending I PDUs" 3th alinea.
+The pdus presented to data_request() could probably go on the 
+awaiting-transmit-queue (atq). The real difficulty is coding a test
+to see if the transmit window is used up and to send the queue
+when space in the window becomes available.
+As I have no network layer that can generate a continous flow of pdus it is
+difficult to simulate a remote busy condition and hence to test the code
+to handle it.
+ 
+4. A simple flow control algorithm, steering the size of the transmit
+window would be nice to have.
diff -u --recursive --new-file v2.1.14/linux/net/802/cl2llc.c linux/net/802/cl2llc.c
--- v2.1.14/linux/net/802/cl2llc.c	Thu Jan  1 02:00:00 1970
+++ linux/net/802/cl2llc.c	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,589 @@
+/*
+ * NET		An implementation of the IEEE 802.2 LLC protocol for the
+ *		LINUX operating system.  LLC is implemented as a set of 
+ *		state machines and callbacks for higher networking layers.
+ *
+ *		Class 2 llc algorithm.
+ *		Pseudocode interpreter, transition table lookup,
+ *			data_request & indicate primitives...
+ *
+ *		Code for initialization, termination, registration and 
+ *		MAC layer glue.
+ *
+ *		Copyright Tim Alpaerts, 
+ *			<Tim_Alpaerts@toyota-motor-europe.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.
+ *
+ *	Changes
+ *		Alan Cox	:	Chainsawed into Linux format
+ *					Modified to use llc_ names
+ *
+ *	This file must be processed by sed before it can be compiled.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/p8022.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/byteorder.h>
+
+#include "pseudo/pseudocode.h"
+#include "transit/pdutr.h"
+#include "transit/timertr.h"
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+/*
+ *	Data_request() is called by the client to present a data unit
+ *	to the llc for transmission.
+ *	In the future this function should also check if the transmit window
+ *	allows the sending of another pdu, and if not put the skb on the atq
+ *	for deferred sending.
+ */
+
+int llc_data_request(llcptr lp, struct sk_buff *skb)
+{
+	if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
+		printk("cl2llc: data_request() not enough headroom in skb\n");
+		return -1;
+	};
+
+	skb_push(skb, 4);
+
+	if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
+	{
+		printk("cl2llc: data_request() while no llc connection\n"); 
+		return -1;  
+	}
+
+	if (lp->remote_busy)
+	{     /* if the remote llc is BUSY, */
+		ADD_TO_ATQ(skb);      /* save skb in the await transmit queue */
+		return 0;
+	}                           
+	else
+	{
+		/*
+		 *	Else proceed with xmit 
+		 */
+
+		switch(lp->state)
+		{
+			case NORMAL:
+				if(lp->p_flag)
+					llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
+				break;
+			case BUSY:
+				if (lp->p_flag)
+					llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
+				break;
+			case REJECT:
+				if (lp->p_flag)
+					llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
+				break;
+			default:
+		}
+		return 0;  
+	}              
+}
+
+
+
+/* 
+ *	Disconnect_request() requests that the llc to terminate a connection
+ */
+
+void disconnect_request(llcptr lp)
+{
+	if ((lp->state == NORMAL) ||
+    		(lp->state == BUSY) ||
+		(lp->state == REJECT) ||
+		(lp->state == AWAIT) ||
+		(lp->state == AWAIT_BUSY) ||
+		(lp->state == AWAIT_REJECT))
+	{
+		lp->state = D_CONN;
+		llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
+	}
+}
+
+
+/*
+ *	Connect_request() requests that the llc to start a connection
+ */
+
+void connect_request(llcptr lp)
+{
+	if (lp->state == ADM)
+	{
+		lp->state = SETUP;
+		llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
+	}
+}
+
+
+/*
+ *	Interpret_pseudo_code() executes the actions in the connection component
+ *	state transition table. Table 4 in document on p88.
+ *
+ *	If this function is called to handle an incomming pdu, skb will point
+ *	to the buffer with the pdu and type will contain the decoded pdu type.
+ *
+ *	If called by data_request skb points to an skb that was skb_alloc-ed by 
+ *	the llc client to hold the information unit to be transmitted, there is
+ *	no valid type in this case.  
+ *
+ *	If called because a timer expired no skb is passed, and there is no 
+ *	type.
+ */
+
+void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, 
+		char type)
+{    
+	short int pc;	/* program counter in pseudo code array */ 
+	char p_flag_received;
+	frameptr fr;
+	int resend_count;   /* number of pdus resend by llc_resend_ipdu() */
+	int ack_count;      /* number of pdus acknowledged */
+	struct sk_buff *skb2;
+
+	if (skb != NULL) 
+	{
+		fr = (frameptr) skb->data;
+	}
+	else
+		fr = NULL;
+
+	pc = pseudo_code_idx[pc_label];
+	while(pseudo_code[pc])
+	{
+		switch(pseudo_code[pc])
+		{
+			case 9:
+				if ((type != I_CMD) || (fr->i_hdr.i_pflag =0))
+					break;
+			case 1:
+				lp->remote_busy = 0;
+				llc_stop_timer(lp, BUSY_TIMER);
+				if ((lp->state == NORMAL) ||
+					(lp->state == REJECT) ||
+					(lp->state == BUSY))
+				{
+					skb2 = llc_pull_from_atq(lp);
+					if (skb2 != NULL) 
+						llc_start_timer(lp, ACK_TIMER);
+					while (skb2 != NULL)
+					{
+						llc_sendipdu( lp, I_CMD, 0, skb2);
+						skb2 = llc_pull_from_atq(lp);
+					}
+				}	   
+				break;
+			case 2:
+				lp->state = NORMAL;  /* needed to eliminate connect_response() */
+				lp->llc_mode = MODE_ABM;
+				if (lp->ops->connect_indication_ep != NULL)
+					lp->ops->connect_indication_ep(lp);
+				break;
+			case 3:
+				lp->llc_mode = MODE_ABM;
+				if (lp->ops->connect_confirm_ep != NULL)
+					lp->ops->connect_confirm_ep(lp);
+				break;
+			case 4:
+				if (lp->ops->data_indication_ep != NULL)
+				{
+					skb_pull(skb, 4);
+					lp->ops->data_indication_ep(lp, skb);
+				}
+				break;
+			case 5:
+				lp->llc_mode = MODE_ADM;
+				if (lp->ops->disconnect_indication_ep != NULL)
+					lp->ops->disconnect_indication_ep(lp);
+				break;
+			case 70:
+				if (lp->ops->reset_indication_ep != NULL)
+					lp->ops->reset_indication_ep(lp, LOCAL);
+				break;
+			case 71:
+				if (lp->ops->reset_indication_ep != NULL)
+					lp->ops->reset_indication_ep(lp, REMOTE);
+				break;
+			case 7:
+				if (lp->ops->reset_confirm_ep != NULL)
+					lp->ops->reset_confirm_ep(lp, REMOTE);
+				break;
+			case 66:
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, FRMR_RECEIVED);
+				break;
+			case 67:
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, FRMR_SENT);
+				break;
+			case 68:
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				break;
+			case 69:
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, REMOTE_NOT_BUSY);
+				break;
+			case 11:
+				llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
+				break;
+			case 12:
+				llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
+				break;
+			case 13:                        
+				lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
+				lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
+				lp->frmr_info_fld.vs = lp->vs;
+				lp->frmr_info_fld.vr_cr = lp->vr;
+				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+				break;
+			case 14:
+				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+				break;
+			case 15:
+				llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
+					5, (char *) &lp->frmr_info_fld);
+				break;
+			case 16:
+				llc_sendipdu(lp, I_CMD, 1, skb);   
+				break;
+			case 17:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+				break;
+			case 18:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+				if (resend_count == 0) 
+				{
+					llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				}    
+				break;
+			case 19:
+				llc_sendipdu(lp, I_CMD, 0, skb);   
+				break;
+			case 20:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+				break;
+			case 21:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+				if (resend_count == 0) 
+				{
+					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+				}    
+				break;
+			case 22:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
+				break;
+			case 23:
+				llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
+				break;
+			case 24:
+				llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
+				break;
+			case 25:
+				if (IS_RSP(fr))
+					llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
+				break;
+			case 26:
+				llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
+				break;
+			case 27:
+				llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
+				break;
+			case 28:
+				if (IS_RSP(fr))
+					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+				break;
+			case 29:
+				if (lp->remote_busy == 0)
+				{
+					lp->remote_busy = 1;
+					llc_start_timer(lp, BUSY_TIMER);
+					if (lp->ops->report_status_ep != NULL)
+						lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				}
+				else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
+				{
+					llc_start_timer(lp, BUSY_TIMER);
+				}
+				break;
+			case 30:
+				if (IS_RSP(fr)) 
+					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+				break;
+			case 31:
+				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				break;
+			case 32:
+				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				break;
+			case 33:
+				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+				break;
+			case 34:
+				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+				break;
+			case 35:
+				llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+				break;
+			case 36:
+				if (IS_RSP(fr)) 
+					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+				break;
+			case 37:
+				llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
+				lp->f_flag = 0;
+				break;
+			case 38:
+				llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
+				break;
+			case 39:
+				lp->s_flag = 0;
+				break;
+			case 40:
+				lp->s_flag = 1;
+				break;
+			case 41:
+				if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
+					llc_stop_timer(lp, P_TIMER);
+				llc_start_timer(lp, P_TIMER);
+				if (lp->p_flag == 0)
+				{
+					lp->retry_count = 0;
+					lp->p_flag = 1;
+				}
+				break;
+			case 44:
+				if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
+					llc_start_timer(lp, ACK_TIMER);
+				break;
+			case 42:
+				llc_start_timer(lp, ACK_TIMER);
+				break;
+			case 43:
+				llc_start_timer(lp, REJ_TIMER);
+				break;
+			case 45:
+				llc_stop_timer(lp, ACK_TIMER);
+				break;
+			case 46:
+				llc_stop_timer(lp, ACK_TIMER);
+				lp->p_flag = 0;
+				break;
+			case 10:
+				if (lp->data_flag == 2)
+					llc_stop_timer(lp, REJ_TIMER);
+				break;
+			case 47:
+				llc_stop_timer(lp, REJ_TIMER);
+				break;
+			case 48:
+				llc_stop_timer(lp, ACK_TIMER);
+				llc_stop_timer(lp, P_TIMER);
+				llc_stop_timer(lp, REJ_TIMER);
+				llc_stop_timer(lp, BUSY_TIMER);
+				break;
+			case 49:
+				llc_stop_timer(lp, P_TIMER);
+				llc_stop_timer(lp, REJ_TIMER);
+				llc_stop_timer(lp, BUSY_TIMER);
+				break;
+			case 50:             
+				ack_count = llc_free_acknowledged_skbs(lp,
+					(unsigned char) fr->s_hdr.nr);
+				if (ack_count > 0)
+				{
+					lp->retry_count = 0;
+					llc_stop_timer(lp, ACK_TIMER);  
+					if (lp->rtq_front != NULL)
+					{
+						/*
+ 						 *	Re-transmit queue not empty 
+						 */
+						llc_start_timer(lp, ACK_TIMER);  
+					}
+				}        
+				break;
+			case 51:
+				if (IS_UFRAME(fr)) 
+					p_flag_received = fr->u_hdr.u_pflag;
+				else
+					p_flag_received = fr->i_hdr.i_pflag;
+				if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
+				{
+					lp->p_flag = 0;
+					llc_stop_timer(lp, P_TIMER);  
+				}
+				break;
+			case 52:
+				lp->data_flag = 2;
+				break;
+			case 53:
+				lp->data_flag = 0;
+				break;
+			case 54:
+				lp->data_flag = 1;
+				break;
+			case 55:
+				if (lp->data_flag == 0)
+					lp->data_flag = 1;
+				break;
+			case 56:
+				lp->p_flag = 0;
+				break;
+			case 57:
+				lp->p_flag = lp->f_flag;
+				break;
+			case 58:
+				lp->remote_busy = 0;
+				break;
+			case 59:
+				lp->retry_count = 0;
+				break;
+			case 60:
+				lp->retry_count++;
+				break;
+			case 61:
+				lp->vr = 0;
+				break;
+			case 62:
+				lp->vr++;
+				break;
+			case 63:
+				lp->vs = 0;
+				break;
+			case 64:
+				lp->vs = fr->i_hdr.nr;
+				break;
+			case 65:
+				if (IS_UFRAME(fr)) 
+					lp->f_flag = fr->u_hdr.u_pflag;
+				else
+					lp->f_flag = fr->i_hdr.i_pflag;
+				break;
+			default:
+		}
+		pc++;	
+	}
+}
+
+
+/*
+ *	Process_otype2_frame will handle incoming frames
+ *	for 802.2 Type 2 Procedure.
+ */
+
+void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
+{
+	int idx;		/*	index in transition table */
+	int pc_label;		/*	action to perform, from tr tbl */
+	int validation;		/*	result of validate_seq_nos */
+	int p_flag_received;	/*	p_flag in received frame */
+	frameptr fr;
+
+	fr = (frameptr) skb->data;
+
+	if (IS_UFRAME(fr))
+		p_flag_received = fr->u_hdr.u_pflag;
+	else
+		p_flag_received = fr->i_hdr.i_pflag;
+
+	switch(lp->state)
+	{
+		/*	Compute index in transition table: */
+		case ADM:
+			idx = type;
+			idx = (idx << 1) + p_flag_received;
+			break;
+		case CONN:
+		case RESET_WAIT:
+		case RESET_CHECK:
+		case ERROR:
+			idx = type;
+			break;
+		case SETUP:
+		case RESET:
+		case D_CONN:
+			idx = type;
+			idx = (idx << 1) + lp->p_flag;
+			break;
+		case NORMAL:
+		case BUSY:
+		case REJECT:
+		case AWAIT:
+		case AWAIT_BUSY:
+		case AWAIT_REJECT:
+			validation = llc_validate_seq_nos(lp, fr);
+			if (validation > 3) 
+				type = BAD_FRAME;
+			idx = type;
+			idx = (idx << 1);
+			if (validation & 1) 
+				idx = idx +1;
+			idx = (idx << 1) + p_flag_received;
+			idx = (idx << 1) + lp->p_flag;
+		default:
+			printk("llc_proc: bad state\n");
+			return;
+	}
+	idx = (idx << 1) + pdutr_offset[lp->state];
+	lp->state = pdutr_entry[idx +1]; 
+	pc_label = pdutr_entry[idx];
+	if (pc_label != 0)
+	{ 
+		llc_interpret_pseudo_code(lp, pc_label, skb, type);
+	}
+}
+
+
+void llc_timer_expired(llcptr lp, int t)
+{
+	int idx;		/* index in transition table	*/
+	int pc_label;       	/* action to perform, from tr tbl */
+
+	lp->timer_state[t] = TIMER_EXPIRED;
+	idx = lp->state;            /* Compute index in transition table: */
+	idx = (idx << 2) + t;
+	idx = idx << 1;
+	if (lp->retry_count >= lp->n2) 
+		idx = idx + 1;
+	idx = (idx << 1) + lp->s_flag;
+	idx = (idx << 1) + lp->p_flag;
+	idx = idx << 1;             /* 2 bytes per entry: action & newstate */
+
+	pc_label = timertr_entry[idx];
+	if (pc_label != 0)
+	{
+		llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
+		lp->state = timertr_entry[idx +1];
+	}
+	lp->timer_state[t] = TIMER_IDLE;
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/802/cl2llc.pre linux/net/802/cl2llc.pre
--- v2.1.14/linux/net/802/cl2llc.pre	Thu Jan  1 02:00:00 1970
+++ linux/net/802/cl2llc.pre	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,589 @@
+/*
+ * NET		An implementation of the IEEE 802.2 LLC protocol for the
+ *		LINUX operating system.  LLC is implemented as a set of 
+ *		state machines and callbacks for higher networking layers.
+ *
+ *		Class 2 llc algorithm.
+ *		Pseudocode interpreter, transition table lookup,
+ *			data_request & indicate primitives...
+ *
+ *		Code for initialization, termination, registration and 
+ *		MAC layer glue.
+ *
+ *		Copyright Tim Alpaerts, 
+ *			<Tim_Alpaerts@toyota-motor-europe.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.
+ *
+ *	Changes
+ *		Alan Cox	:	Chainsawed into Linux format
+ *					Modified to use llc_ names
+ *
+ *	This file must be processed by sed before it can be compiled.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/p8022.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/byteorder.h>
+
+#include "pseudo/pseudocode.h"
+#include "transit/pdutr.h"
+#include "transit/timertr.h"
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+/*
+ *	Data_request() is called by the client to present a data unit
+ *	to the llc for transmission.
+ *	In the future this function should also check if the transmit window
+ *	allows the sending of another pdu, and if not put the skb on the atq
+ *	for deferred sending.
+ */
+
+int llc_data_request(llcptr lp, struct sk_buff *skb)
+{
+	if (skb_headroom(skb) < (lp->dev->hard_header_len +4)){
+		printk("cl2llc: data_request() not enough headroom in skb\n");
+		return -1;
+	};
+
+	skb_push(skb, 4);
+
+	if ((lp->state != NORMAL) && (lp->state != BUSY) && (lp->state != REJECT))
+	{
+		printk("cl2llc: data_request() while no llc connection\n"); 
+		return -1;  
+	}
+
+	if (lp->remote_busy)
+	{     /* if the remote llc is BUSY, */
+		ADD_TO_ATQ(skb);      /* save skb in the await transmit queue */
+		return 0;
+	}                           
+	else
+	{
+		/*
+		 *	Else proceed with xmit 
+		 */
+
+		switch(lp->state)
+		{
+			case NORMAL:
+				if(lp->p_flag)
+					llc_interpret_pseudo_code(lp, NORMAL2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, NORMAL1, skb, NO_FRAME);
+				break;
+			case BUSY:
+				if (lp->p_flag)
+					llc_interpret_pseudo_code(lp, BUSY2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, BUSY1, skb, NO_FRAME);
+				break;
+			case REJECT:
+				if (lp->p_flag)
+					llc_interpret_pseudo_code(lp, REJECT2, skb, NO_FRAME);
+				else
+					llc_interpret_pseudo_code(lp, REJECT1, skb, NO_FRAME);
+				break;
+			default:
+		}
+		return 0;  
+	}              
+}
+
+
+
+/* 
+ *	Disconnect_request() requests that the llc to terminate a connection
+ */
+
+void disconnect_request(llcptr lp)
+{
+	if ((lp->state == NORMAL) ||
+    		(lp->state == BUSY) ||
+		(lp->state == REJECT) ||
+		(lp->state == AWAIT) ||
+		(lp->state == AWAIT_BUSY) ||
+		(lp->state == AWAIT_REJECT))
+	{
+		lp->state = D_CONN;
+		llc_interpret_pseudo_code(lp, SH1, NULL, NO_FRAME);
+	}
+}
+
+
+/*
+ *	Connect_request() requests that the llc to start a connection
+ */
+
+void connect_request(llcptr lp)
+{
+	if (lp->state == ADM)
+	{
+		lp->state = SETUP;
+		llc_interpret_pseudo_code(lp, ADM1, NULL, NO_FRAME);
+	}
+}
+
+
+/*
+ *	Interpret_pseudo_code() executes the actions in the connection component
+ *	state transition table. Table 4 in document on p88.
+ *
+ *	If this function is called to handle an incomming pdu, skb will point
+ *	to the buffer with the pdu and type will contain the decoded pdu type.
+ *
+ *	If called by data_request skb points to an skb that was skb_alloc-ed by 
+ *	the llc client to hold the information unit to be transmitted, there is
+ *	no valid type in this case.  
+ *
+ *	If called because a timer expired no skb is passed, and there is no 
+ *	type.
+ */
+
+void llc_interpret_pseudo_code(llcptr lp, int pc_label, struct sk_buff *skb, 
+		char type)
+{    
+	short int pc;	/* program counter in pseudo code array */ 
+	char p_flag_received;
+	frameptr fr;
+	int resend_count;   /* number of pdus resend by llc_resend_ipdu() */
+	int ack_count;      /* number of pdus acknowledged */
+	struct sk_buff *skb2;
+
+	if (skb != NULL) 
+	{
+		fr = (frameptr) skb->data;
+	}
+	else
+		fr = NULL;
+
+	pc = pseudo_code_idx[pc_label];
+	while(pseudo_code[pc])
+	{
+		switch(pseudo_code[pc])
+		{
+			case IF_F=1_CLEAR_REMOTE_BUSY:
+				if ((type != I_CMD) || (fr->i_hdr.i_pflag =0))
+					break;
+			case CLEAR_REMOTE_BUSY:
+				lp->remote_busy = 0;
+				llc_stop_timer(lp, BUSY_TIMER);
+				if ((lp->state == NORMAL) ||
+					(lp->state == REJECT) ||
+					(lp->state == BUSY))
+				{
+					skb2 = llc_pull_from_atq(lp);
+					if (skb2 != NULL) 
+						llc_start_timer(lp, ACK_TIMER);
+					while (skb2 != NULL)
+					{
+						llc_sendipdu( lp, I_CMD, 0, skb2);
+						skb2 = llc_pull_from_atq(lp);
+					}
+				}	   
+				break;
+			case CONNECT_INDICATION:
+				lp->state = NORMAL;  /* needed to eliminate connect_response() */
+				lp->llc_mode = MODE_ABM;
+				if (lp->ops->connect_indication_ep != NULL)
+					lp->ops->connect_indication_ep(lp);
+				break;
+			case CONNECT_CONFIRM:
+				lp->llc_mode = MODE_ABM;
+				if (lp->ops->connect_confirm_ep != NULL)
+					lp->ops->connect_confirm_ep(lp);
+				break;
+			case DATA_INDICATION:
+				if (lp->ops->data_indication_ep != NULL)
+				{
+					skb_pull(skb, 4);
+					lp->ops->data_indication_ep(lp, skb);
+				}
+				break;
+			case DISCONNECT_INDICATION:
+				lp->llc_mode = MODE_ADM;
+				if (lp->ops->disconnect_indication_ep != NULL)
+					lp->ops->disconnect_indication_ep(lp);
+				break;
+			case RESET_INDICATION(LOCAL):
+				if (lp->ops->reset_indication_ep != NULL)
+					lp->ops->reset_indication_ep(lp, LOCAL);
+				break;
+			case RESET_INDICATION(REMOTE):
+				if (lp->ops->reset_indication_ep != NULL)
+					lp->ops->reset_indication_ep(lp, REMOTE);
+				break;
+			case RESET_CONFIRM:
+				if (lp->ops->reset_confirm_ep != NULL)
+					lp->ops->reset_confirm_ep(lp, REMOTE);
+				break;
+			case REPORT_STATUS(FRMR_RECEIVED):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, FRMR_RECEIVED);
+				break;
+			case REPORT_STATUS(FRMR_SENT):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, FRMR_SENT);
+				break;
+			case REPORT_STATUS(REMOTE_BUSY):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				break;
+			case REPORT_STATUS(REMOTE_NOT_BUSY):
+				if (lp->ops->report_status_ep != NULL)
+					lp->ops->report_status_ep(lp, REMOTE_NOT_BUSY);
+				break;
+			case SEND_DISC_CMD(P=X):
+				llc_sendpdu(lp, DISC_CMD, lp->f_flag, 0, NULL);
+				break;
+			case SEND_DM_RSP(F=X):
+				llc_sendpdu(lp, DM_RSP, 0, 0, NULL);
+				break;
+			case SEND_FRMR_RSP(F=X):                        
+				lp->frmr_info_fld.cntl1 = fr->pdu_cntl.byte1;
+				lp->frmr_info_fld.cntl2 = fr->pdu_cntl.byte2;
+				lp->frmr_info_fld.vs = lp->vs;
+				lp->frmr_info_fld.vr_cr = lp->vr;
+				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+				break;
+			case RE-SEND_FRMR_RSP(F=0):
+				llc_sendpdu(lp, FRMR_RSP, 0, 5, (char *) &lp->frmr_info_fld);
+				break;
+			case RE-SEND_FRMR_RSP(F=P):
+				llc_sendpdu(lp, FRMR_RSP, lp->p_flag,
+					5, (char *) &lp->frmr_info_fld);
+				break;
+			case SEND_I_CMD(P=1):
+				llc_sendipdu(lp, I_CMD, 1, skb);   
+				break;
+			case RE-SEND_I_CMD(P=1):
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+				break;
+			case RE-SEND_I_CMD(P=1)_OR_SEND_RR:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 1);
+				if (resend_count == 0) 
+				{
+					llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				}    
+				break;
+			case SEND_I_XXX(X=0):
+				llc_sendipdu(lp, I_CMD, 0, skb);   
+				break;
+			case RE-SEND_I_XXX(X=0):
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+				break;
+			case RE-SEND_I_XXX(X=0)_OR_SEND_RR:
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_CMD, 0);
+				if (resend_count == 0) 
+				{
+					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+				}    
+				break;
+			case RE-SEND_I_RSP(F=1):
+				resend_count = llc_resend_ipdu(lp, fr->i_hdr.nr, I_RSP, 1);
+				break;
+			case SEND_REJ_CMD(P=1):
+				llc_sendpdu(lp, REJ_CMD, 1, 0, NULL);
+				break;
+			case SEND_REJ_RSP(F=1):
+				llc_sendpdu(lp, REJ_RSP, 1, 0, NULL);
+				break;
+			case SEND_REJ_XXX(X=0):
+				if (IS_RSP(fr))
+					llc_sendpdu(lp, REJ_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, REJ_RSP, 0, 0, NULL);
+				break;
+			case SEND_RNR_CMD(F=1):
+				llc_sendpdu(lp, RNR_CMD, 1, 0, NULL);
+				break;
+			case SEND_RNR_RSP(F=1):
+				llc_sendpdu(lp, RNR_RSP, 1, 0, NULL);
+				break;
+			case SEND_RNR_XXX(X=0):
+				if (IS_RSP(fr))
+					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+				break;
+			case SET_REMOTE_BUSY:
+				if (lp->remote_busy == 0)
+				{
+					lp->remote_busy = 1;
+					llc_start_timer(lp, BUSY_TIMER);
+					if (lp->ops->report_status_ep != NULL)
+						lp->ops->report_status_ep(lp, REMOTE_BUSY);
+				}
+				else if (lp->timer_state[BUSY_TIMER] == TIMER_IDLE)
+				{
+					llc_start_timer(lp, BUSY_TIMER);
+				}
+				break;
+			case OPTIONAL_SEND_RNR_XXX(X=0):
+				if (IS_RSP(fr)) 
+					llc_sendpdu(lp, RNR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RNR_RSP, 0, 0, NULL);
+				break;
+			case SEND_RR_CMD(P=1):
+				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				break;
+			case SEND_ACKNOWLEDGE_CMD(P=1):
+				llc_sendpdu(lp, RR_CMD, 1, 0, NULL);
+				break;
+			case SEND_RR_RSP(F=1):
+				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+				break;
+			case SEND_ACKNOWLEDGE_RSP(F=1):
+				llc_sendpdu(lp, RR_RSP, 1, 0, NULL);
+				break;
+			case SEND_RR_XXX(X=0):
+				llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+				break;
+			case SEND_ACKNOWLEDGE_XXX(X=0):
+				if (IS_RSP(fr)) 
+					llc_sendpdu(lp, RR_CMD, 0, 0, NULL);
+				else
+					llc_sendpdu(lp, RR_RSP, 0, 0, NULL);
+				break;
+			case SEND_SABME_CMD(P=X):
+				llc_sendpdu(lp, SABME_CMD, 0, 0, NULL);
+				lp->f_flag = 0;
+				break;
+			case SEND_UA_RSP(F=X):
+				llc_sendpdu(lp, UA_RSP, lp->f_flag, 0, NULL);
+				break;
+			case S_FLAG:=0:
+				lp->s_flag = 0;
+				break;
+			case S_FLAG:=1:
+				lp->s_flag = 1;
+				break;
+			case START_P_TIMER:
+				if(lp->timer_state[P_TIMER] == TIMER_RUNNING)
+					llc_stop_timer(lp, P_TIMER);
+				llc_start_timer(lp, P_TIMER);
+				if (lp->p_flag == 0)
+				{
+					lp->retry_count = 0;
+					lp->p_flag = 1;
+				}
+				break;
+			case START_ACK_TIMER_IF_NOT_RUNNING:
+				if (lp->timer_state[ACK_TIMER] == TIMER_IDLE)
+					llc_start_timer(lp, ACK_TIMER);
+				break;
+			case START_ACK_TIMER:
+				llc_start_timer(lp, ACK_TIMER);
+				break;
+			case START_REJ_TIMER:
+				llc_start_timer(lp, REJ_TIMER);
+				break;
+			case STOP_ACK_TIMER:
+				llc_stop_timer(lp, ACK_TIMER);
+				break;
+			case STOP_P_TIMER:
+				llc_stop_timer(lp, ACK_TIMER);
+				lp->p_flag = 0;
+				break;
+			case IF_DATA_FLAG=2_STOP_REJ_TIMER:
+				if (lp->data_flag == 2)
+					llc_stop_timer(lp, REJ_TIMER);
+				break;
+			case STOP_REJ_TIMER:
+				llc_stop_timer(lp, REJ_TIMER);
+				break;
+			case STOP_ALL_TIMERS:
+				llc_stop_timer(lp, ACK_TIMER);
+				llc_stop_timer(lp, P_TIMER);
+				llc_stop_timer(lp, REJ_TIMER);
+				llc_stop_timer(lp, BUSY_TIMER);
+				break;
+			case STOP_OTHER_TIMERS:
+				llc_stop_timer(lp, P_TIMER);
+				llc_stop_timer(lp, REJ_TIMER);
+				llc_stop_timer(lp, BUSY_TIMER);
+				break;
+			case UPDATE_N(R)_RECEIVED:             
+				ack_count = llc_free_acknowledged_skbs(lp,
+					(unsigned char) fr->s_hdr.nr);
+				if (ack_count > 0)
+				{
+					lp->retry_count = 0;
+					llc_stop_timer(lp, ACK_TIMER);  
+					if (lp->rtq_front != NULL)
+					{
+						/*
+ 						 *	Re-transmit queue not empty 
+						 */
+						llc_start_timer(lp, ACK_TIMER);  
+					}
+				}        
+				break;
+			case UPDATE_P_FLAG:
+				if (IS_UFRAME(fr)) 
+					p_flag_received = fr->u_hdr.u_pflag;
+				else
+					p_flag_received = fr->i_hdr.i_pflag;
+				if ((fr->pdu_hdr.ssap & 0x01) && (p_flag_received))
+				{
+					lp->p_flag = 0;
+					llc_stop_timer(lp, P_TIMER);  
+				}
+				break;
+			case DATA_FLAG:=2:
+				lp->data_flag = 2;
+				break;
+			case DATA_FLAG:=0:
+				lp->data_flag = 0;
+				break;
+			case DATA_FLAG:=1:
+				lp->data_flag = 1;
+				break;
+			case IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1:
+				if (lp->data_flag == 0)
+					lp->data_flag = 1;
+				break;
+			case P_FLAG:=0:
+				lp->p_flag = 0;
+				break;
+			case P_FLAG:=P:
+				lp->p_flag = lp->f_flag;
+				break;
+			case REMOTE_BUSY:=0:
+				lp->remote_busy = 0;
+				break;
+			case RETRY_COUNT:=0:
+				lp->retry_count = 0;
+				break;
+			case RETRY_COUNT:=RETRY_COUNT+1:
+				lp->retry_count++;
+				break;
+			case V(R):=0:
+				lp->vr = 0;
+				break;
+			case V(R):=V(R)+1:
+				lp->vr++;
+				break;
+			case V(S):=0:
+				lp->vs = 0;
+				break;
+			case V(S):=N(R):
+				lp->vs = fr->i_hdr.nr;
+				break;
+			case F_FLAG:=P:
+				if (IS_UFRAME(fr)) 
+					lp->f_flag = fr->u_hdr.u_pflag;
+				else
+					lp->f_flag = fr->i_hdr.i_pflag;
+				break;
+			default:
+		}
+		pc++;	
+	}
+}
+
+
+/*
+ *	Process_otype2_frame will handle incoming frames
+ *	for 802.2 Type 2 Procedure.
+ */
+
+void llc_process_otype2_frame(llcptr lp, struct sk_buff *skb, char type)
+{
+	int idx;		/*	index in transition table */
+	int pc_label;		/*	action to perform, from tr tbl */
+	int validation;		/*	result of validate_seq_nos */
+	int p_flag_received;	/*	p_flag in received frame */
+	frameptr fr;
+
+	fr = (frameptr) skb->data;
+
+	if (IS_UFRAME(fr))
+		p_flag_received = fr->u_hdr.u_pflag;
+	else
+		p_flag_received = fr->i_hdr.i_pflag;
+
+	switch(lp->state)
+	{
+		/*	Compute index in transition table: */
+		case ADM:
+			idx = type;
+			idx = (idx << 1) + p_flag_received;
+			break;
+		case CONN:
+		case RESET_WAIT:
+		case RESET_CHECK:
+		case ERROR:
+			idx = type;
+			break;
+		case SETUP:
+		case RESET:
+		case D_CONN:
+			idx = type;
+			idx = (idx << 1) + lp->p_flag;
+			break;
+		case NORMAL:
+		case BUSY:
+		case REJECT:
+		case AWAIT:
+		case AWAIT_BUSY:
+		case AWAIT_REJECT:
+			validation = llc_validate_seq_nos(lp, fr);
+			if (validation > 3) 
+				type = BAD_FRAME;
+			idx = type;
+			idx = (idx << 1);
+			if (validation & 1) 
+				idx = idx +1;
+			idx = (idx << 1) + p_flag_received;
+			idx = (idx << 1) + lp->p_flag;
+		default:
+			printk("llc_proc: bad state\n");
+			return;
+	}
+	idx = (idx << 1) + pdutr_offset[lp->state];
+	lp->state = pdutr_entry[idx +1]; 
+	pc_label = pdutr_entry[idx];
+	if (pc_label != NOP)
+	{ 
+		llc_interpret_pseudo_code(lp, pc_label, skb, type);
+	}
+}
+
+
+void llc_timer_expired(llcptr lp, int t)
+{
+	int idx;		/* index in transition table	*/
+	int pc_label;       	/* action to perform, from tr tbl */
+
+	lp->timer_state[t] = TIMER_EXPIRED;
+	idx = lp->state;            /* Compute index in transition table: */
+	idx = (idx << 2) + t;
+	idx = idx << 1;
+	if (lp->retry_count >= lp->n2) 
+		idx = idx + 1;
+	idx = (idx << 1) + lp->s_flag;
+	idx = (idx << 1) + lp->p_flag;
+	idx = idx << 1;             /* 2 bytes per entry: action & newstate */
+
+	pc_label = timertr_entry[idx];
+	if (pc_label != NOP)
+	{
+		llc_interpret_pseudo_code(lp, pc_label, NULL, NO_FRAME);
+		lp->state = timertr_entry[idx +1];
+	}
+	lp->timer_state[t] = TIMER_IDLE;
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/802/llc.c linux/net/802/llc.c
--- v2.1.14/linux/net/802/llc.c	Tue Jun  6 11:22:14 1995
+++ linux/net/802/llc.c	Thu Jan  1 02:00:00 1970
@@ -1,412 +0,0 @@
-/*
- *	802.2 Class 2 LLC service.
- */
- 
- 
-int llc_rx_adm(struct sock *sk,struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
-	if(type==CMD)
-	{
-		if(cmd==DISC)
-			send_response(sk,DM|pf);
-		else if(cmd==SABM)
-		{
-			if(sk->state!=TCP_LISTEN)
-				send_response(sk. DM|pf);
-			else
-			{
-				sk=ll_rx_accept(sk);
-				if(sk!=NULL)
-				{
-					send_response(sk, UA|pf);
-					sk->llc.vs=0;
-					sk->llc.vr=0;
-					sk->llc.p_flag=0;
-					sk->llc.remote_busy=0;
-					llc_state(sk,LLC_NORMAL);
-				}
-			}	
-		}
-		else if(pf)
-			send_response(sk, DM|PF);
-	}
-	return 0;
-}
-
-int llc_rx_setup(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
-	if(type==CMD)
-	{
-		if(cmd==SABM)
-		{
-			sk->llc.vs=0;
-			sk->llc.vr=0;
-			send_response(sk, UA|pf);
-		}
-		if(cmd==DISC)
-		{
-			send_response(sk, DM|pf);
-			llc_error(sk,ECONNRESET);
-			llc_state(sk, LLC_ADM);
-		}
-	}
-	else
-	{
-		if(cmd==UA && pf==sk->llc.p_flag)
-		{
-			del_timer(&sk->llc.t1);
-			sk->llc.vs=0;
-			llc_update_p_flag(sk,pf);
-			llc_state(sk,LLC_NORMAL);
-		}
-		if(cmd==DM)
-		{
-			llc_error(sk, ECONNRESET);
-			llc_state(sk, LLC_ADM);
-		}
-	}
-}
-
-int llc_rx_reset(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
-	if(type==CMD)
-	{
-		if(cmd==SABM)
-		{
-			sk->llc.vr=0;
-			sk->llc.vs=0;
-			send_response(sk, UA|pf);
-		}
-		else if(cmd==DISC)
-		{
-			if(sk->llc.cause_flag==1)
-				llc_shutdown(sk,SHUTDOWN_MASK);
-			else
-				llc_eror(sk, ECONNREFUSED);
-			send_response(sk, DM|pf);
-			llc_state(sk, LLC_ADM);
-		}
-	}
-	else
-	{
-		if(cmd==UA)
-		{
-			if(sk->llc.p_flag==pf)
-			{
-				del_timer(&sk->llc.t1);
-				sk->llc.vs=0;
-				sk->llc.vr=0;
-				llc_update_p_flag(sk,pf);
-				llc_confirm_reset(sk, sk->llc.cause_flag);
-				sk->llc.remote_busy=0;
-				llc_state(sk, LLC_NORMAL);
-			}
-		}
-		if(cmd==DM)
-		{	/* Should check cause_flag */
-			llc_shutdown(sk, SHUTDOWN_MASK);
-			llc_state(sk, LLC_ADM);
-		}
-	}
-	return 0;
-}
-
-int llc_rx_d_conn(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
-	if(type==CMD)
-	{
-		if(cmd==SABM)
-		{
-			llc_error(sk, ECONNRESET);
-			llc_state(sk, ADM);
-		}
-		else if(cmd==DISC)
-		{
-			send_response(UA|pf);
-			llc_state(sk, LLC_D_CONN);
-		}
-		else if(pf)
-			send_response(sk, DM|PF);
-	}
-	else
-	{
-		if(cmd==UA && pf==sk->llc.p_flag)
-		{
-			del_timer(&sk->llc.t1);
-			llc_state(sk, ADM);
-			llc_confirm_reset(sk, sk->llc.cause_flag);
-		}
-		if(cmd==DM)
-		{
-			del_timer(&sk->llc.t1);
-			/*if(sk->llc.cause_flag)*/
-			llc_shutdown(sk, SHUTDOWN_MASK);	
-		}
-		
-	}
-	return 0;
-}
-
-int llc_rx_error(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
-	if(type==CMD)
-	{
-		if(cmd==SABM)
-		{
-			sk->llc.vs=0;
-			sk->llc.vr=0;
-			send_response(sk, UA|pf);
-			llc_error(sk,ECONNRESET);
-			sk->llc.p_flag=0;
-			sk->llc.remote_busy=0;
-			llc_state(sk, LLC_NORMAL);
-		}
-		else if(cmd==DISC)
-		{
-			send_response(sk, UA|pf);
-			llc_shutdown(sk, SHUTDOWN_MASK);
-			llc_state(sk, LLC_ADM);
-		}
-		else
-			llc_resend_frmr_rsp(sk,pf);
-	}
-	else
-	{
-		if(cmd==DM)
-		{
-			llc_error(sk, ECONNRESET);
-			del_timer(&sk->llc.t1);
-			llc_state(sk, LLC_ADM);
-		}
-		if(cmd==FRMR)
-		{
-			send_command(sk, SABM);
-			sk->llc.p_flag=pf;
-			llc_start_t1();
-			sk->llc.retry_count=0;
-			sk->llc.cause_flag=0;
-			llc_error(sk, EPROTO);
-			llc_state(sk, LLC_RESET);
-		}
-	}
-}
-
-
-/*
- *	Subroutine for handling the shared cases of the data modes.
- */
- 
-int llc_rx_nr_shared(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
-	if(type==CMD)
-	{
-		if(cmd==SABM)
-		{
-			/*
-			 *	Optional reset processing. We decline resets.
-			 */
-			send_response(sk,DM|pf);
-			llc_error(sk, ECONNRESET);
-			llc_state(sk, LLC_ADM);
-		}
-		else if(cmd==DISC)
-		{
-			send_response(sk,UA|pf);
-			llc_state(sk, LLC_ADM);
-			llc_shutdown(sk, SHUTDOWN_MASK);
-		}
-		/*
-		 *	We only ever use windows of 7, so there is no illegal NR/NS value
-		 *	otherwise we would FRMR here and go to ERROR state
-		 */
-		else if(cmd==ILLEGAL)
-		{
-			llc_send_frmr_response(sk, ILLEGAL_TYPE,pf);
-			llc_state(sk, LLC_ERROR);
-			llc_error(sk, EPROTO);
-		}
-		else
-			/*
-			 *	Not covered by general rule
-			 */
-			return 0;
-	}
-	else
-	{
-		/*
-		 *	We close on errors
-		 */
-		if(cmd==FRMR)
-		{
-			send_command(sk, DM|pf);
-			sk->llc.p_flag=pf;
-			llc_start_t1(sk);
-			llc_error(sk, EPROTO);
-			sk->llc.cause_flag=0;
-			llc_state(sk, LLC_D_CONN):
-		}
-		else if(cmd==DM)
-		{
-			llc_state(sk, LLC_ADM);
-			llc_error(sk, ECONNREFUSED);
-		}
-		/*
-		 *	We always use a window of 7 so can't get I resp
-		 *	with invalid NS,  or any resp with invalid NR. If
-		 *	we add this they do the same as..
-		 */
-		else if(cmd==UA)
-		{
-			llc_send_frmr_response(sk, UNEXPECTED_CONTROL, pf);
-			llc_state(sk, LLC_ERROR);
-			llc_error(sk, EPROTO);
-		}
-		else if(pf==1 && sk->llc.p_flag==0)
-		{
-			llc_send_frmr_response(sk, UNEXPECTED_RESPONSE, pf);
-			llc_state(sk, LLC_ERROR);
-			llc_error(sk, EPROTO);
-		}
-		else if(cmd==ILLEGAL)
-		{
-			llc_send_frmr_response(sk, ILLEGAL_TYPE,pf);
-			llc_state(sk, LLC_ERROR);
-			llc_error(sk, EPROTO);
-		}
-		else
-			/*
-			 *	Not covered by general rule
-			 */
-			return 0
-	}
-	/*
-	 *	Processed.
-	 */
-	return 1;
-}
-
-int llc_rx_normal(struct sock *sk, struct sk_buff *skb, int type, int cmd, int pf, int nr, int ns)
-{
-	if(llc_rx_nr_shared(sk, skb, type, cmd, pf, nr, ns))
-		return 0;
-	if(cmd==I)
-	{
-		if(llc_invalid_ns(sk,ns))
-		{
-			if((type==RESP && sk->llc.p_flag==pf)||(type==CMD && pf==0 && sk->llc.p_flag==0))
-			{
-				llc_command(sk, REJ|PF);
-				llc_ack_frames(sk,nr);	/* Ack frames and update N(R) */
-				sk->llc.p_flag=PF;
-				llc_state(sk, LLC_REJECT);
-				sk->llc.retry_count=0;
-				llc_start_t1(sk);
-				sk->llc.remote_busy=0;
-			}
-			else if((type==CMD && !pf && sk->llc.p_flag==1) || (type==RESP && !pf && sk->llc.p_flag==1))
-			{
-				if(type==CMD)
-					llc_response(sk, REJ);
-				else
-					llc_command(sk, REJ);
-				llc_ack_frames(sk,nr);
-				sk->llc.retry_count=0;
-				llc_state(sk, LLC_REJECT);
-				llc_start_t1(sk);
-			}
-			else if(pf && type==CMD)
-			{
-				llc_response(sk, REJ|PF);
-				llc_ack_frames(sk,nr);
-				sk->llc.retry_count=0;
-				llc_start_t1(sk);
-			}
-		}
-		else
-		{
-			/*
-			 *	Valid I frame cases
-			 */
-			 
-			 if(sk->llc.p_flag==pf && !(type==CMD && pf))
-			 {
-			 	sk->llc.vr=(sk->llc.vr+1)&7;
-			 	llc_queue_rr_cmd(sk, PF);
-			 	sk->llc.retry_count=0;
-			 	llc_start_t1(sk);
-			 	sk->llc.p_flag=1;
-			 	llc_ack_frames(sk,nr);
-			 	sk->llc.remote_busy=0;
-			 }
-			 else if(sk->ppc.p_flag!=pf)
-			 {
-			 	sk->llc.vr=(sk->llc.vr+1)&7;
-			 	if(type==CMD)
-			 		llc_queue_rr_resp(sk, 0);
-			 	else
-			 		llc_queue_rr_cmd(sk, 0);
-			 	if(sk->llc.nr!=nr)
-			 	{
-			 		llc_ack_frames(sk,nr);
-			 		llc_reset_t1(sk);
-			 	}
-			 }
-			 else if(pf)
-			 {
-			 	sk->llc.vr=(sk->llc.vr+1)&7;
-			 	llc_queue_rr_resp(sk,PF);
-			 	if(sk->llc.nr!=nr)
-			 	{
-					llc_ack_frames(sk,nr);
-					llc_reset_t1(sk);
-			 	}
-			 }
-			 llc_queue_data(sk,skb);
-			 return 1;
-		}
-	}
-	else if(cmd==RR||cmd==RNR)
-	{
-		if(type==CMD || (type==RESP && (!pf || pf==1 && sk->llc.p_flag==1)))
-		{
-			llc_update_p_flag(sk,pf);
-			if(sk->llc.nr!=nr)
-			{
-				llc_ack_frames(sk,nr);
-				llc_reset_t1(sk);
-			}
-			if(cmd==RR)
-				sk->llc.remote_busy=0;
-			else
-			{	sk->llc.remote_busy=1;
-					if(!llc_t1_running(sk))
-					llc_start_t1(sk);
-			}
-		}
-		else if(type==cmd && pf)
-		{
-			if(cmd==RR)
-				llc_queue_rr_resp(sk,PF);
-			else
-			{
-				send_response(sk, RR|PF);
-				if(!llc_t1_running(sk))
-					llc_start_t1(sk);
-			}
-			if(sk->llc.nr!=nr)
-			{
-				llc_ack_frames(sk,nr);
-				llc_reset_t1(sk);
-			}
-			if(cmd==RR)
-				sk->llc.remote_busy=0;
-			else
-				sk->llc.remote_busy=1;
-		}
-	}
-	else if(cmd==REJ)
-	{
-		
-	}
-}
-			
diff -u --recursive --new-file v2.1.14/linux/net/802/llc_macinit.c linux/net/802/llc_macinit.c
--- v2.1.14/linux/net/802/llc_macinit.c	Thu Jan  1 02:00:00 1970
+++ linux/net/802/llc_macinit.c	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,225 @@
+/*
+ * NET		An implementation of the IEEE 802.2 LLC protocol for the
+ *		LINUX operating system.  LLC is implemented as a set of 
+ *		state machines and callbacks for higher networking layers.
+ *
+ *		Code for initialization, termination, registration and 
+ *		MAC layer glue.
+ *
+ *		Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.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.
+ *
+ *	Changes
+ *		Alan Cox	:	Chainsawed to Linux format
+ *					Added llc_ to names
+ *					Started restructuring handlers 
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/unistd.h>
+#include <linux/netdevice.h>
+#include <net/p8022.h>
+
+#include <asm/byteorder.h>
+
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+/*
+ *	All incoming frames pass thru mac_data_indicate().
+ *	Here an llc structure is associated with an skb depending on the source
+ *	MAC address in the pdu. 
+ *	The received sk_buffs with pdus other than I_CMD and I_RSP
+ *	are freed by mac_data_indicate() after processing,
+ *	the I pdu buffers are freed by the cl2llc client when it no longer needs
+ *	the skb. 
+*/
+
+int llc_mac_data_indicate(llcptr lp, struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	int ll;      		/* logical length == 802.3 length field */
+	unsigned char p_flag;
+	unsigned char type;
+	frameptr fr;
+	int free=1;
+	
+	/*
+	 *	Truncate buffer to true 802.3 length  
+	 *	[FIXME: move to 802.2 demux]
+	 */
+	  
+	ll = *(skb->data -2) * 256 + *(skb->data -1);
+	skb_trim( skb, ll );
+
+	fr = (frameptr) skb->data;
+	type = llc_decode_frametype( fr );
+
+
+	if (type <= FRMR_RSP)
+	{
+		/* 
+		 *	PDU is of the type 2 set 
+		 */
+		if ((lp->llc_mode == MODE_ABM)||(type == SABME_CMD)) 
+			llc_process_otype2_frame(lp, skb, type);
+              
+	}
+	else 
+	{
+		/*
+		 *	PDU belongs to type 1 set 
+		 */
+	        p_flag = fr->u_hdr.u_pflag;
+        	switch(type)
+        	{
+		        case TEST_CMD:
+				llc_sendpdu(lp, TEST_RSP, 0,ll -3, 
+					fr->u_hdr.u_info);
+				break;
+			case TEST_RSP:
+				if (lp->ops->test_indication_ep != NULL)
+				{
+					lp->ops->test_indication_ep(lp,
+						ll -3, fr->u_hdr.u_info);
+				}   
+				break;
+			case XID_CMD: 	
+				/*
+				 *	Basic format XID is handled by LLC itself 
+				 *	Doc 5.4.1.1.2 p 48/49 
+				 */
+
+				if ((ll == 6)&&(fr->u_hdr.u_info[0] == 0x81))
+				{
+					lp->k = fr->u_hdr.u_info[2];
+					llc_sendpdu(lp, XID_RSP,
+						fr->u_hdr.u_pflag, ll -3,
+						fr->u_hdr.u_info);
+				}
+				break;
+
+			case XID_RSP:	        
+				if( ll == 6 && fr->u_hdr.u_info[0] == 0x81 )
+				{
+					lp->k = fr->u_hdr.u_info[2]; 
+				}
+				if (lp->ops->xid_indication_ep != NULL)
+				{
+					lp->ops->xid_indication_ep(lp,
+						ll-3, fr->u_hdr.u_info);
+				}
+				break;
+
+			case UI_CMD:
+				if(lp->ops->unit_data_indication_ep != NULL) 
+				{
+					free=lp->ops->unit_data_indication_ep(lp,
+						ll-3, fr->u_hdr.u_info);
+				}
+				break;
+	               
+			default: 
+				/*
+				 *	All other type 1 pdus ignored for now 
+				 */
+		}         
+	}
+	
+	if (free&&(!(IS_IFRAME(fr))))
+	{
+		/*
+		 *	No auto free for I pdus 
+		 */
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_READ);
+	}
+	return 0;
+}
+
+
+/*
+ *	Create an LLC client. As it is the job of the caller to clean up
+ *	LLC's on device down, the device list must be locked before this call.
+ */
+ 
+int register_cl2llc_client(llcptr lp, const char *device, llc_ops *ops, u8 *rmac, u8 ssap, u8 dsap)
+{       
+	char eye_init[] = "LLC\0";
+
+	memset(lp, 0, sizeof(*lp));
+	lp->dev = dev_get(device);
+	if(lp->dev == NULL)
+		return -ENODEV;
+	memcpy(lp->eye, eye_init, sizeof(lp->eye));
+	lp->rw = 1;
+	lp->k = 127;
+	lp->n1 = 1490;
+	lp->n2 = 10;
+	lp->timer_interval[P_TIMER] = HZ;    /* 1 sec */
+	lp->timer_interval[REJ_TIMER] = HZ/8; 
+	lp->timer_interval[ACK_TIMER] = HZ/8;
+	lp->timer_interval[BUSY_TIMER] = HZ*2;
+	lp->local_sap = ssap;
+	lp->ops = ops;
+	lp->remote_mac_len = lp->dev->addr_len; 
+	memcpy(lp->remote_mac, rmac, lp->remote_mac_len);
+	lp->state = 0;
+	lp->llc_mode = MODE_ADM;
+	lp->remote_sap = dsap;
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+void unregister_cl2llc_client(llcptr lp)
+{
+	llc_cancel_timers(lp);
+	MOD_DEC_USE_COUNT;
+	kfree(lp);
+}
+
+
+static struct symbol_table cl2llc_proto_syms = 
+{
+#include <linux/symtab_begin.h>
+	X(register_cl2llc_client),
+	X(unregister_cl2llc_client),
+	X(llc_data_request),
+	X(llc_unit_data_request),
+	X(llc_test_request),
+	X(llc_xid_request),
+#include <linux/symtab_end.h>
+};
+
+
+#define ALL_TYPES_8022 0
+
+void llc_init(struct net_proto *proto)
+{
+	register_symtab( &cl2llc_proto_syms ); 
+	printk(KERN_NOTICE "IEEE 802.2 LLC for Linux 2.1 (c) 1996 Tim Alpaerts\n");
+	return;        
+}
+
+#ifdef MODULE
+
+
+int init_module(void)
+{
+	llc_init(NULL);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+
+}
+
+#endif
diff -u --recursive --new-file v2.1.14/linux/net/802/llc_sendpdu.c linux/net/802/llc_sendpdu.c
--- v2.1.14/linux/net/802/llc_sendpdu.c	Thu Jan  1 02:00:00 1970
+++ linux/net/802/llc_sendpdu.c	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,380 @@
+/*
+ * NET		An implementation of the IEEE 802.2 LLC protocol for the
+ *		LINUX operating system.  LLC is implemented as a set of 
+ *		state machines and callbacks for higher networking layers.
+ *
+ *		llc_sendpdu(), llc_sendipdu(), resend() + queue handling code
+ *
+ *		Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.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.
+ *
+ *	Changes
+ *		Alan Cox	:	Chainsawed into Linux format, style
+ *					Added llc_ to function names
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <net/p8022.h>
+#include <linux/stat.h>
+#include <asm/byteorder.h>
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+static unsigned char cntl_byte_encode[] = 
+{
+	0x00,   /* I_CMD */
+	0x01,   /* RR_CMD */
+	0x05,   /* RNR_CMD */
+	0x09,   /* REJ_CMD */
+	0x43,   /* DISC_CMD */
+	0x7F,   /* SABME_CMD */
+	0x00,   /* I_RSP */
+	0x01,   /* RR_RSP */
+	0x05,   /* RNR_RSP */
+	0x09,   /* REJ_RSP */
+	0x63,   /* UA_RSP */
+	0x0F,   /* DM_RSP */
+	0x87,   /* FRMR_RSP */
+	0xFF,   /* BAD_FRAME */
+	0x03,   /* UI_CMD */
+	0xBF,   /* XID_CMD */
+	0xE3,   /* TEST_CMD */
+	0xBF,   /* XID_RSP */
+	0xE3    /* TEST_RSP */
+};
+
+static unsigned char fr_length_encode[] = 
+{
+	0x04,   /* I_CMD */
+	0x04,   /* RR_CMD */
+	0x04,   /* RNR_CMD */
+	0x04,   /* REJ_CMD */
+	0x03,   /* DISC_CMD */
+	0x03,   /* SABME_CMD */
+	0x04,   /* I_RSP */
+	0x04,   /* RR_RSP */
+	0x04,   /* RNR_RSP */
+	0x04,   /* REJ_RSP */
+	0x03,   /* UA_RSP */
+	0x03,   /* DM_RSP */
+	0x03,   /* FRMR_RSP */
+	0x00,   /* BAD_FRAME */
+	0x03,   /* UI_CMD */
+	0x03,   /* XID_CMD */
+	0x03,   /* TEST_CMD */
+	0x03,   /* XID_RSP */
+	0x03    /* TEST_RSP */
+};
+
+static unsigned char cr_bit_encode[] = {
+	0x00,   /* I_CMD */
+	0x00,   /* RR_CMD */
+	0x00,   /* RNR_CMD */
+	0x00,   /* REJ_CMD */
+	0x00,   /* DISC_CMD */
+	0x00,   /* SABME_CMD */
+	0x01,   /* I_RSP */
+	0x01,   /* RR_RSP */
+	0x01,   /* RNR_RSP */
+	0x01,   /* REJ_RSP */
+	0x01,   /* UA_RSP */
+	0x01,   /* DM_RSP */
+	0x01,   /* FRMR_RSP */
+	0x00,   /* BAD_FRAME */
+	0x00,   /* UI_CMD */
+	0x00,   /* XID_CMD */
+	0x00,   /* TEST_CMD */
+	0x01,   /* XID_RSP */
+	0x01    /* TEST_RSP */
+};
+
+/*
+ *	Sendpdu() constructs an output frame in a new skb and
+ *	gives it to the MAC layer for transmision.
+ *	This function is not used to send I pdus.
+ *	No queues are updated here, nothing is saved for retransmission.
+ *
+ *	Parameter pf controls both the poll/final bit and dsap
+ *	fields in the output pdu. 
+ *	The dsap trick was needed to implement XID_CMD send with
+ *	zero dsap field as described in doc 6.6 item 1 of enum.  
+ */
+
+void llc_sendpdu(llcptr lp, char type, char pf, int data_len, char *pdu_data)
+{
+	frameptr fr;                /* ptr to output pdu buffer */
+	unsigned short int fl;      /* frame length == 802.3 "length" value */
+	struct sk_buff *skb;
+
+	fl = data_len + fr_length_encode[(int)type];
+	skb = alloc_skb(16 + fl, GFP_ATOMIC); 
+	if (skb != NULL)
+	{
+		skb->dev = lp->dev;	
+	        skb_reserve(skb, 16);
+		fr = (frameptr) skb_put(skb, fl);
+		memset(fr, 0, fl);
+                /*
+                 *	Construct 802.2 header 
+                 */
+		if (pf & 0x02) 
+			fr->pdu_hdr.dsap = 0;
+		else
+			fr->pdu_hdr.dsap = lp->remote_sap;
+		fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
+		fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
+		/* 
+		 *	Fill in pflag and seq nbrs: 
+		 */
+		if (IS_SFRAME(fr)) 
+		{
+		  	/* case S-frames */
+			if (pf & 0x01) 
+				fr->i_hdr.i_pflag = 1;
+			fr->i_hdr.nr = lp->vr;
+		}
+		else
+		{
+			/* case U frames */ 
+			if (pf & 0x01) 
+				fr->u_hdr.u_pflag = 1;
+		}
+                       
+		if (data_len > 0) 
+		{ 			/* append data if any  */ 
+			if (IS_UFRAME(fr)) 
+			{
+				memcpy(fr->u_hdr.u_info, pdu_data, data_len);
+			}
+			else 
+			{
+				memcpy(fr->i_hdr.is_info, pdu_data, data_len);
+			}
+		}
+		lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
+			 lp->remote_mac, NULL, fl);
+		skb->arp = 1;
+		skb->free = 1;
+		dev_queue_xmit(skb, lp->dev, SOPRI_NORMAL);
+	}
+	else
+		printk(KERN_DEBUG "cl2llc: skb_alloc() in llc_sendpdu() failed\n");     
+}
+
+void llc_xid_request(llcptr lp, char opt, int ll, char * data)
+{
+	llc_sendpdu(lp, XID_CMD, opt, ll, data); 
+}
+
+void llc_test_request(llcptr lp, int ll, char * data)
+{
+	llc_sendpdu(lp, TEST_CMD, 0, ll, data); 
+}
+
+void llc_unit_data_request(llcptr lp, int ll, char * data)
+{
+	llc_sendpdu(lp, UI_CMD, 0, ll, data); 
+}
+
+
+/*
+ *	llc_sendipdu() Completes an I pdu in an existing skb and gives it
+ *	to the MAC layer for transmision.
+ *	Parameter "type" must be either I_CMD or I_RSP.
+ *	The skb is not freed after xmit, it is kept in case a retransmission
+ *	is requested. If needed it can be picked up again from the rtq.
+ */
+
+void llc_sendipdu(llcptr lp, char type, char pf, struct sk_buff *skb)
+{
+	frameptr fr;                /* ptr to output pdu buffer */
+
+	fr = (frameptr) skb->data;
+
+	fr->pdu_hdr.dsap = lp->remote_sap;
+	fr->pdu_hdr.ssap = lp->local_sap + cr_bit_encode[(int)type];
+	fr->pdu_cntl.byte1 = cntl_byte_encode[(int)type];
+			
+	if (pf)
+		fr->i_hdr.i_pflag = 1; /* p/f and seq numbers */  
+	fr->i_hdr.nr = lp->vr;
+	fr->i_hdr.ns = lp->vs;
+	lp->vs++;
+	if (lp->vs > 127) 
+		lp->vs = 0;
+	lp->dev->hard_header(skb, lp->dev, ETH_P_802_3,
+		lp->remote_mac, NULL, skb->len);
+	skb->arp = 1;
+	skb->free = 0;              /* thanks, Alan */
+	ADD_TO_RTQ(skb);		/* add skb to the retransmit queue */
+	dev_queue_xmit(skb, lp->dev, SOPRI_NORMAL);
+}
+
+
+/*
+ *	Resend_ipdu() will resend the pdus in the retransmit queue (rtq)
+ *	the return value is the number of pdus resend.
+ *	ack_nr is N(R) of 1st pdu to resent.
+ *	Type is I_CMD or I_RSP for 1st pdu resent.
+ *	p is p/f flag 0 or 1 for 1st pdu resent.
+ *	All subsequent pdus will be sent as I_CMDs with p/f set to 0
+ */ 
+
+int llc_resend_ipdu(llcptr lp, unsigned char ack_nr, unsigned char type, char p)
+{
+	struct sk_buff *skb;
+	int resend_count;
+	frameptr fr;
+
+	resend_count = 0;
+	skb = lp->rtq_front;
+
+	while(skb != NULL)
+	{
+		/* 
+		 *	Should not occur: 
+		 */
+		 
+		if (skb_device_locked(skb)) 
+			return resend_count;
+		
+		fr = (frameptr) (skb->data + lp->dev->hard_header_len);
+		if (resend_count == 0) 
+		{
+			/* 
+			 *	Resending 1st pdu: 
+			 */
+
+			if (p) 
+				fr->i_hdr.i_pflag = 1;
+			else
+				fr->i_hdr.i_pflag = 0;
+            
+			if (type == I_CMD)           
+				fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
+			else
+				fr->pdu_hdr.ssap = fr->pdu_hdr.ssap | 0x01;
+		}
+	        else
+	        {
+	        	/*
+	        	 *	Resending pdu 2...n 
+	        	 */
+
+			fr->pdu_hdr.ssap = fr->pdu_hdr.ssap & 0xfe;
+			fr->i_hdr.i_pflag = 0;
+		}
+		fr->i_hdr.nr = lp->vr;
+		fr->i_hdr.ns = lp->vs;
+		lp->vs++;
+		if (lp->vs > 127) 
+			lp->vs = 0;
+		skb->arp = 1;
+		skb->free = 0;
+		dev_queue_xmit(skb, lp->dev, SOPRI_NORMAL);
+		resend_count++;
+		skb = skb->link3;
+	}
+	return resend_count;
+}
+
+/* ************** internal queue management code ****************** */
+
+
+/*
+ *	Add_to_queue() adds an skb at back of an I-frame queue.
+ *	this function is used for both atq and rtq.
+ *	the front and back pointers identify the queue being edited.
+ *	this function is called with macros ADD_TO_RTQ() and ADD_TO_ATQ() .
+ */
+
+void llc_add_to_queue(struct sk_buff *skb,
+	struct sk_buff **front, struct sk_buff **back) 
+{
+	struct sk_buff *t;
+
+	skb->link3 = NULL;		/* there is no more recent skb */ 
+	t = *back;			/* save current back ptr */
+	*back = skb;
+	if (t != NULL) 
+		t->link3 = skb;
+	if (*front == NULL) 
+		*front = *back;
+}
+
+
+/*
+ *	Remove one skb from the front of the awaiting transmit queue
+ *	(this is the skb longest on the queue) and return a pointer to 
+ *	that skb. 
+ */ 
+
+struct sk_buff *llc_pull_from_atq(llcptr lp) 
+{
+	struct sk_buff *t;
+
+	if (lp->atq_front == NULL) 
+		return NULL;     /* empty queue */
+
+	t = lp->atq_front;
+	lp->atq_front = t->link3;
+	if (lp->atq_front == NULL) 
+	{
+		lp->atq_back = lp->atq_front;
+	}
+	return t;
+}
+ 
+/*
+ *	Free_acknowledged_skbs(), remove from retransmit queue (rtq)
+ *	and free all skbs with an N(S) chronologicaly before 'pdu_ack'.
+ *	The return value is the number of pdus acknowledged.
+ */
+ 
+int llc_free_acknowledged_skbs(llcptr lp, unsigned char pdu_ack)
+{
+	struct sk_buff *pp;
+	frameptr fr; 
+	int ack_count;
+	unsigned char ack; 	/* N(S) of most recently ack'ed pdu */
+	unsigned char ns_save; 
+
+	if (pdu_ack > 0) 
+		ack = pdu_ack -1;
+	else 
+		ack = 127;
+
+	ack_count = 0;
+	pp = lp->rtq_front; 
+	while (pp != NULL)
+	{
+		/* 
+		 *	Locate skb with N(S) == ack 
+		 */
+		lp->rtq_front = pp->link3;
+		fr = (frameptr) (pp->data + lp->dev->hard_header_len);
+		ns_save = fr->i_hdr.ns;
+		if (skb_device_locked(pp)) 
+			return ack_count;
+
+		kfree_skb(pp, FREE_WRITE);
+		ack_count++;
+
+		if (ns_save == ack) 
+			break;  
+		pp = lp->rtq_front;   
+	}
+	if (pp == NULL)			/* if rtq empty now */ 
+		lp->rtq_back = NULL;		/* correct back pointer */
+
+	return ack_count; 
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/802/llc_utility.c linux/net/802/llc_utility.c
--- v2.1.14/linux/net/802/llc_utility.c	Thu Jan  1 02:00:00 1970
+++ linux/net/802/llc_utility.c	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,253 @@
+/*
+ * NET		An implementation of the IEEE 802.2 LLC protocol for the
+ *		LINUX operating system.  LLC is implemented as a set of 
+ *		state machines and callbacks for higher networking layers.
+ *
+ *		Small utilities, Linux timer handling.
+ *
+ *		Written by Tim Alpaerts, Tim_Alpaerts@toyota-motor-europe.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.
+ *
+ *	Changes
+ *		Alan Cox	:	Chainsawed into Linux form.
+ *					Added llc_ function name prefixes.
+ *					Fixed bug in stop/start timer.
+ *					Added llc_cancel_timers for closing
+ *						down an llc
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <net/llc_frame.h>
+#include <net/llc.h>
+
+int llc_decode_frametype(frameptr fr)
+{
+	if (IS_UFRAME(fr)) 
+	{      /* unnumbered cmd/rsp */
+		switch(fr->u_mm.mm & 0x3B)
+		{
+			case 0x1B:
+			    return(SABME_CMD);
+			    break;
+			case 0x10:
+			    return(DISC_CMD);
+			    break;
+			case 0x18:
+			    return(UA_RSP);
+			    break;
+			case 0x03:
+			    return(DM_RSP);
+			    break;
+			case 0x21:
+			    return(FRMR_RSP);
+			    break;
+			case 0x00:
+			    return(UI_CMD);
+			    break;
+			case 0x2B:
+		 	    if (IS_RSP(fr)) 
+		 	    	return(XID_RSP);
+			    else
+			    	return(XID_CMD);
+			    break;
+			case 0x38:
+		    	    if (IS_RSP(fr))
+		    	    	return(TEST_RSP);
+			    else
+				return(TEST_CMD);
+			    break;
+			default:
+			    return(BAD_FRAME);
+		}
+	}
+	else if (IS_SFRAME(fr))
+	{  /* supervisory cmd/rsp */
+		switch(fr->s_hdr.ss)
+		{
+			case 0x00:
+			    if (IS_RSP(fr)) 
+			    	return(RR_RSP);
+			    else
+			    	return(RR_CMD);
+			    break;
+			case 0x02:
+			    if (IS_RSP(fr))
+			    	return(REJ_RSP);
+			    else
+			    	return(REJ_CMD);
+			    break;
+			case 0x01:
+			    if (IS_RSP(fr))
+			    	return(RNR_RSP);
+			    else
+			    	return(RNR_CMD);
+			    break;
+			default:
+			    return(BAD_FRAME);
+		}
+	}
+	else
+	{			  /* information xfer */
+		if (IS_RSP(fr)) 
+			return(I_RSP);
+		else	
+			return(I_CMD);
+	}
+}
+
+
+/*
+ *	Validate_seq_nos will check N(S) and N(R) to see if they are
+ *	invalid or unexpected.
+ *	"unexpected" is explained on p44 Send State Variable.
+ *	The return value is:
+ *		4 * invalid N(R) +
+ *		2 * invalid N(S) +
+ *		1 * unexpected N(S)
+ */
+
+int llc_validate_seq_nos(llcptr lp, frameptr fr)
+{
+	int res;
+     
+	/*
+	 *	A U-frame is always good 
+	 */
+
+	if (IS_UFRAME(fr)) 
+		return(0);	
+
+	/*
+	 *	For S- and I-frames check N(R): 
+	 */
+
+	if (fr->i_hdr.nr == lp->vs) 
+	{    	/* if N(R) = V(S)  */
+        	res = 0;                        /* N(R) is good */
+	}
+	else
+	{				/* lp->k = transmit window size */
+    		if (lp->vs >= lp->k) 
+    		{	/* if window not wrapped around 127 */
+			if ((fr->i_hdr.nr < lp->vs) &&
+				(fr->i_hdr.nr > (lp->vs - lp->k)))
+				res = 0;
+			else 
+				res = 4;		/* N(R) invalid */
+		}
+		else
+		{	/* window wraps around 127 */
+			if ((fr->i_hdr.nr < lp->vs) ||
+				(fr->i_hdr.nr > (128 + lp->vs - lp->k))) 
+				res = 0;
+			else
+				res = 4;		/* N(R) invalid */
+		}
+	}
+
+	/*
+	 *	For an I-frame, must check N(S) also:  
+	 */
+
+	if (IS_IFRAME(fr)) 
+	{
+    		if (fr->i_hdr.ns == lp->vr) 
+    			return res;   /* N(S) good */
+		if (lp->vr >= lp->rw) 
+		{
+			/* if receive window not wrapped */
+
+			if ((fr->i_hdr.ns < lp->vr) &&
+				(fr->i_hdr.ns > (lp->vr - lp->k)))
+				res = res +1;   	/* N(S) unexpected */
+			else  
+				res = res +2;         /* N(S) invalid */            
+		}
+		else
+		{
+			/* Window wraps around 127 */
+
+			if ((fr->i_hdr.ns < lp->vr) ||
+				(fr->i_hdr.ns > (128 + lp->vr - lp->k)))
+				res = res +1;   	/* N(S) unexpected */
+			else
+				res = res +2;         /* N(S) invalid */            
+		}
+	}					
+	return(res);
+}
+
+/* **************** timer management routines ********************* */
+
+static void llc_p_timer_expired(unsigned long ulp)
+{
+	llc_timer_expired((llcptr) ulp, P_TIMER);
+}
+
+static void llc_rej_timer_expired(unsigned long ulp)
+{
+	llc_timer_expired((llcptr) ulp, REJ_TIMER);
+}
+
+static void llc_ack_timer_expired(unsigned long ulp)
+{
+	llc_timer_expired((llcptr) ulp, ACK_TIMER);
+} 
+
+static void llc_busy_timer_expired(unsigned long ulp)
+{
+	llc_timer_expired((llcptr) ulp, BUSY_TIMER);
+}
+
+/* exp_fcn is an array holding the 4 entry points of the
+   timer expiry routines above.
+   It is required to keep start_timer() generic.
+   Thank you cdecl.
+ */
+
+static void (* exp_fcn[])(unsigned long) = 
+{
+	llc_p_timer_expired,
+	llc_rej_timer_expired,
+	llc_ack_timer_expired,
+	llc_busy_timer_expired
+};   
+
+void llc_start_timer(llcptr lp, int t)
+{
+	if (lp->timer_state[t] == TIMER_IDLE)
+	{
+    		lp->tl[t].expires = jiffies + lp->timer_interval[t];
+    		lp->tl[t].data = (unsigned long) lp;
+    		lp->tl[t].function = exp_fcn[t];
+    		add_timer(&lp->tl[t]);
+    		lp->timer_state[t] = TIMER_RUNNING;
+	}
+}
+
+void llc_stop_timer(llcptr lp, int t)
+{
+	if (lp->timer_state[t] == TIMER_RUNNING)
+	{
+        	del_timer(&lp->tl[t]);
+        	lp->timer_state[t] = TIMER_IDLE;
+	}
+}
+
+void llc_cancel_timers(llcptr lp)
+{
+	llc_stop_timer(lp, P_TIMER);
+	llc_stop_timer(lp, REJ_TIMER);
+	llc_stop_timer(lp, ACK_TIMER);
+	llc_stop_timer(lp, BUSY_TIMER);
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/Makefile linux/net/802/pseudo/Makefile
--- v2.1.14/linux/net/802/pseudo/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/Makefile	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,13 @@
+all:	pseudocode.h actionnm.h
+
+clean:
+	touch pseudocode.h actionnm.h
+	rm pseudocode.h actionnm.h
+
+pseudocode.h: pseudocode opcd2num.sed compile.awk
+	sed -f opcd2num.sed pseudocode |  awk -f compile.awk >pseudocode.h
+
+actionnm.h: pseudocode.h actionnm.awk
+	awk -f actionnm.awk pseudocode.h>actionnm.h
+
+	
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/actionnm.awk linux/net/802/pseudo/actionnm.awk
--- v2.1.14/linux/net/802/pseudo/actionnm.awk	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/actionnm.awk	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,27 @@
+# usage: awk -f actionnm.awk pseudocode.h
+#
+BEGIN { "date" | getline
+	today = $0
+	printf("\n/* this file generated on %s  */\n", today )
+	printf("\nstatic char *action_names[] = { \n    " )
+	opl = 0
+}
+
+/^#define/ {			
+	if ( opl > 3 ) {
+	    printf("\n    ")
+	    opl = 0
+	}
+	opl = opl +1
+	t = sprintf("\"%s\"", $2 )
+	printf("%-15s ,", t )
+#	printf("%-10s", $2 )
+}
+
+END {		
+	if ( opl > 3 ) {
+	    printf("\n    ")
+	}
+	printf("\t  0\n};\n\n")
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/actionnm.h linux/net/802/pseudo/actionnm.h
--- v2.1.14/linux/net/802/pseudo/actionnm.h	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/actionnm.h	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,51 @@
+
+/* this file generated on Thu Oct 24 11:42:37 GMT 1996  */
+
+static char *action_names[] = { 
+    "NOP"           ,"ADM1"          ,"ADM2"          ,"ADM3"          ,
+    "ADM4"          ,"ADM5"          ,"CONN2"         ,"CONN3"         ,
+    "CONN4"         ,"CONN5"         ,"RESWAIT1"      ,"RESWAIT2"      ,
+    "RESWAIT3"      ,"RESWAIT4"      ,"RESWAIT5"      ,"RESWAIT6"      ,
+    "RESWAIT7"      ,"RESWAIT8"      ,"RESCHK1"       ,"RESCHK2"       ,
+    "RESCHK3"       ,"RESCHK4"       ,"RESCHK5"       ,"RESCHK6"       ,
+    "SETUP1"        ,"SETUP2"        ,"SETUP3"        ,"SETUP4"        ,
+    "SETUP5"        ,"SETUP6"        ,"SETUP7"        ,"SETUP8"        ,
+    "RESET1"        ,"RESET2"        ,"RESET3"        ,"RESET4"        ,
+    "RESET5"        ,"RESET6"        ,"RESET7"        ,"RESET8"        ,
+    "D_CONN1"       ,"D_CONN2"       ,"D_CONN3"       ,"D_CONN4"       ,
+    "D_CONN5"       ,"D_CONN6"       ,"D_CONN7"       ,"ERR1"          ,
+    "ERR2"          ,"ERR3"          ,"ERR4"          ,"ERR5"          ,
+    "ERR6"          ,"ERR7"          ,"ERR8"          ,"SH1"           ,
+    "SH2"           ,"SH3"           ,"SH4"           ,"SH5"           ,
+    "SH6"           ,"SH7"           ,"SH8"           ,"SH9"           ,
+    "SH10"          ,"SH11"          ,"NORMAL1"       ,"NORMAL2"       ,
+    "NORMAL3"       ,"NORMAL4"       ,"NORMAL5"       ,"NORMAL6"       ,
+    "NORMAL7"       ,"NORMAL8A"      ,"NORMAL8B"      ,"NORMAL9"       ,
+    "NORMAL10"      ,"NORMAL11"      ,"NORMAL12"      ,"NORMAL13"      ,
+    "NORMAL14"      ,"NORMAL15"      ,"NORMAL16"      ,"NORMAL17"      ,
+    "NORMAL18"      ,"NORMAL19"      ,"NORMAL20"      ,"BUSY1"         ,
+    "BUSY2"         ,"BUSY3"         ,"BUSY4"         ,"BUSY5"         ,
+    "BUSY6"         ,"BUSY7"         ,"BUSY8"         ,"BUSY9"         ,
+    "BUSY10"        ,"BUSY11"        ,"BUSY12"        ,"BUSY13"        ,
+    "BUSY14"        ,"BUSY15"        ,"BUSY16"        ,"BUSY17"        ,
+    "BUSY18"        ,"BUSY19"        ,"BUSY20"        ,"BUSY21"        ,
+    "BUSY22"        ,"BUSY23"        ,"BUSY24"        ,"BUSY25"        ,
+    "BUSY26"        ,"REJECT1"       ,"REJECT2"       ,"REJECT3"       ,
+    "REJECT4"       ,"REJECT5"       ,"REJECT6"       ,"REJECT7"       ,
+    "REJECT8"       ,"REJECT9"       ,"REJECT10"      ,"REJECT11"      ,
+    "REJECT12"      ,"REJECT13"      ,"REJECT14"      ,"REJECT15"      ,
+    "REJECT16"      ,"REJECT17"      ,"REJECT18"      ,"REJECT19"      ,
+    "REJECT20"      ,"AWAIT1"        ,"AWAIT2"        ,"AWAIT3"        ,
+    "AWAIT4"        ,"AWAIT5"        ,"AWAIT6"        ,"AWAIT7"        ,
+    "AWAIT8"        ,"AWAIT9"        ,"AWAIT10"       ,"AWAIT11"       ,
+    "AWAIT12"       ,"AWAIT13"       ,"AWAIT14"       ,"AWAIT_BUSY1"   ,
+    "AWAIT_BUSY2"   ,"AWAIT_BUSY3"   ,"AWAIT_BUSY4"   ,"AWAIT_BUSY5"   ,
+    "AWAIT_BUSY6"   ,"AWAIT_BUSY7"   ,"AWAIT_BUSY8"   ,"AWAIT_BUSY9"   ,
+    "AWAIT_BUSY10"  ,"AWAIT_BUSY11"  ,"AWAIT_BUSY12"  ,"AWAIT_BUSY13"  ,
+    "AWAIT_BUSY14"  ,"AWAIT_BUSY15"  ,"AWAIT_BUSY16"  ,"AWAIT_REJECT1" ,
+    "AWAIT_REJECT2" ,"AWAIT_REJECT3" ,"AWAIT_REJECT4" ,"AWAIT_REJECT5" ,
+    "AWAIT_REJECT6" ,"AWAIT_REJECT7" ,"AWAIT_REJECT8" ,"AWAIT_REJECT9" ,
+    "AWAIT_REJECT10" ,"AWAIT_REJECT11" ,"AWAIT_REJECT12" ,"AWAIT_REJECT13" ,
+    	  0
+};
+
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/compile.awk linux/net/802/pseudo/compile.awk
--- v2.1.14/linux/net/802/pseudo/compile.awk	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/compile.awk	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,57 @@
+# usage: cat pseudocode | sed -f act2num | awk -f compile.awk
+#
+#
+BEGIN { "date" | getline
+	today = $0
+	printf("\n/* this file generated on %s  */\n", today )
+	printf("\nstatic char pseudo_code [ ] = { \n" )
+	opl = 0			# op codes on the current line
+
+	opc = 0			# opcode counter
+	fpi = 0			# fill pointer for idx array
+}
+
+/^;/ { } 			# line starting with semicolon is comment 
+
+/^[A-Z]/ {			# start of a new action 
+	emit( 0 )
+	idx[ ++fpi ] = opc
+	name[ fpi ] = $1 
+	emit( $2 )
+}
+
+/^[\t ]/ { 
+        emit( $1 )
+}
+
+END {		
+	if ( opl > 8 ) {
+	    printf("\n")
+	}
+	printf("\t  0\n};\n\n")
+	printf("static short int pseudo_code_idx [ ] ={\n")
+	opl = 0
+	emit( 0 )
+	for( ii = 1; ii <= fpi; ii++ )
+	   emit( idx[ ii ] )
+	if ( opl > 8 ) {
+	    printf("\n")
+	}
+	printf("\t  0\n};\n\n")
+
+	printf("#define %-10s \t %3d \n", "NOP", 0 )
+	for( ii = 1; ii <= fpi; ii++ )
+	    printf("#define %-10s \t %3d \n", name[ ii ], ii )
+	printf("\n")
+}
+
+function emit( opcode ){	# Niclaus Wirth
+	if ( opl > 8 ) {
+	    printf("\n")
+	    opl = 0
+	}
+	opl = opl +1
+	printf("\t%4d,", opcode )
+	opc++
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/opcd2num.sed linux/net/802/pseudo/opcd2num.sed
--- v2.1.14/linux/net/802/pseudo/opcd2num.sed	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/opcd2num.sed	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,72 @@
+s/NOP/0/
+s/DUMMY_6/6/
+s/DUMMY_8/8/
+s/IF_F=1_CLEAR_REMOTE_BUSY/9/
+s/CLEAR_REMOTE_BUSY/1/
+s/CONNECT_CONFIRM/3/
+s/DISCONNECT_INDICATION/5/
+s/CONNECT_INDICATION/2/
+s/IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1/55/
+s/DATA_FLAG:=0/53/
+s/DATA_FLAG:=1/54/
+s/DATA_FLAG:=2/52/
+s/DATA_INDICATION/4/
+s/F_FLAG:=P/65/
+s/IF_DATA_FLAG=2_STOP_REJ_TIMER/10/
+s/OPTIONAL_SEND_RNR_XXX(X=0)/30/
+s/P_FLAG:=0/56/
+s/P_FLAG:=P/57/
+s/RE-SEND_FRMR_RSP(F=0)/14/
+s/RE-SEND_FRMR_RSP(F=P)/15/
+s/RE-SEND_I_CMD(P=1)_OR_SEND_RR/18/
+s/RE-SEND_I_CMD(P=1)/17/
+s/RE-SEND_I_RSP(F=1)/22/
+s/RE-SEND_I_XXX(X=0)_OR_SEND_RR/21/
+s/RE-SEND_I_XXX(X=0)/20/
+s/REMOTE_BUSY:=0/58/
+s/REPORT_STATUS(FRMR_RECEIVED)/66/
+s/REPORT_STATUS(FRMR_SENT)/67/
+s/REPORT_STATUS(REMOTE_BUSY)/68/
+s/REPORT_STATUS(REMOTE_NOT_BUSY)/69/
+s/RESET_CONFIRM/7/
+s/RESET_INDICATION(LOCAL)/70/
+s/RESET_INDICATION(REMOTE)/71/
+s/RETRY_COUNT:=RETRY_COUNT+1/60/
+s/RETRY_COUNT:=0/59/
+s/SEND_ACKNOWLEDGE_CMD(P=1)/32/
+s/SEND_ACKNOWLEDGE_RSP(F=1)/34/
+s/SEND_ACKNOWLEDGE_XXX(X=0)/36/
+s/SEND_DISC_CMD(P=X)/11/
+s/SEND_DM_RSP(F=X)/12/
+s/SEND_FRMR_RSP(F=X)/13/
+s/SEND_I_CMD(P=1)/16/
+s/SEND_I_XXX(X=0)/19/
+s/SEND_REJ_CMD(P=1)/23/
+s/SEND_REJ_RSP(F=1)/24/
+s/SEND_REJ_XXX(X=0)/25/
+s/SEND_RNR_CMD(F=1)/26/
+s/SEND_RNR_RSP(F=1)/27/
+s/SEND_RNR_XXX(X=0)/28/
+s/SEND_RR_CMD(P=1)/31/
+s/SEND_RR_RSP(F=1)/33/
+s/SEND_RR_XXX(X=0)/35/
+s/SEND_SABME_CMD(P=X)/37/
+s/SEND_UA_RSP(F=X)/38/
+s/SET_REMOTE_BUSY/29/
+s/START_ACK_TIMER_IF_NOT_RUNNING/44/
+s/START_ACK_TIMER/42/
+s/START_P_TIMER/41/
+s/START_REJ_TIMER/43/
+s/STOP_ACK_TIMER/45/
+s/STOP_ALL_TIMERS/48/
+s/STOP_OTHER_TIMERS/49/
+s/STOP_P_TIMER/46/
+s/STOP_REJ_TIMER/47/
+s/S_FLAG:=0/39/
+s/S_FLAG:=1/40/
+s/UPDATE_N(R)_RECEIVED/50/
+s/UPDATE_P_FLAG/51/
+s/V(R):=0/61/
+s/V(R):=V(R)+1/62/
+s/V(S):=0/63/
+s/V(S):=N(R)/64/
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/opcodes linux/net/802/pseudo/opcodes
--- v2.1.14/linux/net/802/pseudo/opcodes	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/opcodes	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,72 @@
+   0	NOP
+   1	CLEAR_REMOTE_BUSY
+   2	CONNECT_INDICATION
+   3	CONNECT_CONFIRM
+   4	DATA_INDICATION
+   5	DISCONNECT_INDICATION
+   6	DUMMY_6
+   7	RESET_CONFIRM
+   8	DUMMY_8
+   9	IF_F=1_CLEAR_REMOTE_BUSY
+  10	IF_DATA_FLAG=2_STOP_REJ_TIMER
+  11	SEND_DISC_CMD(P=X)
+  12	SEND_DM_RSP(F=X)
+  13	SEND_FRMR_RSP(F=X)
+  14	RE-SEND_FRMR_RSP(F=0)
+  15	RE-SEND_FRMR_RSP(F=P)
+  16	SEND_I_CMD(P=1)
+  17	RE-SEND_I_CMD(P=1)
+  18	RE-SEND_I_CMD(P=1)_OR_SEND_RR
+  19	SEND_I_XXX(X=0)
+  20	RE-SEND_I_XXX(X=0)
+  21	RE-SEND_I_XXX(X=0)_OR_SEND_RR
+  22	RE-SEND_I_RSP(F=1)
+  23	SEND_REJ_CMD(P=1)
+  24	SEND_REJ_RSP(F=1)
+  25	SEND_REJ_XXX(X=0)
+  26	SEND_RNR_CMD(F=1)
+  27	SEND_RNR_RSP(F=1)
+  28	SEND_RNR_XXX(X=0)
+  29	SET_REMOTE_BUSY
+  30	OPTIONAL_SEND_RNR_XXX(X=0)
+  31	SEND_RR_CMD(P=1)
+  32	SEND_ACKNOWLEDGE_CMD(P=1)
+  33	SEND_RR_RSP(F=1)
+  34	SEND_ACKNOWLEDGE_RSP(F=1)
+  35	SEND_RR_XXX(X=0)
+  36	SEND_ACKNOWLEDGE_XXX(X=0)
+  37	SEND_SABME_CMD(P=X)
+  38	SEND_UA_RSP(F=X)
+  39	S_FLAG:=0
+  40	S_FLAG:=1
+  41	START_P_TIMER
+  42	START_ACK_TIMER
+  43	START_REJ_TIMER
+  44	START_ACK_TIMER_IF_NOT_RUNNING
+  45	STOP_ACK_TIMER
+  46	STOP_P_TIMER
+  47	STOP_REJ_TIMER
+  48	STOP_ALL_TIMERS
+  49	STOP_OTHER_TIMERS
+  50	UPDATE_N(R)_RECEIVED
+  51	UPDATE_P_FLAG
+  52	DATA_FLAG:=2
+  53	DATA_FLAG:=0
+  54	DATA_FLAG:=1
+  55	IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+  56	P_FLAG:=0
+  57	P_FLAG:=P
+  58	REMOTE_BUSY:=0
+  59	RETRY_COUNT:=0
+  60	RETRY_COUNT:=RETRY_COUNT+1
+  61	V(R):=0
+  62	V(R):=V(R)+1
+  63	V(S):=0
+  64	V(S):=N(R)
+  65	F_FLAG:=P
+  66	REPORT_STATUS(FRMR_RECEIVED)
+  67	REPORT_STATUS(FRMR_SENT)
+  68	REPORT_STATUS(REMOTE_BUSY)
+  69	REPORT_STATUS(REMOTE_NOT_BUSY)
+  70	RESET_INDICATION(LOCAL)
+  71	RESET_INDICATION(REMOTE)
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/opcodesnm.h linux/net/802/pseudo/opcodesnm.h
--- v2.1.14/linux/net/802/pseudo/opcodesnm.h	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/opcodesnm.h	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,23 @@
+static char *opcode_names[] = {
+"NOP", "CLEAR_REMOTE_BUSY", "CONNECT_INDICATION", "CONNECT_CONFIRM", "DATA_INDICATION",
+"DISCONNECT_INDICATION", "DUMMY_6", "RESET_CONFIRM", "DUMMY_8",
+"IF_F=1_CLEAR_REMOTE_BUSY", "IF_DATA_FLAG=2_STOP_REJ_TIMER", "SEND_DISC_CMD(P=X)",
+"SEND_DM_RSP(F=X)", "SEND_FRMR_RSP(F=X)", "RE-SEND_FRMR_RSP(F=0)",
+"RE-SEND_FRMR_RSP(F=P)", "SEND_I_CMD(P=1)", "RE-SEND_I_CMD(P=1)",
+"RE-SEND_I_CMD(P=1)_OR_SEND_RR", "SEND_I_XXX(X=0)", "RE-SEND_I_XXX(X=0)",
+"RE-SEND_I_XXX(X=0)_OR_SEND_RR", "RE-SEND_I_RSP(F=1)", "SEND_REJ_CMD(P=1)",
+"SEND_REJ_RSP(F=1)", "SEND_REJ_XXX(X=0)", "SEND_RNR_CMD(F=1)", "SEND_RNR_RSP(F=1)",
+"SEND_RNR_XXX(X=0)", "SET_REMOTE_BUSY", "OPTIONAL_SEND_RNR_XXX(X=0)",
+"SEND_RR_CMD(P=1)", "SEND_ACKNOWLEDGE_CMD(P=1)", "SEND_RR_RSP(F=1)",
+"SEND_ACKNOWLEDGE_RSP(F=1)", "SEND_RR_XXX(X=0)", "SEND_ACKNOWLEDGE_XXX(X=0)",
+"SEND_SABME_CMD(P=X)", "SEND_UA_RSP(F=X)", "S_FLAG:=0", "S_FLAG:=1", "START_P_TIMER",
+"START_ACK_TIMER", "START_REJ_TIMER", "START_ACK_TIMER_IF_NOT_RUNNING",
+"STOP_ACK_TIMER", "STOP_P_TIMER", "STOP_REJ_TIMER", "STOP_ALL_TIMERS",
+"STOP_OTHER_TIMERS", "UPDATE_N(R)_RECEIVED", "UPDATE_P_FLAG", "DATA_FLAG:=2",
+"DATA_FLAG:=0", "DATA_FLAG:=1", "IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1", "P_FLAG:=0",
+"P_FLAG:=P", "REMOTE_BUSY:=0", "RETRY_COUNT:=0", "RETRY_COUNT:=RETRY_COUNT+1",
+"V(R):=0", "V(R):=V(R)+1", "V(S):=0", "V(S):=N(R)", "F_FLAG:=P",
+"REPORT_STATUS(FRMR_RECEIVED)", "REPORT_STATUS(FRMR_SENT)",
+"REPORT_STATUS(REMOTE_BUSY)", "REPORT_STATUS(REMOTE_NOT_BUSY)",
+"RESET_INDICATION(LOCAL)", "RESET_INDICATION(REMOTE)"
+};
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/pseudocode linux/net/802/pseudo/pseudocode
--- v2.1.14/linux/net/802/pseudo/pseudocode	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/pseudocode	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,780 @@
+;============================================================================
+;
+; translate this with 
+; 	cat pseudocode | sed -f act2num |  awk -f compile.awk >pseudocode.h
+;
+; actionname	pseudocode
+;
+;============================================================================
+ADM1		SEND_SABME_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		RETRY_COUNT:=0
+		S_FLAG:=0
+;
+; instructions in ADM2 have been changed:
+; 1. 	P_FLAG:=P is probably wrong in doc...
+; 	I think it should be F_FLAG:=P the way it is in CONN3
+; 2.	CONNECT_RESPONSE has been wired in here,
+;	CONN1 is no longer referenced
+;
+ADM2		F_FLAG:=P
+		SEND_UA_RSP(F=X)
+		V(S):=0
+		V(R):=0
+		RETRY_COUNT:=0
+		P_FLAG:=0
+		REMOTE_BUSY:=0
+		CONNECT_INDICATION
+ADM3		SEND_DM_RSP(F=X)
+ADM4		SEND_DM_RSP(F=X)
+ADM5		NOP
+;============================================================================
+;CONN1		SEND_UA_RSP(F=X)
+;		V(S):=0
+;		V(R):=0
+;		RETRY_COUNT:=0
+;		P_FLAG:=0
+;		REMOTE_BUSY:=0
+CONN2		SEND_DM_RSP(F=X)
+CONN3		F_FLAG:=P
+CONN4		DISCONNECT_INDICATION
+CONN5		NOP
+;============================================================================
+RESWAIT1	SEND_SABME_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		RETRY_COUNT:=0
+RESWAIT2	SEND_UA_RSP(F=X)
+		V(S):=0
+		V(R):=0
+		RETRY_COUNT:=0
+		P_FLAG:=0
+		REMOTE_BUSY:=0
+		RESET_CONFIRM
+RESWAIT3	SEND_DISC_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		RETRY_COUNT:=0
+RESWAIT4	SEND_DM_RSP(F=X)		
+RESWAIT5	DISCONNECT_INDICATION
+RESWAIT6	S_FLAG:=1
+		F_FLAG:=P
+RESWAIT7	SEND_DM_RSP(F=X)
+		DISCONNECT_INDICATION
+RESWAIT8	NOP
+;============================================================================
+RESCHK1		SEND_UA_RSP(F=X)
+		V(S):=0
+		V(R):=0
+		RETRY_COUNT:=0
+		P_FLAG:=0
+		REMOTE_BUSY:=0
+RESCHK2		SEND_DM_RSP(F=X)
+RESCHK3		DISCONNECT_INDICATION
+RESCHK4		F_FLAG:=P
+RESCHK5		SEND_DM_RSP(F=X)
+		DISCONNECT_INDICATION
+RESCHK6		NOP
+;============================================================================
+SETUP1		SEND_UA_RSP(F=X)
+		V(S):=0
+		V(R):=0
+		RETRY_COUNT:=0
+		S_FLAG:=1
+SETUP2		STOP_ACK_TIMER
+		V(S):=0
+		V(R):=0
+		RETRY_COUNT:=0
+		UPDATE_P_FLAG
+		CONNECT_CONFIRM
+		REMOTE_BUSY:=0
+SETUP3		P_FLAG:=0
+		CONNECT_CONFIRM
+		REMOTE_BUSY:=0
+SETUP4		SEND_DM_RSP(F=X)
+		DISCONNECT_INDICATION
+		STOP_ACK_TIMER
+SETUP5		DISCONNECT_INDICATION
+		STOP_ACK_TIMER
+SETUP6		NOP
+SETUP7		SEND_SABME_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+SETUP8		DISCONNECT_INDICATION
+;============================================================================
+RESET1		SEND_UA_RSP(F=X)
+		V(S):=0
+		V(R):=0
+		RETRY_COUNT:=0
+		S_FLAG:=1
+RESET2		STOP_ACK_TIMER
+		V(S):=0
+		V(R):=0
+		RETRY_COUNT:=0
+		UPDATE_P_FLAG
+		RESET_CONFIRM
+		REMOTE_BUSY:=0
+RESET3		P_FLAG:=0
+		RESET_CONFIRM
+		REMOTE_BUSY:=0
+RESET4		SEND_DM_RSP(F=X)
+		DISCONNECT_INDICATION
+		STOP_ACK_TIMER
+RESET5		DISCONNECT_INDICATION
+		STOP_ACK_TIMER
+RESET6		NOP
+RESET7		SEND_SABME_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+RESET8		DISCONNECT_INDICATION
+;============================================================================
+D_CONN1		SEND_DM_RSP(F=X)
+		STOP_ACK_TIMER
+D_CONN2		STOP_ACK_TIMER
+D_CONN3		SEND_UA_RSP(F=X)
+D_CONN4		STOP_ACK_TIMER
+D_CONN5		NOP
+D_CONN6		SEND_DISC_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+D_CONN7		NOP
+;============================================================================
+ERR1		RESET_INDICATION(REMOTE)
+		STOP_ACK_TIMER
+		F_FLAG:=P
+ERR2		SEND_UA_RSP(F=X)
+		DISCONNECT_INDICATION
+		STOP_ACK_TIMER
+ERR3		DISCONNECT_INDICATION
+		STOP_ACK_TIMER
+ERR4		RESET_INDICATION(LOCAL)
+		STOP_ACK_TIMER
+		REPORT_STATUS(FRMR_RECEIVED)
+		S_FLAG:=0
+ERR5		RE-SEND_FRMR_RSP(F=P)
+		START_ACK_TIMER
+ERR6		NOP
+ERR7		RE-SEND_FRMR_RSP(F=0)
+		START_ACK_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+ERR8		S_FLAG:=0
+		RESET_INDICATION(LOCAL)
+;============================================================================
+; the shared actions are common to states NORMAL, BUSY, REJECT,
+; AWAIT, AWAIT_BUSY and AWAIT_REJECT.
+;============================================================================
+SH1		SEND_DISC_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		STOP_OTHER_TIMERS
+		RETRY_COUNT:=0
+SH2		SEND_SABME_CMD(P=X)
+		P_FLAG:=P
+		START_ACK_TIMER
+		STOP_OTHER_TIMERS
+		RETRY_COUNT:=0
+		S_FLAG:=0
+SH3		RESET_INDICATION(REMOTE)
+		F_FLAG:=P
+		STOP_ALL_TIMERS
+SH4		SEND_UA_RSP(F=X)
+		DISCONNECT_INDICATION
+		STOP_ALL_TIMERS
+SH5		STOP_ALL_TIMERS
+		RESET_INDICATION(LOCAL)
+		REPORT_STATUS(FRMR_RECEIVED)
+		S_FLAG:=0
+SH6		DISCONNECT_INDICATION
+		STOP_ALL_TIMERS
+SH7		SEND_FRMR_RSP(F=X)
+		REPORT_STATUS(FRMR_SENT)
+		START_ACK_TIMER
+		STOP_OTHER_TIMERS
+		RETRY_COUNT:=0
+SH8		SEND_FRMR_RSP(F=0)
+		REPORT_STATUS(FRMR_SENT)
+		START_ACK_TIMER
+		STOP_OTHER_TIMERS
+		RETRY_COUNT:=0
+SH9		SEND_FRMR_RSP(F=0)
+		REPORT_STATUS(FRMR_SENT)
+		START_ACK_TIMER
+		STOP_OTHER_TIMERS
+		RETRY_COUNT:=0
+SH10		SEND_FRMR_RSP(F=X)
+		REPORT_STATUS(FRMR_SENT)
+		START_ACK_TIMER
+		STOP_OTHER_TIMERS
+		RETRY_COUNT:=0
+SH11		STOP_ALL_TIMERS
+		RESET_INDICATION(LOCAL)
+		S_FLAG:=0
+;============================================================================
+NORMAL1		SEND_I_CMD(P=1)
+		START_P_TIMER
+		START_ACK_TIMER_IF_NOT_RUNNING
+;		SEND_I_XXX(X=0)
+;		START_ACK_TIMER_IF_NOT_RUNNING
+NORMAL2		SEND_I_XXX(X=0)
+		START_ACK_TIMER_IF_NOT_RUNNING
+NORMAL3		SEND_RNR_CMD(F=1)
+		START_P_TIMER
+		DATA_FLAG:=0
+;		SEND_RNR_XXX(X=0)
+;		DATA_FLAG:=0
+NORMAL4		SEND_RNR_XXX(X=0)
+		DATA_FLAG:=0
+NORMAL5		SEND_REJ_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		UPDATE_P_FLAG
+		START_REJ_TIMER
+		IF_F=1_CLEAR_REMOTE_BUSY
+;		SEND_REJ_CMD(P=1)
+;		UPDATE_N(R)_RECEIVED
+;		START_P_TIMER
+;		START_REJ_TIMER
+;		IF_F=1_CLEAR_REMOTE_BUSY
+NORMAL6		SEND_REJ_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		START_REJ_TIMER
+NORMAL7		SEND_REJ_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		START_REJ_TIMER
+;
+; the order of opcodes in NORMAL8 is changed.
+; the transition table will execute NORMAL8A for incomming pdus
+; with p/f 1, pdus with pf 0 are treated in NORMAL8B.
+;   
+NORMAL8A	V(R):=V(R)+1
+		SEND_ACKNOWLEDGE_CMD(P=1)
+		START_P_TIMER
+		UPDATE_N(R)_RECEIVED
+		IF_F=1_CLEAR_REMOTE_BUSY
+		DATA_INDICATION
+;
+NORMAL8B	V(R):=V(R)+1
+		UPDATE_P_FLAG
+		SEND_ACKNOWLEDGE_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		IF_F=1_CLEAR_REMOTE_BUSY
+		DATA_INDICATION
+;
+; the order of opcodes in NORMAL9 is changed 
+NORMAL9		V(R):=V(R)+1
+		SEND_ACKNOWLEDGE_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		DATA_INDICATION
+;
+; the order of opcodes in NORMAL10 is changed 
+NORMAL10	V(R):=V(R)+1
+		SEND_ACKNOWLEDGE_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		DATA_INDICATION
+NORMAL11	UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+NORMAL12	SEND_ACKNOWLEDGE_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+NORMAL13	UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+NORMAL14	SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+NORMAL15	V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		UPDATE_P_FLAG
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+;		V(S):=N(R)
+;		UPDATE_N(R)_RECEIVED
+;		START_P_TIMER
+;		RE-SEND_I_CMD(P=1)
+;		CLEAR_REMOTE_BUSY
+NORMAL16	V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+NORMAL17	V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		RE-SEND_I_RSP(F=1)
+		CLEAR_REMOTE_BUSY
+NORMAL18	SEND_RR_CMD(P=1)
+		START_P_TIMER
+NORMAL19	P_FLAG:=0
+;		SEND_RR_CMD(P=1)
+;		START_P_TIMER
+;		RETRY_COUNT:=RETRY_COUNT+1
+NORMAL20	SEND_RR_CMD(P=1)
+		START_P_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+BUSY1		SEND_I_CMD(P=1)
+		START_P_TIMER
+		START_ACK_TIMER_IF_NOT_RUNNING
+;		SEND_I_XXX(X=0)
+;		START_ACK_TIMER_IF_NOT_RUNNING
+BUSY2		SEND_I_XXX(X=0)
+		START_ACK_TIMER_IF_NOT_RUNNING
+BUSY3		SEND_REJ_CMD(P=1)
+		START_REJ_TIMER
+		START_P_TIMER
+;		SEND_REJ_XXX(X=0)
+;		START_REJ_TIMER
+BUSY4		SEND_REJ_XXX(X=0)
+		START_REJ_TIMER
+BUSY5		SEND_RR_CMD(P=1)
+		START_P_TIMER
+		SEND_RR_XXX(X=0)
+BUSY6		SEND_RR_XXX(X=0)
+BUSY7		SEND_RR_CMD(P=1)
+		START_P_TIMER
+		SEND_RR_XXX(X=0)
+BUSY8		SEND_RR_XXX(X=0)
+BUSY9		OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+		IF_F=1_CLEAR_REMOTE_BUSY
+;		SEND_RNR_CMD(P=1)
+;		START_P_TIMER
+;		UPDATE_N(R)_RECEIVED
+;		IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+;		IF_F=1_CLEAR_REMOTE_BUSY
+BUSY10		OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+BUSY11		SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		IF_DATA_FLAG_=0_THEN_DATA_FLAG:=1
+BUSY12		SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		IF_DATA_FLAG=2_STOP_REJ_TIMER
+		DATA_FLAG:=1
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		SEND_RNR_RSP(F=1)
+;		UPDATE_N(R)_RECEIVED
+;		IF_DATA_FLAG=2_STOP_REJ_TIMER
+;		DATA_FLAG:=0
+BUSY13		OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		IF_DATA_FLAG=2_STOP_REJ_TIMER
+		DATA_FLAG:=1
+		IF_F=1_CLEAR_REMOTE_BUSY
+;		SEND_RNR_CMD(F=1)
+;		START_P_TIMER
+;		UPDATE_N(R)_RECEIVED
+;		IF_DATA_FLAG=2_STOP_REJ_TIMER
+;		DATA_FLAG:=1
+;		IF_F=1_CLEAR_REMOTE_BUSY
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		SEND_RNR_CMD(F=1)
+;		START_P_TIMER
+;		UPDATE_N(R)_RECEIVED
+;		IF_DATA_FLAG=2_STOP_REJ_TIMER
+;		DATA_FLAG:=0
+;		IF_F=1_CLEAR_REMOTE_BUSY
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		UPDATE_P_FLAG
+;		OPTIONAL_SEND_RNR_XXX(X=0)
+;		UPDATE_N(R)_RECEIVED
+;		IF_DATA_FLAG=2_STOP_REJ_TIMER
+;		DATA_FLAG:=0
+;		IF_F=1_CLEAR_REMOTE_BUSY
+BUSY14		OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		IF_DATA_FLAG=2_STOP_REJ_TIMER
+		DATA_FLAG:=1
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		OPTIONAL_SEND_RNR_XXX(X=0)
+;		UPDATE_N(R)_RECEIVED
+;		IF_DATA_FLAG=2_STOP_REJ_TIMER
+;		DATA_FLAG:=0
+BUSY15		UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+BUSY16		SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+BUSY17		UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+BUSY18		SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+BUSY19		V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		UPDATE_P_FLAG
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+;		V(S):=N(R)
+;		UPDATE_N(R)_RECEIVED
+;		RE-SEND_I_CMD(P=1)
+;		CLEAR_REMOTE_BUSY
+BUSY20		V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+BUSY21		V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		SEND_RNR_RSP(F=1)
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+BUSY22		SEND_RNR_CMD(F=1)
+		START_P_TIMER
+BUSY23		P_FLAG:=0
+;		SEND_RNR_CMD(F=1)
+;		START_P_TIMER
+;		RETRY_COUNT:=RETRY_COUNT+1
+BUSY24		SEND_RNR_CMD(F=1)
+		START_P_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+BUSY25		DATA_FLAG:=1
+;		SEND_RNR_CMD(F=1)
+;		START_P_TIMER
+;		RETRY_COUNT:=RETRY_COUNT+1
+;		DATA_FLAG:=1
+BUSY26		DATA_FLAG:=1
+;============================================================================
+REJECT1		SEND_I_CMD(P=1)
+		START_P_TIMER
+		START_ACK_TIMER_IF_NOT_RUNNING
+;		SEND_I_XXX(X=0)
+;		START_ACK_TIMER_IF_NOT_RUNNING
+REJECT2		SEND_I_XXX(X=0)
+		START_ACK_TIMER_IF_NOT_RUNNING
+REJECT3		SEND_RNR_CMD(F=1)
+		START_P_TIMER
+		DATA_FLAG:=2
+;		SEND_RNR_XXX(X=0)
+;		DATA_FLAG:=2
+REJECT4		SEND_RNR_XXX(X=0)
+		DATA_FLAG:=2
+REJECT5		UPDATE_N(R)_RECEIVED
+		UPDATE_P_FLAG
+		IF_F=1_CLEAR_REMOTE_BUSY
+REJECT6		SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+;
+; order of opcodes in REJECT7 is changed 
+REJECT7		V(R):=V(R)+1
+		SEND_ACKNOWLEDGE_CMD(P=1)
+		START_P_TIMER
+		UPDATE_N(R)_RECEIVED
+		IF_F=1_CLEAR_REMOTE_BUSY
+		STOP_REJ_TIMER
+		DATA_INDICATION
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		UPDATE_P_FLAG
+;		SEND_ACKNOWLEDGE_XXX(X=0)
+;		UPDATE_N(R)_RECEIVED
+;		IF_F=1_CLEAR_REMOTE_BUSY
+;		STOP_REJ_TIMER
+;
+; order of opcodes in REJECT8 is changed 
+REJECT8		V(R):=V(R)+1
+		SEND_ACKNOWLEDGE_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		STOP_REJ_TIMER
+		DATA_INDICATION
+;
+; order of opcodes in REJECT9 is changed 
+REJECT9		V(R):=V(R)+1
+		SEND_ACKNOWLEDGE_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		STOP_REJ_TIMER
+		DATA_INDICATION
+REJECT10	UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+REJECT11	SEND_ACKNOWLEDGE_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+REJECT12	UPDATE_P_FLAG
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+REJECT13	SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+REJECT14	V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		UPDATE_P_FLAG
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+;		V(S):=N(R)
+;		UPDATE_N(R)_RECEIVED
+;		RE-SEND_I_CMD(P=1)
+;		START_P_TIMER
+;		CLEAR_REMOTE_BUSY
+REJECT15	V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+REJECT16	V(S):=N(R)
+		UPDATE_N(R)_RECEIVED
+		RE-SEND_I_RSP(F=1)
+		CLEAR_REMOTE_BUSY
+REJECT17	SEND_RR_CMD(P=1)
+		START_P_TIMER
+REJECT18	SEND_REJ_CMD(P=1)
+		START_P_TIMER
+		START_REJ_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+REJECT19	P_FLAG:=0
+;		SEND_RR_CMD(P=1)
+;		START_P_TIMER
+;		START_REJ_TIMER
+;		RETRY_COUNT:=RETRY_COUNT+1
+REJECT20	SEND_RR_CMD(P=1)
+		START_P_TIMER
+		START_REJ_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+AWAIT1		SEND_RNR_XXX(X=0)
+		DATA_FLAG:=0
+AWAIT2		SEND_REJ_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		RE-SEND_I_XXX(X=0)
+		START_REJ_TIMER
+		CLEAR_REMOTE_BUSY
+;		SEND_REJ_CMD(P=1)
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		RE-SEND_I_XXX(X=0)
+;		START_P_TIMER
+;		START_REJ_TIMER
+;		CLEAR_REMOTE_BUSY
+AWAIT3		SEND_REJ_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		START_REJ_TIMER
+AWAIT4		SEND_REJ_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		START_REJ_TIMER
+;
+; order of opcode in AWAIT5 changed
+AWAIT5		V(R):=V(R)+1
+		UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		RE-SEND_I_CMD(P=1)_OR_SEND_RR
+		START_P_TIMER
+		CLEAR_REMOTE_BUSY
+		DATA_INDICATION
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		STOP_P_TIMER
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		RE-SEND_I_XXX(X=0)_OR_SEND_RR
+;		CLEAR_REMOTE_BUSY
+;
+; order of opcode in AWAIT6 changed
+AWAIT6		V(R):=V(R)+1
+		SEND_RR_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		DATA_INDICATION
+;
+; order of opcode in AWAIT7 changed
+AWAIT7		V(R):=V(R)+1
+		SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		DATA_INDICATION
+AWAIT8		UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		RE-SEND_I_CMD(P=1)
+;		START_P_TIMER
+;		CLEAR_REMOTE_BUSY
+AWAIT9		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+AWAIT10		SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+AWAIT11		UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		SET_REMOTE_BUSY
+AWAIT12		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+AWAIT13		SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+AWAIT14		SEND_RR_CMD(P=1)
+		START_P_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+AWAIT_BUSY1	SEND_REJ_XXX(X=0)
+		START_REJ_TIMER		
+AWAIT_BUSY2	SEND_RR_XXX(X=0)
+AWAIT_BUSY3	SEND_RR_XXX(X=0)
+AWAIT_BUSY4	OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		DATA_FLAG:=1
+		CLEAR_REMOTE_BUSY
+		RE-SEND_I_XXX(X=0)
+;		SEND_RNR_CMD(F=1)
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		START_P_TIMER
+;		DATA_FLAG:=1
+;		CLEAR_REMOTE_BUSY
+;		RE-SEND_I_XXX(X=0)
+AWAIT_BUSY5	OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		DATA_FLAG:=1
+AWAIT_BUSY6	SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		DATA_FLAG:=1
+AWAIT_BUSY7	OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		DATA_FLAG:=1
+		STOP_P_TIMER
+		CLEAR_REMOTE_BUSY
+		RE-SEND_I_XXX(X=0)
+;		SEND_RNR_CMD(F=1)
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		START_P_TIMER
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		DATA_FLAG:=0
+;		CLEAR_REMOTE_BUSY
+;		RE-SEND_I_XXX(X=0)
+;		OPTIONAL_SEND_RNR_XXX(X=0)
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		STOP_P_TIMER
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		DATA_FLAG:=0
+;		CLEAR_REMOTE_BUSY
+;		RE-SEND_I_XXX(X=0)
+AWAIT_BUSY8	OPTIONAL_SEND_RNR_XXX(X=0)
+		UPDATE_N(R)_RECEIVED
+		DATA_FLAG:=1
+;		OPTIONAL_SEND_RNR_XXX(X=0)
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		UPDATE_N(R)_RECEIVED
+;		DATA_FLAG:=0
+AWAIT_BUSY9	SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		DATA_FLAG:=1
+;		SEND_RNR_RSP(F=1)
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		UPDATE_N(R)_RECEIVED
+;		DATA_FLAG:=0
+AWAIT_BUSY10	UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		RE-SEND_I_CMD(P=1)
+;		START_P_TIMER
+;		CLEAR_REMOTE_BUSY
+AWAIT_BUSY11	UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+AWAIT_BUSY12	SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+AWAIT_BUSY13	UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		SET_REMOTE_BUSY
+AWAIT_BUSY14	UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+AWAIT_BUSY15	SEND_RNR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+AWAIT_BUSY16	SEND_RNR_CMD(F=1)
+		START_P_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+AWAIT_REJECT1	SEND_RNR_XXX(X=0)
+		DATA_FLAG:=2
+AWAIT_REJECT2	UPDATE_N(R)_RECEIVED
+AWAIT_REJECT3	SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+;
+; order of opcodes in AWAIT_REJECT4 changed
+AWAIT_REJECT4	V(R):=V(R)+1
+		UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		RE-SEND_I_CMD(P=1)_OR_SEND_RR
+		START_P_TIMER
+		STOP_REJ_TIMER
+		CLEAR_REMOTE_BUSY
+		DATA_INDICATION
+;		V(R):=V(R)+1
+;		DATA_INDICATION
+;		STOP_P_TIMER
+;		STOP_REJ_TIMER
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		RE-SEND_I_CMD(P=1)_OR_SEND_RR
+;		CLEAR_REMOTE_BUSY
+;
+; order of opcodes in AWAIT_REJECT5 changed
+AWAIT_REJECT5	V(R):=V(R)+1
+		SEND_RR_XXX(X=0)
+		STOP_REJ_TIMER
+		UPDATE_N(R)_RECEIVED
+		DATA_INDICATION
+;
+; order of opcodes in AWAIT_REJECT6 changed
+AWAIT_REJECT6	V(R):=V(R)+1
+		SEND_RR_RSP(F=1)
+		STOP_REJ_TIMER
+		UPDATE_N(R)_RECEIVED
+		DATA_INDICATION
+AWAIT_REJECT7	UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		RE-SEND_I_XXX(X=0)
+		CLEAR_REMOTE_BUSY
+;		UPDATE_N(R)_RECEIVED
+;		V(S):=N(R)
+;		RE-SEND_I_CMD(P=1)
+;		START_P_TIMER
+;		CLEAR_REMOTE_BUSY
+AWAIT_REJECT8	UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+AWAIT_REJECT9	SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		CLEAR_REMOTE_BUSY
+AWAIT_REJECT10	UPDATE_N(R)_RECEIVED
+		V(S):=N(R)
+		STOP_P_TIMER
+		SET_REMOTE_BUSY
+AWAIT_REJECT11	UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+AWAIT_REJECT12	SEND_RR_RSP(F=1)
+		UPDATE_N(R)_RECEIVED
+		SET_REMOTE_BUSY
+AWAIT_REJECT13	SEND_REJ_CMD(P=1)
+		START_P_TIMER
+		RETRY_COUNT:=RETRY_COUNT+1
+;============================================================================
+
diff -u --recursive --new-file v2.1.14/linux/net/802/pseudo/pseudocode.h linux/net/802/pseudo/pseudocode.h
--- v2.1.14/linux/net/802/pseudo/pseudocode.h	Thu Jan  1 02:00:00 1970
+++ linux/net/802/pseudo/pseudocode.h	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,287 @@
+
+/* this file generated on Thu Oct 24 11:42:35 GMT 1996  */
+
+static char pseudo_code [ ] = { 
+	   0,	  37,	  57,	  42,	  59,	  39,	   0,	  65,	  38,
+	  63,	  61,	  59,	  56,	  58,	   2,	   0,	  12,	   0,
+	  12,	   0,	   0,	   0,	  12,	   0,	  65,	   0,	   5,
+	   0,	   0,	   0,	  37,	  57,	  42,	  59,	   0,	  38,
+	  63,	  61,	  59,	  56,	  58,	   7,	   0,	  11,	  57,
+	  42,	  59,	   0,	  12,	   0,	   5,	   0,	  40,	  65,
+	   0,	  12,	   5,	   0,	   0,	   0,	  38,	  63,	  61,
+	  59,	  56,	  58,	   0,	  12,	   0,	   5,	   0,	  65,
+	   0,	  12,	   5,	   0,	   0,	   0,	  38,	  63,	  61,
+	  59,	  40,	   0,	  45,	  63,	  61,	  59,	  51,	   3,
+	  58,	   0,	  56,	   3,	  58,	   0,	  12,	   5,	  45,
+	   0,	   5,	  45,	   0,	   0,	   0,	  37,	  57,	  42,
+	  60,	   0,	   5,	   0,	  38,	  63,	  61,	  59,	  40,
+	   0,	  45,	  63,	  61,	  59,	  51,	   7,	  58,	   0,
+	  56,	   7,	  58,	   0,	  12,	   5,	  45,	   0,	   5,
+	  45,	   0,	   0,	   0,	  37,	  57,	  42,	  60,	   0,
+	   5,	   0,	  12,	  45,	   0,	  45,	   0,	  38,	   0,
+	  45,	   0,	   0,	   0,	  11,	  57,	  42,	  60,	   0,
+	   0,	   0,	  71,	  45,	  65,	   0,	  38,	   5,	  45,
+	   0,	   5,	  45,	   0,	  70,	  45,	  66,	  39,	   0,
+	  15,	  42,	   0,	   0,	   0,	  14,	  42,	  60,	   0,
+	  39,	  70,	   0,	  11,	  57,	  42,	  49,	  59,	   0,
+	  37,	  57,	  42,	  49,	  59,	  39,	   0,	  71,	  65,
+	  48,	   0,	  38,	   5,	  48,	   0,	  48,	  70,	  66,
+	  39,	   0,	   5,	  48,	   0,	  13,	  67,	  42,	  49,
+	  59,	   0,	   0,	  67,	  42,	  49,	  59,	   0,	   0,
+	  67,	  42,	  49,	  59,	   0,	  13,	  67,	  42,	  49,
+	  59,	   0,	  48,	  70,	  39,	   0,	  16,	  41,	  44,
+	   0,	  19,	  44,	   0,	  26,	  41,	  53,	   0,	  28,
+	  53,	   0,	  25,	  50,	  51,	  43,	   9,	   0,	  25,
+	  50,	  43,	   0,	  24,	  50,	  43,	   0,	  62,	  32,
+	  41,	  50,	   9,	   4,	   0,	  62,	  51,	  36,	  50,
+	   9,	   4,	   0,	  62,	  36,	  50,	   4,	   0,	  62,
+	  34,	  50,	   4,	   0,	  51,	  50,	   1,	   0,	  34,
+	  50,	   1,	   0,	  51,	  50,	  29,	   0,	  33,	  50,
+	  29,	   0,	  64,	  50,	  51,	  20,	   1,	   0,	  64,
+	  50,	  20,	   1,	   0,	  64,	  50,	  22,	   1,	   0,
+	  31,	  41,	   0,	  56,	   0,	  31,	  41,	  60,	   0,
+	  16,	  41,	  44,	   0,	  19,	  44,	   0,	  23,	  43,
+	  41,	   0,	  25,	  43,	   0,	  31,	  41,	  35,	   0,
+	  35,	   0,	  31,	  41,	  35,	   0,	  35,	   0,	  30,
+	  51,	  50,	  55,	   9,	   0,	  30,	  50,	  55,	   0,
+	  27,	  50,	  55,	   0,	  27,	  50,	  10,	  54,	   0,
+	  30,	  51,	  50,	  10,	  54,	   9,	   0,	  30,	  50,
+	  10,	  54,	   0,	  51,	  50,	   1,	   0,	  27,	  50,
+	   1,	   0,	  51,	  50,	  29,	   0,	  27,	  50,	  29,
+	   0,	  64,	  50,	  51,	  20,	   1,	   0,	  64,	  50,
+	  20,	   1,	   0,	  64,	  50,	  27,	  20,	   1,	   0,
+	  26,	  41,	   0,	  56,	   0,	  26,	  41,	  60,	   0,
+	  54,	   0,	  54,	   0,	  16,	  41,	  44,	   0,	  19,
+	  44,	   0,	  26,	  41,	  52,	   0,	  28,	  52,	   0,
+	  50,	  51,	   9,	   0,	  33,	  50,	   0,	  62,	  32,
+	  41,	  50,	   9,	  47,	   4,	   0,	  62,	  36,	  50,
+	  47,	   4,	   0,	  62,	  34,	  50,	  47,	   4,	   0,
+	  51,	  50,	   1,	   0,	  34,	  50,	   1,	   0,	  51,
+	  50,	  29,	   0,	  33,	  50,	  29,	   0,	  64,	  50,
+	  51,	  20,	   1,	   0,	  64,	  50,	  20,	   1,	   0,
+	  64,	  50,	  22,	   1,	   0,	  31,	  41,	   0,	  23,
+	  41,	  43,	  60,	   0,	  56,	   0,	  31,	  41,	  43,
+	  60,	   0,	  28,	  53,	   0,	  25,	  50,	  64,	  46,
+	  20,	  43,	   1,	   0,	  25,	  50,	  43,	   0,	  24,
+	  50,	  43,	   0,	  62,	  50,	  64,	  18,	  41,	   1,
+	   4,	   0,	  62,	  35,	  50,	   4,	   0,	  62,	  33,
+	  50,	   4,	   0,	  50,	  64,	  46,	  20,	   1,	   0,
+	  50,	   1,	   0,	  33,	  50,	   1,	   0,	  50,	  64,
+	  46,	  29,	   0,	  50,	  29,	   0,	  33,	  50,	  29,
+	   0,	  31,	  41,	  60,	   0,	  25,	  43,	   0,	  35,
+	   0,	  35,	   0,	  30,	  50,	  64,	  46,	  54,	   1,
+	  20,	   0,	  30,	  50,	  54,	   0,	  27,	  50,	  54,
+	   0,	  30,	  50,	  64,	  54,	  46,	   1,	  20,	   0,
+	  30,	  50,	  54,	   0,	  27,	  50,	  54,	   0,	  50,
+	  64,	  46,	  20,	   1,	   0,	  50,	   1,	   0,	  27,
+	  50,	   1,	   0,	  50,	  64,	  46,	  29,	   0,	  50,
+	  29,	   0,	  27,	  50,	  29,	   0,	  26,	  41,	  60,
+	   0,	  28,	  52,	   0,	  50,	   0,	  33,	  50,	   0,
+	  62,	  50,	  64,	  18,	  41,	  47,	   1,	   4,	   0,
+	  62,	  35,	  47,	  50,	   4,	   0,	  62,	  33,	  47,
+	  50,	   4,	   0,	  50,	  64,	  46,	  20,	   1,	   0,
+	  50,	   1,	   0,	  33,	  50,	   1,	   0,	  50,	  64,
+	  46,	  29,	   0,	  50,	  29,	   0,	  33,	  50,	  29,
+	   0,	  23,	  41,	  60,	  0
+};
+
+static short int pseudo_code_idx [ ] ={
+	   0,	   1,	   7,	  16,	  18,	  20,	  22,	  24,	  26,
+	  28,	  30,	  35,	  43,	  48,	  50,	  52,	  55,	  58,
+	  60,	  67,	  69,	  71,	  73,	  76,	  78,	  84,	  92,
+	  96,	 100,	 103,	 105,	 110,	 112,	 118,	 126,	 130,
+	 134,	 137,	 139,	 144,	 146,	 149,	 151,	 153,	 155,
+	 157,	 162,	 164,	 168,	 172,	 175,	 180,	 183,	 185,
+	 189,	 192,	 198,	 205,	 209,	 213,	 218,	 221,	 227,
+	 233,	 239,	 245,	 249,	 253,	 256,	 260,	 263,	 269,
+	 273,	 277,	 284,	 291,	 296,	 301,	 305,	 309,	 313,
+	 317,	 323,	 328,	 333,	 336,	 338,	 342,	 346,	 349,
+	 353,	 356,	 360,	 362,	 366,	 368,	 374,	 378,	 382,
+	 387,	 394,	 399,	 403,	 407,	 411,	 415,	 421,	 426,
+	 432,	 435,	 437,	 441,	 443,	 445,	 449,	 452,	 456,
+	 459,	 463,	 466,	 474,	 480,	 486,	 490,	 494,	 498,
+	 502,	 508,	 513,	 518,	 521,	 526,	 528,	 533,	 536,
+	 544,	 548,	 552,	 560,	 565,	 570,	 576,	 579,	 583,
+	 588,	 591,	 595,	 599,	 602,	 604,	 606,	 614,	 618,
+	 622,	 630,	 634,	 638,	 644,	 647,	 651,	 656,	 659,
+	 663,	 667,	 670,	 672,	 675,	 684,	 690,	 696,	 702,
+	 705,	 709,	 714,	 717,	 721,	  0
+};
+
+#define NOP        	   0 
+#define ADM1       	   1 
+#define ADM2       	   2 
+#define ADM3       	   3 
+#define ADM4       	   4 
+#define ADM5       	   5 
+#define CONN2      	   6 
+#define CONN3      	   7 
+#define CONN4      	   8 
+#define CONN5      	   9 
+#define RESWAIT1   	  10 
+#define RESWAIT2   	  11 
+#define RESWAIT3   	  12 
+#define RESWAIT4   	  13 
+#define RESWAIT5   	  14 
+#define RESWAIT6   	  15 
+#define RESWAIT7   	  16 
+#define RESWAIT8   	  17 
+#define RESCHK1    	  18 
+#define RESCHK2    	  19 
+#define RESCHK3    	  20 
+#define RESCHK4    	  21 
+#define RESCHK5    	  22 
+#define RESCHK6    	  23 
+#define SETUP1     	  24 
+#define SETUP2     	  25 
+#define SETUP3     	  26 
+#define SETUP4     	  27 
+#define SETUP5     	  28 
+#define SETUP6     	  29 
+#define SETUP7     	  30 
+#define SETUP8     	  31 
+#define RESET1     	  32 
+#define RESET2     	  33 
+#define RESET3     	  34 
+#define RESET4     	  35 
+#define RESET5     	  36 
+#define RESET6     	  37 
+#define RESET7     	  38 
+#define RESET8     	  39 
+#define D_CONN1    	  40 
+#define D_CONN2    	  41 
+#define D_CONN3    	  42 
+#define D_CONN4    	  43 
+#define D_CONN5    	  44 
+#define D_CONN6    	  45 
+#define D_CONN7    	  46 
+#define ERR1       	  47 
+#define ERR2       	  48 
+#define ERR3       	  49 
+#define ERR4       	  50 
+#define ERR5       	  51 
+#define ERR6       	  52 
+#define ERR7       	  53 
+#define ERR8       	  54 
+#define SH1        	  55 
+#define SH2        	  56 
+#define SH3        	  57 
+#define SH4        	  58 
+#define SH5        	  59 
+#define SH6        	  60 
+#define SH7        	  61 
+#define SH8        	  62 
+#define SH9        	  63 
+#define SH10       	  64 
+#define SH11       	  65 
+#define NORMAL1    	  66 
+#define NORMAL2    	  67 
+#define NORMAL3    	  68 
+#define NORMAL4    	  69 
+#define NORMAL5    	  70 
+#define NORMAL6    	  71 
+#define NORMAL7    	  72 
+#define NORMAL8A   	  73 
+#define NORMAL8B   	  74 
+#define NORMAL9    	  75 
+#define NORMAL10   	  76 
+#define NORMAL11   	  77 
+#define NORMAL12   	  78 
+#define NORMAL13   	  79 
+#define NORMAL14   	  80 
+#define NORMAL15   	  81 
+#define NORMAL16   	  82 
+#define NORMAL17   	  83 
+#define NORMAL18   	  84 
+#define NORMAL19   	  85 
+#define NORMAL20   	  86 
+#define BUSY1      	  87 
+#define BUSY2      	  88 
+#define BUSY3      	  89 
+#define BUSY4      	  90 
+#define BUSY5      	  91 
+#define BUSY6      	  92 
+#define BUSY7      	  93 
+#define BUSY8      	  94 
+#define BUSY9      	  95 
+#define BUSY10     	  96 
+#define BUSY11     	  97 
+#define BUSY12     	  98 
+#define BUSY13     	  99 
+#define BUSY14     	 100 
+#define BUSY15     	 101 
+#define BUSY16     	 102 
+#define BUSY17     	 103 
+#define BUSY18     	 104 
+#define BUSY19     	 105 
+#define BUSY20     	 106 
+#define BUSY21     	 107 
+#define BUSY22     	 108 
+#define BUSY23     	 109 
+#define BUSY24     	 110 
+#define BUSY25     	 111 
+#define BUSY26     	 112 
+#define REJECT1    	 113 
+#define REJECT2    	 114 
+#define REJECT3    	 115 
+#define REJECT4    	 116 
+#define REJECT5    	 117 
+#define REJECT6    	 118 
+#define REJECT7    	 119 
+#define REJECT8    	 120 
+#define REJECT9    	 121 
+#define REJECT10   	 122 
+#define REJECT11   	 123 
+#define REJECT12   	 124 
+#define REJECT13   	 125 
+#define REJECT14   	 126 
+#define REJECT15   	 127 
+#define REJECT16   	 128 
+#define REJECT17   	 129 
+#define REJECT18   	 130 
+#define REJECT19   	 131 
+#define REJECT20   	 132 
+#define AWAIT1     	 133 
+#define AWAIT2     	 134 
+#define AWAIT3     	 135 
+#define AWAIT4     	 136 
+#define AWAIT5     	 137 
+#define AWAIT6     	 138 
+#define AWAIT7     	 139 
+#define AWAIT8     	 140 
+#define AWAIT9     	 141 
+#define AWAIT10    	 142 
+#define AWAIT11    	 143 
+#define AWAIT12    	 144 
+#define AWAIT13    	 145 
+#define AWAIT14    	 146 
+#define AWAIT_BUSY1 	 147 
+#define AWAIT_BUSY2 	 148 
+#define AWAIT_BUSY3 	 149 
+#define AWAIT_BUSY4 	 150 
+#define AWAIT_BUSY5 	 151 
+#define AWAIT_BUSY6 	 152 
+#define AWAIT_BUSY7 	 153 
+#define AWAIT_BUSY8 	 154 
+#define AWAIT_BUSY9 	 155 
+#define AWAIT_BUSY10 	 156 
+#define AWAIT_BUSY11 	 157 
+#define AWAIT_BUSY12 	 158 
+#define AWAIT_BUSY13 	 159 
+#define AWAIT_BUSY14 	 160 
+#define AWAIT_BUSY15 	 161 
+#define AWAIT_BUSY16 	 162 
+#define AWAIT_REJECT1 	 163 
+#define AWAIT_REJECT2 	 164 
+#define AWAIT_REJECT3 	 165 
+#define AWAIT_REJECT4 	 166 
+#define AWAIT_REJECT5 	 167 
+#define AWAIT_REJECT6 	 168 
+#define AWAIT_REJECT7 	 169 
+#define AWAIT_REJECT8 	 170 
+#define AWAIT_REJECT9 	 171 
+#define AWAIT_REJECT10 	 172 
+#define AWAIT_REJECT11 	 173 
+#define AWAIT_REJECT12 	 174 
+#define AWAIT_REJECT13 	 175 
+
diff -u --recursive --new-file v2.1.14/linux/net/802/tr.c linux/net/802/tr.c
--- v2.1.14/linux/net/802/tr.c	Tue Oct 29 19:58:48 1996
+++ linux/net/802/tr.c	Thu Dec 12 16:54:22 1996
@@ -68,18 +68,18 @@
 
 }
 	
-int tr_rebuild_header(void *buff, struct device *dev, unsigned long dest,
-							 struct sk_buff *skb) {
+int tr_rebuild_header(struct sk_buff *skb) {
 
-	struct trh_hdr *trh=(struct trh_hdr *)buff;
-	struct trllc *trllc=(struct trllc *)(buff+sizeof(struct trh_hdr));
+	struct trh_hdr *trh=(struct trh_hdr *)skb->data;
+	struct trllc *trllc=(struct trllc *)(skb->data+sizeof(struct trh_hdr));
+	struct device *dev = skb->dev;
 
 	if(trllc->ethertype != htons(ETH_P_IP)) {
 		printk("tr_rebuild_header: Don't know how to resolve type %04X addresses ?\n",(unsigned int)htons(	trllc->ethertype));
 		return 0;
 	}
 
-	if(arp_find(trh->daddr, dest, dev, dev->pa_addr, skb)) {
+	if(arp_find(trh->daddr, skb)) {
 			return 1;
 	}
 	else {	
diff -u --recursive --new-file v2.1.14/linux/net/802/transit/Makefile linux/net/802/transit/Makefile
--- v2.1.14/linux/net/802/transit/Makefile	Thu Jan  1 02:00:00 1970
+++ linux/net/802/transit/Makefile	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,13 @@
+all:	pdutr.h timertr.h
+
+pdutr.h: pdutr.pre compile.awk			
+	awk -f ./compile.awk pdutr.pre > pdutr.h
+
+timertr.h: timertr.pre compile.awk
+	awk -f ./compile.awk timertr.pre > timertr.h
+
+clean:
+	touch pdutr.h timertr.h
+	rm pdutr.h timertr.h
+
+
diff -u --recursive --new-file v2.1.14/linux/net/802/transit/compile.awk linux/net/802/transit/compile.awk
--- v2.1.14/linux/net/802/transit/compile.awk	Thu Jan  1 02:00:00 1970
+++ linux/net/802/transit/compile.awk	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,81 @@
+# to run: awk -f transit.awk transit.p0
+#
+BEGIN { "date" | getline
+	enable_index = 1
+	today = $0
+	printf("\n/* this file was generated on %s  */\n", today )
+	not_firstone = 0	# flag to avoid empty entry in 1st table
+	fpe = 0			# entry tbl array fill pointer
+	fpeo = 0		# entry tbl offset list fill pointer
+	fpdef = 0 		# define list fill pointer
+}
+
+### /^;/ { }			# line starting with a semicolon is comment 
+
+/^[A-Z]/ {			# table name
+	if ( $1 == "TABLE" ) {
+	   tbl = $2		# get table name
+	   newtbl( tbl )
+	}
+	else if ( $1 == "COMPILE" ) {
+	   array_name = $2
+	   if ( $3 == "NOINDEX" ) { enable_index = 0 }
+	}
+	else {			# table entry
+	   ec = ec +1
+	   n = split( $0, fld, " " )
+	   action = fld[ n-1 ]
+	   newstate = fld[ n ]
+	   store( action, newstate )
+	   ecct = ecct +1
+	}
+}
+
+END {	store( action, newstate )
+
+	if ( enable_index ) {
+	    printf( "\n/* index name #defines: */\n\n",
+		 ec, ecct )
+
+	    for( ii = 1; ii <= fpeo; ii++ ){
+	       printf( "#define %-12s %3d\n", define[ ii ], ii -1 )
+	    }
+	}
+
+	printf( "\n\n/* size of transition table is %d bytes */\n",
+		 fpe )
+
+	if ( enable_index ) {
+	    printf( "\nstatic short int %s_offset [ ] ={", array_name )
+	    for( ii = 1; ii <= fpeo; ii++ ){
+	        if ( (ii % 10) == 1 ) printf("\n  ")
+	        printf( " %4d", entry_offset[ ii ] )
+	        if ( ii < fpeo ) printf( "," )
+	    }
+	    printf(" };\n")
+	}
+
+	printf( "\nstatic char %s_entry [ ] = {", array_name )
+	for( ii = 1; ii <= fpe; ii++ ){
+	    if ( (ii % 6) == 1 ) printf("\n  ")
+	    printf( " %-14s", entry[ ii ] )
+	    if ( ii < fpe ) printf( "," )
+	}
+	printf(" };\n")
+
+}
+
+function store(  act, ns ){
+#	printf( "%s %s\n",  act, ns )
+	entry[ ++fpe ] = act
+	entry[ ++fpe ] = ns 
+}
+
+function newtbl( tbl ){
+	   if ( not_firstone ) {
+		store( action, newstate )
+	   }
+	   not_firstone = 1
+	   entry_offset[ ++fpeo ] = fpe		# entry tbl offset list
+	   define[ ++fpdef ] = tbl	# state name to define
+}
diff -u --recursive --new-file v2.1.14/linux/net/802/transit/pdutr.h linux/net/802/transit/pdutr.h
--- v2.1.14/linux/net/802/transit/pdutr.h	Thu Jan  1 02:00:00 1970
+++ linux/net/802/transit/pdutr.h	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,309 @@
+
+/* this file was generated on Thu Dec  5 13:58:11 GMT 1996  */
+
+/* index name #defines: */
+
+#define ADM            0
+#define CONN           1
+#define RESET_WAIT     2
+#define RESET_CHECK    3
+#define SETUP          4
+#define RESET          5
+#define D_CONN         6
+#define ERROR          7
+#define NORMAL         8
+#define BUSY           9
+#define REJECT        10
+#define AWAIT         11
+#define AWAIT_BUSY    12
+#define AWAIT_REJECT  13
+
+
+/* size of transition table is 1684 bytes */
+
+static short int pdutr_offset [ ] ={
+      0,   54,   82,  110,  138,  192,  246,  300,  328,  554,
+    780, 1006, 1232, 1458 };
+
+static char pdutr_entry [ ] = {
+   ADM5          , ADM           , ADM4          , ADM           , ADM5          , ADM           ,
+   ADM4          , ADM           , ADM5          , ADM           , ADM4          , ADM           ,
+   ADM5          , ADM           , ADM4          , ADM           , ADM3          , ADM           ,
+   ADM3          , ADM           , ADM2          , CONN          , ADM2          , CONN          ,
+   ADM5          , ADM           , ADM5          , ADM           , ADM5          , ADM           ,
+   ADM5          , ADM           , ADM5          , ADM           , ADM5          , ADM           ,
+   ADM5          , ADM           , ADM5          , ADM           , ADM5          , ADM           ,
+   ADM5          , ADM           , ADM5          , ADM           , ADM5          , ADM           ,
+   ADM5          , ADM           , ADM5          , ADM           , ADM5          , ADM           ,
+   CONN5         , CONN          , CONN5         , CONN          , CONN5         , CONN          ,
+   CONN5         , CONN          , CONN5         , CONN          , CONN3         , CONN          ,
+   CONN5         , CONN          , CONN5         , CONN          , CONN5         , CONN          ,
+   CONN5         , CONN          , CONN5         , CONN          , CONN4         , ADM           ,
+   CONN5         , CONN          , CONN5         , CONN          , RESWAIT8      , RESET_WAIT    ,
+   RESWAIT8      , RESET_WAIT    , RESWAIT8      , RESET_WAIT    , RESWAIT8      , RESET_WAIT    ,
+   RESWAIT7      , RESET_WAIT    , RESWAIT6      , RESET_WAIT    , RESWAIT8      , RESET_WAIT    ,
+   RESWAIT8      , RESET_WAIT    , RESWAIT8      , RESET_WAIT    , RESWAIT8      , RESET_WAIT    ,
+   RESWAIT8      , RESET_WAIT    , RESWAIT5      , ADM           , RESWAIT8      , RESET_WAIT    ,
+   RESWAIT8      , RESET_WAIT    , RESCHK6       , RESET_CHECK   , RESCHK6       , RESET_CHECK   ,
+   RESCHK6       , RESET_CHECK   , RESCHK6       , RESET_CHECK   , RESCHK5       , ADM           ,
+   RESCHK4       , RESET_CHECK   , RESCHK6       , RESET_CHECK   , RESCHK6       , RESET_CHECK   ,
+   RESCHK6       , RESET_CHECK   , RESCHK6       , RESET_CHECK   , RESCHK6       , RESET_CHECK   ,
+   RESCHK3       , ADM           , RESCHK6       , RESET_CHECK   , RESCHK6       , RESET_CHECK   ,
+   SETUP6        , SETUP         , SETUP6        , SETUP         , SETUP6        , SETUP         ,
+   SETUP6        , SETUP         , SETUP6        , SETUP         , SETUP6        , SETUP         ,
+   SETUP6        , SETUP         , SETUP6        , SETUP         , SETUP4        , ADM           ,
+   SETUP4        , ADM           , SETUP1        , SETUP         , SETUP1        , SETUP         ,
+   SETUP6        , SETUP         , SETUP6        , SETUP         , SETUP6        , SETUP         ,
+   SETUP6        , SETUP         , SETUP6        , SETUP         , SETUP6        , SETUP         ,
+   SETUP6        , SETUP         , SETUP6        , SETUP         , SETUP6        , SETUP         ,
+   SETUP2        , NORMAL        , SETUP5        , ADM           , SETUP5        , ADM           ,
+   SETUP6        , SETUP         , SETUP6        , SETUP         , SETUP6        , SETUP         ,
+   RESET6        , RESET         , RESET6        , RESET         , RESET6        , RESET         ,
+   RESET6        , RESET         , RESET6        , RESET         , RESET6        , RESET         ,
+   RESET6        , RESET         , RESET6        , RESET         , RESET4        , ADM           ,
+   RESET4        , ADM           , RESET1        , RESET         , RESET1        , RESET         ,
+   RESET6        , RESET         , RESET6        , RESET         , RESET6        , RESET         ,
+   RESET6        , RESET         , RESET6        , RESET         , RESET6        , RESET         ,
+   RESET6        , RESET         , RESET6        , RESET         , RESET6        , RESET         ,
+   RESET2        , NORMAL        , RESET5        , ADM           , RESET5        , ADM           ,
+   RESET6        , RESET         , RESET6        , RESET         , RESET6        , RESET         ,
+   D_CONN5       , D_CONN        , D_CONN5       , D_CONN        , D_CONN5       , D_CONN        ,
+   D_CONN5       , D_CONN        , D_CONN5       , D_CONN        , D_CONN5       , D_CONN        ,
+   D_CONN5       , D_CONN        , D_CONN5       , D_CONN        , D_CONN3       , D_CONN        ,
+   D_CONN3       , D_CONN        , D_CONN1       , ADM           , D_CONN1       , ADM           ,
+   D_CONN5       , D_CONN        , D_CONN5       , D_CONN        , D_CONN5       , D_CONN        ,
+   D_CONN5       , D_CONN        , D_CONN5       , D_CONN        , D_CONN5       , D_CONN        ,
+   D_CONN5       , D_CONN        , D_CONN5       , D_CONN        , D_CONN5       , D_CONN        ,
+   D_CONN4       , ADM           , D_CONN4       , ADM           , D_CONN5       , ADM           ,
+   D_CONN5       , D_CONN        , D_CONN5       , D_CONN        , D_CONN5       , D_CONN        ,
+   ERR5          , ERROR         , ERR5          , ERROR         , ERR5          , ERROR         ,
+   ERR5          , ERROR         , ERR2          , ADM           , ERR1          , RESET_CHECK   ,
+   ERR6          , ERROR         , ERR6          , ERROR         , ERR6          , ERROR         ,
+   ERR6          , ERROR         , ERR6          , ERROR         , ERR3          , ADM           ,
+   ERR4          , RESET_WAIT    , ERR4          , RESET_WAIT    , NORMAL8B      , NORMAL        ,
+   NORMAL9       , NORMAL        , NORMAL10      , NORMAL        , NORMAL10      , NORMAL        ,
+   NORMAL5       , REJECT        , NORMAL6       , REJECT        , NORMAL7       , REJECT        ,
+   NORMAL7       , REJECT        , NORMAL11      , NORMAL        , NORMAL11      , NORMAL        ,
+   NORMAL12      , NORMAL        , NORMAL12      , NORMAL        , NORMAL11      , NORMAL        ,
+   NORMAL11      , NORMAL        , NORMAL12      , NORMAL        , NORMAL12      , NORMAL        ,
+   NORMAL13      , NORMAL        , NORMAL13      , NORMAL        , NORMAL14      , NORMAL        ,
+   NORMAL14      , NORMAL        , NORMAL13      , NORMAL        , NORMAL13      , NORMAL        ,
+   NORMAL14      , NORMAL        , NORMAL14      , NORMAL        , NORMAL15      , NORMAL        ,
+   NORMAL16      , NORMAL        , NORMAL17      , NORMAL        , NORMAL17      , NORMAL        ,
+   NORMAL15      , NORMAL        , NORMAL16      , NORMAL        , NORMAL17      , NORMAL        ,
+   NORMAL17      , NORMAL        , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , NORMAL8B      , NORMAL        ,
+   NORMAL9       , NORMAL        , SH10          , ERROR         , NORMAL8A      , NORMAL        ,
+   NORMAL5       , REJECT        , NORMAL6       , REJECT        , SH10          , ERROR         ,
+   NORMAL5       , REJECT        , NORMAL11      , NORMAL        , NORMAL11      , NORMAL        ,
+   SH10          , ERROR         , NORMAL11      , NORMAL        , NORMAL11      , NORMAL        ,
+   NORMAL11      , NORMAL        , SH10          , ERROR         , NORMAL11      , NORMAL        ,
+   NORMAL13      , NORMAL        , NORMAL13      , NORMAL        , SH10          , ERROR         ,
+   NORMAL13      , NORMAL        , NORMAL13      , NORMAL        , NORMAL13      , NORMAL        ,
+   SH10          , ERROR         , NORMAL13      , NORMAL        , NORMAL15      , NORMAL        ,
+   NORMAL16      , NORMAL        , SH10          , ERROR         , NORMAL15      , NORMAL        ,
+   NORMAL15      , NORMAL        , NORMAL16      , NORMAL        , SH10          , ERROR         ,
+   NORMAL15      , NORMAL        , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , BUSY13        , BUSY          , BUSY14        , BUSY          ,
+   BUSY12        , BUSY          , BUSY12        , BUSY          , BUSY9         , BUSY          ,
+   BUSY10        , BUSY          , BUSY11        , BUSY          , BUSY11        , BUSY          ,
+   BUSY15        , BUSY          , BUSY15        , BUSY          , BUSY16        , BUSY          ,
+   BUSY16        , BUSY          , BUSY15        , BUSY          , BUSY15        , BUSY          ,
+   BUSY16        , BUSY          , BUSY16        , BUSY          , BUSY17        , BUSY          ,
+   BUSY17        , BUSY          , BUSY18        , BUSY          , BUSY18        , BUSY          ,
+   BUSY17        , BUSY          , BUSY17        , BUSY          , BUSY18        , BUSY          ,
+   BUSY18        , BUSY          , BUSY19        , BUSY          , BUSY20        , BUSY          ,
+   BUSY21        , BUSY          , BUSY21        , BUSY          , BUSY19        , BUSY          ,
+   BUSY20        , BUSY          , BUSY21        , BUSY          , BUSY21        , BUSY          ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , BUSY13        , BUSY          , BUSY14        , BUSY          ,
+   SH10          , ERROR         , BUSY13        , BUSY          , BUSY9         , BUSY          ,
+   BUSY10        , BUSY          , SH10          , ERROR         , BUSY9         , BUSY          ,
+   BUSY15        , BUSY          , BUSY15        , BUSY          , SH10          , ERROR         ,
+   BUSY15        , BUSY          , BUSY15        , BUSY          , BUSY15        , BUSY          ,
+   SH10          , ERROR         , BUSY15        , BUSY          , BUSY17        , BUSY          ,
+   BUSY17        , BUSY          , SH10          , ERROR         , BUSY17        , BUSY          ,
+   BUSY17        , BUSY          , BUSY17        , BUSY          , SH10          , ERROR         ,
+   BUSY17        , BUSY          , BUSY19        , BUSY          , BUSY20        , BUSY          ,
+   SH10          , ERROR         , BUSY19        , BUSY          , BUSY19        , BUSY          ,
+   BUSY20        , BUSY          , SH10          , ERROR         , BUSY19        , BUSY          ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   REJECT7       , REJECT        , REJECT8       , REJECT        , REJECT9       , REJECT        ,
+   REJECT9       , REJECT        , REJECT5       , REJECT        , REJECT5       , REJECT        ,
+   REJECT6       , REJECT        , REJECT6       , REJECT        , REJECT10      , REJECT        ,
+   REJECT10      , REJECT        , REJECT11      , REJECT        , REJECT11      , REJECT        ,
+   REJECT10      , REJECT        , REJECT10      , REJECT        , REJECT11      , REJECT        ,
+   REJECT11      , REJECT        , REJECT12      , REJECT        , REJECT12      , REJECT        ,
+   REJECT13      , REJECT        , REJECT13      , REJECT        , REJECT12      , REJECT        ,
+   REJECT12      , REJECT        , REJECT13      , REJECT        , REJECT13      , REJECT        ,
+   REJECT14      , REJECT        , REJECT15      , REJECT        , REJECT16      , REJECT        ,
+   REJECT16      , REJECT        , REJECT14      , REJECT        , REJECT15      , REJECT        ,
+   REJECT16      , REJECT        , REJECT16      , REJECT        , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   REJECT7       , REJECT        , REJECT8       , REJECT        , SH10          , ERROR         ,
+   REJECT7       , REJECT        , REJECT5       , REJECT        , REJECT5       , REJECT        ,
+   SH10          , ERROR         , REJECT5       , REJECT        , REJECT10      , REJECT        ,
+   REJECT10      , REJECT        , SH10          , ERROR         , REJECT10      , REJECT        ,
+   REJECT10      , REJECT        , REJECT10      , REJECT        , SH10          , ERROR         ,
+   REJECT10      , REJECT        , REJECT12      , REJECT        , REJECT12      , REJECT        ,
+   SH10          , ERROR         , REJECT12      , REJECT        , REJECT12      , REJECT        ,
+   REJECT12      , REJECT        , SH10          , ERROR         , REJECT12      , REJECT        ,
+   REJECT14      , REJECT        , REJECT15      , REJECT        , SH10          , ERROR         ,
+   REJECT14      , REJECT        , REJECT14      , REJECT        , REJECT15      , REJECT        ,
+   SH10          , ERROR         , REJECT14      , REJECT        , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , AWAIT6        , AWAIT         ,
+   AWAIT6        , AWAIT         , AWAIT7        , AWAIT         , AWAIT7        , AWAIT         ,
+   AWAIT3        , AWAIT_REJECT  , AWAIT3        , AWAIT_REJECT  , AWAIT4        , AWAIT_REJECT  ,
+   AWAIT4        , AWAIT_REJECT  , AWAIT9        , AWAIT         , AWAIT9        , AWAIT         ,
+   AWAIT10       , AWAIT         , AWAIT10       , AWAIT         , AWAIT9        , AWAIT         ,
+   AWAIT9        , AWAIT         , AWAIT10       , AWAIT         , AWAIT10       , AWAIT         ,
+   AWAIT12       , AWAIT         , AWAIT12       , AWAIT         , AWAIT13       , AWAIT         ,
+   AWAIT13       , AWAIT         , AWAIT12       , AWAIT         , AWAIT12       , AWAIT         ,
+   AWAIT13       , AWAIT         , AWAIT13       , AWAIT         , AWAIT9        , AWAIT         ,
+   AWAIT9        , AWAIT         , AWAIT10       , AWAIT         , AWAIT10       , AWAIT         ,
+   AWAIT9        , AWAIT         , AWAIT9        , AWAIT         , AWAIT10       , AWAIT         ,
+   AWAIT10       , AWAIT         , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , AWAIT6        , AWAIT         ,
+   AWAIT6        , AWAIT         , SH10          , ERROR         , AWAIT5        , NORMAL        ,
+   AWAIT3        , AWAIT_REJECT  , AWAIT3        , AWAIT_REJECT  , SH10          , ERROR         ,
+   AWAIT2        , REJECT        , AWAIT9        , AWAIT         , AWAIT9        , AWAIT         ,
+   SH10          , ERROR         , AWAIT8        , AWAIT         , AWAIT9        , AWAIT         ,
+   AWAIT9        , AWAIT         , SH10          , ERROR         , AWAIT8        , AWAIT         ,
+   AWAIT12       , AWAIT         , AWAIT12       , AWAIT         , SH10          , ERROR         ,
+   AWAIT11       , AWAIT         , AWAIT12       , AWAIT         , AWAIT12       , AWAIT         ,
+   SH10          , ERROR         , AWAIT11       , AWAIT         , AWAIT9        , AWAIT         ,
+   AWAIT9        , AWAIT         , SH10          , ERROR         , AWAIT8        , AWAIT         ,
+   AWAIT9        , AWAIT         , AWAIT9        , AWAIT         , SH10          , ERROR         ,
+   AWAIT8        , AWAIT         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , AWAIT_BUSY8   , AWAIT_BUSY    , AWAIT_BUSY8   , AWAIT_BUSY    ,
+   AWAIT_BUSY9   , AWAIT_BUSY    , AWAIT_BUSY9   , AWAIT_BUSY    , AWAIT_BUSY5   , AWAIT_BUSY    ,
+   AWAIT_BUSY5   , AWAIT_BUSY    , AWAIT_BUSY6   , AWAIT_BUSY    , AWAIT_BUSY6   , AWAIT_BUSY    ,
+   AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY12  , AWAIT_BUSY    ,
+   AWAIT_BUSY12  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    ,
+   AWAIT_BUSY12  , AWAIT_BUSY    , AWAIT_BUSY12  , AWAIT_BUSY    , AWAIT_BUSY14  , AWAIT_BUSY    ,
+   AWAIT_BUSY14  , AWAIT_BUSY    , AWAIT_BUSY15  , AWAIT_BUSY    , AWAIT_BUSY15  , AWAIT_BUSY    ,
+   AWAIT_BUSY14  , AWAIT_BUSY    , AWAIT_BUSY14  , AWAIT_BUSY    , AWAIT_BUSY15  , AWAIT_BUSY    ,
+   AWAIT_BUSY15  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    ,
+   AWAIT_BUSY12  , AWAIT_BUSY    , AWAIT_BUSY12  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    ,
+   AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY12  , AWAIT_BUSY    , AWAIT_BUSY12  , AWAIT_BUSY    ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , AWAIT_BUSY8   , AWAIT_BUSY    , AWAIT_BUSY8   , AWAIT_BUSY    ,
+   SH10          , ERROR         , AWAIT_BUSY7   , BUSY          , AWAIT_BUSY5   , AWAIT_BUSY    ,
+   AWAIT_BUSY5   , AWAIT_BUSY    , SH10          , ERROR         , AWAIT_BUSY4   , BUSY          ,
+   AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    , SH10          , ERROR         ,
+   AWAIT_BUSY10  , BUSY          , AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    ,
+   SH10          , ERROR         , AWAIT_BUSY10  , BUSY          , AWAIT_BUSY14  , AWAIT_BUSY    ,
+   AWAIT_BUSY14  , AWAIT_BUSY    , SH10          , ERROR         , AWAIT_BUSY13  , BUSY          ,
+   AWAIT_BUSY14  , AWAIT_BUSY    , AWAIT_BUSY14  , AWAIT_BUSY    , SH10          , ERROR         ,
+   AWAIT_BUSY13  , BUSY          , AWAIT_BUSY11  , AWAIT_BUSY    , AWAIT_BUSY11  , AWAIT_BUSY    ,
+   SH10          , ERROR         , AWAIT_BUSY10  , BUSY          , AWAIT_BUSY11  , AWAIT_BUSY    ,
+   AWAIT_BUSY11  , AWAIT_BUSY    , SH10          , ERROR         , AWAIT_BUSY10  , BUSY          ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   AWAIT_REJECT5 , AWAIT         , AWAIT_REJECT5 , AWAIT         , AWAIT_REJECT6 , AWAIT         ,
+   AWAIT_REJECT6 , AWAIT         , AWAIT_REJECT2 , AWAIT_REJECT  , AWAIT_REJECT2 , AWAIT_REJECT  ,
+   AWAIT_REJECT3 , AWAIT_REJECT  , AWAIT_REJECT3 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  ,
+   AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT9 , AWAIT_REJECT  , AWAIT_REJECT9 , AWAIT_REJECT  ,
+   AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT9 , AWAIT_REJECT  ,
+   AWAIT_REJECT9 , AWAIT_REJECT  , AWAIT_REJECT11, AWAIT_REJECT  , AWAIT_REJECT11, AWAIT_REJECT  ,
+   AWAIT_REJECT12, AWAIT_REJECT  , AWAIT_REJECT12, AWAIT_REJECT  , AWAIT_REJECT11, AWAIT_REJECT  ,
+   AWAIT_REJECT11, AWAIT_REJECT  , AWAIT_REJECT12, AWAIT_REJECT  , AWAIT_REJECT12, AWAIT_REJECT  ,
+   AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT9 , AWAIT_REJECT  ,
+   AWAIT_REJECT9 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  ,
+   AWAIT_REJECT9 , AWAIT_REJECT  , AWAIT_REJECT9 , AWAIT_REJECT  , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH4           , ADM           , SH4           , ADM           ,
+   SH4           , ADM           , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   SH3           , RESET_CHECK   , SH3           , RESET_CHECK   , SH3           , RESET_CHECK   ,
+   AWAIT_REJECT5 , AWAIT         , AWAIT_REJECT5 , AWAIT         , SH10          , ERROR         ,
+   AWAIT_REJECT4 , NORMAL        , AWAIT_REJECT2 , AWAIT_REJECT  , AWAIT_REJECT2 , AWAIT_REJECT  ,
+   SH10          , ERROR         , AWAIT_REJECT4 , NORMAL        , AWAIT_REJECT8 , AWAIT_REJECT  ,
+   AWAIT_REJECT8 , AWAIT_REJECT  , SH10          , ERROR         , AWAIT_REJECT7 , REJECT        ,
+   AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  , SH10          , ERROR         ,
+   AWAIT_REJECT7 , REJECT        , AWAIT_REJECT11, AWAIT_REJECT  , AWAIT_REJECT11, AWAIT_REJECT  ,
+   SH10          , ERROR         , AWAIT_REJECT10, REJECT        , AWAIT_REJECT11, AWAIT_REJECT  ,
+   AWAIT_REJECT11, AWAIT_REJECT  , SH10          , ERROR         , AWAIT_REJECT10, REJECT        ,
+   AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  , SH10          , ERROR         ,
+   AWAIT_REJECT7 , REJECT        , AWAIT_REJECT8 , AWAIT_REJECT  , AWAIT_REJECT8 , AWAIT_REJECT  ,
+   SH10          , ERROR         , AWAIT_REJECT7 , REJECT        , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH9           , ERROR         , SH9           , ERROR         ,
+   SH9           , ERROR         , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH6           , ADM           , SH6           , ADM           , SH6           , ADM           ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH5           , RESET_WAIT    ,
+   SH5           , RESET_WAIT    , SH5           , RESET_WAIT    , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR         , SH7           , ERROR         ,
+   SH7           , ERROR         , SH7           , ERROR          };
diff -u --recursive --new-file v2.1.14/linux/net/802/transit/pdutr.pre linux/net/802/transit/pdutr.pre
--- v2.1.14/linux/net/802/transit/pdutr.pre	Thu Jan  1 02:00:00 1970
+++ linux/net/802/transit/pdutr.pre	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,1121 @@
+COMPILE pdutr INDEX
+;
+; Transition tables for incomming pdu events.
+; translate this thing into C with
+;  	awk -f ./compile.awk pdu.trans > pdutr.h
+;
+TABLE ADM
+;Transition table for the ADM state:
+;
+;frame type   p bit     action    newstate
+;received     in frame
+;
+I_CMD         0         ADM5      ADM
+I_CMD         1         ADM4      ADM
+RR_CMD        0         ADM5      ADM
+RR_CMD        1         ADM4      ADM
+RNR_CMD       0         ADM5      ADM
+RNR_CMD       1         ADM4      ADM
+REJ_CMD       0         ADM5      ADM
+REJ_CMD       1         ADM4      ADM
+DISC_CMD      0         ADM3      ADM
+DISC_CMD      1         ADM3      ADM
+SABME_CMD     0         ADM2      CONN  
+SABME_CMD     1         ADM2      CONN  
+I_RSP         0         ADM5      ADM
+I_RSP         1         ADM5      ADM
+RR_RSP        0         ADM5      ADM
+RR_RSP        1         ADM5      ADM
+RNR_RSP       0         ADM5      ADM
+RNR_RSP       1         ADM5      ADM
+REJ_RSP       0         ADM5      ADM
+REJ_RSP       1         ADM5      ADM
+UA_RSP        0         ADM5      ADM
+UA_RSP        1         ADM5      ADM
+DM_RSP        0         ADM5      ADM
+DM_RSP        1         ADM5      ADM
+FRMR_RSP      0         ADM5      ADM
+FRMR_RSP      1         ADM5      ADM
+;
+TABLE CONN
+;
+;Transition table for the CONN state:
+;
+;frame type   action    newstate
+;received     
+;
+I_CMD         CONN5     CONN
+RR_CMD        CONN5     CONN
+RNR_CMD       CONN5     CONN
+REJ_CMD       CONN5     CONN
+DISC_CMD      CONN5     CONN
+SABME_CMD     CONN3     CONN  
+I_RSP         CONN5     CONN
+RR_RSP        CONN5     CONN
+RNR_RSP       CONN5     CONN
+REJ_RSP       CONN5     CONN
+UA_RSP        CONN5     CONN
+DM_RSP        CONN4     ADM
+FRMR_RSP      CONN5     CONN
+;
+TABLE RESET_WAIT
+;Transition table for the RESET_WAIT
+;
+;frame type   action    newstate
+;received     
+;
+I_CMD         RESWAIT8  RESET_WAIT
+RR_CMD        RESWAIT8  RESET_WAIT
+RNR_CMD       RESWAIT8  RESET_WAIT
+REJ_CMD       RESWAIT8  RESET_WAIT
+DISC_CMD      RESWAIT7  RESET_WAIT
+SABME_CMD     RESWAIT6  RESET_WAIT
+I_RSP         RESWAIT8  RESET_WAIT
+RR_RSP        RESWAIT8  RESET_WAIT
+RNR_RSP       RESWAIT8  RESET_WAIT
+REJ_RSP       RESWAIT8  RESET_WAIT
+UA_RSP        RESWAIT8  RESET_WAIT
+DM_RSP        RESWAIT5  ADM
+FRMR_RSP      RESWAIT8  RESET_WAIT
+;
+;
+TABLE RESET_CHECK
+;Transition table for the RESET_CHECK state
+;
+;frame type   action    newstate
+;received     
+;
+I_CMD         RESCHK6  RESET_CHECK
+RR_CMD        RESCHK6  RESET_CHECK
+RNR_CMD       RESCHK6  RESET_CHECK
+REJ_CMD       RESCHK6  RESET_CHECK
+DISC_CMD      RESCHK5  ADM
+SABME_CMD     RESCHK4  RESET_CHECK
+I_RSP         RESCHK6  RESET_CHECK
+RR_RSP        RESCHK6  RESET_CHECK
+RNR_RSP       RESCHK6  RESET_CHECK
+REJ_RSP       RESCHK6  RESET_CHECK
+UA_RSP        RESCHK6  RESET_CHECK
+DM_RSP        RESCHK3  ADM
+FRMR_RSP      RESCHK6  RESET_CHECK
+;
+;
+TABLE SETUP
+;Transition table for the SETUP state
+;
+;frame type   p flag    action    newstate
+;received     = f
+;
+I_CMD         0         SETUP6      SETUP
+I_CMD         1         SETUP6      SETUP
+RR_CMD        0         SETUP6      SETUP
+RR_CMD        1         SETUP6      SETUP
+RNR_CMD       0         SETUP6      SETUP
+RNR_CMD       1         SETUP6      SETUP
+REJ_CMD       0         SETUP6      SETUP
+REJ_CMD       1         SETUP6      SETUP
+DISC_CMD      0         SETUP4      ADM
+DISC_CMD      1         SETUP4      ADM  
+SABME_CMD     0         SETUP1      SETUP 
+SABME_CMD     1         SETUP1      SETUP 
+I_RSP         0         SETUP6      SETUP
+I_RSP         1         SETUP6      SETUP
+RR_RSP        0         SETUP6      SETUP
+RR_RSP        1         SETUP6      SETUP
+RNR_RSP       0         SETUP6      SETUP
+RNR_RSP       1         SETUP6      SETUP
+REJ_RSP       0         SETUP6      SETUP
+REJ_RSP       1         SETUP6      SETUP
+UA_RSP        0         SETUP6      SETUP
+UA_RSP        1         SETUP2      NORMAL
+DM_RSP        0         SETUP5      ADM  
+DM_RSP        1         SETUP5      ADM  
+FRMR_RSP      0         SETUP6      SETUP
+FRMR_RSP      1         SETUP6      SETUP
+;
+;
+TABLE RESET
+;Transition table for the RESET state:
+;
+;frame type   p flag    action    newstate
+;received     = f
+;
+I_CMD         0         RESET6      RESET
+I_CMD         1         RESET6      RESET
+RR_CMD        0         RESET6      RESET
+RR_CMD        1         RESET6      RESET
+RNR_CMD       0         RESET6      RESET
+RNR_CMD       1         RESET6      RESET
+REJ_CMD       0         RESET6      RESET
+REJ_CMD       1         RESET6      RESET
+DISC_CMD      0         RESET4      ADM
+DISC_CMD      1         RESET4      ADM  
+SABME_CMD     0         RESET1      RESET 
+SABME_CMD     1         RESET1      RESET 
+I_RSP         0         RESET6      RESET
+I_RSP         1         RESET6      RESET
+RR_RSP        0         RESET6      RESET
+RR_RSP        1         RESET6      RESET
+RNR_RSP       0         RESET6      RESET
+RNR_RSP       1         RESET6      RESET
+REJ_RSP       0         RESET6      RESET
+REJ_RSP       1         RESET6      RESET
+UA_RSP        0         RESET6      RESET
+UA_RSP        1         RESET2      NORMAL
+DM_RSP        0         RESET5      ADM  
+DM_RSP        1         RESET5      ADM  
+FRMR_RSP      0         RESET6      RESET
+FRMR_RSP      1         RESET6      RESET
+;
+;
+TABLE D_CONN
+;Transition table for the D_CONN state:
+;
+;frame type   p bit     action    newstate
+;received     in frame  
+I_CMD         0         D_CONN5      D_CONN
+I_CMD         1         D_CONN5      D_CONN
+RR_CMD        0         D_CONN5      D_CONN
+RR_CMD        1         D_CONN5      D_CONN
+RNR_CMD       0         D_CONN5      D_CONN
+RNR_CMD       1         D_CONN5      D_CONN
+REJ_CMD       0         D_CONN5      D_CONN
+REJ_CMD       1         D_CONN5      D_CONN
+DISC_CMD      0         D_CONN3      D_CONN
+DISC_CMD      1         D_CONN3      D_CONN
+SABME_CMD     0         D_CONN1      ADM  
+SABME_CMD     1         D_CONN1      ADM    
+I_RSP         0         D_CONN5      D_CONN
+I_RSP         1         D_CONN5      D_CONN
+RR_RSP        0         D_CONN5      D_CONN
+RR_RSP        1         D_CONN5      D_CONN
+RNR_RSP       0         D_CONN5      D_CONN
+RNR_RSP       1         D_CONN5      D_CONN
+REJ_RSP       0         D_CONN5      D_CONN
+REJ_RSP       1         D_CONN5      D_CONN
+UA_RSP        0         D_CONN5      D_CONN
+UA_RSP        1         D_CONN4      ADM   
+DM_RSP        0         D_CONN4      ADM  
+DM_RSP        1         D_CONN5      ADM  
+FRMR_RSP      0         D_CONN5      D_CONN
+FRMR_RSP      1         D_CONN5      D_CONN
+;
+;
+TABLE ERROR
+;Transition table for the ERROR state:
+;
+;frame type   action    newstate
+;received     
+;
+I_CMD         ERR5      ERROR
+RR_CMD        ERR5      ERROR
+RNR_CMD       ERR5      ERROR
+REJ_CMD       ERR5      ERROR
+DISC_CMD      ERR2      ADM
+SABME_CMD     ERR1      RESET_CHECK
+I_RSP         ERR6      ERROR
+RR_RSP        ERR6      ERROR
+RNR_RSP       ERR6      ERROR
+REJ_RSP       ERR6      ERROR
+UA_RSP        ERR6      ERROR
+DM_RSP        ERR3      ADM
+FRMR_RSP      ERR4      RESET_WAIT
+;
+TABLE NORMAL
+;Transition table for the NORMAL state:
+;
+;frame type   uexpect   p bit     p_flag    
+;received     N(S)      in frame            
+;
+I_CMD          	0	0	0	NORMAL8B	NORMAL
+I_CMD          	0	0	1	NORMAL9		NORMAL
+I_CMD          	0	1	0	NORMAL10	NORMAL
+I_CMD          	0	1	1	NORMAL10	NORMAL
+I_CMD          	1	0	0	NORMAL5		REJECT
+I_CMD          	1	0	1	NORMAL6		REJECT
+I_CMD          	1	1	0	NORMAL7		REJECT
+I_CMD          	1	1	1	NORMAL7		REJECT
+RR_CMD         	0	0	0	NORMAL11	NORMAL
+RR_CMD         	0	0	1	NORMAL11	NORMAL
+RR_CMD         	0	1	0	NORMAL12	NORMAL
+RR_CMD         	0	1	1	NORMAL12	NORMAL
+RR_CMD         	1	0	0	NORMAL11	NORMAL
+RR_CMD         	1	0	1	NORMAL11	NORMAL
+RR_CMD         	1	1	0	NORMAL12	NORMAL
+RR_CMD         	1	1	1	NORMAL12	NORMAL
+RNR_CMD        	0	0	0	NORMAL13	NORMAL
+RNR_CMD        	0	0	1	NORMAL13	NORMAL
+RNR_CMD        	0	1	0	NORMAL14	NORMAL
+RNR_CMD        	0	1	1	NORMAL14	NORMAL
+RNR_CMD        	1	0	0	NORMAL13	NORMAL
+RNR_CMD        	1	0	1	NORMAL13	NORMAL
+RNR_CMD        	1	1	0	NORMAL14	NORMAL
+RNR_CMD        	1	1	1	NORMAL14	NORMAL
+REJ_CMD        	0	0	0	NORMAL15	NORMAL
+REJ_CMD        	0	0	1	NORMAL16	NORMAL
+REJ_CMD        	0	1	0	NORMAL17	NORMAL 
+REJ_CMD        	0	1	1	NORMAL17	NORMAL 
+REJ_CMD        	1	0	0	NORMAL15	NORMAL
+REJ_CMD        	1	0	1	NORMAL16	NORMAL
+REJ_CMD        	1	1	0	NORMAL17	NORMAL 
+REJ_CMD        	1	1	1	NORMAL17	NORMAL 
+DISC_CMD       	0	0	0	SH4		ADM
+DISC_CMD       	0	0	1	SH4		ADM
+DISC_CMD       	0	1	0	SH4		ADM
+DISC_CMD       	0	1	1	SH4		ADM
+DISC_CMD       	1	0	0	SH4		ADM
+DISC_CMD       	1	0	1	SH4		ADM
+DISC_CMD       	1	1	0	SH4		ADM
+DISC_CMD       	1	1	1	SH4		ADM
+SABME_CMD      	0	0	0	SH3		RESET_CHECK
+SABME_CMD      	0	0	1	SH3		RESET_CHECK
+SABME_CMD      	0	1	0	SH3		RESET_CHECK
+SABME_CMD      	0	1	1	SH3		RESET_CHECK
+SABME_CMD      	1	0	0	SH3		RESET_CHECK
+SABME_CMD      	1	0	1	SH3		RESET_CHECK
+SABME_CMD      	1	1	0	SH3		RESET_CHECK
+SABME_CMD      	1	1	1	SH3		RESET_CHECK
+I_RSP          	0	0	0	NORMAL8B	NORMAL
+I_RSP          	0	0	1	NORMAL9		NORMAL
+I_RSP          	0	1	0	SH10		ERROR
+I_RSP          	0	1	1	NORMAL8A	NORMAL
+I_RSP          	1	0	0	NORMAL5		REJECT
+I_RSP          	1	0	1	NORMAL6		REJECT
+I_RSP          	1	1	0	SH10		ERROR
+I_RSP          	1	1	1	NORMAL5		REJECT
+RR_RSP         	0	0	0	NORMAL11	NORMAL
+RR_RSP         	0	0	1	NORMAL11	NORMAL
+RR_RSP         	0	1	0	SH10		ERROR
+RR_RSP         	0	1	1	NORMAL11	NORMAL
+RR_RSP         	1	0	0	NORMAL11	NORMAL
+RR_RSP         	1	0	1	NORMAL11	NORMAL
+RR_RSP         	1	1	0	SH10		ERROR
+RR_RSP         	1	1	1	NORMAL11	NORMAL
+RNR_RSP        	0	0	0	NORMAL13	NORMAL
+RNR_RSP        	0	0	1	NORMAL13	NORMAL
+RNR_RSP        	0	1	0	SH10		ERROR
+RNR_RSP        	0	1	1	NORMAL13	NORMAL
+RNR_RSP        	1	0	0	NORMAL13	NORMAL
+RNR_RSP        	1	0	1	NORMAL13	NORMAL
+RNR_RSP        	1	1	0	SH10		ERROR
+RNR_RSP        	1	1	1	NORMAL13	NORMAL
+REJ_RSP        	0	0	0	NORMAL15	NORMAL
+REJ_RSP        	0	0	1	NORMAL16	NORMAL
+REJ_RSP        	0	1	0	SH10		ERROR
+REJ_RSP        	0	1	1	NORMAL15	NORMAL
+REJ_RSP        	1	0	0	NORMAL15	NORMAL
+REJ_RSP        	1	0	1	NORMAL16	NORMAL
+REJ_RSP        	1	1	0	SH10		ERROR
+REJ_RSP        	1	1	1	NORMAL15	NORMAL
+UA_RSP         	0	0	0	SH9		ERROR
+UA_RSP         	0	0	1	SH9		ERROR
+UA_RSP         	0	1	0	SH9		ERROR
+UA_RSP         	0	1	1	SH9		ERROR
+UA_RSP         	1	0	0	SH9		ERROR
+UA_RSP         	1	0	1	SH9		ERROR
+UA_RSP         	1	1	0	SH9		ERROR
+UA_RSP         	1	1	1	SH9		ERROR
+DM_RSP         	0	0	0	SH6		ADM
+DM_RSP         	0	0	1	SH6		ADM
+DM_RSP         	0	1	0	SH6		ADM
+DM_RSP         	0	1	1	SH6		ADM
+DM_RSP         	1	0	0	SH6		ADM
+DM_RSP         	1	0	1	SH6		ADM
+DM_RSP         	1	1	0	SH6		ADM
+DM_RSP         	1	1	1	SH6		ADM
+FRMR_RSP       	0	0	0	SH5		RESET_WAIT
+FRMR_RSP       	0	0	1	SH5		RESET_WAIT
+FRMR_RSP       	0	1	0	SH5		RESET_WAIT
+FRMR_RSP       	0	1	1	SH5		RESET_WAIT
+FRMR_RSP       	1	0	0	SH5		RESET_WAIT
+FRMR_RSP       	1	0	1	SH5		RESET_WAIT
+FRMR_RSP       	1	1	0	SH5		RESET_WAIT
+FRMR_RSP       	1	1	1	SH5		RESET_WAIT
+BAD_FRAME      	0	0	0	SH7		ERROR
+BAD_FRAME      	0	0	1	SH7		ERROR
+BAD_FRAME      	0	1	0	SH7		ERROR
+BAD_FRAME      	0	1	1	SH7		ERROR
+BAD_FRAME      	1	0	0	SH7		ERROR
+BAD_FRAME      	1	0	1	SH7		ERROR
+BAD_FRAME      	1	1	0	SH7		ERROR
+BAD_FRAME      	1	1	1	SH7		ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 0 NORMAL5 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 0 NORMAL5 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 1 NORMAL5 REJECT
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 1 NORMAL6 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 1 NORMAL6 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x NORMAL7 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 1 NORMAL8A NORMAL
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 0 NORMAL8B NORMAL
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 0 NORMAL8B NORMAL
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 1 NORMAL9 NORMAL
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 1 NORMAL9 NORMAL
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x NORMAL10 NORMAL
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x NORMAL11 NORMAL
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x NORMAL11 NORMAL
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 1 NORMAL11 NORMAL
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x NORMAL12 NORMAL
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x NORMAL13 NORMAL
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x NORMAL13 NORMAL
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 1 NORMAL13 NORMAL
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x NORMAL14 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 0 NORMAL15 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 1 NORMAL15 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 0 NORMAL15 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 1 NORMAL16 NORMAL
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 1 NORMAL16 NORMAL
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x NORMAL17 NORMAL
+;
+TABLE BUSY
+;Transition table for the BUSY state:
+;
+;frame type   uexpect   p bit     p_flag    
+;received     N(S)      in frame            
+;
+I_CMD          	0	0	0	BUSY13	BUSY
+I_CMD          	0	0	1	BUSY14	BUSY
+I_CMD          	0	1	0	BUSY12	BUSY
+I_CMD          	0	1	1	BUSY12	BUSY
+I_CMD          	1	0	0	BUSY9	BUSY
+I_CMD          	1	0	1	BUSY10	BUSY
+I_CMD          	1	1	0	BUSY11	BUSY
+I_CMD          	1	1	1	BUSY11	BUSY
+RR_CMD         	0	0	0	BUSY15	BUSY
+RR_CMD         	0	0	1	BUSY15	BUSY
+RR_CMD         	0	1	0	BUSY16	BUSY
+RR_CMD         	0	1	1	BUSY16	BUSY
+RR_CMD         	1	0	0	BUSY15	BUSY
+RR_CMD         	1	0	1	BUSY15	BUSY
+RR_CMD         	1	1	0	BUSY16	BUSY
+RR_CMD         	1	1	1	BUSY16	BUSY
+RNR_CMD        	0	0	0	BUSY17	BUSY
+RNR_CMD        	0	0	1	BUSY17	BUSY
+RNR_CMD        	0	1	0	BUSY18	BUSY
+RNR_CMD        	0	1	1	BUSY18	BUSY
+RNR_CMD        	1	0	0	BUSY17	BUSY
+RNR_CMD        	1	0	1	BUSY17	BUSY
+RNR_CMD        	1	1	0	BUSY18	BUSY
+RNR_CMD        	1	1	1	BUSY18	BUSY
+REJ_CMD        	0	0	0	BUSY19	BUSY
+REJ_CMD        	0	0	1	BUSY20	BUSY
+REJ_CMD        	0	1	0	BUSY21	BUSY 
+REJ_CMD        	0	1	1	BUSY21	BUSY 
+REJ_CMD        	1	0	0	BUSY19	BUSY
+REJ_CMD        	1	0	1	BUSY20	BUSY
+REJ_CMD        	1	1	0	BUSY21	BUSY 
+REJ_CMD        	1	1	1	BUSY21	BUSY 
+DISC_CMD       	0	0	0	SH4	ADM
+DISC_CMD       	0	0	1	SH4	ADM
+DISC_CMD       	0	1	0	SH4	ADM
+DISC_CMD       	0	1	1	SH4	ADM
+DISC_CMD       	1	0	0	SH4	ADM
+DISC_CMD       	1	0	1	SH4	ADM
+DISC_CMD       	1	1	0	SH4	ADM
+DISC_CMD       	1	1	1	SH4	ADM
+SABME_CMD      	0	0	0	SH3	RESET_CHECK
+SABME_CMD      	0	0	1	SH3	RESET_CHECK
+SABME_CMD      	0	1	0	SH3	RESET_CHECK
+SABME_CMD      	0	1	1	SH3	RESET_CHECK
+SABME_CMD      	1	0	0	SH3	RESET_CHECK
+SABME_CMD      	1	0	1	SH3	RESET_CHECK
+SABME_CMD      	1	1	0	SH3	RESET_CHECK
+SABME_CMD      	1	1	1	SH3	RESET_CHECK
+I_RSP          	0	0	0	BUSY13	BUSY
+I_RSP          	0	0	1	BUSY14	BUSY
+I_RSP          	0	1	0	SH10	ERROR
+I_RSP          	0	1	1	BUSY13	BUSY
+I_RSP          	1	0	0	BUSY9	BUSY
+I_RSP          	1	0	1	BUSY10	BUSY
+I_RSP          	1	1	0	SH10	ERROR
+I_RSP          	1	1	1	BUSY9	BUSY
+RR_RSP         	0	0	0	BUSY15	BUSY
+RR_RSP         	0	0	1	BUSY15	BUSY
+RR_RSP         	0	1	0	SH10	ERROR
+RR_RSP         	0	1	1	BUSY15	BUSY
+RR_RSP         	1	0	0	BUSY15	BUSY
+RR_RSP         	1	0	1	BUSY15	BUSY
+RR_RSP         	1	1	0	SH10	ERROR
+RR_RSP         	1	1	1	BUSY15	BUSY
+RNR_RSP        	0	0	0	BUSY17	BUSY
+RNR_RSP        	0	0	1	BUSY17	BUSY
+RNR_RSP        	0	1	0	SH10	ERROR
+RNR_RSP        	0	1	1	BUSY17	BUSY
+RNR_RSP        	1	0	0	BUSY17	BUSY
+RNR_RSP        	1	0	1	BUSY17	BUSY
+RNR_RSP        	1	1	0	SH10	ERROR
+RNR_RSP        	1	1	1	BUSY17	BUSY
+REJ_RSP        	0	0	0	BUSY19	BUSY
+REJ_RSP        	0	0	1	BUSY20	BUSY
+REJ_RSP        	0	1	0	SH10	ERROR
+REJ_RSP        	0	1	1	BUSY19	BUSY
+REJ_RSP        	1	0	0	BUSY19	BUSY
+REJ_RSP        	1	0	1	BUSY20	BUSY
+REJ_RSP        	1	1	0	SH10	ERROR
+REJ_RSP        	1	1	1	BUSY19	BUSY
+UA_RSP         	0	0	0	SH9	ERROR
+UA_RSP         	0	0	1	SH9	ERROR
+UA_RSP         	0	1	0	SH9	ERROR
+UA_RSP         	0	1	1	SH9	ERROR
+UA_RSP         	1	0	0	SH9	ERROR
+UA_RSP         	1	0	1	SH9	ERROR
+UA_RSP         	1	1	0	SH9	ERROR
+UA_RSP         	1	1	1	SH9	ERROR
+DM_RSP         	0	0	0	SH6	ADM
+DM_RSP         	0	0	1	SH6	ADM
+DM_RSP         	0	1	0	SH6	ADM
+DM_RSP         	0	1	1	SH6	ADM
+DM_RSP         	1	0	0	SH6	ADM
+DM_RSP         	1	0	1	SH6	ADM
+DM_RSP         	1	1	0	SH6	ADM
+DM_RSP         	1	1	1	SH6	ADM
+FRMR_RSP       	0	0	0	SH5	RESET_WAIT
+FRMR_RSP       	0	0	1	SH5	RESET_WAIT
+FRMR_RSP       	0	1	0	SH5	RESET_WAIT
+FRMR_RSP       	0	1	1	SH5	RESET_WAIT
+FRMR_RSP       	1	0	0	SH5	RESET_WAIT
+FRMR_RSP       	1	0	1	SH5	RESET_WAIT
+FRMR_RSP       	1	1	0	SH5	RESET_WAIT
+FRMR_RSP       	1	1	1	SH5	RESET_WAIT
+BAD_FRAME      	0	0	0	SH7	ERROR
+BAD_FRAME      	0	0	1	SH7	ERROR
+BAD_FRAME      	0	1	0	SH7	ERROR
+BAD_FRAME      	0	1	1	SH7	ERROR
+BAD_FRAME      	1	0	0	SH7	ERROR
+BAD_FRAME      	1	0	1	SH7	ERROR
+BAD_FRAME      	1	1	0	SH7	ERROR
+BAD_FRAME      	1	1	1	SH7	ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 0 BUSY9 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 1 BUSY9 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 0 BUSY9 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP 1 0 1 BUSY10 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD 1 0 1 BUSY10 BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x BUSY11 BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x BUSY12 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 0 BUSY13 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 1 BUSY13 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 0 BUSY13 BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 1 BUSY14 BUSY
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 1 BUSY14 BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x BUSY15 BUSY
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x BUSY15 BUSY
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 1 BUSY15 BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x BUSY16 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x BUSY17 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x BUSY17 BUSY
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 1 BUSY17 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x BUSY18 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 0 BUSY19 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 0 BUSY19 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 1 BUSY19 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 1 BUSY20 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 1 BUSY20 BUSY
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x BUSY21 BUSY
+;
+TABLE REJECT
+;Transition table for the REJECT state:
+;
+;frame type   uexpect   p bit     p_flag    
+;received     N(S)      in frame            
+;
+I_CMD          	0	0	0	REJECT7		REJECT
+I_CMD          	0	0	1	REJECT8		REJECT
+I_CMD          	0	1	0	REJECT9		REJECT
+I_CMD          	0	1	1	REJECT9		REJECT
+I_CMD          	1	0	0	REJECT5		REJECT
+I_CMD          	1	0	1	REJECT5		REJECT
+I_CMD          	1	1	0	REJECT6		REJECT
+I_CMD          	1	1	1	REJECT6		REJECT
+RR_CMD         	0	0	0	REJECT10	REJECT
+RR_CMD         	0	0	1	REJECT10	REJECT
+RR_CMD         	0	1	0	REJECT11	REJECT
+RR_CMD         	0	1	1	REJECT11	REJECT
+RR_CMD         	1	0	0	REJECT10	REJECT
+RR_CMD         	1	0	1	REJECT10	REJECT
+RR_CMD         	1	1	0	REJECT11	REJECT
+RR_CMD         	1	1	1	REJECT11	REJECT
+RNR_CMD        	0	0	0	REJECT12	REJECT
+RNR_CMD        	0	0	1	REJECT12	REJECT
+RNR_CMD        	0	1	0	REJECT13	REJECT
+RNR_CMD        	0	1	1	REJECT13	REJECT
+RNR_CMD        	1	0	0	REJECT12	REJECT
+RNR_CMD        	1	0	1	REJECT12	REJECT
+RNR_CMD        	1	1	0	REJECT13	REJECT
+RNR_CMD        	1	1	1	REJECT13	REJECT
+REJ_CMD        	0	0	0	REJECT14	REJECT
+REJ_CMD        	0	0	1	REJECT15	REJECT
+REJ_CMD        	0	1	0	REJECT16	REJECT 
+REJ_CMD        	0	1	1	REJECT16	REJECT 
+REJ_CMD        	1	0	0	REJECT14	REJECT
+REJ_CMD        	1	0	1	REJECT15	REJECT
+REJ_CMD        	1	1	0	REJECT16	REJECT 
+REJ_CMD        	1	1	1	REJECT16	REJECT 
+DISC_CMD       	0	0	0	SH4		ADM
+DISC_CMD       	0	0	1	SH4		ADM
+DISC_CMD       	0	1	0	SH4		ADM
+DISC_CMD       	0	1	1	SH4		ADM
+DISC_CMD       	1	0	0	SH4		ADM
+DISC_CMD       	1	0	1	SH4		ADM
+DISC_CMD       	1	1	0	SH4		ADM
+DISC_CMD       	1	1	1	SH4		ADM
+SABME_CMD      	0	0	0	SH3		RESET_CHECK
+SABME_CMD      	0	0	1	SH3		RESET_CHECK
+SABME_CMD      	0	1	0	SH3		RESET_CHECK
+SABME_CMD      	0	1	1	SH3		RESET_CHECK
+SABME_CMD      	1	0	0	SH3		RESET_CHECK
+SABME_CMD      	1	0	1	SH3		RESET_CHECK
+SABME_CMD      	1	1	0	SH3		RESET_CHECK
+SABME_CMD      	1	1	1	SH3		RESET_CHECK
+I_RSP          	0	0	0	REJECT7		REJECT
+I_RSP          	0	0	1	REJECT8		REJECT
+I_RSP          	0	1	0	SH10		ERROR
+I_RSP          	0	1	1	REJECT7		REJECT
+I_RSP          	1	0	0	REJECT5		REJECT
+I_RSP          	1	0	1	REJECT5		REJECT
+I_RSP          	1	1	0	SH10		ERROR
+I_RSP          	1	1	1	REJECT5		REJECT
+RR_RSP         	0	0	0	REJECT10	REJECT
+RR_RSP         	0	0	1	REJECT10	REJECT
+RR_RSP         	0	1	0	SH10		ERROR
+RR_RSP         	0	1	1	REJECT10	REJECT
+RR_RSP         	1	0	0	REJECT10	REJECT
+RR_RSP         	1	0	1	REJECT10	REJECT
+RR_RSP         	1	1	0	SH10		ERROR
+RR_RSP         	1	1	1	REJECT10	REJECT
+RNR_RSP        	0	0	0	REJECT12	REJECT
+RNR_RSP        	0	0	1	REJECT12	REJECT
+RNR_RSP        	0	1	0	SH10		ERROR
+RNR_RSP        	0	1	1	REJECT12	REJECT
+RNR_RSP        	1	0	0	REJECT12	REJECT
+RNR_RSP        	1	0	1	REJECT12	REJECT
+RNR_RSP        	1	1	0	SH10		ERROR
+RNR_RSP        	1	1	1	REJECT12	REJECT
+REJ_RSP        	0	0	0	REJECT14	REJECT
+REJ_RSP        	0	0	1	REJECT15	REJECT
+REJ_RSP        	0	1	0	SH10		ERROR
+REJ_RSP        	0	1	1	REJECT14	REJECT
+REJ_RSP        	1	0	0	REJECT14	REJECT
+REJ_RSP        	1	0	1	REJECT15	REJECT
+REJ_RSP        	1	1	0	SH10		ERROR
+REJ_RSP        	1	1	1	REJECT14	REJECT
+UA_RSP         	0	0	0	SH9		ERROR
+UA_RSP         	0	0	1	SH9		ERROR
+UA_RSP         	0	1	0	SH9		ERROR
+UA_RSP         	0	1	1	SH9		ERROR
+UA_RSP         	1	0	0	SH9		ERROR
+UA_RSP         	1	0	1	SH9		ERROR
+UA_RSP         	1	1	0	SH9		ERROR
+UA_RSP         	1	1	1	SH9		ERROR
+DM_RSP         	0	0	0	SH6		ADM
+DM_RSP         	0	0	1	SH6		ADM
+DM_RSP         	0	1	0	SH6		ADM
+DM_RSP         	0	1	1	SH6		ADM
+DM_RSP         	1	0	0	SH6		ADM
+DM_RSP         	1	0	1	SH6		ADM
+DM_RSP         	1	1	0	SH6		ADM
+DM_RSP         	1	1	1	SH6		ADM
+FRMR_RSP       	0	0	0	SH5		RESET_WAIT
+FRMR_RSP       	0	0	1	SH5		RESET_WAIT
+FRMR_RSP       	0	1	0	SH5		RESET_WAIT
+FRMR_RSP       	0	1	1	SH5		RESET_WAIT
+FRMR_RSP       	1	0	0	SH5		RESET_WAIT
+FRMR_RSP       	1	0	1	SH5		RESET_WAIT
+FRMR_RSP       	1	1	0	SH5		RESET_WAIT
+FRMR_RSP       	1	1	1	SH5		RESET_WAIT
+BAD_FRAME      	0	0	0	SH7		ERROR
+BAD_FRAME      	0	0	1	SH7		ERROR
+BAD_FRAME      	0	1	0	SH7		ERROR
+BAD_FRAME      	0	1	1	SH7		ERROR
+BAD_FRAME      	1	0	0	SH7		ERROR
+BAD_FRAME      	1	0	1	SH7		ERROR
+BAD_FRAME      	1	1	0	SH7		ERROR
+BAD_FRAME      	1	1	1	SH7		ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x REJECT5 REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x REJECT5 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 1 REJECT5 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x REJECT6 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 1 REJECT7 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 0 REJECT7 REJECT
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 0 REJECT7 REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 0 1 REJECT8 REJECT
+;112 entries in table, 1 modified by tredit4 I_CMD x 0 1 REJECT8 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x REJECT9 REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x REJECT10 REJECT
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x REJECT10 REJECT
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 1 REJECT10 REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x REJECT11 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x REJECT12 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x REJECT12 REJECT
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 1 REJECT12 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x REJECT13 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 0 REJECT14 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 0 REJECT14 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 1 REJECT14 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_CMD x 0 1 REJECT15 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 0 1 REJECT15 REJECT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x REJECT16 REJECT
+;
+TABLE AWAIT
+;Transition table for the AWAIT state:
+;
+;frame type   uexpect   p bit     p_flag    
+;received     N(S)      in frame            
+;
+I_CMD          	0	0	0	AWAIT6	AWAIT
+I_CMD          	0	0	1	AWAIT6	AWAIT
+I_CMD          	0	1	0	AWAIT7	AWAIT
+I_CMD          	0	1	1	AWAIT7	AWAIT
+I_CMD          	1	0	0	AWAIT3	AWAIT_REJECT
+I_CMD          	1	0	1	AWAIT3	AWAIT_REJECT
+I_CMD          	1	1	0	AWAIT4	AWAIT_REJECT
+I_CMD          	1	1	1	AWAIT4	AWAIT_REJECT
+RR_CMD         	0	0	0	AWAIT9	AWAIT
+RR_CMD         	0	0	1	AWAIT9	AWAIT
+RR_CMD         	0	1	0	AWAIT10	AWAIT
+RR_CMD         	0	1	1	AWAIT10	AWAIT
+RR_CMD         	1	0	0	AWAIT9	AWAIT
+RR_CMD         	1	0	1	AWAIT9	AWAIT
+RR_CMD         	1	1	0	AWAIT10	AWAIT
+RR_CMD         	1	1	1	AWAIT10	AWAIT
+RNR_CMD        	0	0	0	AWAIT12	AWAIT
+RNR_CMD        	0	0	1	AWAIT12	AWAIT
+RNR_CMD        	0	1	0	AWAIT13	AWAIT 
+RNR_CMD        	0	1	1	AWAIT13	AWAIT 
+RNR_CMD        	1	0	0	AWAIT12	AWAIT
+RNR_CMD        	1	0	1	AWAIT12	AWAIT
+RNR_CMD        	1	1	0	AWAIT13	AWAIT 
+RNR_CMD        	1	1	1	AWAIT13	AWAIT 
+REJ_CMD        	0	0	0	AWAIT9	AWAIT
+REJ_CMD        	0	0	1	AWAIT9	AWAIT
+REJ_CMD        	0	1	0	AWAIT10	AWAIT
+REJ_CMD        	0	1	1	AWAIT10	AWAIT
+REJ_CMD        	1	0	0	AWAIT9	AWAIT
+REJ_CMD        	1	0	1	AWAIT9	AWAIT
+REJ_CMD        	1	1	0	AWAIT10	AWAIT
+REJ_CMD        	1	1	1	AWAIT10	AWAIT
+DISC_CMD       	0	0	0	SH4	ADM
+DISC_CMD       	0	0	1	SH4	ADM
+DISC_CMD       	0	1	0	SH4	ADM
+DISC_CMD       	0	1	1	SH4	ADM
+DISC_CMD       	1	0	0	SH4	ADM
+DISC_CMD       	1	0	1	SH4	ADM
+DISC_CMD       	1	1	0	SH4	ADM
+DISC_CMD       	1	1	1	SH4	ADM
+SABME_CMD      	0	0	0	SH3	RESET_CHECK
+SABME_CMD      	0	0	1	SH3	RESET_CHECK
+SABME_CMD      	0	1	0	SH3	RESET_CHECK
+SABME_CMD      	0	1	1	SH3	RESET_CHECK
+SABME_CMD      	1	0	0	SH3	RESET_CHECK
+SABME_CMD      	1	0	1	SH3	RESET_CHECK
+SABME_CMD      	1	1	0	SH3	RESET_CHECK
+SABME_CMD      	1	1	1	SH3	RESET_CHECK
+I_RSP          	0	0	0	AWAIT6	AWAIT
+I_RSP          	0	0	1	AWAIT6	AWAIT
+I_RSP          	0	1	0	SH10	ERROR
+I_RSP          	0	1	1	AWAIT5	NORMAL
+I_RSP          	1	0	0	AWAIT3	AWAIT_REJECT
+I_RSP          	1	0	1	AWAIT3	AWAIT_REJECT
+I_RSP          	1	1	0	SH10	ERROR
+I_RSP          	1	1	1	AWAIT2	REJECT
+RR_RSP         	0	0	0	AWAIT9	AWAIT
+RR_RSP         	0	0	1	AWAIT9	AWAIT
+RR_RSP         	0	1	0	SH10	ERROR
+RR_RSP         	0	1	1	AWAIT8	AWAIT
+RR_RSP         	1	0	0	AWAIT9	AWAIT
+RR_RSP         	1	0	1	AWAIT9	AWAIT
+RR_RSP         	1	1	0	SH10	ERROR
+RR_RSP         	1	1	1	AWAIT8	AWAIT
+RNR_RSP        	0	0	0	AWAIT12	AWAIT
+RNR_RSP        	0	0	1	AWAIT12	AWAIT
+RNR_RSP        	0	1	0	SH10	ERROR
+RNR_RSP        	0	1	1	AWAIT11	AWAIT
+RNR_RSP        	1	0	0	AWAIT12	AWAIT
+RNR_RSP        	1	0	1	AWAIT12	AWAIT
+RNR_RSP        	1	1	0	SH10	ERROR
+RNR_RSP        	1	1	1	AWAIT11	AWAIT
+REJ_RSP        	0	0	0	AWAIT9	AWAIT
+REJ_RSP        	0	0	1	AWAIT9	AWAIT
+REJ_RSP        	0	1	0	SH10	ERROR
+REJ_RSP        	0	1	1	AWAIT8	AWAIT
+REJ_RSP        	1	0	0	AWAIT9	AWAIT
+REJ_RSP        	1	0	1	AWAIT9	AWAIT
+REJ_RSP        	1	1	0	SH10	ERROR
+REJ_RSP        	1	1	1	AWAIT8	AWAIT
+UA_RSP         	0	0	0	SH9	ERROR
+UA_RSP         	0	0	1	SH9	ERROR
+UA_RSP         	0	1	0	SH9	ERROR
+UA_RSP         	0	1	1	SH9	ERROR
+UA_RSP         	1	0	0	SH9	ERROR
+UA_RSP         	1	0	1	SH9	ERROR
+UA_RSP         	1	1	0	SH9	ERROR
+UA_RSP         	1	1	1	SH9	ERROR
+DM_RSP         	0	0	0	SH6	ADM
+DM_RSP         	0	0	1	SH6	ADM
+DM_RSP         	0	1	0	SH6	ADM
+DM_RSP         	0	1	1	SH6	ADM
+DM_RSP         	1	0	0	SH6	ADM
+DM_RSP         	1	0	1	SH6	ADM
+DM_RSP         	1	1	0	SH6	ADM
+DM_RSP         	1	1	1	SH6	ADM
+FRMR_RSP       	0	0	0	SH5	RESET_WAIT
+FRMR_RSP       	0	0	1	SH5	RESET_WAIT
+FRMR_RSP       	0	1	0	SH5	RESET_WAIT
+FRMR_RSP       	0	1	1	SH5	RESET_WAIT
+FRMR_RSP       	1	0	0	SH5	RESET_WAIT
+FRMR_RSP       	1	0	1	SH5	RESET_WAIT
+FRMR_RSP       	1	1	0	SH5	RESET_WAIT
+FRMR_RSP       	1	1	1	SH5	RESET_WAIT
+BAD_FRAME      	0	0	0	SH7	ERROR
+BAD_FRAME      	0	0	1	SH7	ERROR
+BAD_FRAME      	0	1	0	SH7	ERROR
+BAD_FRAME      	0	1	1	SH7	ERROR
+BAD_FRAME      	1	0	0	SH7	ERROR
+BAD_FRAME      	1	0	1	SH7	ERROR
+BAD_FRAME      	1	1	0	SH7	ERROR
+BAD_FRAME      	1	1	1	SH7	ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 x AWAIT2 REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x AWAIT3 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x AWAIT3 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x AWAIT4 AWAIT_REJECT
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 x AWAIT5 NORMAL
+;112 entries in table, 2 modified by tredit4 I_RSP x 0 x AWAIT6 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 0 x AWAIT6 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x AWAIT7 AWAIT
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 x AWAIT8 AWAIT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 x AWAIT8 AWAIT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 REJ_RSP x 0 x AWAIT9 AWAIT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x AWAIT10 AWAIT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x AWAIT10 AWAIT
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 x AWAIT11 AWAIT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x AWAIT12 AWAIT
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x AWAIT12 AWAIT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x AWAIT13 AWAIT
+;
+TABLE AWAIT_BUSY
+;Transition table for the AWAIT_BUSY state:
+;
+;frame type   uexpect   p bit     p_flag    
+;received     N(S)      in frame            
+;
+I_CMD          	0	0	0	AWAIT_BUSY8	AWAIT_BUSY
+I_CMD          	0	0	1	AWAIT_BUSY8	AWAIT_BUSY
+I_CMD          	0	1	0	AWAIT_BUSY9	AWAIT_BUSY
+I_CMD          	0	1	1	AWAIT_BUSY9	AWAIT_BUSY
+I_CMD          	1	0	0	AWAIT_BUSY5	AWAIT_BUSY
+I_CMD          	1	0	1	AWAIT_BUSY5	AWAIT_BUSY
+I_CMD          	1	1	0	AWAIT_BUSY6	AWAIT_BUSY
+I_CMD          	1	1	1	AWAIT_BUSY6	AWAIT_BUSY
+RR_CMD         	0	0	0	AWAIT_BUSY11	AWAIT_BUSY
+RR_CMD         	0	0	1	AWAIT_BUSY11	AWAIT_BUSY
+RR_CMD         	0	1	0	AWAIT_BUSY12	AWAIT_BUSY
+RR_CMD         	0	1	1	AWAIT_BUSY12	AWAIT_BUSY
+RR_CMD         	1	0	0	AWAIT_BUSY11	AWAIT_BUSY
+RR_CMD         	1	0	1	AWAIT_BUSY11	AWAIT_BUSY
+RR_CMD         	1	1	0	AWAIT_BUSY12	AWAIT_BUSY
+RR_CMD         	1	1	1	AWAIT_BUSY12	AWAIT_BUSY
+RNR_CMD        	0	0	0	AWAIT_BUSY14	AWAIT_BUSY
+RNR_CMD        	0	0	1	AWAIT_BUSY14	AWAIT_BUSY
+RNR_CMD        	0	1	0	AWAIT_BUSY15	AWAIT_BUSY 
+RNR_CMD        	0	1	1	AWAIT_BUSY15	AWAIT_BUSY 
+RNR_CMD        	1	0	0	AWAIT_BUSY14	AWAIT_BUSY
+RNR_CMD        	1	0	1	AWAIT_BUSY14	AWAIT_BUSY
+RNR_CMD        	1	1	0	AWAIT_BUSY15	AWAIT_BUSY 
+RNR_CMD        	1	1	1	AWAIT_BUSY15	AWAIT_BUSY 
+REJ_CMD        	0	0	0	AWAIT_BUSY11	AWAIT_BUSY
+REJ_CMD        	0	0	1	AWAIT_BUSY11	AWAIT_BUSY
+REJ_CMD        	0	1	0	AWAIT_BUSY12	AWAIT_BUSY
+REJ_CMD        	0	1	1	AWAIT_BUSY12	AWAIT_BUSY
+REJ_CMD        	1	0	0	AWAIT_BUSY11	AWAIT_BUSY
+REJ_CMD        	1	0	1	AWAIT_BUSY11	AWAIT_BUSY
+REJ_CMD        	1	1	0	AWAIT_BUSY12	AWAIT_BUSY
+REJ_CMD        	1	1	1	AWAIT_BUSY12	AWAIT_BUSY
+DISC_CMD       	0	0	0	SH4		ADM
+DISC_CMD       	0	0	1	SH4		ADM
+DISC_CMD       	0	1	0	SH4		ADM
+DISC_CMD       	0	1	1	SH4		ADM
+DISC_CMD       	1	0	0	SH4		ADM
+DISC_CMD       	1	0	1	SH4		ADM
+DISC_CMD       	1	1	0	SH4		ADM
+DISC_CMD       	1	1	1	SH4		ADM
+SABME_CMD      	0	0	0	SH3		RESET_CHECK
+SABME_CMD      	0	0	1	SH3		RESET_CHECK
+SABME_CMD      	0	1	0	SH3		RESET_CHECK
+SABME_CMD      	0	1	1	SH3		RESET_CHECK
+SABME_CMD      	1	0	0	SH3		RESET_CHECK
+SABME_CMD      	1	0	1	SH3		RESET_CHECK
+SABME_CMD      	1	1	0	SH3		RESET_CHECK
+SABME_CMD      	1	1	1	SH3		RESET_CHECK
+I_RSP          	0	0	0	AWAIT_BUSY8	AWAIT_BUSY
+I_RSP          	0	0	1	AWAIT_BUSY8	AWAIT_BUSY
+I_RSP          	0	1	0	SH10		ERROR
+I_RSP          	0	1	1	AWAIT_BUSY7	BUSY
+I_RSP          	1	0	0	AWAIT_BUSY5	AWAIT_BUSY
+I_RSP          	1	0	1	AWAIT_BUSY5	AWAIT_BUSY
+I_RSP          	1	1	0	SH10		ERROR
+I_RSP          	1	1	1	AWAIT_BUSY4	BUSY
+RR_RSP         	0	0	0	AWAIT_BUSY11	AWAIT_BUSY
+RR_RSP         	0	0	1	AWAIT_BUSY11	AWAIT_BUSY
+RR_RSP         	0	1	0	SH10		ERROR
+RR_RSP         	0	1	1	AWAIT_BUSY10	BUSY
+RR_RSP         	1	0	0	AWAIT_BUSY11	AWAIT_BUSY
+RR_RSP         	1	0	1	AWAIT_BUSY11	AWAIT_BUSY
+RR_RSP         	1	1	0	SH10		ERROR
+RR_RSP         	1	1	1	AWAIT_BUSY10	BUSY
+RNR_RSP        	0	0	0	AWAIT_BUSY14	AWAIT_BUSY
+RNR_RSP        	0	0	1	AWAIT_BUSY14	AWAIT_BUSY
+RNR_RSP        	0	1	0	SH10		ERROR
+RNR_RSP        	0	1	1	AWAIT_BUSY13	BUSY
+RNR_RSP        	1	0	0	AWAIT_BUSY14	AWAIT_BUSY
+RNR_RSP        	1	0	1	AWAIT_BUSY14	AWAIT_BUSY
+RNR_RSP        	1	1	0	SH10		ERROR
+RNR_RSP        	1	1	1	AWAIT_BUSY13	BUSY
+REJ_RSP        	0	0	0	AWAIT_BUSY11	AWAIT_BUSY
+REJ_RSP        	0	0	1	AWAIT_BUSY11	AWAIT_BUSY
+REJ_RSP        	0	1	0	SH10		ERROR
+REJ_RSP        	0	1	1	AWAIT_BUSY10	BUSY
+REJ_RSP        	1	0	0	AWAIT_BUSY11	AWAIT_BUSY
+REJ_RSP        	1	0	1	AWAIT_BUSY11	AWAIT_BUSY
+REJ_RSP        	1	1	0	SH10		ERROR
+REJ_RSP        	1	1	1	AWAIT_BUSY10	BUSY
+UA_RSP         	0	0	0	SH9		ERROR
+UA_RSP         	0	0	1	SH9		ERROR
+UA_RSP         	0	1	0	SH9		ERROR
+UA_RSP         	0	1	1	SH9		ERROR
+UA_RSP         	1	0	0	SH9		ERROR
+UA_RSP         	1	0	1	SH9		ERROR
+UA_RSP         	1	1	0	SH9		ERROR
+UA_RSP         	1	1	1	SH9		ERROR
+DM_RSP         	0	0	0	SH6		ADM
+DM_RSP         	0	0	1	SH6		ADM
+DM_RSP         	0	1	0	SH6		ADM
+DM_RSP         	0	1	1	SH6		ADM
+DM_RSP         	1	0	0	SH6		ADM
+DM_RSP         	1	0	1	SH6		ADM
+DM_RSP         	1	1	0	SH6		ADM
+DM_RSP         	1	1	1	SH6		ADM
+FRMR_RSP       	0	0	0	SH5		RESET_WAIT
+FRMR_RSP       	0	0	1	SH5		RESET_WAIT
+FRMR_RSP       	0	1	0	SH5		RESET_WAIT
+FRMR_RSP       	0	1	1	SH5		RESET_WAIT
+FRMR_RSP       	1	0	0	SH5		RESET_WAIT
+FRMR_RSP       	1	0	1	SH5		RESET_WAIT
+FRMR_RSP       	1	1	0	SH5		RESET_WAIT
+FRMR_RSP       	1	1	1	SH5		RESET_WAIT
+BAD_FRAME      	0	0	0	SH7		ERROR
+BAD_FRAME      	0	0	1	SH7		ERROR
+BAD_FRAME      	0	1	0	SH7		ERROR
+BAD_FRAME      	0	1	1	SH7		ERROR
+BAD_FRAME      	1	0	0	SH7		ERROR
+BAD_FRAME      	1	0	1	SH7		ERROR
+BAD_FRAME      	1	1	0	SH7		ERROR
+BAD_FRAME      	1	1	1	SH7		ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 1 modified by tredit4 I_RSP 1 1 x AWAIT_BUSY4 BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x AWAIT_BUSY5 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x AWAIT_BUSY5 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x AWAIT_BUSY6 AWAIT_BUSY
+;112 entries in table, 1 modified by tredit4 I_RSP x 1 x AWAIT_BUSY7 BUSY
+;112 entries in table, 2 modified by tredit4 I_RSP x 0 x AWAIT_BUSY8 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD x 0 x AWAIT_BUSY8 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x AWAIT_BUSY9 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 x AWAIT_BUSY10 BUSY
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 x AWAIT_BUSY10 BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 REJ_RSP x 0 x AWAIT_BUSY11 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x AWAIT_BUSY12 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x AWAIT_BUSY12 AWAIT_BUSY
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 x AWAIT_BUSY13 BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x AWAIT_BUSY14 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x AWAIT_BUSY14 AWAIT_BUSY
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x AWAIT_BUSY15 AWAIT_BUSY
+;
+TABLE AWAIT_REJECT
+;Transition table for the AWAIT_REJECT state:
+;
+;frame type   uexpect   p bit     p_flag    
+;received     N(S)      in frame            
+;
+I_CMD          	0	0	0	AWAIT_REJECT5	AWAIT
+I_CMD          	0	0	1	AWAIT_REJECT5	AWAIT
+I_CMD          	0	1	0	AWAIT_REJECT6	AWAIT
+I_CMD          	0	1	1	AWAIT_REJECT6	AWAIT
+I_CMD          	1	0	0	AWAIT_REJECT2	AWAIT_REJECT
+I_CMD          	1	0	1	AWAIT_REJECT2	AWAIT_REJECT
+I_CMD          	1	1	0	AWAIT_REJECT3	AWAIT_REJECT
+I_CMD          	1	1	1	AWAIT_REJECT3	AWAIT_REJECT
+RR_CMD         	0	0	0	AWAIT_REJECT8	AWAIT_REJECT
+RR_CMD         	0	0	1	AWAIT_REJECT8	AWAIT_REJECT
+RR_CMD         	0	1	0	AWAIT_REJECT9	AWAIT_REJECT
+RR_CMD         	0	1	1	AWAIT_REJECT9	AWAIT_REJECT
+RR_CMD         	1	0	0	AWAIT_REJECT8	AWAIT_REJECT
+RR_CMD         	1	0	1	AWAIT_REJECT8	AWAIT_REJECT
+RR_CMD         	1	1	0	AWAIT_REJECT9	AWAIT_REJECT
+RR_CMD         	1	1	1	AWAIT_REJECT9	AWAIT_REJECT
+RNR_CMD        	0	0	0	AWAIT_REJECT11	AWAIT_REJECT
+RNR_CMD        	0	0	1	AWAIT_REJECT11	AWAIT_REJECT
+RNR_CMD        	0	1	0	AWAIT_REJECT12	AWAIT_REJECT
+RNR_CMD        	0	1	1	AWAIT_REJECT12	AWAIT_REJECT
+RNR_CMD        	1	0	0	AWAIT_REJECT11	AWAIT_REJECT
+RNR_CMD        	1	0	1	AWAIT_REJECT11	AWAIT_REJECT
+RNR_CMD        	1	1	0	AWAIT_REJECT12	AWAIT_REJECT
+RNR_CMD        	1	1	1	AWAIT_REJECT12	AWAIT_REJECT
+REJ_CMD        	0	0	0	AWAIT_REJECT8	AWAIT_REJECT
+REJ_CMD        	0	0	1	AWAIT_REJECT8	AWAIT_REJECT
+REJ_CMD        	0	1	0	AWAIT_REJECT9	AWAIT_REJECT
+REJ_CMD        	0	1	1	AWAIT_REJECT9	AWAIT_REJECT
+REJ_CMD        	1	0	0	AWAIT_REJECT8	AWAIT_REJECT
+REJ_CMD        	1	0	1	AWAIT_REJECT8	AWAIT_REJECT
+REJ_CMD        	1	1	0	AWAIT_REJECT9	AWAIT_REJECT
+REJ_CMD        	1	1	1	AWAIT_REJECT9	AWAIT_REJECT
+DISC_CMD       	0	0	0	SH4	ADM
+DISC_CMD       	0	0	1	SH4	ADM
+DISC_CMD       	0	1	0	SH4	ADM
+DISC_CMD       	0	1	1	SH4	ADM
+DISC_CMD       	1	0	0	SH4	ADM
+DISC_CMD       	1	0	1	SH4	ADM
+DISC_CMD       	1	1	0	SH4	ADM
+DISC_CMD       	1	1	1	SH4	ADM
+SABME_CMD      	0	0	0	SH3	RESET_CHECK
+SABME_CMD      	0	0	1	SH3	RESET_CHECK
+SABME_CMD      	0	1	0	SH3	RESET_CHECK
+SABME_CMD      	0	1	1	SH3	RESET_CHECK
+SABME_CMD      	1	0	0	SH3	RESET_CHECK
+SABME_CMD      	1	0	1	SH3	RESET_CHECK
+SABME_CMD      	1	1	0	SH3	RESET_CHECK
+SABME_CMD      	1	1	1	SH3	RESET_CHECK
+I_RSP          	0	0	0	AWAIT_REJECT5	AWAIT
+I_RSP          	0	0	1	AWAIT_REJECT5	AWAIT
+I_RSP          	0	1	0	SH10	ERROR
+I_RSP          	0	1	1	AWAIT_REJECT4	NORMAL
+I_RSP          	1	0	0	AWAIT_REJECT2	AWAIT_REJECT
+I_RSP          	1	0	1	AWAIT_REJECT2	AWAIT_REJECT
+I_RSP          	1	1	0	SH10	ERROR
+I_RSP          	1	1	1	AWAIT_REJECT4	NORMAL
+RR_RSP         	0	0	0	AWAIT_REJECT8	AWAIT_REJECT
+RR_RSP         	0	0	1	AWAIT_REJECT8	AWAIT_REJECT
+RR_RSP         	0	1	0	SH10	ERROR
+RR_RSP         	0	1	1	AWAIT_REJECT7	REJECT
+RR_RSP         	1	0	0	AWAIT_REJECT8	AWAIT_REJECT
+RR_RSP         	1	0	1	AWAIT_REJECT8	AWAIT_REJECT
+RR_RSP         	1	1	0	SH10	ERROR
+RR_RSP         	1	1	1	AWAIT_REJECT7	REJECT
+RNR_RSP        	0	0	0	AWAIT_REJECT11	AWAIT_REJECT
+RNR_RSP        	0	0	1	AWAIT_REJECT11	AWAIT_REJECT
+RNR_RSP        	0	1	0	SH10	ERROR
+RNR_RSP        	0	1	1	AWAIT_REJECT10	REJECT
+RNR_RSP        	1	0	0	AWAIT_REJECT11	AWAIT_REJECT
+RNR_RSP        	1	0	1	AWAIT_REJECT11	AWAIT_REJECT
+RNR_RSP        	1	1	0	SH10	ERROR
+RNR_RSP        	1	1	1	AWAIT_REJECT10	REJECT
+REJ_RSP        	0	0	0	AWAIT_REJECT8	AWAIT_REJECT
+REJ_RSP        	0	0	1	AWAIT_REJECT8	AWAIT_REJECT
+REJ_RSP        	0	1	0	SH10	ERROR
+REJ_RSP        	0	1	1	AWAIT_REJECT7	REJECT
+REJ_RSP        	1	0	0	AWAIT_REJECT8	AWAIT_REJECT
+REJ_RSP        	1	0	1	AWAIT_REJECT8	AWAIT_REJECT
+REJ_RSP        	1	1	0	SH10	ERROR
+REJ_RSP        	1	1	1	AWAIT_REJECT7	REJECT
+UA_RSP         	0	0	0	SH9	ERROR
+UA_RSP         	0	0	1	SH9	ERROR
+UA_RSP         	0	1	0	SH9	ERROR
+UA_RSP         	0	1	1	SH9	ERROR
+UA_RSP         	1	0	0	SH9	ERROR
+UA_RSP         	1	0	1	SH9	ERROR
+UA_RSP         	1	1	0	SH9	ERROR
+UA_RSP         	1	1	1	SH9	ERROR
+DM_RSP         	0	0	0	SH6	ADM
+DM_RSP         	0	0	1	SH6	ADM
+DM_RSP         	0	1	0	SH6	ADM
+DM_RSP         	0	1	1	SH6	ADM
+DM_RSP         	1	0	0	SH6	ADM
+DM_RSP         	1	0	1	SH6	ADM
+DM_RSP         	1	1	0	SH6	ADM
+DM_RSP         	1	1	1	SH6	ADM
+FRMR_RSP       	0	0	0	SH5	RESET_WAIT
+FRMR_RSP       	0	0	1	SH5	RESET_WAIT
+FRMR_RSP       	0	1	0	SH5	RESET_WAIT
+FRMR_RSP       	0	1	1	SH5	RESET_WAIT
+FRMR_RSP       	1	0	0	SH5	RESET_WAIT
+FRMR_RSP       	1	0	1	SH5	RESET_WAIT
+FRMR_RSP       	1	1	0	SH5	RESET_WAIT
+FRMR_RSP       	1	1	1	SH5	RESET_WAIT
+BAD_FRAME      	0	0	0	SH7	ERROR
+BAD_FRAME      	0	0	1	SH7	ERROR
+BAD_FRAME      	0	1	0	SH7	ERROR
+BAD_FRAME      	0	1	1	SH7	ERROR
+BAD_FRAME      	1	0	0	SH7	ERROR
+BAD_FRAME      	1	0	1	SH7	ERROR
+BAD_FRAME      	1	1	0	SH7	ERROR
+BAD_FRAME      	1	1	1	SH7	ERROR
+;
+;112 entries in table, 8 modified by tredit4 SABME_CMD x x x SH3 RESET_CHECK
+;112 entries in table, 8 modified by tredit4 DISC_CMD x x x SH4 ADM
+;112 entries in table, 8 modified by tredit4 FRMR_RSP x x x SH5 RESET_WAIT
+;112 entries in table, 8 modified by tredit4 DM_RSP x x x SH6 ADM
+;112 entries in table, 8 modified by tredit4 BAD_FRAME x x x SH7 ERROR
+;112 entries in table, 8 modified by tredit4 UA_RSP x x x SH9 ERROR
+;112 entries in table, 8 modified by tredit4 anyrsp x 1 0 SH10 ERROR
+;112 entries in table, 2 modified by tredit4 I_CMD 1 0 x AWAIT_REJECT2 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP 1 0 x AWAIT_REJECT2 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_CMD 1 1 x AWAIT_REJECT3 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 I_RSP x 1 x AWAIT_REJECT4 NORMAL
+;112 entries in table, 2 modified by tredit4 I_RSP x 0 x AWAIT_REJECT5 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 0 x AWAIT_REJECT5 AWAIT
+;112 entries in table, 2 modified by tredit4 I_CMD x 1 x AWAIT_REJECT6 AWAIT
+;112 entries in table, 2 modified by tredit4 RR_RSP x 1 x AWAIT_REJECT7 REJECT
+;112 entries in table, 2 modified by tredit4 REJ_RSP x 1 x AWAIT_REJECT7 REJECT
+;112 entries in table, 0 modified by tredit4 I_RSP 1 1 x AWAIT_REJECT7 REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RR_RSP x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 REJ_RSP x 0 x AWAIT_REJECT8 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RR_CMD x 1 x AWAIT_REJECT9 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 REJ_CMD x 1 x AWAIT_REJECT9 AWAIT_REJECT
+;112 entries in table, 2 modified by tredit4 RNR_RSP x 1 x AWAIT_REJECT10 REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 0 x AWAIT_REJECT11 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RNR_RSP x 0 x AWAIT_REJECT11 AWAIT_REJECT
+;112 entries in table, 4 modified by tredit4 RNR_CMD x 1 x AWAIT_REJECT12 AWAIT_REJECT
+;112 entries in table, 0 modified by tredit4 RNR_CMD x 1 x AWAIT_REJECT15 AWAIT_BUSY
diff -u --recursive --new-file v2.1.14/linux/net/802/transit/timertr.h linux/net/802/transit/timertr.h
--- v2.1.14/linux/net/802/transit/timertr.h	Thu Jan  1 02:00:00 1970
+++ linux/net/802/transit/timertr.h	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,157 @@
+
+/* this file was generated on Thu Dec  5 13:58:11 GMT 1996  */
+
+
+/* size of transition table is 898 bytes */
+
+static char timertr_entry [ ] = {
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , ADM           ,
+   NOP           , ADM           , NOP           , ADM           , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , CONN          , NOP           , CONN          ,
+   NOP           , CONN          , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_WAIT    , NOP           , RESET_WAIT    , NOP           , RESET_WAIT    ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , NOP           , RESET_CHECK   ,
+   NOP           , RESET_CHECK   , NOP           , RESET_CHECK   , SETUP7        , SETUP         ,
+   SETUP7        , SETUP         , SETUP3        , NORMAL        , SETUP3        , NORMAL        ,
+   SETUP8        , ADM           , SETUP8        , ADM           , SETUP3        , NORMAL        ,
+   SETUP3        , NORMAL        , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , NOP           , SETUP         , NOP           , SETUP         ,
+   NOP           , SETUP         , RESET7        , RESET         , RESET7        , RESET         ,
+   RESET3        , NORMAL        , RESET3        , NORMAL        , RESET8        , ADM           ,
+   RESET8        , ADM           , RESET3        , NORMAL        , RESET3        , NORMAL        ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   NOP           , RESET         , NOP           , RESET         , NOP           , RESET         ,
+   D_CONN6       , D_CONN        , D_CONN6       , D_CONN        , D_CONN6       , D_CONN        ,
+   D_CONN6       , D_CONN        , D_CONN7       , ADM           , D_CONN7       , ADM           ,
+   D_CONN7       , ADM           , D_CONN7       , ADM           , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , NOP           , D_CONN        ,
+   NOP           , D_CONN        , NOP           , D_CONN        , ERR7          , ERROR         ,
+   ERR7          , ERROR         , ERR7          , ERROR         , ERR7          , ERROR         ,
+   ERR8          , RESET_WAIT    , ERR8          , RESET_WAIT    , ERR8          , RESET_WAIT    ,
+   ERR8          , RESET_WAIT    , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NOP           , ERROR         , NOP           , ERROR         ,
+   NOP           , ERROR         , NORMAL20      , AWAIT         , NOP           , NORMAL        ,
+   NORMAL20      , AWAIT         , NOP           , NORMAL        , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   NORMAL19      , NORMAL        , NORMAL19      , NORMAL        , NORMAL19      , NORMAL        ,
+   NORMAL19      , NORMAL        , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , NOP           , NORMAL        ,
+   NOP           , NORMAL        , NOP           , NORMAL        , NOP           , NORMAL        ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , NORMAL20      , AWAIT         , NOP           , NORMAL        ,
+   NORMAL20      , AWAIT         , NOP           , NORMAL        , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   BUSY24        , AWAIT_BUSY    , NOP           , BUSY          , BUSY24        , AWAIT_BUSY    ,
+   NOP           , BUSY          , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , BUSY23        , BUSY          ,
+   BUSY23        , BUSY          , BUSY23        , BUSY          , BUSY23        , BUSY          ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , BUSY25        , BUSY          , BUSY26        , BUSY          ,
+   BUSY25        , BUSY          , BUSY26        , BUSY          , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   NOP           , BUSY          , NOP           , BUSY          , NOP           , BUSY          ,
+   NOP           , BUSY          , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , NOP           , REJECT        ,
+   NOP           , REJECT        , NOP           , REJECT        , NOP           , REJECT        ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , NOP           , REJECT        , NOP           , REJECT        ,
+   NOP           , REJECT        , NOP           , REJECT        , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   NOP           , REJECT        , NOP           , REJECT        , NOP           , REJECT        ,
+   NOP           , REJECT        , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , NOP           , REJECT        ,
+   NOP           , REJECT        , NOP           , REJECT        , NOP           , REJECT        ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , NOP           , AWAIT         , NOP           , AWAIT         ,
+   NOP           , AWAIT         , NOP           , AWAIT         , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   NOP           , AWAIT         , NOP           , AWAIT         , NOP           , AWAIT         ,
+   NOP           , AWAIT         , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , NOP           , AWAIT         ,
+   NOP           , AWAIT         , NOP           , AWAIT         , NOP           , AWAIT         ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , NOP           , AWAIT         , NOP           , AWAIT         ,
+   NOP           , AWAIT         , NOP           , AWAIT         , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    ,
+   NOP           , AWAIT_BUSY    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , NOP           , AWAIT_BUSY    ,
+   NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    ,
+   NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    , NOP           , AWAIT_BUSY    ,
+   NOP           , AWAIT_BUSY    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , NOP           , AWAIT_REJECT  ,
+   NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  ,
+   NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  ,
+   NOP           , AWAIT_REJECT  , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , NOP           , AWAIT_REJECT  ,
+   NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  , NOP           , AWAIT_REJECT  ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT    , SH11          , RESET_WAIT    ,
+   SH11          , RESET_WAIT    , SH11          , RESET_WAIT     };
diff -u --recursive --new-file v2.1.14/linux/net/802/transit/timertr.pre linux/net/802/transit/timertr.pre
--- v2.1.14/linux/net/802/transit/timertr.pre	Thu Jan  1 02:00:00 1970
+++ linux/net/802/transit/timertr.pre	Thu Dec 12 16:54:22 1996
@@ -0,0 +1,527 @@
+COMPILE timertr NOINDEX 
+TABLE XXX
+;
+;Transition table for expiring timers:
+;
+;llc state 	timer   	retry_c	s_flag 	p_flag	action	newstate
+;		expired    	>= N2  
+;				
+ADM		ACK_TIMER	0	0      	0	NOP	ADM	
+ADM		ACK_TIMER	0	0      	1	NOP	ADM
+ADM		ACK_TIMER	0	1      	0	NOP	ADM
+ADM		ACK_TIMER	0	1      	1	NOP	ADM
+ADM		ACK_TIMER	1	0      	0	NOP	ADM
+ADM		ACK_TIMER	1	0      	1	NOP	ADM
+ADM		ACK_TIMER	1	1       0	NOP	ADM
+ADM		ACK_TIMER	1	1	1	NOP	ADM
+;;
+ADM		P_TIMER		0	0	0	NOP	ADM
+ADM		P_TIMER		0	0	1	NOP	ADM
+ADM		P_TIMER		0	1	0	NOP	ADM
+ADM		P_TIMER		0	1	1	NOP	ADM
+ADM		P_TIMER		1	0	0	NOP	ADM
+ADM		P_TIMER		1	0	1	NOP	ADM
+ADM		P_TIMER		1	1	0	NOP	ADM
+ADM		P_TIMER		1	1	1	NOP	ADM
+;;			
+ADM		REJ_TIMER 	0	0	0	NOP	ADM
+ADM		REJ_TIMER 	0	0	1	NOP	ADM
+ADM		REJ_TIMER 	0	1	0	NOP	ADM
+ADM		REJ_TIMER 	0	1	1	NOP	ADM
+ADM		REJ_TIMER 	1	0	0	NOP	ADM
+ADM		REJ_TIMER 	1	0	1	NOP	ADM
+ADM		REJ_TIMER 	1	1	0	NOP	ADM
+ADM		REJ_TIMER 	1	1	1	NOP	ADM
+;;			
+ADM		BUSY_TIMER 	0	0	0	NOP	ADM
+ADM		BUSY_TIMER 	0	0	1	NOP	ADM
+ADM		BUSY_TIMER 	0	1	0	NOP	ADM
+ADM		BUSY_TIMER 	0	1	1	NOP	ADM
+ADM		BUSY_TIMER 	1	0	0	NOP	ADM
+ADM		BUSY_TIMER 	1	0	1	NOP	ADM
+ADM		BUSY_TIMER 	1	1	0	NOP	ADM
+ADM		BUSY_TIMER 	1	1	1	NOP	ADM
+;;
+;;
+CONN		ACK_TIMER	0	0      	0	NOP	CONN	
+CONN		ACK_TIMER	0	0      	1	NOP	CONN
+CONN		ACK_TIMER	0	1      	0	NOP	CONN
+CONN		ACK_TIMER	0	1      	1	NOP	CONN
+CONN		ACK_TIMER	1	0      	0	NOP	CONN
+CONN		ACK_TIMER	1	0      	1	NOP	CONN
+CONN		ACK_TIMER	1	1       0	NOP	CONN
+CONN		ACK_TIMER	1	1	1	NOP	CONN
+;;
+CONN		P_TIMER		0	0	0	NOP	CONN
+CONN		P_TIMER		0	0	1	NOP	CONN
+CONN		P_TIMER		0	1	0	NOP	CONN
+CONN		P_TIMER		0	1	1	NOP	CONN
+CONN		P_TIMER		1	0	0	NOP	CONN
+CONN		P_TIMER		1	0	1	NOP	CONN
+CONN		P_TIMER		1	1	0	NOP	CONN
+CONN		P_TIMER		1	1	1	NOP	CONN
+;;			
+CONN		REJ_TIMER 	0	0	0	NOP	CONN
+CONN		REJ_TIMER 	0	0	1	NOP	CONN
+CONN		REJ_TIMER 	0	1	0	NOP	CONN
+CONN		REJ_TIMER 	0	1	1	NOP	CONN
+CONN		REJ_TIMER 	1	0	0	NOP	CONN
+CONN		REJ_TIMER 	1	0	1	NOP	CONN
+CONN		REJ_TIMER 	1	1	0	NOP	CONN
+CONN		REJ_TIMER 	1	1	1	NOP	CONN
+;;			
+CONN		BUSY_TIMER 	0	0	0	NOP	CONN
+CONN		BUSY_TIMER 	0	0	1	NOP	CONN
+CONN		BUSY_TIMER 	0	1	0	NOP	CONN
+CONN		BUSY_TIMER 	0	1	1	NOP	CONN
+CONN		BUSY_TIMER 	1	0	0	NOP	CONN
+CONN		BUSY_TIMER 	1	0	1	NOP	CONN
+CONN		BUSY_TIMER 	1	1	0	NOP	CONN
+CONN		BUSY_TIMER 	1	1	1	NOP	CONN
+;;
+;;
+RESET_WAIT	ACK_TIMER	0	0      	0	NOP	RESET_WAIT	
+RESET_WAIT	ACK_TIMER	0	0      	1	NOP	RESET_WAIT
+RESET_WAIT	ACK_TIMER	0	1      	0	NOP	RESET_WAIT
+RESET_WAIT	ACK_TIMER	0	1      	1	NOP	RESET_WAIT
+RESET_WAIT	ACK_TIMER	1	0      	0	NOP	RESET_WAIT
+RESET_WAIT	ACK_TIMER	1	0      	1	NOP	RESET_WAIT
+RESET_WAIT	ACK_TIMER	1	1       0	NOP	RESET_WAIT
+RESET_WAIT	ACK_TIMER	1	1	1	NOP	RESET_WAIT
+;;
+RESET_WAIT	P_TIMER		0	0	0	NOP	RESET_WAIT
+RESET_WAIT	P_TIMER		0	0	1	NOP	RESET_WAIT
+RESET_WAIT	P_TIMER		0	1	0	NOP	RESET_WAIT
+RESET_WAIT	P_TIMER		0	1	1	NOP	RESET_WAIT
+RESET_WAIT	P_TIMER		1	0	0	NOP	RESET_WAIT
+RESET_WAIT	P_TIMER		1	0	1	NOP	RESET_WAIT
+RESET_WAIT	P_TIMER		1	1	0	NOP	RESET_WAIT
+RESET_WAIT	P_TIMER		1	1	1	NOP	RESET_WAIT
+;;			
+RESET_WAIT	REJ_TIMER 	0	0	0	NOP	RESET_WAIT
+RESET_WAIT	REJ_TIMER 	0	0	1	NOP	RESET_WAIT
+RESET_WAIT	REJ_TIMER 	0	1	0	NOP	RESET_WAIT
+RESET_WAIT	REJ_TIMER 	0	1	1	NOP	RESET_WAIT
+RESET_WAIT	REJ_TIMER 	1	0	0	NOP	RESET_WAIT
+RESET_WAIT	REJ_TIMER 	1	0	1	NOP	RESET_WAIT
+RESET_WAIT	REJ_TIMER 	1	1	0	NOP	RESET_WAIT
+RESET_WAIT	REJ_TIMER 	1	1	1	NOP	RESET_WAIT
+;;			
+RESET_WAIT	BUSY_TIMER 	0	0	0	NOP	RESET_WAIT
+RESET_WAIT	BUSY_TIMER 	0	0	1	NOP	RESET_WAIT
+RESET_WAIT	BUSY_TIMER 	0	1	0	NOP	RESET_WAIT
+RESET_WAIT	BUSY_TIMER 	0	1	1	NOP	RESET_WAIT
+RESET_WAIT	BUSY_TIMER 	1	0	0	NOP	RESET_WAIT
+RESET_WAIT	BUSY_TIMER 	1	0	1	NOP	RESET_WAIT
+RESET_WAIT	BUSY_TIMER 	1	1	0	NOP	RESET_WAIT
+RESET_WAIT	BUSY_TIMER 	1	1	1	NOP	RESET_WAIT
+;;
+;;
+RESET_CHECK	ACK_TIMER	0	0      	0	NOP	RESET_CHECK	
+RESET_CHECK	ACK_TIMER	0	0      	1	NOP	RESET_CHECK
+RESET_CHECK	ACK_TIMER	0	1      	0	NOP	RESET_CHECK
+RESET_CHECK	ACK_TIMER	0	1      	1	NOP	RESET_CHECK
+RESET_CHECK	ACK_TIMER	1	0      	0	NOP	RESET_CHECK
+RESET_CHECK	ACK_TIMER	1	0      	1	NOP	RESET_CHECK
+RESET_CHECK	ACK_TIMER	1	1       0	NOP	RESET_CHECK
+RESET_CHECK	ACK_TIMER	1	1	1	NOP	RESET_CHECK
+;;
+RESET_CHECK	P_TIMER		0	0	0	NOP	RESET_CHECK
+RESET_CHECK	P_TIMER		0	0	1	NOP	RESET_CHECK
+RESET_CHECK	P_TIMER		0	1	0	NOP	RESET_CHECK
+RESET_CHECK	P_TIMER		0	1	1	NOP	RESET_CHECK
+RESET_CHECK	P_TIMER		1	0	0	NOP	RESET_CHECK
+RESET_CHECK	P_TIMER		1	0	1	NOP	RESET_CHECK
+RESET_CHECK	P_TIMER		1	1	0	NOP	RESET_CHECK
+RESET_CHECK	P_TIMER		1	1	1	NOP	RESET_CHECK
+;;			
+RESET_CHECK	REJ_TIMER 	0	0	0	NOP	RESET_CHECK
+RESET_CHECK	REJ_TIMER 	0	0	1	NOP	RESET_CHECK
+RESET_CHECK	REJ_TIMER 	0	1	0	NOP	RESET_CHECK
+RESET_CHECK	REJ_TIMER 	0	1	1	NOP	RESET_CHECK
+RESET_CHECK	REJ_TIMER 	1	0	0	NOP	RESET_CHECK
+RESET_CHECK	REJ_TIMER 	1	0	1	NOP	RESET_CHECK
+RESET_CHECK	REJ_TIMER 	1	1	0	NOP	RESET_CHECK
+RESET_CHECK	REJ_TIMER 	1	1	1	NOP	RESET_CHECK
+;;			
+RESET_CHECK	BUSY_TIMER 	0	0	0	NOP	RESET_CHECK
+RESET_CHECK	BUSY_TIMER 	0	0	1	NOP	RESET_CHECK
+RESET_CHECK	BUSY_TIMER 	0	1	0	NOP	RESET_CHECK
+RESET_CHECK	BUSY_TIMER 	0	1	1	NOP	RESET_CHECK
+RESET_CHECK	BUSY_TIMER 	1	0	0	NOP	RESET_CHECK
+RESET_CHECK	BUSY_TIMER 	1	0	1	NOP	RESET_CHECK
+RESET_CHECK	BUSY_TIMER 	1	1	0	NOP	RESET_CHECK
+RESET_CHECK	BUSY_TIMER 	1	1	1	NOP	RESET_CHECK
+;;
+;;
+;;
+SETUP		ACK_TIMER	0	0      	0	SETUP7  SETUP	
+SETUP		ACK_TIMER	0	0      	1	SETUP7  SETUP
+SETUP		ACK_TIMER	0	1      	0	SETUP3  NORMAL
+SETUP		ACK_TIMER	0	1      	1	SETUP3  NORMAL
+SETUP		ACK_TIMER	1	0      	0	SETUP8  ADM
+SETUP		ACK_TIMER	1	0      	1	SETUP8  ADM
+SETUP		ACK_TIMER	1	1       0	SETUP3  NORMAL
+SETUP		ACK_TIMER	1	1	1	SETUP3  NORMAL
+;;
+SETUP		P_TIMER		0	0	0	NOP	SETUP
+SETUP		P_TIMER		0	0	1	NOP	SETUP
+SETUP		P_TIMER		0	1	0	NOP	SETUP
+SETUP		P_TIMER		0	1	1	NOP	SETUP
+SETUP		P_TIMER		1	0	0	NOP	SETUP
+SETUP		P_TIMER		1	0	1	NOP	SETUP
+SETUP		P_TIMER		1	1	0	NOP	SETUP
+SETUP		P_TIMER		1	1	1	NOP	SETUP
+;;			
+SETUP		REJ_TIMER 	0	0	0	NOP	SETUP
+SETUP		REJ_TIMER 	0	0	1	NOP	SETUP
+SETUP		REJ_TIMER 	0	1	0	NOP	SETUP
+SETUP		REJ_TIMER 	0	1	1	NOP	SETUP
+SETUP		REJ_TIMER 	1	0	0	NOP	SETUP
+SETUP		REJ_TIMER 	1	0	1	NOP	SETUP
+SETUP		REJ_TIMER 	1	1	0	NOP	SETUP
+SETUP		REJ_TIMER 	1	1	1	NOP	SETUP
+;;			
+SETUP		BUSY_TIMER 	0	0	0	NOP	SETUP
+SETUP		BUSY_TIMER 	0	0	1	NOP	SETUP
+SETUP		BUSY_TIMER 	0	1	0	NOP	SETUP
+SETUP		BUSY_TIMER 	0	1	1	NOP	SETUP
+SETUP		BUSY_TIMER 	1	0	0	NOP	SETUP
+SETUP		BUSY_TIMER 	1	0	1	NOP	SETUP
+SETUP		BUSY_TIMER 	1	1	0	NOP	SETUP
+SETUP		BUSY_TIMER 	1	1	1	NOP	SETUP
+;;
+;;
+;;
+RESET		ACK_TIMER	0	0      	0	RESET7  RESET	
+RESET		ACK_TIMER	0	0      	1	RESET7  RESET
+RESET		ACK_TIMER	0	1      	0	RESET3  NORMAL
+RESET		ACK_TIMER	0	1      	1	RESET3  NORMAL
+RESET		ACK_TIMER	1	0      	0	RESET8  ADM
+RESET		ACK_TIMER	1	0      	1	RESET8  ADM
+RESET		ACK_TIMER	1	1       0	RESET3  NORMAL
+RESET		ACK_TIMER	1	1	1	RESET3  NORMAL
+;;
+RESET		P_TIMER		0	0	0	NOP	RESET
+RESET		P_TIMER		0	0	1	NOP	RESET
+RESET		P_TIMER		0	1	0	NOP	RESET
+RESET		P_TIMER		0	1	1	NOP	RESET
+RESET		P_TIMER		1	0	0	NOP	RESET
+RESET		P_TIMER		1	0	1	NOP	RESET
+RESET		P_TIMER		1	1	0	NOP	RESET
+RESET		P_TIMER		1	1	1	NOP	RESET
+;;			
+RESET		REJ_TIMER 	0	0	0	NOP	RESET
+RESET		REJ_TIMER 	0	0	1	NOP	RESET
+RESET		REJ_TIMER 	0	1	0	NOP	RESET
+RESET		REJ_TIMER 	0	1	1	NOP	RESET
+RESET		REJ_TIMER 	1	0	0	NOP	RESET
+RESET		REJ_TIMER 	1	0	1	NOP	RESET
+RESET		REJ_TIMER 	1	1	0	NOP	RESET
+RESET		REJ_TIMER 	1	1	1	NOP	RESET
+;;			
+RESET		BUSY_TIMER 	0	0	0	NOP	RESET
+RESET		BUSY_TIMER 	0	0	1	NOP	RESET
+RESET		BUSY_TIMER 	0	1	0	NOP	RESET
+RESET		BUSY_TIMER 	0	1	1	NOP	RESET
+RESET		BUSY_TIMER 	1	0	0	NOP	RESET
+RESET		BUSY_TIMER 	1	0	1	NOP	RESET
+RESET		BUSY_TIMER 	1	1	0	NOP	RESET
+RESET		BUSY_TIMER 	1	1	1	NOP	RESET
+;;
+;;
+D_CONN		ACK_TIMER	0	0      	0	D_CONN6 D_CONN	
+D_CONN		ACK_TIMER	0	0      	1	D_CONN6 D_CONN
+D_CONN		ACK_TIMER	0	1      	0	D_CONN6 D_CONN
+D_CONN		ACK_TIMER	0	1      	1	D_CONN6 D_CONN
+D_CONN		ACK_TIMER	1	0      	0	D_CONN7 ADM
+D_CONN		ACK_TIMER	1	0      	1	D_CONN7 ADM
+D_CONN		ACK_TIMER	1	1       0	D_CONN7 ADM
+D_CONN		ACK_TIMER	1	1	1	D_CONN7 ADM
+;;
+D_CONN		P_TIMER		0	0	0	NOP	D_CONN
+D_CONN		P_TIMER		0	0	1	NOP	D_CONN
+D_CONN		P_TIMER		0	1	0	NOP	D_CONN
+D_CONN		P_TIMER		0	1	1	NOP	D_CONN
+D_CONN		P_TIMER		1	0	0	NOP	D_CONN
+D_CONN		P_TIMER		1	0	1	NOP	D_CONN
+D_CONN		P_TIMER		1	1	0	NOP	D_CONN
+D_CONN		P_TIMER		1	1	1	NOP	D_CONN
+;;			
+D_CONN		REJ_TIMER 	0	0	0	NOP	D_CONN
+D_CONN		REJ_TIMER 	0	0	1	NOP	D_CONN
+D_CONN		REJ_TIMER 	0	1	0	NOP	D_CONN
+D_CONN		REJ_TIMER 	0	1	1	NOP	D_CONN
+D_CONN		REJ_TIMER 	1	0	0	NOP	D_CONN
+D_CONN		REJ_TIMER 	1	0	1	NOP	D_CONN
+D_CONN		REJ_TIMER 	1	1	0	NOP	D_CONN
+D_CONN		REJ_TIMER 	1	1	1	NOP	D_CONN
+;;			
+D_CONN		BUSY_TIMER 	0	0	0	NOP	D_CONN
+D_CONN		BUSY_TIMER 	0	0	1	NOP	D_CONN
+D_CONN		BUSY_TIMER 	0	1	0	NOP	D_CONN
+D_CONN		BUSY_TIMER 	0	1	1	NOP	D_CONN
+D_CONN		BUSY_TIMER 	1	0	0	NOP	D_CONN
+D_CONN		BUSY_TIMER 	1	0	1	NOP	D_CONN
+D_CONN		BUSY_TIMER 	1	1	0	NOP	D_CONN
+D_CONN		BUSY_TIMER 	1	1	1	NOP	D_CONN
+;;
+;;
+ERROR		ACK_TIMER	0	0      	0	ERR7    ERROR	
+ERROR		ACK_TIMER	0	0      	1	ERR7    ERROR
+ERROR		ACK_TIMER	0	1      	0	ERR7    ERROR
+ERROR		ACK_TIMER	0	1      	1	ERR7    ERROR
+ERROR		ACK_TIMER	1	0      	0	ERR8    RESET_WAIT
+ERROR		ACK_TIMER	1	0      	1	ERR8    RESET_WAIT
+ERROR		ACK_TIMER	1	1       0       ERR8    RESET_WAIT
+ERROR		ACK_TIMER	1	1	1	ERR8    RESET_WAIT
+;;
+ERROR		P_TIMER		0	0	0	NOP	ERROR
+ERROR		P_TIMER		0	0	1	NOP	ERROR
+ERROR		P_TIMER		0	1	0	NOP	ERROR
+ERROR		P_TIMER		0	1	1	NOP	ERROR
+ERROR		P_TIMER		1	0	0	NOP	ERROR
+ERROR		P_TIMER		1	0	1	NOP	ERROR
+ERROR		P_TIMER		1	1	0	NOP	ERROR
+ERROR		P_TIMER		1	1	1	NOP	ERROR
+;;			
+ERROR		REJ_TIMER 	0	0	0	NOP	ERROR
+ERROR		REJ_TIMER 	0	0	1	NOP	ERROR
+ERROR		REJ_TIMER 	0	1	0	NOP	ERROR
+ERROR		REJ_TIMER 	0	1	1	NOP	ERROR
+ERROR		REJ_TIMER 	1	0	0	NOP	ERROR
+ERROR		REJ_TIMER 	1	0	1	NOP	ERROR
+ERROR		REJ_TIMER 	1	1	0	NOP	ERROR
+ERROR		REJ_TIMER 	1	1	1	NOP	ERROR
+;;			
+ERROR		BUSY_TIMER 	0	0	0	NOP	ERROR
+ERROR		BUSY_TIMER 	0	0	1	NOP	ERROR
+ERROR		BUSY_TIMER 	0	1	0	NOP	ERROR
+ERROR		BUSY_TIMER 	0	1	1	NOP	ERROR
+ERROR		BUSY_TIMER 	1	0	0	NOP	ERROR
+ERROR		BUSY_TIMER 	1	0	1	NOP	ERROR
+ERROR		BUSY_TIMER 	1	1	0	NOP	ERROR
+ERROR		BUSY_TIMER 	1	1	1	NOP	ERROR
+;;
+;;
+NORMAL		ACK_TIMER	0	0      	0	NORMAL20 AWAIT	
+NORMAL		ACK_TIMER	0	0      	1	NOP	NORMAL
+NORMAL		ACK_TIMER	0	1      	0	NORMAL20 AWAIT
+NORMAL		ACK_TIMER	0	1      	1	NOP	NORMAL
+NORMAL		ACK_TIMER	1	0      	0	SH11    RESET_WAIT
+NORMAL		ACK_TIMER	1	0      	1	SH11    RESET_WAIT
+NORMAL		ACK_TIMER	1	1       0	SH11    RESET_WAIT
+NORMAL		ACK_TIMER	1	1	1	SH11    RESET_WAIT
+;;
+NORMAL		P_TIMER		0	0	0	NORMAL19 NORMAL
+NORMAL		P_TIMER		0	0	1	NORMAL19 NORMAL
+NORMAL		P_TIMER		0	1	0	NORMAL19 NORMAL
+NORMAL		P_TIMER		0	1	1	NORMAL19 NORMAL
+NORMAL		P_TIMER		1	0	0	SH11    RESET_WAIT
+NORMAL		P_TIMER		1	0	1	SH11    RESET_WAIT
+NORMAL		P_TIMER		1	1	0	SH11    RESET_WAIT
+NORMAL		P_TIMER		1	1	1	SH11    RESET_WAIT
+;;			
+NORMAL		REJ_TIMER 	0	0	0	NOP	NORMAL
+NORMAL		REJ_TIMER 	0	0	1	NOP	NORMAL
+NORMAL		REJ_TIMER 	0	1	0	NOP	NORMAL
+NORMAL		REJ_TIMER 	0	1	1	NOP	NORMAL
+NORMAL		REJ_TIMER 	1	0	0	SH11    RESET_WAIT
+NORMAL		REJ_TIMER 	1	0	1	SH11    RESET_WAIT
+NORMAL		REJ_TIMER 	1	1	0	SH11    RESET_WAIT
+NORMAL		REJ_TIMER 	1	1	1	SH11    RESET_WAIT
+;;			
+NORMAL		BUSY_TIMER 	0	0	0	NORMAL20 AWAIT
+NORMAL		BUSY_TIMER 	0	0	1	NOP	NORMAL
+NORMAL		BUSY_TIMER 	0	1	0	NORMAL20 AWAIT
+NORMAL		BUSY_TIMER 	0	1	1	NOP	NORMAL
+NORMAL		BUSY_TIMER 	1	0	0	SH11    RESET_WAIT
+NORMAL		BUSY_TIMER 	1	0	1	SH11    RESET_WAIT
+NORMAL		BUSY_TIMER 	1	1	0	SH11    RESET_WAIT
+NORMAL		BUSY_TIMER 	1	1	1	SH11    RESET_WAIT
+;;
+;;
+BUSY		ACK_TIMER	0	0      	0	BUSY24  AWAIT_BUSY
+BUSY		ACK_TIMER	0	0      	1	NOP	BUSY
+BUSY		ACK_TIMER	0	1      	0	BUSY24  AWAIT_BUSY
+BUSY		ACK_TIMER	0	1      	1	NOP	BUSY
+BUSY		ACK_TIMER	1	0      	0	SH11    RESET_WAIT	
+BUSY		ACK_TIMER	1	0      	1	SH11    RESET_WAIT
+BUSY		ACK_TIMER	1	1       0	SH11    RESET_WAIT
+BUSY		ACK_TIMER	1	1	1	SH11    RESET_WAIT
+;;
+BUSY		P_TIMER		0	0	0	BUSY23  BUSY
+BUSY		P_TIMER		0	0	1	BUSY23  BUSY
+BUSY		P_TIMER		0	1	0	BUSY23  BUSY
+BUSY		P_TIMER		0	1	1	BUSY23  BUSY
+BUSY		P_TIMER		1	0	0	SH11    RESET_WAIT
+BUSY		P_TIMER		1	0	1	SH11    RESET_WAIT
+BUSY		P_TIMER		1	1	0	SH11    RESET_WAIT
+BUSY		P_TIMER		1	1	1	SH11    RESET_WAIT
+;;			
+BUSY		REJ_TIMER 	0	0	0	BUSY25  BUSY
+BUSY		REJ_TIMER 	0	0	1	BUSY26  BUSY
+BUSY		REJ_TIMER 	0	1	0	BUSY25  BUSY
+BUSY		REJ_TIMER 	0	1	1	BUSY26  BUSY
+BUSY		REJ_TIMER 	1	0	0	SH11    RESET_WAIT
+BUSY		REJ_TIMER 	1	0	1	SH11    RESET_WAIT
+BUSY		REJ_TIMER 	1	1	0	SH11    RESET_WAIT
+BUSY		REJ_TIMER 	1	1	1	SH11    RESET_WAIT
+;;			
+BUSY		BUSY_TIMER 	0	0	0	NOP	BUSY
+BUSY		BUSY_TIMER 	0	0	1	NOP	BUSY
+BUSY		BUSY_TIMER 	0	1	0	NOP	BUSY
+BUSY		BUSY_TIMER 	0	1	1	NOP	BUSY
+BUSY		BUSY_TIMER 	1	0	0	SH11    RESET_WAIT
+BUSY		BUSY_TIMER 	1	0	1	SH11    RESET_WAIT
+BUSY		BUSY_TIMER 	1	1	0	SH11    RESET_WAIT
+BUSY		BUSY_TIMER 	1	1	1	SH11    RESET_WAIT
+;;
+;;
+REJECT		ACK_TIMER	0	0      	0	NOP	REJECT	
+REJECT		ACK_TIMER	0	0      	1	NOP	REJECT
+REJECT		ACK_TIMER	0	1      	0	NOP	REJECT
+REJECT		ACK_TIMER	0	1      	1	NOP	REJECT
+REJECT		ACK_TIMER	1	0      	0	SH11    RESET_WAIT
+REJECT		ACK_TIMER	1	0      	1	SH11    RESET_WAIT
+REJECT		ACK_TIMER	1	1       0	SH11    RESET_WAIT
+REJECT		ACK_TIMER	1	1	1	SH11    RESET_WAIT
+;;
+REJECT		P_TIMER		0	0	0	NOP	REJECT
+REJECT		P_TIMER		0	0	1	NOP	REJECT
+REJECT		P_TIMER		0	1	0	NOP	REJECT
+REJECT		P_TIMER		0	1	1	NOP	REJECT
+REJECT		P_TIMER		1	0	0	SH11    RESET_WAIT
+REJECT		P_TIMER		1	0	1	SH11    RESET_WAIT
+REJECT		P_TIMER		1	1	0	SH11    RESET_WAIT
+REJECT		P_TIMER		1	1	1	SH11    RESET_WAIT
+;;			
+REJECT		REJ_TIMER 	0	0	0	NOP	REJECT
+REJECT		REJ_TIMER 	0	0	1	NOP	REJECT
+REJECT		REJ_TIMER 	0	1	0	NOP	REJECT
+REJECT		REJ_TIMER 	0	1	1	NOP	REJECT
+REJECT		REJ_TIMER 	1	0	0	SH11    RESET_WAIT
+REJECT		REJ_TIMER 	1	0	1	SH11    RESET_WAIT
+REJECT		REJ_TIMER 	1	1	0	SH11    RESET_WAIT
+REJECT		REJ_TIMER 	1	1	1	SH11    RESET_WAIT
+;;			
+REJECT		BUSY_TIMER 	0	0	0	NOP	REJECT
+REJECT		BUSY_TIMER 	0	0	1	NOP	REJECT
+REJECT		BUSY_TIMER 	0	1	0	NOP	REJECT
+REJECT		BUSY_TIMER 	0	1	1	NOP	REJECT
+REJECT		BUSY_TIMER 	1	0	0	SH11    RESET_WAIT
+REJECT		BUSY_TIMER 	1	0	1	SH11    RESET_WAIT
+REJECT		BUSY_TIMER 	1	1	0	SH11    RESET_WAIT
+REJECT		BUSY_TIMER 	1	1	1	SH11    RESET_WAIT
+;;
+;;
+AWAIT		ACK_TIMER	0	0      	0	NOP	AWAIT	
+AWAIT		ACK_TIMER	0	0      	1	NOP	AWAIT
+AWAIT		ACK_TIMER	0	1      	0	NOP	AWAIT
+AWAIT		ACK_TIMER	0	1      	1	NOP	AWAIT
+AWAIT		ACK_TIMER	1	0      	0	SH11    RESET_WAIT
+AWAIT		ACK_TIMER	1	0      	1	SH11    RESET_WAIT
+AWAIT		ACK_TIMER	1	1       0	SH11    RESET_WAIT
+AWAIT		ACK_TIMER	1	1	1	SH11    RESET_WAIT
+;;
+AWAIT		P_TIMER		0	0	0	NOP	AWAIT
+AWAIT		P_TIMER		0	0	1	NOP	AWAIT
+AWAIT		P_TIMER		0	1	0	NOP	AWAIT
+AWAIT		P_TIMER		0	1	1	NOP	AWAIT
+AWAIT		P_TIMER		1	0	0	SH11    RESET_WAIT
+AWAIT		P_TIMER		1	0	1	SH11    RESET_WAIT
+AWAIT		P_TIMER		1	1	0	SH11    RESET_WAIT
+AWAIT		P_TIMER		1	1	1	SH11    RESET_WAIT
+;;			
+AWAIT		REJ_TIMER 	0	0	0	NOP	AWAIT
+AWAIT		REJ_TIMER 	0	0	1	NOP	AWAIT
+AWAIT		REJ_TIMER 	0	1	0	NOP	AWAIT
+AWAIT		REJ_TIMER 	0	1	1	NOP	AWAIT
+AWAIT		REJ_TIMER 	1	0	0	SH11    RESET_WAIT
+AWAIT		REJ_TIMER 	1	0	1	SH11    RESET_WAIT
+AWAIT		REJ_TIMER 	1	1	0	SH11    RESET_WAIT
+AWAIT		REJ_TIMER 	1	1	1	SH11    RESET_WAIT
+;;			
+AWAIT		BUSY_TIMER 	0	0	0	NOP	AWAIT
+AWAIT		BUSY_TIMER 	0	0	1	NOP	AWAIT
+AWAIT		BUSY_TIMER 	0	1	0	NOP	AWAIT
+AWAIT		BUSY_TIMER 	0	1	1	NOP	AWAIT
+AWAIT		BUSY_TIMER 	1	0	0	SH11    RESET_WAIT
+AWAIT		BUSY_TIMER 	1	0	1	SH11    RESET_WAIT
+AWAIT		BUSY_TIMER 	1	1	0	SH11    RESET_WAIT
+AWAIT		BUSY_TIMER 	1	1	1	SH11    RESET_WAIT
+;;
+;;
+AWAIT_BUSY	ACK_TIMER	0	0      	0	NOP	AWAIT_BUSY	
+AWAIT_BUSY	ACK_TIMER	0	0      	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	ACK_TIMER	0	1      	0	NOP	AWAIT_BUSY
+AWAIT_BUSY	ACK_TIMER	0	1      	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	ACK_TIMER	1	0      	0	SH11    RESET_WAIT
+AWAIT_BUSY	ACK_TIMER	1	0      	1	SH11    RESET_WAIT
+AWAIT_BUSY	ACK_TIMER	1	1       0	SH11    RESET_WAIT
+AWAIT_BUSY	ACK_TIMER	1	1	1	SH11    RESET_WAIT
+;;
+AWAIT_BUSY	P_TIMER		0	0	0	NOP	AWAIT_BUSY
+AWAIT_BUSY	P_TIMER		0	0	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	P_TIMER		0	1	0	NOP	AWAIT_BUSY
+AWAIT_BUSY	P_TIMER		0	1	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	P_TIMER		1	0	0	SH11    RESET_WAIT
+AWAIT_BUSY	P_TIMER		1	0	1	SH11    RESET_WAIT
+AWAIT_BUSY	P_TIMER		1	1	0	SH11    RESET_WAIT
+AWAIT_BUSY	P_TIMER		1	1	1	SH11    RESET_WAIT
+;;			
+AWAIT_BUSY	REJ_TIMER 	0	0	0	NOP	AWAIT_BUSY
+AWAIT_BUSY	REJ_TIMER 	0	0	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	REJ_TIMER 	0	1	0	NOP	AWAIT_BUSY
+AWAIT_BUSY	REJ_TIMER 	0	1	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	REJ_TIMER 	1	0	0	SH11    RESET_WAIT
+AWAIT_BUSY	REJ_TIMER 	1	0	1	SH11    RESET_WAIT
+AWAIT_BUSY	REJ_TIMER 	1	1	0	SH11    RESET_WAIT
+AWAIT_BUSY	REJ_TIMER 	1	1	1	SH11    RESET_WAIT
+;;			
+AWAIT_BUSY	BUSY_TIMER 	0	0	0	NOP	AWAIT_BUSY
+AWAIT_BUSY	BUSY_TIMER 	0	0	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	BUSY_TIMER 	0	1	0	NOP	AWAIT_BUSY
+AWAIT_BUSY	BUSY_TIMER 	0	1	1	NOP	AWAIT_BUSY
+AWAIT_BUSY	BUSY_TIMER 	1	0	0	SH11    RESET_WAIT
+AWAIT_BUSY	BUSY_TIMER 	1	0	1	SH11    RESET_WAIT
+AWAIT_BUSY	BUSY_TIMER 	1	1	0	SH11    RESET_WAIT
+AWAIT_BUSY	BUSY_TIMER 	1	1	1	SH11    RESET_WAIT
+;;
+;;
+AWAIT_REJECT	ACK_TIMER	0	0      	0	NOP	AWAIT_REJECT	
+AWAIT_REJECT	ACK_TIMER	0	0      	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	ACK_TIMER	0	1      	0	NOP	AWAIT_REJECT
+AWAIT_REJECT	ACK_TIMER	0	1      	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	ACK_TIMER	1	0      	0	SH11    RESET_WAIT
+AWAIT_REJECT	ACK_TIMER	1	0      	1	SH11    RESET_WAIT
+AWAIT_REJECT	ACK_TIMER	1	1       0	SH11    RESET_WAIT
+AWAIT_REJECT	ACK_TIMER	1	1	1	SH11    RESET_WAIT
+;;							
+AWAIT_REJECT	P_TIMER		0	0	0	NOP	AWAIT_REJECT
+AWAIT_REJECT	P_TIMER		0	0	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	P_TIMER		0	1	0	NOP	AWAIT_REJECT
+AWAIT_REJECT	P_TIMER		0	1	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	P_TIMER		1	0	0	SH11    RESET_WAIT
+AWAIT_REJECT	P_TIMER		1	0	1	SH11    RESET_WAIT
+AWAIT_REJECT	P_TIMER		1	1	0	SH11    RESET_WAIT
+AWAIT_REJECT	P_TIMER		1	1	1	SH11    RESET_WAIT
+;;			
+AWAIT_REJECT	REJ_TIMER 	0	0	0	NOP	AWAIT_REJECT
+AWAIT_REJECT	REJ_TIMER 	0	0	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	REJ_TIMER 	0	1	0	NOP	AWAIT_REJECT
+AWAIT_REJECT	REJ_TIMER 	0	1	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	REJ_TIMER 	1	0	0	SH11    RESET_WAIT
+AWAIT_REJECT	REJ_TIMER 	1	0	1	SH11    RESET_WAIT
+AWAIT_REJECT	REJ_TIMER 	1	1	0	SH11    RESET_WAIT
+AWAIT_REJECT	REJ_TIMER 	1	1	1	SH11    RESET_WAIT
+;;			
+AWAIT_REJECT	BUSY_TIMER 	0	0	0	NOP	AWAIT_REJECT
+AWAIT_REJECT	BUSY_TIMER 	0	0	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	BUSY_TIMER 	0	1	0	NOP	AWAIT_REJECT
+AWAIT_REJECT	BUSY_TIMER 	0	1	1	NOP	AWAIT_REJECT
+AWAIT_REJECT	BUSY_TIMER 	1	0	0	SH11    RESET_WAIT
+AWAIT_REJECT	BUSY_TIMER 	1	0	1	SH11    RESET_WAIT
+AWAIT_REJECT	BUSY_TIMER 	1	1	0	SH11    RESET_WAIT
+AWAIT_REJECT	BUSY_TIMER 	1	1	1	SH11    RESET_WAIT
+;;
diff -u --recursive --new-file v2.1.14/linux/net/Config.in linux/net/Config.in
--- v2.1.14/linux/net/Config.in	Fri Nov 22 18:28:22 1996
+++ linux/net/Config.in	Thu Dec 12 16:54:22 1996
@@ -12,7 +12,6 @@
 bool 'TCP/IP networking' CONFIG_INET
 if [ "$CONFIG_INET" = "y" ]; then
   source net/ipv4/Config.in
-
   if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
     tristate 'The IPv6 protocol' CONFIG_IPV6
   fi
@@ -22,6 +21,9 @@
 tristate 'The IPX protocol' CONFIG_IPX
 if [ "$CONFIG_IPX" != "n" ]; then
   bool 'Full internal IPX network' CONFIG_IPX_INTERN
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool 'IPX Type 20 Routing' CONFIG_IPX_PPROP_ROUTING
+  fi
 fi
 tristate 'Appletalk DDP' CONFIG_ATALK
 tristate 'Amateur Radio AX.25 Level 2' CONFIG_AX25
@@ -32,5 +34,6 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
 # tristate 'CCITT X.25 Packet Layer' CONFIG_X25
   bool 'Bridging (EXPERIMENTAL)' CONFIG_BRIDGE
+#  bool '802.2 LLC (EXPERIMENTAL)' CONFIG_LLC
 fi
 endmenu
diff -u --recursive --new-file v2.1.14/linux/net/appletalk/aarp.c linux/net/appletalk/aarp.c
--- v2.1.14/linux/net/appletalk/aarp.c	Tue Oct 29 19:58:48 1996
+++ linux/net/appletalk/aarp.c	Thu Dec 12 16:54:22 1996
@@ -115,8 +115,7 @@
 	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
 	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
 	skb->arp	=	1;
-	skb->free	=	1;
-	skb->dev	=	a->dev;
+	skb->dev	=	dev;
 	
 	/*
 	 *	Set up the ARP.
@@ -149,8 +148,9 @@
 	/*
 	 *	Send it.
 	 */	
-	 
-	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+	
+	skb->priority = SOPRI_NORMAL; 
+	dev_queue_xmit(skb);
 	
 	/*
 	 *	Update the sending count
@@ -175,7 +175,6 @@
 	skb_reserve(skb,dev->hard_header_len+aarp_dl->header_length);
 	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));	 
 	skb->arp	=	1;
-	skb->free	=	1;
 	skb->dev	=	dev;
 	
 	/*
@@ -212,8 +211,8 @@
 	/*
 	 *	Send it.
 	 */	
-	 
-	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+	skb->priority = SOPRI_NORMAL;
+	dev_queue_xmit(skb);
 	
 }
 
@@ -239,7 +238,6 @@
 	eah		=	(struct elapaarp *)skb_put(skb,sizeof(struct elapaarp));
 	
 	skb->arp	=	1;
-	skb->free	=	1;
 	skb->dev	=	dev;
 	
 	/*
@@ -273,8 +271,8 @@
 	/*
 	 *	Send it.
 	 */	
-	 
-	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+	skb->priority = SOPRI_NORMAL; 
+	dev_queue_xmit(skb);
 	
 }
 	
@@ -467,9 +465,11 @@
 		skb->data[2]=ft;
 		 
 		if(skb->sk==NULL)
-			dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+			skb->priority = SOPRI_NORMAL;
 		else
-			dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+			skb->priority = skb->sk->priority;
+		skb->dev = dev;
+		dev_queue_xmit(skb);
 		return 1;
 	}	
 	 
@@ -497,9 +497,10 @@
 	{
 		ddp_dl->datalink_header(ddp_dl, skb, ddp_eth_multicast);
 		if(skb->sk==NULL)
-			dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+			skb->priority = SOPRI_NORMAL;
 		else
-			dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+			skb->priority = skb->sk->priority;
+		dev_queue_xmit(skb);
 		restore_flags(flags);
 		return 1;
 	}
@@ -513,9 +514,10 @@
 		a->expires_at=jiffies+AARP_EXPIRY_TIME*10;
 		ddp_dl->datalink_header(ddp_dl, skb, a->hwaddr);
 		if(skb->sk==NULL)
-			dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+			skb->priority = SOPRI_NORMAL;
 		else
-			dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+			skb->priority = skb->sk->priority;
+		dev_queue_xmit(skb);
 		restore_flags(flags);
 		return 1;
 	}
@@ -621,9 +623,10 @@
 				a->expires_at=jiffies+AARP_EXPIRY_TIME*10;
 				ddp_dl->datalink_header(ddp_dl,skb,a->hwaddr);
 				if(skb->sk==NULL)
-					dev_queue_xmit(skb, skb->dev, SOPRI_NORMAL);
+					skb->priority = SOPRI_NORMAL;
 				else
-					dev_queue_xmit(skb, skb->dev, skb->sk->priority);
+					skb->priority = skb->sk->priority;
+				dev_queue_xmit(skb);
 			}
 		}
 		else
diff -u --recursive --new-file v2.1.14/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.14/linux/net/appletalk/ddp.c	Sat Nov 30 12:03:13 1996
+++ linux/net/appletalk/ddp.c	Thu Dec 12 16:54:22 1996
@@ -78,6 +78,7 @@
 #endif
 
 struct datalink_proto *ddp_dl, *aarp_dl;
+static struct proto_ops atalk_dgram_ops;
 
 #define min(a,b)	(((a)<(b))?(a):(b))
 
@@ -87,7 +88,7 @@
 *															*
 \***********************************************************************************************************************/
 
-static atalk_socket *volatile atalk_socket_list=NULL;
+static struct sock *volatile atalk_socket_list=NULL;
 
 /*
  *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
@@ -95,10 +96,10 @@
  *	use this facility.
  */
  
-static void atalk_remove_socket(atalk_socket *sk)
+static void atalk_remove_socket(struct sock *sk)
 {
 	unsigned long flags;
-	atalk_socket *s;
+	struct sock *s;
 	
 	save_flags(flags);
 	cli();
@@ -123,7 +124,7 @@
 	restore_flags(flags);
 }
 
-static void atalk_insert_socket(atalk_socket *sk)
+static void atalk_insert_socket(struct sock *sk)
 {
 	unsigned long flags;
 	save_flags(flags);
@@ -133,9 +134,9 @@
 	restore_flags(flags);
 }
 
-static atalk_socket *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif)
+static struct sock *atalk_search_socket(struct sockaddr_at *to, struct atalk_iface *atif)
 {
-	atalk_socket *s;
+	struct sock *s;
 
 	for( s = atalk_socket_list; s != NULL; s = s->next ) 
 	{
@@ -167,9 +168,9 @@
  *	Find a socket in the list.
  */
  
-static atalk_socket *atalk_find_socket(struct sockaddr_at *sat)
+static struct sock *atalk_find_socket(struct sockaddr_at *sat)
 {
-	atalk_socket *s;
+	struct sock *s;
 
 	for ( s = atalk_socket_list; s != NULL; s = s->next ) 
 	{
@@ -197,7 +198,7 @@
  *	touch it and we are (fairly 8-) ) safe.
  */
 
-static void atalk_destroy_socket(atalk_socket *sk);
+static void atalk_destroy_socket(struct sock *sk);
 
 /*
  *	Handler for deferred kills.
@@ -205,10 +206,10 @@
  
 static void atalk_destroy_timer(unsigned long data)
 {
-	atalk_destroy_socket((atalk_socket *)data);
+	atalk_destroy_socket((struct sock *)data);
 }
 
-static void atalk_destroy_socket(atalk_socket *sk)
+static void atalk_destroy_socket(struct sock *sk)
 {
 	struct sk_buff *skb;
 	atalk_remove_socket(sk);
@@ -243,7 +244,7 @@
  
 int atalk_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
-	atalk_socket *s;
+	struct sock *s;
 	int len=0;
 	off_t pos=0;
 	off_t begin=0;
@@ -1004,7 +1005,7 @@
   
 static int atalk_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-/*	atalk_socket *sk=(atalk_socket *)sock->data;*/
+/*	struct sock *sk=sock->sk;*/
 	switch(cmd)
 	{
 		default:
@@ -1019,10 +1020,10 @@
  
 static int atalk_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
 {
-	atalk_socket *sk;
+	struct sock *sk;
 	int err,opt;
 	
-	sk=(atalk_socket *)sock->data;
+	sk=sock->sk;
 	
 	if(optval==NULL)
 		return(-EINVAL);
@@ -1041,9 +1042,6 @@
 			}
 			break;
 			
-		case SOL_SOCKET:
-			return sock_setsockopt(sk,level,optname,optval,optlen);
-
 		default:
 			return -EOPNOTSUPP;
 	}
@@ -1057,11 +1055,11 @@
 static int atalk_getsockopt(struct socket *sock, int level, int optname,
 	char *optval, int *optlen)
 {
-	atalk_socket *sk;
+	struct sock *sk;
 	int val=0;
 	int err;
 	
-	sk=(atalk_socket *)sock->data;
+	sk=sock->sk;
 
 	switch(level)
 	{
@@ -1073,10 +1071,7 @@
 					return -ENOPROTOOPT;
 			}
 			break;
-			
-		case SOL_SOCKET:
-			return sock_getsockopt(sk,level,optname,optval,optlen);
-			
+
 		default:
 			return -EOPNOTSUPP;
 	}
@@ -1121,8 +1116,8 @@
  
 static int atalk_create(struct socket *sock, int protocol)
 {
-	atalk_socket *sk;
-	sk=(atalk_socket *)sk_alloc(GFP_KERNEL);
+	struct sock *sk;
+	sk=sk_alloc(GFP_KERNEL);
 	if(sk==NULL)
 		return(-ENOMEM);
 	switch(sock->type)
@@ -1132,6 +1127,7 @@
 		case SOCK_RAW:
 		/* We permit DDP datagram sockets */
 		case SOCK_DGRAM:
+			sock->ops = &atalk_dgram_ops;
 			break;
 		default:
 			sk_free((void *)sk);
@@ -1157,8 +1153,8 @@
 	
 	if(sock!=NULL)
 	{
-		sock->data=(void *)sk;
-		sk->sleep=sock->wait;
+		sk->sleep=&sock->wait;
+		sock->sk=sk;
 	}
 	
 	sk->state_change=def_callback1;
@@ -1185,13 +1181,13 @@
  
 static int atalk_release(struct socket *sock, struct socket *peer)
 {
-	atalk_socket *sk=(atalk_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	if(sk==NULL)
 		return(0);
 	if(!sk->dead)
 		sk->state_change(sk);
 	sk->dead=1;
-	sock->data=NULL;
+	sock->sk=NULL;
 	atalk_destroy_socket(sk);
 	return(0);
 }
@@ -1211,7 +1207,7 @@
 	return -EBUSY;
 }
  		
-static int atalk_autobind(atalk_socket *sk)
+static int atalk_autobind(struct sock *sk)
 {
 	struct at_addr *ap = atalk_find_primary();
 	struct sockaddr_at sat;
@@ -1236,10 +1232,10 @@
  
 static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
-	atalk_socket *sk;
+	struct sock *sk;
 	struct sockaddr_at *addr=(struct sockaddr_at *)uaddr;
 	
-	sk=(atalk_socket *)sock->data;
+	sk=sock->sk;
 	
 	if(sk->zapped==0)
 		return(-EINVAL);
@@ -1291,7 +1287,7 @@
 static int atalk_connect(struct socket *sock, struct sockaddr *uaddr,
 	int addr_len, int flags)
 {
-	atalk_socket *sk=(atalk_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	struct sockaddr_at *addr;
 	
 	sk->state = TCP_CLOSE;	
@@ -1339,8 +1335,10 @@
  
 static int atalk_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	if(newsock->data)
-		sk_free(newsock->data);
+	if(newsock->sk) {	  
+		sk_free(newsock->sk);
+		MOD_DEC_USE_COUNT;
+	}
 	return -EOPNOTSUPP;
 }
 
@@ -1353,9 +1351,9 @@
 	int *uaddr_len, int peer)
 {
 	struct sockaddr_at sat;
-	atalk_socket *sk;
+	struct sock *sk;
 	
-	sk=(atalk_socket *)sock->data;
+	sk=sock->sk;
 	if(sk->zapped)
 	{
 		if(atalk_autobind(sk)<0)
@@ -1392,7 +1390,7 @@
  
 static int atalk_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
-	atalk_socket *sock;
+	struct sock *sock;
 	struct ddpehdr *ddp=(void *)skb->h.raw;
 	struct atalk_iface *atif;
 	struct sockaddr_at tosat;
@@ -1627,9 +1625,10 @@
 	return atalk_rcv(skb,dev,pt);
 }
 
-static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+	struct scm_cookie *scm)
 {
-	atalk_socket *sk=(atalk_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	struct sockaddr_at *usat=(struct sockaddr_at *)msg->msg_name;
 	struct sockaddr_at local_satalk, gsat;
 	struct sk_buff *skb;
@@ -1639,10 +1638,11 @@
 	struct atalk_route *rt;
 	int loopback=0;
 	int err;
+	int flags = msg->msg_flags;
 	
-	if(flags)
+	if(flags&~MSG_DONTWAIT)
 		return -EINVAL;
-		
+
 	if(len>587)
 		return -EMSGSIZE;
 		
@@ -1658,10 +1658,8 @@
 			return(-EINVAL);
 		if(usat->sat_family != AF_APPLETALK)
 			return -EINVAL;
-#if 0 	/* netatalk doesn't implement this check */
 		if(usat->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
 			return -EPERM;
-#endif			
 	}
 	else
 	{
@@ -1704,12 +1702,11 @@
 	
 	size += dev->hard_header_len;
 
-	skb = sock_alloc_send_skb(sk, size, 0, 0 , &err);
+	skb = sock_alloc_send_skb(sk, size, 0, flags&MSG_DONTWAIT, &err);
 	if(skb==NULL)
 		return err;
 
 	skb->sk=sk;
-	skb->free=1;
 	skb->arp=1;
 	skb_reserve(skb,ddp_dl->header_length);
 	skb_reserve(skb,dev->hard_header_len);
@@ -1789,9 +1786,8 @@
 		if(sk->debug)
 			printk("SK %p: Loop back.\n", sk);
 		/* loop back */
-		atomic_sub(skb->truesize, &sk->wmem_alloc);
+		skb_orphan(skb);
 		ddp_dl->datalink_header(ddp_dl, skb, dev->dev_addr);
-		skb->sk = NULL;
 		skb->mac.raw=skb->data;
 		skb->h.raw = skb->data + ddp_dl->header_length + dev->hard_header_len;
 		skb_pull(skb,dev->hard_header_len);
@@ -1818,19 +1814,17 @@
 }
 
 
-static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+static int atalk_recvmsg(struct socket *sock, struct msghdr *msg, int size, 
+		int flags, struct scm_cookie *scm)
 {
-	atalk_socket *sk=(atalk_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	struct sockaddr_at *sat=(struct sockaddr_at *)msg->msg_name;
 	struct ddpehdr	*ddp = NULL;
 	int copied = 0;
 	struct sk_buff *skb;
 	int er = 0;
-	
-	if(addr_len)
-		*addr_len=sizeof(*sat);
 
-	skb=skb_recv_datagram(sk,flags,noblock,&er);
+	skb=skb_recv_datagram(sk,flags&~MSG_DONTWAIT,flags&MSG_DONTWAIT,&er);
 	if(skb==NULL)
 		return er;
 
@@ -1866,6 +1860,7 @@
 		sat->sat_addr.s_node=ddp->deh_snode;
 		sat->sat_addr.s_net=ddp->deh_snet;
 	}
+	msg->msg_namelen=sizeof(*sat);
 out:
 	skb_free_datagram(sk, skb);
 	return er ? er : (copied);
@@ -1877,13 +1872,6 @@
 	return -EOPNOTSUPP;
 }
 
-static int atalk_select(struct socket *sock , int sel_type, select_table *wait)
-{
-	atalk_socket *sk=(atalk_socket *)sock->data;
-	
-	return datagram_select(sk,sel_type,wait);
-}
-
 /*
  *	Appletalk ioctl calls.
  */
@@ -1891,7 +1879,7 @@
 static int atalk_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
 {
 	long amount=0;
-	atalk_socket *sk=(atalk_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	
 	switch(cmd)
 	{
@@ -1965,10 +1953,14 @@
 	return put_user(amount, (int *)arg);
 }
 
-static struct proto_ops atalk_proto_ops = {
+static struct net_proto_family atalk_family_ops = {
+	AF_APPLETALK,
+	atalk_create
+};
+
+static struct proto_ops atalk_dgram_ops = {
 	AF_APPLETALK,
 	
-	atalk_create,
 	atalk_dup,
 	atalk_release,
 	atalk_bind,
@@ -1976,7 +1968,7 @@
 	atalk_socketpair,
 	atalk_accept,
 	atalk_getname,
-	atalk_select,
+	datagram_select,
 	atalk_ioctl,
 	atalk_listen,
 	atalk_shutdown,
@@ -2038,7 +2030,7 @@
 
 void atalk_proto_init(struct net_proto *pro)
 {
-	(void) sock_register(atalk_proto_ops.family, &atalk_proto_ops);
+	(void) sock_register(&atalk_family_ops);
 	if ((ddp_dl = register_snap_client(ddp_snap_id, atalk_rcv)) == NULL)
 		printk(KERN_CRIT "Unable to register DDP with SNAP.\n");
 	
diff -u --recursive --new-file v2.1.14/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.14/linux/net/ax25/af_ax25.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ax25/af_ax25.c	Thu Dec 12 16:54:22 1996
@@ -4,7 +4,7 @@
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.10 or higher/ NET3.029
+ *	This code REQUIRES 2.1.15 or higher/ NET3.038
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -86,6 +86,8 @@
  *			Joerg(DL1BKE)		Moved BPQ Ethernet to seperate driver.
  *	AX.25 034	Jonathan(G4KLX)		2.1 changes
  *			Alan(GW4PTS)		Small POSIXisations
+ *	AX.25 035	Alan(GW4PTS)		Started fixing to the new
+ *						format.
  *
  *	To do:
  *		Restructure the ax25_rcv code to be cleaner/faster and
@@ -134,6 +136,8 @@
 
 ax25_cb *volatile ax25_list = NULL;
 
+static struct proto_ops ax25_proto_ops;
+
 /*
  *	ax25 -> ascii conversion
  */
@@ -386,6 +390,7 @@
  *	Find an AX.25 control block given both ends. It will only pick up
  *	floating AX.25 control blocks or non Raw socket bound control blocks.
  */
+ 
 static ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, struct device *dev)
 {
 	ax25_cb *s;
@@ -411,6 +416,7 @@
 /*
  *	Look for any matching address - RAW sockets can bind to arbitrary names
  */
+ 
 static struct sock *ax25_addr_match(ax25_address *addr)
 {
 	unsigned long flags;
@@ -440,8 +446,7 @@
 			if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
 				return;
 
-			copy->sk = sk;
-			atomic_add(copy->truesize, &sk->rmem_alloc);
+			skb_set_owner_r(copy, sk);
 			skb_queue_tail(&sk->receive_queue, copy);
 			if (!sk->dead)
 				sk->data_ready(sk, skb->len);
@@ -941,11 +946,8 @@
 	struct sock *sk;
 	int err, opt;
 
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 	
-	if (level == SOL_SOCKET)
-		return sock_setsockopt(sk, level, optname, optval, optlen);
-
 	if (level != SOL_AX25)
 		return -EOPNOTSUPP;
 
@@ -1029,10 +1031,7 @@
 	int val = 0;
 	int err; 
 
-	sk = (struct sock *)sock->data;
-	
-	if (level == SOL_SOCKET)
-		return sock_getsockopt(sk, level, optname, optval, optlen);
+	sk = (struct sock *)sock->sk;
 	
 	if (level != SOL_AX25)
 		return -EOPNOTSUPP;
@@ -1097,7 +1096,7 @@
 
 static int ax25_listen(struct socket *sock, int backlog)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 
 	if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) {
 		sk->max_ack_backlog = backlog;
@@ -1166,6 +1165,7 @@
 		return -ENOMEM;
 	}
 
+	sock->ops=&ax25_proto_ops;
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->back_log);
@@ -1188,8 +1188,8 @@
 	sk->error_report = def_callback1;
 
 	if (sock != NULL) {
-		sock->data = (void *)sk;
-		sk->sleep  = sock->wait;
+		sock->sk = sk;
+		sk->sleep  = &sock->wait;
 	}
 
 	ax25->sk          = sk;
@@ -1282,14 +1282,14 @@
 
 static int ax25_dup(struct socket *newsock, struct socket *oldsock)
 {
-	struct sock *sk = (struct sock *)oldsock->data;
+	struct sock *sk = (struct sock *)oldsock->sk;
 
 	return ax25_create(newsock, sk->protocol);
 }
 
 static int ax25_release(struct socket *sock, struct socket *peer)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 
 	if (sk == NULL) return 0;
 
@@ -1356,7 +1356,7 @@
 		ax25_destroy_socket(sk->protinfo.ax25);
 	}
 
-	sock->data = NULL;	
+	sock->sk = NULL;	
 	sk->socket = NULL;	/* Not used, but we should do this. **/
 
 	return 0;
@@ -1375,7 +1375,7 @@
 	struct device *dev;
 	ax25_address *call;
 	
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 	
 	if (sk->zapped == 0)
 		return -EINVAL;
@@ -1433,7 +1433,7 @@
 static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
 	int addr_len, int flags)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
 	int err;
 	
@@ -1562,12 +1562,12 @@
 	struct sock *newsk;
 	struct sk_buff *skb;
 
-	if (newsock->data)
-		sk_free(newsock->data);
+	if (newsock->sk)
+		sk_free(newsock->sk);
 
-	newsock->data = NULL;
+	newsock->sk = NULL;
 	
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 
 	if (sk->type != SOCK_SEQPACKET)
 		return -EOPNOTSUPP;
@@ -1602,7 +1602,7 @@
 	skb->sk = NULL;
 	kfree_skb(skb, FREE_READ);
 	sk->ack_backlog--;
-	newsock->data = newsk;
+	newsock->sk = newsk;
 
 	return 0;
 }
@@ -1614,7 +1614,7 @@
 	struct sock *sk;
 	unsigned char ndigi, i;
 	
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 	
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
@@ -1725,7 +1725,9 @@
 #endif
 
 			skb->arp = 1;
-			ax25_queue_xmit(skb, dev_out, SOPRI_NORMAL);
+			skb->dev = dev_out;
+			skb->priority = SOPRI_NORMAL;
+			ax25_queue_xmit(skb);
 		} else {
 			kfree_skb(skb, FREE_READ);
 		}
@@ -1781,8 +1783,7 @@
 						 */
 						skb_pull(skb, 2);
 						skb_queue_tail(&sk->receive_queue, skb);
-						skb->sk = sk;
-						atomic_add(skb->truesize, &sk->rmem_alloc);
+						skb_set_owner_r(skb, sk);
 						if (!sk->dead)
 							sk->data_ready(sk, skb->len);
 					}
@@ -1949,9 +1950,10 @@
 }
 
 
-static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
+static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, 
+	struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
 	int err;
 	struct sockaddr_ax25 sax;
@@ -1963,10 +1965,7 @@
 	int lv;
 	int addr_len = msg->msg_namelen;
 	
-	if (sk->err)
-		return sock_error(sk);
-
-	if (flags || msg->msg_control)
+	if ((msg->msg_flags&~MSG_DONTWAIT) || msg->msg_control)
 		return -EINVAL;
 
 	if (sk->zapped)
@@ -2034,11 +2033,11 @@
 	/* Assume the worst case */
 	size = len + 3 + size_ax25_addr(dp) + AX25_BPQ_HEADER_LEN;
 
-	if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+	if ((skb = sock_alloc_send_skb(sk, size, 0, 
+		msg->msg_flags&MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
 	skb->sk   = sk;
-	skb->free = 1;
 	skb->arp  = 1;
 
 	skb_reserve(skb, size - len);
@@ -2089,25 +2088,25 @@
 		*asmptr = LAPB_UI;
 
 		/* Datagram frames go straight out of the door as UI */
-		ax25_queue_xmit(skb, sk->protinfo.ax25->device, SOPRI_NORMAL);
+		skb->dev=sk->protinfo.ax25->device;
+		skb->priority=SOPRI_NORMAL;
+		ax25_queue_xmit(skb);
 
 		return len;
 	}
 		
 }
 
-static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, 
+	struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
 	int copied, length;
 	struct sk_buff *skb;
 	int er;
 	int dama;
-
-	if (addr_len != NULL)
-		*addr_len = sizeof(*sax);
-
+	
 	/*
 	 * 	This works for seqpacket too. The receiver has ordered the
 	 *	queue for us! We do one quick check first though
@@ -2116,7 +2115,7 @@
 		return -ENOTCONN;
 
 	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+	if ((skb = skb_recv_datagram(sk, flags, flags&MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
 	if (sk->protinfo.ax25->hdrincl) {
@@ -2137,15 +2136,11 @@
 
 	skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	
-	if (sax) {
+	if (sax) 
+	{
 		ax25_digi digi;
 		ax25_address dest;
 
-		if (addr_len == (int *)0)
-			return -EINVAL;
-		if (*addr_len != sizeof(struct sockaddr_ax25) && *addr_len != sizeof(struct full_sockaddr_ax25))
-			return -EINVAL;
-
 		ax25_parse_addr(skb->data, skb->len, NULL, &dest, &digi, NULL, &dama);
 
 		sax->sax25_family = AF_AX25;
@@ -2155,9 +2150,8 @@
 		sax->sax25_ndigis = digi.ndigi;
 		sax->sax25_call   = dest;
 
-		*addr_len = sizeof(struct sockaddr_ax25);
-
-		if (*addr_len == sizeof(struct full_sockaddr_ax25) && sax->sax25_ndigis != 0) {
+		if (sax->sax25_ndigis != 0) 
+		{
 			int ct           = 0;
 			struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax;
 
@@ -2166,10 +2160,10 @@
 				ct++;
 			}
 
-			*addr_len = sizeof(struct full_sockaddr_ax25);
 		}
 	}
 
+	msg->msg_namelen=sizeof(struct full_sockaddr_ax25);
 	skb_free_datagram(sk, skb);
 
 	return copied;
@@ -2181,16 +2175,9 @@
 	return -EOPNOTSUPP;
 }
 
-static int ax25_select(struct socket *sock , int sel_type, select_table *wait)
-{
-	struct sock *sk = (struct sock *)sock->data;
-
-	return datagram_select(sk, sel_type, wait);
-}
-
 static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	int err;
 	long amount = 0;
 
@@ -2349,10 +2336,15 @@
 	return(len);
 } 
 
+static struct net_proto_family ax25_family_ops = 
+{
+	AF_AX25,
+	ax25_create
+};
+
 static struct proto_ops ax25_proto_ops = {
 	AF_AX25,
 	
-	ax25_create,
 	ax25_dup,
 	ax25_release,
 	ax25_bind,
@@ -2360,7 +2352,7 @@
 	ax25_socketpair,
 	ax25_accept,
 	ax25_getname,
-	ax25_select,
+	datagram_select,
 	ax25_ioctl,
 	ax25_listen,
 	ax25_shutdown,
@@ -2434,7 +2426,7 @@
 
 void ax25_proto_init(struct net_proto *pro)
 {
-	sock_register(ax25_proto_ops.family, &ax25_proto_ops);
+	sock_register(&ax25_family_ops);
 	ax25_packet_type.type = htons(ETH_P_AX25);
 	dev_add_pack(&ax25_packet_type);	
 	register_netdevice_notifier(&ax25_dev_notifier);
@@ -2453,7 +2445,7 @@
 /*
  *	A small shim to dev_queue_xmit to add the KISS control byte.
  */
-void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+void ax25_queue_xmit(struct sk_buff *skb)
 {
 	unsigned char *ptr;
 	
@@ -2468,7 +2460,7 @@
 
 	ptr = skb_push(skb, 1);
 	*ptr++ = 0;			/* KISS */
-	dev_queue_xmit(skb, dev, pri);
+	dev_queue_xmit(skb);
 }
 
 /*
@@ -2530,21 +2522,20 @@
 	return -AX25_HEADER_LEN;	/* Unfinished header */
 }
 
-int ax25_rebuild_header(unsigned char *bp, struct device *dev, unsigned long dest, struct sk_buff *skb)
+int ax25_rebuild_header(struct sk_buff *skb)
 {
 	struct sk_buff *ourskb;
 	int mode;
+	unsigned char *bp=skb->data;
+	struct device *dev=skb->dev;
 
-  	if (arp_find(bp + 1, dest, dev, dev->pa_addr, skb))
+  	if (arp_find(bp + 1, skb))
   		return 1;
 
 	if (bp[16] == AX25_P_IP) {
 		mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
 		if (mode == 'V' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE))) {
 			/*
-			 *	This is a workaround to try to keep the device locking
-			 *	straight until skb->free=0 is abolished post 1.4.
-			 *
 			 *	We clone the buffer and release the original thereby
 			 *	keeping it straight
 			 *
@@ -2559,12 +2550,10 @@
 				return 1;
 			}
 
-			ourskb->sk = skb->sk;
-
 			if (ourskb->sk != NULL)
-				atomic_add(ourskb->truesize, &ourskb->sk->wmem_alloc);
+				skb_set_owner_w(ourskb, ourskb->sk);
 
-			dev_kfree_skb(skb, FREE_WRITE);
+			kfree_skb(skb, FREE_WRITE);
 
 			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
 
@@ -2587,7 +2576,9 @@
   	 *		  over ethernet. I don't know if this is valid, though.
   	 */
 	ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
-	ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
+	skb->dev=dev;
+	skb->priority=SOPRI_NORMAL;
+	ax25_queue_xmit(skb);
 
   	return 1;
 }	
diff -u --recursive --new-file v2.1.14/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v2.1.14/linux/net/ax25/ax25_in.c	Thu Dec 12 17:02:47 1996
+++ linux/net/ax25/ax25_in.c	Thu Dec 12 16:54:22 1996
@@ -89,14 +89,11 @@
 						return 1;
 					}
 
-					skbn->free = 1;
 					skbn->arp  = 1;
 					skbn->dev  = ax25->device;
 
-					if (ax25->sk != NULL) {
-						skbn->sk = ax25->sk;
-						atomic_add(skbn->truesize, &ax25->sk->rmem_alloc);
-					}
+					if (ax25->sk != NULL)
+						skb_set_owner_r(skbn, ax25->sk);
 
 					skb_reserve(skbn, AX25_MAX_HEADER_LEN);
 
diff -u --recursive --new-file v2.1.14/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v2.1.14/linux/net/ax25/ax25_out.c	Fri Nov 22 18:28:23 1996
+++ linux/net/ax25/ax25_out.c	Thu Dec 12 16:54:22 1996
@@ -99,19 +99,14 @@
 			if ((skbn = alloc_skb(mtu + 2 + frontlen, GFP_ATOMIC)) == NULL) {
 				restore_flags(flags);
 				printk(KERN_DEBUG "ax25_output: alloc_skb returned NULL\n");
-				if (skb_device_locked(skb))
-					skb_device_unlock(skb);
 				return;
 			}
 
-			skbn->sk   = skb->sk;
-			
 			if (skbn->sk)
-				atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+				skb_set_owner_w(skbn, skb->sk);
 			
 			restore_flags(flags);
 			
-			skbn->free = 1;
 			skbn->arp  = 1;
 
 			len = (mtu > skb->len) ? skb->len : mtu;
@@ -140,7 +135,6 @@
 			skb_queue_tail(&ax25->write_queue, skbn); /* Throw it on the queue */
 		}
 		
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	} else {
 		skb_queue_tail(&ax25->write_queue, skb);	  /* Throw it on the queue */
@@ -269,7 +263,6 @@
 
 	if (skb_headroom(skb) < size_ax25_addr(ax25->digipeat)) {
 		printk(KERN_CRIT "ax25_transmit_buffer: not enough room for digi-peaters\n");
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
@@ -278,8 +271,9 @@
 	build_ax25_addr(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
 
 	skb->arp = 1;
-
-	ax25_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
+	skb->dev=ax25->device;
+	skb->priority=SOPRI_NORMAL;
+	ax25_queue_xmit(skb);
 }
 
 /*
diff -u --recursive --new-file v2.1.14/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v2.1.14/linux/net/ax25/ax25_subr.c	Tue Nov 12 15:56:15 1996
+++ linux/net/ax25/ax25_subr.c	Thu Dec 12 16:54:23 1996
@@ -65,12 +65,10 @@
 	struct sk_buff *skb;
 
 	while ((skb = skb_dequeue(&ax25->write_queue)) != NULL) {
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	}
 
 	while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	}
 
@@ -98,7 +96,6 @@
 	if (ax25->va != nr) {
 		while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) {
 		        skb = skb_dequeue(&ax25->ack_queue);
-			skb->free = 1;
 			kfree_skb(skb, FREE_WRITE);
 			ax25->va = (ax25->va + 1) % ax25->modulus;
 			if (ax25->dama_slave)
@@ -215,10 +212,8 @@
 
 	skb_reserve(skb, AX25_BPQ_HEADER_LEN + size_ax25_addr(ax25->digipeat));
 
-	if (ax25->sk != NULL) {
-		skb->sk = ax25->sk;
-		atomic_add(skb->truesize, &ax25->sk->wmem_alloc);
-	}
+	if (ax25->sk != NULL)
+		skb_set_owner_w(skb, ax25->sk);
 
 	/* Assume a response - address structure for DTE */
 	if (ax25->modulus == MODULUS) {
@@ -240,8 +235,6 @@
 		}
 	}
 
-	skb->free = 1;
-
 	ax25_transmit_buffer(ax25, skb, type);
 }
 
@@ -279,9 +272,10 @@
 	dptr += build_ax25_addr(dptr, dest, src, &retdigi, C_RESPONSE, MODULUS);
 
 	skb->arp  = 1;
-	skb->free = 1;
+	skb->dev = dev;
+	skb->priority = SOPRI_NORMAL;
 
-	ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
+	ax25_queue_xmit(skb);
 }
 
 /*
@@ -531,13 +525,10 @@
 	if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
 		return;
 
-	skb->free = 1;
 	skb->arp = 1;
 
-	if (ax25->sk != NULL) {
-		skb->sk = ax25->sk;
-		atomic_add(skb->truesize, &ax25->sk->wmem_alloc);
-	}
+	if (ax25->sk != NULL)
+		skb_set_owner_w(skb, ax25->sk);
 
 	skb->protocol = htons(ETH_P_AX25);
 
@@ -546,7 +537,9 @@
 	*p++=cmd;
 	*p  =param;
 
-	dev_queue_xmit(skb, ax25->device, SOPRI_NORMAL);
+	skb->dev=ax25->device;
+	skb->priority=SOPRI_NORMAL;
+	dev_queue_xmit(skb);
 }
 
 void ax25_dama_on(ax25_cb *ax25)
diff -u --recursive --new-file v2.1.14/linux/net/bridge/br.c linux/net/bridge/br.c
--- v2.1.14/linux/net/bridge/br.c	Tue Nov 19 15:53:59 1996
+++ linux/net/bridge/br.c	Thu Dec 12 16:54:23 1996
@@ -14,6 +14,7 @@
  *	2 of the License, or (at your option) any later version.
  *
  *	Fixes:
+ *		Yury Shevchuk	:	Bridge with non bridging ports
  *
  *	Todo:
  *		Don't bring up devices automatically. Start ports disabled
@@ -1202,7 +1203,13 @@
 				return(0);	/* pass frame up our stack (this will */
 						/* happen in net_bh() in dev.c) */
 			}
-			/* ok, forward this frame... */
+			/* Now this frame came from one of bridged
+			   ports, and it appears to be not for me;
+			   this means we should attempt to forward it.
+			   But actually this frame can still be for me
+			   [as well] if it is destined to one of our
+			   multicast groups.  br_forward() will not
+			   consume the frame if this is the case */
 			return(br_forward(skb, port));
 		default:
 			printk(KERN_DEBUG "br_receive_frame: port [%i] unknown state [%i]\n",
@@ -1238,6 +1245,11 @@
 	if (skb->dev->flags & IFF_LOOPBACK)
 		return(0);
 
+	/* if bridging is not enabled on the port we are going to send
+           to, we have nothing to do with this frame, hands off */
+	if (! find_port(skb->dev))
+		return(0);
+
 	skb->h.raw = skb->data;
 	port = 0;	/* an impossible port */	
 	if (br_stats.flags & BR_DEBUG)
@@ -1331,6 +1343,10 @@
 }
 
 /*
+ * Forward the frame SKB to proper port[s].  PORT is the port that the
+ * frame has come from; we will not send the frame back there.  PORT == 0
+ * means we have been called from br_tx_fr(), not from br_receive_frame().
+ *
  * this routine returns 1 if it consumes the frame, 0
  * if not...
  */
diff -u --recursive --new-file v2.1.14/linux/net/core/Makefile linux/net/core/Makefile
--- v2.1.14/linux/net/core/Makefile	Tue Apr  9 14:36:31 1996
+++ linux/net/core/Makefile	Thu Dec 12 16:54:23 1996
@@ -9,7 +9,8 @@
 
 O_TARGET := core.o
 
-O_OBJS	:= sock.o skbuff.o iovec.o datagram.o sysctl_net_core.o
+O_OBJS	:= sock.o skbuff.o iovec.o datagram.o sysctl_net_core.o dst.o scm.o \
+	neighbour.o
 
 ifdef CONFIG_NET
 
diff -u --recursive --new-file v2.1.14/linux/net/core/datagram.c linux/net/core/datagram.c
--- v2.1.14/linux/net/core/datagram.c	Tue Nov 19 15:53:59 1996
+++ linux/net/core/datagram.c	Thu Dec 12 16:54:23 1996
@@ -132,7 +132,7 @@
 		cli();
 		skb=skb_peek(&sk->receive_queue);
 		if(skb!=NULL)
-			skb->users++;
+			atomic_inc(&skb->users);
 		restore_flags(flags);
 		if(skb==NULL)		/* shouldn't happen but .. */
 			goto restart;
@@ -141,7 +141,6 @@
 	skb = skb_dequeue(&sk->receive_queue);
 	if (!skb)	/* Avoid race if someone beats us to the data */
 		goto restart;
-	skb->users++;
 	return skb;
 
 no_packet:
@@ -152,18 +151,7 @@
 
 void skb_free_datagram(struct sock * sk, struct sk_buff *skb)
 {
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	skb->users--;
-	if(skb->users <= 0) {
-		/* See if it needs destroying */
-		/* Been dequeued by someone - ie it's read */
-		if(!skb->next && !skb->prev)
-			kfree_skb(skb,FREE_READ);
-	}
-	restore_flags(flags);
+	kfree_skb(skb, FREE_READ);
 	release_sock(sk);
 }
 
@@ -204,8 +192,10 @@
  *	Now does seqpacket.
  */
 
-int datagram_select(struct sock *sk, int sel_type, select_table *wait)
+int datagram_select(struct socket *sock, int sel_type, select_table *wait)
 {
+	struct sock *sk = sock->sk;
+
 	if (sk->err)
 		return 1;
 	switch(sel_type)
diff -u --recursive --new-file v2.1.14/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.14/linux/net/core/dev.c	Tue Nov 19 15:53:59 1996
+++ linux/net/core/dev.c	Thu Dec 12 16:54:23 1996
@@ -84,6 +84,14 @@
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif	/* CONFIG_NET_RADIO */
+
+/*
+ *	The list of devices, that are able to output.
+ */
+static struct device *dev_up_base;
 
 /*
  *	The list of packet types we will receive (as opposed to discard)
@@ -97,7 +105,7 @@
  *	Device list lock
  */
  
-int dev_lockct=0;
+atomic_t dev_lockct=0;
  
 /*
  *	Our notifier list
@@ -209,7 +217,7 @@
  
 #ifdef CONFIG_KERNELD
 
-extern __inline__ void dev_load(const char *name)
+void dev_load(const char *name)
 {
 	if(!dev_get(name)) {
 #ifdef CONFIG_NET_ALIAS
@@ -230,7 +238,7 @@
  
 int dev_open(struct device *dev)
 {
-	int ret = -ENODEV;
+	int ret = 0;
 
 	/*
 	 *	Call device private open method
@@ -245,11 +253,23 @@
 	if (ret == 0) 
 	{
 		dev->flags |= (IFF_UP | IFF_RUNNING);
+		dev->hash = dev_hash_name(dev->name);
 		/*
 		 *	Initialise multicasting status 
 		 */
 		dev_mc_upload(dev);
 		notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+#ifdef CONFIG_NET_ALIAS
+		if (!net_alias_is(dev) || dev->tx_queue_len)
+#else
+		if (dev->tx_queue_len)
+#endif
+		{
+			cli();
+			dev->next_up = dev_up_base;
+			dev_up_base = dev;
+			sti();
+		}
 	}
 	return(ret);
 }
@@ -262,6 +282,7 @@
 int dev_close(struct device *dev)
 {
 	int ct=0;
+	struct device **devp;
 
 	/*
 	 *	Call the device specific close. This cannot fail.
@@ -293,10 +314,20 @@
 	{
 		struct sk_buff *skb;
 		while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
-			if(skb->free)
-				kfree_skb(skb,FREE_WRITE);
+			kfree_skb(skb,FREE_WRITE);
 		ct++;
 	}
+
+	devp = &dev_up_base;
+	while (*devp)
+	{
+		if (*devp == dev)
+		{
+			*devp = dev->next_up;
+			break;
+		}
+		devp = &(*devp)->next_up;
+	}
 	return(0);
 }
 
@@ -332,12 +363,9 @@
 				/* at the front or the back of the	*/
 				/* queue - front is a retransmit try	*/
 
-	if(pri>=0 && !skb_device_locked(skb))
-		skb_device_lock(skb);	/* Shove a lock on the frame */
 #if CONFIG_SKB_CHECK 
 	IS_SKB(skb);
 #endif    
-	skb->dev = dev;
 
 	/*
 	 *	Negative priority is used to flag a frame that is being pulled from the
@@ -354,34 +382,12 @@
 #ifdef CONFIG_NET_DEBUG
 	if (pri >= DEV_NUMBUFFS) 
 	{
-		printk(KERN_WARNING "bad priority in dev_queue_xmit.\n");
+		printk(KERN_WARNING "bad priority in do_dev_queue_xmit.\n");
 		pri = 1;
 	}
 #endif
 
 	/*
-	 *	If the address has not been resolved. Call the device header rebuilder.
-	 *	This can cover all protocols and technically not just ARP either.
-	 */
-	 
-	if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
-		return;
-	}
-
-	/*
-	 *
-	 * 	If dev is an alias, switch to its main device.
-	 *	"arp" resolution has been made with alias device, so
-	 *	arp entries refer to alias, not main.
-	 *
-	 */
-
-#ifdef CONFIG_NET_ALIAS
-	if (net_alias_is(dev))
-	  	skb->dev = dev = net_alias_main_dev(dev);
-#endif
-
-	/*
 	 *	If we are bridging and this is directly generated output
 	 *	pass the frame via the bridge.
 	 */
@@ -424,8 +430,9 @@
 					struct sk_buff *skb2;
 					if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
 						break;
-					skb2->h.raw = skb2->data + dev->hard_header_len;
 					skb2->mac.raw = skb2->data;
+					skb2->nh.raw =
+					skb2->h.raw = skb2->data + dev->hard_header_len;
 					ptype->func(skb2, skb->dev, ptype);
 				}
 			}
@@ -433,10 +440,8 @@
 
 		if (skb_queue_len(list)) {
 			cli();
-			skb_device_unlock(skb);		/* Buffer is on the device queue and can be freed safely */
 			__skb_queue_tail(list, skb);
 			skb = __skb_dequeue(list);
-			skb_device_lock(skb);		/* New buffer needs locking down */
 			restore_flags(flags);
 		}
 	}
@@ -452,18 +457,68 @@
 	 *	no longer device locked (it can be freed safely from the device queue)
 	 */
 	cli();
-	skb_device_unlock(skb);
 	__skb_queue_head(list,skb);
 	restore_flags(flags);
 }
 
-void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+int dev_queue_xmit(struct sk_buff *skb)
 {
+	struct device *dev = skb->dev;
+
 	start_bh_atomic();
-	do_dev_queue_xmit(skb, dev, pri);
+
+
+#if CONFIG_SKB_CHECK 
+	IS_SKB(skb);
+#endif    
+
+	/*
+	 *	If the address has not been resolved. Call the device header rebuilder.
+	 *	This can cover all protocols and technically not just ARP either.
+	 */
+	 
+	if (!skb->arp) {
+		if (dev->rebuild_header) {
+			if (dev->rebuild_header(skb)) {
+				end_bh_atomic();
+				return 0;
+			}
+		} else
+			printk("%s: !skb->arp & !rebuild_header!\n", dev->name);
+	}
+
+	/*
+	 *
+	 * 	If dev is an alias, switch to its main device.
+	 *	"arp" resolution has been made with alias device, so
+	 *	arp entries refer to alias, not main.
+	 *
+	 */
+
+#ifdef CONFIG_NET_ALIAS
+	if (net_alias_is(dev))
+	  	skb->dev = dev = net_alias_main_dev(dev);
+#endif
+
+	do_dev_queue_xmit(skb, dev, skb->priority);
 	end_bh_atomic();
+	return 0;
 }
 
+void dev_loopback_xmit(struct sk_buff *skb)
+{
+	struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
+	if (newskb==NULL)
+		return;
+
+	skb_pull(newskb, newskb->nh.raw - newskb->data);
+	newskb->ip_summed = CHECKSUM_UNNECESSARY;
+	if (newskb->dst==NULL)
+		printk("BUG: packet without dst looped back 1\n");
+	netif_rx(newskb);
+}
+
+
 /*
  *	Receive a packet from a device driver and queue it for the upper
  *	(protocol) levels.  It always succeeds. This is the recommended 
@@ -481,7 +536,6 @@
 	 */
 
 	skb->sk = NULL;
-	skb->free = 1;
 	if(skb->stamp.tv_sec==0)
 		get_fast_time(&skb->stamp);
 
@@ -526,7 +580,7 @@
 {
 	struct device *dev;
 
-	for (dev = dev_base; dev != NULL; dev = dev->next)
+	for (dev = dev_up_base; dev != NULL; dev = dev->next_up)
 	{
 		if (dev->flags != 0 && !dev->tbusy) {
 			/*
@@ -557,6 +611,7 @@
 	struct packet_type *ptype;
 	struct packet_type *pt_prev;
 	unsigned short type;
+	int nit = 301;
 
 	/*
 	 *	Can we send anything now? We want to clear the
@@ -591,6 +646,18 @@
 		__skb_unlink(skb, &backlog);
   		backlog_size--;
 		sti();
+
+		/*
+		 *	We do not want to spin in net_bh infinitely. --ANK
+		 */
+		if (--nit <= 0)
+		{
+			if (nit == 0)
+				printk(KERN_WARNING "net_bh: too many loops, dropping...\n");
+			kfree_skb(skb, FREE_WRITE);
+			continue;
+		}
+		
 		
 
 #ifdef CONFIG_BRIDGE
@@ -629,10 +696,11 @@
 	 	 *	Bump the pointer to the next structure.
 		 * 
 		 *	On entry to the protocol layer. skb->data and
-		 *	skb->h.raw point to the MAC and encapsulated data
+		 *	skb->nh.raw point to the MAC and encapsulated data
 		 */
 
-		skb->h.raw = skb->data;
+		/* XXX until we figure out every place to modify.. */
+		skb->h.raw = skb->nh.raw = skb->data;
 
 		/*
 		 * 	Fetch the packet protocol ID. 
@@ -737,7 +805,10 @@
 	 */
 
 #ifdef CONFIG_NET_ALIAS
-	if (net_alias_is(dev)) return;
+	if (net_alias_is(dev)) {
+		printk("net alias %s transmits\n", dev->name);
+		return;
+	}
 #endif
 	head = dev->buffs;
 	save_flags(flags);
@@ -757,7 +828,6 @@
 			/*
 			 *	Stop anyone freeing the buffer while we retransmit it
 			 */
-			skb_device_lock(skb);
 			restore_flags(flags);
 			/*
 			 *	Feed them to the output stage and if it fails
@@ -931,19 +1001,87 @@
 #endif	/* CONFIG_PROC_FS */
 
 
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Print one entry of /proc/net/wireless
+ * This is a clone of /proc/net/dev (just above)
+ */
+static int sprintf_wireless_stats(char *buffer, struct device *dev)
+{
+	/* Get stats from the driver */
+	struct iw_statistics *stats = (dev->get_wireless_stats ?
+				       dev->get_wireless_stats(dev) :
+				       (struct iw_statistics *) NULL);
+	int size;
+
+	if(stats != (struct iw_statistics *) NULL)
+		size = sprintf(buffer,
+			       "%6s: %02x  %3d%c %3d%c  %3d%c %5d %5d %5d\n",
+			       dev->name,
+			       stats->status,
+			       stats->qual.qual,
+			       stats->qual.updated & 1 ? '.' : ' ',
+			       stats->qual.level,
+			       stats->qual.updated & 2 ? '.' : ' ',
+			       stats->qual.noise,
+			       stats->qual.updated & 3 ? '.' : ' ',
+			       stats->discard.nwid,
+			       stats->discard.crypt,
+			       stats->discard.misc);
+	else
+		size = 0;
+
+	return size;
+}
+
 /*
- *	This checks bitmasks for the ioctl calls for devices.
+ * Print info for /proc/net/wireless (print all entries)
+ * This is a clone of /proc/net/dev (just above)
  */
- 
-static inline int bad_mask(unsigned long mask, unsigned long addr)
+int dev_get_wireless_info(char * buffer, char **start, off_t offset,
+			  int length, int dummy)
 {
-	if (addr & (mask = ~mask))
-		return 1;
-	mask = ntohl(mask);
-	if (mask & (mask+1))
-		return 1;
-	return 0;
+	int		len = 0;
+	off_t		begin = 0;
+	off_t		pos = 0;
+	int		size;
+	
+	struct device *	dev;
+
+	size = sprintf(buffer,
+		       "Inter-|sta|  Quality       |  Discarded packets\n"
+		       " face |tus|link level noise| nwid crypt  misc\n");
+	
+	pos+=size;
+	len+=size;
+
+	for(dev = dev_base; dev != NULL; dev = dev->next) 
+	{
+		size = sprintf_wireless_stats(buffer+len, dev);
+		len+=size;
+		pos=begin+len;
+
+		if(pos < offset)
+		{
+			len=0;
+			begin=pos;
+		}
+		if(pos > offset + length)
+			break;
+	}
+
+	*start = buffer + (offset - begin);	/* Start of wanted data */
+	len -= (offset - begin);		/* Start slop */
+	if(len > length)
+		len = length;		/* Ending slop */
+
+	return len;
 }
+#endif	/* CONFIG_PROC_FS */
+#endif	/* CONFIG_NET_RADIO */
+
 
 /*
  *	Perform the SIOCxIFxxx calls. 
@@ -1004,6 +1142,7 @@
 				 */
 				
 				dev_lock_wait();
+				dev_lock_list();
 				
 				/*
 				 *	Set the flags on our device.
@@ -1044,134 +1183,21 @@
 				 */				
 
 				dev_mc_upload(dev);
-			}
-			break;
-		
-		case SIOCGIFADDR:	/* Get interface address (and family) */
-			if(ifr.ifr_addr.sa_family==AF_UNSPEC)
-			{
-				memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
-				ifr.ifr_hwaddr.sa_family=dev->type;			
-				goto rarok;
-			}
-			else
-			{
-				(*(struct sockaddr_in *)
-					  &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
-				(*(struct sockaddr_in *)
-					  &ifr.ifr_addr).sin_family = dev->family;
-				(*(struct sockaddr_in *)
-					  &ifr.ifr_addr).sin_port = 0;
-			}
-			goto rarok;
-	
-		case SIOCSIFADDR:	/* Set interface address (and family) */
-
-			/*
-			 *	BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
-			 *	physical address. We can cope with this now.
-			 */
-			
-			if(ifr.ifr_addr.sa_family==AF_UNSPEC)
-			{
-				if(dev->set_mac_address==NULL)
-					return -EOPNOTSUPP;
-				ret=dev->set_mac_address(dev,&ifr.ifr_addr);
-			}
-			else
-			{
-				u32 new_pa_addr = (*(struct sockaddr_in *)
-					 &ifr.ifr_addr).sin_addr.s_addr;
-				u16 new_family = ifr.ifr_addr.sa_family;
-
-				if (new_family == dev->family &&
-				    new_pa_addr == dev->pa_addr) {
-					ret =0;
-					break;
+				if ((dev->flags&IFF_UP) && ((old_flags^dev->flags)&~(IFF_UP|IFF_RUNNING|IFF_PROMISC)))
+				{
+					printk(KERN_DEBUG "SIFFL %s(%s)\n", dev->name, current->comm);
+					notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
 				}
-				if (dev->flags & IFF_UP)
-					notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
-
-				/*
-				 *	if dev is an alias, must rehash to update
-				 *	address change
-				 */
-
-#ifdef CONFIG_NET_ALIAS
-			  	if (net_alias_is(dev))
-				    	net_alias_dev_rehash(dev ,&ifr.ifr_addr);
-#endif
-				dev->pa_addr = new_pa_addr;
-				dev->family = new_family;
-			
-#ifdef CONFIG_INET	
-				/* This is naughty. When net-032e comes out It wants moving into the net032
-				   code not the kernel. Till then it can sit here (SIGH) */		
-				if (!dev->pa_mask)
-					dev->pa_mask = ip_get_mask(dev->pa_addr);
-#endif			
-				if (!dev->pa_brdaddr)
-					dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
-				if (dev->flags & IFF_UP)
-					notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
-				ret = 0;
-			}
-			break;
-			
-		case SIOCGIFBRDADDR:	/* Get the broadcast address */
-			(*(struct sockaddr_in *)
-				&ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
-			(*(struct sockaddr_in *)
-				&ifr.ifr_broadaddr).sin_family = dev->family;
-			(*(struct sockaddr_in *)
-				&ifr.ifr_broadaddr).sin_port = 0;
-			goto rarok;
-
-		case SIOCSIFBRDADDR:	/* Set the broadcast address */
-			dev->pa_brdaddr = (*(struct sockaddr_in *)
-				&ifr.ifr_broadaddr).sin_addr.s_addr;
-			ret = 0;
-			break;
-			
-		case SIOCGIFDSTADDR:	/* Get the destination address (for point-to-point links) */
-			(*(struct sockaddr_in *)
-				&ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
-			(*(struct sockaddr_in *)
-				&ifr.ifr_dstaddr).sin_family = dev->family;
-			(*(struct sockaddr_in *)
-				&ifr.ifr_dstaddr).sin_port = 0;
-			goto rarok;
-	
-		case SIOCSIFDSTADDR:	/* Set the destination address (for point-to-point links) */
-			dev->pa_dstaddr = (*(struct sockaddr_in *)
-				&ifr.ifr_dstaddr).sin_addr.s_addr;
-			ret = 0;
-			break;
-			
-		case SIOCGIFNETMASK:	/* Get the netmask for the interface */
-			(*(struct sockaddr_in *)
-				&ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
-			(*(struct sockaddr_in *)
-				&ifr.ifr_netmask).sin_family = dev->family;
-			(*(struct sockaddr_in *)
-				&ifr.ifr_netmask).sin_port = 0;
-			goto rarok;
-
-		case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
-			{
-				unsigned long mask = (*(struct sockaddr_in *)
-					&ifr.ifr_netmask).sin_addr.s_addr;
-				ret = -EINVAL;
-				/*
-				 *	The mask we set must be legal.
-				 */
-				if (bad_mask(mask,0))
-					break;
-				dev->pa_mask = mask;
-				ret = 0;
+				if ((dev->flags^old_flags)&IFF_PROMISC) {
+					if (dev->flags&IFF_PROMISC)
+						printk(KERN_INFO "%s enters promiscuous mode.\n", dev->name);
+					else
+						printk(KERN_INFO "%s leave promiscuous mode.\n", dev->name);
+				}
+				dev_unlock_list();
 			}
 			break;
-			
+		
 		case SIOCGIFMETRIC:	/* Get the metric on the interface (currently unused) */
 			
 			ifr.ifr_metric = dev->metric;
@@ -1188,6 +1214,11 @@
 	
 		case SIOCSIFMTU:	/* Set the MTU of a device */
 		
+			if (ifr.ifr_mtu == dev->mtu) {
+				ret = 0;
+				break;
+			}
+
 			/*
 			 *	MTU must be positive.
 			 */
@@ -1202,6 +1233,10 @@
 				dev->mtu = ifr.ifr_mtu;
 				ret = 0;
 			}
+			if (!ret && (dev->flags&IFF_UP)) {
+				printk(KERN_DEBUG "SIFMTU %s(%s)\n", dev->name, current->comm);
+				notifier_call_chain(&netdev_chain, NETDEV_CHANGE, dev);
+			}
 			break;
 	
 		case SIOCGIFMEM:	/* Get the per device memory space. We can add this but currently
@@ -1273,7 +1308,27 @@
 				}
 				break;
 			}
-			
+
+#ifdef CONFIG_NET_RADIO
+			if((getset >= SIOCIWFIRST) && (getset <= SIOCIWLAST))
+			{
+				if(dev->do_ioctl==NULL)
+					return -EOPNOTSUPP;
+				/* Perform the ioctl */
+				ret=dev->do_ioctl(dev, &ifr, getset);
+				/* If return args... */
+				if(IW_IS_GET(getset))
+				{
+					if (copy_to_user(arg, &ifr,
+							 sizeof(struct ifreq)))
+					{
+						ret = -EFAULT;
+					}
+				}
+				break;
+			}
+#endif	/* CONFIG_NET_RADIO */
+
 			ret = -EINVAL;
 	}
 	return(ret);
@@ -1306,10 +1361,6 @@
 		 */
 		 
 		case SIOCGIFFLAGS:
-		case SIOCGIFADDR:
-		case SIOCGIFDSTADDR:
-		case SIOCGIFBRDADDR:
-		case SIOCGIFNETMASK:
 		case SIOCGIFMETRIC:
 		case SIOCGIFMTU:
 		case SIOCGIFMEM:
@@ -1324,10 +1375,6 @@
 		 */
 		 
 		case SIOCSIFFLAGS:
-		case SIOCSIFADDR:
-		case SIOCSIFDSTADDR:
-		case SIOCSIFBRDADDR:
-		case SIOCSIFNETMASK:
 		case SIOCSIFMETRIC:
 		case SIOCSIFMTU:
 		case SIOCSIFMEM:
@@ -1351,6 +1398,14 @@
 			   (cmd <= (SIOCDEVPRIVATE + 15))) {
 				return dev_ifsioc(arg, cmd);
 			}
+#ifdef CONFIG_NET_RADIO
+			if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
+			{
+				if((IW_IS_SET(cmd)) && (!suser()))
+					return -EPERM;
+				return dev_ifsioc(arg, cmd);
+			}
+#endif	/* CONFIG_NET_RADIO */
 			return -EINVAL;
 	}
 }
@@ -1363,7 +1418,6 @@
  *
  */
 extern int lance_init(void);
-extern int ni65_init(void);
 extern int pi_init(void);
 extern int bpq_init(void);
 extern int scc_init(void);
@@ -1382,6 +1436,17 @@
 };
 #endif
 
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_net_wireless = {
+	PROC_NET_WIRELESS, 8, "wireless",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	dev_get_wireless_info
+};
+#endif	/* CONFIG_PROC_FS */
+#endif	/* CONFIG_NET_RADIO */
+
 int net_dev_init(void)
 {
 	struct device *dev, **dp;
@@ -1473,6 +1538,12 @@
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_dev);
 #endif
+
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_net_wireless);
+#endif	/* CONFIG_PROC_FS */
+#endif	/* CONFIG_NET_RADIO */
 
 	/*	
 	 *	Initialise net_alias engine 
diff -u --recursive --new-file v2.1.14/linux/net/core/dev_mcast.c linux/net/core/dev_mcast.c
--- v2.1.14/linux/net/core/dev_mcast.c	Tue Oct 29 19:58:49 1996
+++ linux/net/core/dev_mcast.c	Thu Dec 12 16:54:23 1996
@@ -23,6 +23,7 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -42,6 +43,9 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/arp.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
 
 
 /*
@@ -67,6 +71,10 @@
 	    
 	if(!(dev->flags&IFF_UP))
 		return;
+
+#ifdef CONFIG_NET_ALIAS
+	dev = net_alias_main_dev(dev);
+#endif
 		
 	/*
 	 *	Devices with no set multicast don't get set 
@@ -85,6 +93,9 @@
 void dev_mc_delete(struct device *dev, void *addr, int alen, int all)
 {
 	struct dev_mc_list **dmi;
+#ifdef CONFIG_NET_ALIAS
+	dev = net_alias_main_dev(dev);
+#endif
 	for(dmi=&dev->mc_list;*dmi!=NULL;dmi=&(*dmi)->next)
 	{
 		if(memcmp((*dmi)->dmi_addr,addr,(*dmi)->dmi_addrlen)==0 && alen==(*dmi)->dmi_addrlen)
@@ -108,6 +119,9 @@
 void dev_mc_add(struct device *dev, void *addr, int alen, int newonly)
 {
 	struct dev_mc_list *dmi;
+#ifdef CONFIG_NET_ALIAS
+	dev = net_alias_main_dev(dev);
+#endif
 	for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
 	{
 		if(memcmp(dmi->dmi_addr,addr,dmi->dmi_addrlen)==0 && dmi->dmi_addrlen==alen)
@@ -135,6 +149,10 @@
 
 void dev_mc_discard(struct device *dev)
 {
+#ifdef CONFIG_NET_ALIAS
+	if (net_alias_is(dev))
+		return;
+#endif
 	while(dev->mc_list!=NULL)
 	{
 		struct dev_mc_list *tmp=dev->mc_list;
diff -u --recursive --new-file v2.1.14/linux/net/core/dst.c linux/net/core/dst.c
--- v2.1.14/linux/net/core/dst.c	Thu Jan  1 02:00:00 1970
+++ linux/net/core/dst.c	Thu Dec 12 16:54:23 1996
@@ -0,0 +1,108 @@
+/*
+ * net/dst.c	Protocol independent destination cache.
+ *
+ * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <net/dst.h>
+
+struct dst_entry * dst_garbage_list;
+atomic_t	dst_total;
+
+static unsigned long dst_gc_timer_expires;
+static unsigned long dst_gc_timer_inc = DST_GC_MAX;
+static void dst_run_gc(unsigned long);
+
+static struct timer_list dst_gc_timer =
+	{ NULL, NULL, DST_GC_MIN, 0L, dst_run_gc };
+
+#if RT_CACHE_DEBUG >= 2
+atomic_t hh_count;
+#endif
+
+static void dst_run_gc(unsigned long dummy)
+{
+	int    delayed = 0;
+	struct dst_entry * dst, **dstp;
+
+	del_timer(&dst_gc_timer);
+	dstp = &dst_garbage_list;
+	while ((dst = *dstp) != NULL) {
+		if (dst->refcnt) {
+			dstp = &dst->next;
+			delayed++;
+			continue;
+		}
+		*dstp = dst->next;
+		dst_destroy(dst);
+	}
+	if (!dst_garbage_list) {
+		dst_gc_timer_inc = DST_GC_MAX;
+		return;
+	}
+	if ((dst_gc_timer_expires += dst_gc_timer_inc) > DST_GC_MAX)
+		dst_gc_timer_expires = DST_GC_MAX;
+	dst_gc_timer_inc += DST_GC_INC;
+	dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
+#if RT_CACHE_DEBUG >= 2
+	printk("dst_total: %d/%d/%d %ld\n", dst_total, delayed, hh_count, dst_gc_timer_expires);
+#endif
+	add_timer(&dst_gc_timer);
+}
+
+static int dst_discard(struct sk_buff *skb)
+{
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+static int dst_blackhole(struct sk_buff *skb)
+{
+	kfree_skb(skb, FREE_WRITE);
+	return 0;
+}
+
+void * dst_alloc(int size, struct dst_ops * ops)
+{
+	struct dst_entry * dst;
+	dst = kmalloc(size, GFP_ATOMIC);
+	if (!dst)
+		return NULL;
+	memset(dst, 0, size);
+	dst->ops = ops;
+	dst->refcnt = 1;
+	dst->lastuse = jiffies;
+	dst->input = dst_discard;
+	dst->output = dst_blackhole;
+	atomic_inc(&dst_total);
+	return dst;
+}
+
+void __dst_free(struct dst_entry * dst)
+{
+	start_bh_atomic();
+	dst->obsolete = 1;
+	dst->next = dst_garbage_list;
+	dst_garbage_list = dst;
+	if (dst_gc_timer_inc > DST_GC_INC) {
+		del_timer(&dst_gc_timer);
+		dst_gc_timer_inc = DST_GC_INC;
+		dst_gc_timer_expires = DST_GC_MIN;
+		dst_gc_timer.expires = jiffies + dst_gc_timer_expires;
+		add_timer(&dst_gc_timer);
+	}
+	end_bh_atomic();
+}
diff -u --recursive --new-file v2.1.14/linux/net/core/iovec.c linux/net/core/iovec.c
--- v2.1.14/linux/net/core/iovec.c	Tue Nov 19 15:53:59 1996
+++ linux/net/core/iovec.c	Thu Dec 12 16:54:23 1996
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
+#include <linux/malloc.h>
 #include <linux/net.h>
 #include <linux/in6.h>
 #include <asm/uaccess.h>
@@ -42,7 +43,7 @@
 	int len=0;
 	int ct;
 	
-	if(m->msg_name!=NULL)
+	if(m->msg_namelen)
 	{
 		if(mode==VERIFY_READ)
 		{
@@ -56,13 +57,22 @@
 		if(err<0)
 			return err;
 		m->msg_name = address;
-	}
-	
-	if(m->msg_control!=NULL)
+	} else
+		m->msg_name = NULL;
+
+	if(m->msg_controllen)
 	{
 		err=verify_area(mode, m->msg_control, m->msg_controllen);
 		if(err)
 			return err;
+	} else
+		m->msg_control = NULL;
+
+	if (m->msg_iovlen > UIO_FASTIOV)
+	{
+		iov = kmalloc(m->msg_iovlen*sizeof(struct iovec), GFP_KERNEL);
+		if (!iov)
+			return -ENOMEM;
 	}
 	
 	for(ct=0;ct<m->msg_iovlen;ct++)
@@ -70,11 +80,19 @@
 		err = copy_from_user(&iov[ct], &m->msg_iov[ct],
 				     sizeof(struct iovec));
 		if (err)
+		{
+			if (m->msg_iovlen > UIO_FASTIOV)
+				kfree(iov);
 			return err;
+		}
 		
 		err = verify_area(mode, iov[ct].iov_base, iov[ct].iov_len);
 		if(err)
+		{
+			if (m->msg_iovlen > UIO_FASTIOV)
+				kfree(iov);
 			return err;
+		}
 		len+=iov[ct].iov_len;
 	}
 	m->msg_iov=&iov[0];
diff -u --recursive --new-file v2.1.14/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.1.14/linux/net/core/neighbour.c	Thu Jan  1 02:00:00 1970
+++ linux/net/core/neighbour.c	Thu Dec 12 16:54:23 1996
@@ -0,0 +1,293 @@
+/*
+ *	Generic address resultion entity
+ *
+ *	Authors:
+ *	Pedro Roque	<roque@di.fc.ul.pt>
+ *
+ *	This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/socket.h>
+#include <linux/sched.h>
+#include <net/neighbour.h>
+
+
+static void neigh_purge_send_q(struct neighbour *neigh);
+
+void neigh_table_init(struct neigh_table *tbl, struct neigh_ops *ops, int size)
+{
+	int bmemlen;
+
+	memset(tbl, 0, sizeof(struct neigh_table));
+	
+	tbl->tbl_size = size;
+	tbl->neigh_ops = ops;
+	
+	/*
+	 *	This should only be called on initialization
+	 *	And interrupts should be on
+	 */
+
+	bmemlen = size * sizeof(struct neighbour *);
+	tbl->hash_buckets = kmalloc(bmemlen, GFP_KERNEL);
+
+	if (tbl->hash_buckets == NULL)
+	{
+		panic("unable to initialize neigh_table");
+	}
+
+	memset(tbl->hash_buckets, 0, bmemlen);
+}
+
+struct neighbour *neigh_alloc(int size, int priority)
+{
+	struct neighbour *neigh;
+	
+	neigh = kmalloc(size, priority);
+	if (neigh == NULL)
+	{
+		return NULL;
+	}
+
+	memset(neigh, 0, size);
+	
+	skb_queue_head_init(&neigh->arp_queue);
+
+	return neigh;
+}
+
+void neigh_queue_ins(struct neigh_table *tbl, struct neighbour *neigh)
+{
+	struct neighbour *entry, **head;
+	entry = tbl->request_queue;
+
+	head = &tbl->request_queue;
+	
+	for (; entry; entry = entry->next)
+	{
+		head = &entry->next;
+	}
+
+	*head = neigh;
+	neigh->next = neigh->prev = NULL;
+}
+
+static struct neighbour *neigh_dequeue(struct neigh_table *tbl)
+{
+	struct neighbour *neigh;
+
+	if ((neigh = tbl->request_queue))
+	{
+		tbl->request_queue = neigh->next;
+	}
+	return neigh;
+}
+
+void neigh_table_ins(struct neigh_table *tbl, struct neighbour *neigh)
+{
+        unsigned int hash_val;
+	struct neighbour **head;
+	
+	hash_val = tbl->neigh_ops->hash(neigh->primary_key) % tbl->tbl_size;
+	
+	neigh->tbl = tbl;
+	neigh->ops = tbl->neigh_ops;
+	
+	head = &tbl->hash_buckets[hash_val];
+	
+	if (!(*head))
+	{
+		neigh->next = neigh;
+		neigh->prev = neigh;
+	}
+	else
+	{
+		struct neighbour *prev;
+		struct neighbour *next;
+	
+		next = *head;
+		prev = next->prev;
+		
+
+		neigh->next = next;
+		neigh->prev = prev;
+		next->prev = neigh;
+		prev->next = neigh;
+	}
+	
+	*head = neigh;
+}
+
+struct neighbour * neigh_lookup(struct neigh_table *tbl, void *pkey,
+				int key_len, struct device *dev)
+{
+	struct neighbour *neigh, *head;
+	unsigned int hash_val;
+	
+	hash_val = tbl->neigh_ops->hash(pkey) % tbl->tbl_size;
+	head = tbl->hash_buckets[hash_val];
+
+	neigh = head;
+
+	if (neigh)
+	{
+		do {
+			if (memcmp(&neigh->primary_key, pkey, key_len) == 0)
+			{
+				if (!dev || dev == neigh->dev)
+					break;
+			}
+			neigh = neigh->next;
+			
+		} while (neigh != head);
+	}
+
+	return neigh;
+}
+
+/*
+ *	neighbour must already be out of the table;
+ *
+ */
+void neigh_destroy(struct neighbour *neigh)
+{	
+	unsigned long flags;
+
+	if (neigh->tbl)
+	{
+		printk(KERN_DEBUG "neigh_destroy: neighbour still in table. "
+		       "called from %p\n", __builtin_return_address(0));
+	}
+
+	if (neigh->ops->destructor)
+	{
+		(neigh->ops->destructor)(neigh);
+	}
+
+	neigh_purge_send_q(neigh);
+
+	save_flags(flags);
+	cli();
+	restore_flags(flags);
+	
+	kfree(neigh);
+}
+
+void neigh_unlink(struct neighbour *neigh)
+{
+	struct neigh_table *tbl;
+	struct neighbour **head;
+	unsigned int hash_val;
+	struct neighbour *next, *prev;
+	
+	tbl = neigh->tbl;	
+	neigh->tbl = NULL;
+	
+	hash_val = neigh->ops->hash(neigh->primary_key) % tbl->tbl_size;
+
+	head = &tbl->hash_buckets[hash_val];
+	tbl->tbl_entries--;
+	
+	next = neigh->next;
+	if (neigh == (*head))
+	{
+		if (next == neigh)
+		{
+			*head = NULL;
+			goto out;
+		}
+		*head = next;
+	}
+	
+	prev = neigh->prev;
+	next->prev = prev;
+	prev->next = next;
+  out:	
+	neigh->next = neigh->prev = NULL;
+}
+
+/*
+ *	Must only be called with an exclusive lock and bh disabled
+ *
+ */
+
+void ntbl_walk_table(struct neigh_table *tbl, ntbl_examine_t func,
+		     unsigned long filter, int max, void *args)
+{
+	int i;
+	
+	if (max == 0)
+		max = tbl->tbl_size;
+	
+	for (i=0; i < max; i++)
+	{
+		struct neighbour **head;
+		struct neighbour *entry;
+		
+		head = &tbl->hash_buckets[i];
+		entry = *head;
+
+		if (!entry)
+			continue;
+		
+		do {
+			if (entry->flags & (~filter))
+			{
+				int ret;
+				ret = (*func)(entry, args);
+
+				if (ret)
+				{
+					struct neighbour *curp;
+
+					curp = entry;
+					entry = curp->next;
+					
+					neigh_unlink(curp);
+					neigh_destroy(curp);
+					
+					if ((*head) == NULL)
+						break;
+					continue;
+				}
+			}
+			entry = entry->next;
+
+		} while (entry != *head);
+	}
+}
+
+void neigh_tbl_run_bh(struct neigh_table *tbl)
+{       
+	if ((tbl->tbl_bh_mask & NT_MASK_QUEUE))
+	{
+		struct neighbour *neigh;
+
+		while((neigh = neigh_dequeue(tbl)))
+		{
+			neigh_table_ins(tbl, neigh);
+		}
+		tbl->tbl_bh_mask &= ~NT_MASK_QUEUE;
+	}
+}
+
+/*
+ * Purge all linked skb's of the entry.
+ */
+
+static void neigh_purge_send_q(struct neighbour *neigh)
+{
+	struct sk_buff *skb;
+
+	/* Release the list of `skb' pointers. */
+	while ((skb = skb_dequeue(&neigh->arp_queue)))
+	{
+		dev_kfree_skb(skb, FREE_WRITE);
+	}
+	return;
+}
diff -u --recursive --new-file v2.1.14/linux/net/core/net_alias.c linux/net/core/net_alias.c
--- v2.1.14/linux/net/core/net_alias.c	Thu Oct 10 19:10:58 1996
+++ linux/net/core/net_alias.c	Thu Dec 12 16:54:23 1996
@@ -44,11 +44,6 @@
 #include <linux/proc_fs.h>
 #include <linux/stat.h>
 
-#ifdef ALIAS_USER_LAND_DEBUG
-#include "net_alias.h"
-#include "user_stubs.h"
-#endif
-
 #include <linux/net_alias.h>
 
 #ifdef CONFIG_KERNELD
@@ -59,7 +54,7 @@
  * Only allow the following flags to pass from main device to aliases
  */
 
-#define  NET_ALIAS_IFF_MASK   (IFF_UP|IFF_BROADCAST|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT)
+#define  NET_ALIAS_IFF_MASK   (IFF_UP|IFF_RUNNING|IFF_NOARP|IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MULTICAST)
 
 static struct net_alias_type * nat_getbytype(int type);
 static int nat_attach_chg(struct net_alias_type *nat, int delta);
@@ -296,6 +291,8 @@
     dev->mtu = main_dev->mtu;
     dev->pa_alen = main_dev->pa_alen;
     dev->hard_header = main_dev->hard_header;
+    dev->hard_header_cache = main_dev->hard_header_cache;
+    dev->header_cache_update = main_dev->header_cache_update;
     dev->rebuild_header = main_dev->rebuild_header;
   }
   
diff -u --recursive --new-file v2.1.14/linux/net/core/scm.c linux/net/core/scm.c
--- v2.1.14/linux/net/core/scm.c	Thu Jan  1 02:00:00 1970
+++ linux/net/core/scm.c	Thu Dec 12 16:54:23 1996
@@ -0,0 +1,306 @@
+/* scm.c - Socket level control messages processing.
+ *
+ * Author:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/socket.h>
+#include <linux/fcntl.h>
+#include <linux/net.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/rarp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/scm.h>
+
+
+/*
+ *	Allow to send credentials, that user could set with setu(g)id.
+ */
+
+static __inline__ int scm_check_creds(struct ucred *creds)
+{
+	if (suser())
+		return 0;
+	if (creds->pid != current->pid ||
+	    (creds->uid != current->uid && creds->uid != current->euid &&
+	     creds->uid != current->suid) ||
+	    (creds->gid != current->gid && creds->gid != current->egid &&
+	     creds->gid != current->sgid))
+		return -EPERM;
+	return 0;
+}
+
+
+static int
+scm_fp_copy(struct cmsghdr *cmsg, struct scm_fp_list **fplp)
+{
+	int num;
+	struct scm_fp_list *fpl = *fplp;
+	struct file **fpp = &fpl->fp[fpl->count];
+	int *fdp = (int*)cmsg->cmsg_data;
+	int i;
+
+	num = (cmsg->cmsg_len - sizeof(struct cmsghdr))/sizeof(int);
+
+	if (!num)
+		return 0;
+
+	if (num > SCM_MAX_FD)
+		return -EINVAL;
+
+	if (!fpl)
+	{
+		fpl = kmalloc(sizeof(struct scm_fp_list), GFP_KERNEL);
+		if (!fpl)
+			return -ENOMEM;
+		*fplp = fpl;
+		fpl->count = 0;
+	}
+
+	if (fpl->count + num > SCM_MAX_FD)
+		return -EINVAL;
+	
+	/*
+	 *	Verify the descriptors.
+	 */
+	 
+	for (i=0; i< num; i++)
+	{
+		int fd;
+		
+		fd = fdp[i];
+
+		if (fd < 0 || fd >= NR_OPEN)
+			return -EBADF;
+		if (current->files->fd[fd]==NULL)
+			return -EBADF;
+		fpp[i] = current->files->fd[fd];
+	}
+	
+        /* add another reference to these files */
+	for (i=0; i< num; i++, fpp++)
+		(*fpp)->f_count++;
+	fpl->count += num;
+	
+	return num;
+}
+
+void __scm_destroy(struct scm_cookie *scm)
+{
+	int i;
+	struct scm_fp_list *fpl = scm->fp;
+
+	if (!fpl)
+		return;
+
+	for (i=fpl->count-1; i>=0; i--)
+		close_fp(fpl->fp[i]);
+
+	kfree(fpl);
+}
+
+
+
+static __inline__ int not_one_bit(unsigned val)
+{
+	return (val-1) & val;
+}
+
+
+int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
+{
+	int err;
+	struct cmsghdr kcm, *cmsg;
+	struct file *file;
+	int acc_fd;
+	unsigned scm_flags=0;
+
+	for (cmsg = KCMSG_FIRSTHDR(msg); cmsg; cmsg = KCMSG_NXTHDR(msg, cmsg)) {
+		if (kcm.cmsg_level != SOL_SOCKET)
+			continue;
+
+		err = -EINVAL;
+
+		/*
+		 * Temporary hack: no protocols except for AF_UNIX
+		 * undestand scm now.
+		 */
+		if (sock->ops->family != AF_UNIX)
+			goto error;
+
+		switch (kcm.cmsg_type)
+		{
+		case SCM_RIGHTS:
+			err=scm_fp_copy(cmsg, &p->fp);
+			if (err<0)
+				goto error;
+			break;
+		case SCM_CREDENTIALS:
+			if (kcm.cmsg_len < sizeof(kcm) + sizeof(struct ucred))
+				goto error;
+			memcpy(&p->creds, cmsg->cmsg_data, sizeof(struct ucred));
+			err = scm_check_creds(&p->creds);
+			if (err)
+				goto error;
+			break;
+		case SCM_CONNECT:
+			if (scm_flags)
+				goto error;
+			if (kcm.cmsg_len < sizeof(kcm) + sizeof(int))
+				goto error;
+			memcpy(&acc_fd, cmsg->cmsg_data, sizeof(int));
+
+			p->sock = NULL;
+			if (acc_fd != -1) {
+				if (acc_fd < 0 || acc_fd >= NR_OPEN ||
+				    (file=current->files->fd[acc_fd])==NULL)
+					return -EBADF;
+				if (!file->f_inode || !file->f_inode->i_sock)
+					return -ENOTSOCK;
+				p->sock = &file->f_inode->u.socket_i;
+				if (p->sock->state != SS_UNCONNECTED) 
+					return -EINVAL;
+			}
+			scm_flags |= MSG_SYN;
+			break;
+		default:
+			goto error;
+		}
+	}
+
+
+	if (p->fp && !p->fp->count)
+	{
+		kfree(p->fp);
+		p->fp = NULL;
+	}
+	
+	err = -EINVAL;
+	msg->msg_flags |= scm_flags;
+	scm_flags = msg->msg_flags&MSG_CTLFLAGS;
+	if (not_one_bit(scm_flags))
+		goto error;
+
+	if (!(scm_flags && p->fp))
+		return 0;
+
+error:
+	scm_destroy(p);
+	return err;
+}
+
+void put_cmsg(struct msghdr * msg, int level, int type, int len, void *data)
+{
+	struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
+	int cmlen = sizeof(*cm) + len;
+
+	if (cm==NULL || msg->msg_controllen < sizeof(*cm)) {
+		msg->msg_flags |= MSG_CTRUNC;
+		return;
+	}
+	if (msg->msg_controllen < cmlen) {
+		msg->msg_flags |= MSG_CTRUNC;
+		cmlen = msg->msg_controllen;
+	}
+	
+	cm->cmsg_level = level;
+	cm->cmsg_type = type;
+	cm->cmsg_len = cmlen;
+	memcpy(cm->cmsg_data, data, cmlen - sizeof(*cm));
+
+	cmlen = CMSG_ALIGN(cmlen);
+	msg->msg_control += cmlen;
+	msg->msg_controllen -= cmlen;
+
+}
+
+void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm)
+{
+	struct cmsghdr *cm = (struct cmsghdr*)msg->msg_control;
+
+	int fdmax = (msg->msg_controllen - sizeof(struct cmsghdr))/sizeof(int);
+	int fdnum = scm->fp->count;
+	int *cmfptr;
+	int i;
+	struct file **fp = scm->fp->fp;
+
+	if (fdnum > fdmax)
+		fdmax = fdnum;
+
+	for (i=0, cmfptr=(int*)cm->cmsg_data; i<fdmax; i++, cmfptr++)
+	{
+		int new_fd = get_unused_fd();
+		if (new_fd < 0)
+			break;
+		current->files->fd[new_fd] = fp[i];
+		*cmfptr = new_fd;
+		cmfptr++;
+	}
+
+	if (i > 0)
+	{
+		int cmlen = i*sizeof(int) + sizeof(struct cmsghdr);
+
+		cm->cmsg_level = SOL_SOCKET;
+		cm->cmsg_type = SCM_RIGHTS;
+		cm->cmsg_len = cmlen;
+
+		cmlen = CMSG_ALIGN(cmlen);
+		msg->msg_control += cmlen;
+		msg->msg_controllen -= cmlen;
+	}
+
+	/*
+	 *	Dump those that don't fit.
+	 */
+	for ( ; i < fdnum; i++)	{
+		msg->msg_flags |= MSG_CTRUNC;
+		close_fp(fp[i]);
+	}
+
+	kfree (scm->fp);
+	scm->fp = NULL;
+}
+
+struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl)
+{
+	int i;
+	struct scm_fp_list *new_fpl;
+
+	if (!fpl)
+		return NULL;
+
+	new_fpl = kmalloc(fpl->count*sizeof(int) + sizeof(*fpl), GFP_KERNEL);
+	if (!new_fpl)
+		return NULL;
+
+	memcpy(new_fpl, fpl, fpl->count*sizeof(int) + sizeof(*fpl));
+
+	for (i=fpl->count-1; i>=0; i--)
+		fpl->fp[i]->f_count++;
+
+	return new_fpl;
+}
diff -u --recursive --new-file v2.1.14/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v2.1.14/linux/net/core/skbuff.c	Thu Dec 12 17:02:47 1996
+++ linux/net/core/skbuff.c	Thu Dec 12 16:54:23 1996
@@ -15,12 +15,13 @@
  *		Alan Cox	:	Added all the changed routines Linus
  *					only put in the headers
  *		Ray VanTassle	:	Fixed --skb->lock in free
+ *		Alan Cox	:	skb_copy copy arp field
  *
- *	TO FIX:
- *		The __skb_ routines ought to check interrupts are disabled
- *	when called, and bitch like crazy if not. Unfortunately I don't think
- *	we currently have a portable way to check if interrupts are off - 
- *	Linus ???
+ *	NOTE:
+ *		The __skb_ routines should be called with interrupts 
+ *	disabled, or you better be *real* sure that the operation is atomic 
+ *	with respect to whatever list is being frobbed (e.g. via lock_sock()
+ *	or via disabling bottom half handlers, etc).
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -48,7 +49,7 @@
 #include <net/ip.h>
 #include <net/ipv6.h>
 #include <net/protocol.h>
-#include <net/route.h>
+#include <net/dst.h>
 #include <net/tcp.h>
 #include <net/udp.h>
 #include <net/sock.h>
@@ -61,10 +62,8 @@
  */
 
 atomic_t net_skbcount = 0;
-atomic_t net_locked = 0;
 atomic_t net_allocs = 0;
 atomic_t net_fails  = 0;
-atomic_t net_free_locked = 0;
 
 extern atomic_t ip_frag_mem;
 
@@ -78,10 +77,8 @@
 void show_net_buffers(void)
 {
 	printk(KERN_INFO "Networking buffers in use          : %u\n",net_skbcount);
-	printk(KERN_INFO "Network buffers locked by drivers  : %u\n",net_locked);
 	printk(KERN_INFO "Total network buffer allocations   : %u\n",net_allocs);
 	printk(KERN_INFO "Total failed network buffer allocs : %u\n",net_fails);
-	printk(KERN_INFO "Total free while locked events     : %u\n",net_free_locked);
 #ifdef CONFIG_INET
 	printk(KERN_INFO "IP fragment buffer size            : %u\n",ip_frag_mem);
 #endif	
@@ -581,57 +578,26 @@
  *	not need to like protocols and sockets.
  */
 
-void kfree_skb(struct sk_buff *skb, int rw)
+void __kfree_skb(struct sk_buff *skb)
 {
+#if CONFIG_SKB_CHECK
 	if (skb == NULL)
 	{
 		printk(KERN_CRIT "kfree_skb: skb = NULL (from %p)\n",
-			__builtin_return_address(0));
+ 			__builtin_return_address(0));
 		return;
   	}
-#if CONFIG_SKB_CHECK
 	IS_SKB(skb);
 #endif
-	if (skb->lock)
-	{
-		skb->free = 3;    /* Free when unlocked */
-		net_free_locked++;
-		return;
-  	}
-  	if (skb->free == 2)
-		printk(KERN_WARNING "Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p)\n",
-			__builtin_return_address(0));
 	if (skb->list)
 	 	printk(KERN_WARNING "Warning: kfree_skb passed an skb still on a list (from %p).\n",
 			__builtin_return_address(0));
 
+	dst_release(skb->dst);
+
 	if(skb->destructor)
 		skb->destructor(skb);
-	if (skb->sk)
-	{
-		struct sock * sk = skb->sk;
-	        if(sk->prot!=NULL)
-		{
-			if (rw)
-		     		sock_rfree(sk, skb);
-		     	else
-		     		sock_wfree(sk, skb);
-
-		}
-		else
-		{
-			if (rw)
-				atomic_sub(skb->truesize, &sk->rmem_alloc);
-			else {
-				if(!sk->dead)
-					sk->write_space(sk);
-				atomic_sub(skb->truesize, &sk->wmem_alloc);
-			}
-			kfree_skbmem(skb);
-		}
-	}
-	else
-		kfree_skbmem(skb);
+	kfree_skbmem(skb);
 }
 
 /*
@@ -685,33 +651,31 @@
 	skb=(struct sk_buff *)(bptr+size)-1;
 
 	skb->count = 1;		/* only one reference to this */
-	skb->data_skb = NULL;	/* and we're our own data skb */
+	skb->data_skb = skb;	/* and we're our own data skb */
 
-	skb->free = 2;	/* Invalid so we pick up forgetful users */
-	skb->lock = 0;
 	skb->pkt_type = PACKET_HOST;	/* Default type */
 	skb->pkt_bridged = 0;		/* Not bridged */
-	skb->prev = skb->next = skb->link3 = NULL;
+	skb->prev = skb->next = NULL;
 	skb->list = NULL;
 	skb->sk = NULL;
 	skb->truesize=size;
-	skb->localroute=0;
 	skb->stamp.tv_sec=0;	/* No idea about time */
-	skb->localroute = 0;
 	skb->ip_summed = 0;
-	memset(skb->proto_priv, 0, sizeof(skb->proto_priv));
+	skb->dst = NULL;
+	skb->destructor = NULL;
+	memset(skb->cb, 0, sizeof(skb->cb));
+	skb->priority = SOPRI_NORMAL;
 	net_skbcount++;
 #if CONFIG_SKB_CHECK
 	skb->magic_debug_cookie = SK_GOOD_SKB;
 #endif
-	skb->users = 0;
+	skb->users = 1;
 	/* Load the data pointers */
 	skb->head=bptr;
 	skb->data=bptr;
 	skb->tail=bptr;
 	skb->end=bptr+len;
 	skb->len=0;
-	skb->destructor=NULL;
 	skb->inclone = 0;
 	return skb;
 }
@@ -741,7 +705,7 @@
 		free_head = (skb->inclone != SKB_CLONE_INLINE);
 
 		/* free the skb that contains the actual data if we've clone()'d */
-		if (skb->data_skb) {
+		if (skb->data_skb != skb) {
 			addr = skb;
 			__kfree_skbmem(skb->data_skb);
 		}
@@ -752,8 +716,7 @@
 }
 
 /*
- *	Duplicate an sk_buff. The new one is not owned by a socket or locked
- *	and will be freed on deletion.
+ *	Duplicate an sk_buff. The new one is not owned by a socket.
  */
 
 struct sk_buff *skb_clone(struct sk_buff *skb, int priority)
@@ -777,20 +740,19 @@
 	}
 	memcpy(n, skb, sizeof(*n));
 	n->count = 1;
-	if (skb->data_skb)
-		skb = skb->data_skb;
+	skb = skb->data_skb;
 	atomic_inc(&skb->count);
 	atomic_inc(&net_allocs);
 	atomic_inc(&net_skbcount);
+	dst_clone(n->dst);
 	n->data_skb = skb;
-	n->next = n->prev = n->link3 = NULL;
+	n->next = n->prev = NULL;
 	n->list = NULL;
 	n->sk = NULL;
-	n->free = 1;
 	n->tries = 0;
-	n->lock = 0;
-	n->users = 0;
+	n->users = 1;
 	n->inclone = inbuff;
+	n->destructor = NULL;
 	return n;
 }
 
@@ -825,82 +787,95 @@
 	skb_put(n,skb->len);
 	/* Copy the bytes */
 	memcpy(n->head,skb->head,skb->end-skb->head);
-	n->link3=NULL;
 	n->list=NULL;
 	n->sk=NULL;
 	n->when=skb->when;
 	n->dev=skb->dev;
+	n->priority=skb->priority;
+	n->protocol=skb->protocol;
+	n->dst=dst_clone(skb->dst);
 	n->h.raw=skb->h.raw+offset;
+	n->nh.raw=skb->nh.raw+offset;
 	n->mac.raw=skb->mac.raw+offset;
-	n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
-	n->ipv6_hdr=(struct ipv6hdr *)(((char *)skb->ipv6_hdr)+offset);
 	n->nexthop = skb->nexthop;
 #endif
-	n->saddr=skb->saddr;
-	n->daddr=skb->daddr;
-	n->raddr=skb->raddr;
 	n->seq=skb->seq;
 	n->end_seq=skb->end_seq;
 	n->ack_seq=skb->ack_seq;
 	n->acked=skb->acked;
-	memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+	memcpy(n->cb, skb->cb, sizeof(skb->cb));
 	n->used=skb->used;
-	n->free=1;
 	n->arp=skb->arp;
 	n->tries=0;
-	n->lock=0;
-	n->users=0;
+	n->users=1;
 	n->pkt_type=skb->pkt_type;
 	n->stamp=skb->stamp;
+	n->arp=skb->arp;
+	n->destructor = NULL;
 	
 	IS_SKB(n);
 	return n;
 }
 
-/*
- *     Skbuff device locking
- */
-
-void skb_device_lock(struct sk_buff *skb)
+struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, int newheadroom)
 {
-	if(skb->lock)
-		printk("double lock on device queue, lock=%d caller=%p\n",
-			skb->lock, (&skb)[-1]);
-	else
-		net_locked++;
-	skb->lock++;
-}
+	struct sk_buff *n;
+	unsigned long offset;
+	int headroom = skb_headroom(skb);
 
-void skb_device_unlock(struct sk_buff *skb)
-{
-	if(skb->lock==0)
-		printk("double unlock on device queue!\n");
-	skb->lock--;
-	if(skb->lock==0)
-		net_locked--;
-}
+	/*
+	 *	Allocate the copy buffer
+	 */
+ 	 
+	IS_SKB(skb);
+	
+	n=alloc_skb(skb->truesize+newheadroom-headroom-sizeof(struct sk_buff), GFP_ATOMIC);
+	if(n==NULL)
+		return NULL;
 
-void dev_kfree_skb(struct sk_buff *skb, int mode)
-{
-	unsigned long flags;
+	skb_reserve(n,newheadroom);
 
-	save_flags(flags);
-	cli();
-	if(skb->lock)
-	{
-		net_locked--;
-		skb->lock--;
-	}
-	if (!skb->lock && (skb->free == 1 || skb->free == 3))
-	{
-		restore_flags(flags);
-		kfree_skb(skb,mode);
-	}
-	else
-		restore_flags(flags);
-}
+	/*
+	 *	Shift between the two data areas in bytes
+	 */
+	 
+	offset=n->data-skb->data;
 
+	/* Set the tail pointer and length */
+	skb_put(n,skb->len);
+	/* Copy the bytes */
+	memcpy(n->data,skb->data,skb->len);
+	n->list=NULL;
+	n->sk=NULL;
+	n->when=skb->when;
+	n->priority=skb->priority;
+	n->protocol=skb->protocol;
+	n->dev=skb->dev;
+	n->dst=dst_clone(skb->dst);
+	n->h.raw=skb->h.raw+offset;
+	n->nh.raw=skb->nh.raw+offset;
+	n->mac.raw=skb->mac.raw+offset;
+	memcpy(n->cb, skb->cb, sizeof(skb->cb));
+#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
+ 	n->nexthop = skb->nexthop;
+#endif
+	n->seq=skb->seq;
+ 	n->end_seq=skb->end_seq;
+	n->ack_seq=skb->ack_seq;
+	n->acked=skb->acked;
+	n->used=skb->used;
+	n->arp=skb->arp;
+	n->tries=0;
+	n->users=1;
+	n->pkt_type=skb->pkt_type;
+	n->stamp=skb->stamp;
+	n->destructor = NULL;
+ 	
+	IS_SKB(n);
+	return n;
+}
+  
 struct sk_buff *dev_alloc_skb(unsigned int length)
 {
 	struct sk_buff *skb;
@@ -909,9 +884,4 @@
 	if (skb)
 		skb_reserve(skb,16);
 	return skb;
-}
-
-int skb_device_locked(struct sk_buff *skb)
-{
-	return skb->lock? 1 : 0;
 }
diff -u --recursive --new-file v2.1.14/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.14/linux/net/core/sock.c	Tue Nov 19 15:54:00 1996
+++ linux/net/core/sock.c	Thu Dec 12 17:32:05 1996
@@ -121,9 +121,10 @@
  *	at the socket level. Everything here is generic.
  */
 
-int sock_setsockopt(struct sock *sk, int level, int optname,
+int sock_setsockopt(struct socket *sock, int level, int optname,
 		    char *optval, int optlen)
 {
+	struct sock *sk=sock->sk;
 	int val;
 	int valbool;
 	int err;
@@ -244,7 +245,11 @@
 		case SO_BSDCOMPAT:
 			sk->bsdism = valbool;
 			break;
-			
+
+		case SO_PASSCRED:
+			sock->passcred = valbool;
+			break;
+
 		/* We implementation the SO_SNDLOWAT etc to
 		   not be settable (1003.1g 5.3) */
 		default:
@@ -254,9 +259,10 @@
 }
 
 
-int sock_getsockopt(struct sock *sk, int level, int optname,
-		   char *optval, int *optlen)
-{		
+int sock_getsockopt(struct socket *sock, int level, int optname,
+		    char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
   	int val;
   	int err;
   	struct linger ling;
@@ -332,12 +338,25 @@
 		case SO_SNDTIMEO:
 		{
 			static struct timeval tm={0,0};
-			return copy_to_user(optval,&tm,sizeof(tm));
+			int err=copy_to_user(optval,&tm,sizeof(tm));
+			if(err!=sizeof(struct timeval))
+				return -EFAULT;
+			return 0;
 		}
 		case SO_RCVLOWAT:
 		case SO_SNDLOWAT:
 			val=1;
 
+		case SO_PASSCRED:
+			val = sock->passcred;
+			break;
+
+		case SO_PEERCRED:
+			err = put_user(sizeof(sk->peercred), optlen);
+			if (!err)
+				err = copy_to_user((void*)optval, &sk->peercred, sizeof(struct ucred));
+			return err;
+
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -362,33 +381,66 @@
 	kfree_s(sk,sizeof(*sk));
 }
 
+void sock_wfree(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+#if CONFIG_SKB_CHECK
+	IS_SKB(skb);
+#endif
+#if 1
+	if (!sk) {
+		printk("sock_wfree: sk==NULL\n");
+		return;
+	}
+#endif
+	/* In case it might be waiting for more memory. */
+	atomic_sub(skb->truesize, &sk->wmem_alloc);
+	sk->write_space(sk);
+}
+
+
+void sock_rfree(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+#if CONFIG_SKB_CHECK
+	IS_SKB(skb);
+#endif	
+#if 1
+	if (!sk) {
+		printk("sock_rfree: sk==NULL\n");
+		return;
+	}
+#endif
+	atomic_sub(skb->truesize, &sk->rmem_alloc);
+}
+
 
 struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority)
 {
-	if (sk) {
-		if (force || sk->wmem_alloc < sk->sndbuf) {
-			struct sk_buff * skb = alloc_skb(size, priority);
-			if (skb)
-				atomic_add(skb->truesize, &sk->wmem_alloc);
-			return skb;
+	if (force || sk->wmem_alloc < sk->sndbuf) {
+		struct sk_buff * skb = alloc_skb(size, priority);
+		if (skb) {
+			atomic_add(skb->truesize, &sk->wmem_alloc);
+			skb->destructor = sock_wfree;
+			skb->sk = sk;
 		}
-		return NULL;
+		return skb;
 	}
-	return alloc_skb(size, priority);
+	return NULL;
 }
 
 struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, int priority)
 {
-	if (sk) {
-		if (force || sk->rmem_alloc < sk->rcvbuf) {
-			struct sk_buff *skb = alloc_skb(size, priority);
-			if (skb)
-				atomic_add(skb->truesize, &sk->rmem_alloc);
-			return skb;
+	if (force || sk->rmem_alloc < sk->rcvbuf) {
+		struct sk_buff *skb = alloc_skb(size, priority);
+		if (skb) {
+			atomic_add(skb->truesize, &sk->rmem_alloc);
+			skb->destructor = sock_rfree;
+			skb->sk = sk;
 		}
-		return NULL;
+		return skb;
 	}
-	return alloc_skb(size, priority);
+	return NULL;
 }
 
 
@@ -423,34 +475,6 @@
 }
 
 
-void sock_wfree(struct sock *sk, struct sk_buff *skb)
-{
-	int s=skb->truesize;
-#if CONFIG_SKB_CHECK
-	IS_SKB(skb);
-#endif
-	kfree_skbmem(skb);
-	if (sk) 
-	{
-		/* In case it might be waiting for more memory. */
-		sk->write_space(sk);
-		atomic_sub(s, &sk->wmem_alloc);
-	}
-}
-
-
-void sock_rfree(struct sock *sk, struct sk_buff *skb)
-{
-	int s=skb->truesize;
-#if CONFIG_SKB_CHECK
-	IS_SKB(skb);
-#endif	
-	kfree_skbmem(skb);
-	if (sk) 
-	{
-		atomic_sub(s, &sk->rmem_alloc);
-	}
-}
 
 /*
  *	Generic send/receive buffer handlers
diff -u --recursive --new-file v2.1.14/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v2.1.14/linux/net/ethernet/eth.c	Sun Nov 10 20:12:24 1996
+++ linux/net/ethernet/eth.c	Thu Dec 12 16:54:23 1996
@@ -46,11 +46,13 @@
 #include <linux/socket.h>
 #include <linux/in.h>
 #include <linux/inet.h>
+#include <linux/ip.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/errno.h>
 #include <linux/config.h>
+#include <net/dst.h>
 #include <net/arp.h>
 #include <net/sock.h>
 #include <net/ipv6.h>
@@ -150,10 +152,10 @@
  *	sk_buff. We now let ARP fill in the other fields.
  */
  
-int eth_rebuild_header(void *buff, struct device *dev, unsigned long dst,
-			struct sk_buff *skb)
+int eth_rebuild_header(struct sk_buff *skb)
 {
-	struct ethhdr *eth = (struct ethhdr *)buff;
+	struct ethhdr *eth = (struct ethhdr *)skb->data;
+	struct device *dev = skb->dev;
 
 	/*
 	 *	Only ARP/IP and NDISC/IPv6 are currently supported
@@ -168,8 +170,7 @@
 		 *	Try to get ARP to resolve the header.
 		 */
 
-		return (arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb) ? 
-			1 : 0);
+		return arp_find(eth->h_dest, skb) ? 1 : 0;
 		break;
 #endif
 
@@ -250,30 +251,38 @@
 	return htons(ETH_P_802_2);
 }
 
-/*
- * Upper level calls this function to bind hardware header cache entry.
- * If the call is successful, then corresponding Address Resolution Protocol
- * (maybe, not ARP) takes responsibility for updating cache content.
- */
-
-void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
-			   unsigned short htype, __u32 daddr)
+int eth_header_cache(struct dst_entry *dst, struct dst_entry *neigh, struct hh_cache *hh)
 {
-	struct hh_cache *hh;
+	unsigned short type = hh->hh_type;
+	struct ethhdr *eth = (struct ethhdr*)hh->hh_data;
+	struct device *dev = dst->dev;
 
-	if (htype != ETH_P_IP)
-	{
-		printk(KERN_DEBUG "eth_header_cache_bind: %04x cache is not implemented\n", htype);
-		return;
+	if (type == ETH_P_802_3)
+		return -1;
+	
+	eth->h_proto = htons(type);
+
+	memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+
+	if (dev->flags & IFF_LOOPBACK) {
+		memset(eth->h_dest, 0, dev->addr_len);
+		hh->hh_uptodate = 1;
+		return 0;
 	}
-	if (arp_bind_cache(hhp, dev, htype, daddr))
-		return;
-	if ((hh=*hhp) != NULL)
+
+	if (type != ETH_P_IP) 
 	{
-		memcpy(hh->hh_data+6, dev->dev_addr, ETH_ALEN);
-		hh->hh_data[12] = htype>>8;
-		hh->hh_data[13] = htype&0xFF;
+		printk(KERN_DEBUG "%s: unable to resolve type %X addresses.\n",dev->name,(int)eth->h_proto);
+		hh->hh_uptodate = 0;
+		return 0;
 	}
+
+#ifdef CONFIG_INET
+	hh->hh_uptodate = arp_find_1(eth->h_dest, dst, neigh);
+#else
+	hh->hh_uptodate = 0;
+#endif
+	return 0;
 }
 
 /*
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v2.1.14/linux/net/ipv4/Config.in	Tue Nov 19 15:54:00 1996
+++ linux/net/ipv4/Config.in	Thu Dec 12 16:54:23 1996
@@ -10,12 +10,14 @@
 	bool 'IP: firewall packet netlink device' CONFIG_IP_FIREWALL_NETLINK
     fi
     bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE
-    if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_IP_FORWARD" = "y" ]; then
-      bool 'IP: masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE
+    if [ "$CONFIG_IP_FORWARD" = "y" ]; then
+      bool 'IP: masquerading' CONFIG_IP_MASQUERADE
       if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
         comment 'Protocol-specific masquerading support will be built as modules.'
       fi
-      bool 'IP: transparent proxy support (EXPERIMENTAL)' CONFIG_IP_TRANSPARENT_PROXY
+# hmm... but transparent proxy is useful without forwarding too..
+# i.e. non-lan users will get anonftpd instead of wu-ftpd...
+      bool 'IP: transparent proxy support' CONFIG_IP_TRANSPARENT_PROXY
       bool 'IP: always defragment' CONFIG_IP_ALWAYS_DEFRAG
     fi
   fi
@@ -25,9 +27,7 @@
   bool 'IP: optimize as router not host' CONFIG_IP_ROUTER
   tristate 'IP: tunneling' CONFIG_NET_IPIP
   if [ "$CONFIG_IP_MULTICAST" = "y" ]; then
-      if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-        bool 'IP: multicast routing (EXPERIMENTAL)' CONFIG_IP_MROUTE
-      fi		
+    bool 'IP: multicast routing' CONFIG_IP_MROUTE
   fi
 fi
 if [ "$CONFIG_NET_ALIAS" = "y" ]; then
@@ -41,7 +41,7 @@
 comment '(it is safe to leave these untouched)'
 bool 'IP: PC/TCP compatibility mode' CONFIG_INET_PCTCP
 tristate 'IP: Reverse ARP' CONFIG_INET_RARP
-bool 'IP: Disable Path MTU Discovery (normally enabled)' CONFIG_NO_PATH_MTU_DISCOVERY
+bool 'IP: Path MTU Discovery (normally enabled)' CONFIG_PATH_MTU_DISCOVERY
 #bool 'IP: Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF
 bool 'IP: Drop source routed frames' CONFIG_IP_NOSR
 bool 'IP: Allow large windows (not recommended if <16Mb of memory)' CONFIG_SKB_LARGE
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/Makefile linux/net/ipv4/Makefile
--- v2.1.14/linux/net/ipv4/Makefile	Sun Nov 10 20:12:24 1996
+++ linux/net/ipv4/Makefile	Thu Dec 12 16:54:23 1996
@@ -13,7 +13,7 @@
 	     ip_output.o ip_sockglue.o \
 	     tcp.o tcp_input.o tcp_output.o tcp_timer.o tcp_ipv4.o\
 	     raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o ip_fw.o \
-	     sysctl_net_ipv4.o
+	     sysctl_net_ipv4.o fib.o ip_nat_dumb.o
 
 MOD_LIST_NAME := IPV4_MODULES
 M_OBJS :=
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.14/linux/net/ipv4/af_inet.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipv4/af_inet.c	Thu Dec 12 16:54:23 1996
@@ -91,6 +91,7 @@
 #include <net/sock.h>
 #include <net/raw.h>
 #include <net/icmp.h>
+#include <net/inet_common.h>
 #include <linux/ip_fw.h>
 #ifdef CONFIG_IP_MASQUERADE
 #include <net/ip_masq.h>
@@ -104,6 +105,9 @@
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif	/* CONFIG_NET_RADIO */
 
 #define min(a,b)	((a)<(b)?(a):(b))
 
@@ -314,7 +318,6 @@
 
 	lock_sock(sk);			/* just to be safe. */
 
-  
   	/*
   	 *	Now we can no longer get new packets or once the
   	 *	timers are killed, send them.
@@ -324,7 +327,7 @@
 
 	if (sk->prot->destroy)
 		sk->prot->destroy(sk);
-  	
+
   	/*
   	 *	Clean up the read buffer.
   	 */
@@ -345,13 +348,22 @@
 	}
 
   	/*
+  	 *	Clean up the error queue.
+  	 */
+
+	while((skb=skb_dequeue(&sk->error_queue))!=NULL) 
+	{
+		IS_SKB(skb);
+		kfree_skb(skb, FREE_READ);
+	}
+
+  	/*
   	 *	Now the backlog. 
   	 */
   	 
   	while((skb=skb_dequeue(&sk->back_log))!=NULL) 
   	{
-		/* this should [almost] never happen. */
-		skb->sk = NULL;
+		IS_SKB(skb);
 		kfree_skb(skb, FREE_READ);
 	}
 
@@ -373,11 +385,17 @@
 
 	if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
 	{
+/*
+ *	It is wrong! We MUST unlink socket from socket table
+ *	even earlier, than it used to be.
+ *	F.e. TCP socket must be unlinked at the moment, when
+ *	it goes to TCP_CLOSE. --ANK
+ */
 	    	inet_remove_sock(sk);
 
 		if(sk->opt)
 			kfree(sk->opt);
-		ip_rt_put(sk->ip_route_cache);
+		dst_release(sk->dst_cache);
 		/*
 		 *	This one is pure paranoia. I'll take it out
 		 *	later once I know the bug is buried.
@@ -413,11 +431,9 @@
  
 int inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk;
+	struct sock *sk = sock->sk;
 
-	sk = (struct sock *) sock->data;
-
-	switch(cmd) 
+	switch(cmd)
 	{
 		case F_SETOWN:
 			/*
@@ -443,13 +459,10 @@
 int inet_setsockopt(struct socket *sock, int level, int optname,
 		    char *optval, int optlen)
 {
-  	struct sock *sk = (struct sock *) sock->data;  
-	if (level == SOL_SOCKET)
-		return sock_setsockopt(sk,level,optname,optval,optlen);
+	struct sock *sk=sock->sk;
 	if (sk->prot->setsockopt==NULL)
 		return(-EOPNOTSUPP);
-	else
-		return sk->prot->setsockopt(sk,level,optname,optval,optlen);
+	return sk->prot->setsockopt(sk,level,optname,optval,optlen);
 }
 
 /*
@@ -463,13 +476,10 @@
 int inet_getsockopt(struct socket *sock, int level, int optname,
 		    char *optval, int *optlen)
 {
-  	struct sock *sk = (struct sock *) sock->data;  	
-  	if (level == SOL_SOCKET) 
-  		return sock_getsockopt(sk,level,optname,optval,optlen);
-  	if(sk->prot->getsockopt==NULL)  	
-  		return(-EOPNOTSUPP);
-  	else
-  		return sk->prot->getsockopt(sk,level,optname,optval,optlen);
+	struct sock *sk=sock->sk;
+	if (sk->prot->getsockopt==NULL)
+		return(-EOPNOTSUPP);
+	return sk->prot->getsockopt(sk,level,optname,optval,optlen);
 }
 
 /*
@@ -498,9 +508,12 @@
  
 int inet_listen(struct socket *sock, int backlog)
 {
-	struct sock *sk = (struct sock *) sock->data;
+	struct sock *sk = sock->sk;
 
-	if(inet_autobind(sk)!=0)
+	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
+		return(-EINVAL);
+
+	if (inet_autobind(sk)!=0)
 		return -EAGAIN;
 
 	/* We might as well re use these. */ 
@@ -520,6 +533,7 @@
 		sk->ack_backlog = 0;
 		sk->state = TCP_LISTEN;
 	}
+	sk->socket->flags |= SO_ACCEPTCON;
 	return(0);
 }
 
@@ -552,6 +566,7 @@
 	}
 }
 
+
 /*
  *	Create an inet socket.
  *
@@ -565,14 +580,15 @@
 	struct proto *prot;
 	int err;
 
+	sock->state = SS_UNCONNECTED;
 	sk = sk_alloc(GFP_KERNEL);
 	if (sk == NULL) 
 		return(-ENOBUFS);
-	memset(sk,0,sizeof(*sk));	/* Efficient way to set most fields to zero */
+
 	/*
 	 *	Note for tcp that also wiped the dummy_th block for us.
 	 */
-	switch(sock->type) 
+	switch (sock->type) 
 	{
 		case SOCK_STREAM:
 		case SOCK_SEQPACKET:
@@ -583,7 +599,12 @@
 			}
 			protocol = IPPROTO_TCP;
 			sk->no_check = TCP_NO_CHECK;
+			if (ipv4_config.no_pmtu_disc)
+				sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+			else
+				sk->ip_pmtudisc = IP_PMTUDISC_WANT;
 			prot = &tcp_prot;
+			sock->ops = &inet_stream_ops;
 			break;
 
 		case SOCK_DGRAM:
@@ -594,7 +615,9 @@
 			}
 			protocol = IPPROTO_UDP;
 			sk->no_check = UDP_NO_CHECK;
+			sk->ip_pmtudisc = IP_PMTUDISC_DONT;
 			prot=&udp_prot;
+			sock->ops = &inet_dgram_ops;
 			break;
       
 		case SOCK_RAW:
@@ -610,7 +633,9 @@
 			}
 			prot = &raw_prot;
 			sk->reuse = 1;
+			sk->ip_pmtudisc = IP_PMTUDISC_DONT;
 			sk->num = protocol;
+			sock->ops = &inet_dgram_ops;
 			break;
 
 		case SOCK_PACKET:
@@ -626,42 +651,51 @@
 			}
 			prot = &packet_prot;
 			sk->reuse = 1;
+			sk->ip_pmtudisc = IP_PMTUDISC_DONT;
 			sk->num = protocol;
+			sock->ops = &inet_dgram_ops;
 			break;
 
 		default:
 			sk_free(sk);
 			return(-ESOCKTNOSUPPORT);
 	}
+	sk->type = sock->type;
 	sk->socket = sock;
+	sk->sleep = &sock->wait;
+
+	sk->peercred.pid = 0;
+	sk->peercred.uid = -1;
+	sk->peercred.gid = -1;
+
+	sock->sk = sk;
 #ifdef CONFIG_TCP_NAGLE_OFF
 	sk->nonagle = 1;
 #endif  
 	sk->family = AF_INET;
-	sk->type = sock->type;
 	sk->protocol = protocol;
 	sk->allocation = GFP_KERNEL;
 	sk->sndbuf = SK_WMEM_MAX;
 	sk->rcvbuf = SK_RMEM_MAX;
-	sk->priority = 1;
+	sk->priority = SOPRI_NORMAL;
 
 	sk->prot = prot;
 	sk->backlog_rcv = prot->backlog_rcv;
 
-	sk->sleep = sock->wait;
-	sock->data =(void *) sk;
+	sk->sleep = &sock->wait;
+	sock->sk = sk;
 
 	sk->state = TCP_CLOSE;
 
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->error_queue);
 	skb_queue_head_init(&sk->back_log);
 
 
 	sk->timer.data = (unsigned long)sk;
 	sk->timer.function = &net_timer;
 
-	sock->data =(void *) sk;
 	sk->ip_ttl=ip_statistics.IpDefaultTTL;
 
 	if(sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
@@ -669,12 +703,10 @@
 	else
 		sk->ip_hdrincl=0;
 
-#ifdef CONFIG_IP_MULTICAST
 	sk->ip_mc_loop=1;
 	sk->ip_mc_ttl=1;
 	*sk->ip_mc_name=0;
 	sk->ip_mc_list=NULL;
-#endif
 	/*
 	 *	Speed up by setting some standard state for the dummy_th
 	 *	if TCP uses it (maybe move to tcp_init later)
@@ -716,7 +748,7 @@
  
 static int inet_dup(struct socket *newsock, struct socket *oldsock)
 {
-	return(inet_create(newsock,((struct sock *)(oldsock->data))->protocol));
+	return inet_create(newsock, oldsock->sk->protocol);
 }
 
 /*
@@ -725,22 +757,24 @@
  *	should refer to it.
  */
  
-int inet_release(struct socket *sock, struct socket *peer)
+int inet_release(struct socket *sock, struct socket *peersock)
 {
+	struct sock *sk = sock->sk;
 	unsigned long timeout;
-	struct sock *sk = (struct sock *) sock->data;
 
-	if (sk == NULL) 
-		return(0);
+	if (sk==NULL)
+		return 0;
+
+	if (sock->state != SS_UNCONNECTED)
+		sock->state = SS_DISCONNECTING;
 
 	sk->state_change(sk);
 
 	/* Start closing the connection.  This may take a while. */
 
-#ifdef CONFIG_IP_MULTICAST
 	/* Applications forget to leave groups before exiting */
 	ip_mc_drop_socket(sk);
-#endif
+
 	/*
 	 * If linger is set, we don't return until the close
 	 * is complete.  Otherwise we return immediately. The
@@ -750,7 +784,8 @@
 	 * linger..
 	 */
 	timeout = 0;
-	if (sk->linger) {
+	if (sk->linger)
+	{
 		timeout = ~0UL;
 		if (!sk->lingertime)
 			timeout = jiffies + HZ*sk->lingertime;
@@ -758,7 +793,7 @@
 	if (current->flags & PF_EXITING)
 		timeout = 0;
 
-	sock->data = NULL;
+	sock->sk = NULL;
 	sk->socket = NULL;
 
 	sk->prot->close(sk, timeout);
@@ -767,10 +802,10 @@
 
 
 static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
-	       int addr_len)
+		     int addr_len)
 {
 	struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
-	struct sock *sk=(struct sock *)sock->data, *sk2;
+	struct sock *sk=sock->sk, *sk2;
 	unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
 	int chk_addr_ret;
 
@@ -787,7 +822,7 @@
 	if(addr_len<sizeof(struct sockaddr_in))
 		return -EINVAL;
 		
-	if(sock->type != SOCK_RAW)
+	if (sock->type != SOCK_RAW)
 	{
 		if (sk->num != 0) 
 			return(-EINVAL);
@@ -808,7 +843,7 @@
 			return(-EACCES);
 	}
 	
-	chk_addr_ret = ip_chk_addr(addr->sin_addr.s_addr);
+	chk_addr_ret = __ip_chk_addr(addr->sin_addr.s_addr);
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	/*
 	 * Superuser may bind to any address to allow transparent proxying.
@@ -844,7 +879,7 @@
 #ifndef CONFIG_IP_TRANSPARENT_PROXY
 	}
 #endif
-	if(sock->type != SOCK_RAW)
+	if (sock->type != SOCK_RAW)
 	{
 		/* Make sure we are allowed to bind here. */
 		cli();
@@ -903,17 +938,33 @@
 		sti();
 
 		inet_remove_sock(sk);
-		if(sock->type==SOCK_DGRAM)
+		if (sock->type==SOCK_DGRAM)
 			udp_cache_zap();
-		if(sock->type==SOCK_STREAM)
+		if (sock->type==SOCK_STREAM)
 			tcp_cache_zap();
 		inet_put_sock(snum, sk);
 		sk->dummy_th.source = ntohs(sk->num);
 		sk->daddr = 0;
 		sk->dummy_th.dest = 0;
 	}
-	ip_rt_put(sk->ip_route_cache);
-	sk->ip_route_cache=NULL;
+	dst_release(sk->dst_cache);
+	sk->dst_cache=NULL;
+	return(0);
+}
+
+int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
+		       int addr_len, int flags)
+{
+	struct sock *sk=sock->sk;
+	int err;
+
+	if (inet_autobind(sk)!=0)
+		return(-EAGAIN);
+	if (sk->prot->connect == NULL) 
+		return(-EOPNOTSUPP);
+	err = sk->prot->connect(sk, (struct sockaddr *)uaddr, addr_len);
+	if (err < 0) 
+		return(err);
 	return(0);
 }
 
@@ -922,12 +973,32 @@
  *	TCP 'magic' in here.
  */
  
-int inet_connect(struct socket *sock, struct sockaddr * uaddr,
-		 int addr_len, int flags)
+int inet_stream_connect(struct socket *sock, struct sockaddr * uaddr,
+			int addr_len, int flags)
 {
-	struct sock *sk=(struct sock *)sock->data;
+	struct sock *sk=sock->sk;
 	int err;
-	sock->conn = NULL;
+
+	switch (sock->state)
+	{
+		case SS_UNCONNECTED:
+			/* This is ok... continue with connect */
+			break;
+		case SS_CONNECTED:
+			/* Socket is already connected */
+			return -EISCONN;
+		case SS_CONNECTING:
+			/* Not yet connected... we will check this. */
+		
+			/*
+			 *	FIXME:  for all protocols what happens if you start
+			 *	an async connect fork and both children connect. Clean
+			 *	this up in the protocols!
+			 */
+			break;
+		default:
+			return(-EINVAL);
+	}
 
 	if (sock->state == SS_CONNECTING && tcp_connected(sk->state))
 	{
@@ -945,7 +1016,7 @@
 	if (sock->state != SS_CONNECTING) 
 	{
 		/* We may need to bind the socket. */
-		if(inet_autobind(sk)!=0)
+		if (inet_autobind(sk)!=0)
 			return(-EAGAIN);
 		if (sk->prot->connect == NULL) 
 			return(-EOPNOTSUPP);
@@ -961,8 +1032,8 @@
 		return sock_error(sk);
 	}
 
-	if (sk->state != TCP_ESTABLISHED &&(flags & O_NONBLOCK)) 
-	  	return(-EINPROGRESS);
+	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) 
+	  	return (-EINPROGRESS);
 
 	cli(); /* avoid the race condition */
 	while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
@@ -975,7 +1046,7 @@
 		}
 		/* This fixes a nasty in the tcp/ip code. There is a hideous hassle with
 		   icmp error packets wanting to close a tcp or udp socket. */
-		if(sk->err && sk->protocol == IPPROTO_TCP)
+		if (sk->err && sk->protocol == IPPROTO_TCP)
 		{
 			sock->state = SS_UNCONNECTED;
 			sti();
@@ -993,39 +1064,23 @@
 	return(0);
 }
 
-
-static int inet_socketpair(struct socket *sock1, struct socket *sock2)
-{
-	 return(-EOPNOTSUPP);
-}
-
-
 /*
  *	Accept a pending connection. The TCP layer now gives BSD semantics.
  */
 
 int inet_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	struct sock *sk1, *sk2;
+	struct sock *sk1 = sock->sk;
+	struct sock *newsk = newsock->sk;
+	struct sock *sk2;
 	int err;
 
-	sk1 = (struct sock *) sock->data;
-
-	/*
-	 *	We've been passed an extra socket.
-	 *	We need to free it up because the tcp module creates
-	 *	its own when it accepts one.
-	 */
-	 
-	if (newsock->data)
-	{
-	  	struct sock *sk=(struct sock *)newsock->data;
-	  	newsock->data=NULL;
-	  	destroy_sock(sk);
-	}
-  
+	if (sock->state != SS_UNCONNECTED)
+		return -EINVAL;
+	if (!(sock->flags & SO_ACCEPTCON)) 
+		return -EINVAL;
 	if (sk1->prot->accept == NULL) 
-		return(-EOPNOTSUPP);
+		return -EOPNOTSUPP;
 
 	/*
 	 *	Restore the state if we have been interrupted, and then returned. 
@@ -1040,19 +1095,29 @@
 	{
 		sk2 = sk1->prot->accept(sk1,flags);
 		if (sk2 == NULL) 
-		{
 			return sock_error(sk1);
-		}
 	}
-	newsock->data = (void *)sk2;
-	sk2->sleep = newsock->wait;
+
+	/*
+	 *	We've been passed an extra socket.
+	 *	We need to free it up because the tcp module creates
+	 *	its own when it accepts one.
+	 */
+
+	sk2->sleep = newsk->sleep;
+
+	newsock->sk = sk2;
 	sk2->socket = newsock;
-	newsock->conn = NULL;
-	if (flags & O_NONBLOCK) 
+	newsk->socket = NULL;
+
+	if (flags & O_NONBLOCK)
+	{
+		destroy_sock(newsk);
 		return(0);
+	}
 
 	cli(); /* avoid the race. */
-	while(sk2->state == TCP_SYN_RECV) 
+	while (sk2->state == TCP_SYN_RECV) 
 	{
 		interruptible_sleep_on(sk2->sleep);
 		if (current->signal & ~current->blocked) 
@@ -1060,9 +1125,11 @@
 			sti();
 			sk1->pair = sk2;
 			sk2->sleep = NULL;
-			sk2->socket=NULL;
-			newsock->data = NULL;
-			return(-ERESTARTSYS);
+			sk2->socket = NULL;
+
+			newsock->sk = newsk;
+			newsk->socket = newsock;
+			return -ERESTARTSYS;
 		}
 	}
 	sti();
@@ -1070,16 +1137,28 @@
 	if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) 
 	{
 		err = sock_error(sk2);
+		sk2->sleep = NULL;
+		sk2->socket = NULL;
 		destroy_sock(sk2);
-		newsock->data = NULL;
+
+		newsock->sk = newsk;
+		newsk->socket = newsock;
+
 		return err;
 	}
 	if (sk2->state == TCP_CLOSE)
 	{
+		sk2->sleep = NULL;
+		sk2->socket = NULL;
 		destroy_sock(sk2);
-		newsock->data=NULL;
+
+		newsock->sk = newsk;
+		newsk->socket = newsock;
+
 		return -ECONNABORTED;
 	}
+
+	destroy_sock(newsk);
 	newsock->state = SS_CONNECTED;
 	return(0);
 }
@@ -1092,11 +1171,10 @@
 static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
 		 int *uaddr_len, int peer)
 {
+	struct sock *sk=sock->sk;
 	struct sockaddr_in *sin=(struct sockaddr_in *)uaddr;
-	struct sock *sk;
   
 	sin->sin_family = AF_INET;
-	sk = (struct sock *) sock->data;
 	if (peer) 
 	{
 		if (!tcp_connected(sk->state)) 
@@ -1107,11 +1185,8 @@
 	else 
 	{
 		__u32 addr = sk->rcv_saddr;
-		if (!addr) {
+		if (!addr)
 			addr = sk->saddr;
-			if (!addr)
-				addr = ip_my_addr();
-		}
 		sin->sin_port = sk->dummy_th.source;
 		sin->sin_addr.s_addr = addr;
 	}
@@ -1121,28 +1196,36 @@
 
 
 
-int inet_recvmsg(struct socket *sock, struct msghdr *ubuf, int size, 
-		 int noblock, int flags, int *addr_len)
+int inet_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+		 int flags, struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *) sock->data;
+	struct sock *sk = sock->sk;
+	int addr_len = 0;
+	int err;
 	
+	if (sock->flags & SO_ACCEPTCON)
+		return(-EINVAL);
 	if (sk->prot->recvmsg == NULL) 
 		return(-EOPNOTSUPP);
-	if(sk->err)
+	if (sk->err)
 		return sock_error(sk);
 	/* We may need to bind the socket. */
-	if(inet_autobind(sk)!=0)
+	if (inet_autobind(sk)!=0)
 		return(-EAGAIN);
-	return(sk->prot->recvmsg(sk, ubuf, size, noblock, flags,addr_len));
+	err = sk->prot->recvmsg(sk, msg, size, flags&MSG_DONTWAIT,
+				flags&~MSG_DONTWAIT, &addr_len);
+	if (err >= 0)
+		msg->msg_namelen = addr_len;
+	return err;
 }
 
 
-int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size, 
-		 int noblock, int flags)
+int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size,
+		 struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *) sock->data;
-	if (sk->shutdown & SEND_SHUTDOWN) 
-	{
+	struct sock *sk = sock->sk;
+
+	if (sk->shutdown & SEND_SHUTDOWN) {
 		send_sig(SIGPIPE, current, 1);
 		return(-EPIPE);
 	}
@@ -1153,14 +1236,13 @@
 	/* We may need to bind the socket. */
 	if(inet_autobind(sk)!=0)
 		return -EAGAIN;
-	return(sk->prot->sendmsg(sk, msg, size, noblock, flags));
-			   
+	return sk->prot->sendmsg(sk, msg, size);
 }
 
 
 int inet_shutdown(struct socket *sock, int how)
 {
-	struct sock *sk=(struct sock*)sock->data;
+	struct sock *sk = sock->sk;
 
 	/*
 	 * This should really check to make sure
@@ -1182,14 +1264,13 @@
 }
 
 
-int inet_select(struct socket *sock, int sel_type, select_table *wait )
+int inet_select(struct socket *sock, int sel_type, select_table *wait)
 {
-	struct sock *sk=(struct sock *) sock->data;
-	if (sk->prot->select == NULL) 
-	{
+	struct sock *sk = sock->sk;
+
+	if (sk->prot->select == NULL)
 		return(0);
-	}
-	return(sk->prot->select(sk, sel_type, wait));
+	return sk->prot->select(sock, sel_type, wait);
 }
 
 /*
@@ -1204,7 +1285,7 @@
 
 static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk=(struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 	int err;
 	int pid;
 
@@ -1223,17 +1304,17 @@
 		case FIOGETOWN:
 		case SIOCGPGRP:
 			return put_user(sk->proc, (int *)arg);
+			return(0);			
 		case SIOCGSTAMP:
 			if(sk->stamp.tv_sec==0)
 				return -ENOENT;
 			err = copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval));
 			if (err)
-			{
 				err = -EFAULT;
-			}
 			return err;
 		case SIOCADDRT:
 		case SIOCDELRT:
+		case SIOCRTMSG:
 			return(ip_rt_ioctl(cmd,(void *) arg));
 		case SIOCDARP:
 		case SIOCGARP:
@@ -1251,19 +1332,20 @@
 #endif
 			if (rarp_ioctl_hook != NULL)
 				return(rarp_ioctl_hook(cmd,(void *) arg));
-		case SIOCGIFCONF:
-		case SIOCGIFFLAGS:
-		case SIOCSIFFLAGS:
 		case SIOCGIFADDR:
 		case SIOCSIFADDR:
-		case SIOCADDMULTI:
-		case SIOCDELMULTI:
-		case SIOCGIFDSTADDR:
-		case SIOCSIFDSTADDR:
 		case SIOCGIFBRDADDR:
 		case SIOCSIFBRDADDR:
 		case SIOCGIFNETMASK:
 		case SIOCSIFNETMASK:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+			return(devinet_ioctl(cmd,(void *) arg));
+		case SIOCGIFCONF:
+		case SIOCGIFFLAGS:
+		case SIOCSIFFLAGS:
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
 		case SIOCGIFMETRIC:
 		case SIOCSIFMETRIC:
 		case SIOCGIFMEM:
@@ -1286,6 +1368,7 @@
 #else
 			return -ENOPKG;
 #endif						
+			
 		case SIOCADDDLCI:
 		case SIOCDELDLCI:
 #ifdef CONFIG_DLCI
@@ -1303,11 +1386,16 @@
 				return((*dlci_ioctl_hook)(cmd, (void *) arg));
 #endif
 			return -ENOPKG;
-			
+
 		default:
 			if ((cmd >= SIOCDEVPRIVATE) &&
-			   (cmd <= (SIOCDEVPRIVATE + 15)))
+			    (cmd <= (SIOCDEVPRIVATE + 15)))
+				return(dev_ioctl(cmd,(void *) arg));
+
+#ifdef CONFIG_NET_RADIO
+			if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
 				return(dev_ioctl(cmd,(void *) arg));
+#endif
 
 			if (sk->prot->ioctl==NULL) 
 				return(-EINVAL);
@@ -1326,7 +1414,7 @@
 static __inline__ struct sock *secondlist(unsigned short hpnum, struct sock *s,
 				int *pfirstpass, struct proto *prot)
 {
-	if (hpnum && s == NULL && (*pfirstpass)-- )
+	if (s == NULL && (*pfirstpass)-- )
 		return prot->sock_array[hpnum & (SOCK_ARRAY_SIZE - 1)];
 	else
 		return s;
@@ -1345,35 +1433,21 @@
 	s = s->next;
 	return secondlist(hpnum, s, pfirstpass, prot);
 }
-#endif
-
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
 
-struct sock *get_sock(struct proto *prot, unsigned short num,
-		      unsigned long raddr, unsigned short rnum, 
-		      unsigned long laddr, unsigned long paddr,
-		      unsigned short pnum)
+struct sock *get_sock_proxy(struct proto *prot, unsigned short num,
+				unsigned long raddr,
+				unsigned short rnum, unsigned long laddr,
+				unsigned long paddr, unsigned short pnum)
 {
 	struct sock *s = 0;
 	struct sock *result = NULL;
 	int badness = -1;
 	unsigned short hnum;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
 	unsigned short hpnum;
 	int firstpass = 1;
-#endif 
 
 	hnum = ntohs(num);
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
 	hpnum = ntohs(pnum);
-#endif 
 
 	/*
 	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
@@ -1384,43 +1458,28 @@
 	 * socket number when we choose an arbitrary one.
 	 */
 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
 	for(s = get_sock_loop_init(hnum, hpnum, s, &firstpass, prot);
 		s != NULL;
 		s = get_sock_loop_next(hnum, hpnum, s, &firstpass, prot))
-#else
-	for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-			s != NULL; s = s->next) 
-#endif
 	{
 		int score = 0;
 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
 		/* accept the addressed port or the redirect (proxy) port */
-		if (s->num != hnum && (hpnum == 0 || s->num != hpnum))
-#else
-		if (s->num != hnum) 
-#endif
+		if (s->num != hnum && s->num != hpnum)
 			continue;
 
 		if(s->dead && (s->state == TCP_CLOSE))
 			continue;
 		/* local address matches? */
 		if (s->rcv_saddr) {
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
 			/*
 			 * If this is redirected traffic, it must either
 			 * match on the redirected port/ip-address or on
 			 * the actual destination, not on a mixture.
 			 * There must be a simpler way to express this...
 			 */
-			if (hpnum
-			    ? ((s->num != hpnum || s->rcv_saddr != paddr)
-			      && (s->num != hnum || s->rcv_saddr != laddr))
-			    : (s->rcv_saddr != laddr))
-#else
-			if (s->rcv_saddr != laddr)
-#endif
+			if ((s->num != hpnum || s->rcv_saddr != paddr)
+			    && (s->num != hnum || s->rcv_saddr != laddr))
 				continue;
 			score++;
 		}
@@ -1437,29 +1496,94 @@
 			score++;
 		}
 		/* perfect match? */
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
 		if (score == 3 && s->num == hnum)
-#else
-		if (score == 3)
-#endif
 			return s;
 		/* no, check if this is the best so far.. */
 		if (score <= badness)
 			continue;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
 		/* don't accept near matches on the actual destination
 		 * port with IN_ADDR_ANY for redirected traffic, but do
 		 * allow explicit remote address listens.  (disputable)
 		 */
-		if (hpnum && s->num != hpnum && !s->rcv_saddr)
+		if (s->num != hpnum && !s->rcv_saddr)
 			continue;
+		result = s;
+		badness = score;
+  	}
+  	return result;
+}
 #endif
+
+/*
+ * This routine must find a socket given a TCP or UDP header.
+ * Everything is assumed to be in net order.
+ *
+ * We give priority to more closely bound ports: if some socket
+ * is bound to a particular foreign address, it will get the packet
+ * rather than somebody listening to any address..
+ */
+
+struct sock *get_sock(struct proto *prot, unsigned short num,
+				unsigned long raddr,
+				unsigned short rnum, unsigned long laddr)
+{
+	struct sock *s = 0;
+	struct sock *result = NULL;
+	int badness = -1;
+	unsigned short hnum;
+
+	hnum = ntohs(num);
+
+	/*
+	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
+	 * than a prime unless 3 or more sockets end up using the same
+	 * array entry.  This should not be a problem because most
+	 * well known sockets don't overlap that much, and for
+	 * the other ones, we can just be careful about picking our
+	 * socket number when we choose an arbitrary one.
+	 */
+
+	for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
+			s != NULL; s = s->next) 
+	{
+		int score = 0;
+
+		if (s->num != hnum) 
+			continue;
+
+		if(s->dead && (s->state == TCP_CLOSE))
+			continue;
+		/* local address matches? */
+		if (s->rcv_saddr) {
+			if (s->rcv_saddr != laddr)
+				continue;
+			score++;
+		}
+		/* remote address matches? */
+		if (s->daddr) {
+			if (s->daddr != raddr)
+				continue;
+			score++;
+		}
+		/* remote port matches? */
+		if (s->dummy_th.dest) {
+			if (s->dummy_th.dest != rnum)
+				continue;
+			score++;
+		}
+		/* perfect match? */
+		if (score == 3)
+			return s;
+		/* no, check if this is the best so far.. */
+		if (score <= badness)
+			continue;
 		result = s;
 		badness = score;
   	}
   	return result;
 }
 
+
 /*
  *	Deliver a datagram to raw sockets.
  */
@@ -1488,7 +1612,6 @@
   	return(NULL);
 }
 
-#ifdef CONFIG_IP_MULTICAST
 /*
  *	Deliver a datagram to broadcast/multicast sockets.
  */
@@ -1531,17 +1654,15 @@
   	return(NULL);
 }
 
-#endif
 
-struct proto_ops inet_proto_ops = {
+struct proto_ops inet_stream_ops = {
 	AF_INET,
 
-	inet_create,
 	inet_dup,
 	inet_release,
 	inet_bind,
-	inet_connect,
-	inet_socketpair,
+	inet_stream_connect,
+	NULL,
 	inet_accept,
 	inet_getname, 
 	inet_select,
@@ -1555,6 +1676,35 @@
 	inet_recvmsg
 };
 
+struct proto_ops inet_dgram_ops = {
+	AF_INET,
+
+	inet_dup,
+	inet_release,
+	inet_bind,
+	inet_dgram_connect,
+	NULL,
+	NULL,
+	inet_getname, 
+	datagram_select,
+	inet_ioctl,
+	NULL,
+	inet_shutdown,
+	inet_setsockopt,
+	inet_getsockopt,
+	inet_fcntl,
+	inet_sendmsg,
+	inet_recvmsg
+};
+
+
+
+
+struct net_proto_family inet_family_ops = {
+	AF_INET,
+	inet_create
+};
+
 extern unsigned long seq_offset;
 
 #ifdef CONFIG_PROC_FS
@@ -1596,37 +1746,33 @@
 	0, &proc_net_inode_operations,
 	udp_get_info
 };
-static struct proc_dir_entry proc_net_route = {
-	PROC_NET_ROUTE, 5, "route",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_net_inode_operations,
-	rt_get_info
-};
-static struct proc_dir_entry proc_net_rtcache = {
-	PROC_NET_RTCACHE, 8, "rt_cache",
-	S_IFREG | S_IRUGO, 1, 0, 0,
-	0, &proc_net_inode_operations,
-	rt_cache_get_info
-};
 #endif		/* CONFIG_PROC_FS */
 
+
 /*
  *	Called by socket.c on kernel startup.  
  */
  
 void inet_proto_init(struct net_proto *pro)
 {
+	struct sk_buff *dummy_skb;
 	struct inet_protocol *p;
 	int i;
 
 
 	printk("Swansea University Computer Society TCP/IP for NET3.037\n");
 
+	if (sizeof(struct inet_skb_parm) > sizeof(dummy_skb->cb))
+	{
+		printk(KERN_CRIT "inet_proto_init: panic\n");
+		return;
+	}
+
 	/*
 	 *	Tell SOCKET that we are alive... 
 	 */
    
-  	(void) sock_register(inet_proto_ops.family, &inet_proto_ops);
+  	(void) sock_register(&inet_family_ops);
 
   	seq_offset = CURRENT_TIME*250;
 
@@ -1640,7 +1786,6 @@
 		udp_sock_array[i] = NULL;
 		raw_sock_array[i] = NULL;
   	}
-
 	tcp_prot.inuse = 0;
 	tcp_prot.highestinuse = 0;
 	tcp_prot.sock_array = tcp_sock_array;
@@ -1660,28 +1805,28 @@
 		p = tmp;
 	}
 
-
 	/*
 	 *	Set the ARP module up
 	 */
 	arp_init();
-
   	/*
   	 *	Set the IP module up
   	 */
 	ip_init();
-
 	/*
 	 *	Set the ICMP layer up
 	 */
-	icmp_init(&inet_proto_ops);
+	icmp_init(&inet_family_ops);
 	/*
 	 *	Set the firewalling up
 	 */
-#if defined(CONFIG_IP_ACCT)||defined(CONFIG_IP_FIREWALL)|| \
-    defined(CONFIG_IP_MASQUERADE)
+#if defined(CONFIG_IP_ACCT)||defined(CONFIG_IP_FIREWALL)
 	ip_fw_init();
 #endif
+#ifdef CONFIG_IP_MASQUERADE
+	ip_masq_init();
+#endif
+
 	/*
 	 *	Initialise the multicast router
 	 */
@@ -1705,17 +1850,13 @@
 	 */
 
 #ifdef CONFIG_PROC_FS
-
 #ifdef CONFIG_INET_RARP
 	proc_net_register(&proc_net_rarp);
 #endif		/* RARP */
-
 	proc_net_register(&proc_net_raw);
 	proc_net_register(&proc_net_snmp);
 	proc_net_register(&proc_net_sockstat);
 	proc_net_register(&proc_net_tcp);
 	proc_net_register(&proc_net_udp);
-	proc_net_register(&proc_net_route);
-	proc_net_register(&proc_net_rtcache);
 #endif		/* CONFIG_PROC_FS */
 }
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.14/linux/net/ipv4/arp.c	Fri Nov 15 23:49:11 1996
+++ linux/net/ipv4/arp.c	Thu Dec 12 16:54:23 1996
@@ -7,13 +7,6 @@
  * high-level addresses) into a low-level hardware address (like an Ethernet
  * address).
  *
- * FIXME:
- *	Experiment with better retransmit timers
- *	Clean up the timer deletions
- *	If you create a proxy entry, set your interface address to the address
- *	and then delete it, proxies may get out of sync with reality - 
- *	check this.
- *
  * 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
@@ -60,6 +53,7 @@
  *		Mike Shaver	:	/proc/sys/net/ipv4/arp_* support
  *		Stuart Cheshire	:	Metricom and grat arp fixes
  *					*** FOR 2.1 clean this up ***
+ *		Lawrence V. Stefani: (08/12/96) Added FDDI support.
  */
 
 /* RFC1122 Status:
@@ -86,6 +80,7 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/fddidevice.h>
 #include <linux/if_arp.h>
 #include <linux/trdevice.h>
 #include <linux/skbuff.h>
@@ -105,9 +100,7 @@
 #include <net/netrom.h>
 #endif
 #endif
-#ifdef CONFIG_NET_ALIAS
 #include <linux/net_alias.h>
-#endif
 #ifdef CONFIG_ARPD
 #include <net/netlink.h>
 #endif
@@ -115,8 +108,6 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-#include <stdarg.h>
-
 /*
  *	Configurable Parameters
  */
@@ -142,9 +133,6 @@
 
 /*
  *	Soft limit on ARP cache size.
- *	Note that this number should be greater than
- *	number of simultaneously opened sockets, or else
- *	hardware header cache will not be efficient.
  */
 
 #if RT_CACHE_DEBUG >= 2
@@ -158,6 +146,16 @@
 #endif
 
 /*
+ *	Limit on unresolved ARP cache entries.
+ */
+#define ARP_MAX_UNRES (ARP_MAXSIZE/2)
+
+/*
+ *	Maximal number of skb's queued for resolution.
+ */
+#define ARP_MAX_UNRES_PACKETS 3
+
+/*
  *	If an arp request is send, ARP_RES_TIME is the timeout value until the
  *	next request is send.
  * 	RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST)
@@ -234,52 +232,67 @@
 
 struct arp_table
 {
-	struct arp_table		*next;			/* Linked entry list 		*/
-	unsigned long			last_used;		/* For expiry 			*/
+	union {
+		struct dst_entry	dst;
+		struct arp_table	*next;
+	} u;
 	unsigned long			last_updated;		/* For expiry 			*/
 	unsigned int			flags;			/* Control status 		*/
-	u32				ip;			/* ip address of entry 		*/
+	u32				ip;
 	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
+	int				hatype;
 	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
-	struct device			*dev;			/* Device the entry is tied to 	*/
-	struct hh_cache			*hh;			/* Hardware headers chain	*/
 
 	/*
 	 *	The following entries are only used for unresolved hw addresses.
 	 */
-	
 	struct timer_list		timer;			/* expire timer 		*/
 	int				retries;		/* remaining retries	 	*/
 	struct sk_buff_head		skb;			/* list of queued packets 	*/
 };
 
+#if RT_CACHE_DEBUG >= 1
+#define ASSERT_BH() if (!intr_count) printk(KERN_CRIT __FUNCTION__ " called from SPL=0\n");
+#else
+#define ASSERT_BH()
+#endif
 
-static atomic_t arp_size = 0;
+/*
+ *	Interface to generic destionation cache.
+ */
 
-#ifdef CONFIG_ARPD
-static int arpd_not_running;
-static int arpd_stamp;
-#endif
+static void arp_dst_destroy(struct dst_entry * dst);
+static struct dst_entry * arp_dst_check(struct dst_entry * dst)
+{
+	return dst;
+}
+
+static struct dst_entry * arp_dst_reroute(struct dst_entry * dst)
+{
+	return dst;
+}
 
-static unsigned int arp_bh_mask;
 
-#define ARP_BH_BACKLOG	1
+struct dst_ops arp_dst_ops =
+{
+	AF_UNSPEC,
+	arp_dst_check,
+	arp_dst_reroute,
+	arp_dst_destroy
+};
 
-/*
- *	Backlog for ARP updates.
- */
-static struct arp_table *arp_backlog;
 
-/*
- *	Backlog for incomplete entries.
- */
-static struct arp_table *arp_req_backlog;
+static atomic_t arp_size = 0;
+static atomic_t arp_unres_size = 0;
 
+#ifdef CONFIG_ARPD
+static int arpd_not_running;
+static int arpd_stamp;
+#endif
 
-static void arp_run_bh(void);
 static void arp_check_expire (unsigned long);  
 static int  arp_update (u32 sip, char *sha, struct device * dev,
-	    unsigned long updated, struct arp_table *ientry, int grat);
+			unsigned long updated, int grat);
 
 static struct timer_list arp_timer =
 	{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
@@ -313,93 +326,41 @@
 #define HASH(paddr) 		(htonl(paddr) & (ARP_TABLE_SIZE - 1))
 
 /*
- *	ARP cache semaphore.
- *
- *	Every time when someone wants to traverse arp table,
- *	he MUST call arp_fast_lock.
- *	It will guarantee that arp cache list will not change
- *	by interrupts and the entry that you found will not
- *	disappear unexpectedly.
- *	
- *	If you want to modify arp cache lists, you MUST
- *	call arp_fast_lock, and check that you are the only
- *	owner of semaphore (arp_lock == 1). If it is not the case
- *	you can defer your operation or forgot it,
- *	but DO NOT TOUCH lists.
- *
- *	However, you are allowed to change arp entry contents.
+ *	Hardware header cache.
  *
- *	Assumptions:
- *	     -- interrupt code MUST have lock/unlock balanced,
- *		you cannot lock cache on interrupt and defer unlocking
- *		to callback.
- *		In particular, it means that lock/unlock are allowed
- *		to be non-atomic. They are made atomic, but it was not
- *		necessary.
- *	     -- nobody is allowed to sleep while
- *		it keeps arp locked. (route cache has similar locking
- *		scheme, but allows sleeping)
- *		
  */
 
-static atomic_t arp_lock;
-
-#define ARP_LOCKED() (arp_lock != 1)
-
-static __inline__ void arp_fast_lock(void)
-{
-	atomic_inc(&arp_lock);
-}
-
-static __inline__ void arp_unlock(void)
-{
-	if (atomic_dec_and_test(&arp_lock) && arp_bh_mask)
-		arp_run_bh();
-}
-
 /*
- * Enqueue to FIFO list.
+ * Signal to device layer, that hardware address may be changed.
  */
 
-static void arp_enqueue(struct arp_table **q, struct arp_table *entry)
+static __inline__ void arp_update_hhs(struct arp_table * entry)
 {
-	unsigned long flags;
-	struct arp_table * tail;
+	struct hh_cache *hh;
+	void (*update)(struct hh_cache*, struct device*, unsigned char*) =
+		entry->u.dst.dev->header_cache_update;
 
-	save_flags(flags);
-	cli();
-	tail = *q;
-	if (!tail)
-		entry->next = entry;
-	else
+#if RT_CACHE_DEBUG >= 1
+	if (!update && entry->u.dst.hh)
 	{
-		entry->next = tail->next;
-		tail->next = entry;
+		printk(KERN_DEBUG "arp_update_hhs: no update callback for %s\n", entry->u.dst.dev->name);
+		return;
 	}
-	*q = entry;
-	restore_flags(flags);
-	return;
+#endif
+	for (hh=entry->u.dst.hh; hh; hh=hh->hh_next)
+		update(hh, entry->u.dst.dev, entry->ha);
 }
 
 /*
- * Dequeue from FIFO list,
- * caller should mask interrupts.
+ *	Invalidate all hh's, so that higher level will not try to use it.
  */
 
-static struct arp_table * arp_dequeue(struct arp_table **q)
+static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
 {
-	struct arp_table * entry;
+	struct hh_cache *hh;
 
-	if (*q)
-	{
-		entry = (*q)->next;
-		(*q)->next = entry->next;
-		if (entry->next == entry)
-			*q = NULL;
-		entry->next = NULL;
-		return entry;
-	}
-	return NULL;
+	for (hh=entry->u.dst.hh; hh; hh=hh->hh_next)
+		hh->hh_uptodate = 0;
 }
 
 /*
@@ -409,185 +370,62 @@
 static void arp_purge_send_q(struct arp_table *entry)
 {
 	struct sk_buff *skb;
-	unsigned long flags;
 
-	save_flags(flags);
-	cli();
+	ASSERT_BH();
+
 	/* Release the list of `skb' pointers. */
 	while ((skb = skb_dequeue(&entry->skb)) != NULL)
-	{
-		skb_device_lock(skb);
-		restore_flags(flags);
-		dev_kfree_skb(skb, FREE_WRITE);
-		cli();
-	}
-	restore_flags(flags);
+		kfree_skb(skb, FREE_WRITE);
 	return;
 }
 
-/*
- * 	Release the entry and all resources linked to it: skb's, hh's, timer
- * 	and certainly memory.
- *	The entry should be already removed from lists.
- */
-
-static void arp_free_entry(struct arp_table *entry)
+static void __inline__ arp_free(struct arp_table **entryp)
 {
-	unsigned long flags;
-	struct hh_cache *hh, *next;
-
-	del_timer(&entry->timer);
-	arp_purge_send_q(entry);
+	struct arp_table *entry = *entryp;
+	*entryp = entry->u.next;
 
-	save_flags(flags);
-	cli();
-	hh = entry->hh;
-	entry->hh = NULL;
-	restore_flags(flags);
+	ASSERT_BH();
 
-	for ( ; hh; hh = next)
-	{
-		next = hh->hh_next;
-		hh->hh_uptodate = 0;
-		hh->hh_next = NULL;
-		hh->hh_arp = NULL;
-		if (atomic_dec_and_test(&hh->hh_refcnt))
-			kfree_s(hh, sizeof(struct(struct hh_cache)));
+	if (!(entry->flags&ATF_PUBL)) {
+		atomic_dec(&arp_size);
+		if (!(entry->flags&ATF_COM))
+			atomic_dec(&arp_unres_size);
 	}
+	del_timer(&entry->timer);
+	arp_purge_send_q(entry);
+	arp_invalidate_hhs(entry);
 
-	kfree_s(entry, sizeof(struct arp_table));
-	atomic_dec(&arp_size);
-	return;
-}
-
-/*
- *	Hardware header cache.
- *
- *	BEWARE! Hardware header cache has no locking, so that
- *	it requires especially careful handling.
- *	It is the only part of arp+route, where a list
- *	should be traversed with masked interrupts.
- *	Luckily, this list contains one element 8), as rule.
- */
-
-/*
- *	How many users has this entry?
- *	The answer is reliable only when interrupts are masked.
- */
-
-static __inline__ int arp_count_hhs(struct arp_table * entry)
-{
-	struct hh_cache *hh;
-	int count = 0;
-
-	for (hh = entry->hh; hh; hh = hh->hh_next)
-		count += hh->hh_refcnt-1;
-
-	return count;
-}
-
-/*
- * Signal to device layer, that hardware address may be changed.
- */
-
-static __inline__ void arp_update_hhs(struct arp_table * entry)
-{
-	struct hh_cache *hh;
-
-	for (hh=entry->hh; hh; hh=hh->hh_next)
-		entry->dev->header_cache_update(hh, entry->dev, entry->ha);
-}
-
-/*
- *	Invalidate all hh's, so that higher level will not try to use it.
- */
-
-static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
-{
-	struct hh_cache *hh;
-
-	for (hh=entry->hh; hh; hh=hh->hh_next)
-		hh->hh_uptodate = 0;
+	dst_free(&entry->u.dst);
 }
 
-/*
- *	Atomic attaching new hh entry.
- *	Return 1, if entry has been freed, rather than attached.
- */
 
-static int arp_set_hh(struct hh_cache **hhp, struct hh_cache *hh)
+static void arp_dst_destroy(struct dst_entry * dst)
 {
-	unsigned long flags;
-	struct hh_cache *hh1;
-	struct arp_table *entry;
+	struct arp_table *entry = (struct arp_table*)dst;
+	struct hh_cache *hh, *next;
 
-	atomic_inc(&hh->hh_refcnt);
+	ASSERT_BH();
 
-	save_flags(flags);
-	cli();
-	if ((hh1 = *hhp) == NULL)
-	{
-		*hhp = hh;
-		restore_flags(flags);
-		return 0;
-	}
+	del_timer(&entry->timer);
+	arp_purge_send_q(entry);
 
-	entry = (struct arp_table*)hh->hh_arp;
+	hh = entry->u.dst.hh;
+	entry->u.dst.hh = NULL;
 
-	/*
-	 *	An hh1 entry is already attached to this point.
-	 *	Is it not linked to arp entry? Link it!
-	 */
-	if (!hh1->hh_arp && entry)
+	for ( ; hh; hh = next)
 	{
-		atomic_inc(&hh1->hh_refcnt);
-		hh1->hh_next = entry->hh;
-		entry->hh = hh1;
-		hh1->hh_arp = (void*)entry;
-		restore_flags(flags);
-
-		if (entry->flags & ATF_COM)
-			entry->dev->header_cache_update(hh1, entry->dev, entry->ha);
-#if RT_CACHE_DEBUG >= 1
-		printk("arp_set_hh: %08x is reattached. Good!\n", entry->ip);
-#endif
-	}
-#if RT_CACHE_DEBUG >= 1
-	else if (entry)
-		printk("arp_set_hh: %08x rr1 ok!\n", entry->ip);
+		next = hh->hh_next;
+		hh->hh_uptodate = 0;
+		hh->hh_next = NULL;
+		if (atomic_dec_and_test(&hh->hh_refcnt))
+		{
+#if RT_CACHE_DEBUG >= 2
+			extern atomic_t hh_count;
+			atomic_dec(&hh_count);
 #endif
-	restore_flags(flags);
-	if (atomic_dec_and_test(&hh->hh_refcnt))
-		kfree_s(hh, sizeof(struct hh_cache));
-	return 1;
-}
-
-static __inline__ struct hh_cache * arp_alloc_hh(int htype)
-{
-	struct hh_cache *hh;
-	hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
-	if (hh)
-	{
-		memset(hh, 0, sizeof(struct hh_cache));
-		hh->hh_type = htype;
-	}
-	return hh;
-}
-
-/*
- * Test if a hardware address is all zero
- */
-
-static __inline__ int empty(unsigned char * addr, int len)
-{
-	while (len > 0)
-	{
-		if (*addr)
-			return 0;
-		len--;
-		addr++;
+			kfree_s(hh, sizeof(struct(struct hh_cache)));
+		}
 	}
-	return 1;
 }
 
 
@@ -610,7 +448,6 @@
 	if (skb == NULL)
 		return;
 
-	skb->free=1;
 	arpreq=(struct arpd_request *)skb_put(skb, sizeof(struct arpd_request));
 	arpreq->req = req;
 	arpreq->ip  = addr;
@@ -633,14 +470,14 @@
  *	Send ARPD update message.
  */
 
-static __inline__ void arpd_update(struct arp_table * entry)
+static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha)
 {
 	if (arpd_not_running)
 		return;
-	arpd_send(ARPD_UPDATE, entry->ip, entry->dev, entry->ha,
-		  entry->last_updated);
+	arpd_send(ARPD_UPDATE, ip, dev, ha, jiffies);
 }
 
+
 /*
  *	Send ARPD lookup request.
  */
@@ -664,7 +501,7 @@
 }
 
 
-static int arpd_callback(struct sk_buff *skb)
+static int arpd_callback(int minor, struct sk_buff *skb)
 {
 	struct device * dev;
 	struct arpd_request *retreq;
@@ -686,7 +523,7 @@
 		return -EINVAL;
 	}
 
-	if (!retreq->updated || empty(retreq->ha, sizeof(retreq->ha)))
+	if (!retreq->updated)
 	{
 /*
  *	Invalid mapping: drop it and send ARP broadcast.
@@ -696,9 +533,9 @@
 	}
 	else
 	{
-		arp_fast_lock();
-		arp_update(retreq->ip, retreq->ha, dev, retreq->updated, NULL, 0);
-		arp_unlock();
+		start_bh_atomic();
+		arp_update(retreq->ip, retreq->ha, dev, retreq->updated, 0);
+		end_bh_atomic();
 	}
 
 	kfree_skb(skb, FREE_READ);
@@ -707,7 +544,7 @@
 
 #else
 
-static __inline__ void arpd_update(struct arp_table * entry)
+static __inline__ void arpd_update(u32 ip, struct device *dev, char *ha)
 {
 	return;
 }
@@ -732,17 +569,11 @@
 	struct arp_table *entry, **pentry;
 	struct arp_table **oldest_entry = NULL;
 	unsigned long oldest_used = ~0;
-	unsigned long flags;
 	unsigned long now = jiffies;
 	int result = 0;
 
 	static last_index;
 
-	if (ARP_LOCKED())
-		return 0;
-
-	save_flags(flags);
-
 	if (last_index >= ARP_TABLE_SIZE)
 		last_index = 0;
 
@@ -754,31 +585,26 @@
 		{
 			if (!(entry->flags & ATF_PERM))
 			{
-				int users;
-				cli();
-				users = arp_count_hhs(entry);
-
-				if (!users && now - entry->last_used > sysctl_arp_timeout)
+				if (!entry->u.dst.refcnt &&
+				    now - entry->u.dst.lastuse > sysctl_arp_timeout)
 				{
-					*pentry = entry->next;
-					restore_flags(flags);
 #if RT_CACHE_DEBUG >= 2
 					printk("arp_force_expire: %08x expired\n", entry->ip);
 #endif
-					arp_free_entry(entry);
+					arp_free(pentry);
 					result++;
 					if (arp_size < ARP_MAXSIZE)
 						goto done;
 					continue;
 				}
-				restore_flags(flags);
-				if (!users && entry->last_used < oldest_used)
+				if (!entry->u.dst.refcnt &&
+				    entry->u.dst.lastuse < oldest_used)
 				{
 					oldest_entry = pentry;
-					oldest_used = entry->last_used;
+					oldest_used = entry->u.dst.lastuse;
 				}
 			}
-			pentry = &entry->next;
+			pentry = &entry->u.next;
 		}
 	}
 
@@ -786,15 +612,42 @@
 	if (result || !oldest_entry)
 		return result;
 
-	entry = *oldest_entry;
-	*oldest_entry = entry->next;
 #if RT_CACHE_DEBUG >= 2
-	printk("arp_force_expire: expiring %08x\n", entry->ip);
+	printk("arp_force_expire: expiring %08x\n", (*oldest_entry)->ip);
 #endif
-	arp_free_entry(entry);
+	arp_free(oldest_entry);
 	return 1;
 }
 
+static void arp_unres_expire(void)
+{
+	int i;
+	struct arp_table *entry, **pentry;
+	unsigned long now = jiffies;
+
+	for (i = 0; i < ARP_TABLE_SIZE; i++) {
+		pentry = &arp_tables[i & (ARP_TABLE_SIZE-1)];
+
+		while ((entry = *pentry) != NULL) {
+			if (!(entry->flags & (ATF_PERM|ATF_COM)) &&
+			    (entry->retries < sysctl_arp_max_tries ||
+			     entry->timer.expires - now <
+			     sysctl_arp_res_time - sysctl_arp_res_time/32)) {
+				if (!entry->u.dst.refcnt) {
+#if RT_CACHE_DEBUG >= 2
+					printk("arp_unres_expire: %08x discarded\n", entry->ip);
+#endif
+					arp_free(pentry);
+					continue;
+				}
+				arp_purge_send_q(entry);
+			}
+			pentry = &entry->u.next;
+		}
+	}
+}
+
+
 /*
  *	Check if there are entries that are too old and remove them. If the
  *	ATF_PERM flag is set, they are always left in the arp cache (permanent
@@ -817,61 +670,48 @@
 
 	ip_rt_check_expire();
 
-	arp_fast_lock();
-
-	if (!ARP_LOCKED())
+	for (i = 0; i < ARP_TABLE_SIZE; i++)
 	{
-
-		for (i = 0; i < ARP_TABLE_SIZE; i++)
-		{
-			struct arp_table *entry, **pentry;
+		struct arp_table *entry, **pentry;
 		
-			pentry = &arp_tables[i];
+		pentry = &arp_tables[i];
 
-			while ((entry = *pentry) != NULL)
+		while ((entry = *pentry) != NULL)
+		{
+			if (entry->flags & ATF_PERM)
 			{
-				if (entry->flags & ATF_PERM)
-				{
-					pentry = &entry->next;
-					continue;
-				}
+				pentry = &entry->u.next;
+				continue;
+			}
 
-				cli();
-				if (now - entry->last_used > sysctl_arp_timeout
-				    && !arp_count_hhs(entry))
-				{
-					*pentry = entry->next;
-					sti();
+			if (!entry->u.dst.refcnt &&
+			    now - entry->u.dst.lastuse > sysctl_arp_timeout)
+			{
 #if RT_CACHE_DEBUG >= 2
-					printk("arp_expire: %08x expired\n", entry->ip);
+				printk("arp_expire: %08x expired\n", entry->ip);
 #endif
-					arp_free_entry(entry);
-					continue;
-				}
-				sti();
-				if (entry->last_updated
-				    && now - entry->last_updated > sysctl_arp_confirm_interval
-				    && !(entry->flags & ATF_PERM))
-				{
-					struct device * dev = entry->dev;
-					entry->retries = sysctl_arp_max_tries+sysctl_arp_max_pings;
-					del_timer(&entry->timer);
-					entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
-					add_timer(&entry->timer);
-					arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
-						 dev, dev->pa_addr, entry->ha,
-						 dev->dev_addr, NULL);
+				arp_free(pentry);
+				continue;
+			}
+			if (entry->last_updated &&
+			    now - entry->last_updated > sysctl_arp_confirm_interval)
+			{
+				struct device * dev = entry->u.dst.dev;
+				entry->retries = sysctl_arp_max_tries+sysctl_arp_max_pings;
+				del_timer(&entry->timer);
+				entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
+				add_timer(&entry->timer);
+				arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip,
+					 dev, dev->pa_addr, entry->ha,
+					 dev->dev_addr, NULL);
 #if RT_CACHE_DEBUG >= 2
-					printk("arp_expire: %08x requires confirmation\n", entry->ip);
+				printk("arp_expire: %08x requires confirmation\n", entry->ip);
 #endif
-				}
-				pentry = &entry->next;	/* go to next entry */
 			}
+			pentry = &entry->u.next;	/* go to next entry */
 		}
 	}
 
-	arp_unlock();
-
 	/*
 	 *	Set the timer again.
 	 */
@@ -891,35 +731,10 @@
 	struct arp_table *entry = (struct arp_table *) arg;
 	struct arp_table **pentry;
 	unsigned long hash;
-	unsigned long flags;
-
-	arp_fast_lock();
 
-	save_flags(flags);
-	cli();
 	del_timer(&entry->timer);
 
-	/*
-	 *	If arp table is locked, defer expire processing.
-	 */
-	if (ARP_LOCKED())
-	{
-#if RT_CACHE_DEBUG >= 1
-		printk(KERN_DEBUG "arp_expire_request: %08x deferred\n", entry->ip);
-#endif
-		entry->timer.expires = jiffies + HZ/10;
-		add_timer(&entry->timer);
-		restore_flags(flags);
-		arp_unlock();
-		return;
-	}
-
-	/*
-	 *	Since all timeouts are handled with interrupts enabled, there is a
-	 *	small chance, that this entry has just been resolved by an incoming
-	 *	packet. This is the only race condition, but it is handled...
-	 *
-	 *	One exception: if entry is COMPLETE but old,
+	/*	If entry is COMPLETE but old,
 	 *	it means that point-to-point ARP ping has been failed
 	 *	(It really occurs with Cisco 4000 routers)
 	 *	We should reconfirm it.
@@ -927,17 +742,11 @@
 	
 	if ((entry->flags & ATF_COM) && entry->last_updated
 	    && jiffies - entry->last_updated <= sysctl_arp_confirm_interval)
-	{
-		restore_flags(flags);
-		arp_unlock();
 		return;
-	}
-
-	restore_flags(flags);
 
 	if (entry->last_updated && --entry->retries > 0)
 	{
-		struct device *dev = entry->dev;
+		struct device *dev = entry->u.dst.dev;
 
 #if RT_CACHE_DEBUG >= 2
 		printk("arp_expire_request: %08x timed out\n", entry->ip);
@@ -948,7 +757,6 @@
 		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr,
 			 entry->retries > sysctl_arp_max_tries ? entry->ha : NULL,
 			 dev->dev_addr, NULL);
-		arp_unlock();
 		return;
 	}
 
@@ -958,8 +766,7 @@
 
 	arp_purge_send_q(entry);
 
-	cli();
-	if (arp_count_hhs(entry))
+	if (entry->u.dst.refcnt)
 	{
 		/*
 		 *	The host is dead, but someone refers to it.
@@ -969,32 +776,29 @@
 		 *	to ARP_DEAD_RES_TIME.
 		 */
 
-		struct device *dev = entry->dev;
+		struct device *dev = entry->u.dst.dev;
 #if RT_CACHE_DEBUG >= 2
 		printk("arp_expire_request: %08x is dead\n", entry->ip);
 #endif
 		entry->retries = sysctl_arp_max_tries;
+		if (entry->flags&ATF_COM)
+			atomic_inc(&arp_unres_size);
 		entry->flags &= ~ATF_COM;
 		arp_invalidate_hhs(entry);
-		restore_flags(flags);
 
 		/*
 		 *	Declare the entry dead.
 		 */
 		entry->last_updated = 0;
-		arpd_update(entry);
 
 		entry->timer.expires = jiffies + sysctl_arp_dead_res_time;
 		add_timer(&entry->timer);
 		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
 			 NULL, dev->dev_addr, NULL);
-		arp_unlock();
 		return;
 	}
-	restore_flags(flags);
 
 	entry->last_updated = 0;
-	arpd_update(entry);
 
 	hash = HASH(entry->ip);
 
@@ -1004,47 +808,48 @@
 	{
 		if (*pentry != entry)
 		{
-			pentry = &(*pentry)->next;
+			pentry = &(*pentry)->u.next;
 			continue;
 		}
-		*pentry = entry->next;
 #if RT_CACHE_DEBUG >= 2
 		printk("arp_expire_request: %08x is killed\n", entry->ip);
 #endif
-		arp_free_entry(entry);
+		arp_free(pentry);
 	}
-	arp_unlock();
 }
 
 
 /* 
  * Allocate memory for a new entry.  If we are at the maximum limit
- * of the internal ARP cache, arp_force_expire() an entry.  NOTE:  
- * arp_force_expire() needs the cache to be locked, so therefore
- * arp_alloc_entry() should only be called with the cache locked too!
+ * of the internal ARP cache, arp_force_expire() an entry.
  */
 
-static struct arp_table * arp_alloc_entry(void)
+static struct arp_table * arp_alloc(int how)
 {
 	struct arp_table * entry;
 
-
-	if (arp_size >= ARP_MAXSIZE)
+	if (how && arp_size >= ARP_MAXSIZE)
 		arp_force_expire();
+	if (how > 1 && arp_unres_size >= ARP_MAX_UNRES) {
+		arp_unres_expire();
+		if (arp_unres_size >= ARP_MAX_UNRES) {
+			printk("arp_unres_size=%d\n", arp_unres_size);
+			return NULL;
+		}
+	}
 
-	entry = (struct arp_table *)
-		kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+	entry = (struct arp_table *)dst_alloc(sizeof(struct arp_table), &arp_dst_ops);
 
 	if (entry != NULL)
 	{
-		atomic_inc(&arp_size);
-		memset(entry, 0, sizeof(struct arp_table));
+		if (how)
+			atomic_inc(&arp_size);
 
                 entry->mask = DEF_ARP_NETMASK;
 		init_timer(&entry->timer);
 		entry->timer.function = arp_expire_request;
 		entry->timer.data = (unsigned long)entry;
-		entry->last_updated = entry->last_used = jiffies;
+		entry->last_updated = jiffies;
 		skb_queue_head_init(&entry->skb);
 	}
 	return entry;
@@ -1069,29 +874,25 @@
 	arpd_stamp++;
 #endif
 
-	arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1	 
-	if (ARP_LOCKED())
-		printk("arp_device_event: impossible\n");
-#endif
-
 	for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
 	{
 		struct arp_table *entry;
 		struct arp_table **pentry = &arp_tables[i];
 
+		start_bh_atomic();
+
 		while ((entry = *pentry) != NULL)
 		{
-			if (entry->dev == dev)
+			if (entry->u.dst.dev != dev)
 			{
-				*pentry = entry->next;	/* remove from list */
-				arp_free_entry(entry);
+				pentry = &entry->u.next;
+				continue;
 			}
-			else
-				pentry = &entry->next;	/* go to next entry */
+			arp_free(pentry);
 		}
+
+		end_bh_atomic();
 	}
-	arp_unlock();
 	return NOTIFY_DONE;
 }
 
@@ -1105,63 +906,31 @@
 {
 	struct sk_buff *skb;
 
-	unsigned long flags;
+	ASSERT_BH();
 
-	/*
-	 *	Empty the entire queue, building its data up ready to send
-	 */
-	
-	if(!(entry->flags&ATF_COM))
-	{
-		printk(KERN_ERR "arp_send_q: incomplete entry for %s\n",
-				in_ntoa(entry->ip));
-		/* Can't flush the skb, because RFC1122 says to hang on to */
-		/* at least one from any unresolved entry.  --MS */
-		/* What's happened is that someone has 'unresolved' the entry
-		   as we got to use it - this 'can't happen' -- AC */
-		return;
-	}
-
-	save_flags(flags);
-	
-	cli();
-	while((skb = skb_dequeue(&entry->skb)) != NULL)
-	{
-		IS_SKB(skb);
-		skb_device_lock(skb);
-		restore_flags(flags);
-		if(!skb->dev->rebuild_header(skb->data,skb->dev,skb->raddr,skb))
-		{
-			skb->arp  = 1;
-			if(skb->sk==NULL)
-				dev_queue_xmit(skb, skb->dev, 0);
-			else
-				dev_queue_xmit(skb,skb->dev,skb->sk->priority);
-		}
-		cli();
+	while((skb = skb_dequeue(&entry->skb)) != NULL)	{
+		dev_queue_xmit(skb);
 	}
-	restore_flags(flags);
 }
 
 
 static int
 arp_update (u32 sip, char *sha, struct device * dev,
-	    unsigned long updated, struct arp_table *ientry, int grat)
+	    unsigned long updated, int grat)
 {
 	struct arp_table * entry;
 	unsigned long hash;
-	int do_arpd = 0;
 
 	if (updated == 0)
 	{
 		updated = jiffies;
-		do_arpd = 1;
+		arpd_update(sip, dev, sha);
 	}
 
 	hash = HASH(sip);
 
-	for (entry=arp_tables[hash]; entry; entry = entry->next)
-		if (entry->ip == sip && entry->dev == dev)
+	for (entry=arp_tables[hash]; entry; entry = entry->u.next)
+		if (entry->ip == sip && entry->u.dst.dev == dev)
 			break;
 
 	if (entry)
@@ -1173,28 +942,24 @@
 		{
 			del_timer(&entry->timer);
 			entry->last_updated = updated;
-			if (memcmp(entry->ha, sha, dev->addr_len)!=0)
+			if (memcmp(entry->ha, sha, dev->addr_len) != 0)
 			{
 				memcpy(entry->ha, sha, dev->addr_len);
 				if (entry->flags & ATF_COM)
 					arp_update_hhs(entry);
 			}
-			if (do_arpd)
-				arpd_update(entry);
 		}
 
 		if (!(entry->flags & ATF_COM))
 		{
 /*
- *	This entry was incomplete.  Delete the retransmit timer
- *	and switch to complete status.
+ *	Switch to complete status.
  */
 			entry->flags |= ATF_COM;
+			atomic_dec(&arp_unres_size);
 			arp_update_hhs(entry);
 /* 
- *	Send out waiting packets. We might have problems, if someone is 
- *	manually removing entries right now -- entry might become invalid 
- *	underneath us.
+ *	Send out waiting packets.
  */
 			arp_send_q(entry);
 		}
@@ -1204,39 +969,23 @@
 /*
  * 	No entry found.  Need to add a new entry to the arp table.
  */
-	entry = ientry;
-
-	if (grat && !entry)
+	if (grat)
 		return 0;
 
+	entry = arp_alloc(1);
 	if (!entry)
-	{
-		entry = arp_alloc_entry();
-		if (!entry)
-			return 0;
-
-		entry->ip = sip;
-		entry->flags = ATF_COM;
-		memcpy(entry->ha, sha, dev->addr_len);
-		entry->dev = dev;
-	}
+		return 0;
 
+	entry->ip = sip;
+	entry->flags = ATF_COM;
+	memcpy(entry->ha, sha, dev->addr_len);
+	entry->u.dst.dev = dev;
+	entry->hatype = dev->type;
 	entry->last_updated = updated;
-	entry->last_used = jiffies;
-	if (do_arpd)
-		arpd_update(entry);
 
-	if (!ARP_LOCKED())
-	{
-		entry->next = arp_tables[hash];
-		arp_tables[hash] = entry;
-		return 0;
-	}
-#if RT_CACHE_DEBUG >= 2
-	printk("arp_update: %08x backlogged\n", entry->ip);
-#endif
-	arp_enqueue(&arp_backlog, entry);
-	arp_bh_mask |= ARP_BH_BACKLOG;
+	entry->u.next = arp_tables[hash];
+	arp_tables[hash] = entry;
+	dst_release(&entry->u.dst);
 	return 0;
 }
 
@@ -1246,8 +995,8 @@
 {
 	struct arp_table *entry;
 
-	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->next)
-		if (entry->ip == paddr && (!dev || entry->dev == dev))
+	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->u.next)
+		if (entry->ip == paddr && entry->u.dst.dev == dev)
 			return entry;
 	return NULL;
 }
@@ -1260,21 +1009,21 @@
 {
 	struct arp_table *entry;
 
-	arp_fast_lock();
+	start_bh_atomic();
 
 	entry = arp_lookup(paddr, dev);
 
 	if (entry != NULL)
 	{
-		entry->last_used = jiffies;
+		entry->u.dst.lastuse = jiffies;
 		if (entry->flags & ATF_COM)
 		{
 			memcpy(haddr, entry->ha, dev->addr_len);
-			arp_unlock();
+			end_bh_atomic();
 			return 1;
 		}
 	}
-	arp_unlock();
+	end_bh_atomic();
 	return 0;
 }
 
@@ -1287,9 +1036,9 @@
 			printk(KERN_DEBUG "ARP: arp called for own IP address\n");
 			memcpy(haddr, dev->dev_addr, dev->addr_len);
 			return 1;
-#ifdef CONFIG_IP_MULTICAST
 		case IS_MULTICAST:
-			if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802)
+			if(dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802 
+			   || dev->type==ARPHRD_FDDI)
 			{
 				u32 taddr;
 				haddr[0]=0x01;
@@ -1306,7 +1055,6 @@
 		/*
 		 *	If a device does not support multicast broadcast the stuff (eg AX.25 for now)
 		 */
-#endif
 		
 		case IS_BROADCAST:
 			memcpy(haddr, dev->broadcast, dev->addr_len);
@@ -1315,57 +1063,49 @@
 	return 0;
 }
 
-/*
- *	Create a new unresolved entry.
- */
 
-struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct hh_cache *hh, struct sk_buff *skb)
+static void arp_start_resolution(struct arp_table *entry)
 {
-	struct arp_table *entry;
+	struct device * dev = entry->u.dst.dev;
 
-	entry = arp_alloc_entry();
-
-	if (entry != NULL)
-	{
-		entry->ip = paddr;
-		entry->dev = dev;
-		if (hh)
-		{
-			entry->hh = hh;
-			atomic_inc(&hh->hh_refcnt);
-			hh->hh_arp = (void*)entry;
-		}
-		entry->timer.expires = jiffies + sysctl_arp_res_time;
+	del_timer(&entry->timer);
+	entry->timer.expires = jiffies + sysctl_arp_res_time;
+	entry->retries = sysctl_arp_max_tries;
+	add_timer(&entry->timer);
+#ifdef CONFIG_ARPD
+	if (!arpd_not_running)
+		arpd_lookup(entry->ip, entry->dev);
+	else
+#endif
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev,
+			 dev->pa_addr, NULL, dev->dev_addr, NULL);
+}
+
+/*
+ *	Create a new unresolved entry.
+ */
+
+struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct sk_buff *skb)
+{
+	struct arp_table *entry;
+	unsigned long hash = HASH(paddr);
+
+	entry = arp_alloc(2);
+
+	if (entry != NULL)
+	{
+		entry->ip = paddr;
+		entry->u.dst.dev = dev;
+		entry->hatype = dev->type;
 
 		if (skb != NULL)
-		{
 			skb_queue_tail(&entry->skb, skb);
-			skb_device_unlock(skb);
-		}
 
-		if (!ARP_LOCKED())
-		{
-			unsigned long hash = HASH(paddr);
-			entry->next = arp_tables[hash];
-			arp_tables[hash] = entry;
-			add_timer(&entry->timer);
-			entry->retries = sysctl_arp_max_tries;
-#ifdef CONFIG_ARPD
-			if (!arpd_not_running)
-				arpd_lookup(paddr, dev);
-			else
-#endif
-				arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, 
-					 dev->dev_addr, NULL);
-		}
-		else
-		{
-#if RT_CACHE_DEBUG >= 2
-			printk("arp_new_entry: %08x backlogged\n", entry->ip);
-#endif
-			arp_enqueue(&arp_req_backlog, entry);
-			arp_bh_mask |= ARP_BH_BACKLOG;
-		}
+		atomic_inc(&arp_unres_size);
+		entry->u.next = arp_tables[hash];
+		arp_tables[hash] = entry;
+		arp_start_resolution(entry);
+		dst_release(&entry->u.dst);
 	}
 	return entry;
 }
@@ -1375,21 +1115,29 @@
  *	Find an arp mapping in the cache. If not found, post a request.
  */
 
-int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
-	     u32 saddr, struct sk_buff *skb)
+int arp_find(unsigned char *haddr, struct sk_buff *skb)
 {
+	struct device *dev = skb->dev;
+	u32 paddr;
 	struct arp_table *entry;
 	unsigned long hash;
 
-	if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
-	{
+	if (!skb->dst) {
+		printk(KERN_DEBUG "arp_find called with dst==NULL\n");
+		return 1;
+	}
+
+	paddr = ((struct rtable*)skb->dst)->rt_gateway;
+
+	if (arp_set_predefined(__ip_chk_addr(paddr), haddr, paddr, dev)) {
 		if (skb)
 			skb->arp = 1;
 		return 0;
 	}
 
 	hash = HASH(paddr);
-	arp_fast_lock();
+
+	start_bh_atomic();
 
 	/*
 	 *	Find an entry
@@ -1400,11 +1148,11 @@
 	{
 		if (entry->flags & ATF_COM)
 		{
-			entry->last_used = jiffies;
+			entry->u.dst.lastuse = jiffies;
 			memcpy(haddr, entry->ha, dev->addr_len);
 			if (skb)
 				skb->arp = 1;
-			arp_unlock();
+			end_bh_atomic();
 			return 0;
 		}
 
@@ -1417,8 +1165,10 @@
 		{
 			if (entry->last_updated)
 			{
-				skb_queue_tail(&entry->skb, skb);
-				skb_device_unlock(skb);
+				if (entry->skb.qlen < ARP_MAX_UNRES_PACKETS)
+					skb_queue_tail(&entry->skb, skb);
+				else
+					kfree_skb(skb, FREE_WRITE);
 			}
 			/*
 			 * If last_updated==0 host is dead, so
@@ -1426,235 +1176,126 @@
 			 */
 			else
 			{
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
-				dev_kfree_skb(skb, FREE_WRITE);
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+				kfree_skb(skb, FREE_WRITE);
 			}
 		}
-		arp_unlock();
+		end_bh_atomic();
 		return 1;
 	}
 
-	entry = arp_new_entry(paddr, dev, NULL, skb);
+	entry = arp_new_entry(paddr, dev, skb);
 
 	if (skb != NULL && !entry)
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 
-	arp_unlock();
+	end_bh_atomic();
 	return 1;
 }
 
-/*
- *	Binding hardware header cache entry.
- *	It is the only really complicated part of arp code.
- *	We have no locking for hh records, so that
- *	all possible race conditions should be resolved by
- *	cli()/sti() pairs.
- *
- *	Important note: hhs never disappear from lists, if ARP_LOCKED,
- *	this fact allows to scan hh lists with enabled interrupts,
- *	but results in generating duplicate hh entries.
- *	It is harmless. (and I've never seen such event)
- *
- *	Returns 0, if hh has been just created, so that
- *	caller should fill it.
- */
-
-int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr)
+int arp_find_1(unsigned char *haddr, struct dst_entry *dst,
+	       struct dst_entry *neigh)
 {
+	struct rtable *rt = (struct rtable*)dst;
+	struct device *dev = dst->dev;
+	u32 paddr = rt->rt_gateway;
 	struct arp_table *entry;
-	struct hh_cache *hh;
-	int addr_hint;
-	unsigned long flags;
-
-	save_flags(flags);
+	unsigned long hash;
 
-	if ((addr_hint = ip_chk_addr(paddr)) != 0)
+	if (!neigh)
 	{
-		unsigned char haddr[MAX_ADDR_LEN];
-		if (*hhp)
+		if ((rt->rt_flags & RTF_MULTICAST) &&
+		    (dev->type==ARPHRD_ETHER || dev->type==ARPHRD_IEEE802))
+		{
+			u32 taddr;
+			haddr[0]=0x01;
+			haddr[1]=0x00;
+			haddr[2]=0x5e;
+			taddr=ntohl(paddr);
+			haddr[5]=taddr&0xff;
+			taddr=taddr>>8;
+			haddr[4]=taddr&0xff;
+			taddr=taddr>>8;
+			haddr[3]=taddr&0x7f;
 			return 1;
-		hh = arp_alloc_hh(htype);
-		if (!hh)
+		}
+		if (rt->rt_flags & (RTF_BROADCAST|RTF_MULTICAST))
+		{
+			memcpy(haddr, dev->broadcast, dev->addr_len);
 			return 1;
-		arp_set_predefined(addr_hint, haddr, paddr, dev);
-		dev->header_cache_update(hh, dev, haddr);
-		return arp_set_hh(hhp, hh);
-	}
-
-	arp_fast_lock();
-
-	entry = arp_lookup(paddr, dev);
-
-	if (entry)
-	{
-		for (hh = entry->hh; hh; hh=hh->hh_next)
-			if (hh->hh_type == htype)
-				break;
-
-		if (hh)
+		}
+		if (rt->rt_flags & RTF_LOCAL)
 		{
-			arp_set_hh(hhp, hh);
-			arp_unlock();
+			printk(KERN_DEBUG "ARP: arp called for own IP address\n");
+			memcpy(haddr, dev->dev_addr, dev->addr_len);
 			return 1;
 		}
+		return 0;
 	}
 
-	hh = arp_alloc_hh(htype);
-	if (!hh)
-	{
-		arp_unlock();
-		return 1;
-	}
-
-	if (entry)
-	{
-
-		cli();
-		hh->hh_arp = (void*)entry;
-		hh->hh_next = entry->hh;
-		entry->hh = hh;
-		atomic_inc(&hh->hh_refcnt);
-		restore_flags(flags);
+	hash = HASH(paddr);
 
-		if (entry->flags & ATF_COM)
-			dev->header_cache_update(hh, dev, entry->ha);
+	start_bh_atomic();
 
-		if (arp_set_hh(hhp, hh))
-		{
-			arp_unlock();
-			return 0;
-		}
-
-		entry->last_used = jiffies;
-		arp_unlock();
-		return 0;
-	}
+	entry = (struct arp_table*)neigh;
 
-	entry = arp_new_entry(paddr, dev, hh, NULL);
-	if (entry == NULL)
+	if (entry->flags & ATF_COM)
 	{
-		kfree_s(hh, sizeof(struct hh_cache));
-		arp_unlock();
+		entry->u.dst.lastuse = jiffies;
+		memcpy(haddr, entry->ha, dev->addr_len);
+		end_bh_atomic();
 		return 1;
 	}
 
-	if (!arp_set_hh(hhp, hh))
-	{
-		arp_unlock();
-		return 0;
-	}
-	arp_unlock();
-	return 1;
+	end_bh_atomic();
+	return 0;
 }
 
-static void arp_run_bh()
+
+struct dst_entry* arp_find_neighbour(struct dst_entry *dst, int resolve)
 {
-	unsigned long flags;
-	struct arp_table *entry, *entry1;
-	struct device  * dev;
+	struct rtable *rt = (struct rtable*)dst;
+	struct device *dev = rt->u.dst.dev;
+	u32 paddr = rt->rt_gateway;
+	struct arp_table *entry;
 	unsigned long hash;
-	struct hh_cache *hh;
-	u32 sip;
 
-	save_flags(flags);
-	cli();
-	arp_fast_lock();
+	if (dst->ops->family != AF_INET)
+		return NULL;
 
-	while (arp_bh_mask)
-	{
-		arp_bh_mask  &= ~ARP_BH_BACKLOG;
+	if ((dev->flags & (IFF_LOOPBACK|IFF_NOARP)) ||
+	    (rt->rt_flags & (RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST)))
+		return NULL;
 
-		while ((entry = arp_dequeue(&arp_backlog)) != NULL)
-		{
-			restore_flags(flags);
-			if (arp_update(entry->ip, entry->ha, entry->dev, 0, entry, 0))
-				arp_free_entry(entry);
-			cli();
-		}
+	hash = HASH(paddr);
 
-		cli();
-		while ((entry = arp_dequeue(&arp_req_backlog)) != NULL)
-		{
-			restore_flags(flags);
+	start_bh_atomic();
 
-			dev = entry->dev;
-			sip = entry->ip;
-			hash = HASH(sip);
+	/*
+	 *	Find an entry
+	 */
+	entry = arp_lookup(paddr, dev);
 
-			for (entry1 = arp_tables[hash]; entry1; entry1 = entry1->next)
-				if (entry1->ip == sip && entry1->dev == dev)
-					break;
+	if (entry != NULL) 	/* It exists */
+	{
+		atomic_inc(&entry->u.dst.refcnt);
+		end_bh_atomic();
+		entry->u.dst.lastuse = jiffies;
+		return (struct dst_entry*)entry;
+	}
 
-			if (!entry1)
-			{
-				cli();
-				entry->next = arp_tables[hash];
-				arp_tables[hash] = entry;
-				restore_flags(flags);
-				entry->timer.expires = jiffies + sysctl_arp_res_time;
-				entry->retries = sysctl_arp_max_tries;
-				entry->last_used = jiffies;
-				if (!(entry->flags & ATF_COM))
-				{
-					add_timer(&entry->timer);
-#ifdef CONFIG_ARPD
-					if (!arpd_not_running)
-						arpd_lookup(sip, dev);
-					else
-#endif
-						arp_send(ARPOP_REQUEST, ETH_P_ARP, sip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
-				}
-#if RT_CACHE_DEBUG >= 1
-				printk(KERN_DEBUG "arp_run_bh: %08x reinstalled\n", sip);
-#endif
-			}
-			else
-			{
-				struct sk_buff * skb;
-				struct hh_cache * next;
+	if (!resolve)
+		return NULL;
 
-				/* Discard entry, but preserve its hh's and
-				 * skb's.
-				 */
-				cli();
-				for (hh=entry->hh; hh; hh=next)
-				{
-					next = hh->hh_next;
-					hh->hh_next = entry1->hh;
-					entry1->hh = hh;
-					hh->hh_arp = (void*)entry1;
-				}
-				entry->hh = NULL;
+	entry = arp_new_entry(paddr, dev, NULL);
 
-				/* Prune skb list from entry
-				 * and graft it to entry1.
-				 */
-				while ((skb = skb_dequeue(&entry->skb)) != NULL)
-				{
-					skb_device_lock(skb);
-					restore_flags(flags);
-					skb_queue_tail(&entry1->skb, skb);
-					skb_device_unlock(skb);
-					cli();
-				}
-				restore_flags(flags);
-				
-				arp_free_entry(entry);
+	if (entry)
+		atomic_inc(&entry->u.dst.refcnt);
 
-				if (entry1->flags & ATF_COM)
-				{
-					arp_update_hhs(entry1);
-					arp_send_q(entry1);
-				}
-			}
-			cli();
-		}
-		cli();
-	}
-	arp_unlock();
-	restore_flags(flags);
-}
+	end_bh_atomic();
 
+	return (struct dst_entry*)entry;
+}
 
 /*
  *	Interface to link layer: send routine and receive handler.
@@ -1696,17 +1337,28 @@
 	arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
 	skb->arp = 1;
 	skb->dev = dev;
-	skb->free = 1;
 	skb->protocol = htons (ETH_P_IP);
 
 	/*
 	 *	Fill the device header for the ARP frame
 	 */
-
 	dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len);
 
-	/* Fill out the arp protocol part. */
+	/*
+	 * Fill out the arp protocol part.
+	 *
+	 * The arp hardware type should match the device type, except for FDDI,
+	 * which (according to RFC 1390) should always equal 1 (Ethernet).
+	 */
+#ifdef CONFIG_FDDI
+	arp->ar_hrd = (dev->type == ARPHRD_FDDI) ? htons(ARPHRD_ETHER) : htons(dev->type);
+#else
 	arp->ar_hrd = htons(dev->type);
+#endif
+	/*
+	 *	Exceptions everywhere. AX.25 uses the AX.25 PID value not the
+	 *	DIX code for the protocol. Make these device structure fields.
+	 */
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
 	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
@@ -1732,8 +1384,10 @@
 		memset(arp_ptr, 0, dev->addr_len);
 	arp_ptr+=dev->addr_len;
 	memcpy(arp_ptr, &dest_ip, 4);
+	skb->dev = dev;
+	skb->priority = 0;
 
-	dev_queue_xmit(skb, dev, 0);
+	dev_queue_xmit(skb);
 }
 
 
@@ -1743,14 +1397,11 @@
 
 int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
-/*
- *	We shouldn't use this type conversion. Check later.
- */
-	
-	struct arphdr *arp = (struct arphdr *)skb->h.raw;
+	struct arphdr *arp = skb->nh.arph;
 	unsigned char *arp_ptr= (unsigned char *)(arp+1);
-	unsigned char *sha,*tha;
-	u32 sip,tip;
+	struct rtable *rt;
+	unsigned char *sha, *tha;
+	u32 sip, tip;
 	
 /*
  *	The hardware length of the packet should match the hardware length
@@ -1759,16 +1410,46 @@
  *	is not from an IP number.  We can't currently handle this, so toss
  *	it. 
  */  
+#ifdef CONFIG_FDDI
+	if (dev->type == ARPHRD_FDDI)
+	{
+		/*
+		 * According to RFC 1390, FDDI devices should accept ARP hardware types
+		 * of 1 (Ethernet).  However, to be more robust, we'll accept hardware
+		 * types of either 1 (Ethernet) or 6 (IEEE 802.2).
+		 */
+		if (arp->ar_hln != dev->addr_len    || 
+		    ((ntohs(arp->ar_hrd) != ARPHRD_ETHER) && (ntohs(arp->ar_hrd) != ARPHRD_IEEE802)) ||
+		    dev->flags & IFF_NOARP          ||
+		    skb->pkt_type == PACKET_OTHERHOST ||
+		    arp->ar_pln != 4)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+	}
+	else
+	{
+		if (arp->ar_hln != dev->addr_len    || 
+		    dev->type != ntohs(arp->ar_hrd) ||
+		    dev->flags & IFF_NOARP          ||
+		    skb->pkt_type == PACKET_OTHERHOST ||
+		    arp->ar_pln != 4)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+	}
+#else
 	if (arp->ar_hln != dev->addr_len    || 
      		dev->type != ntohs(arp->ar_hrd) || 
 		dev->flags & IFF_NOARP          ||
-		arp->ar_pln != 4)
-	{
+	        skb->pkt_type == PACKET_OTHERHOST ||
+		arp->ar_pln != 4) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
-		/* Should this be an error/printk?  Seems like something */
-		/* you'd want to know about. Unless it's just !IFF_NOARP. -- MS */
 	}
+#endif
 
 /*
  *	Another test.
@@ -1776,7 +1457,6 @@
  *	match the protocol the device speaks.  If it doesn't, there is a
  *	problem, so toss the packet.
  */
-/* Again, should this be an error/printk? -- MS */
 
   	switch (dev->type)
   	{
@@ -1801,14 +1481,8 @@
 		case ARPHRD_ETHER:
 		case ARPHRD_ARCNET:
 		case ARPHRD_METRICOM:
-			if(arp->ar_pro != htons(ETH_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
-
 		case ARPHRD_IEEE802:
+		case ARPHRD_FDDI:
 			if(arp->ar_pro != htons(ETH_P_IP))
 			{
 				kfree_skb(skb, FREE_READ);
@@ -1833,13 +1507,24 @@
 	tha=arp_ptr;
 	arp_ptr += dev->addr_len;
 	memcpy(&tip, arp_ptr, 4);
-  
+
+
 /* 
  *	Check for bad requests for 127.x.x.x and requests for multicast
  *	addresses.  If this is one such, delete it.
  */
-	if (LOOPBACK(tip) || MULTICAST(tip))
-	{
+	if (LOOPBACK(tip) || MULTICAST(tip)) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	if (ip_route_input(skb, tip, sip, 0, dev)) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	dev = skb->dev;
+	rt = (struct rtable*)skb->dst;
+	if (dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP ||
+	    rt->rt_flags&RTF_BROADCAST) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -1861,84 +1546,30 @@
  *  cache.
  */
 
-/*
- *	try to switch to alias device whose addr is tip or closest to sip.
- */
-
-#ifdef CONFIG_NET_ALIAS
-	if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
-	{
-		/*
-		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
-		 */
-		dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+	if (arp->ar_op == htons(ARPOP_REQUEST)) {
+		struct arp_table *entry;
 
-		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
-		{
-			kfree_skb(skb, FREE_READ);
-			return 0;
+		for (entry = arp_proxy_list; entry; entry = entry->u.next) {
+			if (!((entry->ip^tip)&entry->mask) &&
+			    ((!entry->u.dst.dev &&
+			      (!(entry->flags & ATF_COM) || entry->hatype == dev->type))
+			     || entry->u.dst.dev == dev) )
+				break;
 		}
-	}
-#endif
-
-	if (arp->ar_op == htons(ARPOP_REQUEST))
-	{ 
-
-/*
- * Only reply for the real device address or when it's in our proxy tables
- */
-		if (tip != dev->pa_addr)
-		{
-			struct arp_table *proxy_entry;
-
-/*
- * 	To get in here, it is a request for someone else.  We need to
- * 	check if that someone else is one of our proxies.  If it isn't,
- * 	we can toss it.
- *
- *	Make "longest match" lookup, a la routing.
- */
-
-			arp_fast_lock();
-
-			for (proxy_entry = arp_proxy_list; proxy_entry;
-			     proxy_entry = proxy_entry->next)
-			{
-				if (proxy_entry->dev == dev &&
-				    !((proxy_entry->ip^tip)&proxy_entry->mask))
-					break;
-			}
-
-			if (proxy_entry && (proxy_entry->mask || ((dev->pa_addr^tip)&dev->pa_mask)))
-			{
-				char ha[MAX_ADDR_LEN];
-				struct rtable * rt;
 
-				/* Unlock arp tables to make life for
-				 * ip_rt_route easy. Note, that we are obliged
-				 * to make local copy of hardware address.
-				 */
-
-				memcpy(ha, proxy_entry->ha, dev->addr_len);
-				arp_unlock();
-
-				rt = ip_rt_route(tip, 0);
-				if (rt  && rt->rt_dev != dev)
-					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
-				ip_rt_put(rt);
+		if (entry && !(entry->flags & ATF_DONTPUB)) {
+			char *ha = (entry->flags & ATF_COM) ? entry->ha : dev->dev_addr;
 
-			}
-			else
-				arp_unlock();
+			if (rt->rt_flags&(RTF_LOCAL|RTF_NAT) ||
+			    (!(rt->rt_flags&RTCF_DOREDIRECT) &&
+			     rt->u.dst.dev != dev))
+				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
 		}
-		else
-			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
-
 	}
 
-	arp_fast_lock();
-	arp_update(sip, sha, dev, 0, NULL, ip_chk_addr(tip) != IS_MYADDR && dev->type != ARPHRD_METRICOM);
-	arp_unlock();
+	start_bh_atomic();
+	arp_update(sip, sha, dev, 0, !RT_LOCALADDR(rt->rt_flags));
+	end_bh_atomic();
 	kfree_skb(skb, FREE_READ);
 	return 0;
 }
@@ -1953,14 +1584,13 @@
  *	Set (create) an ARP cache entry.
  */
 
-static int arp_req_set(struct arpreq *r, struct device * dev)
+int arp_req_set(struct arpreq *r, struct device * dev)
 {
 	struct arp_table *entry, **entryp;
 	struct sockaddr_in *si;
-	unsigned char *ha;
+	unsigned char *ha = NULL;
 	u32 ip;
 	u32 mask = DEF_ARP_NETMASK;
-	unsigned long flags;
 
 	/*
 	 *	Extract netmask (if supplied).
@@ -1979,44 +1609,43 @@
 	si = (struct sockaddr_in *) &r->arp_pa;
 	ip = si->sin_addr.s_addr;
 
-
 	if (r->arp_flags&ATF_PUBL)
 	{
-		if (!mask && ip)
+		if (ip & ~mask)
 			return -EINVAL;
-		if (!dev) {
-			dev = dev_getbytype(r->arp_ha.sa_family);
+		if (!dev && (r->arp_flags & ATF_COM))
+		{
+			dev = dev_getbyhwaddr(r->arp_ha.sa_family, r->arp_ha.sa_data);
 			if (!dev)
 				return -ENODEV;
 		}
 	}
 	else
 	{
+		struct rtable * rt;
+		int err;
+
+		if ((r->arp_flags & ATF_PERM) && !(r->arp_flags & ATF_COM))
+			return -EINVAL;
+		err = ip_route_output(&rt, ip, 0, 1, dev);
+		if (err)
+			return err;
 		if (!dev)
-		{
-			struct rtable * rt;
-			rt = ip_rt_route(ip, 0);
-			if (!rt)
-				return -ENETUNREACH;
-			dev = rt->rt_dev;
+			dev = rt->u.dst.dev;
+		if (rt->rt_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTCF_NAT)) {
 			ip_rt_put(rt);
-			if (!dev)
-				return -ENODEV;
-		}
-		if (dev->type != ARPHRD_METRICOM && ip_chk_addr(ip))
 			return -EINVAL;
+		}
+		ip_rt_put(rt);
 	}
-	if (dev->flags & (IFF_LOOPBACK | IFF_NOARP))
+	
+	if (dev && (dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
 		return -ENODEV;
 
-	if (r->arp_ha.sa_family != dev->type)	
+	if (dev && r->arp_ha.sa_family != dev->type)	
 		return -EINVAL;
 		
-	arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1
-	if (ARP_LOCKED())
-		printk("arp_req_set: bug\n");
-#endif
+	start_bh_atomic();
 
 	if (!(r->arp_flags & ATF_PUBL))
 		entryp = &arp_tables[HASH(ip)];
@@ -2025,49 +1654,100 @@
 
 	while ((entry = *entryp) != NULL)
 	{
-		/* User supplied arp entries are definitive - RHP 960603 */
+		if (entry->mask == mask)
+			break;
+		if ((entry->mask & mask) != mask)
+			break;
+		entryp = &entry->u.next;
+	}
+	while ((entry = *entryp) != NULL && entry->mask == mask)
+	{
+		if (entry->ip == ip)
+			break;
+		entryp = &entry->u.next;
+	}
+	while ((entry = *entryp) != NULL && entry->mask == mask &&
+	       entry->ip == ip)
+	{
+		if (!entry->u.dst.dev || entry->u.dst.dev == dev)
+			break;
+		entryp = &entry->u.next;
+	}
 
-		if (entry->ip == ip && entry->mask == mask && entry->dev == dev) {
-			*entryp=entry->next;
-			arp_free_entry(entry);
-			continue;
+	while ((entry = *entryp) != NULL)
+	{
+		if (entry->ip != ip || entry->mask != mask ||
+		    entry->u.dst.dev != dev)
+		{
+			entry = NULL;
+			break;
 		}
-		if ((entry->mask & mask) != mask)
+		if (entry->hatype == r->arp_ha.sa_family &&
+		    (!(r->arp_flags & ATF_MAGIC) ||
+		     entry->flags == r->arp_flags))
 			break;
-		entryp = &entry->next;
+		entryp = &entry->u.next;
 	}
 
-	entry = arp_alloc_entry();
-	if (entry == NULL)
+	if (entry)
+		atomic_inc(&entry->u.dst.refcnt);
+	else
 	{
-		arp_unlock();
-		return -ENOMEM;
+		entry = arp_alloc(r->arp_flags&ATF_PUBL ? 0 : 1);
+		if (entry == NULL)
+		{
+			end_bh_atomic();
+			return -ENOMEM;
+		}
+		entry->ip = ip;
+		entry->u.dst.dev = dev;
+		entry->mask = mask;
+
+		if (dev)
+			entry->hatype = dev->type;
+
+		entry->u.next = *entryp;
+		*entryp = entry;
 	}
-	entry->ip = ip;
-	entry->dev = dev;
-	entry->mask = mask;
 	entry->flags = r->arp_flags;
+	if (!(entry->flags&(ATF_PUBL|ATF_COM)))
+		atomic_inc(&arp_unres_size);
 
-	entry->next = *entryp;
-	*entryp = entry;
+	if (entry->flags & ATF_PUBL)
+	{
+		if (entry->flags & ATF_COM)
+		{
+			entry->hatype = r->arp_ha.sa_family;
+			ha = r->arp_ha.sa_data;
+		}
+		else if (dev)
+			ha = dev->dev_addr;
+	}
+	else
+		ha = r->arp_ha.sa_data;
 
-	ha = r->arp_ha.sa_data;
-	if (empty(ha, dev->addr_len))
-		ha = dev->dev_addr;
-
-	save_flags(flags);
-	cli();
-	memcpy(entry->ha, ha, dev->addr_len);
-	entry->last_updated = entry->last_used = jiffies;
-	entry->flags |= ATF_COM;
-	restore_flags(flags);
-	arpd_update(entry);
-	arp_update_hhs(entry);
-	arp_unlock();
-	return 0;
-}
+	if (ha)
+		memcpy(entry->ha, ha, dev ? dev->addr_len : MAX_ADDR_LEN);
+	else
+		memset(entry->ha, 0, MAX_ADDR_LEN);
 
+	entry->last_updated = entry->u.dst.lastuse = jiffies;
+	
+	if (!(entry->flags & ATF_PUBL))
+	{
+		if (entry->flags & ATF_COM)
+		{
+			arpd_update(entry->ip, entry->u.dst.dev, ha);
+			arp_update_hhs(entry);
+		}
+		else
+			arp_start_resolution(entry);
+	}
 
+	dst_release(&entry->u.dst);
+	end_bh_atomic();
+	return 0;
+}
 
 /*
  *	Get an ARP cache entry.
@@ -2087,37 +1767,43 @@
 
 	si = (struct sockaddr_in *) &r->arp_pa;
 
-	arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1
-	if (ARP_LOCKED())
-		printk("arp_req_set: impossible\n");
-#endif
+	start_bh_atomic();
 
 	if (!(r->arp_flags & ATF_PUBL))
 		entry = arp_tables[HASH(si->sin_addr.s_addr)];
 	else
 		entry = arp_proxy_list;
 
-	for ( ; entry ;entry = entry->next)
+	for ( ; entry ;entry = entry->u.next)
 	{
-		if (entry->ip == si->sin_addr.s_addr 
-		    && (!dev || entry->dev == dev)
-		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
+		if (entry->ip == si->sin_addr.s_addr &&
+		    (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask) &&
+		    ( (r->arp_flags&ATF_PUBL) ?
+		      (entry->u.dst.dev == dev && entry->hatype == r->arp_ha.sa_family)
+		     : (entry->u.dst.dev == dev || !dev)))
 		{
-			memcpy(r->arp_ha.sa_data, entry->ha, entry->dev->addr_len);
-			r->arp_ha.sa_family = entry->dev->type;
+			if (entry->u.dst.dev)
+			{
+				memcpy(r->arp_ha.sa_data, entry->ha, entry->u.dst.dev->addr_len);
+				r->arp_ha.sa_family = entry->u.dst.dev->type;
+				strncpy(r->arp_dev, entry->u.dst.dev->name, sizeof(r->arp_dev));
+			}
+			else
+			{
+				r->arp_ha.sa_family = entry->hatype;
+				memset(r->arp_ha.sa_data, 0, sizeof(r->arp_ha.sa_data));
+			}
 			r->arp_flags = entry->flags;
-			strncpy(r->arp_dev, entry->dev->name, sizeof(r->arp_dev));
-			arp_unlock();
+			end_bh_atomic();
 			return 0;
 		}
 	}
 
-	arp_unlock();
+	end_bh_atomic();
 	return -ENXIO;
 }
 
-static int arp_req_delete(struct arpreq *r, struct device * dev)
+int arp_req_delete(struct arpreq *r, struct device * dev)
 {
 	struct sockaddr_in	*si;
 	struct arp_table	*entry, **entryp;
@@ -2132,11 +1818,7 @@
 
 	si = (struct sockaddr_in *) &r->arp_pa;
 
-	arp_fast_lock();
-#if RT_CACHE_DEBUG >= 1
-	if (ARP_LOCKED())
-		printk("arp_req_delete: impossible\n");
-#endif
+	start_bh_atomic();
 
 	if (!(r->arp_flags & ATF_PUBL))
 		entryp = &arp_tables[HASH(si->sin_addr.s_addr)];
@@ -2145,19 +1827,24 @@
 
 	while ((entry = *entryp) != NULL)
 	{
-		if (entry->ip == si->sin_addr.s_addr 
-		    && (!dev || entry->dev == dev)
-		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
-		{
-			*entryp = entry->next;
-			arp_free_entry(entry);
-			retval = 0;
-			continue;
+		if (entry->ip == si->sin_addr.s_addr
+		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask)
+		    && (entry->u.dst.dev == dev || (!(r->arp_flags&ATF_PUBL) && !dev))
+		    && (!(r->arp_flags&ATF_MAGIC) || r->arp_flags == entry->flags))
+		{
+			if (!entry->u.dst.refcnt)
+			{
+				arp_free(entryp);
+				retval = 0;
+				continue;
+			}
+			if (retval)
+				retval = -EBUSY;
 		}
-		entryp = &entry->next;
+		entryp = &entry->u.next;
 	}
 
-	arp_unlock();
+	end_bh_atomic();
 	return retval;
 }
 
@@ -2169,7 +1856,6 @@
 {
 	int err;
 	struct arpreq r;
-
 	struct device * dev = NULL;
 
 	switch(cmd)
@@ -2181,7 +1867,7 @@
 		case SIOCGARP:
 			err = copy_from_user(&r, arg, sizeof(struct arpreq));
 			if (err)
-				return -EFAULT; 
+				return -EFAULT;
 			break;
 		case OLD_SIOCDARP:
 		case OLD_SIOCSARP:
@@ -2190,7 +1876,7 @@
 		case OLD_SIOCGARP:
 			err = copy_from_user(&r, arg, sizeof(struct arpreq_old));
 			if (err)
-				return -EFAULT; 
+				return -EFAULT;
 			memset(&r.arp_dev, 0, sizeof(r.arp_dev));
 			break;
 		default:
@@ -2200,8 +1886,9 @@
 	if (r.arp_pa.sa_family != AF_INET)
 		return -EPFNOSUPPORT;
 
-	if (!(r.arp_flags & ATF_PUBL))
-		r.arp_flags &= ~ATF_NETMASK;
+	if (!(r.arp_flags & ATF_PUBL) &&
+	    (r.arp_flags & (ATF_NETMASK|ATF_DONTPUB|ATF_MAGIC)))
+		return -EINVAL;
 	if (!(r.arp_flags & ATF_NETMASK))
 		((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=DEF_ARP_NETMASK;
 
@@ -2212,7 +1899,7 @@
 
 		if (!r.arp_ha.sa_family)
 			r.arp_ha.sa_family = dev->type;
-		else if (r.arp_ha.sa_family != dev->type)
+		if ((r.arp_flags & ATF_COM) && r.arp_ha.sa_family != dev->type)
 			return -EINVAL;
 	}
 
@@ -2250,11 +1937,7 @@
 		case SIOCGARP:
 			err = arp_req_get(&r, dev);
 			if (!err)
-			{
 				err = copy_to_user(arg, &r, sizeof(r));
-				if (err)
-					err = -EFAULT;
-			}
 			return err;
 		case OLD_SIOCGARP:
 			r.arp_flags &= ~ATF_PUBL;
@@ -2265,11 +1948,7 @@
 				err = arp_req_get(&r, dev);
 			}
 			if (!err)
-			{
 				err = copy_to_user(arg, &r, sizeof(struct arpreq_old));
-				if (err)
-					err = -EFAULT;
-			}
 			return err;
 	}
 	/*NOTREACHED*/
@@ -2297,56 +1976,66 @@
 	pos+=size;
 	len+=size;
 
-	arp_fast_lock();
 
 	for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
 	{
-		for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
+		start_bh_atomic();
+
+		for(entry=arp_tables[i]; entry!=NULL; entry=entry->u.next)
 		{
 /*
  *	Convert hardware address to XX:XX:XX:XX ... form.
  */
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 #if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
-			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
+			if (entry->hatype == ARPHRD_AX25 || entry->hatype == ARPHRD_NETROM)
 			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
 			else {
 #else
-			if(entry->dev->type==ARPHRD_AX25)
+			if(entry->hatype==ARPHRD_AX25)
 			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
 			else {
 #endif
 #endif
-
-			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
+				
+			if (entry->u.dst.dev)
 			{
-				hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
-				hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
-				hbuffer[k++]=':';
+				for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->u.dst.dev->addr_len;j++)
+				{
+					hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
+					hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
+					hbuffer[k++]=':';
+				}
+				hbuffer[--k]=0;
 			}
-			hbuffer[--k]=0;
+			else
+				strcpy(hbuffer, "00:00:00:00:00:00");
 	
 #if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
 			}
 #endif
+
 			size = sprintf(buffer+len,
 				"%-17s0x%-10x0x%-10x%s",
 				in_ntoa(entry->ip),
-				(unsigned int)entry->dev->type,
+				entry->hatype,
 				entry->flags,
 				hbuffer);
 #if RT_CACHE_DEBUG < 2
 			size += sprintf(buffer+len+size,
 				 "     %-17s %s\n",
 				 entry->mask==DEF_ARP_NETMASK ?
-				 "*" : in_ntoa(entry->mask), entry->dev->name);
+				 "*" : in_ntoa(entry->mask),
+					entry->u.dst.dev ? entry->u.dst.dev->name : "*");
 #else
 			size += sprintf(buffer+len+size,
-				 "     %-17s %s\t%d\t%1d\n",
+				 "     %-17s %s\t%d\t%d\t%1d\n",
 				 entry->mask==DEF_ARP_NETMASK ?
-				 "*" : in_ntoa(entry->mask), entry->dev->name, 
-				 entry->hh ? entry->hh->hh_refcnt : -1,
-				 entry->hh ? entry->hh->hh_uptodate : 0);
+				 "*" : in_ntoa(entry->mask),
+				 entry->u.dst.dev ? entry->u.dst.dev->name : "*", 
+				 entry->u.dst.refcnt,
+				 entry->u.dst.hh ? entry->u.dst.hh->hh_refcnt : -1,
+				 entry->u.dst.hh ? entry->u.dst.hh->hh_uptodate : 0);
 #endif
 	
 			len += size;
@@ -2355,11 +2044,14 @@
 			if (pos <= offset)
 				len=0;
 			if (pos >= offset+length)
+			{
+				end_bh_atomic();
 				goto done;
+			}
 		}
+		end_bh_atomic();	
 	}
 done:
-	arp_unlock();
   
 	*start = buffer+len-(pos-offset);	/* Start of wanted data */
 	len = pos-offset;			/* Start slop */
@@ -2376,7 +2068,7 @@
 
 static struct packet_type arp_packet_type =
 {
-	0,	/* Should be: __constant_htons(ETH_P_ARP) - but this _doesn't_ come out constant! */
+	__constant_htons(ETH_P_ARP),
 	NULL,		/* All devices */
 	arp_rcv,
 	NULL,
@@ -2400,8 +2092,6 @@
 
 void arp_init (void)
 {
-	/* Register the packet type */
-	arp_packet_type.type=htons(ETH_P_ARP);
 	dev_add_pack(&arp_packet_type);
 	/* Start with the regular checks for expired arp entries. */
 	add_timer(&arp_timer);
@@ -2416,6 +2106,7 @@
 	netlink_attach(NETLINK_ARPD, arpd_callback);
 #endif
 }
+
 
 #ifdef CONFIG_AX25_MODULE
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.14/linux/net/ipv4/devinet.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ipv4/devinet.c	Thu Dec 12 16:54:23 1996
@@ -34,6 +34,7 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/if_arp.h>
 #include <net/ip.h>
 #include <net/route.h>
 #include <net/protocol.h>
@@ -41,16 +42,26 @@
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/arp.h>
+#include <linux/notifier.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+extern struct notifier_block *netdev_chain;
 
 /* 
  *	Determine a default network mask, based on the IP address. 
  */
- 
+
+static
 unsigned long ip_get_mask(unsigned long addr)
 {
   	unsigned long dst;
 
-  	if (addr == 0L) 
+  	if (ZERONET(addr))
   		return(0L);	/* special case */
 
   	dst = ntohl(addr);
@@ -68,184 +79,235 @@
   	return(0);
 }
 
-/* 
- *	Check the address for our address, broadcasts, etc. 
- *
- *	I intend to fix this to at the very least cache the last
- *	resolved entry.
- */
- 
-int ip_chk_addr(unsigned long addr)
+struct device *dev_getbyhwaddr(unsigned short type, char *ha)
 {
 	struct device *dev;
-#ifndef CONFIG_IP_CLASSLESS
-	unsigned long mask;
-#endif
-
-	/* 
-	 *	Accept both `all ones' and `all zeros' as BROADCAST. 
-	 *	(Support old BSD in other words). This old BSD 
-	 *	support will go very soon as it messes other things
-	 *	up.
-	 *	Also accept `loopback broadcast' as BROADCAST.
-	 */
-
-	if (addr == INADDR_ANY || addr == INADDR_BROADCAST ||
-	    addr == htonl(0x7FFFFFFFL))
-		return IS_BROADCAST;
-
-#ifndef  CONFIG_IP_CLASSLESS
-	mask = ip_get_mask(addr);
-
-	/*
-	 *	Accept all of the `loopback' class A net. 
-	 */
-	 
-	if ((addr & mask) == htonl(0x7F000000L))
-		return IS_MYADDR;
-#else
-	if ((addr & htonl(0x7F000000L)) == htonl(0x7F000000L))
-		return IS_MYADDR;
-#endif
 
-	/*
-	 *	OK, now check the interface addresses. We could
-	 *	speed this by keeping a dev and a dev_up chain.
-	 */
-	 
 	for (dev = dev_base; dev != NULL; dev = dev->next) 
 	{
-		if ((!(dev->flags & IFF_UP)) || dev->family!=AF_INET)
-			continue;
-		/*
-		 *	If the protocol address of the device is 0 this is special
-		 *	and means we are address hunting (eg bootp).
-		 */
-		 
-		if (dev->pa_addr == 0)
-			return IS_MYADDR;
-		/*
-		 *	Is it the exact IP address? 
-		 */
-		 
-		if (addr == dev->pa_addr)
-			return IS_MYADDR;
-		/*
-		 *	Is it our broadcast address? 
-		 */
-		 
-		if ((dev->flags & IFF_BROADCAST) && addr == dev->pa_brdaddr)
-			return IS_BROADCAST;
-		/*
-		 *	Nope. Check for a subnetwork broadcast. 
-		 */
-		 
-		if (((addr ^ dev->pa_addr) & dev->pa_mask) == 0) 
-		{
-			if ((addr & ~dev->pa_mask) == 0)
-				return IS_BROADCAST;
-			if ((addr & ~dev->pa_mask) == ~dev->pa_mask)
-				return IS_BROADCAST;
-		}
-		
-#ifndef CONFIG_IP_CLASSLESS
-		/*
-	 	 *	Nope. Check for Network broadcast. 
-	 	 */
-	 	 
-		if (((addr ^ dev->pa_addr) & mask) == 0) 
-		{
-			if ((addr & ~mask) == 0)
-				return IS_BROADCAST;
-			if ((addr & ~mask) == ~mask)
-				return IS_BROADCAST;
-		}
-#endif
+		if (dev->type == type &&
+		    !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)) &&
+		    memcmp(dev->dev_addr, ha, dev->addr_len) == 0)
+			return(dev);
 	}
-	if(IN_MULTICAST(ntohl(addr)))
-		return IS_MULTICAST;
-	return 0;		/* no match at all */
+	return(NULL);
 }
 
-
 /*
- *	Retrieve our own address.
- *
- *	Because the loopback address (127.0.0.1) is already recognized
- *	automatically, we can use the loopback interface's address as
- *	our "primary" interface.  This is the address used by IP et
- *	al when it doesn't know which address to use (i.e. it does not
- *	yet know from or to which interface to go...).
+ *	This checks bitmasks for the ioctl calls for devices.
  */
  
-unsigned long ip_my_addr(void)
+static inline int bad_mask(unsigned long mask, unsigned long addr)
 {
-  	struct device *dev;
-
-  	for (dev = dev_base; dev != NULL; dev = dev->next) 
-  	{
-		if (dev->flags & IFF_LOOPBACK) 
-			return(dev->pa_addr);
-  	}
-  	return(0);
+	if (addr & (mask = ~mask))
+		return 1;
+	mask = ntohl(mask);
+	if (mask & (mask+1))
+		return 1;
+	return 0;
 }
 
-/*
- *	Find an interface that can handle addresses for a certain address. 
- */
-
-struct device * ip_dev_bynet(unsigned long addr, unsigned long mask)
+ 
+int devinet_ioctl(unsigned int cmd, void *arg)
 {
+	struct ifreq ifr;
 	struct device *dev;
-	struct device *best_dev = NULL;
-	__u32  best_mask = mask;
+	__u32 addr;
+#ifdef CONFIG_NET_ALIAS
+	int err;
+#endif
 
-	for (dev = dev_base; dev; dev = dev->next) 
-	{
-		if (!(dev->flags & IFF_UP))
-			continue;
-		if (dev->flags & IFF_POINTOPOINT)
-		{
-			if (addr == dev->pa_dstaddr)
-				return dev;
-			continue;
-		}
-		if (dev->pa_mask & (addr ^ dev->pa_addr))
-			continue;
-		if (mask == dev->pa_mask)
-			return dev;
-		if (best_dev && (best_mask & dev->pa_mask) != best_mask)
-			continue;
-		best_dev = dev;
-		best_mask = dev->pa_mask;
-	}
-	return best_dev;
-}
+	/*
+	 *	Fetch the caller's info block into kernel space
+	 */
 
-/*
- *	Find the first device with a given source address.
- */
- 
-struct device *ip_dev_find(unsigned long addr)
-{
-	struct device *dev;
-	for(dev = dev_base; dev; dev=dev->next)
-	{
-		if((dev->flags&IFF_UP) && dev->pa_addr==addr)
-			return dev;
-	}
-	return NULL;
-}
+	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
+		return -EFAULT;
 
-struct device *dev_getbytype(unsigned short type)
-{
-	struct device *dev;
+	/*
+	 *	See which interface the caller is talking about. 
+	 */
+	 
+	/*
+	 *
+	 *	net_alias_dev_get(): dev_get() with added alias naming magic.
+	 *	only allow alias creation/deletion if (getset==SIOCSIFADDR)
+	 *
+	 */
+	 
+#ifdef CONFIG_KERNELD
+	dev_load(ifr.ifr_name);
+#endif	
+
+#ifdef CONFIG_NET_ALIAS
+	if ((dev = net_alias_dev_get(ifr.ifr_name, cmd == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
+		return(err);
+#else
+	if ((dev = dev_get(ifr.ifr_name)) == NULL) 	
+		return(-ENODEV);
+#endif
 
-	for (dev = dev_base; dev != NULL; dev = dev->next) 
+	if (cmd != SIOCSIFADDR && dev->family != AF_INET)
+		return(-EINVAL);
+
+	switch(cmd) 
 	{
-		if (dev->type == type && !(dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
-			return(dev);
+		case SIOCGIFADDR:	/* Get interface address (and family) */
+			if (ifr.ifr_addr.sa_family == AF_UNSPEC)
+			{
+				memcpy(ifr.ifr_hwaddr.sa_data, dev->dev_addr, MAX_ADDR_LEN);
+				ifr.ifr_hwaddr.sa_family = dev->type;			
+			}
+			else
+			{
+				(*(struct sockaddr_in *)
+					  &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+				(*(struct sockaddr_in *)
+					  &ifr.ifr_addr).sin_family = dev->family;
+				(*(struct sockaddr_in *)
+					  &ifr.ifr_addr).sin_port = 0;
+			}
+			break;
+	
+		case SIOCSIFADDR:	/* Set interface address (and family) */
+		
+			if (!suser())
+				return -EPERM;
+
+			/*
+			 *	BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
+			 *	physical address. We can cope with this now.
+			 */
+			
+			if(ifr.ifr_addr.sa_family==AF_UNSPEC)
+			{
+				if(dev->set_mac_address==NULL)
+					return -EOPNOTSUPP;
+				return dev->set_mac_address(dev,&ifr.ifr_addr);
+			}
+			if(ifr.ifr_addr.sa_family!=AF_INET)
+				return -EINVAL;
+
+			addr = (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr;
+
+			dev_lock_wait();
+			dev_lock_list();
+
+			if (dev->family == AF_INET && addr == dev->pa_addr) {
+				dev_unlock_list();
+				return 0;
+			}
+
+			if (dev->flags & IFF_UP)
+				notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
+
+			/*
+			 *	if dev is an alias, must rehash to update
+			 *	address change
+			 */
+
+#ifdef CONFIG_NET_ALIAS
+			if (net_alias_is(dev))
+				net_alias_dev_rehash(dev, &ifr.ifr_addr);
+#endif
+			dev->pa_addr = addr;
+			dev->ip_flags |= IFF_IP_ADDR_OK;
+			dev->ip_flags &= ~(IFF_IP_BRD_OK|IFF_IP_MASK_OK);
+			dev->family = AF_INET;
+			if (dev->flags & IFF_POINTOPOINT) {
+				dev->pa_mask = 0xFFFFFFFF;
+				dev->pa_brdaddr = 0xFFFFFFFF;
+			} else {
+				dev->pa_mask = ip_get_mask(dev->pa_addr);
+				dev->pa_brdaddr = dev->pa_addr|~dev->pa_mask;
+			}
+			if (dev->flags & IFF_UP)
+				notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+			dev_unlock_list();
+			return 0;
+			
+		case SIOCGIFBRDADDR:	/* Get the broadcast address */
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_family = dev->family;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_broadaddr).sin_port = 0;
+			break;
+
+		case SIOCSIFBRDADDR:	/* Set the broadcast address */
+			if (!suser())
+				return -EPERM;
+
+			addr = (*(struct sockaddr_in *)&ifr.ifr_broadaddr).sin_addr.s_addr;
+
+			if (addr == dev->pa_brdaddr) {
+				dev->ip_flags |= IFF_IP_BRD_OK;
+				return 0;
+			}
+			if (dev->flags & IFF_UP)
+				ip_rt_change_broadcast(dev, addr);
+			dev->pa_brdaddr = addr;
+			dev->ip_flags |= IFF_IP_BRD_OK;
+			return 0;
+			
+		case SIOCGIFDSTADDR:	/* Get the destination address (for point-to-point links) */
+			(*(struct sockaddr_in *)
+				&ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_dstaddr).sin_family = dev->family;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_dstaddr).sin_port = 0;
+			break;
+	
+		case SIOCSIFDSTADDR:	/* Set the destination address (for point-to-point links) */
+			if (!suser())
+				return -EPERM;
+			addr = (*(struct sockaddr_in *)&ifr.ifr_dstaddr).sin_addr.s_addr;
+			if (addr == dev->pa_dstaddr)
+				return 0;
+			if (dev->flags & IFF_UP)
+				ip_rt_change_dstaddr(dev, addr);
+			dev->pa_dstaddr = addr;
+			return 0;
+			
+		case SIOCGIFNETMASK:	/* Get the netmask for the interface */
+			(*(struct sockaddr_in *)
+				&ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_netmask).sin_family = dev->family;
+			(*(struct sockaddr_in *)
+				&ifr.ifr_netmask).sin_port = 0;
+			break;
+
+		case SIOCSIFNETMASK: 	/* Set the netmask for the interface */
+			if (!suser())
+				return -EPERM;
+			addr = (*(struct sockaddr_in *)&ifr.ifr_netmask).sin_addr.s_addr;
+
+			if (addr == dev->pa_mask) {
+				dev->ip_flags |= IFF_IP_MASK_OK;
+				return 0;
+			}
+
+			/*
+			 *	The mask we set must be legal.
+			 */
+			if (bad_mask(addr, 0))
+				return -EINVAL;
+			if (addr == htonl(0xFFFFFFFE))
+				return -EINVAL;
+			if (dev->flags & IFF_UP)
+				ip_rt_change_netmask(dev, addr);
+			dev->pa_mask = addr;
+			dev->ip_flags |= IFF_IP_MASK_OK;
+			dev->ip_flags &= ~IFF_IP_BRD_OK;
+			return 0;
+		default:
+			return -EINVAL;
+			
 	}
-	return(NULL);
+	if (copy_to_user(arg, &ifr, sizeof(struct ifreq)))
+		return -EFAULT;
+	return 0;
 }
-
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/fib.c linux/net/ipv4/fib.c
--- v2.1.14/linux/net/ipv4/fib.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/fib.c	Thu Dec 12 16:54:23 1996
@@ -0,0 +1,2056 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		IPv4 Forwarding Information Base.
+ *
+ * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *		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.
+ *
+ *
+ *	NOTE:	This file is scheduled to be removed from kernel.
+ *		The natural place for router FIB is user level
+ *		routing daemon (it has to keep its copy in any case)
+ *		
+ *		Kernel should keep only interface routes and,
+ *		if host is not router, default gateway.
+ *
+ *		We have good proof that it is feasible and efficient -
+ *		multicast routing.
+ */
+
+#include <linux/config.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/route.h>
+#include <net/tcp.h>
+#include <net/sock.h>
+#include <net/icmp.h>
+#include <net/arp.h>
+#include <net/netlink.h>
+#include <net/ip_fib.h>
+#include <net/dst.h>
+#include <linux/net_alias.h>
+
+static struct fib_class local_class = {RT_CLASS_LOCAL, };
+static struct fib_class default_class = {RT_CLASS_DEFAULT, };
+static struct fib_class main_class = {RT_CLASS_MAIN, };
+static struct fib_class *fib_classes[RT_CLASS_MAX+1];
+
+static struct fib_rule  *fib_rules;
+
+static struct fib_info 	*fib_info_list;
+
+static int fib_stamp;
+
+static int rtmsg_process(struct nlmsghdr *n, struct in_rtmsg *r);
+
+
+#ifdef CONFIG_RTNETLINK
+
+static unsigned rt_nl_flags;
+static int rt_nl_owner = -1;
+
+/*
+ *	Default mode is delayed for 0.5sec batch delivery.
+ *	If someone starts to use user->level calls,
+ *	we turn on synchronous message passing.
+ */
+
+#define RTMSG_DELAY (HZ/2)
+
+static struct nlmsg_ctl rtmsg_ctl = {
+	{ NULL, NULL, 0, 0L, NULL },
+	NULL,
+	NETLINK_ROUTE,
+	RTMSG_DELAY,
+	NLMSG_GOODSIZE,
+	0, 0, 0, 0
+};
+
+static void __rtmsg_ack(struct nlmsghdr *n, int err);
+
+static __inline__ void rtmsg_ack(struct nlmsghdr *n, int err)
+{
+	if (n->nlmsg_seq && rt_nl_flags&RTCTL_ACK)
+		__rtmsg_ack(n, err);
+}
+
+static void rtmsg_fib(unsigned long type, struct fib_node *f, int logmask,
+		      struct fib_class *class, struct nlmsghdr *n);
+static void rtmsg_dev(unsigned long type, struct device *dev, struct nlmsghdr *n);
+#define rtmsg_kick() ({ if (rtmsg_ctl.nlmsg_skb) nlmsg_transmit(&rtmsg_ctl); })
+
+#else
+#define rtmsg_fib(a,b,c,d,e)
+#define rtmsg_dev(a,b,c)
+#define rtmsg_ack(a,b)
+#define rtmsg_kick()
+#endif
+
+
+/*
+ *	FIB locking.
+ */
+
+static struct wait_queue *fib_wait;
+atomic_t fib_users;
+
+static void fib_lock(void)
+{
+	while (fib_users)
+		sleep_on(&fib_wait);
+	atomic_inc(&fib_users);
+	dev_lock_list();
+}
+
+static void fib_unlock(void)
+{
+	dev_unlock_list();
+	if (atomic_dec_and_test(&fib_users)) {
+		rtmsg_kick();
+		wake_up(&fib_wait);
+	}
+}
+
+/*
+ *	Check if a mask is acceptable.
+ */
+ 
+static __inline__ int bad_mask(u32 mask, u32 addr)
+{
+	if (addr & (mask = ~mask))
+		return 1;
+	mask = ntohl(mask);
+	if (mask & (mask+1))
+		return 1;
+	return 0;
+}
+
+/* 
+ * Evaluate mask length.
+ */
+
+static __inline__ int fib_logmask(u32 mask)
+{
+	if (!(mask = ntohl(mask)))
+		return 32;
+	return ffz(~mask);
+}
+
+/* 
+ * Create mask from mask length.
+ */
+
+static __inline__ u32 fib_mask(int logmask)
+{
+	if (logmask >= 32)
+		return 0;
+	return htonl(~((1<<logmask)-1));
+}
+
+static __inline__ u32 fib_netmask(int logmask)
+{
+	return fib_mask(32-logmask);
+}
+
+
+static struct fib_class *fib_alloc_class(int id)
+{
+	struct fib_class *class;
+
+	if (fib_classes[id])
+		return fib_classes[id];
+
+	class = kmalloc(sizeof(*class), GFP_KERNEL);
+	if (!class)
+		return NULL;
+	memset(class, 0, sizeof(*class));
+	class->cl_id = id;
+	fib_classes[id] = class;
+	return class;
+}
+
+static struct fib_class *fib_empty_class(void)
+{
+	int id;
+	for (id = 1; id <= RT_CLASS_MAX; id++)
+		if (fib_classes[id] == NULL)
+			return fib_alloc_class(id);
+	return NULL;
+}
+
+static int fib_rule_delete(struct in_rtrulemsg *r, struct device *dev, struct nlmsghdr *n)
+{
+	u32 src = r->rtrmsg_src.s_addr;
+	u32 dst = r->rtrmsg_dst.s_addr;
+	u32 srcmask = fib_netmask(r->rtrmsg_srclen);
+	u32 dstmask = fib_netmask(r->rtrmsg_dstlen);
+	struct fib_rule *cl, **clp;
+
+	for (clp=&fib_rules; (cl=*clp) != NULL; clp=&cl->cl_next) {
+		if (src == cl->cl_src &&
+		    srcmask == cl->cl_srcmask &&
+		    dst == cl->cl_dst &&
+		    dstmask == cl->cl_dstmask &&
+		    r->rtrmsg_tos == cl->cl_tos &&
+		    dev == cl->cl_dev &&
+		    r->rtrmsg_action == cl->cl_action &&
+		    (!r->rtrmsg_preference || r->rtrmsg_preference == cl->cl_preference) &&
+		    (!r->rtrmsg_class || (cl && r->rtrmsg_class == cl->cl_class->cl_id))) {
+			cli();
+			*clp = cl->cl_next;
+			sti();
+			if (cl->cl_class)
+				cl->cl_class->cl_users--;
+			kfree(cl);
+			return 0;
+		}
+	}
+	return -ESRCH;
+}
+
+static int fib_rule_add(struct in_rtrulemsg *r, struct device *dev, struct nlmsghdr *n)
+{
+	u32 src = r->rtrmsg_src.s_addr;
+	u32 dst = r->rtrmsg_dst.s_addr;
+	u32 srcmask = fib_netmask(r->rtrmsg_srclen);
+	u32 dstmask = fib_netmask(r->rtrmsg_dstlen);
+
+	struct fib_rule *cl, *new_cl, **clp;
+	struct fib_class *class = NULL;
+
+	if ((src&~srcmask) || (dst&~dstmask))
+		return -EINVAL;
+	if (dev && net_alias_main_dev(dev) != dev)
+		return -ENODEV;
+
+	if (!r->rtrmsg_class) {
+		if (r->rtrmsg_action==RTP_GO || r->rtrmsg_action==RTP_NAT
+		    || r->rtrmsg_action==RTP_MASQUERADE) {
+			if ((class = fib_empty_class()) == NULL)
+				return -ENOMEM;
+			class->cl_auto = 1;
+		} else if (r->rtrmsg_rtmsgs)
+			return -EINVAL;
+	} else if ((class = fib_alloc_class(r->rtrmsg_class)) == NULL)
+		return -ENOMEM;
+
+	new_cl = kmalloc(sizeof(*new_cl), GFP_KERNEL);
+	if (!new_cl)
+		return -ENOMEM;
+	new_cl->cl_src = src;
+	new_cl->cl_srcmask = srcmask;
+	new_cl->cl_dst = dst;
+	new_cl->cl_dstmask = dstmask;
+	new_cl->cl_dev = dev;
+	new_cl->cl_srcmap = r->rtrmsg_srcmap.s_addr;
+	new_cl->cl_tos = r->rtrmsg_tos;
+	new_cl->cl_action = r->rtrmsg_action;
+	new_cl->cl_flags = r->rtrmsg_flags;
+	new_cl->cl_preference = r->rtrmsg_preference;
+	new_cl->cl_class = class;
+	if (class)
+		class->cl_users++;
+
+	clp = &fib_rules;
+
+	if (!new_cl->cl_preference) {
+		cl = fib_rules;
+		if (cl && (cl = cl->cl_next) != NULL) {
+			clp = &fib_rules->cl_next;
+			if (cl->cl_preference)
+				new_cl->cl_preference = cl->cl_preference - 1;
+		}
+	}
+
+	while ( (cl = *clp) != NULL ) {
+		if (cl->cl_preference >= new_cl->cl_preference)
+			break;
+		clp = &cl->cl_next;
+	}
+
+	new_cl->cl_next = cl;
+	cli();
+	*clp = new_cl;
+	sti();
+
+	if (r->rtrmsg_rtmsgs) {
+		n->nlmsg_type = RTMSG_NEWROUTE;
+		r->rtrmsg_rtmsg->rtmsg_class = class->cl_id;
+		return rtmsg_process(n, r->rtrmsg_rtmsg);
+	}
+	return 0;
+}
+
+
+#define FZ_MAX_DIVISOR 1024
+
+static __inline__ u32 fib_hash(u32 key, u32 mask)
+{
+	u32 h;
+	h = key^(key>>20);
+	h = h^(h>>10);
+	h = h^(h>>5);
+	return h & mask;
+}
+
+static __inline__ struct fib_node ** fz_hash_p(u32 key, struct fib_zone *fz)
+{
+	return &fz->fz_hash[fib_hash(key, fz->fz_hashmask)];
+}
+
+static __inline__ struct fib_node * fz_hash(u32 key, struct fib_zone *fz)
+{
+	return fz->fz_hash[fib_hash(key, fz->fz_hashmask)];
+}
+
+/*
+ * Free FIB node.
+ */
+
+static void fib_free_node(struct fib_node * f)
+{
+	struct fib_info * fi = f->fib_info;
+	if (fi && !--fi->fib_refcnt) {
+#if RT_CACHE_DEBUG >= 2
+		printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null");
+#endif
+		if (fi->fib_next)
+			fi->fib_next->fib_prev = fi->fib_prev;
+		if (fi->fib_prev)
+			fi->fib_prev->fib_next = fi->fib_next;
+		if (fi == fib_info_list)
+			fib_info_list = fi->fib_next;
+	}
+	kfree_s(f, sizeof(struct fib_node));
+}
+
+static __inline__ int fib_flags_trans(unsigned flags)
+{
+	if (flags & RTF_BROADCAST)
+		return IS_BROADCAST;
+	if (flags & RTF_MULTICAST)
+		return IS_MULTICAST;
+	if (flags & RTF_LOCAL)
+		return IS_MYADDR;
+	return 0;
+}
+
+unsigned ip_fib_chk_addr(u32 addr)
+{
+	struct fib_zone * fz;
+	struct fib_node * f;
+
+	/* 
+	 *	Accept both `all ones' and `all zeros' as BROADCAST. 
+	 *	(Support old BSD in other words). This old BSD 
+	 *	support will go very soon as it messes other things
+	 *	up.
+	 */
+
+	if (addr == INADDR_ANY || addr == 0xFFFFFFFF)
+		return RTF_LOCAL|RTF_BROADCAST;
+
+	if ((addr & htonl(0x7F000000L)) == htonl(0x7F000000L))
+		return RTF_LOCAL|RTF_INTERFACE;
+
+	if (MULTICAST(addr))
+		return RTF_MULTICAST;
+
+	addr = ntohl(addr);
+	for (fz = local_class.fib_zone_list; fz; fz = fz->fz_next) {
+		u32 key = (addr&fz->fz_mask)>>fz->fz_logmask;
+		for (f = fz_hash(key, fz); f; f = f->fib_next) {
+			if (key != f->fib_key || (f->fib_flag & FIBFLG_DOWN))
+				continue;
+			if (!f->fib_info)
+				return 0;
+			return f->fib_info->fib_flags&RTF_ADDRCLASSMASK;
+		}
+	}
+
+	return 0;
+}
+
+int __ip_chk_addr(unsigned long addr)
+{
+	return fib_flags_trans(ip_fib_chk_addr(addr));
+}
+
+/*
+ *	Find the first device with a given source address.
+ */
+ 
+struct device *ip_dev_find(unsigned long addr, char *name)
+{
+	struct fib_zone * fz = local_class.fib_zones[0];
+	u32 key;
+	struct fib_node * f;
+
+	key = (ntohl(addr)&fz->fz_mask)>>fz->fz_logmask;
+	for (f = fz_hash(key, fz); f; f = f->fib_next) {
+		if (key == f->fib_key &&
+		    !(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
+		    f->fib_info->fib_flags == (RTF_IFLOCAL&~RTF_UP)) {
+			if (!name || strcmp(name, f->fib_info->fib_dev->name) == 0)
+				return f->fib_info->fib_dev;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ *	Find tunnel with a given source and destination.
+ */
+ 
+struct device *ip_dev_find_tunnel(u32 daddr, u32 saddr)
+{
+	struct fib_zone * fz = local_class.fib_zones[0];
+	u32 key;
+	struct fib_node * f;
+
+	key = (ntohl(daddr)&fz->fz_mask)>>fz->fz_logmask;
+	for (f = fz_hash(key, fz); f; f = f->fib_next) {
+		if (key == f->fib_key &&
+		    !(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
+		    f->fib_info->fib_flags == (RTF_IFLOCAL&~RTF_UP)) {
+			struct device *dev = f->fib_info->fib_dev;
+			if (dev->type == ARPHRD_TUNNEL &&
+			    dev->pa_dstaddr == saddr)
+				return dev;
+		}
+		if (!f->fib_info)
+			return NULL;
+	}
+
+	return NULL;
+}
+
+
+int ip_fib_chk_default_gw(u32 addr, struct device *dev)
+{
+	struct fib_rule *cl;
+	struct fib_node * f;
+
+	for (cl = fib_rules; cl; cl = cl->cl_next) {
+		if (cl->cl_srcmask || cl->cl_dstmask || cl->cl_tos ||
+		    cl->cl_dev || cl->cl_action != RTP_GO || !cl->cl_class ||
+		    !cl->cl_class->fib_zones[32])
+			continue;
+		for (f = cl->cl_class->fib_zones[32]->fz_hash[0]; f; f = f->fib_next) {
+			struct fib_info *fi = f->fib_info;
+			if (!(f->fib_flag & (FIBFLG_DOWN|FIBFLG_REJECT|FIBFLG_THROW)) &&
+			    fi->fib_gateway == addr &&
+			    fi->fib_dev == dev &&
+			    fi->fib_flags&RTF_GATEWAY)
+				return 0;
+		}
+	}
+	return -1;
+}
+
+
+/*
+ * Main lookup routine.
+ */
+
+
+int
+fib_lookup(struct fib_result *res, u32 daddr, u32 src, u8 tos,
+	   struct device *devin, struct device *devout)
+{
+	struct fib_node * f;
+	struct fib_rule * cl;
+	u32		dst;
+	int		local = tos & 1;
+
+	tos &= IPTOS_TOS_MASK;
+	dst = ntohl(daddr);
+
+	for (cl = fib_rules; cl; cl=cl->cl_next) {
+		struct fib_zone * fz;
+
+		if (((src^cl->cl_src) & cl->cl_srcmask) ||
+		    ((dst^cl->cl_dst) & cl->cl_dstmask) ||
+		    (cl->cl_tos && cl->cl_tos != tos) ||
+		    (cl->cl_dev && cl->cl_dev != devin))
+			continue;
+
+		switch (cl->cl_action) {
+		case RTP_GO:
+		case RTP_NAT:
+		case RTP_MASQUERADE:
+		default:
+			break;
+		case RTP_UNREACHABLE:
+			return -ENETUNREACH;
+		case RTP_DROP:
+			return -EINVAL;
+		case RTP_PROHIBIT:
+			return -EACCES;
+		}
+
+		for (fz = cl->cl_class->fib_zone_list; fz; fz = fz->fz_next) {
+			u32 key = (dst&fz->fz_mask)>>fz->fz_logmask;
+
+			for (f = fz_hash(key, fz); f; f = f->fib_next) {
+				if (key != f->fib_key ||
+				    (f->fib_flag & FIBFLG_DOWN) ||
+				    (f->fib_tos && f->fib_tos != tos))
+					continue;
+				if (f->fib_flag & FIBFLG_THROW)
+					goto next_class;
+				if (f->fib_flag & FIBFLG_REJECT)
+					return -ENETUNREACH;
+				if (devout && f->fib_info->fib_dev != devout)
+					continue;
+				if (!local || !(f->fib_info->fib_flags&RTF_GATEWAY)) {
+					res->f = f;
+					res->fr = cl;
+					res->fm = fz->fz_logmask;
+					return 0;
+				}
+			}
+		}
+next_class:
+	}
+	return -ENETUNREACH;
+}
+
+static int fib_autopublish(int op, struct fib_node *f, int logmask)
+{	
+	struct fib_zone *fz;
+	struct fib_node *f1;
+	struct arpreq r;
+	u32 addr = htonl(f->fib_key<<logmask);
+
+	if (f->fib_flag || LOOPBACK(addr) ||
+	    (!RT_LOCALADDR(f->fib_info->fib_flags) &&
+	     !(f->fib_info->fib_flags&RTF_NAT)))
+		return 0;
+
+	memset(&r, 0, sizeof(struct arpreq));
+	r.arp_flags = ATF_PUBL|ATF_PERM|ATF_MAGIC;
+	if (logmask)
+		r.arp_flags |= ATF_NETMASK;
+	((struct sockaddr_in*)&r.arp_pa)->sin_family = AF_INET;
+	((struct sockaddr_in*)&r.arp_pa)->sin_addr.s_addr = addr;
+	((struct sockaddr_in*)&r.arp_netmask)->sin_family = AF_INET;
+	((struct sockaddr_in*)&r.arp_netmask)->sin_addr.s_addr = fib_netmask(logmask);
+
+	if (op)
+		return arp_req_set(&r, NULL);
+	
+	fz = &local_class.fib_zone_list[logmask];
+
+	for (f1 = fz_hash(f->fib_key, fz); f1; f1=f1->fib_next)	{
+		if (f->fib_key != f1->fib_key || f1->fib_flag ||
+		    f->fib_info->fib_flags != f1->fib_info->fib_flags)
+			continue;
+		return 0;
+	}
+
+	return arp_req_delete(&r, NULL);
+}
+
+#define FIB_SCAN(f, fp) \
+for ( ; ((f) = *(fp)) != NULL; (fp) = &(f)->fib_next)
+
+#define FIB_SCAN_KEY(f, fp, key) \
+for ( ; ((f) = *(fp)) != NULL && (f)->fib_key == (key); (fp) = &(f)->fib_next)
+
+#define FIB_CONTINUE(f, fp) \
+{ \
+	fp = &f->fib_next; \
+	continue; \
+}
+
+static int fib_delete(struct in_rtmsg * r, struct device *dev,
+		      struct fib_class *class, struct nlmsghdr *n)
+{
+	struct fib_node **fp, *f;
+	struct fib_zone *fz = class->fib_zones[32-r->rtmsg_prefixlen];
+	int logmask = 32 - r->rtmsg_prefixlen;
+	u32 dst = ntohl(r->rtmsg_prefix.s_addr);
+	u32 gw = r->rtmsg_gateway.s_addr;
+	short metric = r->rtmsg_metric;
+	u8 tos = r->rtmsg_tos;
+	u8 fibflg = 0;
+	int found=0;
+	unsigned flags;
+	u32 key;
+
+	flags = r->rtmsg_flags;
+	if (flags & RTF_REJECT)
+		fibflg |= FIBFLG_REJECT;
+	else if (flags & RTF_THROW)
+		fibflg |= FIBFLG_THROW;
+	flags &= ~(RTF_UP|RTF_REJECT|RTF_THROW);
+
+	if (fz != NULL)	{
+		key = (dst&fz->fz_mask)>>logmask;
+		fp = fz_hash_p(key, fz);
+
+		FIB_SCAN(f, fp) {
+			if (f->fib_key == key)
+				break;
+		}
+		FIB_SCAN_KEY(f, fp, key) {
+			if (f->fib_tos == tos)
+				break;
+		}
+
+		while ((f = *fp) != NULL && f->fib_key == key && f->fib_tos == tos) {
+			struct fib_info * fi = f->fib_info;
+
+			/*
+			 * If metric was not specified (<0), match all metrics.
+			 */
+			if (metric >= 0 && f->fib_metric != metric)
+				FIB_CONTINUE(f, fp);
+
+			if (flags & RTF_MAGIC) {
+				/* "Magic" deletions require exact match */
+				if (!fi || (fi->fib_flags^flags) ||
+				    fi->fib_dev != dev ||
+				    fi->fib_gateway != gw)
+					FIB_CONTINUE(f, fp);
+			} else {
+				/*
+				 * Device, gateway, reject and throw are
+				 * also checked if specified.
+				 */
+				if ((dev && fi && fi->fib_dev != dev) ||
+				    (gw  && fi && fi->fib_gateway != gw) ||
+				    (fibflg && (f->fib_flag^fibflg)&~FIBFLG_DOWN))
+					FIB_CONTINUE(f, fp);
+			}
+			cli();
+			/* It's interesting, can this operation be not atomic? */
+			*fp = f->fib_next;
+			sti();
+			if (class == &local_class)
+				fib_autopublish(0, f, logmask);
+			rtmsg_fib(RTMSG_DELROUTE, f, logmask, class, n);
+			fib_free_node(f);
+			found++;
+		}
+		fz->fz_nent -= found;
+	}
+
+	if (found) {
+		fib_stamp++;
+		rt_cache_flush(0);
+		rtmsg_ack(n, 0);
+		return 0;
+	}
+	rtmsg_ack(n, ESRCH);
+	return -ESRCH;
+}
+
+static struct fib_info * fib_create_info(struct device * dev, struct in_rtmsg *r)
+{
+	struct fib_info * fi;
+	unsigned flags = r->rtmsg_flags;
+	u32 gw = r->rtmsg_gateway.s_addr;
+	unsigned short mtu;
+	unsigned short irtt;
+	unsigned long  window;
+
+	mtu = dev ? dev->mtu : 0;
+	if (flags&RTF_MSS && r->rtmsg_mtu < mtu && r->rtmsg_mtu >= 68)
+		mtu = r->rtmsg_mtu;
+	window = (flags & RTF_WINDOW) ? r->rtmsg_window : 0;
+	irtt = (flags & RTF_IRTT) ? r->rtmsg_rtt : TCP_TIMEOUT_INIT;
+
+	flags &= RTF_FIB;
+
+	for (fi=fib_info_list; fi; fi = fi->fib_next) {
+		if (fi->fib_gateway != gw ||
+		    fi->fib_dev != dev  ||
+		    fi->fib_flags != flags ||
+		    fi->fib_mtu != mtu ||
+		    fi->fib_window != window ||
+		    fi->fib_irtt != irtt)
+			continue;
+		fi->fib_refcnt++;
+#if RT_CACHE_DEBUG >= 2
+		printk("fib_create_info: fi %08x/%s/%04x is duplicate\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null", fi->fib_flags);
+#endif
+		return fi;
+	}
+	fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL);
+	if (!fi)
+		return NULL;
+	memset(fi, 0, sizeof(struct fib_info));
+	fi->fib_flags = flags;
+	fi->fib_dev = dev;
+	fi->fib_gateway = gw;
+	fi->fib_mtu = mtu;
+	fi->fib_window = window;
+	fi->fib_refcnt++;
+	fi->fib_next = fib_info_list;
+	fi->fib_prev = NULL;
+	fi->fib_irtt = irtt;
+	if (fib_info_list)
+		fib_info_list->fib_prev = fi;
+	fib_info_list = fi;
+#if RT_CACHE_DEBUG >= 2
+	printk("fib_create_info: fi %08x/%s/%04x is created\n", fi->fib_gateway, fi->fib_dev ? fi->fib_dev->name : "null", fi->fib_flags);
+#endif
+	return fi;
+}
+
+static __inline__ void fib_rebuild_zone(struct fib_zone *fz,
+					struct fib_node **old_ht,
+					int old_divisor)
+{
+	int i;
+	struct fib_node **ht = fz->fz_hash;
+	u32 hashmask = fz->fz_hashmask;
+	struct fib_node *f, **fp, *next;
+	unsigned hash;
+
+	for (i=0; i<old_divisor; i++) {
+		for (f=old_ht[i]; f; f=next) {
+			next = f->fib_next;
+			f->fib_next = NULL;
+			hash = fib_hash(f->fib_key, hashmask);
+			for (fp = &ht[hash]; *fp; fp = &(*fp)->fib_next)
+				/* NONE */;
+			*fp = f;
+		}
+	}
+}
+
+static void fib_rehash_zone(struct fib_zone *fz)
+{
+	struct fib_node **ht, **old_ht;
+	int old_divisor, new_divisor;
+	u32 new_hashmask;
+		
+	old_divisor = fz->fz_divisor;
+
+	switch (old_divisor) {
+	case 16:
+		new_divisor = 256;
+		new_hashmask = 0xFF;
+		break;
+	case 256:
+		new_divisor = 1024;
+		new_hashmask = 0x3FF;
+		break;
+	default:
+		printk(KERN_CRIT "route.c: bad divisor %d!\n", old_divisor);
+		return;
+	}
+#if RT_CACHE_DEBUG >= 2
+	printk("fib_rehash_zone: hash for zone %d grows from %d\n", fz->fz_logmask, old_divisor);
+#endif
+
+	ht = kmalloc(new_divisor*sizeof(struct rtable*), GFP_KERNEL);
+
+	if (ht)	{
+		memset(ht, 0, new_divisor*sizeof(struct fib_node*));
+		start_bh_atomic();
+		old_ht = fz->fz_hash;
+		fz->fz_hash = ht;
+		fz->fz_hashmask = new_hashmask;
+		fz->fz_divisor = new_divisor;
+		fib_rebuild_zone(fz, old_ht, old_divisor);
+		fib_stamp++;
+		end_bh_atomic();
+		kfree(old_ht);
+	}
+}
+
+static struct fib_zone *
+fib_new_zone(struct fib_class *class, int logmask)
+{
+	int i;
+	struct fib_zone *fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL);
+	if (!fz)
+		return NULL;
+
+	memset(fz, 0, sizeof(struct fib_zone));
+	if (logmask < 32) {
+		fz->fz_divisor = 16;
+		fz->fz_hashmask = 0xF;
+	} else {
+		fz->fz_divisor = 1;
+		fz->fz_hashmask = 0;
+	}
+	fz->fz_hash = kmalloc(fz->fz_divisor*sizeof(struct fib_node*), GFP_KERNEL);
+	if (!fz->fz_hash) {
+		kfree(fz);
+		return NULL;
+	}
+	memset(fz->fz_hash, 0, fz->fz_divisor*sizeof(struct fib_node*));
+	fz->fz_logmask = logmask;
+	fz->fz_mask = ntohl(fib_mask(logmask));
+	for (i=logmask-1; i>=0; i--)
+		if (class->fib_zones[i])
+			break;
+	start_bh_atomic();
+	if (i<0) {
+		fz->fz_next = class->fib_zone_list;
+		class->fib_zone_list = fz;
+	} else {
+		fz->fz_next = class->fib_zones[i]->fz_next;
+		class->fib_zones[i]->fz_next = fz;
+	}
+	class->fib_zones[logmask] = fz;
+	fib_stamp++;
+	end_bh_atomic();
+	return fz;
+}
+
+static int fib_create(struct in_rtmsg *r, struct device *dev,
+		      struct fib_class *class, struct nlmsghdr *n)
+{
+	struct fib_node *f, *f1, **fp;
+	struct fib_node **dup_fp = NULL;
+	struct fib_zone * fz;
+	struct fib_info * fi;
+
+	int logmask = 32 - r->rtmsg_prefixlen;
+	u32 dst = ntohl(r->rtmsg_prefix.s_addr);
+	u32 gw  = r->rtmsg_gateway.s_addr;
+	short metric = r->rtmsg_metric;
+	unsigned flags = r->rtmsg_flags;
+	u8 tos = r->rtmsg_tos;
+	u8 fibflg = 0;
+	u32 key;
+
+	/*
+	 *	Allocate an entry and fill it in.
+	 */
+	 
+	f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
+	if (f == NULL) {
+		rtmsg_ack(n, ENOMEM);
+		return -ENOMEM;
+	}
+
+	memset(f, 0, sizeof(struct fib_node));
+
+	if (!(flags & RTF_UP))
+		fibflg = FIBFLG_DOWN;
+	if (flags & RTF_REJECT)
+		fibflg |= FIBFLG_REJECT;
+	else if (flags & RTF_THROW)
+		fibflg |= FIBFLG_THROW;
+
+	flags &= ~(RTF_UP|RTF_REJECT|RTF_THROW);
+	r->rtmsg_flags = flags;
+
+	fi = NULL;
+	if (!(fibflg & (FIBFLG_REJECT|FIBFLG_THROW))) {
+		if  ((fi = fib_create_info(dev, r)) == NULL) {
+			kfree_s(f, sizeof(struct fib_node));
+			rtmsg_ack(n, ENOMEM);
+			return -ENOMEM;
+		}
+		f->fib_info = fi;
+		flags = fi->fib_flags;
+	}
+
+	f->fib_key = key = dst>>logmask;
+	f->fib_metric = metric;
+	f->fib_tos    = tos;
+	f->fib_flag = fibflg;
+	fz = class->fib_zones[logmask];
+
+	if (!fz && !(fz = fib_new_zone(class, logmask))) {
+		fib_free_node(f);
+		rtmsg_ack(n, ENOMEM);
+		return -ENOMEM;
+	}
+
+	if (fz->fz_nent > (fz->fz_divisor<<2) &&
+	    fz->fz_divisor < FZ_MAX_DIVISOR &&
+	    (!logmask || (1<<(32-logmask)) > fz->fz_divisor))
+		fib_rehash_zone(fz);
+
+	fp = fz_hash_p(key, fz);
+
+	/*
+	 * Scan list to find the first route with the same destination
+	 */
+	FIB_SCAN(f1, fp) {
+		if (f1->fib_key == key)
+			break;
+	}
+
+	/*
+	 * Find route with the same destination and tos.
+	 */
+	FIB_SCAN_KEY(f1, fp, dst) {
+		if (f1->fib_tos <= tos)
+			break;
+	}
+
+	/*
+	 * Find route with the same destination/tos and less (or equal) metric.
+	 * "Magic" additions go to the end of list.
+	 */
+	for ( ; (f1 = *fp) != NULL && f1->fib_key == key && f1->fib_tos == tos;
+	     fp = &f1->fib_next) {
+		if (f1->fib_metric >= metric && metric != MAGIC_METRIC)
+			break;
+
+		/*
+		 *	Record route with the same destination/tos/gateway/dev,
+		 *	but less metric.
+		 */
+		if (!dup_fp) {
+			struct fib_info *fi1 = f1->fib_info;
+			
+			if ((fibflg^f1->fib_flag) & ~FIBFLG_DOWN)
+				continue;
+			if (fi == fi1 ||
+			    (fi && fi1 &&
+			     fi->fib_dev == fi1->fib_dev &&
+			     fi->fib_gateway == fi1->fib_gateway &&
+			     !(flags&RTF_MAGIC)))
+				dup_fp = fp;
+		}
+	}
+
+	/*
+	 * Is it already present?
+	 */
+
+	if (f1 && f1->fib_key == key && f1->fib_tos == tos &&
+	    f1->fib_metric == metric && f1->fib_info == fi) {
+		fib_free_node(f);
+
+		if (fibflg == f1->fib_flag) {
+			rtmsg_ack(n, EEXIST);
+			return -EEXIST;
+		} else {
+			fib_stamp++;
+			f1->fib_flag = fibflg;
+			rt_cache_flush(0);
+			rtmsg_ack(n, 0);
+			return 0;
+		}
+	}
+
+	/*
+	 * Do not add "magic" route, if better one is already present.
+	 */
+	if ((flags & RTF_MAGIC) && dup_fp) {
+		fib_free_node(f);
+		rtmsg_ack(n, EEXIST);
+		return -EEXIST;
+	}
+
+	/*
+	 * Insert new entry to the list.
+	 */
+
+	cli();
+	f->fib_next = f1;
+	*fp = f;
+	sti();
+	fz->fz_nent++;
+	if (class == &local_class && !dup_fp)
+		fib_autopublish(1, f, logmask);
+	rtmsg_fib(RTMSG_NEWROUTE, f, logmask, class, n);
+
+	if (flags & RTF_MAGIC) {
+		fib_stamp++;
+		rt_cache_flush(0);
+		rtmsg_ack(n, 0);
+		return 0;
+	}
+
+	/*
+	 *	Clean routes with the same destination,tos,gateway and device,
+	 *	but different metric.
+	 */
+	fp = dup_fp ? : &f->fib_next;
+
+	while ((f1 = *fp) != NULL && f1->fib_key == key && f1->fib_tos == tos) {
+		if (f1 == f || ((f1->fib_flag^fibflg)&~FIBFLG_DOWN))
+			FIB_CONTINUE(f1, fp);
+
+		if (f1->fib_info != fi &&
+		    (!fi || !f1->fib_info ||
+		     f1->fib_info->fib_gateway != gw ||
+		     f1->fib_info->fib_dev != dev))
+			FIB_CONTINUE(f1, fp);
+
+		cli();
+		*fp = f1->fib_next;
+		sti();
+		fz->fz_nent--;
+		rtmsg_fib(RTMSG_DELROUTE, f1, logmask, class, n);
+		fib_free_node(f1);
+	}
+	fib_stamp++;
+	rt_cache_flush(0);
+	rtmsg_ack(n, 0);
+	return 0;
+}
+
+static int fib_flush_list(struct fib_node ** fp, struct device *dev,
+			  int logmask, struct fib_class *class)
+{
+	int found = 0;
+	struct fib_node *f;
+
+	while ((f = *fp) != NULL) {
+		if (!f->fib_info || f->fib_info->fib_dev != dev)
+			FIB_CONTINUE(f, fp);
+		cli();
+		*fp = f->fib_next;
+		sti();
+#ifdef CONFIG_RTNETLINK
+		if (rt_nl_flags&RTCTL_FLUSH)
+		    rtmsg_fib(RTMSG_DELROUTE, f, logmask, class, 0);
+#endif
+		fib_free_node(f);
+		found++;
+	}
+	return found;
+}
+
+static void fib_flush(struct device *dev)
+{
+	struct fib_class *class;
+	struct fib_rule *cl, **clp;
+	struct fib_zone *fz;
+	int found = 0;
+	int i, tmp, cl_id;
+
+
+	for (cl_id = RT_CLASS_MAX; cl_id>=0; cl_id--) {
+		if ((class = fib_classes[cl_id])==NULL)
+			continue;
+		for (fz = class->fib_zone_list; fz; fz = fz->fz_next) {
+			tmp = 0;
+			for (i=fz->fz_divisor-1; i>=0; i--)
+				tmp += fib_flush_list(&fz->fz_hash[i], dev,
+						      fz->fz_logmask, class);
+			fz->fz_nent -= tmp;
+			found += tmp;
+		}
+	}
+	
+	clp = &fib_rules;
+	while ( (cl=*clp) != NULL) {
+		if (cl->cl_dev != dev) {
+			clp = &cl->cl_next;
+			continue;
+		}
+		found++;
+		cli();
+		*clp = cl->cl_next;
+		sti();
+		kfree(cl);
+	}
+		
+	if (found) {
+		fib_stamp++;
+		rt_cache_flush(1);
+	}
+}
+
+#ifdef CONFIG_PROC_FS
+
+static unsigned __inline__ fib_flag_trans(u8 fibflg)
+{
+	unsigned ret = RTF_UP;
+	if (!fibflg)
+		return ret;
+	if (fibflg & FIBFLG_DOWN)
+		ret &= ~RTF_UP;
+	if (fibflg & FIBFLG_REJECT)
+		ret |= RTF_REJECT;
+	if (fibflg & FIBFLG_THROW)
+		ret |= RTF_THROW;
+	return ret;
+}
+
+/* 
+ *	Called from the PROCfs module. This outputs /proc/net/route.
+ *
+ *	We preserve the old format but pad the buffers out. This means that
+ *	we can spin over the other entries as we read them. Remember the
+ *	gated BGP4 code could need to read 60,000+ routes on occasion (that's
+ *	about 7Mb of data). To do that ok we will need to also cache the
+ *	last route we got to (reads will generally be following on from
+ *	one another without gaps).
+ */
+ 
+static int fib_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct fib_class *class;
+	struct fib_zone *fz;
+	struct fib_node *f;
+	int len=0;
+	off_t pos=0;
+	char temp[129];
+	int i;
+	int cl_id;
+	
+	pos = 128;
+
+	if (offset<128)
+	{
+		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\tTOS\tClass");
+		len = 128;
+  	}
+  	
+	fib_lock();
+
+	for (cl_id=RT_CLASS_MAX-1; cl_id >= 0; cl_id--) {
+		class = fib_classes[cl_id];
+		if (!class)
+			continue;
+		for (fz=class->fib_zone_list; fz; fz = fz->fz_next)
+		{
+		int maxslot;
+		struct fib_node ** fp;
+
+		if (fz->fz_nent == 0)
+			continue;
+
+		if (pos + 128*fz->fz_nent <= offset) {
+			pos += 128*fz->fz_nent;
+			len = 0;
+			continue;
+		}
+
+		maxslot = fz->fz_divisor;
+		fp	= fz->fz_hash;
+			
+		for (i=0; i < maxslot; i++, fp++) {
+			
+			for (f = *fp; f; f = f->fib_next) 
+			{
+				struct fib_info * fi;
+				unsigned	flags;
+
+				/*
+				 *	Spin through entries until we are ready
+				 */
+				pos += 128;
+
+				if (pos <= offset)
+				{
+					len=0;
+					continue;
+				}
+					
+				fi = f->fib_info;
+				flags = fib_flag_trans(f->fib_flag);
+
+				if (fi)
+					flags |= fi->fib_flags;
+				sprintf(temp, "%s\t%08lX\t%08X\t%04X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%02x\t%02x",
+					fi && fi->fib_dev ? fi->fib_dev->name : "*", htonl(f->fib_key<<fz->fz_logmask), fi ? fi->fib_gateway : 0,
+					flags, 0, 0, f->fib_metric,
+					htonl(fz->fz_mask), fi ? (int)fi->fib_mtu : 0, fi ? fi->fib_window : 0, fi ? (int)fi->fib_irtt : 0, f->fib_tos, class->cl_id);
+				sprintf(buffer+len,"%-127s\n",temp);
+
+				len += 128;
+				if (pos >= offset+length)
+					goto done;
+			}
+		}
+        }
+	}
+
+done:
+	fib_unlock();
+  	
+  	*start = buffer+len-(pos-offset);
+  	len = pos - offset;
+  	if (len>length)
+  		len = length;
+  	return len;
+}
+
+static int fib_local_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	struct fib_zone *fz;
+	struct fib_node *f;
+	int len=0;
+	off_t pos=0;
+	char temp[129];
+	int i;
+	
+	pos = 128;
+
+	if (offset<128)
+	{
+		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\tTOS\tClass");
+		len = 128;
+  	}
+  	
+	fib_lock();
+
+	for (fz=local_class.fib_zone_list; fz; fz = fz->fz_next)
+	{
+		int maxslot;
+		struct fib_node ** fp;
+
+		if (fz->fz_nent == 0)
+			continue;
+
+		if (pos + 128*fz->fz_nent <= offset)
+		{
+			pos += 128*fz->fz_nent;
+			len = 0;
+			continue;
+		}
+
+		maxslot = fz->fz_divisor;
+		fp	= fz->fz_hash;
+			
+		for (i=0; i < maxslot; i++, fp++)
+		{
+			
+			for (f = *fp; f; f = f->fib_next) 
+			{
+				unsigned	flags;
+				struct fib_info * fi;
+
+				/*
+				 *	Spin through entries until we are ready
+				 */
+				pos += 128;
+
+				if (pos <= offset)
+				{
+					len=0;
+					continue;
+				}
+					
+				fi = f->fib_info;
+				flags = fib_flag_trans(f->fib_flag);
+
+				if (fi)
+					flags |= fi->fib_flags;
+				sprintf(temp, "%s\t%08lX\t%08X\t%X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%02x\t%02x",
+					fi && fi->fib_dev ? fi->fib_dev->name : "*",
+					htonl(f->fib_key<<fz->fz_logmask),
+					fi ? fi->fib_gateway : 0,
+					flags, 0, 0, f->fib_metric,
+					htonl(fz->fz_mask), fi ? (int)fi->fib_mtu : 0, fi ? fi->fib_window : 0, fi ? (int)fi->fib_irtt : 0, f->fib_tos, RT_CLASS_LOCAL);
+				sprintf(buffer+len,"%-127s\n",temp);
+
+				len += 128;
+				if (pos >= offset+length)
+					goto done;
+			}
+		}
+        }
+
+done:
+	fib_unlock();
+  	
+  	*start = buffer+len-(pos-offset);
+  	len = pos - offset;
+  	if (len>length)
+  		len = length;
+  	return len;
+}
+
+static int fib_rules_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len=0;
+	off_t pos=0;
+	char temp[129];
+	struct fib_rule *cl;
+
+	pos = 128;
+
+	if (offset<128) {
+		sprintf(buffer,"%-127s\n","Pref\tSource\t\tSrcMask\t\tDst\t\tDstMask\t\tIface\tTOS\tClass\tFlags\tSrcMap\n");
+		len = 128;
+  	}
+	
+  	
+	fib_lock();
+
+	for (cl = fib_rules; cl; cl = cl->cl_next) {
+		/*
+		 *	Spin through entries until we are ready
+		 */
+		pos += 128;
+
+		if (pos <= offset) {
+			len = 0;
+			continue;
+		}
+					
+		sprintf(temp, "%d\t%08X\t%08X\t%08X\t%08X\t%s\t%02X\t%02x\t%02X\t%02X\t%08X",
+			cl->cl_preference,
+			cl->cl_src, cl->cl_srcmask,
+			cl->cl_dst, cl->cl_dstmask,
+			cl->cl_dev ? cl->cl_dev->name : "*",
+			cl->cl_tos, cl->cl_class ? cl->cl_class->cl_id : 0,
+			cl->cl_flags, cl->cl_action, cl->cl_srcmap
+			);
+		sprintf(buffer+len,"%-127s\n",temp);
+		len += 128;
+		if (pos >= offset+length)
+			goto done;
+	}
+
+done:
+	fib_unlock();
+  	
+  	*start = buffer+len-(pos-offset);
+  	len = pos-offset;
+  	if (len>length)
+  		len = length;
+  	return len;
+}
+
+static int fib_class_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len=0;
+	off_t pos=0;
+	char temp[129];
+	int i;
+	struct fib_class *cl;
+
+	pos = 128;
+
+	if (offset<128)
+	{
+		sprintf(buffer,"%-127s\n","Class\tSize\n");
+		len = 128;
+  	}
+	
+  	
+	fib_lock();
+
+	for (i = RT_CLASS_MAX; i>=0; i--) 
+	{
+		int sz = 0;
+		struct fib_zone *fz;
+
+		if ((cl=fib_classes[i])==NULL)
+			continue;
+
+		for (fz=cl->fib_zone_list; fz; fz=fz->fz_next)
+			sz += fz->fz_nent;
+
+		/*
+		 *	Spin through entries until we are ready
+		 */
+		pos += 128;
+
+		if (pos <= offset)
+		{
+			len = 0;
+			continue;
+		}
+					
+		sprintf(temp, "%d\t%d\n", cl->cl_id, sz);
+		sprintf(buffer+len,"%-127s\n",temp);
+		len += 128;
+		if (pos >= offset+length)
+			goto done;
+	}
+
+done:
+	fib_unlock();
+  	
+  	*start = buffer+len-(pos-offset);
+  	len = pos-offset;
+  	if (len>length)
+  		len = length;
+  	return len;
+}
+
+#endif
+
+static int rtmsg_process(struct nlmsghdr *n, struct in_rtmsg *r)
+{
+	unsigned long cmd=n->nlmsg_type;
+	struct device * dev = NULL;
+	struct fib_class *class;
+
+	if ((cmd != RTMSG_NEWROUTE && cmd != RTMSG_DELROUTE) ||
+	    (r->rtmsg_flags & (RTF_MAGIC|RTF_XRESOLVE|RTF_REINSTATE)) ||
+	    r->rtmsg_prefixlen > 32 ||
+	    (r->rtmsg_tos & ~IPTOS_TOS_MASK)) {
+		rtmsg_ack(n, EINVAL);
+		return -EINVAL;
+	}
+
+	/* Reject/throw directives have no interface/gateway specification */
+
+	if (r->rtmsg_flags & (RTF_REJECT|RTF_THROW)) {
+		r->rtmsg_device[0] = 0;
+		r->rtmsg_gateway.s_addr = 0;
+		r->rtmsg_flags &= ~RTF_GATEWAY;
+	}
+
+	/* Silly metric hack, it is preserved for "compatibility",
+	 * though I do not know any program using it.
+	 */
+
+	r->rtmsg_metric--;
+	if (cmd == RTMSG_NEWROUTE && r->rtmsg_metric < 0)
+		r->rtmsg_metric = 0;
+
+	if (cmd == RTMSG_DELROUTE)
+		r->rtmsg_flags &= RTF_FIB;
+
+	if (r->rtmsg_device[0]) {
+		dev = dev_get(r->rtmsg_device);
+		if (!dev) {
+			rtmsg_ack(n, ENODEV);
+			return -ENODEV;
+		}
+	}
+
+	if (r->rtmsg_gateway.s_addr && !(r->rtmsg_flags&RTF_NAT)) {
+		struct fib_info *fi;
+
+		fi = fib_lookup_info(r->rtmsg_gateway.s_addr, 0, 1,
+				     &loopback_dev, dev);
+		if (fi) {
+			if (fi->fib_flags&(RTF_BROADCAST|RTF_MULTICAST) &&
+			    cmd != RTMSG_DELROUTE)
+				return -EINVAL;
+			dev = fi->fib_dev;
+			if (fi->fib_flags&RTF_LOCAL) {
+				r->rtmsg_flags &= ~RTF_GATEWAY;
+				r->rtmsg_gateway.s_addr = 0;
+			}
+		} else if (cmd != RTMSG_DELROUTE)
+			return -ENETUNREACH;
+
+		/* If gateway is not found in routing table,
+		 * we could assume that user knows that he does.
+		 * It is link layer problem to decide reachable
+		 * this gateway or not. Good example is tunnel interface.
+		 * Another example is ethernet, ARP could (in theory)
+		 * resolve addresses, even if we had no routes.
+		 */
+	}
+
+	if (dev && (dev->flags&IFF_LOOPBACK)) {
+		if (r->rtmsg_flags&RTF_GATEWAY)
+			return -EINVAL;
+		/*
+		 * Loopback routes: we declare them local addresses.
+		 * It is the only reasonable solution to avoid
+		 * loopback routing loops.
+		 */
+		r->rtmsg_flags |= RTF_LOCAL|RTF_INTERFACE;
+	}
+
+	if (r->rtmsg_flags&RTF_GATEWAY) {
+		if (!dev && cmd != RTMSG_DELROUTE) {
+			rtmsg_ack(n, ENETUNREACH);
+			return -ENETUNREACH;
+		}
+	} else {
+		if (!dev && !(r->rtmsg_flags & (RTF_NAT|RTF_REJECT|RTF_THROW)) &&
+		    cmd != RTMSG_DELROUTE) {
+			rtmsg_ack(n, ENODEV);
+			return -ENODEV;
+		}
+	}
+
+	if (dev && dev->family != AF_INET)
+	{
+		rtmsg_ack(n, ENODEV);
+		return -ENODEV;
+	}
+
+	if (r->rtmsg_class == 0) {
+		if (r->rtmsg_flags&(RTF_LOCAL|RTF_NAT))
+			r->rtmsg_class = RT_CLASS_LOCAL;
+		else if ((r->rtmsg_flags&RTF_GATEWAY) &&
+			 (ipv4_config.fib_model==2 ||
+			  (ipv4_config.fib_model==1 && !r->rtmsg_prefixlen)))
+			r->rtmsg_class = RT_CLASS_DEFAULT;
+		else
+			r->rtmsg_class = RT_CLASS_MAIN;
+	}
+
+	if ((class = fib_classes[r->rtmsg_class]) == NULL)
+	{
+		rtmsg_ack(n, EINVAL);
+		return -EINVAL;
+	}
+
+	return (cmd == RTMSG_NEWROUTE ? fib_create : fib_delete)(r, dev, class, n);
+}
+
+
+static int rtrulemsg_process(struct nlmsghdr *n, struct in_rtrulemsg *r)
+{
+	unsigned long cmd=n->nlmsg_type;
+	struct device * dev = NULL;
+
+	if ((cmd != RTMSG_NEWRULE && cmd != RTMSG_DELRULE) ||
+	    r->rtrmsg_srclen > 32 || r->rtrmsg_dstlen > 32 ||
+	    (r->rtrmsg_tos & ~IPTOS_TOS_MASK))
+		return -EINVAL;
+
+	if (r->rtrmsg_device[0]) {
+		dev = dev_get(r->rtrmsg_device);
+		if (!dev)
+			return -ENODEV;
+		if (dev->family != AF_INET)
+			return -ENODEV;
+	}
+
+	if (cmd == RTMSG_DELRULE)
+		return fib_rule_delete(r, dev, n);
+
+	return fib_rule_add(r, dev, n);
+}
+
+
+static int ifmsg_process(struct nlmsghdr *n, struct in_ifmsg *r)
+{
+	unsigned long cmd=n->nlmsg_type;
+
+	if (cmd != RTMSG_NEWDEVICE && cmd != RTMSG_DELDEVICE) {
+		rtmsg_ack(n, EINVAL);
+		return -EINVAL;
+	}
+	rtmsg_ack(n, EINVAL);
+	return -EINVAL;
+}
+
+static int rtcmsg_process(struct nlmsghdr *n, struct in_rtctlmsg *r)
+{
+#ifdef CONFIG_RTNETLINK
+    if (r->rtcmsg_flags&RTCTL_DELAY)
+	rtmsg_ctl.nlmsg_delay = r->rtcmsg_delay;
+    if (r->rtcmsg_flags&RTCTL_OWNER)
+	rt_nl_owner = n->nlmsg_pid;
+    rt_nl_flags = r->rtcmsg_flags;
+    return 0;
+#else
+    return -EINVAL;
+#endif
+}
+
+static int get_rt_from_user(struct in_rtmsg *rtm, void *arg)
+{
+	int err;
+	struct rtentry r;
+
+	err = copy_from_user(&r, arg, sizeof(struct rtentry));
+	if (err)
+		return -EFAULT;
+	if (r.rt_dev)
+		if (copy_from_user(&rtm->rtmsg_device, r.rt_dev, 15))
+			return -EFAULT;
+
+	rtm->rtmsg_flags = r.rt_flags;
+
+	if (r.rt_dst.sa_family != AF_INET)
+		return -EAFNOSUPPORT;
+	rtm->rtmsg_prefix = ((struct sockaddr_in*)&r.rt_dst)->sin_addr;
+
+	if (rtm->rtmsg_flags&RTF_HOST) {
+		rtm->rtmsg_flags &= ~RTF_HOST;
+		rtm->rtmsg_prefixlen = 32;
+	} else {
+		u32 mask = ((struct sockaddr_in*)&r.rt_genmask)->sin_addr.s_addr;
+		if (r.rt_genmask.sa_family != AF_INET) {
+			printk(KERN_WARNING "%s forgot to specify route netmask.\n", current->comm);
+			if (r.rt_genmask.sa_family)
+				return -EAFNOSUPPORT;
+		}
+		if (bad_mask(mask, rtm->rtmsg_prefix.s_addr))
+			return -EINVAL;
+		rtm->rtmsg_prefixlen = 32 - fib_logmask(mask);
+	}
+	if ((rtm->rtmsg_flags & RTF_GATEWAY) &&
+	    r.rt_gateway.sa_family != AF_INET)
+		return -EAFNOSUPPORT;
+	rtm->rtmsg_gateway = ((struct sockaddr_in*)&r.rt_gateway)->sin_addr;
+	rtm->rtmsg_rtt = r.rt_irtt;
+	rtm->rtmsg_window = r.rt_window;
+	rtm->rtmsg_mtu = r.rt_mtu;
+	rtm->rtmsg_class = r.rt_class;
+	rtm->rtmsg_metric = r.rt_metric;
+        rtm->rtmsg_tos = r.rt_tos;
+	return 0;
+}
+
+
+/*
+ *	Handle IP routing ioctl calls. These are used to manipulate the routing tables
+ */
+ 
+int ip_rt_ioctl(unsigned int cmd, void *arg)
+{
+	int err;
+	union
+	{
+		struct in_rtmsg rtmsg;
+		struct in_ifmsg ifmsg;
+		struct in_rtrulemsg rtrmsg;
+	        struct in_rtctlmsg rtcmsg;
+	} m;
+	struct nlmsghdr dummy_nlh;
+
+	memset(&m, 0, sizeof(m));
+	dummy_nlh.nlmsg_seq = 0;
+	dummy_nlh.nlmsg_pid = current->pid;
+
+	switch (cmd)
+	{
+		case SIOCADDRT:		/* Add a route */
+		case SIOCDELRT:		/* Delete a route */
+			if (!suser())
+				return -EPERM;
+			err = get_rt_from_user(&m.rtmsg, arg);
+			if (err)
+				return err;
+			fib_lock();
+			dummy_nlh.nlmsg_type = cmd == SIOCDELRT ? RTMSG_DELROUTE
+					    : RTMSG_NEWROUTE;
+			err = rtmsg_process(&dummy_nlh, &m.rtmsg);
+			fib_unlock();
+			return err;
+		case SIOCRTMSG:
+			if (!suser())
+				return -EPERM;
+			err = copy_from_user(&dummy_nlh, arg, sizeof(dummy_nlh));
+			if (err)
+				return err;
+			switch (dummy_nlh.nlmsg_type)
+			{
+			case RTMSG_NEWROUTE:
+			case RTMSG_DELROUTE:
+				if (dummy_nlh.nlmsg_len < sizeof(m.rtmsg) + sizeof(dummy_nlh))
+					return -EINVAL;
+				err = copy_from_user(&m.rtmsg, arg+sizeof(dummy_nlh), sizeof(m.rtmsg));
+				if (err)
+					return err;
+				fib_lock();
+				err = rtmsg_process(&dummy_nlh, &m.rtmsg);
+				fib_unlock();
+				return err;
+			case RTMSG_NEWRULE:
+			case RTMSG_DELRULE:
+				if (dummy_nlh.nlmsg_len < sizeof(m.rtrmsg) + sizeof(dummy_nlh))
+					return -EINVAL;
+				err = copy_from_user(&m.rtrmsg, arg+sizeof(dummy_nlh), sizeof(m.rtrmsg));
+				if (err)
+					return err;
+				fib_lock();
+				err = rtrulemsg_process(&dummy_nlh, &m.rtrmsg);
+				fib_unlock();
+				return err;
+			case RTMSG_NEWDEVICE:
+			case RTMSG_DELDEVICE:
+				if (dummy_nlh.nlmsg_len < sizeof(m.ifmsg) + sizeof(dummy_nlh))
+					return -EINVAL;
+				err = copy_from_user(&m.ifmsg, arg+sizeof(dummy_nlh), sizeof(m.ifmsg));
+				if (err)
+					return err;
+				fib_lock();
+				err = ifmsg_process(&dummy_nlh, &m.ifmsg);
+				fib_unlock();
+				return err;
+			case RTMSG_CONTROL:
+				if (dummy_nlh.nlmsg_len < sizeof(m.rtcmsg) + sizeof(dummy_nlh))
+					return -EINVAL;
+				err = copy_from_user(&m.rtcmsg, arg+sizeof(dummy_nlh), sizeof(m.rtcmsg));
+				if (err)
+					return err;
+				fib_lock();
+				err = rtcmsg_process(&dummy_nlh, &m.rtcmsg);
+				fib_unlock();
+				return err;
+			default:
+				return -EINVAL;
+			}
+	}
+
+	return -EINVAL;
+}
+
+#ifdef CONFIG_RTNETLINK
+
+/*
+ *	Netlink hooks for IP
+ */
+
+
+static void
+rtmsg_fib(unsigned long type, struct fib_node *f, int logmask,
+	  struct fib_class *class, struct nlmsghdr *n)
+{
+	struct in_rtmsg *r;
+	struct fib_info *fi;
+
+	if (n && !(rt_nl_flags&RTCTL_ECHO) && rt_nl_owner == n->nlmsg_pid)
+	        return;
+
+	start_bh_atomic();
+	r = nlmsg_send(&rtmsg_ctl, type, sizeof(*r), n ? n->nlmsg_seq : 0,
+		       n ? n->nlmsg_pid : 0);
+	if (r) {
+		r->rtmsg_prefix.s_addr = htonl(f->fib_key<<logmask);
+		r->rtmsg_prefixlen = 32 - logmask;
+		r->rtmsg_metric= f->fib_metric;
+		r->rtmsg_tos = f->fib_tos;
+		r->rtmsg_class=class->cl_id;
+		r->rtmsg_flags = fib_flag_trans(f->fib_flag);
+
+		if ((fi = f->fib_info) != NULL)	{
+			r->rtmsg_gateway.s_addr = fi->fib_gateway;
+			r->rtmsg_flags |= fi->fib_flags;
+			r->rtmsg_mtu = fi->fib_mtu;
+			r->rtmsg_window = fi->fib_window;
+			r->rtmsg_rtt = fi->fib_irtt;
+			memset(r->rtmsg_device, 0, sizeof(r->rtmsg_device));
+			if (fi->fib_dev)
+				strcpy(r->rtmsg_device, fi->fib_dev->name);
+		}
+	}
+	end_bh_atomic();
+}
+
+static void
+__rtmsg_ack(struct nlmsghdr *n, int err)
+{
+	nlmsg_ack(&rtmsg_ctl, n->nlmsg_seq, n->nlmsg_pid, err);
+}
+
+
+static void
+rtmsg_dev(unsigned long type, struct device *dev, struct nlmsghdr *n)
+{
+	struct in_ifmsg *r;
+
+	start_bh_atomic();
+	r = nlmsg_send(&rtmsg_ctl, type, sizeof(*r), n ? n->nlmsg_seq : 0,
+		       n ? n->nlmsg_pid : 0);
+	if (r)
+	{
+		memset(r, 0, sizeof(*r));
+		r->ifmsg_lladdr.sa_family = dev->type;
+		memcpy(&r->ifmsg_lladdr.sa_data, dev->dev_addr, dev->addr_len);
+		r->ifmsg_prefix.s_addr = dev->pa_addr;
+		if (dev->flags & IFF_POINTOPOINT || dev->type == ARPHRD_TUNNEL)
+			r->ifmsg_brd.s_addr = dev->pa_dstaddr;
+		else
+			r->ifmsg_brd.s_addr = dev->pa_brdaddr;
+		r->ifmsg_flags = dev->flags;
+		r->ifmsg_mtu = dev->mtu;
+		r->ifmsg_metric = dev->metric;
+		r->ifmsg_prefixlen = 32 - fib_logmask(dev->pa_mask);
+		strcpy(r->ifmsg_device, dev->name);
+	}
+	end_bh_atomic();
+}
+
+static int fib_netlink_call(int minor, struct sk_buff *skb)
+{
+	struct nlmsghdr *nlh;
+	int    totlen = 0;
+	int    err = 0;
+
+	fib_lock();
+	while (skb->len >= sizeof(*nlh)) {
+		int rlen;
+		nlh = (struct nlmsghdr *)skb->data;
+		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
+		if (skb->len < rlen)
+			break;
+		totlen += rlen;
+		err = 0;
+		skb_pull(skb, rlen);
+		switch (nlh->nlmsg_type) {
+		case RTMSG_NEWROUTE:
+		case RTMSG_DELROUTE:
+			if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtmsg)) {
+				rtmsg_ack(nlh, EINVAL);
+				err = -EINVAL;
+				break;
+			}
+			err = rtmsg_process(nlh, (struct in_rtmsg*)nlh->nlmsg_data);
+			break;
+		case RTMSG_NEWRULE:
+		case RTMSG_DELRULE:
+			if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtrulemsg)) {
+				rtmsg_ack(nlh, EINVAL);
+				err = -EINVAL;
+				break;
+			}
+			err = rtrulemsg_process(nlh, (struct in_rtrulemsg*)nlh->nlmsg_data);
+			break;
+		case RTMSG_NEWDEVICE:
+		case RTMSG_DELDEVICE:
+			if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_ifmsg)) {
+				rtmsg_ack(nlh, EINVAL);
+				err = -EINVAL;
+				break;
+			}
+			err = ifmsg_process(nlh, (struct in_ifmsg*)nlh->nlmsg_data);
+			break;
+		case RTMSG_CONTROL:
+		        if (nlh->nlmsg_len < sizeof(*nlh)+sizeof(struct in_rtctlmsg)) {
+				rtmsg_ack(nlh, EINVAL);
+				err = -EINVAL;
+				break;
+			}
+			err = rtcmsg_process(nlh, (struct in_rtctlmsg*)nlh->nlmsg_data);
+			break;
+		default:
+			break;
+		}
+	}
+	kfree_skb(skb, FREE_READ);
+	fib_unlock();
+	if (!err || rt_nl_flags&RTCTL_ACK)
+	    return totlen;
+	return err;
+}
+
+#endif
+
+
+static int fib_magic(int op, unsigned flags, u32 dst, u32 mask, struct device *dev)
+{
+	struct nlmsghdr n;
+	struct in_rtmsg r;
+	memset(&r, 0, sizeof(r));
+	n.nlmsg_seq=0;
+	n.nlmsg_pid=0;
+	r.rtmsg_metric = MAGIC_METRIC;
+	r.rtmsg_prefix.s_addr = dst;
+	if (dev->flags&IFF_LOOPBACK)
+		flags |= RTF_LOCAL;
+	r.rtmsg_flags = flags;
+	r.rtmsg_prefixlen = 32 - fib_logmask(mask);
+
+	return (op == RTMSG_NEWROUTE ? fib_create : fib_delete)
+		(&r, dev, (flags&RTF_LOCAL) ? &local_class : &main_class, &n);
+}
+
+void ip_rt_change_broadcast(struct device *dev, u32 new_brd)
+{
+	fib_lock();
+	printk(KERN_DEBUG "%s changes brd %08lX -> %08X\n",
+	       dev->name, dev->pa_brdaddr, new_brd);
+	if (!ZERONET(dev->pa_addr) && dev->flags&IFF_BROADCAST) {
+		fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
+		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+		rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+		fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, new_brd, ~0, dev);
+	}
+	fib_unlock();
+}
+
+void ip_rt_change_dstaddr(struct device *dev, u32 dstaddr)
+{
+	fib_lock();
+	if (!ZERONET(dev->pa_addr) && (dev->flags&IFF_POINTOPOINT) && dev->type != ARPHRD_TUNNEL) {
+		printk(KERN_DEBUG "%s changes dst %08lX -> %08X\n",
+		       dev->name, dev->pa_dstaddr, dstaddr);
+		fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+		rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+		if (dstaddr)
+			fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, dstaddr, ~0, dev);
+	}
+	fib_unlock();
+}
+
+void ip_rt_change_netmask(struct device *dev, u32 mask)
+{
+	u32 net;
+
+	fib_lock();
+	printk(KERN_DEBUG "%s changes netmask %08lX -> %08X\n",
+	       dev->name, dev->pa_mask, mask);
+	if (ZERONET(dev->pa_addr)) {
+		fib_unlock();
+		return;
+	}
+	net = dev->pa_addr&dev->pa_mask;
+	fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
+	fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net, ~0, dev);
+	fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, dev);
+	if (mask != 0xFFFFFFFF && dev->flags&IFF_POINTOPOINT)
+		fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+	rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+
+	dev->flags &= ~IFF_POINTOPOINT;
+
+	rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+	net = dev->pa_addr&mask;
+	if (net)
+		fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, net, mask, dev);
+	if (net && mask != 0xFFFFFFFF) {
+		fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net, ~0, dev);
+		fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net|~mask, ~0, dev);
+	}
+	fib_unlock();
+}
+
+int ip_rt_event(int event, struct device *dev)
+{
+	fib_lock();
+	if (event == NETDEV_DOWN) {
+		fib_flush(dev);
+		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+		fib_unlock();
+		return NOTIFY_DONE;
+	}
+	if (event == NETDEV_CHANGE) {
+		printk(KERN_DEBUG "%s(%s) changes state fl=%08x pa=%08lX/%08lX brd=%08lX dst=%08lX\n",
+		       dev->name, current->comm, dev->flags, dev->pa_addr, dev->pa_mask,
+		       dev->pa_brdaddr, dev->pa_dstaddr);
+		if (!(dev->flags&IFF_BROADCAST))
+			fib_magic(RTMSG_DELROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
+		if (!(dev->flags&IFF_POINTOPOINT))
+			fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+		else {
+			u32 net = dev->pa_addr&dev->pa_mask;
+			fib_magic(RTMSG_DELROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
+			fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net, ~0, dev);
+			fib_magic(RTMSG_DELROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, dev);
+		}
+		rtmsg_dev(RTMSG_DELDEVICE, dev, NULL);
+	}
+
+	if ((event == NETDEV_UP || event == NETDEV_CHANGE) && !ZERONET(dev->pa_addr)) {
+		if (dev->flags&IFF_POINTOPOINT) {
+			dev->pa_mask = 0xFFFFFFFF;
+			dev->ip_flags &= ~IFF_IP_MASK_OK;
+			dev->flags &= ~IFF_BROADCAST;
+			dev->pa_brdaddr = 0;
+		}
+
+		if (event == NETDEV_UP)
+			printk(KERN_DEBUG "%s UP fl=%08x pa=%08lX/%08lX brd=%08lX dst=%08lX\n",
+			       dev->name, dev->flags, dev->pa_addr,
+			       dev->pa_mask, dev->pa_brdaddr, dev->pa_dstaddr);
+
+		rtmsg_dev(RTMSG_NEWDEVICE, dev, NULL);
+
+		if (dev->flags&IFF_POINTOPOINT) {
+			if (dev->pa_dstaddr && dev->type != ARPHRD_TUNNEL)
+				fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, dev->pa_dstaddr, ~0, dev);
+		} else if (dev->pa_addr&dev->pa_mask) {
+			u32 net = dev->pa_addr&dev->pa_mask;
+
+			fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX, net, dev->pa_mask, dev);
+			if (dev->pa_mask != 0xFFFFFFFF) {
+				fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net, ~0, dev);
+				fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, net|~dev->pa_mask, ~0, dev);
+			}
+		}
+		fib_magic(RTMSG_NEWROUTE, RTF_IFLOCAL, dev->pa_addr, ~0, dev);
+		if (dev == &loopback_dev) {
+			if (dev->pa_addr != htonl(INADDR_LOOPBACK)) {
+				u32 mask = htonl(0xFF000000);
+				fib_magic(RTMSG_NEWROUTE, RTF_IFPREFIX,
+					  htonl(INADDR_LOOPBACK)&mask,
+					  mask, dev);
+				fib_magic(RTMSG_NEWROUTE, RTF_IFLOCAL,
+					  htonl(INADDR_LOOPBACK),
+					  mask, dev);
+			}
+		}
+		if (dev->flags&IFF_BROADCAST)
+			fib_magic(RTMSG_NEWROUTE, RTF_IFBRD, dev->pa_brdaddr, ~0, dev);
+	}
+	fib_unlock();
+	return NOTIFY_DONE;
+}
+
+
+void ip_fib_init()
+{
+	struct in_rtrulemsg r;
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_ROUTE, 5, "route",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		fib_get_info
+	});
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RTCLASSES, 10, "rt_classes",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		fib_class_get_info
+	});
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RTRULES, 8, "rt_local",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		fib_local_get_info
+	});
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RTRULES, 8, "rt_rules",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		fib_rules_get_info
+	});
+#endif		/* CONFIG_PROC_FS */
+
+	fib_classes[RT_CLASS_LOCAL] = &local_class;
+	fib_classes[RT_CLASS_MAIN] = &main_class;
+	fib_classes[RT_CLASS_DEFAULT] = &default_class;
+
+	memset(&r, 0, sizeof(r));
+	r.rtrmsg_class = RT_CLASS_LOCAL;
+	r.rtrmsg_preference = 0;
+	fib_rule_add(&r, NULL, NULL);
+
+	memset(&r, 0, sizeof(r));
+	r.rtrmsg_class = RT_CLASS_DEFAULT;
+	r.rtrmsg_preference = 255;
+	fib_rule_add(&r, NULL, NULL);
+
+	memset(&r, 0, sizeof(r));
+	r.rtrmsg_class = RT_CLASS_MAIN;
+	r.rtrmsg_preference = 254;
+	fib_rule_add(&r, NULL, NULL);
+
+#ifdef  CONFIG_RTNETLINK
+	netlink_attach(NETLINK_ROUTE, fib_netlink_call);
+#endif
+}
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.1.14/linux/net/ipv4/icmp.c	Tue Nov 19 15:54:00 1996
+++ linux/net/ipv4/icmp.c	Thu Dec 12 16:54:23 1996
@@ -250,6 +250,7 @@
 #include <net/icmp.h>
 #include <net/tcp.h>
 #include <net/udp.h>
+#include <net/raw.h>
 #include <net/snmp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -275,18 +276,18 @@
   { EHOSTUNREACH,	0 },	/*	ICMP_HOST_UNREACH	*/
   { ENOPROTOOPT,	1 },	/*	ICMP_PROT_UNREACH	*/
   { ECONNREFUSED,	1 },	/*	ICMP_PORT_UNREACH	*/
-  { EOPNOTSUPP,		0 },	/*	ICMP_FRAG_NEEDED	*/
+  { EMSGSIZE,		0 },	/*	ICMP_FRAG_NEEDED	*/
   { EOPNOTSUPP,		0 },	/*	ICMP_SR_FAILED		*/
   { ENETUNREACH,	1 },	/* 	ICMP_NET_UNKNOWN	*/
   { EHOSTDOWN,		1 },	/*	ICMP_HOST_UNKNOWN	*/
   { ENONET,		1 },	/*	ICMP_HOST_ISOLATED	*/
   { ENETUNREACH,	1 },	/*	ICMP_NET_ANO		*/
   { EHOSTUNREACH,	1 },	/*	ICMP_HOST_ANO		*/
-  { EOPNOTSUPP,		0 },	/*	ICMP_NET_UNR_TOS	*/
-  { EOPNOTSUPP,		0 },	/*	ICMP_HOST_UNR_TOS	*/
-  { EOPNOTSUPP,		1 },	/*	ICMP_PKT_FILTERED	*/
-  { EOPNOTSUPP,		1 },	/*	ICMP_PREC_VIOLATION	*/
-  { EOPNOTSUPP,		1 }	/*	ICMP_PREC_CUTOFF	*/
+  { ENETUNREACH,	0 },	/*	ICMP_NET_UNR_TOS	*/
+  { EHOSTUNREACH,	0 },	/*	ICMP_HOST_UNR_TOS	*/
+  { EHOSTUNREACH,	1 },	/*	ICMP_PKT_FILTERED	*/
+  { EHOSTUNREACH,	1 },	/*	ICMP_PREC_VIOLATION	*/
+  { EHOSTUNREACH,	1 }	/*	ICMP_PREC_CUTOFF	*/
 };
 
 /*
@@ -326,12 +327,12 @@
 /*
  *	ICMP control array. This specifies what to do with each ICMP.
  */
- 
+
 struct icmp_control
 {
 	unsigned long *output;		/* Address to increment on output */
 	unsigned long *input;		/* Address to increment on input */
-	void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len);
+	void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, int len);
 	unsigned long error;		/* This ICMP is classed as an error message */
 	struct icmp_xrlim *xrlim;	/* Transmit rate limit control structure or NULL for no limits */
 };
@@ -348,7 +349,7 @@
 	int data_len;
 	struct icmphdr icmph;
 	unsigned long csum;
-	struct options replyopts;
+	struct ip_options replyopts;
 	unsigned char  optbuf[40];
 };
 
@@ -358,7 +359,8 @@
  *	all layers. All Socketless IP sends will soon be gone.
  */
 	
-struct socket icmp_socket;
+struct inode icmp_inode;
+struct socket *icmp_socket=&icmp_inode.u.socket_i;
 
 /*
  *	Send an ICMP frame.
@@ -464,7 +466,7 @@
  
 static void icmp_out_count(int type)
 {
-	if(type>18)
+	if (type>18)
 		return;
 	(*icmp_pointers[type].output)++;
 	icmp_statistics.IcmpOutMsgs++;
@@ -474,14 +476,13 @@
  *	Checksum each fragment, and on the first include the headers and final checksum.
  */
  
-static int icmp_glue_bits(const void *p, __u32 saddr, char *to, unsigned int offset, unsigned int fraglen)
+static int icmp_glue_bits(const void *p, char *to, unsigned int offset, unsigned int fraglen)
 {
 	struct icmp_bxm *icmp_param = (struct icmp_bxm *)p;
 	struct icmphdr *icmph;
 	unsigned long csum;
 
-	if (offset)
-	{
+	if (offset) {
 		icmp_param->csum=csum_partial_copy(icmp_param->data_ptr+offset-sizeof(struct icmphdr), 
 				to, fraglen,icmp_param->csum);
 		return 0;
@@ -500,24 +501,38 @@
 		fraglen-sizeof(struct icmphdr), csum);
 	icmph=(struct icmphdr *)to;
 	icmph->checksum = csum_fold(csum);
-
-	return 0; 
+	return 0;
 }
  
 /*
  *	Driving logic for building and sending ICMP messages.
  */
 
-static void icmp_build_xmit(struct icmp_bxm *icmp_param, __u32 saddr, __u32 daddr, __u8 tos)
+static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
 {
-	struct sock *sk=icmp_socket.data;
+	struct sock *sk=icmp_socket->sk;
+	struct ipcm_cookie ipc;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	u32 daddr;
+
+	if (ip_options_echo(&icmp_param->replyopts, skb))
+		return;
+
 	icmp_param->icmph.checksum=0;
 	icmp_param->csum=0;
 	icmp_out_count(icmp_param->icmph.type);
-	sk->ip_tos = tos;
+
+	sk->ip_tos = skb->nh.iph->tos;
+	daddr = ipc.addr = rt->rt_src;
+	ipc.opt = &icmp_param->replyopts;
+	if (ipc.opt->srr)
+		daddr = icmp_param->replyopts.faddr;
+	if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), NULL))
+		return;
 	ip_build_xmit(sk, icmp_glue_bits, icmp_param, 
 		icmp_param->data_len+sizeof(struct icmphdr),
-		daddr, saddr, &icmp_param->replyopts, 0, IPPROTO_ICMP, 1);
+		&ipc, rt, MSG_DONTWAIT);
+	ip_rt_put(rt);
 }
 
 
@@ -531,61 +546,63 @@
  *			MUST reply to only the first fragment.
  */
 
-void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info, struct device *dev)
+void icmp_send(struct sk_buff *skb_in, int type, int code, unsigned long info)
 {
 	struct iphdr *iph;
 	struct icmphdr *icmph;
-	int atype, room;
+	int room;
 	struct icmp_bxm icmp_param;
-	__u32 saddr;
+	struct rtable *rt = (struct rtable*)skb_in->dst;
+	struct ipcm_cookie ipc;
+	u32 saddr;
+	u8  tos;
 	
 	/*
 	 *	Find the original header
 	 */
 	 
-	iph = skb_in->ip_hdr;
+	iph = skb_in->nh.iph;
 	
 	/*
 	 *	No replies to physical multicast/broadcast
 	 */
 	 
-	if(skb_in->pkt_type!=PACKET_HOST)
+	if (skb_in->pkt_type!=PACKET_HOST)
 		return;
 		
 	/*
 	 *	Now check at the protocol level
 	 */
-	 
-	atype=ip_chk_addr(iph->daddr);
-	if(atype==IS_BROADCAST||atype==IS_MULTICAST)
+	if (!rt)
 		return;
+	if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST))
+		return;
+	 
 		
 	/*
 	 *	Only reply to fragment 0. We byte re-order the constant
 	 *	mask for efficiency.
 	 */
 	 
-	if(iph->frag_off&htons(IP_OFFSET))
+	if (iph->frag_off&htons(IP_OFFSET))
 		return;
 		
 	/* 
 	 *	If we send an ICMP error to an ICMP error a mess would result..
 	 */
 	 
-	if(icmp_pointers[type].error)
-	{
+	if (icmp_pointers[type].error) {
 		/*
 		 *	We are an error, check if we are replying to an ICMP error
 		 */
 		 
-		if(iph->protocol==IPPROTO_ICMP)
-		{
+		if (iph->protocol==IPPROTO_ICMP) {
 			icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
 			/*
 			 *	Assume any unknown ICMP type is an error. This isn't
 			 *	specified by the RFC, but think about it..
 			 */
-			if(icmph->type>18 || icmp_pointers[icmph->type].error)
+			if (icmph->type>18 || icmp_pointers[icmph->type].error)
 				return;
 		}
 	}
@@ -597,17 +614,27 @@
 #ifndef CONFIG_NO_ICMP_LIMIT
 	if (!xrlim_allow(type, iph->saddr))
 		return;
-#endif	
+#endif
 
 	/*
 	 *	Construct source address and options.
 	 */
-	 
-	saddr=iph->daddr;
-	if(saddr!=dev->pa_addr && ip_chk_addr(saddr)!=IS_MYADDR)
-		saddr=dev->pa_addr;
-	if(ip_options_echo(&icmp_param.replyopts, NULL, saddr, iph->saddr, skb_in))
+	
+	saddr = iph->daddr;
+	if (!(rt->rt_flags&RTF_LOCAL))
+		saddr = 0;
+
+	tos = icmp_pointers[type].error ?
+		((iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL) :
+			iph->tos;
+
+	if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), NULL))
 		return;
+	
+	if (ip_options_echo(&icmp_param.replyopts, skb_in)) {
+		ip_rt_put(rt);
+		return;
+	}
 
 	/*
 	 *	Prepare data for ICMP header.
@@ -616,136 +643,87 @@
 	icmp_param.icmph.type=type;
 	icmp_param.icmph.code=code;
 	icmp_param.icmph.un.gateway = info;
+	icmp_param.icmph.checksum=0;
+	icmp_param.csum=0;
 	icmp_param.data_ptr=iph;
-	room = 576 - sizeof(struct iphdr) - icmp_param.replyopts.optlen;
-	icmp_param.data_len=(iph->ihl<<2)+skb_in->len;	/* RFC says return as much as we can without exceeding 576 bytes */
+	icmp_out_count(icmp_param.icmph.type);
+	icmp_socket->sk->ip_tos = tos;
+	ipc.addr = iph->saddr;
+	ipc.opt = &icmp_param.replyopts;
+	if (icmp_param.replyopts.srr) {
+		ip_rt_put(rt);
+		if (ip_route_output(&rt, icmp_param.replyopts.faddr, saddr, RT_TOS(tos), NULL))
+			return;
+	}
+
+	/* RFC says return as much as we can without exceeding 576 bytes. */
+
+	room = rt->u.dst.pmtu;
+	if (room > 576)
+		room = 576;
+	room -= sizeof(struct iphdr) - icmp_param.replyopts.optlen;
+	
+	icmp_param.data_len=(iph->ihl<<2)+skb_in->len;
 	if (icmp_param.data_len > room)
 		icmp_param.data_len = room;
 	
-	/*
-	 *	Build and send the packet.
-	 */
+	ip_build_xmit(icmp_socket->sk, icmp_glue_bits, &icmp_param, 
+		icmp_param.data_len+sizeof(struct icmphdr),
+		&ipc, rt, MSG_DONTWAIT);
 
-	icmp_build_xmit(&icmp_param, saddr, iph->saddr, 
-			icmp_pointers[type].error ? 
-			(iph->tos & 0x1E) | 0xC0 : iph->tos);
+	ip_rt_put(rt);
 }
 
 
 /* 
  *	Handle ICMP_DEST_UNREACH, ICMP_TIME_EXCEED, and ICMP_QUENCH. 
  */
- 
-static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+
+static void icmp_unreach(struct icmphdr *icmph, struct sk_buff *skb, int len)
 {
 	struct iphdr *iph;
 	int hash;
 	struct inet_protocol *ipprot;
-	unsigned char *dp;	
-	__u32 info = 0;
+	unsigned char *dp;
+	struct sock *raw_sk;
 	
-	if(len<sizeof(struct iphdr))
-		goto flush_it;
-
 	iph = (struct iphdr *) (icmph + 1);
+	dp = (unsigned char*)iph;
 	
-	len-=iph->ihl<<2;
-	if(len<0)
-		goto flush_it;
-	
-	dp= ((unsigned char *)iph)+(iph->ihl<<2);
-	
-	if(icmph->type==ICMP_DEST_UNREACH)
-	{
-		switch(icmph->code & 15)
-		{
+	if(icmph->type==ICMP_DEST_UNREACH) {
+		switch(icmph->code & 15) {
 			case ICMP_NET_UNREACH:
 				break;
 			case ICMP_HOST_UNREACH:
 				break;
 			case ICMP_PROT_UNREACH:
-/*				printk(KERN_INFO "ICMP: %s:%d: protocol unreachable.\n",
-					in_ntoa(iph->daddr), (int)iph->protocol);*/
 				break;
 			case ICMP_PORT_UNREACH:
 				break;
 			case ICMP_FRAG_NEEDED:
-#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
-				printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
-								in_ntoa(iph->daddr));
-				break;
-#else
-			{
-				unsigned short old_mtu = ntohs(iph->tot_len);
-				unsigned short new_mtu = ntohs(icmph->un.echo.sequence);
-
-				/*
-				 * RFC1191 5.  4.2BSD based router can return incorrect
-				 * Total Length.  If current mtu is unknown or old_mtu
-				 * is not less than current mtu, reduce old_mtu by 4 times
-				 * the header length.
-				 */
-
-				if (skb->sk == NULL /* can this happen? */
-					|| skb->sk->ip_route_cache == NULL
-					|| skb->sk->ip_route_cache->rt_mtu <= old_mtu)
-				{
-					NETDEBUG(printk(KERN_INFO "4.2BSD based fragmenting router between here and %s, mtu corrected from %d", in_ntoa(iph->daddr), old_mtu));
-					old_mtu -= 4 * iph->ihl;
-					NETDEBUG(printk(" to %d\n", old_mtu));
+				if (ipv4_config.no_pmtu_disc)
+					printk(KERN_INFO "ICMP: %s: fragmentation needed and DF set.\n",
+					       in_ntoa(iph->daddr));
+				else {
+					unsigned short new_mtu;
+					new_mtu = ip_rt_frag_needed(iph, ntohs(icmph->un.frag.mtu));
+					if (!new_mtu) {
+						kfree_skb(skb, FREE_READ);
+						return;
+					}
+					icmph->un.frag.mtu = htons(new_mtu);
 				}
-
-				if (new_mtu < 68 || new_mtu >= old_mtu)
-				{
-					/*
-					 * 	It is either dumb router, which does not
-					 *	understand Path MTU Disc. protocol
-					 *	or broken (f.e. Linux<=1.3.37 8) router.
-					 *	Try to guess...
-					 *	The table is taken from RFC-1191.
-					 */
-					if (old_mtu > 32000)
-						new_mtu = 32000;
-					else if (old_mtu > 17914)
-						new_mtu = 17914;
-					else if (old_mtu > 8166)
-						new_mtu = 8166;
-					else if (old_mtu > 4352)
-						new_mtu = 4352;
-					else if (old_mtu > 2002)
-						new_mtu = 2002;
-					else if (old_mtu > 1492)
-						new_mtu = 1492;
-					else if (old_mtu > 576)
-						new_mtu = 576;
-					else if (old_mtu > 296)
-						new_mtu = 296;
-					/*
-					 *	These two are not from the RFC but
-					 *	are needed for AMPRnet AX.25 paths.
-					 */
-					else if (old_mtu > 216)
-						new_mtu = 216;
-					else if (old_mtu > 128)
-						new_mtu = 128;
-					else
-					/*
-					 *	Despair..
-					 */
-						new_mtu = 68;
-				}
-				info = new_mtu;
 				break;
-			}
-#endif
 			case ICMP_SR_FAILED:
 				printk(KERN_INFO "ICMP: %s: Source Route Failed.\n", in_ntoa(iph->daddr));
 				break;
 			default:
 				break;
 		}
-		if(icmph->code>NR_ICMP_UNREACH)	/* Invalid type */
+		if (icmph->code>NR_ICMP_UNREACH) {
+			kfree_skb(skb, FREE_READ);
 			return;
+		}
 	}
 	
 	/*
@@ -756,8 +734,20 @@
 	 *	RFC 1122: 3.2.2.2 MUST pass ICMP time expired messages to transport layer.
 	 */
 
+	/* Deliver ICMP message to raw sockets. Pretty useless feature?
+	 */
+
+	hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
+	if ((raw_sk=raw_prot.sock_array[hash]) != NULL) {
+		raw_sk = get_sock_raw(raw_sk, iph->protocol, iph->saddr, iph->daddr);
+		while (raw_sk) {
+			raw_err(raw_sk, skb);
+			raw_sk=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
+		}
+	}
+
 	/*
-	 *	Get the protocol(s). 
+	 *	Get the protocol(s).
 	 */
 	 
 	hash = iph->protocol & (MAX_INET_PROTOS -1);
@@ -767,10 +757,9 @@
 	 *
 	 *	FIXME: Deliver to appropriate raw sockets too.
 	 */
-	 
+
 	ipprot = (struct inet_protocol *) inet_protos[hash];
-	while(ipprot != NULL) 
-	{
+	while(ipprot != NULL) {
 		struct inet_protocol *nextip;
 
 		nextip = (struct inet_protocol *) ipprot->next;
@@ -782,15 +771,12 @@
 		/* RFC1122: OK. Passes appropriate ICMP errors to the */
 		/* appropriate protocol layer (MUST), as per 3.2.2. */
 
-		if (iph->protocol == ipprot->protocol && ipprot->err_handler) 
-		{
-			ipprot->err_handler(icmph->type, icmph->code, dp, info,
-					    iph->daddr, iph->saddr, ipprot, len);
-		}
+		if (iph->protocol == ipprot->protocol && ipprot->err_handler)
+			ipprot->err_handler(skb, dp);
 
 		ipprot = nextip;
   	}
-flush_it:
+
 	kfree_skb(skb, FREE_READ);
 }
 
@@ -799,7 +785,7 @@
  *	Handle ICMP_REDIRECT. 
  */
 
-static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 source, __u32 daddr, int len)
+static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, int len)
 {
 	struct iphdr *iph;
 	unsigned long ip;
@@ -807,65 +793,30 @@
 	/*
 	 *	Get the copied header of the packet that caused the redirect
 	 */
-	
-	if(len<=sizeof(struct iphdr))
-		goto flush_it;
 	 
 	iph = (struct iphdr *) (icmph + 1);
 	ip = iph->daddr;
 
-	/*
-	 *	If we are a router and we run a routing protocol, we MUST NOT follow redirects.
-	 *	When using no routing protocol, we MAY follow redirects. (RFC 1812, 5.2.7.2)
-	 */
 
-#if defined(CONFIG_IP_FORWARD) && !defined(CONFIG_IP_DUMB_ROUTER)
-	NETDEBUG(printk(KERN_INFO "icmp: ICMP redirect ignored. dest = %lX, "
-	       "orig gw = %lX, \"new\" gw = %lX, device = %s.\n", ntohl(ip),
-		ntohl(source), ntohl(icmph->un.gateway), dev->name));
-#else	
-	switch(icmph->code & 7) 
-	{
+	switch(icmph->code & 7) {
 		case ICMP_REDIR_NET:
-			/*
-			 *	This causes a problem with subnetted networks. What we should do
-			 *	is use ICMP_ADDRESS to get the subnet mask of the problem route
-			 *	and set both. But we don't.. [RFC1812 says routers MUST NOT
-			 *	generate Network Redirects]
-			 */
-#ifdef not_a_good_idea
-			ip_rt_add((RTF_DYNAMIC | RTF_MODIFIED | RTF_GATEWAY),
-				ip, 0, icmph->un.gateway, dev,0, 0, 0);
-#endif
+		case ICMP_REDIR_NETTOS:
 			/*
 			 *	As per RFC recommendations now handle it as
 			 *	a host redirect.
 			 */
 			 
 		case ICMP_REDIR_HOST:
-			/*
-			 *	Add better route to host.
-			 *	But first check that the redirect
-			 *	comes from the old gateway..
-			 *	And make sure it's an ok host address
-			 *	(not some confused thing sending our
-			 *	address)
-			 */
-			printk(KERN_INFO "ICMP redirect from %s\n", in_ntoa(source));
-			ip_rt_redirect(source, ip, icmph->un.gateway, dev);
-			break;
-		case ICMP_REDIR_NETTOS:
 		case ICMP_REDIR_HOSTTOS:
-			printk(KERN_INFO "ICMP: cannot handle TOS redirects yet!\n");
+			ip_rt_redirect(skb->nh.iph->saddr, ip, icmph->un.gateway, iph->saddr, iph->tos, skb->dev);
 			break;
 		default:
 			break;
   	}
-#endif  	
   	/*
   	 *	Discard the original packet
   	 */
-flush_it:
+  	 
   	kfree_skb(skb, FREE_READ);
 }
 
@@ -878,16 +829,16 @@
  *	See also WRT handling of options once they are done and working.
  */
  
-static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+static void icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, int len)
 {
 #ifndef CONFIG_IP_IGNORE_ECHO_REQUESTS
 	struct icmp_bxm icmp_param;
+
 	icmp_param.icmph=*icmph;
 	icmp_param.icmph.type=ICMP_ECHOREPLY;
 	icmp_param.data_ptr=(icmph+1);
 	icmp_param.data_len=len;
-	if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
-		icmp_build_xmit(&icmp_param, daddr, saddr, skb->ip_hdr->tos);
+	icmp_reply(&icmp_param, skb);
 #endif
 	kfree_skb(skb, FREE_READ);
 }
@@ -900,8 +851,9 @@
  *		  MUST be updated at least at 15Hz.
  */
  
-static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+static void icmp_timestamp(struct icmphdr *icmph, struct sk_buff *skb, int len)
 {
+	struct timeval tv;
 	__u32 times[3];		/* So the new timestamp works on ALPHA's.. */
 	struct icmp_bxm icmp_param;
 	
@@ -909,8 +861,7 @@
 	 *	Too short.
 	 */
 	 
-	if(len<12)
-	{
+	if(len<12) {
 		icmp_statistics.IcmpInErrors++;
 		kfree_skb(skb, FREE_READ);
 		return;
@@ -920,11 +871,8 @@
 	 *	Fill in the current time as ms since midnight UT: 
 	 */
 	 
-	{
-		struct timeval tv;
-		do_gettimeofday(&tv);
-		times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
-	}
+	do_gettimeofday(&tv);
+	times[1] = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);
 	times[2] = times[1];
 	memcpy((void *)&times[0], icmph+1, 4);		/* Incoming stamp */
 	icmp_param.icmph=*icmph;
@@ -932,8 +880,7 @@
 	icmp_param.icmph.code=0;
 	icmp_param.data_ptr=&times;
 	icmp_param.data_len=12;
-	if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
-		icmp_build_xmit(&icmp_param, daddr, saddr, skb->ip_hdr->tos);
+	icmp_reply(&icmp_param, skb);
 	kfree_skb(skb,FREE_READ);
 }
 
@@ -946,27 +893,79 @@
  * agent.  Receiving a request doesn't constitute implicit permission to 
  * act as one. Of course, implementing this correctly requires (SHOULD) 
  * a way to turn the functionality on and off.  Another one for sysctl(), 
- * I guess. -- MS 
- * Botched with a CONFIG option for now - Linus add scts sysctl please.. 
+ * I guess. -- MS
+ *
+ * RFC1812 (4.3.3.9).	A router MUST implement it.
+ *			A router SHOULD have switch turning it on/off.
+ *		      	This switch MUST be ON by default.
+ *
+ * Gratuitous replies, zero-source replies are not implemented,
+ * that complies with RFC. DO NOT implement them!!! All the idea
+ * of broadcast addrmask replies as specified in RFC950 is broken.
+ * The problem is that it is not uncommon to have several prefixes
+ * on one physical interface. Moreover, addrmask agent can even be
+ * not aware of existing another prefixes.
+ * If source is zero, addrmask agent cannot choose correct prefix.
+ * Gratuitous mask announcements suffer from the same problem.
+ * RFC1812 explains it, but still allows to use ADDRMASK,
+ * that is pretty silly. --ANK
  */
  
-static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+static void icmp_address(struct icmphdr *icmph, struct sk_buff *skb, int len)
 {
-#ifdef CONFIG_IP_ADDR_AGENT	/* Don't use, broken */
 	struct icmp_bxm icmp_param;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct device *dev = skb->dev;
+
+	if (!ipv4_config.addrmask_agent ||
+	    ZERONET(rt->rt_src) ||
+	    rt->rt_src_dev != rt->u.dst.dev ||
+	    !(rt->rt_flags&RTCF_DIRECTSRC) ||
+	    (rt->rt_flags&RTF_GATEWAY) ||
+	    !(dev->ip_flags&IFF_IP_ADDR_OK) ||
+	    !(dev->ip_flags&IFF_IP_MASK_OK)) {
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+
 	icmp_param.icmph.type=ICMP_ADDRESSREPLY;
 	icmp_param.icmph.code=0;
-	icmp_param.icmph.un.echo.id = icmph->un.echo.id;
-	icmp_param.icmph.un.echo.sequence = icmph->un.echo.sequence;
+	icmp_param.icmph.un.echo = icmph->un.echo;
 	icmp_param.data_ptr=&dev->pa_mask;
 	icmp_param.data_len=4;
-	if (ip_options_echo(&icmp_param.replyopts, NULL, daddr, saddr, skb)==0)
-		icmp_build_xmit(&icmp_param, daddr, saddr, skb->iph->tos);
-#endif	
-	kfree_skb(skb, FREE_READ);	
+	icmp_reply(&icmp_param, skb);
+	kfree_skb(skb, FREE_READ);
 }
 
-static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len)
+/*
+ * RFC1812 (4.3.3.9).	A router SHOULD listen all replies, and complain
+ *			loudly if an inconsistency is found.
+ */
+
+static void icmp_address_reply(struct icmphdr *icmph, struct sk_buff *skb, int len)
+{
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct device *dev = skb->dev;
+	u32 mask;
+
+	if (!ipv4_config.log_martians ||
+	    len < 4 ||
+	    !(rt->rt_flags&RTCF_DIRECTSRC) ||
+	    (rt->rt_flags&RTF_GATEWAY) ||
+	    !(dev->ip_flags&IFF_IP_ADDR_OK) ||
+	    !(dev->ip_flags&IFF_IP_MASK_OK)) {
+		kfree_skb(skb, FREE_READ);
+		return;
+	}
+
+	mask = *(u32*)&icmph[1];
+	if (mask != dev->pa_mask)
+		printk(KERN_INFO "Wrong address mask %08lX from %08lX/%s\n",
+		       ntohl(mask), ntohl(rt->rt_src), dev->name);
+	kfree_skb(skb, FREE_READ);
+}
+
+static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, int len)
 {
 	kfree_skb(skb, FREE_READ);
 }
@@ -984,7 +983,7 @@
 
 int icmp_chkaddr(struct sk_buff *skb)
 {
-	struct icmphdr *icmph=(struct icmphdr *)(skb->h.raw + skb->h.iph->ihl*4);
+	struct icmphdr *icmph=(struct icmphdr *)(skb->nh.raw + skb->nh.iph->ihl*4);
 	struct iphdr *iph = (struct iphdr *) (icmph + 1);
 	void (*handler)(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr, int len) = icmp_pointers[icmph->type].handler;
 
@@ -997,7 +996,7 @@
 			struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
 			sk = get_sock(&tcp_prot, th->source, iph->daddr,
-						th->dest, iph->saddr, 0, 0);
+						th->dest, iph->saddr);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr) return 0;
 			if (sk->daddr != iph->daddr) return 0;
@@ -1012,9 +1011,9 @@
 			struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
 			sk = get_sock(&udp_prot, uh->source, iph->daddr,
-						uh->dest, iph->saddr, 0, 0);
+						uh->dest, iph->saddr);
 			if (!sk) return 0;
-			if (sk->saddr != iph->saddr && ip_chk_addr(iph->saddr) != IS_MYADDR)
+			if (sk->saddr != iph->saddr && __ip_chk_addr(iph->saddr) != IS_MYADDR)
 				return 0;
 			/*
 			 * This packet may have come from us.
@@ -1028,29 +1027,33 @@
 }
 
 #endif
+
 /* 
- *	Deal with incoming ICMP packets. 
+ *	Deal with incoming ICMP packets.
  */
  
-int icmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
-	 __u32 daddr, unsigned short len,
-	 __u32 saddr, int redo, struct inet_protocol *protocol)
+int icmp_rcv(struct sk_buff *skb, unsigned short len)
 {
-	struct icmphdr *icmph=(void *)skb->h.raw;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-	int r;
-#endif
+	struct icmphdr *icmph = skb->h.icmph;
+	struct rtable *rt = (struct rtable*)skb->dst;
+
 	icmp_statistics.IcmpInMsgs++;
 	
+	if(len < sizeof(struct icmphdr))
+	{
+		icmp_statistics.IcmpInErrors++;
+		printk(KERN_INFO "ICMP: runt packet\n");
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+ 	
   	/*
 	 *	Validate the packet
   	 */
 	
-	if (ip_compute_csum((unsigned char *) icmph, len)) 
-	{
-		/* Failed checksum! */
+	if (ip_compute_csum((unsigned char *) icmph, len)) {
 		icmp_statistics.IcmpInErrors++;
-		printk(KERN_INFO "ICMP: failed checksum from %s!\n", in_ntoa(saddr));
+		printk(KERN_INFO "ICMP: failed checksum from %s!\n", in_ntoa(skb->nh.iph->saddr));
 		kfree_skb(skb, FREE_READ);
 		return(0);
 	}
@@ -1061,8 +1064,7 @@
 	 *	RFC 1122: 3.2.2  Unknown ICMP messages types MUST be silently discarded.
 	 */
 	 
-	if(icmph->type > 18)
-	{
+	if (icmph->type > 18) {
 		icmp_statistics.IcmpInErrors++;		/* Is this right - or do we ignore ? */
 		kfree_skb(skb,FREE_READ);
 		return(0);
@@ -1072,39 +1074,25 @@
 	 *	Parse the ICMP message 
 	 */
 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-	/*
-	 *	We may get non-local addresses and still want to handle them
-	 *	locally, due to transparent proxying.
-	 *	Thus, narrow down the test to what is really meant.
-	 */
-	if (daddr!=dev->pa_addr && ((r = ip_chk_addr(daddr)) == IS_BROADCAST || r == IS_MULTICAST))
-#else
-	if (daddr!=dev->pa_addr && ip_chk_addr(daddr) != IS_MYADDR)
-#endif
-	{
+	if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST)) {
 		/*
 		 *	RFC 1122: 3.2.2.6 An ICMP_ECHO to broadcast MAY be silently ignored (we don't as it is used
 		 *	by some network mapping tools).
 		 *	RFC 1122: 3.2.2.8 An ICMP_TIMESTAMP MAY be silently discarded if to broadcast/multicast.
 		 */
-		if (icmph->type != ICMP_ECHO) 
-		{
+		if (icmph->type != ICMP_ECHO &&
+		    icmph->type != ICMP_TIMESTAMP &&
+		    icmph->type != ICMP_ADDRESS &&
+		    icmph->type != ICMP_ADDRESSREPLY) {
 			icmp_statistics.IcmpInErrors++;
 			kfree_skb(skb, FREE_READ);
 			return(0);
   		}
-  		/*
-  		 *	Reply the multicast/broadcast using a legal
-  		 *	interface - in this case the device we got
-  		 *	it from.
-  		 */
-		daddr=dev->pa_addr;
 	}
-	
-	len-=sizeof(struct icmphdr);
+
+	len -= sizeof(struct icmphdr);
 	(*icmp_pointers[icmph->type].input)++;
-	(icmp_pointers[icmph->type].handler)(icmph,skb,skb->dev,saddr,daddr,len);
+	(icmp_pointers[icmph->type].handler)(icmph, skb, len);
 	return 0;
 }
 
@@ -1114,7 +1102,6 @@
 
 static struct icmp_xrlim
 	xrl_unreach = { 4*HZ, 80, HZ/4 },		/* Host Unreachable */
-	xrl_redirect = { 2*HZ, 10, HZ/2 },		/* Redirect */
 	xrl_generic = { 3*HZ, 30, HZ/4 };		/* All other errors */
 
 /*
@@ -1131,7 +1118,7 @@
 /* SOURCE QUENCH (4) */
  { &icmp_statistics.IcmpOutSrcQuenchs, &icmp_statistics.IcmpInSrcQuenchs, icmp_unreach, 1, NULL },
 /* REDIRECT (5) */
- { &icmp_statistics.IcmpOutRedirects, &icmp_statistics.IcmpInRedirects, icmp_redirect, 1, &xrl_redirect },
+ { &icmp_statistics.IcmpOutRedirects, &icmp_statistics.IcmpInRedirects, icmp_redirect, 1, NULL },
  { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
  { &dummy, &icmp_statistics.IcmpInErrors, icmp_discard, 1, NULL },
 /* ECHO (8) */
@@ -1154,20 +1141,27 @@
 /* ADDR MASK (17) */
  { &icmp_statistics.IcmpOutAddrMasks, &icmp_statistics.IcmpInAddrMasks, icmp_address, 0, NULL },
 /* ADDR MASK REPLY (18) */
- { &icmp_statistics.IcmpOutAddrMaskReps, &icmp_statistics.IcmpInAddrMaskReps, icmp_discard, 0, NULL }
+ { &icmp_statistics.IcmpOutAddrMaskReps, &icmp_statistics.IcmpInAddrMaskReps, icmp_address_reply, 0, NULL }
 };
 
-void icmp_init(struct proto_ops *ops)
+void icmp_init(struct net_proto_family *ops)
 {
-	struct sock *sk;
 	int err;
-	icmp_socket.type=SOCK_RAW;
-	icmp_socket.ops=ops;
-	if((err=ops->create(&icmp_socket, IPPROTO_ICMP))<0)
+
+	icmp_inode.i_mode = S_IFSOCK;
+	icmp_inode.i_sock = 1;
+	icmp_inode.i_uid = 0;
+	icmp_inode.i_gid = 0;
+
+	icmp_socket->inode = &icmp_inode;
+	icmp_socket->state = SS_UNCONNECTED;
+	icmp_socket->type=SOCK_RAW;
+
+	if ((err=ops->create(icmp_socket, IPPROTO_ICMP))<0)
 		panic("Failed to create the ICMP control socket.\n");
-	sk=icmp_socket.data;
-	sk->allocation=GFP_ATOMIC;
-	sk->num = 256;			/* Don't receive any data */
+	icmp_socket->sk->allocation=GFP_ATOMIC;
+	icmp_socket->sk->num = 256;		/* Don't receive any data */
+	icmp_socket->sk->ip_ttl = MAXTTL;
 #ifndef CONFIG_NO_ICMP_LIMIT
 	xrlim_init();
 #endif
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.1.14/linux/net/ipv4/igmp.c	Tue Nov 19 15:54:00 1996
+++ linux/net/ipv4/igmp.c	Thu Dec 12 16:54:23 1996
@@ -73,7 +73,6 @@
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/config.h>
 #include <linux/socket.h>
 #include <linux/sockios.h>
 #include <linux/in.h>
@@ -88,8 +87,6 @@
 #include <linux/igmp.h>
 #include <net/checksum.h>
 
-#ifdef CONFIG_IP_MULTICAST
-
 
 /*
  *	If time expired, change the router type to IGMP_NEW_ROUTER.
@@ -206,13 +203,13 @@
 
 static void igmp_stop_timer(struct ip_mc_list *im)
 {
-  if (im->tm_running) {
-    del_timer(&im->timer);
-    im->tm_running=0;
-  }
-  else {
-    printk(KERN_ERR "igmp_stop_timer() called with timer not running by %p\n",__builtin_return_address(0));
+  if (im->tm_running)
+  {
+	  del_timer(&im->timer);
+	  im->tm_running=0;
   }
+  else
+	  printk(KERN_ERR "igmp_stop_timer() called with timer not running by %p\n",__builtin_return_address(0));
 }
 
 extern __inline__ unsigned int random(void)
@@ -241,30 +238,55 @@
  *	Send an IGMP report.
  */
 
-#define MAX_IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+64)
+#define IGMP_SIZE (sizeof(struct igmphdr)+sizeof(struct iphdr)+4)
 
-static void igmp_send_report(struct device *dev, unsigned long address, int type)
+static void igmp_send_report(struct device *dev, u32 group, int type)
 {
-	struct sk_buff *skb=alloc_skb(MAX_IGMP_SIZE, GFP_ATOMIC);
-	int tmp;
+	struct sk_buff *skb;
+	struct iphdr *iph;
 	struct igmphdr *ih;
+	struct rtable *rt;
 
-	if(skb==NULL)
+	if (ip_route_output(&rt, group, 0, 0, dev))
 		return;
-	tmp=ip_build_header(skb, INADDR_ANY, address, &dev, IPPROTO_IGMP, NULL,
-				28 , 0, 1, NULL);
-	if(tmp<0)
-	{
-		kfree_skb(skb, FREE_WRITE);
+
+	skb=alloc_skb(IGMP_SIZE+dev->hard_header_len+15, GFP_ATOMIC);
+	if (skb == NULL) {
+		ip_rt_put(rt);
 		return;
 	}
-	ih=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
+
+	skb->dst = &rt->u.dst;
+
+	skb_reserve(skb, (dev->hard_header_len+15)&~15);
+	ip_ll_header(skb);
+
+	skb->nh.iph = iph = (struct iphdr *)skb_put(skb, sizeof(struct iphdr)+4);
+
+	iph->version  = 4;
+	iph->ihl      = (sizeof(struct iphdr)+4)>>2;
+	iph->tos      = 0;
+	iph->frag_off = 0;
+	iph->ttl      = 1;
+	iph->daddr    = group;
+	iph->saddr    = rt->rt_src;
+	iph->protocol = IPPROTO_IGMP;
+	iph->tot_len  = htons(IGMP_SIZE);
+	iph->id	      = htons(ip_id_count++);
+	((u8*)&iph[1])[0] = IPOPT_RA;
+	((u8*)&iph[1])[1] = 4;
+	((u8*)&iph[1])[2] = 0;
+	((u8*)&iph[1])[3] = 0;
+	ip_send_check(iph);
+
+	ih = (struct igmphdr *)skb_put(skb, sizeof(struct igmphdr));
 	ih->type=type;
 	ih->code=0;
 	ih->csum=0;
-	ih->group=address;
-	ih->csum=ip_compute_csum((void *)ih,sizeof(struct igmphdr));	/* Checksum fill */
-	ip_queue_xmit(NULL,dev,skb,1);
+	ih->group=group;
+	ih->csum=ip_compute_csum((void *)ih, sizeof(struct igmphdr));
+
+	skb->dst->output(skb);
 }
 
 
@@ -281,7 +303,7 @@
 		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_NEW_MEMBERSHIP_REPORT);
 	else
 		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
-	im->reporter=1;
+	im->reporter = 1;
 }
 
 static void igmp_init_timer(struct ip_mc_list *im)
@@ -293,28 +315,27 @@
 }
 
 
-static void igmp_heard_report(struct device *dev, __u32 address, __u32 src)
+static void igmp_heard_report(struct device *dev, u32 group, u32 source)
 {
 	struct ip_mc_list *im;
 
-	if ((address & IGMP_LOCAL_GROUP_MASK) != IGMP_LOCAL_GROUP) 
-	{
-		/* Timers are only set for non-local groups */
-		for(im=dev->ip_mc_list;im!=NULL;im=im->next) 
-		{
-			if(im->multiaddr==address)
-			{
-				if(im->tm_running) 
-					igmp_stop_timer(im);
-				if(src!=dev->pa_addr)
-					im->reporter=0;
-				return;
-			}
+	/* Timers are only set for non-local groups */
+	if (LOCAL_MCAST(group))
+		return;
+
+	for (im=dev->ip_mc_list; im!=NULL; im=im->next) {
+		if (im->multiaddr == group) {
+			if (im->tm_running)
+				igmp_stop_timer(im);
+			if (source != dev->pa_addr)
+				im->reporter = 0;
+			return;
 		}
 	}
 }
 
-static void igmp_heard_query(struct device *dev,unsigned char max_resp_time)
+static void igmp_heard_query(struct device *dev, unsigned char max_resp_time,
+			     u32 group)
 {
 	struct ip_mc_list *im;
 	int	mrouter_type;
@@ -322,11 +343,10 @@
 	/*
 	 *	The max_resp_time is in units of 1/10 second.
 	 */
-	if(max_resp_time>0)
-	{
+	if(max_resp_time>0) {
 		mrouter_type=IGMP_NEW_ROUTER;
 
-		if(igmp_set_mrouter_info(dev,mrouter_type,0)==NULL)
+		if (igmp_set_mrouter_info(dev,mrouter_type,0)==NULL)
 			return;
 		/*
 		 * - Start the timers in all of our membership records
@@ -338,25 +358,18 @@
 		 * - Use the igmp->igmp_code field as the maximum
 		 *   delay possible
 		 */
-		for(im=dev->ip_mc_list;im!=NULL;im=im->next)
-		{
-			if(im->tm_running)
-			{
-				if(im->timer.expires>jiffies+max_resp_time*HZ/IGMP_TIMER_SCALE)
-				{
+		for(im=dev->ip_mc_list;im!=NULL;im=im->next) {
+			if (group && group != im->multiaddr)
+				continue;
+			if(im->tm_running) {
+				if(im->timer.expires>jiffies+max_resp_time*HZ/IGMP_TIMER_SCALE) {
 					igmp_stop_timer(im);
 					igmp_start_timer(im,max_resp_time);
 				}
-			}
-			else
-			{
-				if((im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
-					igmp_start_timer(im,max_resp_time);
-			}
+			} else if (!LOCAL_MCAST(im->multiaddr))
+				igmp_start_timer(im,max_resp_time);
 		}
-	}
-	else
-	{
+	} else {
 		mrouter_type=IGMP_OLD_ROUTER;
 		max_resp_time=IGMP_MAX_HOST_REPORT_DELAY*IGMP_TIMER_SCALE;
 
@@ -370,9 +383,8 @@
 		 * "local" group (224.0.0.X).
 		 */
 
-		for(im=dev->ip_mc_list;im!=NULL;im=im->next)
-		{
-			if(!im->tm_running && (im->multiaddr & IGMP_LOCAL_GROUP_MASK)!=IGMP_LOCAL_GROUP)
+		for(im=dev->ip_mc_list;im!=NULL;im=im->next) {
+			if(!im->tm_running && !LOCAL_MCAST(im->multiaddr))
 				igmp_start_timer(im,max_resp_time);
 		}
 	}
@@ -402,6 +414,9 @@
 void ip_mc_filter_add(struct device *dev, unsigned long addr)
 {
 	char buf[6];
+	ip_rt_multicast_event(dev);
+	if(!(dev->flags & IFF_MULTICAST))
+		return;
 	if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_FDDI)
 		return; /* Only do ethernet or FDDI for now */
 	ip_mc_map(addr,buf);
@@ -415,6 +430,7 @@
 void ip_mc_filter_del(struct device *dev, unsigned long addr)
 {
 	char buf[6];
+	ip_rt_multicast_event(dev);
 	if(dev->type!=ARPHRD_ETHER && dev->type!=ARPHRD_FDDI)
 		return; /* Only do ethernet or FDDI for now */
 	ip_mc_map(addr,buf);
@@ -424,7 +440,8 @@
 extern __inline__ void igmp_group_dropped(struct ip_mc_list *im)
 {
 	del_timer(&im->timer);
-	igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
+	if (im->reporter)
+		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_LEAVE_MESSAGE);
 	ip_mc_filter_del(im->interface, im->multiaddr);
 }
 
@@ -442,51 +459,34 @@
 		igmp_send_report(im->interface, im->multiaddr, IGMP_HOST_MEMBERSHIP_REPORT);
 }
 
-int igmp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
-	__u32 daddr, unsigned short len, __u32 saddr, int redo,
-	struct inet_protocol *protocol)
+int igmp_rcv(struct sk_buff *skb, unsigned short len)
 {
 	/* This basically follows the spec line by line -- see RFC1112 */
-	struct igmphdr *ih;
-
-	/*
-	 *	Mrouted needs to able to query local interfaces. So
-	 *	report for the device this was sent at. (Which can
-	 *	be the loopback this time)
-	 */
-
-	if(dev->flags&IFF_LOOPBACK)
-	{
-		dev=ip_dev_find(saddr);
-		if(dev==NULL)
-			dev=&loopback_dev;
-	}
-	ih=(struct igmphdr *)skb->h.raw;
+	struct igmphdr *ih = skb->h.igmph;
 
-	if(skb->len <sizeof(struct igmphdr) || skb->ip_hdr->ttl<1 || ip_compute_csum((void *)skb->h.raw,sizeof(struct igmphdr)))
-	{
+	if (len < sizeof(struct igmphdr) || ip_compute_csum((void *)ih, len)) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
 	
-	/*
-	 *	I have a report that someone does this!
-	 */
-	 
-	if(saddr==0)
-	{
-		printk(KERN_INFO "Broken multicast host using 0.0.0.0 heard on %s\n",
-			dev->name);
-		kfree_skb(skb, FREE_READ);
-		return 0;
+	switch (ih->type) {
+	case IGMP_HOST_MEMBERSHIP_QUERY:
+		igmp_heard_query(skb->dev, ih->code, ih->group);
+		break;
+	case IGMP_HOST_MEMBERSHIP_REPORT:
+	case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
+		igmp_heard_report(skb->dev, ih->group, skb->nh.iph->saddr);
+		break;
+	case IGMP_DVMRP:
+	case IGMP_PIM:
+	case IGMP_TRACE:
+	case IGMP_HOST_LEAVE_MESSAGE:
+	case IGMP_MTRACE:
+	case IGMP_MTRACE_RESP:
+		break;
+	default:
+		NETDEBUG(printk(KERN_DEBUG "Unknown IGMP type=%d\n", ih->type));
 	}
-
-	if(ih->type==IGMP_HOST_MEMBERSHIP_QUERY && daddr==IGMP_ALL_HOSTS)
-		igmp_heard_query(dev,ih->code);
-	if(ih->type==IGMP_HOST_MEMBERSHIP_REPORT && daddr==ih->group)
-		igmp_heard_report(dev,ih->group, saddr);
-	if(ih->type==IGMP_HOST_NEW_MEMBERSHIP_REPORT && daddr==ih->group)
-		igmp_heard_report(dev,ih->group, saddr);
 	kfree_skb(skb, FREE_READ);
 	return 0;
 }
@@ -581,7 +581,6 @@
 	i->next=dev->ip_mc_list;
 	dev->ip_mc_list=i;
 	ip_mc_filter_add(i->interface, i->multiaddr);
-
 }
 
 /*
@@ -594,8 +593,6 @@
 	int i;
 	if(!MULTICAST(addr))
 		return -EINVAL;
-	if(!(dev->flags&IFF_MULTICAST))
-		return -EADDRNOTAVAIL;
 	if(sk->ip_mc_list==NULL)
 	{
 		if((sk->ip_mc_list=(struct ip_mc_socklist *)kmalloc(sizeof(*sk->ip_mc_list), GFP_KERNEL))==NULL)
@@ -627,8 +624,6 @@
 	int i;
 	if(!MULTICAST(addr))
 		return -EINVAL;
-	if(!(dev->flags&IFF_MULTICAST))
-		return -EADDRNOTAVAIL;
 	if(sk->ip_mc_list==NULL)
 		return -EADDRNOTAVAIL;
 
@@ -667,4 +662,52 @@
 	sk->ip_mc_list=NULL;
 }
 
-#endif
+
+/*
+ *	Write an multicast group list table for the IGMP daemon to
+ *	read.
+ */
+ 
+int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	off_t pos=0, begin=0;
+	struct ip_mc_list *im;
+	unsigned long flags;
+	int len=0;
+	struct device *dev;
+	
+	len=sprintf(buffer,"Device    : Count\tGroup    Users Timer\tReporter\n");  
+	save_flags(flags);
+	cli();
+	
+	for(dev = dev_base; dev; dev = dev->next)
+	{
+                if(dev->flags&IFF_UP)
+                {
+                        len+=sprintf(buffer+len,"%-10s: %5d\n",
+					dev->name, dev->mc_count);
+                        for(im = dev->ip_mc_list; im; im = im->next)
+                        {
+                                len+=sprintf(buffer+len,
+					"\t\t\t%08lX %5d %d:%08lX\t%d\n",
+                                        im->multiaddr, im->users,
+					im->tm_running, im->timer.expires-jiffies, im->reporter);
+                                pos=begin+len;
+                                if(pos<offset)
+                                {
+                                        len=0;
+                                        begin=pos;
+                                }
+                                if(pos>offset+length)
+                                        break;
+                        }
+                }
+	}
+	restore_flags(flags);
+	*start=buffer+(offset-begin);
+	len-=(offset-begin);
+	if(len>length)
+		len=length;	
+	return len;
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_alias.c linux/net/ipv4/ip_alias.c
--- v2.1.14/linux/net/ipv4/ip_alias.c	Mon May 13 12:15:24 1996
+++ linux/net/ipv4/ip_alias.c	Thu Dec 12 16:54:23 1996
@@ -79,7 +79,9 @@
 struct device *ip_alias_dev_select(struct net_alias_type *this, struct device *main_dev, struct sockaddr *sa)
 {
 	__u32 addr;
+#if 0
 	struct rtable *rt;
+#endif
 	struct device *dev=NULL;
   
 	/*
@@ -102,12 +104,14 @@
 	 *	net_alias module will check if returned device is main_dev's alias
 	 */
 
+#if 0
 	rt = ip_rt_route(addr, 0);
 	if(rt)
 	{
 		dev=rt->rt_dev;
 		ip_rt_put(rt);
 	}
+#endif
 	return dev;
 }
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.1.14/linux/net/ipv4/ip_forward.c	Tue Nov 19 15:54:00 1996
+++ linux/net/ipv4/ip_forward.c	Thu Dec 12 16:54:23 1996
@@ -16,7 +16,6 @@
  *					use output device for accounting.
  *		Jos Vos		:	Call forward firewall after routing
  *					(always use output device).
- *		Alan Cox	: 	Unshare buffer on forward.
  */
 
 #include <linux/config.h>
@@ -41,534 +40,199 @@
 #include <linux/route.h>
 #include <net/route.h>
 
-#ifdef CONFIG_IP_FORWARD
-#ifdef CONFIG_IP_MROUTE
-
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
 /*
- * 	Encapsulate a packet by attaching a valid IPIP header to it.
- *	This avoids tunnel drivers and other mess and gives us the speed so
- *	important for multicast video.
+ *	Check the packet against our socket administration to see
+ *	if it is related to a connection on our system.
+ *	Needed for transparent proxying.
  */
- 
-static void ip_encap(struct sk_buff *skb, int len, struct device *out, __u32 daddr)
-{
-	/*
-	 *	There is space for the IPIP header and MAC left.
-	 *
-	 *	Firstly push down and install the IPIP header.
-	 */
-	struct iphdr *iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
-
-	if(len>65515)
-		len=65515;
-
-
-	iph->version	= 	4;
-	iph->tos	=	skb->ip_hdr->tos;
-	iph->ttl	=	skb->ip_hdr->ttl;
-	iph->frag_off	=	0;
-	iph->daddr	=	daddr;
-	iph->saddr	=	out->pa_addr;
-	iph->protocol	=	IPPROTO_IPIP;
-	iph->ihl	=	5;
-	iph->tot_len	=	htons(skb->len + len);  /* Anand, ernet */
-	iph->id		=	htons(ip_id_count++);
-	ip_send_check(iph);
-
-	skb->dev = out;
-	skb->arp = 1;
-	skb->raddr=daddr;  /* Router address is not destination address. The
-			    * correct value is given eventually. I have not
-			    * removed this statement. But could have. 
-			    * Anand, ernet.
-			    */ 
-	/*
-	 *	Now add the physical header (driver will push it down).
-	 */
 
-	/* The last parameter of out->hard_header() needed skb->len + len.
- 	 * Anand, ernet.
-	 */
-	if (out->hard_header && out->hard_header(skb, out, ETH_P_IP, NULL, NULL, 
-	skb->len + len)<0)
-			skb->arp=0;
-	/*
-	 *	Read to queue for transmission.
-	 */
+int ip_chksock(struct sk_buff *skb)
+{
+	switch (skb->nh.iph->protocol) {
+	case IPPROTO_ICMP:
+		return icmp_chkaddr(skb);
+	case IPPROTO_TCP:
+		return tcp_chkaddr(skb);
+	case IPPROTO_UDP:
+		return udp_chkaddr(skb);
+	default:
+		return 0;
+	}
 }
-
 #endif
 
-/*
- *	Forward an IP datagram to its next destination.
- */
 
-int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
-	       __u32 target_addr)
+int ip_forward(struct sk_buff *skb)
 {
 	struct device *dev2;	/* Output device */
 	struct iphdr *iph;	/* Our header */
-	struct sk_buff *skb2;	/* Output packet */
 	struct rtable *rt;	/* Route we use */
-	unsigned char *ptr;	/* Data pointer */
-	unsigned long raddr;	/* Router IP address */
-	struct   options * opt	= (struct options*)skb->proto_priv;
-	struct hh_cache *hh = NULL;
-	int encap = 0;		/* Encap length */
-#ifdef CONFIG_FIREWALL
-	int fw_res = 0;		/* Forwarding result */	
-#ifdef CONFIG_IP_MASQUERADE	
-	struct sk_buff *skb_in = skb;	/* So we can remember if the masquerader did some swaps */
-#endif /* CONFIG_IP_MASQUERADE */
-#endif /* CONFIG_FIREWALL */
+	struct ip_options * opt	= &(IPCB(skb)->opt);
+#if defined(CONFIG_FIREWALL) || defined(CONFIG_IP_MASQUERADE)
+	int fw_res = 0;
+#endif
 
-	/*
-	 *	We may be sharing the buffer with a snooper. That won't do
-	 */
-	 
-	if((skb=skb_unshare(skb, GFP_ATOMIC,FREE_READ))==NULL)
-		return -1;
+	if (skb->pkt_type != PACKET_HOST) {
+		kfree_skb(skb,FREE_WRITE);
+		return 0;
+	}
 	
 	/*
 	 *	According to the RFC, we must first decrease the TTL field. If
 	 *	that reaches zero, we must reply an ICMP control message telling
 	 *	that the packet's lifetime expired.
-	 *
-	 *	Exception:
-	 *	We may not generate an ICMP for an ICMP. icmp_send does the
-	 *	enforcement of this so we can forget it here. It is however
-	 *	sometimes VERY important.
 	 */
 
-	iph = skb->h.iph;
-	if (!(is_frag&IPFWD_NOTTLDEC))
-	{
-		unsigned long checksum = iph->check;
-		iph->ttl--;
+	iph = skb->nh.iph;
+	rt = (struct rtable*)skb->dst;
 
-	/*
-	 *	Re-compute the IP header checksum.
-	 *	This is efficient. We know what has happened to the header
-	 *	and can thus adjust the checksum as Phil Karn does in KA9Q
-	 *	except we do this in "network byte order".
-	 */
-		checksum += htons(0x0100);
-		/* carry overflow? */
-		checksum += checksum >> 16;
-		iph->check = checksum;
-	}
+#ifdef CONFIG_TRANSPARENT_PROXY
+	if (ip_chk_sock(skb))
+		return ip_local_deliver(skb);
+#endif
 
-	if (iph->ttl <= 0)
-	{
+	if (ip_decrease_ttl(iph) <= 0) {
 		/* Tell the sender its packet died... */
-		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0, dev);
+		icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
+		kfree_skb(skb, FREE_WRITE);
 		return -1;
 	}
 
-	/* If IPFWD_MULTITUNNEL flag is set, then we have to perform routing
-	 * decision so as to reach the other end of the tunnel. This condition
-	 * also means that we are dealing with a unicast IP packet "in a way". 
-	 * Anand, ernet.
-	 */
-
-#ifdef CONFIG_IP_MROUTE
-	if(!(is_frag&IPFWD_MULTICASTING) || (is_frag&IPFWD_MULTITUNNEL))
-	{
-#endif	
+	if (opt->is_strictroute && (rt->rt_flags&RTF_GATEWAY)) {
 		/*
-		 * OK, the packet is still valid.  Fetch its destination address,
-		 * and give it to the IP sender for further processing.
+		 *	Strict routing permits no gatewaying
 		 */
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
+		kfree_skb(skb, FREE_WRITE);
+		return -1;
+	}
 
-		rt = ip_rt_route(target_addr, 0);
 
-		if (rt == NULL)
-		{
-			/*
-			 *	Tell the sender its packet cannot be delivered. Again
-			 *	ICMP is screened later.
-			 */
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_NET_UNREACH, 0, dev);
-			return -1;
-		}
-	
-	
-		/*
-		 * Gosh.  Not only is the packet valid; we even know how to
-		 * forward it onto its final destination.  Can we say this
-		 * is being plain lucky?
-		 * If the router told us that there is no GW, use the dest.
-		 * IP address itself- we seem to be connected directly...
-		 */
+	/*
+	 *	Having picked a route we can now send the frame out
+	 *	after asking the firewall permission to do so.
+	 */
 
-		raddr = rt->rt_gateway;
-	
-		if (opt->is_strictroute && (rt->rt_flags & RTF_GATEWAY)) {
-			/*
-			 *	Strict routing permits no gatewaying
-			 */
+	skb->priority = rt->u.dst.priority;
+	dev2 = rt->u.dst.dev;
+
+	/*
+	 *	In IP you never have to forward a frame on the interface that it 
+	 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
+	 *	we calculated.
+	 */
+	if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
+		ip_rt_send_redirect(skb);
 	
-			ip_rt_put(rt);
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0, dev);
+	/*
+	 * We now may allocate a new buffer, and copy the datagram into it.
+	 * If the indicated interface is up and running, kick it.
+	 */
+
+	if (dev2->flags & IFF_UP) {
+		if (skb->len > dev2->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+			ip_statistics.IpFragFails++;
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu));
+			kfree_skb(skb, FREE_WRITE);
 			return -1;
 		}
 
-		/*
-		 *	Having picked a route we can now send the frame out
-		 *	after asking the firewall permission to do so.
-		 */
-
-		dev2 = rt->rt_dev;
-		hh = rt->rt_hh;
-		/*
-		 *	In IP you never have to forward a frame on the interface that it 
-		 *	arrived upon. We now generate an ICMP HOST REDIRECT giving the route
-		 *	we calculated.
-		 */
-#ifndef CONFIG_IP_NO_ICMP_REDIRECT
-		if (dev == dev2 && 
-			!((iph->saddr^dev->pa_addr)&dev->pa_mask) &&
-			/* The daddr!=raddr test isn't obvious - what it's doing
-			   is avoiding sending a frame the receiver will not 
-			   believe anyway.. */
-			iph->daddr != raddr/*ANK*/ && !opt->srr)
-				icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, raddr, dev);
-#endif
-#ifdef CONFIG_IP_MROUTE
-
-		/* This is for ip encap. Anand, ernet.*/
+		if (rt->rt_flags&RTCF_NAT) {
+			if (ip_do_nat(skb)) {
+				kfree_skb(skb, FREE_WRITE);
+				return -1;
+			}
+		}
 
-		if (is_frag&IPFWD_MULTITUNNEL) {
-			encap=20;
-				}
-	}
-	else
-	{
-		/*
-		 *	Multicast route forward. Routing is already done
-		 */
-		dev2=skb->dev;
-		raddr=skb->raddr;
-		if(is_frag&IPFWD_MULTITUNNEL)	/* VIFF_TUNNEL mode */
-			encap=20;
-		rt=NULL;
-	}
-#endif	
-	
-	/* 
-	 *	See if we are allowed to forward this.
- 	 *	Note: demasqueraded fragments are always 'back'warded.
-	 */
-	
-#ifdef CONFIG_FIREWALL
-	if(!(is_frag&IPFWD_MASQUERADED))
-	{
 #ifdef CONFIG_IP_MASQUERADE
-		/* 
-		 *	Check that any ICMP packets are not for a 
-		 *	masqueraded connection.  If so rewrite them
-		 *	and skip the firewall checks
-		 */
-		if (iph->protocol == IPPROTO_ICMP)
-		{
-			if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0)
-			{
-				if (rt)
-					ip_rt_put(rt);
-				/* Problem - ie bad checksum */
+		if(!(IPCB(skb)->flags&IPSKB_MASQUERADED)) {
+
+			if (rt->rt_flags&RTCF_VALVE) {
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PKT_FILTERED, 0);
+				kfree_skb(skb, FREE_READ);
 				return -1;
 			}
 
-			if (fw_res)
-				/* ICMP matched - skip firewall */
+			/* 
+			 *	Check that any ICMP packets are not for a 
+			 *	masqueraded connection.  If so rewrite them
+			 *	and skip the firewall checks
+			 */
+			if (iph->protocol == IPPROTO_ICMP) {
+				if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0) {
+					kfree_skb(skb, FREE_READ);
+					return -1;
+				}
+
+				if (fw_res)
+					/* ICMP matched - skip firewall */
+					goto skip_call_fw_firewall;
+			}
+			if (rt->rt_flags&RTCF_MASQ)
 				goto skip_call_fw_firewall;
 		}
 #endif
+#ifdef CONFIG_FIREWALL
 		fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL);
 		switch (fw_res) {
 		case FW_ACCEPT:
 		case FW_MASQUERADE:
 			break;
 		case FW_REJECT:
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
+			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
 			/* fall thru */
 		default:
+			kfree_skb(skb, FREE_READ);
 			return -1;
 		}
-
-#ifdef CONFIG_IP_MASQUERADE
-		skip_call_fw_firewall:
-#endif		
-	}
 #endif
 
-	/*
-	 * We now may allocate a new buffer, and copy the datagram into it.
-	 * If the indicated interface is up and running, kick it.
-	 */
-
-	if (dev2->flags & IFF_UP)
-	{
 #ifdef CONFIG_IP_MASQUERADE
+skip_call_fw_firewall:
 		/*
 		 * If this fragment needs masquerading, make it so...
 		 * (Don't masquerade de-masqueraded fragments)
 		 */
-		if (!(is_frag&IPFWD_MASQUERADED) && fw_res==FW_MASQUERADE)
-			if (ip_fw_masquerade(&skb, dev2) < 0)
-			{
-				/*
-				 * Masquerading failed; silently discard this packet.
-				 */
-				if (rt)
-					ip_rt_put(rt);
+		if (!(IPCB(skb)->flags&IPSKB_MASQUERADED) &&
+		    (fw_res==FW_MASQUERADE || rt->rt_flags&RTCF_MASQ)) {
+			if (ip_fw_masquerade(&skb, dev2) < 0) {
+				kfree_skb(skb, FREE_READ);
 				return -1;
 			}
+		}
 #endif
-		IS_SKB(skb);
 
-		if (skb->len+encap > dev2->mtu && (iph->frag_off & htons(IP_DF))) 
-		{
-			ip_statistics.IpFragFails++;
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(dev2->mtu), dev);
-			if(rt)
-				ip_rt_put(rt);
-			return -1;
-		}
+		if (skb_headroom(skb) < dev2->hard_header_len) {
+			struct sk_buff *skb2;
+			skb2 = skb_realloc_headroom(skb, (dev2->hard_header_len + 15)&~15);
+			kfree_skb(skb, FREE_WRITE);
 
-#ifdef CONFIG_IP_MROUTE
-		if(skb_headroom(skb)-encap<dev2->hard_header_len)
-		{
-			skb2 = alloc_skb(dev2->hard_header_len + skb->len + encap + 15, GFP_ATOMIC);
-#else
-		if(skb_headroom(skb)<dev2->hard_header_len)
-		{
-			skb2 = alloc_skb(dev2->hard_header_len + skb->len + 15, GFP_ATOMIC);
-#endif		
-			/*
-			 *	This is rare and since IP is tolerant of network failures
-			 *	quite harmless.
-			 */
-		
-			if (skb2 == NULL)
-			{
+			if (skb2 == NULL) {
 				NETDEBUG(printk("\nIP: No memory available for IP forward\n"));
-				if(rt)
-					ip_rt_put(rt);
 				return -1;
 			}
-		
-			IS_SKB(skb2);
-			/*
-			 *	Add the physical headers.
-			 */
-			skb2->protocol=htons(ETH_P_IP);
-#ifdef CONFIG_IP_MROUTE
-			if(is_frag&IPFWD_MULTITUNNEL)
-			{
-				skb_reserve(skb2,(encap+dev2->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
-
-/* We need to pass on IP information of the incoming packet to ip_encap() 
- * to fillin ttl, and tos fields.The destination should be target_addr. 
- *  Anand, ernet. 
- */
-
-				skb2->ip_hdr = skb->ip_hdr; 
-
-				ip_encap(skb2,skb->len, dev2, target_addr);
-
-/*  The router address is got earlier that to take us to the remote tunnel
- *  Anand, ernet.
- */
-				skb2->raddr = rt->rt_gateway;
-			}
-			else
-#endif			
-		 		ip_send(rt,skb2,raddr,skb->len,dev2,dev2->pa_addr);
-
-			/*
-			 *	We have to copy the bytes over as the new header wouldn't fit
-			 *	the old buffer. This should be very rare.
-			 */		 
-		 	
-			ptr = skb_put(skb2,skb->len);
-			skb2->free = 1;
-			skb2->h.raw = ptr;
-			/*
-			 *	Copy the packet data into the new buffer.
-			 */
-			memcpy(ptr, skb->h.raw, skb->len);
-			memcpy(skb2->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
-			iph = skb2->ip_hdr = skb2->h.iph;
+			skb = skb2;
+			iph = skb2->nh.iph;
 		}
-		else
-		{
-			/* 
-			 *	Build a new MAC header. 
-			 */
 
-			skb2 = skb;		
-			skb2->dev=dev2;
-#ifdef CONFIG_IP_MROUTE
-			if(is_frag&IPFWD_MULTITUNNEL)
-				ip_encap(skb,skb->len, dev2, raddr);
-			else
-			{
-#endif
-				skb->arp=1;
-				skb->raddr=raddr;
-				if (hh)
-				{
-					memcpy(skb_push(skb, dev2->hard_header_len), hh->hh_data, dev2->hard_header_len);
-					if (!hh->hh_uptodate)
-					{
-#if RT_CACHE_DEBUG >= 2
-						printk("ip_forward: hh miss %08x via %08x\n", target_addr, rt->rt_gateway);
-#endif						
-						skb->arp = 0;
-					}
-				}
-				else if (dev2->hard_header)
-				{
-					if(dev2->hard_header(skb, dev2, ETH_P_IP, NULL, NULL, skb->len)<0)
-						skb->arp=0;
-				}
-#ifdef CONFIG_IP_MROUTE
-			}				
-#endif			
-		}
 #ifdef CONFIG_FIREWALL
-		if((fw_res = call_out_firewall(PF_INET, skb2->dev, iph, NULL)) < FW_ACCEPT)
-		{
+		if ((fw_res = call_out_firewall(PF_INET, skb->dev, iph, NULL)) < FW_ACCEPT) {
 			/* FW_ACCEPT and FW_MASQUERADE are treated equal:
 			   masquerading is only supported via forward rules */
 			if (fw_res == FW_REJECT)
-				icmp_send(skb2, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
-			if (skb != skb2)
-				kfree_skb(skb2,FREE_WRITE);
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0);
+			kfree_skb(skb,FREE_WRITE);
 			return -1;
 		}
 #endif
-		ip_statistics.IpForwDatagrams++;
 
-		if (opt->optlen) 
-		{
-			unsigned char * optptr;
-			if (opt->rr_needaddr) 
-			{
-				optptr = (unsigned char *)iph + opt->rr;
-				memcpy(&optptr[optptr[2]-5], &dev2->pa_addr, 4);
-				opt->is_changed = 1;
-			}
-			if (opt->srr_is_hit) 
-			{
-				int srrptr, srrspace;
-
-				optptr = (unsigned char *)iph + opt->srr;
-
-				for ( srrptr=optptr[2], srrspace = optptr[1];
-				      srrptr <= srrspace;
-				     srrptr += 4
-				    ) 
-				{
-					if (srrptr + 3 > srrspace)
-						break;
-					if (memcmp(&target_addr, &optptr[srrptr-1], 4) == 0)
-						break;
-				}
-				if (srrptr + 3 <= srrspace) 
-				{
-					opt->is_changed = 1;
-					memcpy(&optptr[srrptr-1], &dev2->pa_addr, 4);
-					iph->daddr = target_addr;
-					optptr[2] = srrptr+4;
-				}
-				else
-				        printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
-			}
-			if (opt->ts_needaddr) 
-			{
-				optptr = (unsigned char *)iph + opt->ts;
-				memcpy(&optptr[optptr[2]-9], &dev2->pa_addr, 4);
-				opt->is_changed = 1;
-			}
-			if (opt->is_changed) 
-			{
-				opt->is_changed = 0;
-				ip_send_check(iph);
-			}
-		}
-/*
- * ANK:  this is point of "no return", we cannot send an ICMP,
- *       because we changed SRR option.
- */
-
-		/*
-		 *	See if it needs fragmenting. Note in ip_rcv we tagged
-		 *	the fragment type. This must be right so that
-		 *	the fragmenter does the right thing.
-		 */
+		ip_statistics.IpForwDatagrams++;
 
-		if(skb2->len > dev2->mtu + dev2->hard_header_len)
-		{
-			ip_fragment(NULL,skb2,dev2, is_frag);
-			kfree_skb(skb2,FREE_WRITE);
-		}
-		else
-		{
-#ifdef CONFIG_IP_ACCT		
-			/*
-			 *	Count mapping we shortcut
-			 */
-			 
-			ip_fw_chk(iph,dev2,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif			
-			
-			/*
-			 *	Map service types to priority. We lie about
-			 *	throughput being low priority, but it's a good
-			 *	choice to help improve general usage.
-			 */
-			if(iph->tos & IPTOS_LOWDELAY)
-				dev_queue_xmit(skb2, dev2, SOPRI_INTERACTIVE);
-			else if(iph->tos & IPTOS_THROUGHPUT)
-				dev_queue_xmit(skb2, dev2, SOPRI_BACKGROUND);
-			else
-				dev_queue_xmit(skb2, dev2, SOPRI_NORMAL);
-		}
-	}
-	else
-	{
-	        if(rt)
-	        	ip_rt_put(rt);
-		return -1;
-	}
-	if(rt)
-		ip_rt_put(rt);
-	
-	/*
-	 *	Tell the caller if their buffer is free.
-	 */	 
-	 
-	if(skb==skb2)
-		return 0;	
+		if (opt->optlen)
+			ip_forward_options(skb);
 
-#ifdef CONFIG_IP_MASQUERADE	
-	/*
-	 *	The original is free. Free our copy and
-	 *	tell the caller not to free.
-	 */
-	if(skb!=skb_in)
-	{
-		kfree_skb(skb_in, FREE_WRITE);
-		return 0;
+		ip_send(skb);
 	}
-#endif	
-	return 1;
+	return 0;
 }
-
-
-#endif
-
-
-
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.14/linux/net/ipv4/ip_fragment.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ipv4/ip_fragment.c	Thu Dec 12 16:54:24 1996
@@ -24,6 +24,7 @@
 #include <net/icmp.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
+#include <linux/inet.h>
 #include <linux/firewall.h>
 #include <linux/ip_fw.h>
 #include <net/checksum.h>
@@ -47,6 +48,8 @@
 
 atomic_t ip_frag_mem = 0;			/* Memory used for fragments */
 
+char *in_ntoa(unsigned long in);
+
 /*
  *	Memory Tracking Functions
  */
@@ -208,7 +211,7 @@
 	/* This if is always true... shrug */
 	if(qp->fragments!=NULL)
 		icmp_send(qp->fragments->skb,ICMP_TIME_EXCEEDED,
-				ICMP_EXC_FRAGTIME, 0, qp->dev);
+				ICMP_EXC_FRAGTIME, 0);
 
 	/*
 	 *	Nuke the fragment queue.
@@ -238,7 +241,7 @@
  * 	will insert the received fragments at their respective positions.
  */
 
-static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph, struct device *dev)
+static struct ipq *ip_create(struct sk_buff *skb, struct iphdr *iph)
 {
 	struct ipq *qp;
 	int ihlen;
@@ -268,7 +271,7 @@
 	qp->len = 0;
 	qp->ihlen = ihlen;
 	qp->fragments = NULL;
-	qp->dev = dev;
+	qp->dev = skb->dev;
 
 	/* Start a timer for this entry. */
 	qp->timer.expires = jiffies + IP_FRAG_TIME;	/* about 30 seconds	*/
@@ -337,7 +340,15 @@
 	 *	Allocate a new buffer for the datagram.
 	 */
 	len = qp->ihlen + qp->len;
-
+	
+	if(len>65535)
+	{
+		printk("Oversized IP packet from %s.\n", in_ntoa(qp->iph->saddr));
+		ip_statistics.IpReasmFails++;
+		ip_free(qp);
+		return NULL;
+	}
+	
 	if ((skb = dev_alloc_skb(len)) == NULL)
 	{
 		ip_statistics.IpReasmFails++;
@@ -347,13 +358,11 @@
 	}
 
 	/* Fill in the basic details. */
-	skb_put(skb,len);
-	skb->h.raw = skb->data;
-	skb->free = 1;
+	skb->mac.raw = ptr = skb->data;
+	skb->nh.iph = iph = (struct iphdr*)skb_put(skb,len);
 
 	/* Copy the original IP headers into the new buffer. */
-	ptr = (unsigned char *) skb->h.raw;
-	memcpy(ptr, ((unsigned char *) qp->iph), qp->ihlen);
+	memcpy(ptr, qp->iph, qp->ihlen);
 	ptr += qp->ihlen;
 
 	count = 0;
@@ -371,6 +380,10 @@
 			return NULL;
 		}
 		memcpy((ptr + fp->offset), fp->ptr, fp->len);
+		if (!count) {
+			skb->dst = dst_clone(fp->skb->dst);
+			skb->dev = fp->skb->dev;
+		}
 		count += fp->len;
 		fp = fp->next;
 	}
@@ -379,10 +392,9 @@
 	ip_free(qp);
 
 	/* Done with all fragments. Fixup the new IP header. */
-	iph = skb->h.iph;
+	iph = skb->nh.iph;
 	iph->frag_off = 0;
 	iph->tot_len = htons((iph->ihl * 4) + count);
-	skb->ip_hdr = iph;
 
 	ip_statistics.IpReasmOKs++;
 	return(skb);
@@ -393,8 +405,9 @@
  *	Process an incoming IP datagram fragment.
  */
 
-struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev)
+struct sk_buff *ip_defrag(struct sk_buff *skb)
 {
+	struct iphdr *iph = skb->nh.iph;
 	struct ipfrag *prev, *next, *tmp;
 	struct ipfrag *tfp;
 	struct ipq *qp;
@@ -424,7 +437,7 @@
 	if (((flags & IP_MF) == 0) && (offset == 0))
 	{
 		if (qp != NULL)
-			ip_free(qp);	/* Huh? How could this exist?? */
+			ip_free(qp);	/* Fragmented frame replaced by full unfragmented copy */
 		return(skb);
 	}
 
@@ -458,10 +471,9 @@
 		/*
 		 *	If we failed to create it, then discard the frame
 		 */
-		if ((qp = ip_create(skb, iph, dev)) == NULL)
+		if ((qp = ip_create(skb, iph)) == NULL)
 		{
-			skb->sk = NULL;
-			frag_kfree_skb(skb, FREE_READ);
+			kfree_skb(skb, FREE_READ);
 			ip_statistics.IpReasmFails++;
 			return NULL;
 		}
@@ -473,7 +485,7 @@
 	 
 	if(ntohs(iph->tot_len)+(int)offset>65535)
 	{
-		skb->sk = NULL;
+		printk("Oversized packet received from %s\n",in_ntoa(iph->saddr));
 		frag_kfree_skb(skb, FREE_READ);
 		ip_statistics.IpReasmFails++;
 		return NULL;
@@ -573,7 +585,6 @@
 
 	if (!tfp)
 	{
-		skb->sk = NULL;
 		frag_kfree_skb(skb, FREE_READ);
 		return NULL;
 	}
@@ -600,200 +611,3 @@
 	}
 	return(NULL);
 }
-
-
-/*
- *	This IP datagram is too large to be sent in one piece.  Break it up into
- *	smaller pieces (each of size equal to the MAC header plus IP header plus
- *	a block of the data of the original IP data part) that will yet fit in a
- *	single device frame, and queue such a frame for sending by calling the
- *	ip_queue_xmit().  Note that this is recursion, and bad things will happen
- *	if this function causes a loop...
- *
- *	Yes this is inefficient, feel free to submit a quicker one.
- *
- */
- 
-void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag)
-{
-	struct iphdr *iph;
-	unsigned char *raw;
-	unsigned char *ptr;
-	struct sk_buff *skb2;
-	int left, mtu, hlen, len;
-	int offset;
-	
-	unsigned short true_hard_header_len;
-
-	/*
-	 *	Point into the IP datagram header.
-	 */
-
-	raw = skb->data;
-#if 0
-	iph = (struct iphdr *) (raw + dev->hard_header_len);	
-	skb->ip_hdr = iph;
-#else
-	iph = skb->ip_hdr;
-#endif
-
-	/*
-	 * Calculate the length of the link-layer header appended to
-	 * the IP-packet.
-	 */
-	true_hard_header_len = ((unsigned char *)iph) - raw;
-
-	/*
-	 *	Setup starting values.
-	 */
-
-	hlen = iph->ihl * 4;
-	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
-	hlen += true_hard_header_len;
-	mtu = (dev->mtu - hlen);		/* Size of data space */
-	ptr = (raw + hlen);			/* Where to start from */
-
-	/*
-	 *	Check for any "DF" flag. [DF means do not fragment]
-	 */
-
-	if (iph->frag_off & htons(IP_DF))
-	{
-		ip_statistics.IpFragFails++;
-		NETDEBUG(printk("ip_queue_xmit: frag needed\n"));
-		return;
-	}
-
-	/*
-	 *	The protocol doesn't seem to say what to do in the case that the
-	 *	frame + options doesn't fit the mtu. As it used to fall down dead
-	 *	in this case we were fortunate it didn't happen
-	 */
-
-	if(mtu<8)
-	{
-		/* It's wrong but it's better than nothing */
-		icmp_send(skb,ICMP_DEST_UNREACH,ICMP_FRAG_NEEDED,dev->mtu, dev);
-		ip_statistics.IpFragFails++;
-		return;
-	}
-
-	/*
-	 *	Fragment the datagram.
-	 */
-
-	/*
-	 *	The initial offset is 0 for a complete frame. When
-	 *	fragmenting fragments it's wherever this one starts.
-	 */
-
-	if (is_frag & 2)
-		offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
-	else
-		offset = 0;
-
-
-	/*
-	 *	Keep copying data until we run out.
-	 */
-
-	while(left > 0)
-	{
-		len = left;
-		/* IF: it doesn't fit, use 'mtu' - the data space left */
-		if (len > mtu)
-			len = mtu;
-		/* IF: we are not sending upto and including the packet end
-		   then align the next start on an eight byte boundary */
-		if (len < left)
-		{
-			len/=8;
-			len*=8;
-		}
-		/*
-		 *	Allocate buffer.
-		 */
-
-		if ((skb2 = alloc_skb(len + hlen+15,GFP_ATOMIC)) == NULL)
-		{
-			NETDEBUG(printk("IP: frag: no memory for new fragment!\n"));
-			ip_statistics.IpFragFails++;
-			return;
-		}
-
-		/*
-		 *	Set up data on packet
-		 */
-
-		skb2->arp = skb->arp;
-		skb2->protocol = htons(ETH_P_IP); /* Atleast PPP needs this */
-#if 0		
-		if(skb->free==0)
-			printk(KERN_ERR "IP fragmenter: BUG free!=1 in fragmenter\n");
-#endif			
-		skb2->free = 1;
-		skb_put(skb2,len + hlen);
-		skb2->h.raw=(char *) skb2->data;
-		/*
-		 *	Charge the memory for the fragment to any owner
-		 *	it might possess
-		 */
-
-		if (sk)
-		{
-			atomic_add(skb2->truesize, &sk->wmem_alloc);
-			skb2->sk=sk;
-		}
-		skb2->raddr = skb->raddr;	/* For rebuild_header - must be here */
-
-		/*
-		 *	Copy the packet header into the new buffer.
-		 */
-
-		memcpy(skb2->h.raw, raw, hlen);
-
-		/*
-		 *	Copy a block of the IP datagram.
-		 */
-		memcpy(skb2->h.raw + hlen, ptr, len);
-		left -= len;
-
-		skb2->h.raw+=true_hard_header_len;
-
-		/*
-		 *	Fill in the new header fields.
-		 */
-		iph = (struct iphdr *)(skb2->h.raw/*+dev->hard_header_len*/);
-		iph->frag_off = htons((offset >> 3));
-		skb2->ip_hdr = iph;
-
-		/* ANK: dirty, but effective trick. Upgrade options only if
-		 * the segment to be fragmented was THE FIRST (otherwise,
-		 * options are already fixed) and make it ONCE
-		 * on the initial skb, so that all the following fragments
-		 * will inherit fixed options.
-		 */
-		if (offset == 0)
-			ip_options_fragment(skb);
-
-		/*
-		 *	Added AC : If we are fragmenting a fragment that's not the
-		 *		   last fragment then keep MF on each bit
-		 */
-		if (left > 0 || (is_frag & 1))
-			iph->frag_off |= htons(IP_MF);
-		ptr += len;
-		offset += len;
-
-		/*
-		 *	Put this fragment into the sending queue.
-		 */
-
-		ip_statistics.IpFragCreates++;
-
-		ip_queue_xmit(sk, dev, skb2, 2);
-	}
-	ip_statistics.IpFragOKs++;
-}
-
-
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.14/linux/net/ipv4/ip_fw.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/ip_fw.c	Thu Dec 12 16:54:24 1996
@@ -153,7 +153,7 @@
 static struct ip_fw **chains[] =
 	{&ip_fw_fwd_chain, &ip_fw_in_chain, &ip_fw_out_chain, &ip_acct_chain};
 #endif /* CONFIG_IP_ACCT || CONFIG_IP_FIREWALL */
-
+ 
 #ifdef CONFIG_IP_FIREWALL
 int ip_fw_fwd_policy=IP_FW_F_ACCEPT;
 int ip_fw_in_policy=IP_FW_F_ACCEPT;
@@ -1297,6 +1297,7 @@
 #endif
 #endif
 
+
 void ip_fw_init(void)
 {
 #ifdef CONFIG_PROC_FS
@@ -1314,14 +1315,6 @@
 	proc_net_register(&proc_net_ipfwout);
 	proc_net_register(&proc_net_ipfwfwd);
 #endif
-#endif
-#ifdef CONFIG_IP_MASQUERADE
-        
-        /*
-         *	Initialize masquerading. 
-         */
-        
-        ip_masq_init();
 #endif
         
 #if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.1.14/linux/net/ipv4/ip_input.c	Tue Oct 29 19:58:49 1996
+++ linux/net/ipv4/ip_input.c	Thu Dec 12 16:54:24 1996
@@ -109,8 +109,6 @@
  *		output should probably do its own fragmentation at the UDP/RAW layer. TCP shouldn't cause
  *		fragmentation anyway.
  *
- *		FIXME: copy frag 0 iph to qp->iph
- *
  *		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
@@ -160,20 +158,12 @@
 #include <linux/net_alias.h>
 #endif
 
-extern int last_retran;
-extern void sort_send(struct sock *sk);
-
-#define min(a,b)	((a)<(b)?(a):(b))
-
 /*
  *	SNMP management statistics
  */
 
-#ifdef CONFIG_IP_FORWARD
-struct ip_mib ip_statistics={1,64,};	/* Forwarding=Yes, Default TTL=64 */
-#else
-struct ip_mib ip_statistics={2,64,};	/* Forwarding=No, Default TTL=64 */
-#endif
+struct ip_mib ip_statistics={2,IPDEFTTL,};	/* Forwarding=No, Default TTL=64 */
+
 
 /*
  *	Handle the issuing of an ioctl() request
@@ -190,548 +180,287 @@
 	}
 }
 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-/*
- *	Check the packet against our socket administration to see
- *	if it is related to a connection on our system.
- *	Needed for transparent proxying.
- */
 
-int ip_chksock(struct sk_buff *skb)
-{
-	switch (skb->h.iph->protocol) {
-	case IPPROTO_ICMP:
-		return icmp_chkaddr(skb);
-	case IPPROTO_TCP:
-		return tcp_chkaddr(skb);
-	case IPPROTO_UDP:
-		return udp_chkaddr(skb);
-	default:
-		return 0;
-	}
-}
+#if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG)
+#define CONFIG_IP_ALWAYS_DEFRAG 1
 #endif
 
 
-/*
- *	This function receives all incoming IP datagrams.
- *
- *	On entry skb->data points to the start of the IP header and
- *	the MAC header has been removed.
- */
-
-int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+int ip_local_deliver(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->h.iph;
+	struct iphdr *iph = skb->nh.iph;
+#ifdef CONFIG_IP_MASQUERADE
+	struct device *dev = skb->dev;
+#endif
+	struct inet_protocol *ipprot;
 	struct sock *raw_sk=NULL;
 	unsigned char hash;
-	unsigned char flag = 0;
-	struct inet_protocol *ipprot;
-	int brd=IS_MYADDR;
-	struct options * opt = NULL;
-	int is_frag=0;
-	__u32 daddr;
-
-#ifdef CONFIG_FIREWALL
-	int fwres;
-	__u16 rport;
-#endif	
-#ifdef CONFIG_IP_MROUTE
-	int mroute_pkt=0;
-#endif	
-
-#ifdef CONFIG_NET_IPV6
-	/* 
-	 *	Intercept IPv6 frames. We dump ST-II and invalid types just below..
-	 */
-	 
-	if(iph->version == 6)
-		return ipv6_rcv(skb,dev,pt);
-#endif		
-
-	ip_statistics.IpInReceives++;
+	int flag = 0;
 
+#ifndef CONFIG_IP_ALWAYS_DEFRAG
 	/*
-	 *	Account for the packet (even if the packet is
-	 *	not accepted by the firewall!).
+	 *	Reassemble IP fragments.
 	 */
 
-#ifdef CONFIG_IP_ACCT
-	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
-#endif	
+	if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+		skb = ip_defrag(skb);
+		if (!skb)
+			return 0;
+		iph = skb->nh.iph;
+	}
+#endif
 
+#ifdef CONFIG_IP_MASQUERADE
 	/*
-	 *	Tag the ip header of this packet so we can find it
+	 * Do we need to de-masquerade this packet?
 	 */
+        {
+		int ret = ip_fw_demasquerade(&skb, dev);
+		if (ret < 0) {
+			kfree_skb(skb, FREE_WRITE);
+			return 0;
+		}
 
-	skb->ip_hdr = iph;
+		if (ret) {
+			iph=skb->nh.iph;
+			IPCB(skb)->flags |= IPSKB_MASQUERADED;
+			dst_release(skb->dst);
+			skb->dst = NULL;
+			if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, skb->dev)) {
+				kfree_skb(skb, FREE_WRITE);
+				return 0;
+			}
+			return skb->dst->input(skb);
+		}
+        }
+#endif
 
-	/*
-	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
-	 *	RFC1122: 3.1.2.3 MUST discard a frame with invalid source address [NEEDS FIXING].
-	 *
-	 *	Is the datagram acceptable?
-	 *
-	 *	1.	Length at least the size of an ip header
-	 *	2.	Version of 4
-	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
-	 *	4.	Doesn't have a bogus length
-	 *	(5.	We ought to check for IP multicast addresses and undefined types.. does this matter ?)
+        /*
+	 *	Point into the IP datagram, just past the header.
 	 */
 
-	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4 || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
-		|| skb->len < ntohs(iph->tot_len))
-	{
-		ip_statistics.IpInHdrErrors++;
-		kfree_skb(skb, FREE_WRITE);
-		return(0);
-	}
+        skb->h.raw = skb->nh.raw + iph->ihl*4;
 
 	/*
-	 *	Our transport medium may have padded the buffer out. Now we know it
-	 *	is IP we can trim to the true length of the frame.
-	 *	Note this now means skb->len holds ntohs(iph->tot_len).
+	 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
+	 *
+	 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
 	 */
+ 
+	hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
 
-	skb_trim(skb,ntohs(iph->tot_len));
-
-	/*
-	 *	Try to select closest <src,dst> alias device, if any.
-	 *	net_alias_dev_rcv_sel32 returns main device if it 
-	 *	fails to found other.
+	/* 
+	 *	If there maybe a raw socket we must check - if not we don't care less 
 	 */
-
-#ifdef CONFIG_NET_ALIAS
-	if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) 
-		skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
-#endif
-
-	if (iph->ihl > 5) 
+		 
+	if((raw_sk=raw_prot.sock_array[hash])!=NULL)
 	{
-		skb->ip_summed = 0;
-		if (ip_options_compile(NULL, skb))
-			return(0);
-		opt = (struct options*)skb->proto_priv;
-#ifdef CONFIG_IP_NOSR
-		if (opt->srr) 
+		struct sock *sknext=NULL;
+		struct sk_buff *skb1;
+		raw_sk=get_sock_raw(raw_sk, iph->protocol,  iph->saddr, iph->daddr);
+		if(raw_sk)	/* Any raw sockets */
 		{
-			kfree_skb(skb, FREE_READ);
-			return -EINVAL;
+			do
+			{
+				/* Find the next */
+				sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
+				if(sknext)
+					skb1=skb_clone(skb, GFP_ATOMIC);
+				else
+					break;	/* One pending raw socket left */
+				if(skb1)
+					raw_rcv(raw_sk, skb1);
+				raw_sk=sknext;
+			}
+			while(raw_sk!=NULL);
+				
+			/*
+			 *	Here either raw_sk is the last raw socket, or NULL if none 
+			 */
+			
+			/*
+			 *	We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy 
+			 */
 		}
-#endif					
 	}
 	
-#if defined(CONFIG_IP_TRANSPARENT_PROXY) && !defined(CONFIG_IP_ALWAYS_DEFRAG)
-#define CONFIG_IP_ALWAYS_DEFRAG 1
-#endif
-#ifdef CONFIG_IP_ALWAYS_DEFRAG
-	/*
-	 * Defragment all incoming traffic before even looking at it.
-	 * If you have forwarding enabled, this makes the system a
-	 * defragmenting router.  Not a common thing.
-	 * You probably DON'T want to enable this unless you have to.
-	 * You NEED to use this if you want to use transparent proxying,
-	 * otherwise, we can't vouch for your sanity.
-	 */
-
 	/*
-	 *	See if the frame is fragmented.
+	 *	skb->h.raw now points at the protocol beyond the IP header.
 	 */
-	 
-	if(iph->frag_off)
+	
+	hash = iph->protocol & (MAX_INET_PROTOS -1);
+	for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
 	{
-		if (iph->frag_off & htons(IP_MF))
-			is_frag|=IPFWD_FRAGMENT;
+		struct sk_buff *skb2;
+	
+		if (ipprot->protocol != iph->protocol)
+			continue;
 		/*
-		 *	Last fragment ?
+		 * 	See if we need to make a copy of it.  This will
+		 * 	only be set if more than one protocol wants it.
+		 * 	and then not for the last one. If there is a pending
+		 *	raw delivery wait for that
 		 */
 	
-		if (iph->frag_off & htons(IP_OFFSET))
-			is_frag|=IPFWD_LASTFRAG;
-	
+		if (ipprot->copy || raw_sk)
+		{
+			skb2 = skb_clone(skb, GFP_ATOMIC);
+			if(skb2==NULL)
+				continue;
+		}
+		else
+		{
+			skb2 = skb;
+		}
+		flag = 1;
+
 		/*
-		 *	Reassemble IP fragments.
+		 *	Pass on the datagram to each protocol that wants it,
+		 *	based on the datagram protocol.  We should really
+		 *	check the protocol handler's return values here...
 		 */
 
-		if(is_frag)
-		{
-			/* Defragment. Obtain the complete packet if there is one */
-			skb=ip_defrag(iph,skb,dev);
-			if(skb==NULL)
-				return 0;
-			skb->dev = dev;
-			iph=skb->h.iph;
-			is_frag = 0;
-			/*
-			 * When the reassembled packet gets forwarded, the ip
-			 * header checksum should be correct.
-			 * For better performance, this should actually only
-			 * be done in that particular case, i.e. set a flag
-			 * here and calculate the checksum in ip_forward.
-			 */
-			ip_send_check(iph);
-		}
+		ipprot->handler(skb2, ntohs(iph->tot_len) - (iph->ihl * 4));
 	}
 
-#endif
 	/*
-	 *	See if the firewall wants to dispose of the packet. 
+	 *	All protocols checked.
+	 *	If this packet was a broadcast, we may *not* reply to it, since that
+	 *	causes (proven, grin) ARP storms and a leakage of memory (i.e. all
+	 *	ICMP reply messages get queued up for transmission...)
 	 */
-	
-#ifdef	CONFIG_FIREWALL
 
-	if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT)
+	if(raw_sk!=NULL)	/* Shift to last raw user */
+		raw_rcv(raw_sk, skb);
+	else if (!flag)		/* Free and report errors */
 	{
-		if(fwres==FW_REJECT)
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0);	
 		kfree_skb(skb, FREE_WRITE);
-		return 0;	
 	}
 
-#ifdef	CONFIG_IP_TRANSPARENT_PROXY
-	if (fwres==FW_REDIRECT)
-		skb->redirport = rport;
-	else
-#endif
-		skb->redirport = 0;
-#endif
-	
-#ifndef CONFIG_IP_ALWAYS_DEFRAG
-	/*
-	 *	Remember if the frame is fragmented.
+	return(0);
+}
+
+int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	struct iphdr *iph = skb->nh.iph;
+	struct ip_options * opt = NULL;
+	int err;
+
+#ifdef CONFIG_NET_IPV6
+	/* 
+	 *	Intercept IPv6 frames. We dump ST-II and invalid types just below..
 	 */
 	 
-	if(iph->frag_off)
-	{
-		if (iph->frag_off & htons(IP_MF))
-			is_frag|=IPFWD_FRAGMENT;
-		/*
-		 *	Last fragment ?
-		 */
-	
-		if (iph->frag_off & htons(IP_OFFSET))
-			is_frag|=IPFWD_LASTFRAG;
-	}
-	
+	if(iph->version == 6)
+		return ipv6_rcv(skb,dev,pt);
 #endif
+
 	/*
-	 *	Do any IP forwarding required.  chk_addr() is expensive -- avoid it someday.
-	 *
-	 *	This is inefficient. While finding out if it is for us we could also compute
-	 *	the routing table entry. This is where the great unified cache theory comes
-	 *	in as and when someone implements it
-	 *
-	 *	For most hosts over 99% of packets match the first conditional
-	 *	and don't go via ip_chk_addr. Note: brd is set to IS_MYADDR at
-	 *	function entry.
-	 */
-	daddr = iph->daddr;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-	/*
-	 *	ip_chksock adds still more overhead for forwarded traffic...
+	 * When interface is in promisc. mode, drop all the crap
+	 * that it receives, do not truing to analyse it.
 	 */
-	if ( iph->daddr == skb->dev->pa_addr || skb->redirport || (brd = ip_chk_addr(iph->daddr)) != 0 || ip_chksock(skb))
-#else
-	if ( iph->daddr == skb->dev->pa_addr || (brd = ip_chk_addr(iph->daddr)) != 0)
-#endif
-	{
-		if (opt && opt->srr) 
-	        {
-			int srrspace, srrptr;
-			__u32 nexthop;
-			unsigned char * optptr = ((unsigned char *)iph) + opt->srr;
-
-			if (brd != IS_MYADDR || skb->pkt_type != PACKET_HOST) 
-			{
-				kfree_skb(skb, FREE_WRITE);
-				return 0;
-			}
-
-			for ( srrptr=optptr[2], srrspace = optptr[1];
-			      srrptr <= srrspace;
-			      srrptr += 4
-			     ) 
-			{
-				int brd2;
-				if (srrptr + 3 > srrspace) 
-				{
-					icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2,
-						  skb->dev);
-					kfree_skb(skb, FREE_WRITE);
-					return 0;
-				}
-				memcpy(&nexthop, &optptr[srrptr-1], 4);
-				if ((brd2 = ip_chk_addr(nexthop)) == 0)
-					break;
-				if (brd2 != IS_MYADDR) 
-				{
-
-					/*
-					 *	ANK: should we implement weak tunneling of multicasts?
-					 *	Are they obsolete? DVMRP specs (RFC-1075) is old enough...
-					 *	[They are obsolete]
-					 */
-					kfree_skb(skb, FREE_WRITE);
-					return -EINVAL;
-				}
-				memcpy(&daddr, &optptr[srrptr-1], 4);
-			}
-			if (srrptr <= srrspace) 
-			{
-				opt->srr_is_hit = 1;
-				opt->is_changed = 1;
-#ifdef CONFIG_IP_FORWARD
-				if (ip_forward(skb, dev, is_frag, nexthop))
-					kfree_skb(skb, FREE_WRITE);
-#else
-				ip_statistics.IpInAddrErrors++;
-				kfree_skb(skb, FREE_WRITE);
-#endif
-				return 0;
-			}
-		}
+	if (skb->pkt_type == PACKET_OTHERHOST)
+		goto drop;
 
-#ifdef CONFIG_IP_MULTICAST	
-		if(!(dev->flags&IFF_ALLMULTI) && brd==IS_MULTICAST && iph->daddr!=IGMP_ALL_HOSTS && !(dev->flags&IFF_LOOPBACK))
-		{
-			/*
-			 *	Check it is for one of our groups
-			 */
-			struct ip_mc_list *ip_mc=dev->ip_mc_list;
-			do
-			{
-				if(ip_mc==NULL)
-				{	
-					kfree_skb(skb, FREE_WRITE);
-					return 0;
-				}
-				if(ip_mc->multiaddr==iph->daddr)
-					break;
-				ip_mc=ip_mc->next;
-			}
-			while(1);
-		}
-#endif
-
-#ifndef CONFIG_IP_ALWAYS_DEFRAG
-		/*
-		 *	Reassemble IP fragments.
-		 */
+	ip_statistics.IpInReceives++;
 
-		if(is_frag)
-		{
-			/* Defragment. Obtain the complete packet if there is one */
-			skb=ip_defrag(iph,skb,dev);
-			if(skb==NULL)
-				return 0;
-			skb->dev = dev;
-			iph=skb->h.iph;
-		}
+	/*
+	 *	Account for the packet (even if the packet is
+	 *	not accepted by the firewall!).
+	 */
 
-#endif
+#ifdef CONFIG_IP_ACCT
+	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_IN);
+#endif	
 
-#ifdef CONFIG_IP_MASQUERADE
-		/*
-		 * Do we need to de-masquerade this packet?
-		 */
-		{
-			int ret = ip_fw_demasquerade(&skb,dev);
-			if (ret < 0) {
-				kfree_skb(skb, FREE_WRITE);
-				return 0;
-			}
+	/*
+	 *	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
+	 *
+	 *	Is the datagram acceptable?
+	 *
+	 *	1.	Length at least the size of an ip header
+	 *	2.	Version of 4
+	 *	3.	Checksums correctly. [Speed optimisation for later, skip loopback checksums]
+	 *	4.	Doesn't have a bogus length
+	 */
 
-			if (ret)
-			{
-				struct iphdr *iph=skb->h.iph;
-				if (ip_forward(skb, dev, IPFWD_MASQUERADED, iph->daddr))
-					kfree_skb(skb, FREE_WRITE);
-				return 0;
-			}
-		}
-#endif
+	if (skb->len<sizeof(struct iphdr) || iph->ihl<5 || iph->version != 4
+	    || ip_fast_csum((unsigned char *)iph, iph->ihl) !=0
+		|| skb->len < ntohs(iph->tot_len))
+		goto inhdr_error;
 
-		/*
-		 *	Point into the IP datagram, just past the header.
-		 */
+	/*
+	 *	Our transport medium may have padded the buffer out. Now we know it
+	 *	is IP we can trim to the true length of the frame.
+	 *	Note this now means skb->len holds ntohs(iph->tot_len).
+	 */
 
-		skb->ip_hdr = iph;
-		skb->h.raw += iph->ihl*4;
+	skb_trim(skb, ntohs(iph->tot_len));
 
-#ifdef CONFIG_IP_MROUTE		
-		/*
-		 *	Check the state on multicast routing (multicast and not 224.0.0.z)
-		 */
-		 
-		if(brd==IS_MULTICAST && (iph->daddr&htonl(0xFFFFFF00))!=htonl(0xE0000000))
-			mroute_pkt=1;
+	if (skb->dst == NULL) {
+		err = ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev);
+		if (err)
+			goto drop;
+	}
 
+#ifdef CONFIG_IP_ALWAYS_DEFRAG
+	if (iph->frag_off & htons(IP_MF|IP_OFFSET)) {
+		skb = ip_defrag(skb);
+		if (!skb)
+			return 0;
+		iph = skb->nh.iph;
+		ip_send_check(iph);
+	}
 #endif
-		/*
-		 *	Deliver to raw sockets. This is fun as to avoid copies we want to make no surplus copies.
-		 *
-		 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
-		 */
- 
-		hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
-
-		/* 
-		 *	If there maybe a raw socket we must check - if not we don't care less 
-		 */
-		 
-		if((raw_sk=raw_prot.sock_array[hash])!=NULL)
-		{
-			struct sock *sknext=NULL;
-			struct sk_buff *skb1;
-			raw_sk=get_sock_raw(raw_sk, iph->protocol,  iph->saddr, iph->daddr);
-			if(raw_sk)	/* Any raw sockets */
-			{
-				do
-				{
-					/* Find the next */
-					sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
-					if(sknext)
-						skb1=skb_clone(skb, GFP_ATOMIC);
-					else
-						break;	/* One pending raw socket left */
-					if(skb1)
-						raw_rcv(raw_sk, skb1, dev, iph->saddr,daddr);
-					raw_sk=sknext;
-				}
-				while(raw_sk!=NULL);
-				
-				/*
-				 *	Here either raw_sk is the last raw socket, or NULL if none 
-				 */
-				 
-				/*
-				 *	We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy 
-				 */
-			}
-		}
-	
-		/*
-		 *	skb->h.raw now points at the protocol beyond the IP header.
-		 */
-	
-		hash = iph->protocol & (MAX_INET_PROTOS -1);
-		for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
-		{
-			struct sk_buff *skb2;
-	
-			if (ipprot->protocol != iph->protocol)
-				continue;
-		       /*
-			* 	See if we need to make a copy of it.  This will
-			* 	only be set if more than one protocol wants it.
-			* 	and then not for the last one. If there is a pending
-			*	raw delivery wait for that
-			*/
-	
-#ifdef CONFIG_IP_MROUTE
-			if (ipprot->copy || raw_sk || mroute_pkt)
-#else	
-			if (ipprot->copy || raw_sk)
-#endif			
-			{
-				skb2 = skb_clone(skb, GFP_ATOMIC);
-				if(skb2==NULL)
-					continue;
-			}
-			else
-			{
-				skb2 = skb;
-			}
-			flag = 1;
-
-		       /*
-			*	Pass on the datagram to each protocol that wants it,
-			*	based on the datagram protocol.  We should really
-			*	check the protocol handler's return values here...
-			*/
-
-			ipprot->handler(skb2, dev, opt, daddr,
-				(ntohs(iph->tot_len) - (iph->ihl * 4)),
-				iph->saddr, 0, ipprot);
-		}
 
-		/*
-		 *	All protocols checked.
-		 *	If this packet was a broadcast, we may *not* reply to it, since that
-		 *	causes (proven, grin) ARP storms and a leakage of memory (i.e. all
-		 *	ICMP reply messages get queued up for transmission...)
-		 */
+	if (iph->ihl > 5) {
+		skb->ip_summed = 0;
+		if (ip_options_compile(NULL, skb))
+			goto inhdr_error;
 
-#ifdef CONFIG_IP_MROUTE		 
-		/*
-		 *	Forward the last copy to the multicast router. If
-		 *	there is a pending raw delivery however make a copy
-		 *	and forward that.
-		 */
-		 
-		if(mroute_pkt)
-		{
-			flag=1;
-			if(raw_sk==NULL)
-				ipmr_forward(skb, is_frag);
-			else
-			{
-				struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
-				if(skb2)
-				{
-					skb2->free=1;
-					ipmr_forward(skb2, is_frag);
-				}
+		opt = &(IPCB(skb)->opt);
+		if (opt->srr) {
+			if (!ipv4_config.source_route) {
+				if (ipv4_config.log_martians)
+					printk(KERN_INFO "source route option %08lx -> %08lx\n",
+				       ntohl(iph->saddr), ntohl(iph->daddr));
+				goto drop;
 			}
+			if (RT_LOCALADDR(((struct rtable*)skb->dst)->rt_flags) &&
+			    ip_options_rcv_srr(skb))
+				goto drop;
 		}
-#endif		
-
-		if(raw_sk!=NULL)	/* Shift to last raw user */
-			raw_rcv(raw_sk, skb, dev, iph->saddr, daddr);
-		else if (!flag)		/* Free and report errors */
-		{
-			if (brd != IS_BROADCAST && brd!=IS_MULTICAST)
-				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PROT_UNREACH, 0, dev);	
-			kfree_skb(skb, FREE_WRITE);
-		}
-
-		return(0);
 	}
-
-	/*
-	 *	Do any unicast IP forwarding required.
-	 */
 	
 	/*
-	 *	Don't forward multicast or broadcast frames.
+	 *	See if the firewall wants to dispose of the packet. 
 	 */
+	
+#ifdef	CONFIG_FIREWALL
+        {
+		int fwres;
+		u16 rport;
+
+		if ((fwres=call_in_firewall(PF_INET, skb->dev, iph, &rport))<FW_ACCEPT) {
+			if (fwres==FW_REJECT)
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+			goto drop;
+		}
 
-	if(skb->pkt_type!=PACKET_HOST || brd==IS_BROADCAST)
-	{
-		kfree_skb(skb,FREE_WRITE);
-		return 0;
+#ifdef	CONFIG_IP_TRANSPARENT_PROXY
+		if (fwres==FW_REDIRECT && (IPCB(skb)->redirport = rport) != 0)
+			return ip_local_deliver(skb);
+#endif
 	}
+#endif
 
-	/*
-	 *	The packet is for another target. Forward the frame
-	 */
+	return skb->dst->input(skb);
 
-#ifdef CONFIG_IP_FORWARD
-	if (opt && opt->is_strictroute) 
-	{
-	      icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev);
-	      kfree_skb(skb, FREE_WRITE);
-	      return -1;
-	}
-	if (ip_forward(skb, dev, is_frag, iph->daddr))
-		kfree_skb(skb, FREE_WRITE);
-#else
-/*	printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
-			iph->saddr,iph->daddr);*/
-	ip_statistics.IpInAddrErrors++;
-	kfree_skb(skb, FREE_WRITE);
-#endif
-	return(0);
+inhdr_error:
+	ip_statistics.IpInHdrErrors++;
+drop:
+        kfree_skb(skb, FREE_WRITE);
+        return(0);
 }
-	
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v2.1.14/linux/net/ipv4/ip_masq.c	Sun Nov 10 20:12:29 1996
+++ linux/net/ipv4/ip_masq.c	Thu Dec 12 16:54:24 1996
@@ -455,7 +455,7 @@
 int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
 {
 	struct sk_buff  *skb=*skb_ptr;
-	struct iphdr	*iph = skb->h.iph;
+	struct iphdr	*iph = skb->nh.iph;
 	__u16	*portptr;
 	struct ip_masq	*ms;
 	int		size;
@@ -504,7 +504,7 @@
  	 *	Change the fragments origin
  	 */
  	
- 	size = skb->len - ((unsigned char *)portptr - skb->h.raw);
+ 	size = skb->len - ((unsigned char *)portptr - skb->nh.raw);
         /*
          *	Set iph addr and port from ip_masq obj.
          */
@@ -521,9 +521,9 @@
                  *	skb has possibly changed, update pointers.
                  */
                 skb = *skb_ptr;
-                iph = skb->h.iph;
+                iph = skb->nh.iph;
                 portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
-                size = skb->len - ((unsigned char *)portptr-skb->h.raw);
+                size = skb->len - ((unsigned char *)portptr-skb->nh.raw);
         }
 
  	/*
@@ -566,8 +566,7 @@
  		else timeout = ip_masq_expire->tcp_timeout;
 
 		skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0);
-		tcp_v4_check(th, size, iph->saddr, iph->daddr,
-			     skb->csum);
+		tcp_v4_check(th, size, iph->saddr, iph->daddr, skb->csum);
  	}
         ip_masq_set_expire(ms, timeout);
  	ip_send_check(iph);
@@ -590,7 +589,7 @@
 int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
 {
         struct sk_buff 	*skb   = *skb_p;
- 	struct iphdr	*iph   = skb->h.iph;
+ 	struct iphdr	*iph   = skb->nh.iph;
 	struct icmphdr  *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
 	struct iphdr    *ciph;	/* The ip header contained within the ICMP */
 	__u16	        *pptr;	/* port numbers from TCP/UDP contained header */
@@ -689,7 +688,7 @@
 int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
 {
         struct sk_buff 	*skb   = *skb_p;
- 	struct iphdr	*iph   = skb->h.iph;
+ 	struct iphdr	*iph   = skb->nh.iph;
 	struct icmphdr  *icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
 	struct iphdr    *ciph;	/* The ip header contained within the ICMP */
 	__u16	        *pptr;	/* port numbers from TCP/UDP contained header */
@@ -782,7 +781,7 @@
 int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
 {
         struct sk_buff 	*skb = *skb_p;
- 	struct iphdr	*iph = skb->h.iph;
+ 	struct iphdr	*iph = skb->nh.iph;
  	__u16	*portptr;
  	struct ip_masq	*ms;
 	unsigned short len;
@@ -877,7 +876,7 @@
                          */
 
                         skb = *skb_p;
-                        iph = skb->h.iph;
+                        iph = skb->nh.iph;
                         portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
                         len = ntohs(iph->tot_len) - (iph->ihl * 4);
                 }
@@ -904,7 +903,6 @@
 				     skb->csum);
 			
 			/* Check if TCP FIN or RST */
-			
 			if (th->fin)
 			{
 				ms->flags |= IP_MASQ_F_SAW_FIN_IN;
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_masq_app.c linux/net/ipv4/ip_masq_app.c
--- v2.1.14/linux/net/ipv4/ip_masq_app.c	Thu Oct 10 19:10:58 1996
+++ linux/net/ipv4/ip_masq_app.c	Thu Dec 12 16:54:24 1996
@@ -327,7 +327,7 @@
         if ( (mapp = ms->app) == NULL)
                 return 0;
         
-        iph = (*skb_p)->h.iph;
+        iph = (*skb_p)->nh.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
         
         /*
@@ -390,7 +390,7 @@
         if ( (mapp = ms->app) == NULL)
                 return 0;
         
-        iph = (*skb_p)->h.iph;
+        iph = (*skb_p)->nh.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
         
         /*
@@ -488,7 +488,7 @@
 
 int ip_masq_app_init(void)
 {
-        
+
         register_symtab (&ip_masq_app_syms);
 #ifdef CONFIG_PROC_FS        
 	proc_net_register(&proc_net_ip_masq_app);
@@ -539,7 +539,6 @@
                         return skb;
 
                 }
-                n_skb->free = skb->free;
                 skb_reserve(n_skb, MAX_HEADER);
                 skb_put(n_skb, skb->len + diff);
 
@@ -549,14 +548,15 @@
                  *	like skb->protocol (PPP driver wants it).
                  */
                 offset = n_skb->data - skb->data;
+                n_skb->nh.raw = skb->nh.raw + offset;
                 n_skb->h.raw = skb->h.raw + offset;
                 n_skb->when = skb->when;
                 n_skb->dev = skb->dev;
                 n_skb->mac.raw = skb->mac.raw + offset;
-                n_skb->ip_hdr = (struct iphdr *)(((char *)skb->ip_hdr)+offset);
                 n_skb->pkt_type = skb->pkt_type;
                 n_skb->protocol = skb->protocol;
                 n_skb->ip_summed = skb->ip_summed;
+		n_skb->dst = dst_clone(skb->dst);
 
                 /*
                  * Copy pkt in new buffer
@@ -600,7 +600,7 @@
                 /*
                  * 	update ip header
                  */
-                iph = n_skb->h.iph;
+                iph = n_skb->nh.iph;
                 iph->check = 0;
                 iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
                 iph->tot_len = htons(skb_len + diff);
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_masq_ftp.c linux/net/ipv4/ip_masq_ftp.c
--- v2.1.14/linux/net/ipv4/ip_masq_ftp.c	Mon Apr 22 11:27:00 1996
+++ linux/net/ipv4/ip_masq_ftp.c	Thu Dec 12 16:54:24 1996
@@ -63,7 +63,7 @@
 	int diff;
 
         skb = *skb_p;
-	iph = skb->h.iph;
+	iph = skb->nh.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
         data = (char *)&th[1];
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_masq_irc.c linux/net/ipv4/ip_masq_irc.c
--- v2.1.14/linux/net/ipv4/ip_masq_irc.c	Mon Apr 22 11:27:00 1996
+++ linux/net/ipv4/ip_masq_irc.c	Thu Dec 12 16:54:24 1996
@@ -65,7 +65,7 @@
         char *dcc_p, *addr_beg_p, *addr_end_p;
 
         skb = *skb_p;
-	iph = skb->h.iph;
+	iph = skb->nh.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
         data = (char *)&th[1];
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_masq_raudio.c linux/net/ipv4/ip_masq_raudio.c
--- v2.1.14/linux/net/ipv4/ip_masq_raudio.c	Fri May 31 13:46:27 1996
+++ linux/net/ipv4/ip_masq_raudio.c	Thu Dec 12 16:54:24 1996
@@ -102,7 +102,7 @@
 		return 0;
 
         skb = *skb_p;
-	iph = skb->h.iph;
+	iph = skb->nh.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
         data = (char *)&th[1];
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_nat_dumb.c linux/net/ipv4/ip_nat_dumb.c
--- v2.1.14/linux/net/ipv4/ip_nat_dumb.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/ip_nat_dumb.c	Thu Dec 12 16:54:24 1996
@@ -0,0 +1,77 @@
+/*
+ * INET		An implementation of the TCP/IP protocol suite for the LINUX
+ *		operating system.  INET is implemented using the  BSD Socket
+ *		interface as the means of communication with the user level.
+ *
+ *		Dumb Network Address Translation.
+ *
+ * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ *		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.
+ *
+ *
+ * NOTE:	It is just working model of real NAT.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <linux/netdevice.h>
+#include <net/sock.h>
+#include <net/ip.h>
+#include <net/icmp.h>
+#include <linux/tcp.h>
+#include <linux/udp.h>
+#include <linux/firewall.h>
+#include <linux/ip_fw.h>
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+#include <net/checksum.h>
+#include <linux/route.h>
+#include <net/route.h>
+
+
+int
+ip_do_nat(struct sk_buff *skb)
+{
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct iphdr *iph = skb->nh.iph;
+	u32 odaddr = iph->daddr;
+	u32 osaddr = iph->saddr;
+	u16	check;
+	u16	*cksum = NULL;
+
+	IPCB(skb)->flags |= IPSKB_TRANSLATED;
+
+	/* Rewrite IP header */
+	iph->daddr = rt->rt_dst_map;
+	iph->saddr = rt->rt_src_map;
+	iph->check = 0;
+	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
+
+	/* If it is the first fragment, rewrite protocol headers */
+
+	if (!(iph->frag_off & htons(IP_OFFSET))) {
+		/* Only plain TCP/UDP headers rewriting is implemented :-( */
+		if (iph->protocol == IPPROTO_TCP)
+			cksum = (u16*)&((struct tcphdr*)(((char*)iph) + iph->ihl*4))->check;
+		else if (iph->protocol == IPPROTO_UDP)
+			cksum = (u16*)&((struct udphdr*)(((char*)iph) + iph->ihl*4))->check;
+		if (cksum && (check = *cksum) != 0) {
+			check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check);
+			check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+			if (!check)
+				check = 0xFFFF;
+			*cksum = check;
+		}
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v2.1.14/linux/net/ipv4/ip_options.c	Thu Apr 18 09:47:39 1996
+++ linux/net/ipv4/ip_options.c	Thu Dec 12 16:54:24 1996
@@ -10,6 +10,7 @@
  */
 
 #include <linux/types.h>
+#include <asm/uaccess.h>
 #include <linux/skbuff.h>
 #include <linux/ip.h>
 #include <linux/icmp.h>
@@ -17,6 +18,7 @@
 #include <net/sock.h>
 #include <net/ip.h>
 #include <net/icmp.h>
+#include <linux/net_alias.h>
 
 /* 
  * Write options to IP header, record destination address to
@@ -24,30 +26,30 @@
  * (we should already know it, so that this  function is allowed be
  * called only after routing decision) and timestamp,
  * if we originate this datagram.
+ *
+ * daddr is real destination address, next hop is recorded in IP header.
+ * saddr is address of outgoing interface.
  */
 
-void ip_options_build(struct sk_buff * skb, struct options * opt,
-			    __u32 daddr, __u32 saddr,
-			    int is_frag) 
+void ip_options_build(struct sk_buff * skb, struct ip_options * opt,
+			    u32 daddr, u32 saddr, int is_frag) 
 {
-	unsigned char * iph = (unsigned char*)skb->ip_hdr;
+	unsigned char * iph = skb->nh.raw;
 
-	memcpy(skb->proto_priv, opt, sizeof(struct options));
+	memcpy(&(IPCB(skb)->opt), opt, sizeof(struct ip_options));
 	memcpy(iph+sizeof(struct iphdr), opt->__data, opt->optlen);
-	opt = (struct options*)skb->proto_priv;
+	opt = &(IPCB(skb)->opt);
 	opt->is_data = 0;
 
 	if (opt->srr)
 		memcpy(iph+opt->srr+iph[opt->srr+1]-4, &daddr, 4);
 
-	if (!is_frag) 
-	{
+	if (!is_frag) {
 		if (opt->rr_needaddr)
 			memcpy(iph+opt->rr+iph[opt->rr+2]-5, &saddr, 4);
 		if (opt->ts_needaddr)
 			memcpy(iph+opt->ts+iph[opt->ts+2]-9, &saddr, 4);
-		if (opt->ts_needtime) 
-		{
+		if (opt->ts_needtime) {
 			struct timeval tv;
 			__u32 midtime;
 			do_gettimeofday(&tv);
@@ -56,88 +58,96 @@
 		}
 		return;
 	}
-	if (opt->rr) 
-	{
+	if (opt->rr) {
 		memset(iph+opt->rr, IPOPT_NOP, iph[opt->rr+1]);
 		opt->rr = 0;
 		opt->rr_needaddr = 0;
 	}
-	if (opt->ts) 
-	{
+	if (opt->ts) {
 		memset(iph+opt->ts, IPOPT_NOP, iph[opt->ts+1]);
 		opt->ts = 0;
 		opt->ts_needaddr = opt->ts_needtime = 0;
 	}
 }
 
-int ip_options_echo(struct options * dopt, struct options * sopt,
-		     __u32 daddr, __u32 saddr,
-		     struct sk_buff * skb) 
+/* 
+ * Provided (sopt, skb) points to received options,
+ * build in dopt compiled option set appropriate for answering.
+ * i.e. invert SRR option, copy anothers,
+ * and grab room in RR/TS options.
+ *
+ * NOTE: dopt cannot point to skb.
+ */
+
+int ip_options_echo(struct ip_options * dopt, struct sk_buff * skb) 
 {
+	struct ip_options *sopt;
 	unsigned char *sptr, *dptr;
 	int soffset, doffset;
 	int	optlen;
+	u32	daddr;
 
-	memset(dopt, 0, sizeof(struct options));
+#if 111
+	if (skb == NULL) {
+		printk(KERN_DEBUG "no skb in ip_options_echo\n");
+		return -EINVAL;
+	}
+#endif
+	memset(dopt, 0, sizeof(struct ip_options));
 
 	dopt->is_data = 1;
 
-	if (!sopt)
-		sopt = (struct options*)skb->proto_priv;
+	sopt = &(IPCB(skb)->opt);
 
-	if (sopt->optlen == 0) 
-	{
+	if (sopt->optlen == 0) {
 		dopt->optlen = 0;
 		return 0;
 	}
 
-	sptr = (sopt->is_data ? sopt->__data - sizeof(struct iphdr) :
-		(unsigned char *)skb->ip_hdr);
+	sptr = skb->nh.raw;
 	dptr = dopt->__data;
 
-	if (sopt->rr) 
-	{
+	if (skb->dst)
+		daddr = ((struct rtable*)skb->dst)->rt_spec_dst;
+	else
+		daddr = skb->nh.iph->daddr;
+
+	if (sopt->rr) {
 		optlen  = sptr[sopt->rr+1];
 		soffset = sptr[sopt->rr+2];
 		dopt->rr = dopt->optlen + sizeof(struct iphdr);
 		memcpy(dptr, sptr+sopt->rr, optlen);
 		if (sopt->rr_needaddr && soffset <= optlen) {
 			if (soffset + 3 > optlen)
-			  return -EINVAL;
+				return -EINVAL;
 			dptr[2] = soffset + 4;
 			dopt->rr_needaddr = 1;
 		}
-		dptr 	 += optlen;
+		dptr += optlen;
 		dopt->optlen += optlen;
 	}
-	if (sopt->ts) 
-	{
+	if (sopt->ts) {
 		optlen = sptr[sopt->ts+1];
 		soffset = sptr[sopt->ts+2];
 		dopt->ts = dopt->optlen + sizeof(struct iphdr);
 		memcpy(dptr, sptr+sopt->ts, optlen);
-		if (soffset <= optlen) 
-		{
-			if (sopt->ts_needaddr) 
-			{
+		if (soffset <= optlen) {
+			if (sopt->ts_needaddr) {
 				if (soffset + 3 > optlen)
 					return -EINVAL;
 				dopt->ts_needaddr = 1;
 				soffset += 4;
 			}
-			if (sopt->ts_needtime) 
-			{
+			if (sopt->ts_needtime) {
 				if (soffset + 3 > optlen)
 					return -EINVAL;
 				dopt->ts_needtime = 1;
 				soffset += 4;
 			}
-			if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) 
-			{
+			if (((struct timestamp*)(dptr+1))->flags == IPOPT_TS_PRESPEC) {
 				__u32 addr;
 				memcpy(&addr, sptr+soffset-9, 4);
-				if (ip_chk_addr(addr) == 0) 
-				{
+				if (__ip_chk_addr(addr) == 0) {
 					dopt->ts_needtime = 0;
 					dopt->ts_needaddr = 0;
 					soffset -= 8;
@@ -148,10 +158,9 @@
 		dptr += optlen;
 		dopt->optlen += optlen;
 	}
-	if (sopt->srr) 
-	{
+	if (sopt->srr) {
 		unsigned char * start = sptr+sopt->srr;
-		__u32 faddr;
+		u32 faddr;
 
 		optlen  = start[1];
 		soffset = start[2];
@@ -159,19 +168,17 @@
 		if (soffset > optlen)
 			soffset = optlen + 1;
 		soffset -= 4;
-		if (soffset > 3) 
-		{
+		if (soffset > 3) {
 			memcpy(&faddr, &start[soffset-1], 4);
 			for (soffset-=4, doffset=4; soffset > 3; soffset-=4, doffset+=4)
 				memcpy(&dptr[doffset-1], &start[soffset-1], 4);
 			/*
 			 * RFC1812 requires to fix illegal source routes.
 			 */
-			if (memcmp(&saddr, &start[soffset+3], 4) == 0)
+			if (memcmp(&skb->nh.iph->saddr, &start[soffset+3], 4) == 0)
 				doffset -= 4;
 		}
-		if (doffset > 3) 
-		{
+		if (doffset > 3) {
 			memcpy(&start[doffset-1], &daddr, 4);
 			dopt->faddr = faddr;
 			dptr[0] = start[0];
@@ -183,28 +190,31 @@
 			dopt->is_strictroute = sopt->is_strictroute;
 		}
 	}
-	while (dopt->optlen & 3) 
-	{
+	while (dopt->optlen & 3) {
 		*dptr++ = IPOPT_END;
 		dopt->optlen++;
 	}
 	return 0;
 }
 
+/*
+ *	Options "fragmenting", just fill options not
+ *	allowed in fragments with NOOPs.
+ *	Simple and stupid 8), but the most efficient way.
+ */
+
 void ip_options_fragment(struct sk_buff * skb) 
 {
-	unsigned char * optptr = (unsigned char*)skb->ip_hdr;
-	struct options * opt = (struct options*)skb->proto_priv;
+	unsigned char * optptr = skb->nh.raw;
+	struct ip_options * opt = &(IPCB(skb)->opt);
 	int  l = opt->optlen;
 	int  optlen;
 
-	while (l > 0) 
-	{
-		switch (*optptr) 
-		{
-		      case IPOPT_END:
+	while (l > 0) {
+		switch (*optptr) {
+		case IPOPT_END:
 			return;
-		      case IPOPT_NOOP:
+		case IPOPT_NOOP:
 			l--;
 			optptr++;
 			continue;
@@ -212,7 +222,7 @@
 		optlen = optptr[1];
 		if (optlen<2 || optlen>l)
 		  return;
-		if (!(*optptr & 0x80))
+		if (!IPOPT_COPIED(*optptr))
 			memset(optptr, IPOPT_NOOP, optlen);
 		l -= optlen;
 		optptr += optlen;
@@ -231,7 +241,7 @@
  * If opt == NULL, then skb->data should point to IP header.
  */
 
-int ip_options_compile(struct options * opt, struct sk_buff * skb)
+int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
 {
 	int l;
 	unsigned char * iph;
@@ -239,30 +249,23 @@
 	int optlen;
 	unsigned char * pp_ptr = NULL;
 
-	if (!opt) 
-	{
-		opt = (struct options*)skb->proto_priv;
-		memset(opt, 0, sizeof(struct options));
-		iph = (unsigned char*)skb->ip_hdr;
+	if (!opt) {
+		opt = &(IPCB(skb)->opt);
+		memset(opt, 0, sizeof(struct ip_options));
+		iph = skb->nh.raw;
 		opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);
 		optptr = iph + sizeof(struct iphdr);
 		opt->is_data = 0;
-	}
-	else 
-	{
-		optptr = opt->is_data ? opt->__data : (unsigned char*)&skb->ip_hdr[1];
+	} else {
+		optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]);
 		iph = optptr - sizeof(struct iphdr);
 	}
 
-	for (l = opt->optlen; l > 0; ) 
-	{
-		switch (*optptr) 
-		{
+	for (l = opt->optlen; l > 0; ) {
+		switch (*optptr) {
 		      case IPOPT_END:
-			for (optptr++, l--; l>0; l--) 
-			{
-				if (*optptr != IPOPT_END) 
-				{
+			for (optptr++, l--; l>0; l--) {
+				if (*optptr != IPOPT_END) {
 					*optptr = IPOPT_END;
 					opt->is_changed = 1;
 				}
@@ -274,35 +277,28 @@
 			continue;
 		}
 		optlen = optptr[1];
-		if (optlen<2 || optlen>l)
-		{
+		if (optlen<2 || optlen>l) {
 			pp_ptr = optptr;
 			goto error;
 		}
-		switch (*optptr) 
-		{
+		switch (*optptr) {
 		      case IPOPT_SSRR:
 		      case IPOPT_LSRR:
-			if (optlen < 3) 
-			{
+			if (optlen < 3) {
 				pp_ptr = optptr + 1;
 				goto error;
 			}
-			if (optptr[2] < 4) 
-			{
+			if (optptr[2] < 4) {
 				pp_ptr = optptr + 2;
 				goto error;
 			}
 			/* NB: cf RFC-1812 5.2.4.1 */
-			if (opt->srr) 
-			{
+			if (opt->srr) {
 				pp_ptr = optptr;
 				goto error;
 			}
-			if (!skb) 
-			{
-				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) 
-				{
+			if (!skb) {
+				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
 					pp_ptr = optptr + 1;
 					goto error;
 				}
@@ -314,30 +310,24 @@
 			opt->srr = optptr - iph;
 			break;
 		      case IPOPT_RR:
-			if (opt->rr) 
-			{
+			if (opt->rr) {
 				pp_ptr = optptr;
 				goto error;
 			}
-			if (optlen < 3) 
-			{
+			if (optlen < 3) {
 				pp_ptr = optptr + 1;
 				goto error;
 			}
-			if (optptr[2] < 4) 
-			{
+			if (optptr[2] < 4) {
 				pp_ptr = optptr + 2;
 				goto error;
 			}
-			if (optptr[2] <= optlen) 
-			{
-				if (optptr[2]+3 > optlen) 
-				{
+			if (optptr[2] <= optlen) {
+				if (optptr[2]+3 > optlen) {
 					pp_ptr = optptr + 2;
 					goto error;
 				}
-				if (skb) 
-				{
+				if (skb) {
 					memcpy(&optptr[optptr[2]-1], &skb->dev->pa_addr, 4);
 					opt->is_changed = 1;
 				}
@@ -347,32 +337,26 @@
 			opt->rr = optptr - iph;
 			break;
 		      case IPOPT_TIMESTAMP:
-			if (opt->ts) 
-			{
+			if (opt->ts) {
 				pp_ptr = optptr;
 				goto error;
 			}
-			if (optlen < 4) 
-			{
+			if (optlen < 4) {
 				pp_ptr = optptr + 1;
 				goto error;
 			}
-			if (optptr[2] < 5) 
-			{
+			if (optptr[2] < 5) {
 				pp_ptr = optptr + 2;
 				goto error;
 			}
-			if (optptr[2] <= optlen) 
-			{
+			if (optptr[2] <= optlen) {
 				struct timestamp * ts = (struct timestamp*)(optptr+1);
 				__u32 * timeptr = NULL;
-				if (ts->ptr+3 > ts->len) 
-				{
+				if (ts->ptr+3 > ts->len) {
 					pp_ptr = optptr + 2;
 					goto error;
 				}
-				switch (ts->flags) 
-				{
+				switch (ts->flags) {
 				      case IPOPT_TS_TSONLY:
 					opt->ts = optptr - iph;
 					if (skb) 
@@ -381,14 +365,12 @@
 					ts->ptr += 4;
 					break;
 				      case IPOPT_TS_TSANDADDR:
-					if (ts->ptr+7 > ts->len) 
-					{
+					if (ts->ptr+7 > ts->len) {
 						pp_ptr = optptr + 2;
 						goto error;
 					}
 					opt->ts = optptr - iph;
-					if (skb) 
-					{
+					if (skb) {
 						memcpy(&optptr[ts->ptr-1], &skb->dev->pa_addr, 4);
 						timeptr = (__u32*)&optptr[ts->ptr+3];
 					}
@@ -397,16 +379,15 @@
 					ts->ptr += 8;
 					break;
 				      case IPOPT_TS_PRESPEC:
-					if (ts->ptr+7 > ts->len) 
-					{
+					if (ts->ptr+7 > ts->len) {
 						pp_ptr = optptr + 2;
 						goto error;
 					}
 					opt->ts = optptr - iph;
 					{
-						__u32 addr;
+						u32 addr;
 						memcpy(&addr, &optptr[ts->ptr-1], 4);
-						if (ip_chk_addr(addr) == 0)
+						if (__ip_chk_addr(addr) == 0)
 							break;
 						if (skb)
 							timeptr = (__u32*)&optptr[ts->ptr+3];
@@ -419,8 +400,7 @@
 					pp_ptr = optptr + 3;
 					goto error;
 				}
-				if (timeptr) 
-				{
+				if (timeptr) {
 					struct timeval tv;
 					__u32  midtime;
 					do_gettimeofday(&tv);
@@ -428,28 +408,31 @@
 					memcpy(timeptr, &midtime, sizeof(__u32));
 					opt->is_changed = 1;
 				}
-			} 
-			else 
-			{
+			} else {
 				struct timestamp * ts = (struct timestamp*)(optptr+1);
-				if (ts->overflow == 15) 
-				{
+				if (ts->overflow == 15) {
 					pp_ptr = optptr + 3;
 					goto error;
 				}
 				opt->ts = optptr - iph;
-				if (skb) 
-				{
+				if (skb) {
 					ts->overflow++;
 					opt->is_changed = 1;
 				}
 			}
 			break;
+		      case IPOPT_RA:
+			if (optlen < 4) {
+				pp_ptr = optptr + 1;
+				goto error;
+			}
+			if (optptr[2] == 0 && optptr[3] == 0)
+				opt->router_alert = optptr - iph;
+			break;
 		      case IPOPT_SEC:
 		      case IPOPT_SID:
 		      default:
-			if (!skb) 
-			{
+			if (!skb) {
 				pp_ptr = optptr;
 				goto error;
 			}
@@ -464,11 +447,162 @@
 		return 0;
 
 error:
-	if (skb) 
-	{
-		icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph, skb->dev);
+	if (skb) {
+		icmp_send(skb, ICMP_PARAMETERPROB, 0, pp_ptr-iph);
 		kfree_skb(skb, FREE_READ);
 	}
 	return -EINVAL;
 }
 
+
+/*
+ *	Undo all the changes done by ip_options_compile().
+ */
+
+void ip_options_undo(struct ip_options * opt)
+{
+	if (opt->srr) {
+		unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
+		memmove(optptr+7, optptr+3, optptr[1]-7);
+		memcpy(optptr+3, &opt->faddr, 4);
+	}
+	if (opt->rr_needaddr) {
+		unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
+		memset(&optptr[optptr[2]-1], 0, 4);
+		optptr[2] -= 4;
+	}
+	if (opt->ts) {
+		unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
+		if (opt->ts_needtime) {
+			memset(&optptr[optptr[2]-1], 0, 4);
+			optptr[2] -= 4;
+		}
+		if (opt->ts_needaddr) {
+			memset(&optptr[optptr[2]-1], 0, 4);
+			optptr[2] -= 4;
+		}
+	}
+}
+
+int ip_options_getfromuser(struct ip_options **optp, unsigned char *data, int optlen)
+{
+	struct ip_options *opt;
+	opt = kmalloc(sizeof(struct ip_options)+((optlen+3)&~3), GFP_KERNEL);
+	if (!opt)
+		return -ENOMEM;
+	memset(opt, 0, sizeof(struct ip_options));
+	if (optlen && copy_from_user(opt->__data, data, optlen))
+		return -EFAULT;
+	while (optlen & 3)
+		opt->__data[optlen++] = IPOPT_END;
+	opt->optlen = optlen;
+	opt->is_data = 1;
+	opt->is_setbyuser = 1;
+	if (optlen && ip_options_compile(opt, NULL)) {
+		kfree_s(opt, sizeof(struct options) + optlen);
+		return -EINVAL;
+	}
+	*optp = opt;
+	return 0;
+}
+
+void ip_forward_options(struct sk_buff *skb)
+{
+	struct   ip_options * opt	= &(IPCB(skb)->opt);
+	unsigned char * optptr;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	unsigned char *raw = skb->nh.raw;
+
+	if (opt->rr_needaddr) {
+		optptr = (unsigned char *)raw + opt->rr;
+		memcpy(&optptr[optptr[2]-5], &rt->u.dst.dev->pa_addr, 4);
+		opt->is_changed = 1;
+	}
+	if (opt->srr_is_hit) {
+		int srrptr, srrspace;
+
+		optptr = raw + opt->srr;
+
+		for ( srrptr=optptr[2], srrspace = optptr[1];
+		     srrptr <= srrspace;
+		     srrptr += 4
+		     ) {
+			if (srrptr + 3 > srrspace)
+				break;
+			if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0)
+				break;
+		}
+		if (srrptr + 3 <= srrspace) {
+			opt->is_changed = 1;
+			memcpy(&optptr[srrptr-1], &rt->u.dst.dev->pa_addr, 4);
+			skb->nh.iph->daddr = rt->rt_dst;
+			optptr[2] = srrptr+4;
+		} else
+			printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");
+		if (opt->ts_needaddr) {
+			optptr = raw + opt->ts;
+			memcpy(&optptr[optptr[2]-9], &rt->u.dst.dev->pa_addr, 4);
+			opt->is_changed = 1;
+		}
+		if (opt->is_changed) {
+			opt->is_changed = 0;
+			ip_send_check(skb->nh.iph);
+		}
+	}
+}
+
+int ip_options_rcv_srr(struct sk_buff *skb)
+{
+	struct ip_options *opt = &(IPCB(skb)->opt);
+	int srrspace, srrptr;
+	u32 nexthop;
+	struct iphdr *iph = skb->nh.iph;
+	unsigned char * optptr = skb->nh.raw + opt->srr;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct rtable *rt2;
+	int err;
+
+	if (!opt->srr)
+		return 0;
+
+	if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST|RTF_NAT)
+	    || skb->pkt_type != PACKET_HOST)
+		return -EINVAL;
+
+	if (!(rt->rt_flags & RTF_LOCAL)) {
+		if (!opt->is_strictroute)
+			return 0;
+		icmp_send(skb, ICMP_PARAMETERPROB, 0, 16);
+		return -EINVAL;
+	}
+
+	for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {
+		if (srrptr + 3 > srrspace) {
+			icmp_send(skb, ICMP_PARAMETERPROB, 0, opt->srr+2);
+			return -EINVAL;
+		}
+		memcpy(&nexthop, &optptr[srrptr-1], 4);
+
+		rt = (struct rtable*)skb->dst;
+		skb->dst = NULL;
+		err = ip_route_input(skb, nexthop, iph->saddr, iph->tos,
+				     net_alias_main_dev(skb->dev));
+		rt2 = (struct rtable*)skb->dst;
+		if (err || rt2->rt_flags&(RTF_BROADCAST|RTF_MULTICAST|RTF_NAT)) {
+			ip_rt_put(rt2);
+			skb->dst = &rt->u.dst;
+			return -EINVAL;
+		}
+		ip_rt_put(rt);
+		if (!(rt2->rt_flags&RTF_LOCAL))
+			break;
+		/* Superfast 8) loopback forward */
+		memcpy(&iph->daddr, &optptr[srrptr-1], 4);
+		opt->is_changed = 1;
+	}
+	if (srrptr <= srrspace) {
+		opt->srr_is_hit = 1;
+		opt->is_changed = 1;
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.14/linux/net/ipv4/ip_output.c	Fri Nov 22 18:28:23 1996
+++ linux/net/ipv4/ip_output.c	Thu Dec 12 16:54:24 1996
@@ -26,6 +26,7 @@
  *		Alexander Demenshin:	Missing sk/skb free in ip_queue_xmit
  *					(in case if packet not accepted by
  *					output firewall rules)
+ *		Alexey Kuznetsov:	use new route cache
  */
 
 #include <asm/uaccess.h>
@@ -65,225 +66,134 @@
 #include <linux/mroute.h>
 #include <net/netlink.h>
 
-/*
- *	Loop a packet back to the sender.
- */
- 
-static void ip_loopback(struct device *old_dev, struct sk_buff *skb)
+static void __inline__ ip_ll_header_reserve(struct sk_buff *skb)
 {
-	struct device *dev=&loopback_dev;
-	int len=ntohs(skb->ip_hdr->tot_len);
-	struct sk_buff *newskb=dev_alloc_skb(len+dev->hard_header_len+15);
-	
-	if(newskb==NULL)
-		return;
-		
-	newskb->link3=NULL;
-	newskb->sk=NULL;
-	newskb->dev=dev;
-	newskb->saddr=skb->saddr;
-	newskb->daddr=skb->daddr;
-	newskb->raddr=skb->raddr;
-	newskb->free=1;
-	newskb->lock=0;
-	newskb->users=0;
-	newskb->pkt_type=skb->pkt_type;
-	
-	/*
-	 *	Put a MAC header on the packet
-	 */
-	ip_send(NULL,newskb, skb->ip_hdr->daddr, len, dev, skb->ip_hdr->saddr);
-	/*
-	 *	Add the rest of the data space.	
-	 */
-	newskb->ip_hdr=(struct iphdr *)skb_put(newskb, len);
-	memcpy(newskb->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
-
-	/*
-	 *	Copy the data
-	 */
-	memcpy(newskb->ip_hdr,skb->ip_hdr,len);
-
-	/* Recurse. The device check against IFF_LOOPBACK will stop infinite recursion */
-		
-	/*printk("Loopback output queued [%lX to %lX].\n", newskb->ip_hdr->saddr,newskb->ip_hdr->daddr);*/
-	ip_queue_xmit(NULL, dev, newskb, 2);
+	struct rtable *rt = (struct rtable*)skb->dst;
+	skb_reserve(skb, (rt->u.dst.dev->hard_header_len+15)&~15);
+	ip_ll_header(skb);
 }
 
 
+int ip_id_count = 0;
 
-/*
- *	Take an skb, and fill in the MAC header.
- */
-
-int ip_send(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
+int ip_build_pkt(struct sk_buff *skb, struct sock *sk, u32 saddr, u32 daddr,
+		 struct ip_options *opt)
 {
-	int mac = 0;
+	struct rtable *rt;
+	u32 final_daddr = daddr;
+	struct iphdr *iph;
+	int err;
+	
+	if (opt && opt->srr)
+		daddr = opt->faddr;
 
-	skb->dev = dev;
-	skb->arp = 1;
-	skb->protocol = htons(ETH_P_IP);
-	if (dev->hard_header)
+	err = ip_route_output(&rt, daddr, saddr, RT_TOS(sk->ip_tos) |
+			      (sk->localroute||0), NULL);
+	if (err)
 	{
-		/*
-		 *	Build a hardware header. Source address is our mac, destination unknown
-		 *  	(rebuild header will sort this out)
-		 */
-		skb_reserve(skb,(dev->hard_header_len+15)&~15);	/* 16 byte aligned IP headers are good */
-		if (rt && dev == rt->rt_dev && rt->rt_hh)
-		{
-			memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
-			if (rt->rt_hh->hh_uptodate)
-				return dev->hard_header_len;
-#if RT_CACHE_DEBUG >= 2
-			printk("ip_send: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
-#endif
-			skb->arp = 0;
-			skb->raddr = daddr;
-			return dev->hard_header_len;
-		}
-		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
-		if (mac < 0)
-		{
-			mac = -mac;
-			skb->arp = 0;
-			skb->raddr = daddr;	/* next routing address */
-		}
+		ip_statistics.IpOutNoRoutes++;
+		return err;
 	}
-	return mac;
-}
 
-static int ip_send_room(struct rtable * rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr)
-{
-	int mac = 0;
+	if (opt && opt->is_strictroute && rt->rt_flags&RTF_GATEWAY) {
+		ip_rt_put(rt);
+		ip_statistics.IpOutNoRoutes++;
+		return -ENETUNREACH;
+	}
 
-	skb->dev = dev;
-	skb->arp = 1;
-	skb->protocol = htons(ETH_P_IP);
-	skb_reserve(skb,MAX_HEADER);
-	if (dev->hard_header)
-	{
-		if (rt && dev == rt->rt_dev && rt->rt_hh)
-		{
-			memcpy(skb_push(skb,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
-			if (rt->rt_hh->hh_uptodate)
-				return dev->hard_header_len;
-#if RT_CACHE_DEBUG >= 2
-			printk("ip_send_room: hh miss %08x via %08x\n", daddr, rt->rt_gateway);
-#endif
-			skb->arp = 0;
-			skb->raddr = daddr;
-			return dev->hard_header_len;
-		}
-		mac = dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, len);
-		if (mac < 0)
-		{
-			mac = -mac;
-			skb->arp = 0;
-			skb->raddr = daddr;	/* next routing address */
-		}
+	skb->dst = dst_clone(&rt->u.dst);
+
+	skb->dev = rt->u.dst.dev;
+	skb->arp = 0;
+
+	ip_ll_header_reserve(skb);
+	
+	/*
+	 *	Now build the IP header.
+	 */
+
+	/*
+	 *	Build the IP addresses
+	 */
+	 
+	if (opt)
+		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr) + opt->optlen);
+	else
+		iph=(struct iphdr *)skb_put(skb,sizeof(struct iphdr));
+
+	iph->version  = 4;
+	iph->ihl      = 5;
+	iph->tos      = sk->ip_tos;
+	iph->frag_off = 0;
+	if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+	    (sk->ip_pmtudisc == IP_PMTUDISC_WANT && 
+	     rt->rt_flags&RTF_NOPMTUDISC))
+		iph->frag_off |= htons(IP_DF);
+	iph->ttl      = sk->ip_ttl;
+	iph->daddr    = rt->rt_dst;
+	iph->saddr    = rt->rt_src;
+	iph->protocol = sk->protocol;
+	skb->nh.iph   = iph;
+	skb->h.raw    = (unsigned char*)(iph+1);
+
+	if (opt && opt->optlen)
+	{
+		iph->ihl += opt->optlen>>2;
+		skb->h.raw += opt->optlen;
+		ip_options_build(skb, opt, final_daddr,
+				 rt->u.dst.dev->pa_addr, 0);
 	}
-	return mac;
+	
+	ip_rt_put(rt);
+	return 0;
 }
 
-int ip_id_count = 0;
-
 /*
  * This routine builds the appropriate hardware/IP headers for
- * the routine.  It assumes that if *dev != NULL then the
- * protocol knows what it's doing, otherwise it uses the
- * routing/ARP tables to select a device struct.
+ * the routine.
  */
-int ip_build_header(struct sk_buff *skb, __u32 saddr, __u32 daddr,
-		struct device **dev, int type, struct options *opt,
-		int len, int tos, int ttl, struct rtable ** rp)
+int ip_build_header(struct sk_buff *skb, struct sock *sk)
 {
 	struct rtable *rt;
-	__u32 raddr;
-	int tmp;
+	struct ip_options *opt = sk->opt;
+	u32 daddr = sk->daddr;
+	u32 final_daddr = daddr;
 	struct iphdr *iph;
-	__u32 final_daddr = daddr;
-
+	int err;
 
 	if (opt && opt->srr)
 		daddr = opt->faddr;
 
-	/*
-	 *	See if we need to look up the device.
-	 */
-
-#ifdef CONFIG_IP_MULTICAST	
-	if(MULTICAST(daddr) && *dev==NULL && skb->sk && *skb->sk->ip_mc_name)
-		*dev=dev_get(skb->sk->ip_mc_name);
-#endif
-	if (rp)
-	{
-		rt = ip_check_route(rp, daddr, skb->localroute);
-		/*
-		 * If rp != NULL rt_put following below should not
-		 * release route, so that...
-		 */
-		if (rt)
-			atomic_inc(&rt->rt_refcnt);
-	}
-	else
-		rt = ip_rt_route(daddr, skb->localroute);
-
+	rt = (struct rtable*)sk->dst_cache;
 
-	if (*dev == NULL)
-	{
-		if (rt == NULL)
-		{
-			ip_statistics.IpOutNoRoutes++;
-			return(-ENETUNREACH);
-		}
-
-		*dev = rt->rt_dev;
+	if (!rt || rt->u.dst.obsolete) {
+		ip_rt_put(rt);
+		err = ip_route_output(&rt, daddr, sk->saddr, RT_TOS(sk->ip_tos) |
+				      (sk->localroute||0), NULL);
+		if (err)
+			return err;
+		sk->dst_cache = &rt->u.dst;
 	}
 
-	if ((LOOPBACK(saddr) && !LOOPBACK(daddr)) || !saddr)
-		saddr = rt ? rt->rt_src : (*dev)->pa_addr;
-
-	raddr = rt ? rt->rt_gateway : daddr;
-
-	if (opt && opt->is_strictroute && rt && (rt->rt_flags & RTF_GATEWAY))
-	{
+	if (opt && opt->is_strictroute && rt->rt_flags&RTF_GATEWAY) {
+		sk->dst_cache = NULL;
 		ip_rt_put(rt);
 		ip_statistics.IpOutNoRoutes++;
 		return -ENETUNREACH;
 	}
 
-	/*
-	 *	Now build the MAC header.
-	 */
-
-	if (type==IPPROTO_TCP)
-		tmp = ip_send_room(rt, skb, raddr, len, *dev, saddr);
-	else
-		tmp = ip_send(rt, skb, raddr, len, *dev, saddr);
-
-	ip_rt_put(rt);
-
-	/*
-	 *	Book keeping
-	 */
+	skb->dst = dst_clone(sk->dst_cache);
 
-	skb->dev = *dev;
-	skb->saddr = saddr;
+	skb->dev = rt->u.dst.dev;
+	skb->arp = 0;
+	skb_reserve(skb, MAX_HEADER);
+	skb->mac.raw = skb->data;
 	
 	/*
 	 *	Now build the IP header.
 	 */
 
 	/*
-	 *	If we are using IPPROTO_RAW, then we don't need an IP header, since
-	 *	one is being supplied to us by the user
-	 */
-
-	if(type == IPPROTO_RAW)
-		return (tmp);
-
-	/*
 	 *	Build the IP addresses
 	 */
 	 
@@ -294,21 +204,118 @@
 
 	iph->version  = 4;
 	iph->ihl      = 5;
-	iph->tos      = tos;
+	iph->tos      = sk->ip_tos;
 	iph->frag_off = 0;
-	iph->ttl      = ttl;
-	iph->daddr    = daddr;
-	iph->saddr    = saddr;
-	iph->protocol = type;
-	skb->ip_hdr   = iph;
+	if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+	    (sk->ip_pmtudisc == IP_PMTUDISC_WANT && 
+	     rt->rt_flags&RTF_NOPMTUDISC))
+		iph->frag_off |= htons(IP_DF);
+	iph->ttl      = sk->ip_ttl;
+	iph->daddr    = rt->rt_dst;
+	iph->saddr    = rt->rt_src;
+	iph->protocol = sk->protocol;
+	skb->nh.iph   = iph;
+	skb->h.raw    = (unsigned char*)(iph+1);
 
 	if (!opt || !opt->optlen)
-		return sizeof(struct iphdr) + tmp;
+		return 0;
 	iph->ihl += opt->optlen>>2;
-	ip_options_build(skb, opt, final_daddr, (*dev)->pa_addr, 0);
-	return iph->ihl*4 + tmp;
+	skb->h.raw += opt->optlen;
+	ip_options_build(skb, opt, final_daddr, rt->u.dst.dev->pa_addr, 0);
+
+	return 0;
 }
 
+int ip_mc_output(struct sk_buff *skb)
+{
+	struct sock *sk = skb->sk;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct device *dev = rt->u.dst.dev;
+
+	/*
+	 *	If the indicated interface is up and running, send the packet.
+	 */
+	 
+	ip_statistics.IpOutRequests++;
+#ifdef CONFIG_IP_ACCT
+	ip_fw_chk(skb->nh.iph, skb->dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
+#endif
+
+	if (rt->rt_flags & RTCF_NAT)
+		ip_do_nat(skb);
+
+	/*
+	 *	Multicasts are looped back for other local users
+	 */
+	 
+	if (rt->rt_flags&RTF_MULTICAST && !(dev->flags&IFF_LOOPBACK)) {
+		if (sk==NULL || sk->ip_mc_loop)
+			dev_loopback_xmit(skb);
+
+		/* Multicasts with ttl 0 must not go beyond the host */
+		
+		if (skb->nh.iph->ttl == 0) {
+			kfree_skb(skb, FREE_WRITE);
+			return 0;
+		}
+	}
+
+	if ((rt->rt_flags&(RTF_LOCAL|RTF_BROADCAST)) == (RTF_LOCAL|RTF_BROADCAST) &&
+	    !(dev->flags&IFF_LOOPBACK))
+		dev_loopback_xmit(skb);
+
+	if (dev->flags & IFF_UP) {
+		dev_queue_xmit(skb);
+		return 0;
+	}
+	ip_statistics.IpOutDiscards++;
+
+	kfree_skb(skb, FREE_WRITE);
+	return -ENETDOWN;
+}
+
+int ip_output(struct sk_buff *skb)
+{
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct device *dev = rt->u.dst.dev;
+
+	/*
+	 *	If the indicated interface is up and running, send the packet.
+	 */
+	 
+	ip_statistics.IpOutRequests++;
+
+#ifdef CONFIG_IP_ACCT
+	ip_fw_chk(skb->nh.iph, skb->dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
+#endif
+
+	if (rt->rt_flags&RTCF_NAT)
+		ip_do_nat(skb);
+
+	if (dev->flags & IFF_UP) {
+		dev_queue_xmit(skb);
+		return 0;
+	}
+	ip_statistics.IpOutDiscards++;
+
+	kfree_skb(skb, FREE_WRITE);
+	return -ENETDOWN;
+}
+
+#ifdef CONFIG_IP_ACCT
+int ip_acct_output(struct sk_buff *skb)
+{
+	/*
+	 *	Count mapping we shortcut
+	 */
+			 
+	ip_fw_chk(skb->nh.iph, skb->dev, NULL, ip_acct_chain, 0, IP_FW_MODE_ACCT_OUT);
+
+	dev_queue_xmit(skb);
+
+	return 0;
+}
+#endif			
 
 /*
  *	Generate a checksum for an outgoing IP datagram.
@@ -331,54 +338,48 @@
  * and compute the checksum
  */
 
-void ip_queue_xmit(struct sock *sk, struct device *dev,
-	      struct sk_buff *skb, int free)
+void ip_queue_xmit(struct sk_buff *skb)
 {
+	struct sock *sk = skb->sk;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct device *dev = rt->u.dst.dev;
 	unsigned int tot_len;
-	struct iphdr *iph;
-
-	IS_SKB(skb);
+	struct iphdr *iph = skb->nh.iph;
 
 	/*
-	 *	Do some book-keeping in the packet for later
-	 */
-
-	skb->sk = sk;
-	skb->dev = dev;
-	skb->when = jiffies;
-
-	/*
-	 *	Find the IP header and set the length. This is bad
-	 *	but once we get the skb data handling code in the
-	 *	hardware will push its header sensibly and we will
-	 *	set skb->ip_hdr to avoid this mess and the fixed
-	 *	header length problem
+	 *	Discard the surplus MAC header
 	 */
+		 
+	skb_pull(skb, skb->nh.raw - skb->data);
+	tot_len = skb->len;
 
-	iph = skb->ip_hdr;
-	tot_len = skb->len - (((unsigned char *)iph) - skb->data);
 	iph->tot_len = htons(tot_len);
+	iph->id = htons(ip_id_count++);
 
-	switch (free) {
-		/* No reassigning numbers to fragments... */
-		case 2:
-			free = 1;
-			break;
-		default:
-			free = 1;
-			iph->id = htons(ip_id_count++);
+#ifdef CONFIG_FIREWALL
+	if (call_out_firewall(PF_INET, dev, iph, NULL) < FW_ACCEPT) {
+		kfree_skb(skb, FREE_WRITE);
+		return;
 	}
+#endif
 
-	skb->free = free;
+	if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {
+		struct sk_buff *skb2;
+		/* ANK: It is almost impossible, but
+		 * if you loaded module device with hh_len > MAX_HEADER,
+		 * and if a route changed to this device,
+		 * and if (uh...) TCP had segments queued on this route...
+		 */
+		skb2 = skb_realloc_headroom(skb, (dev->hard_header_len+15)&~15);
+		kfree_skb(skb, FREE_WRITE);
+		if (skb2 == NULL)
+			return;
+		skb = skb2;
+		iph = skb->nh.iph;
+	}
 
-	/* Sanity check */
-	if (dev == NULL)
-		goto no_device;
+	ip_ll_header(skb);
 
-#ifdef CONFIG_FIREWALL
-	if (call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
-		goto out;
-#endif	
 
 	/*
 	 *	Do we need to fragment. Again this is inefficient.
@@ -386,10 +387,8 @@
 	 *	bits of it.
 	 */
 
-	if (tot_len > dev->mtu)
-	{
+	if (tot_len > rt->u.dst.pmtu)
 		goto fragment;
-	}
 
 	/*
 	 *	Add an IP checksum
@@ -397,101 +396,27 @@
 
 	ip_send_check(iph);
 
-	/*
-	 *	More debugging. You cannot queue a packet already on a list
-	 *	Spot this and moan loudly.
-	 */
-	if (skb->next != NULL)
-	{
-		NETDEBUG(printk("ip_queue_xmit: next != NULL\n"));
-		skb_unlink(skb);
-	}
-
-	/*
-	 *	If the indicated interface is up and running, send the packet.
-	 */
-	 
-	ip_statistics.IpOutRequests++;
-#ifdef CONFIG_IP_ACCT
-	ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif	
-
-#ifdef CONFIG_IP_MULTICAST	
-
-	/*
-	 *	Multicasts are looped back for other local users
-	 */
-	 
-	if (MULTICAST(iph->daddr) && !(dev->flags&IFF_LOOPBACK))
-	{
-		if(sk==NULL || sk->ip_mc_loop)
-		{
-			if(iph->daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
-			{
-				ip_loopback(dev,skb);
-			}
-			else
-			{
-				struct ip_mc_list *imc=dev->ip_mc_list;
-				while(imc!=NULL)
-				{
-					if(imc->multiaddr==iph->daddr)
-					{
-						ip_loopback(dev,skb);
-						break;
-					}
-					imc=imc->next;
-				}
-			}
-		}
-		/* Multicasts with ttl 0 must not go beyond the host */
-		
-		if (iph->ttl==0)
-			goto out;
-	}
-#endif
-	if ((dev->flags & IFF_BROADCAST) && !(dev->flags & IFF_LOOPBACK)
-	    && (iph->daddr==dev->pa_brdaddr || iph->daddr==0xFFFFFFFF))
-		ip_loopback(dev,skb);
-		
-	if (dev->flags & IFF_UP)
-	{
-		/*
-		 *	If we have an owner use its priority setting,
-		 *	otherwise use NORMAL
-		 */
-		int priority = SOPRI_NORMAL;
-		if (sk)
-			priority = sk->priority;
-
-		dev_queue_xmit(skb, dev, priority);
-		return;
-	}
-	if(sk)
-		sk->err = ENETDOWN;
-	ip_statistics.IpOutDiscards++;
-out:
-	if (free)
-		kfree_skb(skb, FREE_WRITE);
+	if (sk)
+		skb->priority = sk->priority;
+	skb->dst->output(skb);
 	return;
 
-no_device:
-	NETDEBUG(printk("IP: ip_queue_xmit dev = NULL\n"));
-	goto out;
-
 fragment:
 	if ((iph->frag_off & htons(IP_DF)))
 	{
 		printk(KERN_DEBUG "sending pkt_too_big to self\n");
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
-			  htonl(dev->mtu), dev);
-		goto out;
+			  htonl(dev->mtu));
+			  
+		kfree_skb(skb, FREE_WRITE);
+		return;
 	}
-	ip_fragment(sk,skb,dev,0);
-	goto out;
+	
+	ip_fragment(skb, 1, skb->dst->output);
 }
 
 
+
 /*
  *	Build and send a packet, with as little as one copy
  *
@@ -514,167 +439,87 @@
 
 int ip_build_xmit(struct sock *sk,
 		  int getfrag (const void *,
-			       __u32,
 			       char *,
 			       unsigned int,	
 			       unsigned int),
-		   const void *frag,
-		   unsigned short int length,
-		   __u32 daddr,
-		   __u32 user_saddr,
-		   struct options * opt,
-		   int flags,
-		   int type,
-		   int noblock) 
+		  const void *frag,
+		  unsigned short length,
+		  struct ipcm_cookie *ipc,
+		  struct rtable *rt,
+		  int flags)
 {
-	struct rtable *rt;
 	unsigned int fraglen, maxfraglen, fragheaderlen;
+	int err;
 	int offset, mf;
-	__u32 saddr;
 	unsigned short id;
 	struct iphdr *iph;
-	__u32 raddr;
-	struct device *dev = NULL;
-	struct hh_cache * hh=NULL;
+	int hh_len = rt->u.dst.dev->hard_header_len;
 	int nfrags=0;
-	__u32 true_daddr = daddr;
-	int err; 
-
-	if (opt && opt->srr && !sk->ip_hdrincl)
-	  daddr = opt->faddr;
+	struct ip_options *opt = ipc->opt;
+	struct device *dev = rt->u.dst.dev;
+	int df = htons(IP_DF);
 	
-	ip_statistics.IpOutRequests++;
-
-#ifdef CONFIG_IP_MULTICAST	
-	if(MULTICAST(daddr) && *sk->ip_mc_name)
-	{
-		dev=dev_get(sk->ip_mc_name);
-		if(!dev)
-			return -ENODEV;
-		rt=NULL;
-		if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
-			saddr = sk->saddr;
-		else
-			saddr = dev->pa_addr;
-	}
-	else
-	{
-#endif	
-		rt = ip_check_route(&sk->ip_route_cache, daddr,
-				    sk->localroute || (flags&MSG_DONTROUTE) ||
-				    (opt && opt->is_strictroute));
-		if (rt == NULL) 
-		{
-			ip_statistics.IpOutNoRoutes++;
-			return(-ENETUNREACH);
-		}
-		saddr = rt->rt_src;
 
-		hh = rt->rt_hh;
-	
-		if (sk->saddr && (!LOOPBACK(sk->saddr) || LOOPBACK(daddr)))
-			saddr = sk->saddr;
-			
-		dev=rt->rt_dev;
-#ifdef CONFIG_IP_MULTICAST
-	}
-	if (rt && !dev)
-		dev = rt->rt_dev;
-#endif		
-	if (user_saddr)
-		saddr = user_saddr;
+	if (sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+	    (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+	     rt->rt_flags&RTF_NOPMTUDISC))
+		df = 0;
 
-	raddr = rt ? rt->rt_gateway : daddr;
-	/*
-	 *	Now compute the buffer space we require
-	 */ 
 	 
 	/*
-	 *	Try the simple case first. This leaves broadcast, multicast, fragmented frames, and by
+	 *	Try the simple case first. This leaves fragmented frames, and by
 	 *	choice RAW frames within 20 bytes of maximum size(rare) to the long path
 	 */
 
-	if (!sk->ip_hdrincl) {
+	if (!sk->ip_hdrincl)
 		length += sizeof(struct iphdr);
-		if(opt) length += opt->optlen;
-	}
 
-	if(length <= dev->mtu && !MULTICAST(daddr) && daddr!=0xFFFFFFFF && daddr!=dev->pa_brdaddr)
-	{	
+	if (length <= rt->u.dst.pmtu && opt == NULL) {
 		int error;
-		struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+dev->hard_header_len,0, noblock, &error);
-		if(skb==NULL)
-		{
+		struct sk_buff *skb=sock_alloc_send_skb(sk, length+15+hh_len,
+							0, flags&MSG_DONTWAIT, &error);
+		if(skb==NULL) {
 			ip_statistics.IpOutDiscards++;
 			return error;
 		}
-		skb->dev=dev;
-		skb->protocol = htons(ETH_P_IP);
-		skb->free=1;
+
 		skb->when=jiffies;
-		skb->sk=sk;
-		skb->arp=0;
-		skb->saddr=saddr;
-		skb->raddr = raddr;
-		skb_reserve(skb,(dev->hard_header_len+15)&~15);
-		if (hh)
-		{
-			skb->arp=1;
-			memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
-			if (!hh->hh_uptodate)
-			{
-				skb->arp = 0;
-#if RT_CACHE_DEBUG >= 2
-				printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
-#endif				
-			}
-		}
-		else if(dev->hard_header)
-		{
-			if(dev->hard_header(skb,dev,ETH_P_IP,NULL,NULL,0)>0)
-				skb->arp=1;
-		}
-		else
-			skb->arp=1;
-		skb->ip_hdr=iph=(struct iphdr *)skb_put(skb,length);
+		skb->priority = sk->priority;
+		skb->dst = dst_clone(&rt->u.dst);
+
+		ip_ll_header_reserve(skb);
+
+		skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
+
 		dev_lock_list();
-		if(!sk->ip_hdrincl)
-		{
+
+		if(!sk->ip_hdrincl) {
 			iph->version=4;
 			iph->ihl=5;
 			iph->tos=sk->ip_tos;
 			iph->tot_len = htons(length);
 			iph->id=htons(ip_id_count++);
-			iph->frag_off = 0;
-			iph->ttl=sk->ip_ttl;
-			iph->protocol=type;
-			iph->saddr=saddr;
-			iph->daddr=daddr;
-			if (opt) 
-			{
-				iph->ihl += opt->optlen>>2;
-				ip_options_build(skb, opt,
-						 true_daddr, dev->pa_addr, 0);
-			}
+			iph->frag_off = df;
+			iph->ttl=sk->ip_mc_ttl;
+			if (!(rt->rt_flags&RTF_MULTICAST))
+				iph->ttl=sk->ip_ttl;
+			iph->protocol=sk->protocol;
+			iph->saddr=rt->rt_src;
+			iph->daddr=rt->rt_dst;
 			iph->check=0;
 			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-			err = getfrag(frag,saddr,((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
+			err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
 		}
 		else
-			err = getfrag(frag, saddr, (void *)iph, 0, length);
-		
+			err = getfrag(frag, (void *)iph, 0, length);
 		dev_unlock_list();
-		
+
 		if (err)
-		{
 			err = -EFAULT;
-		}
 
 #ifdef CONFIG_FIREWALL
-		if(!err && call_out_firewall(PF_INET, skb->dev, iph, NULL)< FW_ACCEPT)
-		{
-			err = -EPERM; 
-		}
+		if(!err && call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
+			err = -EPERM;
 #endif
 
 		if (err)
@@ -683,39 +528,26 @@
 			return err;
 		}
 
-#ifdef CONFIG_IP_ACCT
-		ip_fw_chk(iph,dev,NULL,ip_acct_chain,0,IP_FW_MODE_ACCT_OUT);
-#endif		
-		if(dev->flags&IFF_UP)
-			dev_queue_xmit(skb,dev,sk->priority);
-		else
-		{
-			ip_statistics.IpOutDiscards++;
-			kfree_skb(skb, FREE_WRITE);
-		}
-		return 0;
+		return rt->u.dst.output(skb);
 	}
+
 	if (!sk->ip_hdrincl)
 		length -= sizeof(struct iphdr);
-		
-	if(opt)
-	{
-		length -= opt->optlen;
-		fragheaderlen = dev->hard_header_len + sizeof(struct iphdr) + opt->optlen;
-		maxfraglen = ((dev->mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
-	}
-	else 
-	{
-		fragheaderlen = dev->hard_header_len;
+
+	if (opt) {
+		fragheaderlen = hh_len + sizeof(struct iphdr) + opt->optlen;
+		maxfraglen = ((rt->u.dst.pmtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
+	} else {
+		fragheaderlen = hh_len;
 		if(!sk->ip_hdrincl)
-			fragheaderlen += 20;
+			fragheaderlen += sizeof(struct iphdr);
 		
 		/*
-		 *	Fragheaderlen is the size of 'overhead' on each buffer.
-		 *	Now work out the size of the frames to send.
+		 *	Fragheaderlen is the size of 'overhead' on each buffer. Now work
+		 *	out the size of the frames to send.
 		 */
 	 
-		maxfraglen = ((dev->mtu-20) & ~7) + fragheaderlen;
+		maxfraglen = ((rt->u.dst.pmtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
         }
 
 	/*
@@ -730,8 +562,7 @@
 	 
 	fraglen = length - offset + fragheaderlen;
 	
-	if(length-offset==0)
-	{
+	if (length-offset==0) {
 		fraglen = maxfraglen;
 		offset -= maxfraglen-fragheaderlen;
 	}
@@ -747,7 +578,7 @@
 	 *	Can't fragment raw packets 
 	 */
 	 
-	if (sk->ip_hdrincl && offset > 0)
+	if (offset > 0 && df)
  		return(-EMSGSIZE);
 
 	/*
@@ -766,8 +597,7 @@
 	 *	Being outputting the bytes.
 	 */
 	 
-	do 
-	{
+	do {
 		struct sk_buff * skb;
 		int error;
 		char *data;
@@ -776,12 +606,11 @@
 		 *	Get the memory we require with some space left for alignment.
 		 */
 
-		skb = sock_alloc_send_skb(sk, fraglen+15, 0, noblock, &error);
-		if (skb == NULL)
-		{
+		skb = sock_alloc_send_skb(sk, fraglen+15, 0, flags&MSG_DONTWAIT, &error);
+		if (skb == NULL) {
 			ip_statistics.IpOutDiscards++;
 			if(nfrags>1)
-				ip_statistics.IpFragCreates++;
+				ip_statistics.IpFragCreates++;			
 			dev_unlock_list();
 			return(error);
 		}
@@ -790,81 +619,44 @@
 		 *	Fill in the control structures
 		 */
 		 
-		skb->dev = dev;
-		skb->protocol = htons(ETH_P_IP);
 		skb->when = jiffies;
-		skb->free = 1; /* dubious, this one */
-		skb->sk = sk;
-		skb->arp = 0;
-		skb->saddr = saddr;
-		skb->daddr = daddr;
-		skb->raddr = raddr;
-		skb_reserve(skb,(dev->hard_header_len+15)&~15);
-		data = skb_put(skb, fraglen-dev->hard_header_len);
+		skb->priority = sk->priority;
+		skb->dst = dst_clone(&rt->u.dst);
+
+		ip_ll_header_reserve(skb);
 
-		/*
-		 *	Save us ARP and stuff. In the optimal case we do no route lookup (route cache ok)
-		 *	no ARP lookup (arp cache ok) and output. The cache checks are still too slow but
-		 *	this can be fixed later. For gateway routes we ought to have a rt->.. header cache
-		 *	pointer to speed header cache builds for identical targets.
-		 */
-		 
-		if (hh)
-		{
-			skb->arp=1;
-			memcpy(skb_push(skb,dev->hard_header_len),hh->hh_data,dev->hard_header_len);
-			if (!hh->hh_uptodate)
-			{
-				skb->arp = 0;
-#if RT_CACHE_DEBUG >= 2
-				printk("ip_build_xmit: hh miss %08x via %08x\n", rt->rt_dst, rt->rt_gateway);
-#endif
-			}
-		}
-		else if (dev->hard_header)
-		{
-			if(dev->hard_header(skb, dev, ETH_P_IP, 
-						NULL, NULL, 0)>0)
-				skb->arp=1;
-		}
-		else
-			skb->arp = 1;
-		
 		/*
 		 *	Find where to start putting bytes.
 		 */
 		 
-		skb->ip_hdr = iph = (struct iphdr *)data;
+		data = skb_put(skb, fraglen-hh_len);
+		skb->nh.iph = iph = (struct iphdr *)data;
 
 		/*
 		 *	Only write IP header onto non-raw packets 
 		 */
 		 
-		if(!sk->ip_hdrincl) 
-		{
-
+		if(!sk->ip_hdrincl) {
 			iph->version = 4;
-			iph->ihl = 5; /* ugh */
+			iph->ihl = 5;
 			if (opt) {
 				iph->ihl += opt->optlen>>2;
 				ip_options_build(skb, opt,
-						 true_daddr, dev->pa_addr, offset);
+						 ipc->addr, dev->pa_addr, offset);
 			}
 			iph->tos = sk->ip_tos;
 			iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
 			iph->id = id;
 			iph->frag_off = htons(offset>>3);
-			iph->frag_off |= mf;
-#ifdef CONFIG_IP_MULTICAST
-			if (MULTICAST(daddr))
+			iph->frag_off |= mf|df;
+			if (rt->rt_flags&RTF_MULTICAST)
 				iph->ttl = sk->ip_mc_ttl;
 			else
-#endif
 				iph->ttl = sk->ip_ttl;
-			iph->protocol = type;
+			iph->protocol = sk->protocol;
 			iph->check = 0;
-			iph->saddr = saddr;
-			iph->daddr = daddr;
+			iph->saddr = rt->rt_src;
+			iph->daddr = rt->rt_dst;
 			iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
 			data += iph->ihl*4;
 			
@@ -879,11 +671,9 @@
 		 *	User data callback
 		 */
 
-		err = getfrag(frag, saddr, data, offset, fraglen-fragheaderlen);
+		err = getfrag(frag, data, offset, fraglen-fragheaderlen);
 		if (err)
-		{
 			err = -EFAULT;
-		}
 		
 		/*
 		 *	Account for the fragment.
@@ -891,115 +681,271 @@
 		 
 #ifdef CONFIG_FIREWALL
 		if(!err && !offset && call_out_firewall(PF_INET, skb->dev, iph, NULL) < FW_ACCEPT)
-		{
-			err = -EPERM; 
-		}
-#endif
+			err = -EPERM;
+#endif		
 		if (err)
-		{
+ 		{
 			kfree_skb(skb, FREE_WRITE);
 			dev_unlock_list();
 			return err;
-		}
-
-#ifdef CONFIG_IP_ACCT
-		if(!offset)
-			ip_fw_chk(iph, dev, NULL, ip_acct_chain, 0, IP_FW_MODE_ACCT_OUT);
-#endif	
+ 		}
 		offset -= (maxfraglen-fragheaderlen);
 		fraglen = maxfraglen;
 
-#ifdef CONFIG_IP_MULTICAST
 
+		nfrags++;
+		
+		if (rt->u.dst.output(skb)) {
+			if (nfrags>1)
+				ip_statistics.IpFragCreates += nfrags;
+			dev_unlock_list();
+			return -ENETDOWN;
+		}
+	} while (offset >= 0);
+
+	if (nfrags>1)
+		ip_statistics.IpFragCreates += nfrags;
+
+	dev_unlock_list();
+	return 0;
+}
+
+/*
+ *	This IP datagram is too large to be sent in one piece.  Break it up into
+ *	smaller pieces (each of size equal to the MAC header plus IP header plus
+ *	a block of the data of the original IP data part) that will yet fit in a
+ *	single device frame, and queue such a frame for sending.
+ *
+ *	Assumption: packet was ready for transmission, link layer header
+ *	is already in.
+ *
+ *	Yes this is inefficient, feel free to submit a quicker one.
+ */
+ 
+void ip_fragment(struct sk_buff *skb, int local, int (*output)(struct sk_buff*))
+{
+	struct iphdr *iph;
+	unsigned char *raw;
+	unsigned char *ptr;
+	struct device *dev;
+	struct sk_buff *skb2;
+	int left, mtu, hlen, len;
+	int offset;
+	int not_last_frag;
+	u16 dont_fragment;
+	struct rtable *rt = (struct rtable*)skb->dst;
+
+	dev = skb->dev;
+
+	/*
+	 *	Point into the IP datagram header.
+	 */
+
+	raw = skb->data;
+	iph = skb->nh.iph;
+
+	/*
+	 *	Setup starting values.
+	 */
+
+	hlen = iph->ihl * 4;
+	left = ntohs(iph->tot_len) - hlen;	/* Space per frame */
+	hlen += skb->nh.raw - raw;
+	if (local)
+		mtu = rt->u.dst.pmtu - hlen;	/* Size of data space */
+	else
+		mtu = dev->mtu - hlen;
+	ptr = raw + hlen;			/* Where to start from */
+
+	/*
+	 *	The protocol doesn't seem to say what to do in the case that the
+	 *	frame + options doesn't fit the mtu. As it used to fall down dead
+	 *	in this case we were fortunate it didn't happen
+	 */
+
+	if (mtu<8) {
+		ip_statistics.IpFragFails++;
+		kfree_skb(skb, FREE_WRITE);
+		return;
+	}
+
+	/*
+	 *	Fragment the datagram.
+	 */
+
+	offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+	not_last_frag = iph->frag_off & htons(IP_MF);
+
+	/*
+	 *	Nice moment: if DF is set and we are here,
+	 *	it means that packet should be fragmented and
+	 *	DF is set on fragments. If it works,
+	 *	path MTU discovery can be done by ONE segment(!). --ANK
+	 */
+	dont_fragment = iph->frag_off & htons(IP_DF);
+
+	/*
+	 *	Keep copying data until we run out.
+	 */
+
+	while(left > 0)	{
+		len = left;
+		/* IF: it doesn't fit, use 'mtu' - the data space left */
+		if (len > mtu)
+			len = mtu;
+		/* IF: we are not sending upto and including the packet end
+		   then align the next start on an eight byte boundary */
+		if (len < left)	{
+			len/=8;
+			len*=8;
+		}
 		/*
-		 *	Multicasts are looped back for other local users
+		 *	Allocate buffer.
 		 */
-	 
-		if (MULTICAST(daddr) && !(dev->flags&IFF_LOOPBACK)) 
-		{
-			/*
-			 *	Loop back any frames. The check for IGMP_ALL_HOSTS is because
-			 *	you are always magically a member of this group.
-			 *
-			 *	Always loop back all host messages when running as a multicast router.
-			 */
-			 
-			if(sk==NULL || sk->ip_mc_loop)
-			{
-				if(daddr==IGMP_ALL_HOSTS || (dev->flags&IFF_ALLMULTI))
-					ip_loopback(dev,skb);
-				else 
-				{
-					struct ip_mc_list *imc=dev->ip_mc_list;
-					while(imc!=NULL) 
-					{
-						if(imc->multiaddr==daddr) 
-						{
-							ip_loopback(dev,skb);
-							break;
-						}
-						imc=imc->next;
-					}
-				}
-			}
 
-			/*
-			 *	Multicasts with ttl 0 must not go beyond the host. Fixme: avoid the
-			 *	extra clone.
-			 */
-
-			if(skb->ip_hdr->ttl==0)
-			{
-				kfree_skb(skb, FREE_WRITE);
-				nfrags++;
-				continue;
-			}
+		if ((skb2 = alloc_skb(len+hlen+15,GFP_ATOMIC)) == NULL) {
+			NETDEBUG(printk("IP: frag: no memory for new fragment!\n"));
+			ip_statistics.IpFragFails++;
+			kfree_skb(skb, FREE_WRITE);
+			return;
 		}
-#endif
 
-		nfrags++;
-		
 		/*
-		 *	BSD loops broadcasts
+		 *	Set up data on packet
 		 */
-		 
-		if((dev->flags&IFF_BROADCAST) && (daddr==0xFFFFFFFF || daddr==dev->pa_brdaddr) && !(dev->flags&IFF_LOOPBACK))
-			ip_loopback(dev,skb);
+
+		skb2->arp = skb->arp;
+		skb2->dev = skb->dev;
+		skb2->when = skb->when;
+		skb2->pkt_type = skb->pkt_type;
+		skb2->priority = skb->priority;
+		skb_put(skb2, len + hlen);
+		skb2->mac.raw = (char *) skb2->data;
+		skb2->nh.raw = skb2->mac.raw + dev->hard_header_len;
+		skb2->h.raw = skb2->mac.raw + hlen;
 
 		/*
-		 *	Now queue the bytes into the device.
+		 *	Charge the memory for the fragment to any owner
+		 *	it might possess
 		 */
-		 
-		if (dev->flags & IFF_UP) 
-		{
-			dev_queue_xmit(skb, dev, sk->priority);
-		} 
-		else 
-		{
-			/*
-			 *	Whoops... 
-			 */
-			 
-			ip_statistics.IpOutDiscards++;
-			if(nfrags>1)
-				ip_statistics.IpFragCreates+=nfrags;
-			kfree_skb(skb, FREE_WRITE);
-			dev_unlock_list();
-			/*
-			 *	BSD behaviour.
-			 */
-			if(sk!=NULL)
-				sk->err=ENETDOWN;
-			return(0); /* lose rest of fragments */
-		}
-	} 
-	while (offset >= 0);
-	if(nfrags>1)
-		ip_statistics.IpFragCreates+=nfrags;
-	dev_unlock_list();
-	return(0);
+
+		if (skb->sk)
+			skb_set_owner_w(skb2, skb->sk);
+		skb2->dst = dst_clone(skb->dst);
+
+		/*
+		 *	Copy the packet header into the new buffer.
+		 */
+
+		memcpy(skb2->mac.raw, raw, hlen);
+
+		/*
+		 *	Copy a block of the IP datagram.
+		 */
+		memcpy(skb2->h.raw, ptr, len);
+		left -= len;
+
+		/*
+		 *	Fill in the new header fields.
+		 */
+		iph = skb2->nh.iph;
+		iph->frag_off = htons((offset >> 3))|dont_fragment;
+
+		/* ANK: dirty, but effective trick. Upgrade options only if
+		 * the segment to be fragmented was THE FIRST (otherwise,
+		 * options are already fixed) and make it ONCE
+		 * on the initial skb, so that all the following fragments
+		 * will inherit fixed options.
+		 */
+		if (offset == 0)
+			ip_options_fragment(skb2);
+
+		/*
+		 *	Added AC : If we are fragmenting a fragment that's not the
+		 *		   last fragment then keep MF on each bit
+		 */
+		if (left > 0 || not_last_frag)
+			iph->frag_off |= htons(IP_MF);
+		ptr += len;
+		offset += len;
+
+		/*
+		 *	Put this fragment into the sending queue.
+		 */
+
+		ip_statistics.IpFragCreates++;
+
+		iph->tot_len = htons(len + hlen - dev->hard_header_len);
+
+		ip_send_check(iph);
+
+		output(skb2);
+	}
+	kfree_skb(skb, FREE_WRITE);
+	ip_statistics.IpFragOKs++;
+}
+
+struct sk_buff * ip_reply(struct sk_buff *skb, int payload)
+{
+	struct {
+		struct ip_options	opt;
+		char			data[40];
+	} replyopts;
+
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct sk_buff *reply;
+	int    iphlen;
+	struct iphdr *iph;
+
+	struct ipcm_cookie ipc;
+	u32 daddr;
+
+	if (ip_options_echo(&replyopts.opt, skb))
+		return NULL;
+
+	daddr = ipc.addr = rt->rt_src;
+	ipc.opt = &replyopts.opt;
+	if (ipc.opt->srr)
+		daddr = replyopts.opt.faddr;
+
+	if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), NULL))
+		return NULL;
+
+	iphlen = sizeof(struct iphdr) + replyopts.opt.optlen;
+	reply = alloc_skb(rt->u.dst.dev->hard_header_len+15+iphlen+payload, GFP_ATOMIC);
+	if (reply == NULL) {
+		ip_rt_put(rt);
+		return NULL;
+	}
+
+	reply->priority = skb->priority;
+	reply->dst = &rt->u.dst;
+
+	ip_ll_header_reserve(reply);
+
+	/*
+	 *	Now build the IP header.
+	 */
+
+	/*
+	 *	Build the IP addresses
+	 */
+	 
+	reply->nh.iph = iph = (struct iphdr *)skb_put(reply, iphlen);
+
+	iph->version  = 4;
+	iph->ihl      = iphlen>>2;
+	iph->tos      = skb->nh.iph->tos;
+	iph->frag_off = 0;
+	iph->ttl      = MAXTTL;
+	iph->daddr    = rt->rt_dst;
+	iph->saddr    = rt->rt_src;
+	iph->protocol = skb->nh.iph->protocol;
+	
+	ip_options_build(reply, &replyopts.opt, daddr, rt->u.dst.dev->pa_addr, 0);
+
+	return reply;
 }
-    
 
 /*
  *	IP protocol layer initialiser
@@ -1007,80 +953,40 @@
 
 static struct packet_type ip_packet_type =
 {
-	0,	/* MUTTER ntohs(ETH_P_IP),*/
+	__constant_htons(ETH_P_IP),
 	NULL,	/* All devices */
 	ip_rcv,
 	NULL,
 	NULL,
 };
 
-#ifdef CONFIG_RTNETLINK
-
-/*
- *	Netlink hooks for IP
- */
- 
-void ip_netlink_msg(unsigned long msg, __u32 daddr, __u32 gw, __u32 mask, short flags, short metric, char *name)
-{
-	struct sk_buff *skb=alloc_skb(sizeof(struct netlink_rtinfo), GFP_ATOMIC);
-	struct netlink_rtinfo *nrt;
-	struct sockaddr_in *s;
-	if(skb==NULL)
-		return;
-	skb->free=1;
-	nrt=(struct netlink_rtinfo *)skb_put(skb, sizeof(struct netlink_rtinfo));
-	nrt->rtmsg_type=msg;
-	s=(struct sockaddr_in *)&nrt->rtmsg_dst;
-	s->sin_family=AF_INET;
-	s->sin_addr.s_addr=daddr;
-	s=(struct sockaddr_in *)&nrt->rtmsg_gateway;
-	s->sin_family=AF_INET;
-	s->sin_addr.s_addr=gw;
-	s=(struct sockaddr_in *)&nrt->rtmsg_genmask;
-	s->sin_family=AF_INET;
-	s->sin_addr.s_addr=mask;
-	nrt->rtmsg_flags=flags;
-	nrt->rtmsg_metric=metric;
-	strcpy(nrt->rtmsg_device,name);
-	if (netlink_post(NETLINK_ROUTE, skb))
-		kfree_skb(skb, FREE_WRITE);
-}	
-
-#endif
 
 /*
  *	Device notifier
  */
  
-static int ip_rt_event(struct notifier_block *this, unsigned long event, void *ptr)
+static int ip_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct device *dev=ptr;
-	if(event==NETDEV_DOWN)
-	{
-		ip_netlink_msg(RTMSG_DELDEVICE, 0,0,0,0,0,dev->name);
-		ip_rt_flush(dev);
-	}
+
+	if (dev->family != AF_INET)
+		return NOTIFY_DONE;
+
+	if(event==NETDEV_UP) {
 /*
  *	Join the initial group if multicast.
  */		
-	if(event==NETDEV_UP)
-	{
-#ifdef CONFIG_IP_MULTICAST	
 		ip_mc_allhost(dev);
-#endif		
-		ip_netlink_msg(RTMSG_NEWDEVICE, 0,0,0,0,0,dev->name);
-		ip_rt_update(NETDEV_UP, dev);
 	}
-	return NOTIFY_DONE;
+	return ip_rt_event(event, dev);
 }
 
-struct notifier_block ip_rt_notifier={
-	ip_rt_event,
+struct notifier_block ip_netdev_notifier={
+	ip_netdev_event,
 	NULL,
 	0
 };
 
-#ifdef CONFIG_IP_MULTICAST
 #ifdef CONFIG_PROC_FS
 static struct proc_dir_entry proc_net_igmp = {
 	PROC_NET_IGMP, 4, "igmp",
@@ -1089,7 +995,6 @@
 	ip_mc_procinfo
 };
 #endif	
-#endif
 
 /*
  *	IP registers the packet type and then calls the subprotocol initialisers
@@ -1097,21 +1002,15 @@
 
 void ip_init(void)
 {
-	ip_packet_type.type=htons(ETH_P_IP);
 	dev_add_pack(&ip_packet_type);
 
-	/* So we flush routes when a device is downed */	
-	register_netdevice_notifier(&ip_rt_notifier);
+	ip_rt_init();
 
-/*	ip_raw_init();
-	ip_packet_init();
-	ip_tcp_init();
-	ip_udp_init();*/
+	/* So we flush routes and multicast lists when a device is downed */
+	register_netdevice_notifier(&ip_netdev_notifier);
 
-#ifdef CONFIG_IP_MULTICAST
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_igmp);
 #endif	
-#endif
 }
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.14/linux/net/ipv4/ip_sockglue.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/ip_sockglue.c	Thu Dec 12 16:54:24 1996
@@ -36,78 +36,131 @@
 
 #include <asm/uaccess.h>
 
-#ifdef CONFIG_IP_MULTICAST
-
 /*
- *	Write an multicast group list table for the IGMP daemon to
- *	read.
+ *	SOL_IP control messages.
  */
- 
-int ip_mc_procinfo(char *buffer, char **start, off_t offset, int length, int dummy)
+
+static void ip_cmsg_recv_localdev(struct msghdr *msg, struct sk_buff *skb)
 {
-	off_t pos=0, begin=0;
-	struct ip_mc_list *im;
-	unsigned long flags;
-	int len=0;
-	struct device *dev;
-	
-	len=sprintf(buffer,"Device    : Count\tGroup    Users Timer\n");  
-	save_flags(flags);
-	cli();
-	
-	for(dev = dev_base; dev; dev = dev->next)
-	{
-                if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST))
-                {
-                        len+=sprintf(buffer+len,"%-10s: %5d\n",
-					dev->name, dev->mc_count);
-                        for(im = dev->ip_mc_list; im; im = im->next)
-                        {
-                                len+=sprintf(buffer+len,
-					"\t\t\t%08lX %5d %d:%08lX\n",
-                                        im->multiaddr, im->users,
-					im->tm_running, im->timer.expires-jiffies);
-                                pos=begin+len;
-                                if(pos<offset)
-                                {
-                                        len=0;
-                                        begin=pos;
-                                }
-                                if(pos>offset+length)
-                                        break;
-                        }
-                }
+	static char empty_name[MAX_ADDR_LEN];
+	put_cmsg(msg, SOL_IP, IP_LOCALDEV, MAX_ADDR_LEN,
+		 skb->dev ? skb->dev->name : empty_name);
+}
+
+static void ip_cmsg_recv_localaddr(struct msghdr *msg, struct sk_buff *skb, int local)
+{
+	struct in_addr addr;
+
+	addr.s_addr = skb->nh.iph->daddr;
+
+	if (local) {
+		struct rtable *rt = (struct rtable *)skb->dst;
+		addr.s_addr = rt->rt_spec_dst;
 	}
-	restore_flags(flags);
-	*start=buffer+(offset-begin);
-	len-=(offset-begin);
-	if(len>length)
-		len=length;	
-	return len;
+	put_cmsg(msg, SOL_IP, local ? IP_LOCALADDR : IP_RECVDSTADDR,
+		 sizeof(addr), &addr);
 }
 
+static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb)
+{
+	if (IPCB(skb)->opt.optlen == 0)
+		return;
+
+	put_cmsg(msg, SOL_IP, IP_RECVOPTS, IPCB(skb)->opt.optlen, skb->nh.iph+1);
+}
 
-/*
- *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
- *	an IP socket.
- *
- *	We implement IP_TOS (type of service), IP_TTL (time to live).
- */
 
-static struct device *ip_mc_find_devfor(unsigned long addr)
+void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb)
 {
-	struct device *dev;
-	for(dev = dev_base; dev; dev = dev->next)
-	{
-		if((dev->flags&IFF_UP)&&(dev->flags&IFF_MULTICAST)&&
-			(dev->pa_addr==addr))
-			return dev;
+	unsigned char optbuf[sizeof(struct ip_options) + 40];
+	struct ip_options * opt = (struct ip_options*)optbuf;
+
+	if (IPCB(skb)->opt.optlen == 0)
+		return;
+
+	if (ip_options_echo(opt, skb)) {
+		msg->msg_flags |= MSG_CTRUNC;
+		return;
 	}
+	ip_options_undo(opt);
 
-	return NULL;
+	put_cmsg(msg, SOL_IP, IP_RETOPTS, opt->optlen, opt->__data);
 }
 
-#endif
+
+void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb)
+{
+	unsigned flags = skb->sk->ip_cmsg_flags;
+
+	/* Ordered by supposed usage frequency */
+	if (flags & 1)
+		ip_cmsg_recv_localaddr(msg, skb, 1);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_localdev(msg, skb);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_opts(msg, skb);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_retopts(msg, skb);
+	if ((flags>>1) == 0)
+		return;
+	if (flags & 1)
+		ip_cmsg_recv_localaddr(msg, skb, 0);
+}
+
+int ip_cmsg_send(struct msghdr *msg, struct ipcm_cookie *ipc, struct device **devp)
+{
+	int err;
+	struct cmsghdr kcm, *cmsg;
+	char	devname[MAX_ADDR_LEN];
+
+	for (cmsg = KCMSG_FIRSTHDR(msg); cmsg; cmsg = KCMSG_NXTHDR(msg, cmsg)) {
+		if (kcm.cmsg_level != SOL_IP)
+			continue;
+		switch (kcm.cmsg_type)
+		{
+		case IP_LOCALADDR:
+			if (kcm.cmsg_len < sizeof(struct in_addr)+sizeof(kcm))
+				return -EINVAL;
+			if (copy_from_user(&ipc->addr, cmsg->cmsg_data, 4))
+				return -EFAULT;
+			break;
+		case IP_RETOPTS:
+			err = kcm.cmsg_len - sizeof(kcm);
+			err = ip_options_getfromuser(&ipc->opt, cmsg->cmsg_data,
+						     err < 40 ? err : 40);
+			if (err)
+				return err;
+			break;
+		case IP_LOCALDEV:
+			if (kcm.cmsg_len < MAX_ADDR_LEN+sizeof(kcm))
+				return -EINVAL;
+			if (!devp)
+				return -EINVAL;
+			if (copy_from_user(devname, cmsg->cmsg_data, MAX_ADDR_LEN-1))
+				return -EFAULT;
+			devname[MAX_ADDR_LEN-1] = 0;
+			if ((*devp = dev_get(devname)) == NULL)
+				return -ENODEV;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/*
+ *	Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
+ *	an IP socket.
+ *
+ *	We implement IP_TOS (type of service), IP_TTL (time to live).
+ */
 
 int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
 {
@@ -143,34 +196,13 @@
 	{
 		case IP_OPTIONS:
 	          {
-			  struct options * opt = NULL;
-			  struct options * old_opt;
+			  struct ip_options * opt = NULL;
+			  struct ip_options * old_opt;
 			  if (optlen > 40 || optlen < 0)
 			  	return -EINVAL;
-			  opt = kmalloc(sizeof(struct options)+((optlen+3)&~3), GFP_KERNEL);
-			  if (!opt)
-			  	return -ENOMEM;
-			  memset(opt, 0, sizeof(struct options));
-			  if (optlen)
-			  {
-				  err = copy_from_user(opt->__data, optval, optlen);
-				  if (err)
-				  {
-					  kfree_s(opt, sizeof(struct options) + ((optlen+3)&~3));
-					  return -EFAULT;
-				  }
-			  }
-
-			  while (optlen & 3)
-			  	opt->__data[optlen++] = IPOPT_END;
-			  opt->optlen = optlen;
-			  opt->is_data = 1;
-			  opt->is_setbyuser = 1;
-			  if (optlen && ip_options_compile(opt, NULL)) 
-			  {
-				  kfree_s(opt, sizeof(struct options) + optlen);
-				  return -EINVAL;
-			  }
+			  err = ip_options_getfromuser(&opt, optval, optlen);
+			  if (err)
+			  	return err;
 			  /*
 			   * ANK: I'm afraid that receive handler may change
 			   * options from under us.
@@ -183,24 +215,44 @@
 			  	kfree_s(old_opt, sizeof(struct optlen) + old_opt->optlen);
 			  return 0;
 		  }
-		case IP_TOS:			/* This sets both TOS and Precedence */
-			if (val & ~0xfe)	/* Reject setting of unused bits */
+		case IP_LOCALADDR:
+			if (val)
+				sk->ip_cmsg_flags |= 1;
+			else
+				sk->ip_cmsg_flags &= ~1;
+			return 0;
+		case IP_LOCALDEV:
+			if (val)
+				sk->ip_cmsg_flags |= 2;
+			else
+				sk->ip_cmsg_flags &= ~2;
+			return 0;
+		case IP_RECVOPTS:
+			if (val)
+				sk->ip_cmsg_flags |= 4;
+			else
+				sk->ip_cmsg_flags &= ~4;
+			return 0;
+		case IP_RETOPTS:
+			if (val)
+				sk->ip_cmsg_flags |= 8;
+			else
+				sk->ip_cmsg_flags &= ~8;
+			return 0;
+		case IP_RECVDSTADDR:
+			if (val)
+				sk->ip_cmsg_flags |= 0x10;
+			else
+				sk->ip_cmsg_flags &= ~0x10;
+			return 0;
+		case IP_TOS:	/* This sets both TOS and Precedence */
+			  /* Reject setting of unused bits */
+			if (val & ~(IPTOS_TOS_MASK|IPTOS_PREC_MASK))
 				return -EINVAL;
-			if ((val>>5) > 4 && !suser())	/* Only root can set Prec>4 */
+			if (IPTOS_PREC(val) >= IPTOS_PREC_CRITIC_ECP && !suser())
 				return -EPERM;
 			sk->ip_tos=val;
-			switch (val & 0x1E) {
-				case IPTOS_LOWDELAY:
-					sk->priority=SOPRI_INTERACTIVE;
-					break;
-				case IPTOS_THROUGHPUT:
-				case IPTOS_MINCOST:
-					sk->priority=SOPRI_BACKGROUND;
-					break;
-				default:
-					sk->priority=SOPRI_NORMAL;
-					break;
-			}
+			sk->priority = rt_tos2priority(val);
 			return 0;
 		case IP_TTL:
 			if(val<1||val>255)
@@ -212,40 +264,56 @@
 				return -ENOPROTOOPT;
 			sk->ip_hdrincl=val?1:0;
 			return 0;
-#ifdef CONFIG_IP_MULTICAST
+		case IP_PMTUDISC:
+			if (val<0 || val>2)
+				return -EINVAL;
+			sk->ip_pmtudisc = val;
+			return 0;
+		case IP_RECVERR:
+			if (sk->type==SOCK_STREAM)
+				return -ENOPROTOOPT;
+			lock_sock(sk);
+			if (sk->ip_recverr && !val) {
+				struct sk_buff *skb;
+				/* Drain queued errors */
+				while((skb=skb_dequeue(&sk->error_queue))!=NULL) {
+					IS_SKB(skb);
+					kfree_skb(skb, FREE_READ);
+				}
+			}
+			sk->ip_recverr = val?1:0;
+			release_sock(sk);
+			return 0;
 		case IP_MULTICAST_TTL: 
-		{
 			sk->ip_mc_ttl=(int)ucval;
 	                return 0;
-		}
 		case IP_MULTICAST_LOOP: 
-		{
 			if(ucval!=0 && ucval!=1)
 				 return -EINVAL;
 			sk->ip_mc_loop=(int)ucval;
 			return 0;
-		}
 		case IP_MULTICAST_IF: 
 		{
 			struct in_addr addr;
-			struct device *dev=NULL;
+			struct device *dev = NULL;
 			
 			/*
 			 *	Check the arguments are allowable
 			 */
-				
+
 			err = copy_from_user(&addr,optval,sizeof(addr));
 			if (err)
 				return -EFAULT; 
 
 			
+			
 			/*
 			 *	What address has been requested
 			 */
 			
-			if(addr.s_addr==INADDR_ANY)	/* Default */
+			if (addr.s_addr==INADDR_ANY)	/* Default */
 			{
-				sk->ip_mc_name[0]=0;
+				memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
 				return 0;
 			}
 			
@@ -253,7 +321,7 @@
 			 *	Find the device
 			 */
 			 
-			dev=ip_mc_find_devfor(addr.s_addr);
+			dev=ip_dev_find(addr.s_addr, NULL);
 						
 			/*
 			 *	Did we find one
@@ -261,11 +329,13 @@
 			 
 			if(dev) 
 			{
-				strcpy(sk->ip_mc_name,dev->name);
+				memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
+				strcpy(sk->ip_mc_name, dev->name);
 				return 0;
 			}
 			return -EADDRNOTAVAIL;
 		}
+
 		
 		case IP_ADD_MEMBERSHIP: 
 		{
@@ -289,26 +359,14 @@
 			 *	Get device for use later
 			 */
 
-			if(mreq.imr_interface.s_addr==INADDR_ANY) 
-			{
-				/*
-				 *	Not set so scan.
-				 */
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
-				{
-					dev=rt->rt_dev;
-					atomic_dec(&rt->rt_use);
-					ip_rt_put(rt);
-				}
-			}
-			else
-			{
-				/*
-				 *	Find a suitable device.
-				 */
-				
-				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
-			}
+			if (mreq.imr_interface.s_addr==INADDR_ANY) {
+				err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL);
+				if (err)
+					return err;
+				dev = rt->u.dst.dev;
+				ip_rt_put(rt);
+			} else
+				dev = ip_dev_find(mreq.imr_interface.s_addr, NULL);
 			
 			/*
 			 *	No device, no cookies.
@@ -342,20 +400,14 @@
 			 *	Get device for use later 
 			 */
  
-			if(mreq.imr_interface.s_addr==INADDR_ANY) 
-			{
-				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
-			        {
-					dev=rt->rt_dev;
-					atomic_dec(&rt->rt_use);
-					ip_rt_put(rt);
-				}
-			}
-			else 
-			{
-			
-				dev=ip_mc_find_devfor(mreq.imr_interface.s_addr);
-			}
+			if (mreq.imr_interface.s_addr==INADDR_ANY) {
+				err = ip_route_output(&rt, mreq.imr_multiaddr.s_addr, 0, 1, NULL);
+				if (err)
+					return err;
+				dev = rt->u.dst.dev;
+				ip_rt_put(rt);
+			} else
+				dev = ip_dev_find(mreq.imr_interface.s_addr, NULL);
 			
 			/*
 			 *	Did we find a suitable device.
@@ -370,7 +422,67 @@
 			 
 			return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
 		}
-#endif			
+
+		case IP_MULTICAST_IFN:
+		{
+			struct device *dev;
+			char *devname;
+			
+			err = getname(optval, &devname);
+			if (err)
+				return err;
+			if (!devname[0])
+			{
+				memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
+				putname(devname);
+				return 0;
+			}
+			dev = dev_get(devname);
+			putname(devname);
+			if (!dev)
+				return -ENODEV;
+
+			memset(sk->ip_mc_name, 0, sizeof(sk->ip_mc_name));
+			strcpy(sk->ip_mc_name,dev->name);
+			return 0;
+		}
+		case IP_ADD_MEMBERSHIPN:
+		{
+			struct ip_mreqn mreq;
+			struct device *dev = NULL;
+			
+			/*
+			 *	Check the arguments.
+			 */
+
+			err = copy_from_user(&mreq,optval,sizeof(mreq));
+			if (err)
+				return -EFAULT; 
+			dev = dev_get(mreq.imr_interface);
+			if (!dev)
+				return -ENODEV;
+			return ip_mc_join_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
+		
+		case IP_DROP_MEMBERSHIPN: 
+		{
+			struct ip_mreqn mreq;
+			struct device *dev=NULL;
+
+			/*
+			 *	Check the arguments
+			 */
+			 
+			err = copy_from_user(&mreq,optval,sizeof(mreq));
+			if (err)
+				return -EFAULT; 
+
+			dev=dev_get(mreq.imr_interface);
+			if(!dev)
+				return -ENODEV;
+			 
+			return ip_mc_leave_group(sk,dev,mreq.imr_multiaddr.s_addr);
+		}
 #ifdef CONFIG_IP_FIREWALL
 		case IP_FW_INSERT_IN:
 		case IP_FW_INSERT_OUT:
@@ -421,7 +533,6 @@
 			err=ip_acct_ctl(optname, &tmp_fw,optlen);
 			return -err;	/* -0 is 0 after all */
 #endif
-		/* IP_OPTIONS and friends go here eventually */
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -435,9 +546,7 @@
 int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
 {
 	int val,err;
-#ifdef CONFIG_IP_MULTICAST
 	int len;
-#endif
 	
 	if(level!=SOL_IP)
 		return -EOPNOTSUPP;
@@ -453,47 +562,18 @@
 	{
 		case IP_OPTIONS:
 			{
-				unsigned char optbuf[sizeof(struct options)+40];
-				struct options * opt = (struct options*)optbuf;
-
+				unsigned char optbuf[sizeof(struct ip_options)+40];
+				struct ip_options * opt = (struct ip_options*)optbuf;
 				cli();
 				opt->optlen = 0;
 				if (sk->opt)
-					memcpy(optbuf, sk->opt, sizeof(struct options)+sk->opt->optlen);
+					memcpy(optbuf, sk->opt, sizeof(struct ip_options)+sk->opt->optlen);
 				sti();
 				if (opt->optlen == 0) 
-				{
 					return put_user(0, optlen);
-				}
-/*
- * Now we should undo all the changes done by ip_options_compile().
- */
-				if (opt->srr) 
-				{
-					unsigned  char * optptr = opt->__data+opt->srr-sizeof(struct  iphdr);
-					memmove(optptr+7, optptr+3, optptr[1]-7);
-					memcpy(optptr+3, &opt->faddr, 4);
-				}
-				if (opt->rr_needaddr) 
-				{
-					unsigned  char * optptr = opt->__data+opt->rr-sizeof(struct  iphdr);
-					memset(&optptr[optptr[2]-1], 0, 4);
-					optptr[2] -= 4;
-				}
-				if (opt->ts) 
-				{
-					unsigned  char * optptr = opt->__data+opt->ts-sizeof(struct  iphdr);
-					if (opt->ts_needtime) 
-					{
-						memset(&optptr[optptr[2]-1], 0, 4);
-						optptr[2] -= 4;
-					}
-					if (opt->ts_needaddr) 
-					{
-						memset(&optptr[optptr[2]-1], 0, 4);
-						optptr[2] -= 4;
-					}
-				}
+
+				ip_options_undo(opt);
+
 				err = put_user(opt->optlen, optlen);
 				if (!err) 
 				{
@@ -503,6 +583,21 @@
 				return err; 
 			}
 			return 0;
+		case IP_LOCALADDR:
+			val = (sk->ip_cmsg_flags & 1) != 0;
+			return 0;
+		case IP_LOCALDEV:
+			val = (sk->ip_cmsg_flags & 2) != 0;
+			return 0;
+		case IP_RECVOPTS:
+			val = (sk->ip_cmsg_flags & 4) != 0;
+			return 0;
+		case IP_RETOPTS:
+			val = (sk->ip_cmsg_flags & 8) != 0;
+			return 0;
+		case IP_RECVDSTADDR:
+			val = (sk->ip_cmsg_flags & 0x10) != 0;
+			return 0;
 		case IP_TOS:
 			val=sk->ip_tos;
 			break;
@@ -512,7 +607,12 @@
 		case IP_HDRINCL:
 			val=sk->ip_hdrincl;
 			break;
-#ifdef CONFIG_IP_MULTICAST			
+		case IP_PMTUDISC:
+			val=sk->ip_pmtudisc;
+			return 0;
+		case IP_RECVERR:
+			val=sk->ip_recverr;
+			return 0;
 		case IP_MULTICAST_TTL:
 			val=sk->ip_mc_ttl;
 			break;
@@ -520,6 +620,7 @@
 			val=sk->ip_mc_loop;
 			break;
 		case IP_MULTICAST_IF:
+		case IP_MULTICAST_IFN:
   			len=strlen(sk->ip_mc_name);
   			err = put_user(len, optlen);
 			if (!err)
@@ -529,7 +630,6 @@
 				    err = -EFAULT;
 			}
 			return err;
-#endif
 		default:
 			return(-ENOPROTOOPT);
 	}
@@ -537,4 +637,6 @@
 	if (err) 
 		return err; 
 	return put_user(val,(int *) optval);
+
+	return(0);
 }
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.1.14/linux/net/ipv4/ipip.c	Mon May 13 12:15:24 1996
+++ linux/net/ipv4/ipip.c	Thu Dec 12 16:54:24 1996
@@ -20,7 +20,7 @@
  */
  
 #include <linux/module.h>
-
+#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -30,6 +30,8 @@
 #include <linux/tcp.h>
 #include <linux/udp.h>
 #include <linux/firewall.h>
+#include <linux/if_arp.h>
+#include <linux/mroute.h>
 
 #include <net/datalink.h>
 #include <net/sock.h>
@@ -38,21 +40,26 @@
 #include <net/protocol.h>
 #include <net/ipip.h>
 
+void ipip_err(struct sk_buff *skb, unsigned char *dp)
+{
+	/* NI */
+	return;
+}
+
 /*
  *	The IPIP protocol driver.
  *
  *	On entry here
  *		skb->data is the original IP header
- *		skb->ip_hdr points to the initial IP header.
- *		skb->h.raw points at the new header.
+ *		skb->nh points to the initial IP header.
+ *		skb->h points at the new header.
  */
 
-int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt, 
-		__u32 daddr, unsigned short len, __u32 saddr,
-                                   int redo, struct inet_protocol *protocol)
+int ipip_rcv(struct sk_buff *skb, unsigned short len)
 {
-	/* Don't unlink in the middle of a turnaround */
-	MOD_INC_USE_COUNT;
+	struct device *dev;
+	struct iphdr *iph;
+
 #ifdef TUNNEL_DEBUG
 	printk("ipip_rcv: got a packet!\n");
 #endif
@@ -60,15 +67,15 @@
 	 *	Discard the original IP header
 	 */
 	 
-	skb_pull(skb, ((struct iphdr *)skb->data)->ihl<<2);
+	skb_pull(skb, skb->h.raw - skb->nh.raw);
 	
 	/*
 	 *	Adjust pointers
 	 */
 	 
-	skb->h.iph=(struct iphdr *)skb->data;
-	skb->ip_hdr=(struct iphdr *)skb->data;
-	memset(skb->proto_priv, 0, sizeof(struct options));
+	iph = skb->nh.iph;
+	skb->nh.iph = skb->h.ipiph;
+	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
 
 	/*
 	 *	If you want to add LZ compressed IP or things like that here,
@@ -77,8 +84,35 @@
 	
 	skb->protocol = htons(ETH_P_IP);
 	skb->ip_summed = 0;
+	skb->pkt_type = PACKET_HOST;
+
+	/*
+	 * Is it draconic? I do not think so. --ANK
+	 */
+	dev = ip_dev_find_tunnel(iph->daddr, iph->saddr);
+	if (dev == NULL) {
+#ifdef CONFIG_IP_MROUTE
+		int vif;
+
+		if (!MULTICAST(skb->nh.iph->daddr) ||
+		    !ipv4_config.multicast_route ||
+		    LOCAL_MCAST(skb->nh.iph->daddr) ||
+		    (vif=ip_mr_find_tunnel(iph->daddr, iph->saddr)) < 0)
+		{
+#endif
+			kfree_skb(skb, FREE_READ);
+			return -EINVAL;
+#ifdef CONFIG_IP_MROUTE
+		}
+		IPCB(skb)->flags |= IPSKB_TUNNELED;
+		IPCB(skb)->vif = vif;
+		dev = skb->dev;
+#endif
+	}
+	skb->dev = dev;
+	dst_release(skb->dst);
+	skb->dst = NULL;
 	netif_rx(skb);
-	MOD_DEC_USE_COUNT;
 	return(0);
 }
 
@@ -86,10 +120,7 @@
 
 static struct inet_protocol ipip_protocol = {
   ipip_rcv,             /* IPIP handler          */
-#if 0
-  NULL,                 /* Will be UDP fraglist handler */
-#endif
-  NULL,                 /* TUNNEL error control    */
+  ipip_err,             /* TUNNEL error control */
   0,                    /* next                 */
   IPPROTO_IPIP,         /* protocol ID          */
   0,                    /* copy                 */
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.1.14/linux/net/ipv4/ipmr.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/ipmr.c	Thu Dec 12 16:54:24 1996
@@ -12,7 +12,7 @@
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
- *	Alan Cox		:	Added the cache manager code.
+ *	Alan Cox		:	Added the cache manager code
  *	Alan Cox		:	Fixed the clone/copy bug and device race.
  *	Malcolm Beattie		:	Buffer handling fixes.
  *	Alexey Kuznetsov	:	Double buffer free and other fixes.
@@ -59,39 +59,66 @@
 
 static struct vif_device vif_table[MAXVIFS];		/* Devices 		*/
 static unsigned long vifc_map;				/* Active device map	*/
+static int maxvif;
 int mroute_do_pim = 0;					/* Set in PIM assert	*/
 static struct mfc_cache *mfc_cache_array[MFC_LINES];	/* Forwarding cache	*/
-static struct mfc_cache *cache_resolve_queue;		/* Unresolved cache	*/
 int cache_resolve_queue_len = 0;			/* Size of unresolved	*/
 
 /*
  *	Delete a VIF entry
  */
  
-static void vif_delete(struct vif_device *v)
+static int vif_delete(int vifi)
 {
-	if(!(v->flags&VIFF_TUNNEL))
-	{
-		v->dev->flags&=~IFF_ALLMULTI;
-		dev_mc_upload(v->dev);
+	struct vif_device *v;
+	
+	if (vifi < 0 || vifi >= maxvif || !(vifc_map&(1<<vifi)))
+		return -EADDRNOTAVAIL;
+
+	v = &vif_table[vifi];
+
+	start_bh_atomic();
+
+	if (!(v->flags&VIFF_TUNNEL)) {
+		v->u.dev->flags &= ~IFF_ALLMULTI;
+		dev_mc_upload(v->u.dev);
+		ip_rt_multicast_event(v->u.dev);
+		v->u.dev = NULL;
+	} else {
+		ip_rt_put(v->u.rt);
+		v->u.rt = NULL;
 	}
-	v->dev=NULL;
+
+	vifc_map&=~(1<<vifi);
+
+	end_bh_atomic();
+
+	if (vifi+1 == maxvif) {
+		int tmp;
+		for (tmp=vifi-1; tmp>=0; tmp--) {
+			if (vifc_map&(1<<tmp))
+				break;
+		}
+		maxvif = tmp+1;
+	}
+	return 0;
 }
 
-/*
- *	Find a vif
- */
- 
-static int ipmr_vifi_find(struct device *dev)
+static void ipmr_set_bounds(struct mfc_cache *cache)
 {
-	struct vif_device *v=&vif_table[0];
-	int ct;
-	for(ct=0;ct<MAXVIFS;ct++,v++)
-	{
-		if(v->dev==dev)
-			return ct;
+	int vifi;
+	for (vifi=0; vifi<maxvif; vifi++) {
+		if (vifc_map&(1<<vifi) && cache->mfc_ttls[vifi]) {
+			cache->mfc_minvif = vifi;
+			cache->mfc_maxvif = vifi+1;
+			vifi++;
+			break;
+		}
+	}
+	for ( ; vifi<maxvif; vifi++) {
+		if (vifc_map&(1<<vifi) && cache->mfc_ttls[vifi])
+			cache->mfc_maxvif = vifi+1;
 	}
-	return -1;
 }
 
 /*
@@ -108,16 +135,11 @@
 	 *	Find the right cache line
 	 */
 
+	line=MFC_HASH(cache->mfc_mcastgrp,cache->mfc_origin);
+	cp=&(mfc_cache_array[line]);
+
 	if(cache->mfc_flags&MFC_QUEUED)
-	{	
-		cp=&cache_resolve_queue;
 		del_timer(&cache->mfc_timer);
-	}	
-	else
-	{
-		line=MFC_HASH(cache->mfc_mcastgrp,cache->mfc_origin);
-		cp=&(mfc_cache_array[line]);
-	}
 	
 	/*
 	 *	Unlink the buffer
@@ -176,6 +198,7 @@
 {
 	int line=MFC_HASH(mcastgrp,origin);
 	struct mfc_cache *cache;
+
 	cache=mfc_cache_array[line];
 	while(cache!=NULL)
 	{
@@ -183,13 +206,6 @@
 			return cache;
 		cache=cache->next;
 	}
-	cache=cache_resolve_queue;
-	while(cache!=NULL)
-	{
-		if(cache->mfc_origin==origin && cache->mfc_mcastgrp==mcastgrp)
-			return cache;
-		cache=cache->next;
-	}
 	return NULL;
 }
 
@@ -207,6 +223,9 @@
 	init_timer(&c->mfc_timer);
 	c->mfc_timer.data=(long)c;
 	c->mfc_timer.function=ipmr_cache_timer;
+	c->mfc_last_assert=0;
+	c->mfc_minvif = MAXVIFS;
+	c->mfc_maxvif = 0;
 	return c;
 }
  
@@ -216,37 +235,28 @@
  
 static void ipmr_cache_resolve(struct mfc_cache *cache)
 {
-	struct mfc_cache **p;
 	struct sk_buff *skb;
+
+	start_bh_atomic();
+
 	/*
 	 *	Kill the queue entry timer.
 	 */
+
 	del_timer(&cache->mfc_timer);
-	cache->mfc_flags&=~MFC_QUEUED;
-	/*
-	 *	Remove from the resolve queue
-	 */
-	p=&cache_resolve_queue;
-	while((*p)!=NULL)
-	{
-		if((*p)==cache)
-		{
-			*p=cache->next;
-			break;
-		}
-		p=&((*p)->next);
+
+	if (cache->mfc_flags&MFC_QUEUED) {
+		cache->mfc_flags&=~MFC_QUEUED;
+		cache_resolve_queue_len--;
 	}
-	cache_resolve_queue_len--;
-	sti();
-	/*
-	 *	Insert into the main cache
-	 */
-	ipmr_cache_insert(cache);
+
+	end_bh_atomic();
+
 	/*
 	 *	Play the pending entries through our router
 	 */
 	while((skb=skb_dequeue(&cache->mfc_unresolved)))
-		ipmr_forward(skb, skb->protocol);
+		ip_mr_input(skb);
 }
 
 /*
@@ -254,50 +264,52 @@
  *	expects the following bizarre scheme..
  */
  
-static void ipmr_cache_report(struct sk_buff *pkt)
+static void ipmr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
 {
-	struct sk_buff *skb=alloc_skb(128, GFP_ATOMIC);
-	int ihl=pkt->ip_hdr->ihl<<2;
+	struct sk_buff *skb = alloc_skb(128, GFP_ATOMIC);
+	int ihl = pkt->nh.iph->ihl<<2;
 	struct igmphdr *igmp;
+	struct igmpmsg *msg;
+
 	if(!skb)
 		return;
 		
-	skb->free=1;
-	
 	/*
 	 *	Copy the IP header
 	 */
 
-	skb->ip_hdr=(struct iphdr *)skb_put(skb,ihl);
-	skb->h.iph=skb->ip_hdr;
+	skb->nh.iph = (struct iphdr *)skb_put(skb, ihl);
 	memcpy(skb->data,pkt->data,ihl);
-	skb->ip_hdr->protocol = 0;			/* Flag to the kernel this is a route add */
+	skb->nh.iph->protocol = 0;			/* Flag to the kernel this is a route add */
+	msg = (struct igmpmsg*)skb->nh.iph;
+	if (assert)
+		msg->im_vif = vifi;
 	
 	/*
 	 *	Add our header
 	 */
 	
 	igmp=(struct igmphdr *)skb_put(skb,sizeof(struct igmphdr));
-	igmp->type	=	IGMPMSG_NOCACHE;		/* non IGMP dummy message */
+	igmp->type	=
+	msg->im_msgtype = assert ? IGMPMSG_WRONGVIF : IGMPMSG_NOCACHE;
 	igmp->code 	=	0;
-	skb->ip_hdr->tot_len=htons(skb->len);			/* Fix the length */
+	skb->nh.iph->tot_len=htons(skb->len);			/* Fix the length */
+	skb->h.raw = skb->nh.raw;
 	
 	/*
 	 *	Deliver to mrouted
 	 */
 	if(sock_queue_rcv_skb(mroute_socket,skb)<0)
 	{
-		skb->sk=NULL;
 		kfree_skb(skb, FREE_READ);
 	}
 }
-		
- 
+
 /*
  *	Queue a packet for resolution
  */
  
-static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct sk_buff *skb, int is_frag)
+static void ipmr_cache_unresolved(struct mfc_cache *cache, vifi_t vifi, struct sk_buff *skb)
 {
 	if(cache==NULL)
 	{	
@@ -313,14 +325,13 @@
 		 *	Fill in the new cache entry
 		 */
 		cache->mfc_parent=vifi;
-		cache->mfc_origin=skb->ip_hdr->saddr;
-		cache->mfc_mcastgrp=skb->ip_hdr->daddr;
+		cache->mfc_origin=skb->nh.iph->saddr;
+		cache->mfc_mcastgrp=skb->nh.iph->daddr;
 		cache->mfc_flags=MFC_QUEUED;
 		/*
 		 *	Link to the unresolved list
 		 */
-		cache->next=cache_resolve_queue;
-		cache_resolve_queue=cache;
+		ipmr_cache_insert(cache);
 		cache_resolve_queue_len++;
 		/*
 		 *	Fire off the expiry timer
@@ -331,7 +342,7 @@
 		 *	Reflect first query at mrouted.
 		 */
 		if(mroute_socket)
-			ipmr_cache_report(skb);
+			ipmr_cache_report(skb, vifi, 0);
 	}
 	/*
 	 *	See if we can append the packet
@@ -341,12 +352,7 @@
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
-	/*
-	 *	Add to our 'pending' list. Cache the is_frag data
-	 *	in skb->protocol now it is spare.
-	 */
 	cache->mfc_queuelen++;
-	skb->protocol=is_frag;
 	skb_queue_tail(&cache->mfc_unresolved,skb);
 }
 
@@ -357,13 +363,14 @@
 int ipmr_mfc_modify(int action, struct mfcctl *mfc)
 {
 	struct mfc_cache *cache;
+
 	if(!MULTICAST(mfc->mfcc_mcastgrp.s_addr))
 		return -EINVAL;
 	/*
 	 *	Find the cache line
 	 */
 	
-	cli();
+	start_bh_atomic();
 
 	cache=ipmr_cache_find(mfc->mfcc_origin.s_addr,mfc->mfcc_mcastgrp.s_addr);
 	
@@ -375,20 +382,23 @@
 		if(cache)
 		{
 			ipmr_cache_delete(cache);
-			sti();
+			end_bh_atomic();
 			return 0;
 		}
-		sti();
+		end_bh_atomic();
 		return -ENOENT;
 	}
 	if(cache)
 	{
+
 		/*
 		 *	Update the cache, see if it frees a pending queue
 		 */
 
 		cache->mfc_flags|=MFC_RESOLVED;
+		cache->mfc_parent=mfc->mfcc_parent;
 		memcpy(cache->mfc_ttls, mfc->mfcc_ttls,sizeof(cache->mfc_ttls));
+		ipmr_set_bounds(cache);
 		 
 		/*
 		 *	Check to see if we resolved a queued list. If so we
@@ -397,9 +407,10 @@
 		 
 		if(cache->mfc_flags&MFC_QUEUED)
 			ipmr_cache_resolve(cache);	/* Unhook & send the frames */
-		sti();
+		end_bh_atomic();
 		return 0;
 	}
+
 	/*
 	 *	Unsolicited update - that's ok, add anyway.
 	 */
@@ -408,7 +419,7 @@
 	cache=ipmr_cache_alloc(GFP_ATOMIC);
 	if(cache==NULL)
 	{
-		sti();
+		end_bh_atomic();
 		return -ENOMEM;
 	}
 	cache->mfc_flags=MFC_RESOLVED;
@@ -416,8 +427,9 @@
 	cache->mfc_mcastgrp=mfc->mfcc_mcastgrp.s_addr;
 	cache->mfc_parent=mfc->mfcc_parent;
 	memcpy(cache->mfc_ttls, mfc->mfcc_ttls,sizeof(cache->mfc_ttls));
+	ipmr_set_bounds(cache);
 	ipmr_cache_insert(cache);
-	sti();
+	end_bh_atomic();
 	return 0;
 }
  
@@ -458,9 +470,11 @@
 			if(mroute_socket)
 				return -EADDRINUSE;
 			mroute_socket=sk;
+			ipv4_config.multicast_route = 1;
 			/* Initialise state */
 			return 0;
 		case MRT_DONE:
+			ipv4_config.multicast_route = 0;
 			mroute_close(sk);
 			mroute_socket=NULL;
 			return 0;
@@ -481,7 +495,7 @@
 				if(vifc_map&(1<<vif.vifc_vifi))
 					return -EADDRINUSE;
 				/* Find the interface */
-				dev=ip_dev_find(vif.vifc_lcl_addr.s_addr);
+				dev=ip_dev_find(vif.vifc_lcl_addr.s_addr, NULL);
 				if(!dev)
 					return -EADDRNOTAVAIL;
 				/* Must be tunnelled or multicastable */
@@ -489,7 +503,6 @@
 				{
 					if(vif.vifc_flags&VIFF_SRCRT)
 						return -EOPNOTSUPP;
-					/* IPIP will do all the work */
 				}
 				else
 				{
@@ -499,6 +512,7 @@
 						   how to do this yet.. */
 						dev->flags|=IFF_ALLMULTI;
 						dev_mc_upload(dev);
+						ip_rt_multicast_event(dev);
 					}
 					else
 					{
@@ -515,30 +529,21 @@
 				v->remote=vif.vifc_rmt_addr.s_addr;
 				v->flags=vif.vifc_flags;
 				v->threshold=vif.vifc_threshold;
-				v->dev=dev;
+				v->u.dev=NULL;
+				if (!(vif.vifc_flags&VIFF_TUNNEL))
+					v->u.dev=dev;
 				v->bytes_in = 0;
 				v->bytes_out = 0;
 				v->pkt_in = 0;
 				v->pkt_out = 0;
 				vifc_map|=(1<<vif.vifc_vifi);
+				if (vif.vifc_vifi+1 > maxvif)
+					maxvif = vif.vifc_vifi+1;
 				sti();
 				return 0;
-			}
-			else
-			/*
-			 *	VIF deletion
-			 */
-			{
-				struct vif_device *v=&vif_table[vif.vifc_vifi];
-				if(vifc_map&(1<<vif.vifc_vifi))
-				{
-					vif_delete(v);
-					vifc_map&=~(1<<vif.vifc_vifi);
-					return 0;					
-				}
-				else
-					return -EADDRNOTAVAIL;
-			}
+			} else
+				return vif_delete(vif.vifc_vifi);
+
 		/*
 		 *	Manipulate the forwarding caches. These live
 		 *	in a sort of kernel/user symbiosis.
@@ -609,6 +614,7 @@
 	struct sioc_sg_req sr;
 	struct sioc_vif_req vr;
 	struct vif_device *vif;
+	struct mfc_cache *c;
 	
 	switch(cmd)
 	{
@@ -616,7 +622,7 @@
 			err = copy_from_user(&vr,(void *)arg,sizeof(vr));
 			if (err)
 				return -EFAULT; 
-			if(vr.vifi>=MAXVIFS)
+			if(vr.vifi>=maxvif)
 				return -EINVAL;
 			vif=&vif_table[vr.vifi];
 			if(vifc_map&(1<<vr.vifi))
@@ -629,15 +635,28 @@
 				if (err)
 					err = -EFAULT;
 				return err;
+				return 0;
 			}
 			return -EADDRNOTAVAIL;
 		case SIOCGETSGCNT:
 			err = copy_from_user(&sr,(void *)arg,sizeof(sr));
-			if (!err)
-				err = copy_to_user((void *)arg,&sr,sizeof(sr));
 			if (err)
-				err = -EFAULT;
-			return err;
+				return -EFAULT; 
+			for (c = mfc_cache_array[MFC_HASH(sr.grp.s_addr, sr.src.s_addr)];
+			     c; c = c->next) {
+				if (sr.grp.s_addr == c->mfc_mcastgrp &&
+				    sr.src.s_addr == c->mfc_origin) {
+					sr.pktcnt = c->mfc_pkt;
+					sr.bytecnt = c->mfc_bytes;
+					sr.wrong_if = c->mfc_wrong_if;
+					err = copy_to_user((void *)arg,&sr,sizeof(sr));
+					if (err)
+						err = -EFAULT;
+					return err;
+					return 0;
+				}
+			}
+			return -EADDRNOTAVAIL;
 		default:
 			return -EINVAL;
 	}
@@ -650,34 +669,24 @@
 void mroute_close(struct sock *sk)
 {
 	int i;
-	struct vif_device *v=&vif_table[0];
 		
 	/*
 	 *	Shut down all active vif entries
 	 */
 	 
-	for(i=0;i<MAXVIFS;i++)
-	{
-		if(vifc_map&(1<<i))
-		{
-			if(!(v->flags&VIFF_TUNNEL))
-			{
-				v->dev->flags&=~IFF_ALLMULTI;
-				dev_mc_upload(v->dev);
-			}
-		}
-		v++;
-	}		
-	vifc_map=0;	
+	for(i=0; i<maxvif; i++)
+		vif_delete(i);
+
 	/*
 	 *	Wipe the cache
 	 */
 	for(i=0;i<MFC_LINES;i++)
 	{
+		start_bh_atomic();
 		while(mfc_cache_array[i]!=NULL)
 			ipmr_cache_delete(mfc_cache_array[i]);
-	}	
-	/* The timer will clear any 'pending' stuff */
+		end_bh_atomic();
+	}
 }
 
 static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
@@ -687,13 +696,10 @@
 	if(event!=NETDEV_DOWN)
 		return NOTIFY_DONE;
 	v=&vif_table[0];
-	for(ct=0;ct<MAXVIFS;ct++)
+	for(ct=0;ct<maxvif;ct++)
 	{
-		if((vifc_map&(1<<ct)) && v->dev==ptr)
-		{
-			vif_delete(v);
-			vifc_map&=~(1<<ct);
-		}
+		if(vifc_map&(1<<ct) && !(v->flags&VIFF_TUNNEL) && v->u.dev==ptr)
+			vif_delete(ct);
 		v++;
 	}
 	return NOTIFY_DONE;
@@ -707,104 +713,204 @@
 };
 
 /*
+ * 	Encapsulate a packet by attaching a valid IPIP header to it.
+ *	This avoids tunnel drivers and other mess and gives us the speed so
+ *	important for multicast video.
+ */
+ 
+static void ip_encap(struct sk_buff *skb, u32 saddr, u32 daddr)
+{
+	struct iphdr *iph = (struct iphdr *)skb_push(skb,sizeof(struct iphdr));
+
+	iph->version	= 	4;
+	iph->tos	=	skb->nh.iph->tos;
+	iph->ttl	=	skb->nh.iph->ttl;
+	iph->frag_off	=	0;
+	iph->daddr	=	daddr;
+	iph->saddr	=	saddr;
+	iph->protocol	=	IPPROTO_IPIP;
+	iph->ihl	=	5;
+	iph->tot_len	=	htons(skb->len);
+	iph->id		=	htons(ip_id_count++);
+	ip_send_check(iph);
+
+	skb->h.ipiph = skb->nh.iph;
+	skb->nh.iph = iph;
+}
+
+/*
  *	Processing handlers for ipmr_forward
  */
 
-static void ipmr_queue_xmit(struct sk_buff *skb, struct vif_device *vif, struct device *in_dev, int frag)
+static void ipmr_queue_xmit(struct sk_buff *skb, struct mfc_cache *c,
+			   int vifi, int last)
 {
-	int tunnel=0;
-	__u32 raddr=skb->raddr;
-	if(vif->flags&VIFF_TUNNEL)
-	{
-		tunnel=IPFWD_MULTITUNNEL;
-		raddr=vif->remote;
+	struct iphdr *iph = skb->nh.iph;
+	struct vif_device *vif = &vif_table[vifi];
+	struct device *dev;
+	struct rtable *rt;
+	int    encap = 0;
+	struct sk_buff *skb2;
+	int    err;
+		
+	if (vif->flags&VIFF_TUNNEL) {
+		rt = vif->u.rt;
+		if (!rt || rt->u.dst.obsolete) {
+			ip_rt_put(rt);
+			vif->u.rt = NULL;
+			err = ip_route_output(&rt, vif->remote, vif->local, RT_TOS(iph->tos), NULL);
+			if (err)
+				return;
+			vif->u.rt = rt;
+		}
+		dst_clone(&rt->u.dst);
+		encap = sizeof(struct iphdr);
+	} else {
+		dev = vif->u.dev;
+		if (dev == NULL)
+			return;
+		err = ip_route_output(&rt, iph->daddr, 0, RT_TOS(iph->tos), dev);
+		if (err)
+			return;
+	}
+
+	dev = rt->u.dst.dev;
+
+	if (skb->len+encap > dev->mtu && (ntohs(iph->frag_off) & IP_DF)) {
+		ip_statistics.IpFragFails++;
+		ip_rt_put(rt);
+		return;
 	}
+
+	encap += dev->hard_header_len;
+
+	if (skb->len+encap > 65534) {
+		ip_rt_put(rt);
+		return;
+	}
+
+	if (skb_headroom(skb) < encap || (encap && !last))
+		skb2 = skb_realloc_headroom(skb, (encap + 15)&~15);
+	else
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+
+	if (skb2 == NULL) {
+		ip_rt_put(rt);
+		return;
+	}
+
 	vif->pkt_out++;
 	vif->bytes_out+=skb->len;
-	skb->dev=vif->dev;
-	skb->raddr=skb->h.iph->daddr;
-	/*
-	 *	If the vif went down as we were forwarding.. just throw the
-	 *	frame.
-	 */
-	if(vif->dev==NULL || ip_forward(skb, in_dev, frag|IPFWD_MULTICASTING|tunnel, raddr)==-1)
-		kfree_skb(skb, FREE_WRITE);
+
+	dst_release(skb2->dst);
+	skb2->dst = &rt->u.dst;
+
+	iph = skb2->nh.iph;
+	ip_decrease_ttl(iph);
+
+	if (vif->flags & VIFF_TUNNEL)
+		ip_encap(skb2, vif->local, vif->remote);
+
+	ip_send(skb2);
 }
 
 /*
  *	Multicast packets for forwarding arrive here
  */
 
-void ipmr_forward(struct sk_buff *skb, int is_frag)
+int ip_mr_input(struct sk_buff *skb)
 {
 	struct mfc_cache *cache;
-	struct sk_buff *skb2;
 	int psend = -1;
-	int vif=ipmr_vifi_find(skb->dev);
-	if(vif==-1)
-	{
-		kfree_skb(skb, FREE_WRITE);
-		return;
+	int vif, ct;
+	int local = 0;
+	int tunneled = IPCB(skb)->flags&IPSKB_TUNNELED;
+
+	cache = ipmr_cache_find(skb->nh.iph->saddr, skb->nh.iph->daddr);
+	
+	/*
+	 *	No usable cache entry
+	 */
+	 
+	if (cache==NULL || (cache->mfc_flags&MFC_QUEUED)) {
+		ipmr_cache_unresolved(cache, ALL_VIFS, skb);
+		return -EAGAIN;
 	}
 
+	vif = cache->mfc_parent;
+	cache->mfc_pkt++;
+	cache->mfc_bytes += skb->len;
+
 	/*
-	 *	Without the following addition, skb->h.iph points to something
-	 *	different that is not the ip header.
+	 * Wrong interface: drop packet and (maybe) send PIM assert.
 	 */
-	
-	skb->h.iph = skb->ip_hdr;  /* Anand, ernet.  */
+	if (vif >= maxvif || !(vifc_map&(1<<vif)) ||
+	    (tunneled && IPCB(skb)->vif != vif) ||
+	    (!tunneled && (vif_table[vif].flags&VIFF_TUNNEL ||
+	     vif_table[vif].u.dev != skb->dev))) {
+		cache->mfc_wrong_if++;
+		if (vif < MAXVIFS && mroute_do_pim &&
+		    !(vif_table[vif].flags&VIFF_TUNNEL) &&
+		    skb->dev->flags&IFF_BROADCAST &&
+		    jiffies - cache->mfc_last_assert > MFC_ASSERT_THRESH) {
+			cache->mfc_last_assert = jiffies;
+			/*
+			 *	It is wrong! Routing daemon can
+			 *	determine vif itself, but it cannot
+			 *	determine REAL device.
+			 *	BSD bug. Fix it later, PIM does not
+			 *	work in any case 8) _ANK_
+			 */
+			ipmr_cache_report(skb, vif, 1);
+		}
+		kfree_skb(skb, FREE_WRITE);
+		return -EINVAL;
+	}
 
 	vif_table[vif].pkt_in++;
 	vif_table[vif].bytes_in+=skb->len;
-	
-	cache=ipmr_cache_find(skb->ip_hdr->saddr,skb->ip_hdr->daddr);
-	
+
+	if (IPCB(skb)->opt.router_alert ||
+	    ((struct rtable*)skb->dst)->rt_flags&RTF_LOCAL ||
+	    skb->nh.iph->protocol == IPPROTO_IGMP)
+		local = 1;
+
 	/*
-	 *	No usable cache entry
+	 *	Forward the frame
 	 */
-	 
-	if(cache==NULL || (cache->mfc_flags&MFC_QUEUED))
-		ipmr_cache_unresolved(cache,vif,skb, is_frag);
-	else
-	{
+	ct = cache->mfc_maxvif-1;
+	while (ct>=cache->mfc_minvif) {
 		/*
-		 *	Forward the frame
+		 *	0 means don't do it. Silly idea, 255 as don't do it would be cleaner!
 		 */
-		 int ct=0;
-		 while(ct<MAXVIFS)
-		 {
-		 	/*
-		 	 *	0 means don't do it. Silly idea, 255 as don't do it would be cleaner!
-		 	 */
-		 	if(skb->ip_hdr->ttl > cache->mfc_ttls[ct] && cache->mfc_ttls[ct]>0)
-		 	{
-		 		if(psend!=-1)
-		 		{
-		 			/*
-		 			 *	May get variant mac headers
-		 			 *	so must copy -- boo hoo.
-		 			 */
-		 			skb2=skb_copy(skb, GFP_ATOMIC);
-		 			if(skb2)
-		 			{
-		 				skb2->free=1;
-		 				ipmr_queue_xmit(skb2, &vif_table[psend], skb->dev, is_frag);
-		 			}
-				}
-				psend=ct;
-			}
-			ct++;
+		if (skb->nh.iph->ttl > cache->mfc_ttls[ct] && cache->mfc_ttls[ct]>0) {
+			if (psend != -1)
+				ipmr_queue_xmit(skb, cache, psend, 0);
+			psend=ct;
 		}
-		if(psend==-1)
-			kfree_skb(skb, FREE_WRITE);
-		else
-		{
-			ipmr_queue_xmit(skb, &vif_table[psend], skb->dev, is_frag);
-		}
-		/*
-		 *	Adjust the stats
-		 */
+		ct--;
 	}
+	if (psend != -1)
+		ipmr_queue_xmit(skb, cache, psend, 1);
+	if (!local) {
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+	return ip_local_deliver(skb);
+}
+
+int ip_mr_find_tunnel(u32 local, u32 remote)
+{
+	int ct;
+	struct vif_device *vif;
+
+	for (ct=0; ct<maxvif; ct++) {
+		vif = &vif_table[ct];
+		if (vifc_map&(1<<ct) && vif->flags&VIFF_TUNNEL &&
+		    vif->local == local && vif->remote == remote)
+			return ct;
+	}
+	return -1;
 }
 
 /*
@@ -824,15 +930,13 @@
 		 "Interface  Bytes In  Pkts In  Bytes Out  Pkts Out  Flags Local    Remote\n");
 	pos=len;
   
-	for (ct=0;ct<MAXVIFS;ct++) 
+	for (ct=0;ct<maxvif;ct++) 
 	{
 		vif=&vif_table[ct];
 		if(!(vifc_map&(1<<ct)))
 			continue;
-		if(vif->dev==NULL)
-			continue;
-        	size = sprintf(buffer+len, "%-10s %8ld  %7ld  %8ld   %7ld   %05X %08lX %08lX\n",
-        		vif->dev->name,vif->bytes_in, vif->pkt_in, vif->bytes_out,vif->pkt_out,
+        	size = sprintf(buffer+len, "%2d %-10s %8ld  %7ld  %8ld   %7ld   %05X %08lX %08lX\n",
+        		ct, vif->flags&VIFF_TUNNEL ? "Tunnel" : vif->u.dev->name, vif->bytes_in, vif->pkt_in, vif->bytes_out, vif->pkt_out,
         		vif->flags, vif->local, vif->remote);
 		len+=size;
 		pos+=size;
@@ -862,40 +966,44 @@
 	int ct;
 
 	len += sprintf(buffer,
-		 "Group    Origin   SrcIface \n");
+		 "Group    Origin   SrcIface Pkts     Bytes    Wrong    VifTtls\n");
 	pos=len;
   
 	for (ct=0;ct<MFC_LINES;ct++) 
 	{
-		cli();
+		start_bh_atomic();
 		mfc=mfc_cache_array[ct];
 		while(mfc!=NULL)
 		{
 			char *name="none";
-			char vifmap[MAXVIFS+1];
 			int n;
 			/*
 			 *	Device name
 			 */
-			if(vifc_map&(1<<mfc->mfc_parent))
-				name=vif_table[mfc->mfc_parent].dev->name;
-			/*
-			 *	Interface forwarding map
-			 */
-			for(n=0;n<MAXVIFS;n++)
-				if(vifc_map&(1<<n) && mfc->mfc_ttls[ct])
-					vifmap[n]='X';
+			if(mfc->mfc_parent < maxvif && vifc_map&(1<<mfc->mfc_parent)) {
+				if (vif_table[mfc->mfc_parent].flags&VIFF_TUNNEL)
+					name="Tunnel";
 				else
-					vifmap[n]='-';
-			vifmap[n]=0;
+					name=vif_table[mfc->mfc_parent].u.dev->name;
+			}
 			/*
-			 *	Now print it out
+			 *	Interface forwarding map
 			 */
-			size = sprintf(buffer+len, "%08lX %08lX %-8s %s\n",
+			size = sprintf(buffer+len, "%08lX %08lX %-8s %8ld %8ld %8ld",
 				(unsigned long)mfc->mfc_mcastgrp,
 				(unsigned long)mfc->mfc_origin,
 				name,
-				vifmap);
+				mfc->mfc_bytes,
+				mfc->mfc_pkt,
+				mfc->mfc_wrong_if);
+			for(n=0;n<maxvif;n++)
+			{
+				if(vifc_map&(1<<n))
+					size += sprintf(buffer+len+size, " %-3d", mfc->mfc_ttls[n]);
+				else
+					size += sprintf(buffer+len+size, " --- ");
+			}
+			size += sprintf(buffer+len+size, "\n");
 			len+=size;
 			pos+=size;
 			if(pos<offset)
@@ -910,7 +1018,7 @@
 			}
 			mfc=mfc->next;
 	  	}
-	  	sti();
+	  	end_bh_atomic();
   	}
 done:
   	*start=buffer+(offset-begin);
@@ -934,6 +1042,7 @@
 	ipmr_mfc_info
 };
 #endif	
+
 
 /*
  *	Setup for IP multicast routing
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v2.1.14/linux/net/ipv4/packet.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipv4/packet.c	Thu Dec 12 16:54:24 1996
@@ -96,7 +96,6 @@
 	 
 	if(sock_queue_rcv_skb(sk,skb)<0)
 	{
-		skb->sk = NULL;
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -113,8 +112,7 @@
  *	protocol layers and you must therefore supply it with a complete frame
  */
  
-static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len,
-	      int noblock, int flags)
+static int packet_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
 	struct sk_buff *skb;
 	struct device *dev;
@@ -126,7 +124,7 @@
 	 *	Check the flags. 
 	 */
 
-	if (flags) 
+	if (msg->msg_flags&~MSG_DONTWAIT) 
 		return(-EINVAL);
 
 	/*
@@ -179,11 +177,11 @@
 	 *	Fill it in 
 	 */
 	 
-	skb->sk = sk;
-	skb->free = 1;
 	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 	skb->arp = 1;	/* No ARP needs doing on this (complete) frame */
 	skb->protocol = proto;
+	skb->dev = dev;
+	skb->priority = sk->priority;
 
 	/*
 	 *	Now send it
@@ -207,7 +205,7 @@
 		return err;
 	}
 		
-	dev_queue_xmit(skb, dev, sk->priority);
+	dev_queue_xmit(skb);
 	return(len);
 }
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.14/linux/net/ipv4/proc.c	Sun Nov 10 20:12:29 1996
+++ linux/net/ipv4/proc.c	Thu Dec 12 16:54:24 1996
@@ -84,9 +84,10 @@
  *	a memory timer destroy. Instead of playing with timers we just
  *	concede defeat and cli().
  */
+	start_bh_atomic();
+
 	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
 	{
-	  	cli();
 		sp = s_array[i];
 
 		while(sp != NULL) 
@@ -130,10 +131,9 @@
 				format==0?sp->write_seq-tp->snd_una:sp->wmem_alloc, 
 				format==0?tp->rcv_nxt-sp->copied_seq:sp->rmem_alloc,
 				timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
-				(sp->socket&&SOCK_INODE(sp->socket))?SOCK_INODE(sp->socket)->i_uid:0,
+				sp->socket ? sp->socket->inode->i_uid:0,
 				timer_active?sp->timeout:0,
-				sp->socket && SOCK_INODE(sp->socket) ?
-				SOCK_INODE(sp->socket)->i_ino : 0);
+				sp->socket ? sp->socket->inode->i_ino:0);
 
 			if (timer_active1) add_timer(&sp->retransmit_timer);
 			if (timer_active2) add_timer(&sp->timer);
@@ -147,13 +147,10 @@
 				break;
 			sp = sp->next;
 		}
-		sti();	/* We only turn interrupts back on for a moment,
-			   but because the interrupt queues anything built
-			   up before this will clear before we jump back
-			   and cli(), so it's not as bad as it looks */
 		if(len>= length)
 			break;
 	}
+	end_bh_atomic();
 	begin = len - (pos - offset);
 	*start = buffer + begin;
 	len -= begin;
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/protocol.c linux/net/ipv4/protocol.c
--- v2.1.14/linux/net/ipv4/protocol.c	Sun Nov 10 20:12:29 1996
+++ linux/net/ipv4/protocol.c	Thu Dec 12 16:54:24 1996
@@ -46,13 +46,12 @@
 #include <linux/igmp.h>
 
 
-#ifdef CONFIG_IP_FORWARD
 #ifdef CONFIG_NET_IPIP
 
 static struct inet_protocol ipip_protocol = 
 {
 	ipip_rcv,             /* IPIP handler          */
-	NULL,                 /* TUNNEL error control    */
+	ipip_err,             /* TUNNEL error control    */
 	0,                    /* next                 */
 	IPPROTO_IPIP,         /* protocol ID          */
 	0,                    /* copy                 */
@@ -62,13 +61,12 @@
 
 
 #endif
-#endif
 
 static struct inet_protocol tcp_protocol = 
 {
 	tcp_v4_rcv,		/* TCP handler		*/
 	tcp_v4_err,		/* TCP error control	*/  
-#if defined(CONFIG_NET_IPIP) && defined(CONFIG_IP_FORWARD)
+#ifdef CONFIG_NET_IPIP
 	&ipip_protocol,
 #else  
 	NULL,			/* next			*/
@@ -103,9 +101,6 @@
 	"ICMP"			/* name			*/
 };
 
-#ifndef CONFIG_IP_MULTICAST
-struct inet_protocol *inet_protocol_base = &icmp_protocol;
-#else
 static struct inet_protocol igmp_protocol = 
 {
 	igmp_rcv,		/* IGMP handler		*/
@@ -118,7 +113,6 @@
 };
 
 struct inet_protocol *inet_protocol_base = &igmp_protocol;
-#endif
 
 struct inet_protocol *inet_protos[MAX_INET_PROTOS] = 
 {
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v2.1.14/linux/net/ipv4/rarp.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/rarp.c	Thu Dec 12 16:54:24 1996
@@ -330,11 +330,14 @@
  *	Is it reachable directly ?
  */
   
-	rt = ip_rt_route(ip, 0);
-	if (rt == NULL)
-		return -ENETUNREACH;
-	dev = rt->rt_dev;
-	ip_rt_put(rt);
+	err = ip_route_output(&rt, ip, 0, 1, NULL);
+	if (err)
+		return err;
+	if (rt->rt_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTF_NAT)) {
+		ip_rt_put(rt);
+		return -EINVAL;
+	}
+	dev = rt->u.dst.dev;
 
 /*
  *	Is there an existing entry for this address?  Find out...
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.1.14/linux/net/ipv4/raw.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/raw.c	Thu Dec 12 16:54:24 1996
@@ -64,54 +64,38 @@
 struct sock *mroute_socket=NULL;
 #endif
 
-
 /*
  *	Raw_err does not currently get called by the icmp module - FIXME:
  */
  
-void raw_err (int type, int code, unsigned char *header, __u32 daddr,
-	 __u32 saddr, struct inet_protocol *protocol)
+void raw_err (struct sock *sk, struct sk_buff *skb)
 {
-	struct sock *sk;
-   
-	if (protocol == NULL) 
-		return;
-	sk = (struct sock *) protocol->data;
-	if (sk == NULL) 
-		return;
+	int type = skb->h.icmph->type;
+	int code = skb->h.icmph->code;
 
-	/* This is meaningless in raw sockets. */
-	if (type == ICMP_SOURCE_QUENCH) 
-	{
-		if (sk->cong_window > 1) sk->cong_window = sk->cong_window/2;
-		return;
-	}
-	
-	if(type == ICMP_PARAMETERPROB)
-	{
-		sk->err = EPROTO;
-		sk->error_report(sk);
+	if (sk->ip_recverr && !sk->users) {
+		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 && sock_queue_err_skb(sk, skb2))
+			kfree_skb(skb, FREE_READ);
 	}
 
-	if(code<=NR_ICMP_UNREACH)
-	{
-		sk->err = icmp_err_convert[code & 0xff].errno;
-		sk->error_report(sk);
+	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+		if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
+			sk->err = EMSGSIZE;
+			sk->error_report(sk);
+		}
 	}
-	
-	return;
 }
 
-static inline int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
+static int raw_rcv_skb(struct sock * sk, struct sk_buff * skb)
 {
 	/* Charge it to the socket. */
 	
 	if (__sock_queue_rcv_skb(sk,skb)<0)
 	{
 		ip_statistics.IpInDiscards++;
-		skb->sk=NULL;
 		kfree_skb(skb, FREE_READ);
-		return 0;
+		return -1;
 	}
 
 	ip_statistics.IpInDelivers++;
@@ -124,27 +108,13 @@
  *	in ip.c
  */
 
-int raw_rcv(struct sock *sk, struct sk_buff *skb, struct device *dev, __u32 saddr, __u32 daddr)
+int raw_rcv(struct sock *sk, struct sk_buff *skb)
 {
 	/* Now we need to copy this into memory. */
-	skb->sk = sk;
-	skb_trim(skb,ntohs(skb->ip_hdr->tot_len));
+	skb_trim(skb, ntohs(skb->nh.iph->tot_len));
 	
-	skb->h.raw = (unsigned char *) skb->ip_hdr;
-	skb->dev = dev;
-	skb->saddr = daddr;
-	skb->daddr = saddr;
+	skb->h.raw = skb->nh.raw;
 
-#if 0	
-	/*
-	 *	For no adequately explained reasons BSD likes to mess up the header of
-	 *	the received frame. 
-	 */
-	 
-	if(sk->bsdism)
-		skb->ip_hdr->tot_len=ntohs(skb->ip_hdr->tot_len-4*skb->ip_hdr->ihl);
-#endif
-	
 	if (sk->users) {
 		__skb_queue_tail(&sk->back_log, skb);
 		return 0;
@@ -153,6 +123,12 @@
 	return 0;
 }
 
+struct rawfakehdr 
+{
+	const char *from;
+	u32	saddr;
+};
+
 /*
  *	Send a RAW IP packet.
  */
@@ -161,30 +137,30 @@
  *	Callback support is trivial for SOCK_RAW
  */
   
-static int raw_getfrag(const void *p, __u32 saddr, char *to,
-		       unsigned int offset, unsigned int fraglen)
+static int raw_getfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
 {
-	return copy_from_user(to, (const unsigned char *)p+offset, fraglen);
+	struct rawfakehdr *rfh = (struct rawfakehdr*)p;
+	return copy_from_user(to, (const unsigned char *)rfh->from+offset, fraglen);
 }
 
 /*
  *	IPPROTO_RAW needs extra work.
  */
  
-static int raw_getrawfrag(const void *p, __u32 saddr, char *to, unsigned int offset, unsigned int fraglen)
+static int raw_getrawfrag(const void *p, char *to, unsigned int offset, unsigned int fraglen)
 {
+	struct rawfakehdr *rfh = (struct rawfakehdr*)p;
 	int err; 
 	err = copy_from_user(to, (const unsigned char *)p+offset, fraglen);
 	if (err)
 		return err; 
-	if(offset==0)
-	{
-		struct iphdr *iph=(struct iphdr *)to;
-		if(!iph->saddr)
-			iph->saddr=saddr;
+	if (offset==0) {
+		struct iphdr *iph = (struct iphdr *)to;
+		if (!iph->saddr)
+			iph->saddr = rfh->saddr;
 		iph->check=0;
 		iph->tot_len=htons(fraglen);	/* This is right as you can't frag
-					   RAW packets */
+						   RAW packets */
 		/*
 	 	 *	Deliberate breach of modularity to keep 
 	 	 *	ip_build_xmit clean (well less messy).
@@ -193,87 +169,132 @@
 			iph->id = htons(ip_id_count++);
 		iph->check=ip_fast_csum((unsigned char *)iph, iph->ihl);
 	}
-	return 0; 
+	return 0;
 }
 
 static int raw_sendto(struct sock *sk, const unsigned char *from, 
-	int len, int noblock, unsigned flags, struct sockaddr_in *usin, int addr_len)
+		      int len, struct msghdr *msg)
 {
+	struct device *dev = NULL;
+	struct ipcm_cookie ipc;
+	struct rawfakehdr rfh;
+	struct rtable *rt;
+	int free = 0;
+	u32 daddr;
+	u8  tos;
 	int err;
-	struct sockaddr_in sin;
+
+	if (len>65535)
+		return -EMSGSIZE;
 
 	/*
-	 *	Check the flags. Only MSG_DONTROUTE is permitted.
+	 *	Check the flags.
 	 */
 
-	if (flags & MSG_OOB)		/* Mirror BSD error message compatibility */
+	if (msg->msg_flags & MSG_OOB)		/* Mirror BSD error message compatibility */
 		return -EOPNOTSUPP;
 			 
-	if (flags & ~MSG_DONTROUTE)
+	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
 		return(-EINVAL);
+
 	/*
 	 *	Get and verify the address. 
 	 */
 
-	if (usin) 
-	{
-		if (addr_len < sizeof(sin)) 
-			return(-EINVAL);
-		memcpy(&sin, usin, sizeof(sin));
-		if (sin.sin_family && sin.sin_family != AF_INET) 
+	if (msg->msg_namelen) {
+		struct sockaddr_in *usin = (struct sockaddr_in*)msg->msg_name;
+		if (msg->msg_namelen < sizeof(*usin))
 			return(-EINVAL);
-		/*
-		 *	Protocol type is host ordered byte.
+		if (usin->sin_family != AF_INET) {
+			static int complained;
+			if (!complained++)
+				printk("%s forgot to set AF_INET in raw sendmsg. Fix it!\n", current->comm);
+			if (usin->sin_family)
+				return -EINVAL;
+		}
+		daddr = usin->sin_addr.s_addr;
+		/* ANK: I did not forget to get protocol from port field.
+		 * I just do not know, who uses this weirdness.
+		 * IP_HDRINCL is much more convenient.
 		 */
-		sin.sin_port=ntohs(sin.sin_port);
-	}
-	else 
-	{
+	} else {
 		if (sk->state != TCP_ESTABLISHED) 
 			return(-EINVAL);
-		sin.sin_family = AF_INET;
-		sin.sin_port = sk->num;
-		sin.sin_addr.s_addr = sk->daddr;
+		daddr = sk->daddr;
 	}
-	if (sin.sin_port == 0) 
-		sin.sin_port = sk->num;
-  
-	if (sin.sin_addr.s_addr == INADDR_ANY)
-		sin.sin_addr.s_addr = ip_my_addr();
 
-	/*
-	 *	BSD raw sockets forget to check SO_BROADCAST ....
-	 */
-	 
-	if (!sk->bsdism && sk->broadcast == 0 && ip_chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
-		return -EACCES;
+	ipc.addr = sk->saddr;
+	ipc.opt = NULL;
 
-	if(sk->ip_hdrincl)
-	{
-		if(len>65535)
-			return -EMSGSIZE;
-		err=ip_build_xmit(sk, raw_getrawfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock);
+	if (msg->msg_controllen) {
+		int tmp = ip_cmsg_send(msg, &ipc, &dev);
+		if (tmp)
+			return tmp;
+		if (ipc.opt && sk->ip_hdrincl) {
+			kfree(ipc.opt);
+			return -EINVAL;
+		}
+		if (ipc.opt)
+			free=1;
+	}
+
+	rfh.saddr = ipc.addr;
+	ipc.addr = daddr;
+
+	if (!ipc.opt)
+		ipc.opt = sk->opt;
+	if (ipc.opt && ipc.opt->srr) {
+		if (!daddr)
+			return -EINVAL;
+		daddr = ipc.opt->faddr;
 	}
+	tos = RT_TOS(sk->ip_tos) | (sk->localroute || (msg->msg_flags&MSG_DONTROUTE));
+
+	if (MULTICAST(daddr) && sk->ip_mc_name[0] && dev==NULL)
+		err = ip_route_output_dev(&rt, daddr, rfh.saddr, tos, sk->ip_mc_name);
 	else
-	{
-		if(len>65535-sizeof(struct iphdr))
-			return -EMSGSIZE;
-		err=ip_build_xmit(sk, raw_getfrag, from, len, sin.sin_addr.s_addr, 0, sk->opt, flags, sin.sin_port, noblock);
+		err = ip_route_output(&rt, daddr, rfh.saddr, tos, dev);
+
+	if (err) {
+		if (free) kfree(ipc.opt);
+		return err;
+	}
+
+	if (rt->rt_flags&RTF_BROADCAST && !sk->broadcast) {
+		if (free) kfree(ipc.opt);
+		ip_rt_put(rt);
+		return -EACCES;
 	}
-	return err<0?err:len;
+
+	rfh.from = from;
+	rfh.saddr = rt->rt_src;
+	if (!ipc.addr)
+		ipc.addr = rt->rt_dst;
+	if(sk->ip_hdrincl)
+		err=ip_build_xmit(sk, raw_getrawfrag, &rfh, len, &ipc, rt, msg->msg_flags);
+	else {
+		if (len>65535-sizeof(struct iphdr))
+			err = -EMSGSIZE;
+		else
+			err=ip_build_xmit(sk, raw_getfrag, &rfh, len, &ipc, rt, msg->msg_flags);
+	}
+
+	if (free)
+		kfree(ipc.opt);
+	ip_rt_put(rt);
+
+	return err<0 ? err : len;
 }
 
 /*
  *	Temporary
  */
  
-static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, 
-	int flags)
+static int raw_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
-	if(msg->msg_iovlen==1)
-		return raw_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
-	else
-	{
+	if (msg->msg_iovlen==1)
+		return raw_sendto(sk, msg->msg_iov[0].iov_base,len, msg);
+	else {
 		/*
 		 *	For awkward cases we linearise the buffer first. In theory this is only frames
 		 *	whose iovec's don't split on 4 byte boundaries, and soon encrypted stuff (to keep
@@ -281,7 +302,6 @@
 		 */
 		 
 		unsigned char *buf;
-		int fs;
 		int err;
 		if(len>65515)
 			return -EMSGSIZE;
@@ -291,9 +311,10 @@
 		err = memcpy_fromiovec(buf, msg->msg_iov, len);
 		if (!err)
 		{
+			unsigned short fs;
 			fs=get_fs();
 			set_fs(get_ds());
-			err=raw_sendto(sk,buf,len, noblock, flags, msg->msg_name, msg->msg_namelen);
+			err=raw_sendto(sk,buf,len, msg);
 			set_fs(fs);
 		}
 		else
@@ -310,10 +331,12 @@
 #ifdef CONFIG_IP_MROUTE	
 	if(sk==mroute_socket)
 	{
+		ipv4_config.multicast_route = 0;
 		mroute_close(sk);
 		mroute_socket=NULL;
 	}
 #endif	
+	sk->dead=1;
 	destroy_sock(sk);
 }
 
@@ -343,29 +366,41 @@
 	if (sk->shutdown & RCV_SHUTDOWN) 
 		return(0);
 
-	if (addr_len) 
+	if (addr_len)
 		*addr_len=sizeof(*sin);
 
+	if (sk->ip_recverr && (skb = skb_dequeue(&sk->error_queue)) != NULL) {
+		err = sock_error(sk);
+		if (msg->msg_controllen == 0) {
+			skb_free_datagram(sk, skb);
+			return err;
+		}
+		put_cmsg(msg, SOL_IP, IP_RECVERR, skb->len, skb->data);
+		skb_free_datagram(sk, skb);
+		return 0;
+	}
+
 	skb=skb_recv_datagram(sk,flags,noblock,&err);
 	if(skb==NULL)
  		return err;
 
-	copied=skb->len;
-	if(copied>len)
+	copied = skb->len;
+	if (len < copied)
 	{
-		copied=len;
-		msg->msg_flags|=MSG_TRUNC;
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
 	}
 	
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 	sk->stamp=skb->stamp;
 
 	/* Copy the address. */
-	if (sin) 
-	{
+	if (sin) {
 		sin->sin_family = AF_INET;
-		sin->sin_addr.s_addr = skb->daddr;
+		sin->sin_addr.s_addr = skb->nh.iph->saddr;
 	}
+	if (sk->ip_cmsg_flags)
+		ip_cmsg_recv(msg, skb);
 	skb_free_datagram(sk, skb);
 	return err ? err : (copied);
 }
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.14/linux/net/ipv4/route.c	Fri Nov 15 23:49:11 1996
+++ linux/net/ipv4/route.c	Thu Dec 12 16:54:24 1996
@@ -11,6 +11,7 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  *		Linus Torvalds, <Linus.Torvalds@helsinki.fi>
+ *		Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
  * Fixes:
  *		Alan Cox	:	Verify area fixes.
@@ -42,6 +43,8 @@
  *		Bjorn Ekwall	:	Kerneld route support.
  *		Alan Cox	:	Multicast fixed (I hope)
  * 		Pavel Krauz	:	Limited broadcast fixed
+ *	Alexey Kuznetsov	:	End of old history. Splitted to fib.c and
+ *					route.c and rewritten from scratch.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -65,761 +68,60 @@
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/if_arp.h>
+#include <linux/proc_fs.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <net/route.h>
+#include <net/arp.h>
 #include <net/tcp.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
 #include <net/icmp.h>
-#include <net/netlink.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
+#include <linux/net_alias.h>
+  
+static void rt_run_flush(unsigned long);
+  
+static struct timer_list rt_flush_timer =
+	{ NULL, NULL, RT_FLUSH_DELAY, 0L, rt_run_flush };
 
 /*
- * Forwarding Information Base definitions.
+ *	Interface to generic destination cache.
  */
 
-struct fib_node
-{
-	struct fib_node		*fib_next;
-	__u32			fib_dst;
-	unsigned long		fib_use;
-	struct fib_info		*fib_info;
-	short			fib_metric;
-	unsigned char		fib_tos;
-};
+static void ipv4_dst_destroy(struct dst_entry * dst);
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst);
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst);
 
-/*
- * This structure contains data shared by many of routes.
- */	
 
-struct fib_info
+struct dst_ops ipv4_dst_ops =
 {
-	struct fib_info		*fib_next;
-	struct fib_info		*fib_prev;
-	__u32			fib_gateway;
-	struct device		*fib_dev;
-	int			fib_refcnt;
-	unsigned long		fib_window;
-	unsigned short		fib_flags;
-	unsigned short		fib_mtu;
-	unsigned short		fib_irtt;
-};
-
-struct fib_zone
-{
-	struct fib_zone	*fz_next;
-	struct fib_node	**fz_hash_table;
-	struct fib_node	*fz_list;
-	int		fz_nent;
-	int		fz_logmask;
-	__u32		fz_mask;
-};
-
-static struct fib_zone 	*fib_zones[33];
-static struct fib_zone 	*fib_zone_list;
-static struct fib_node 	*fib_loopback = NULL;
-static struct fib_info 	*fib_info_list;
-
-/*
- * Backlogging.
- */
-
-#define RT_BH_REDIRECT		0
-#define RT_BH_GARBAGE_COLLECT 	1
-#define RT_BH_FREE	 	2
-
-struct rt_req
-{
-	struct rt_req * rtr_next;
-	struct device *dev;
-	__u32 dst;
-	__u32 gw;
-	unsigned char tos;
+	AF_INET,
+	ipv4_dst_check,
+	ipv4_dst_reroute,
+	ipv4_dst_destroy
 };
 
-int		    	ip_rt_lock;
-unsigned		ip_rt_bh_mask;
-static struct rt_req 	*rt_backlog;
 
 /*
  * Route cache.
  */
 
-struct rtable 		*ip_rt_hash_table[RT_HASH_DIVISOR];
-static int		rt_cache_size;
-static struct rtable 	*rt_free_queue;
-struct wait_queue	*rt_wait;
-
-static void rt_kick_backlog(void);
-static void rt_cache_add(unsigned hash, struct rtable * rth);
-static void rt_cache_flush(void);
-static void rt_garbage_collect_1(void);
-
-/* 
- * Evaluate mask length.
- */
-
-static __inline__ int rt_logmask(__u32 mask)
-{
-	if (!(mask = ntohl(mask)))
-		return 32;
-	return ffz(~mask);
-}
-
-/* 
- * Create mask from length.
- */
-
-static __inline__ __u32 rt_mask(int logmask)
-{
-	if (logmask >= 32)
-		return 0;
-	return htonl(~((1<<logmask)-1));
-}
-
-static __inline__ unsigned fz_hash_code(__u32 dst, int logmask)
-{
-	return ip_rt_hash_code(ntohl(dst)>>logmask);
-}
-
-/*
- * Free FIB node.
- */
-
-static void fib_free_node(struct fib_node * f)
-{
-	struct fib_info * fi = f->fib_info;
-	if (!--fi->fib_refcnt)
-	{
-#if RT_CACHE_DEBUG >= 2
-		printk("fib_free_node: fi %08x/%s is free\n", fi->fib_gateway, fi->fib_dev->name);
-#endif
-		if (fi->fib_next)
-			fi->fib_next->fib_prev = fi->fib_prev;
-		if (fi->fib_prev)
-			fi->fib_prev->fib_next = fi->fib_next;
-		if (fi == fib_info_list)
-			fib_info_list = fi->fib_next;
-	}
-	kfree_s(f, sizeof(struct fib_node));
-}
-
-/*
- * Find gateway route by address.
- */
-
-static struct fib_node * fib_lookup_gateway(__u32 dst)
-{
-	struct fib_zone * fz;
-	struct fib_node * f;
-
-	for (fz = fib_zone_list; fz; fz = fz->fz_next) 
-	{
-		if (fz->fz_hash_table)
-			f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
-		else
-			f = fz->fz_list;
-		
-		for ( ; f; f = f->fib_next)
-		{
-			if ((dst ^ f->fib_dst) & fz->fz_mask)
-				continue;
-			if (f->fib_info->fib_flags & RTF_GATEWAY)
-				return NULL;
-			return f;
-		}
-	}
-	return NULL;
-}
-
-/*
- * Find local route by address.
- * FIXME: I use "longest match" principle. If destination
- *	  has some non-local route, I'll not search shorter matches.
- *	  It's possible, I'm wrong, but I wanted to prevent following
- *	  situation:
- *	route add 193.233.7.128 netmask 255.255.255.192 gw xxxxxx
- *	route add 193.233.7.0	netmask 255.255.255.0 eth1
- *	  (Two ethernets connected by serial line, one is small and other is large)
- *	  Host 193.233.7.129 is locally unreachable,
- *	  but old (<=1.3.37) code will send packets destined for it to eth1.
- *
- */
-
-static struct fib_node * fib_lookup_local(__u32 dst)
-{
-	struct fib_zone * fz;
-	struct fib_node * f;
-
-	for (fz = fib_zone_list; fz; fz = fz->fz_next) 
-	{
-		int longest_match_found = 0;
-
-		if (fz->fz_hash_table)
-			f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
-		else
-			f = fz->fz_list;
-		
-		for ( ; f; f = f->fib_next)
-		{
-			if ((dst ^ f->fib_dst) & fz->fz_mask)
-				continue;
-			if (!(f->fib_info->fib_flags & RTF_GATEWAY))
-				return f;
-			longest_match_found = 1;
-		}
-		if (longest_match_found)
-			return NULL;
-	}
-	return NULL;
-}
-
-/*
- * Main lookup routine.
- *	IMPORTANT NOTE: this algorithm has small difference from <=1.3.37 visible
- *	by user. It doesn't route non-CIDR broadcasts by default.
- *
- *	F.e.
- *		ifconfig eth0 193.233.7.65 netmask 255.255.255.192 broadcast 193.233.7.255
- *	is valid, but if you really are not able (not allowed, do not want) to
- *	use CIDR compliant broadcast 193.233.7.127, you should add host route:
- *		route add -host 193.233.7.255 eth0
- */
-
-static struct fib_node * fib_lookup(__u32 dst)
-{
-	struct fib_zone * fz;
-	struct fib_node * f;
-
-	for (fz = fib_zone_list; fz; fz = fz->fz_next) 
-	{
-		if (fz->fz_hash_table)
-			f = fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
-		else
-			f = fz->fz_list;
-		
-		for ( ; f; f = f->fib_next)
-		{
-			if ((dst ^ f->fib_dst) & fz->fz_mask)
-				continue;
-			return f;
-		}
-	}
-	return NULL;
-}
-
-static __inline__ struct device * get_gw_dev(__u32 gw)
-{
-	struct fib_node * f;
-	f = fib_lookup_gateway(gw);
-	if (f)
-		return f->fib_info->fib_dev;
-	return NULL;
-}
-
-/*
- *	Check if a mask is acceptable.
- */
- 
-static inline int bad_mask(__u32 mask, __u32 addr)
-{
-	if (addr & (mask = ~mask))
-		return 1;
-	mask = ntohl(mask);
-	if (mask & (mask+1))
-		return 1;
-	return 0;
-}
-
-
-static int fib_del_list(struct fib_node **fp, __u32 dst,
-		struct device * dev, __u32 gtw, short flags, short metric, __u32 mask)
-{
-	struct fib_node *f;
-	int found=0;
-
-	while((f = *fp) != NULL) 
-	{
-		struct fib_info * fi = f->fib_info;
-
-		/*
-		 *	Make sure the destination and netmask match.
-		 *	metric, gateway and device are also checked
-		 *	if they were specified.
-		 */
-		if (f->fib_dst != dst ||
-		    (gtw && fi->fib_gateway != gtw) ||
-		    (metric >= 0 && f->fib_metric != metric) ||
-		    (dev && fi->fib_dev != dev) )
-		{
-			fp = &f->fib_next;
-			continue;
-		}
-		cli();
-		*fp = f->fib_next;
-		if (fib_loopback == f)
-			fib_loopback = NULL;
-		sti();
-		ip_netlink_msg(RTMSG_DELROUTE, dst, gtw, mask, flags, metric, fi->fib_dev->name);
-		fib_free_node(f);
-		found++;
-	}
-	return found;
-}
-
-static __inline__ int fib_del_1(__u32 dst, __u32 mask,
-		struct device * dev, __u32 gtw, short flags, short metric)
-{
-	struct fib_node **fp;
-	struct fib_zone *fz;
-	int found=0;
-
-	if (!mask)
-	{
-		for (fz=fib_zone_list; fz; fz = fz->fz_next)
-		{
-			int tmp;
-			if (fz->fz_hash_table)
-				fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
-			else
-				fp = &fz->fz_list;
-
-			tmp = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
-			fz->fz_nent -= tmp;
-			found += tmp;
-		}
-	} 
-	else
-	{
-		if ((fz = fib_zones[rt_logmask(mask)]) != NULL)
-		{
-			if (fz->fz_hash_table)
-				fp = &fz->fz_hash_table[fz_hash_code(dst, fz->fz_logmask)];
-			else
-				fp = &fz->fz_list;
-	
-			found = fib_del_list(fp, dst, dev, gtw, flags, metric, mask);
-			fz->fz_nent -= found;
-		}
-	}
-
-	if (found)
-	{
-		rt_cache_flush();
-		return 0;
-	}
-	return -ESRCH;
-}
-
-
-static struct fib_info * fib_create_info(__u32 gw, struct device * dev,
-					 unsigned short flags, unsigned short mss,
-					 unsigned long window, unsigned short irtt)
-{
-	struct fib_info * fi;
-
-	if (!(flags & RTF_MSS))
-	{
-		mss = dev->mtu;
-#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
-		/*
-		 *	If MTU was not specified, use default.
-		 *	If you want to increase MTU for some net (local subnet)
-		 *	use "route add .... mss xxx".
-		 *
-		 * 	The MTU isn't currently always used and computed as it
-		 *	should be as far as I can tell. [Still verifying this is right]
-		 */
-		if ((flags & RTF_GATEWAY) && mss > 576)
-			mss = 576;
-#endif
-	}
-	if (!(flags & RTF_WINDOW))
-		window = 0;
-	if (!(flags & RTF_IRTT))
-		irtt = 0;
-
-	for (fi=fib_info_list; fi; fi = fi->fib_next)
-	{
-		if (fi->fib_gateway != gw ||
-		    fi->fib_dev != dev  ||
-		    fi->fib_flags != flags ||
-		    fi->fib_mtu != mss ||
-		    fi->fib_window != window ||
-		    fi->fib_irtt != irtt)
-			continue;
-		fi->fib_refcnt++;
-#if RT_CACHE_DEBUG >= 2
-		printk("fib_create_info: fi %08x/%s is duplicate\n", fi->fib_gateway, fi->fib_dev->name);
-#endif
-		return fi;
-	}
-	fi = (struct fib_info*)kmalloc(sizeof(struct fib_info), GFP_KERNEL);
-	if (!fi)
-		return NULL;
-	memset(fi, 0, sizeof(struct fib_info));
-	fi->fib_flags = flags;
-	fi->fib_dev = dev;
-	fi->fib_gateway = gw;
-	fi->fib_mtu = mss;
-	fi->fib_window = window;
-	fi->fib_refcnt++;
-	fi->fib_next = fib_info_list;
-	fi->fib_prev = NULL;
-	fi->fib_irtt = irtt;
-	if (fib_info_list)
-		fib_info_list->fib_prev = fi;
-	fib_info_list = fi;
-#if RT_CACHE_DEBUG >= 2
-	printk("fib_create_info: fi %08x/%s is created\n", fi->fib_gateway, fi->fib_dev->name);
-#endif
-	return fi;
-}
-
-
-static __inline__ void fib_add_1(short flags, __u32 dst, __u32 mask,
-	__u32 gw, struct device *dev, unsigned short mss,
-	unsigned long window, unsigned short irtt, short metric)
-{
-	struct fib_node *f, *f1;
-	struct fib_node **fp;
-	struct fib_node **dup_fp = NULL;
-	struct fib_zone * fz;
-	struct fib_info * fi;
-	int logmask;
-
-	/*
-	 *	Allocate an entry and fill it in.
-	 */
-	 
-	f = (struct fib_node *) kmalloc(sizeof(struct fib_node), GFP_KERNEL);
-	if (f == NULL)
-		return;
-
-	memset(f, 0, sizeof(struct fib_node));
-	f->fib_dst = dst;
-	f->fib_metric = metric;
-	f->fib_tos    = 0;
-
-	if  ((fi = fib_create_info(gw, dev, flags, mss, window, irtt)) == NULL)
-	{
-		kfree_s(f, sizeof(struct fib_node));
-		return;
-	}
-	f->fib_info = fi;
-
-	logmask = rt_logmask(mask);
-	fz = fib_zones[logmask];
-
-
-	if (!fz)
-	{
-		int i;
-		fz = kmalloc(sizeof(struct fib_zone), GFP_KERNEL);
-		if (!fz)
-		{
-			fib_free_node(f);
-			return;
-		}
-		memset(fz, 0, sizeof(struct fib_zone));
-		fz->fz_logmask = logmask;
-		fz->fz_mask = mask;
-		for (i=logmask-1; i>=0; i--)
-			if (fib_zones[i])
-				break;
-		cli();
-		if (i<0)
-		{
-			fz->fz_next = fib_zone_list;
-			fib_zone_list = fz;
-		}
-		else
-		{
-			fz->fz_next = fib_zones[i]->fz_next;
-			fib_zones[i]->fz_next = fz;
-		}
-		fib_zones[logmask] = fz;
-		sti();
-	}
-
-	/*
-	 * If zone overgrows RTZ_HASHING_LIMIT, create hash table.
-	 */
-
-	if (fz->fz_nent >= RTZ_HASHING_LIMIT && !fz->fz_hash_table && logmask<32)
-	{
-		struct fib_node ** ht;
-#if RT_CACHE_DEBUG >= 2
-		printk("fib_add_1: hashing for zone %d started\n", logmask);
-#endif
-		ht = kmalloc(RTZ_HASH_DIVISOR*sizeof(struct rtable*), GFP_KERNEL);
-
-		if (ht)
-		{
-			memset(ht, 0, RTZ_HASH_DIVISOR*sizeof(struct fib_node*));
-			cli();
-			f1 = fz->fz_list;
-			while (f1)
-			{
-				struct fib_node * next;
-				unsigned hash = fz_hash_code(f1->fib_dst, logmask);
-				next = f1->fib_next;
-				f1->fib_next = ht[hash];
-				ht[hash] = f1;
-				f1 = next;
-			}
-			fz->fz_list = NULL;
-			fz->fz_hash_table = ht; 
-			sti();
-		}
-	}
-
-	if (fz->fz_hash_table)
-		fp = &fz->fz_hash_table[fz_hash_code(dst, logmask)];
-	else
-		fp = &fz->fz_list;
-
-	/*
-	 * Scan list to find the first route with the same destination
-	 */
-	while ((f1 = *fp) != NULL)
-	{
-		if (f1->fib_dst == dst)
-			break;
-		fp = &f1->fib_next;
-	}
-
-	/*
-	 * Find route with the same destination and less (or equal) metric.
-	 */
-	while ((f1 = *fp) != NULL && f1->fib_dst == dst)
-	{
-		if (f1->fib_metric >= metric)
-			break;
-		/*
-		 *	Record route with the same destination and gateway,
-		 *	but less metric. We'll delete it 
-		 *	after instantiation of new route.
-		 */
-		if (f1->fib_info->fib_gateway == gw &&
-		    (gw || f1->fib_info->fib_dev == dev))
-			dup_fp = fp;
-		fp = &f1->fib_next;
-	}
-
-	/*
-	 * Is it already present?
-	 */
-
-	if (f1 && f1->fib_metric == metric && f1->fib_info == fi)
-	{
-		fib_free_node(f);
-		return;
-	}
-	
-	/*
-	 * Insert new entry to the list.
-	 */
-
-	cli();
-	f->fib_next = f1;
-	*fp = f;
-	if (!fib_loopback && (fi->fib_dev->flags & IFF_LOOPBACK))
-		fib_loopback = f;
-	sti();
-	fz->fz_nent++;
-	ip_netlink_msg(RTMSG_NEWROUTE, dst, gw, mask, flags, metric, fi->fib_dev->name);
-
-	/*
-	 *	Delete route with the same destination and gateway.
-	 *	Note that we should have at most one such route.
-	 */
-	if (dup_fp)
-		fp = dup_fp;
-	else
-		fp = &f->fib_next;
-
-	while ((f1 = *fp) != NULL && f1->fib_dst == dst)
-	{
-		if (f1->fib_info->fib_gateway == gw &&
-		    (gw || f1->fib_info->fib_dev == dev))
-		{
-			cli();
-			*fp = f1->fib_next;
-			if (fib_loopback == f1)
-				fib_loopback = NULL;
-			sti();
-			ip_netlink_msg(RTMSG_DELROUTE, dst, gw, mask, flags, metric, f1->fib_info->fib_dev->name);
-			fib_free_node(f1);
-			fz->fz_nent--;
-			break;
-		}
-		fp = &f1->fib_next;
-	}
-	rt_cache_flush();
-	return;
-}
-
-static int rt_flush_list(struct fib_node ** fp, struct device *dev)
-{
-	int found = 0;
-	struct fib_node *f;
+static atomic_t		 rt_cache_size;
+static struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
 
-	while ((f = *fp) != NULL) {
-/*
- *	"Magic" device route is allowed to point to loopback,
- *	discard it too.
- */
-		if (f->fib_info->fib_dev != dev &&
-		    (f->fib_info->fib_dev != &loopback_dev || f->fib_dst != dev->pa_addr)) {
-			fp = &f->fib_next;
-			continue;
-		}
-		cli();
-		*fp = f->fib_next;
-		if (fib_loopback == f)
-			fib_loopback = NULL;
-		sti();
-		fib_free_node(f);
-		found++;
-	}
-	return found;
-}
+static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth, u16 protocol);
 
-static __inline__ void fib_flush_1(struct device *dev)
+static __inline__ unsigned rt_hash_code(u32 daddr, u32 saddr, u8 tos)
 {
-	struct fib_zone *fz;
-	int found = 0;
-
-	for (fz = fib_zone_list; fz; fz = fz->fz_next)
-	{
-		if (fz->fz_hash_table)
-		{
-			int i;
-			int tmp = 0;
-			for (i=0; i<RTZ_HASH_DIVISOR; i++)
-				tmp += rt_flush_list(&fz->fz_hash_table[i], dev);
-			fz->fz_nent -= tmp;
-			found += tmp;
-		}
-		else
-		{
-			int tmp;
-			tmp = rt_flush_list(&fz->fz_list, dev);
-			fz->fz_nent -= tmp;
-			found += tmp;
-		}
-	}
-		
-	if (found)
-		rt_cache_flush();
+	unsigned hash = ((daddr&0xF0F0F0F0)>>4)|((daddr&0x0F0F0F0F)<<4);
+	hash = hash^saddr^tos;
+	hash = hash^(hash>>16);
+	return (hash^(hash>>8)) & 0xFF;
 }
 
+#ifdef CONFIG_PROC_FS
 
-/* 
- *	Called from the PROCfs module. This outputs /proc/net/route.
- *
- *	We preserve the old format but pad the buffers out. This means that
- *	we can spin over the other entries as we read them. Remember the
- *	gated BGP4 code could need to read 60,000+ routes on occasion (that's
- *	about 7Mb of data). To do that ok we will need to also cache the
- *	last route we got to (reads will generally be following on from
- *	one another without gaps).
- */
- 
-int rt_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
-{
-	struct fib_zone *fz;
-	struct fib_node *f;
-	int len=0;
-	off_t pos=0;
-	char temp[129];
-	int i;
-	
-	pos = 128;
-
-	if (offset<128)
-	{
-		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT");
-		len = 128;
-  	}
-  	
-	while  (ip_rt_lock)
-		sleep_on(&rt_wait);
-	ip_rt_fast_lock();
-
-	for (fz=fib_zone_list; fz; fz = fz->fz_next)
-	{
-		int maxslot;
-		struct fib_node ** fp;
-
-		if (fz->fz_nent == 0)
-			continue;
-
-		if (pos + 128*fz->fz_nent <= offset)
-		{
-			pos += 128*fz->fz_nent;
-			len = 0;
-			continue;
-		}
-
-		if (fz->fz_hash_table)
-		{
-			maxslot = RTZ_HASH_DIVISOR;
-			fp	= fz->fz_hash_table;
-		}
-		else
-		{
-			maxslot	= 1;
-			fp	= &fz->fz_list;
-		}
-			
-		for (i=0; i < maxslot; i++, fp++)
-		{
-			
-			for (f = *fp; f; f = f->fib_next) 
-			{
-				struct fib_info * fi;
-				/*
-				 *	Spin through entries until we are ready
-				 */
-				pos += 128;
-
-				if (pos <= offset)
-				{
-					len=0;
-					continue;
-				}
-					
-				fi = f->fib_info;
-				sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%lu\t%d\t%08lX\t%d\t%lu\t%u",
-					fi->fib_dev->name, (unsigned long)f->fib_dst, (unsigned long)fi->fib_gateway,
-					fi->fib_flags, 0, f->fib_use, f->fib_metric,
-					(unsigned long)fz->fz_mask, (int)fi->fib_mtu, fi->fib_window, (int)fi->fib_irtt);
-				sprintf(buffer+len,"%-127s\n",temp);
-
-				len += 128;
-				if (pos >= offset+length)
-					goto done;
-			}
-		}
-        }
-
-done:
-	ip_rt_unlock();
-	wake_up(&rt_wait);
-  	
-  	*start = buffer+len-(pos-offset);
-  	len = pos - offset;
-  	if (len>length)
-  		len = length;
-  	return len;
-}
-
-int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+static int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
 {
 	int len=0;
 	off_t pos=0;
@@ -829,36 +131,39 @@
 
 	pos = 128;
 
-	if (offset<128)
-	{
-		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tHH\tARP");
+	if (offset<128)	{
+		sprintf(buffer,"%-127s\n","Iface\tDestination\tGateway \tFlags\tRefCnt\tUse\tMetric\tSource\t\tMTU\tWindow\tIRTT\tTOS\tHH\tARP");
 		len = 128;
   	}
 	
   	
-	while  (ip_rt_lock)
-		sleep_on(&rt_wait);
-	ip_rt_fast_lock();
-
-	for (i = 0; i<RT_HASH_DIVISOR; i++)
-	{
-		for (r = ip_rt_hash_table[i]; r; r = r->rt_next) 
-		{
+	start_bh_atomic();
+
+	for (i = 0; i<RT_HASH_DIVISOR; i++) {
+		for (r = rt_hash_table[i]; r; r = r->u.rt_next) {
 			/*
 			 *	Spin through entries until we are ready
 			 */
 			pos += 128;
 
-			if (pos <= offset)
-			{
+			if (pos <= offset) {
 				len = 0;
 				continue;
 			}
 					
-			sprintf(temp, "%s\t%08lX\t%08lX\t%02X\t%d\t%u\t%d\t%08lX\t%d\t%lu\t%u\t%d\t%1d",
-				r->rt_dev->name, (unsigned long)r->rt_dst, (unsigned long)r->rt_gateway,
-				r->rt_flags, r->rt_refcnt, r->rt_use, 0,
-				(unsigned long)r->rt_src, (int)r->rt_mtu, r->rt_window, (int)r->rt_irtt, r->rt_hh ? r->rt_hh->hh_refcnt : -1, r->rt_hh ? r->rt_hh->hh_uptodate : 0);
+			sprintf(temp, "%s\t%08lX\t%08lX\t%X\t%d\t%u\t%d\t%08lX\t%d\t%u\t%u\t%02x\t%d\t%1d\t%08x\t%02x",
+				r->u.dst.dev ? r->u.dst.dev->name : "*",
+				(unsigned long)r->rt_dst,
+				(unsigned long)r->rt_gateway,
+				r->rt_flags, r->u.dst.refcnt,
+				r->u.dst.use, 0,
+				(unsigned long)r->rt_src, (int)r->u.dst.pmtu,
+				r->u.dst.window,
+				(int)r->u.dst.rtt, r->key.tos,
+				r->u.dst.hh ? r->u.dst.hh->hh_refcnt : -1,
+				r->u.dst.hh ? r->u.dst.hh->hh_uptodate : 0,
+				r->rt_spec_dst,
+				i);
 			sprintf(buffer+len,"%-127s\n",temp);
 			len += 128;
 			if (pos >= offset+length)
@@ -867,8 +172,7 @@
         }
 
 done:
-	ip_rt_unlock();
-	wake_up(&rt_wait);
+	end_bh_atomic();
   	
   	*start = buffer+len-(pos-offset);
   	len = pos-offset;
@@ -876,218 +180,123 @@
   		len = length;
   	return len;
 }
-
-
-static void rt_free(struct rtable * rt)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	if (!rt->rt_refcnt)
-	{
-		struct hh_cache * hh = rt->rt_hh;
-		rt->rt_hh = NULL;
-		restore_flags(flags);
-		if (hh && atomic_dec_and_test(&hh->hh_refcnt))
-			kfree_s(hh, sizeof(struct hh_cache));
-		kfree_s(rt, sizeof(struct rt_table));
-		return;
-	}
-	rt->rt_next = rt_free_queue;
-	rt->rt_flags &= ~RTF_UP;
-	rt_free_queue = rt;
-	ip_rt_bh_mask |= RT_BH_FREE;
-#if RT_CACHE_DEBUG >= 2
-	printk("rt_free: %08x\n", rt->rt_dst);
 #endif
-	restore_flags(flags);
+  
+static void __inline__ rt_free(struct rtable *rt)
+{
+	dst_free(&rt->u.dst);
 }
 
-/*
- * RT "bottom half" handlers. Called with masked interrupts.
- */
 
-static __inline__ void rt_kick_free_queue(void)
+void ip_rt_check_expire()
 {
-	struct rtable *rt, **rtp;
-
-	rtp = &rt_free_queue;
-
-	while ((rt = *rtp) != NULL)
-	{
-		if  (!rt->rt_refcnt)
-		{
-			struct hh_cache * hh = rt->rt_hh;
-#if RT_CACHE_DEBUG >= 2
-			__u32 daddr = rt->rt_dst;
-#endif
-			*rtp = rt->rt_next;
-			rt->rt_hh = NULL;
-			sti();
-			if (hh && atomic_dec_and_test(&hh->hh_refcnt))
-				kfree_s(hh, sizeof(struct hh_cache));
-			kfree_s(rt, sizeof(struct rt_table));
-#if RT_CACHE_DEBUG >= 2
-			printk("rt_kick_free_queue: %08x is free\n", daddr);
-#endif
-			cli();
-			continue;
-		}
-		rtp = &rt->rt_next;
-	}
-}
+	int i;
+	static int rover;
+	struct rtable *rth, **rthp;
+	unsigned long now = jiffies;
 
-void ip_rt_run_bh()
-{
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	if (ip_rt_bh_mask && !ip_rt_lock)
-	{
-		if (ip_rt_bh_mask & RT_BH_REDIRECT)
-			rt_kick_backlog();
+	start_bh_atomic();
 
-		if (ip_rt_bh_mask & RT_BH_GARBAGE_COLLECT)
-		{
-			ip_rt_fast_lock();
-			ip_rt_bh_mask &= ~RT_BH_GARBAGE_COLLECT;
-			sti();
-			rt_garbage_collect_1();
-			cli();
-			ip_rt_fast_unlock();
-		}
+	for (i=0; i<RT_HASH_DIVISOR/5; i++) {
+		rover = (rover + 1) & (RT_HASH_DIVISOR-1);
+		rthp = &rt_hash_table[rover];
 
-		if (ip_rt_bh_mask & RT_BH_FREE)
-			rt_kick_free_queue();
-	}
-	restore_flags(flags);
-}
+		while ((rth = *rthp) != NULL) {
+			struct rtable * rth_next = rth->u.rt_next;
 
+			/*
+			 * Cleanup aged off entries.
+			 */
 
-void ip_rt_check_expire()
-{
-	ip_rt_fast_lock();
-	if (ip_rt_lock == 1)
-	{
-		int i;
-		struct rtable *rth, **rthp;
-		unsigned long flags;
-		unsigned long now = jiffies;
-
-		save_flags(flags);
-		for (i=0; i<RT_HASH_DIVISOR; i++)
-		{
-			rthp = &ip_rt_hash_table[i];
-
-			while ((rth = *rthp) != NULL)
-			{
-				struct rtable * rth_next = rth->rt_next;
-
-				/*
-				 * Cleanup aged off entries.
-				 */
-
-				cli();
-				if (!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
-				{
-					*rthp = rth_next;
-					sti();
-					rt_cache_size--;
+			if (!rth->u.dst.refcnt && now - rth->u.dst.lastuse > RT_CACHE_TIMEOUT) {
+				*rthp = rth_next;
+				atomic_dec(&rt_cache_size);
 #if RT_CACHE_DEBUG >= 2
-					printk("rt_check_expire clean %02x@%08x\n", i, rth->rt_dst);
+				printk("rt_check_expire clean %02x@%08x\n", rover, rth->rt_dst);
 #endif
-					rt_free(rth);
-					continue;
-				}
-				sti();
+				rt_free(rth);
+				continue;
+			}
 
-				if (!rth_next)
-					break;
+			if (!rth_next)
+				break;
+
+			/*
+			 * Pseudo-LRU ordering.
+			 * Really we should teach it to move
+			 * rarely used but permanently living entries
+			 * (f.e. rdisc, igmp etc.) to the end of list.
+			 */
 
-				/*
-				 * LRU ordering.
-				 */
-
-				if (rth->rt_lastuse + RT_CACHE_BUBBLE_THRESHOLD < rth_next->rt_lastuse ||
-				    (rth->rt_lastuse < rth_next->rt_lastuse &&
-				     rth->rt_use < rth_next->rt_use))
-				{
+			if ( rth_next->u.dst.lastuse - rth->u.dst.lastuse > RT_CACHE_BUBBLE_THRESHOLD ||
+			    (rth->u.dst.lastuse - rth_next->u.dst.lastuse < 0 &&
+			     rth->u.dst.use < rth_next->u.dst.use)) {
 #if RT_CACHE_DEBUG >= 2
-					printk("rt_check_expire bubbled %02x@%08x<->%08x\n", i, rth->rt_dst, rth_next->rt_dst);
+				printk("rt_check_expire bubbled %02x@%08x<->%08x\n", rover, rth->rt_dst, rth_next->rt_dst);
 #endif
-					cli();
-					*rthp = rth_next;
-					rth->rt_next = rth_next->rt_next;
-					rth_next->rt_next = rth;
-					sti();
-					rthp = &rth_next->rt_next;
-					continue;
-				}
-				rthp = &rth->rt_next;
+				*rthp = rth_next;
+ 				rth->u.rt_next = rth_next->u.rt_next;
+				rth_next->u.rt_next = rth;
+				sti();
+				rthp = &rth_next->u.rt_next;
+				continue;
 			}
+			rthp = &rth->u.rt_next;
 		}
-		restore_flags(flags);
-		rt_kick_free_queue();
 	}
-	ip_rt_unlock();
-}
 
-static void rt_redirect_1(__u32 dst, __u32 gw, struct device *dev)
+	end_bh_atomic();
+}
+  
+  
+void rt_cache_flush(int how)
 {
-	struct rtable *rt;
-	unsigned long hash = ip_rt_hash_code(dst);
-
-	if (gw == dev->pa_addr)
-		return;
-	if (dev != get_gw_dev(gw))
+	start_bh_atomic();
+	if (rt_flush_timer.expires) {
+		if (jiffies - rt_flush_timer.expires > 0 ||
+		    rt_flush_timer.expires - jiffies > RT_FLUSH_DELAY/2)
+			how = 1;
+	}
+	if (how) {
+		if (rt_flush_timer.expires)
+			del_timer(&rt_flush_timer);
+		rt_flush_timer.expires = 0;
+		end_bh_atomic();
+		rt_run_flush(0);
 		return;
-	rt = (struct rtable *) kmalloc(sizeof(struct rtable), GFP_ATOMIC);
-	if (rt == NULL) 
+	}
+	if (rt_flush_timer.expires) {
+		end_bh_atomic();
 		return;
-	memset(rt, 0, sizeof(struct rtable));
-	rt->rt_flags = RTF_DYNAMIC | RTF_MODIFIED | RTF_HOST | RTF_GATEWAY | RTF_UP;
-	rt->rt_dst = dst;
-	rt->rt_dev = dev;
-	rt->rt_gateway = gw;
-	rt->rt_src = dev->pa_addr;
-	rt->rt_mtu = dev->mtu;
-#ifdef CONFIG_NO_PATH_MTU_DISCOVERY
-	if (dev->mtu > 576)
-		rt->rt_mtu = 576;
-#endif
-	rt->rt_lastuse  = jiffies;
-	rt->rt_refcnt  = 1;
-	rt_cache_add(hash, rt);
-	ip_rt_put(rt);
-	return;
+	}
+	del_timer(&rt_flush_timer);
+	rt_flush_timer.expires = jiffies + RT_FLUSH_DELAY;
+	add_timer(&rt_flush_timer);
+	end_bh_atomic();
 }
-
-static void rt_cache_flush(void)
+  
+void rt_run_flush(unsigned long dummy)
 {
 	int i;
 	struct rtable * rth, * next;
 
-	for (i=0; i<RT_HASH_DIVISOR; i++)
-	{
+	for (i=0; i<RT_HASH_DIVISOR; i++) {
 		int nr=0;
 
 		cli();
-		if (!(rth = ip_rt_hash_table[i]))
-		{
+		if (!(rth = rt_hash_table[i])) {
 			sti();
 			continue;
 		}
 
-		ip_rt_hash_table[i] = NULL;
+		rt_hash_table[i] = NULL;
 		sti();
 
-		for (; rth; rth=next)
-		{
-			next = rth->rt_next;
-			rt_cache_size--;
+		for (; rth; rth=next) {
+			next = rth->u.rt_next;
+			atomic_dec(&rt_cache_size);
 			nr++;
-			rth->rt_next = NULL;
+			rth->u.rt_next = NULL;
 			rt_free(rth);
 		}
 #if RT_CACHE_DEBUG >= 2
@@ -1095,631 +304,1090 @@
 			printk("rt_cache_flush: %d@%02x\n", nr, i);
 #endif
 	}
-#if RT_CACHE_DEBUG >= 1
-	if (rt_cache_size)
-	{
-		printk("rt_cache_flush: bug rt_cache_size=%d\n", rt_cache_size);
-		rt_cache_size = 0;
+}
+
+static void rt_garbage_collect(void)
+{
+	int i;
+	static unsigned expire = RT_CACHE_TIMEOUT>>1;
+	static unsigned long last_gc;
+	struct rtable *rth, **rthp;
+	unsigned long now;
+
+	start_bh_atomic();
+	now = jiffies;
+
+	/*
+	 * Garbage collection is pretty expensive,
+	 * do not make it too frequently.
+	 */
+	if (now - last_gc < 1*HZ) {
+		expire >>= 1;
+		end_bh_atomic();
+		return;
+	}
+
+	expire++;
+
+	for (i=0; i<RT_HASH_DIVISOR; i++) {
+		if (!rt_hash_table[i])
+			continue;
+		for (rthp=&rt_hash_table[i]; (rth=*rthp); rthp=&rth->u.rt_next)	{
+			if (rth->u.dst.refcnt || now - rth->u.dst.lastuse > expire)
+				continue;
+			atomic_dec(&rt_cache_size);
+			*rthp = rth->u.rt_next;
+			rth->u.rt_next = NULL;
+			rt_free(rth);
+			break;
+		}
+	}
+
+	last_gc = now;
+	if (rt_cache_size < RT_CACHE_MAX_SIZE)
+		expire = RT_CACHE_TIMEOUT>>1;
+	else
+		expire >>= 1;
+	end_bh_atomic();
+}
+
+static int rt_ll_bind(struct rtable *rt)
+{
+	struct dst_entry *neigh;
+	struct hh_cache	*hh = NULL;
+
+	if (rt->u.dst.dev && rt->u.dst.dev->hard_header_cache) {
+		neigh = rt->u.dst.neighbour;
+		if (!neigh)
+			neigh = arp_find_neighbour(&rt->u.dst, 1);
+
+		if (neigh) {
+			rt->u.dst.neighbour = neigh;
+			for (hh=neigh->hh; hh; hh = hh->hh_next)
+				if (hh->hh_type == ETH_P_IP)
+					break;
+		}
+
+		if (!hh && (hh = kmalloc(sizeof(*hh), GFP_ATOMIC)) != NULL) {
+#if RT_CACHE_DEBUG >= 2
+			extern atomic_t hh_count;
+			atomic_inc(&hh_count);
+#endif
+			memset(hh, 0, sizeof(struct hh_cache));
+			hh->hh_type = ETH_P_IP;
+			hh->hh_refcnt = 0;
+			hh->hh_next = NULL;
+			if (rt->u.dst.dev->hard_header_cache(&rt->u.dst, neigh, hh)) {
+				kfree(hh);
+#if RT_CACHE_DEBUG >= 2
+				atomic_dec(&hh_count);
+#endif
+				hh = NULL;
+			} else if (neigh) {
+				atomic_inc(&hh->hh_refcnt);
+				hh->hh_next = neigh->hh;
+				neigh->hh = hh;
+			}
+		}
+		if (hh)	{
+			atomic_inc(&hh->hh_refcnt);
+			rt->u.dst.hh = hh;
+			return hh->hh_uptodate;
+		}
+	}
+	return 0;
+}
+
+
+static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt, u16 protocol)
+{
+	struct rtable	*rth, **rthp;
+	unsigned long	now = jiffies;
+
+	rt->u.dst.priority = rt_tos2priority(rt->key.tos);
+
+	start_bh_atomic();
+
+	rthp = &rt_hash_table[hash];
+
+	while ((rth = *rthp) != NULL) {
+		if (memcmp(&rth->key, &rt->key, sizeof(rt->key)) == 0) {
+			/* Put it first */
+			*rthp = rth->u.rt_next;
+			rth->u.rt_next = rt_hash_table[hash];
+			rt_hash_table[hash] = rth;
+
+			atomic_inc(&rth->u.dst.refcnt);
+			atomic_inc(&rth->u.dst.use);
+			rth->u.dst.lastuse = now;
+			end_bh_atomic();
+
+			ip_rt_put(rt);
+			rt_free(rt);
+			return rth;
+		}
+
+		rthp = &rth->u.rt_next;
+	}
+
+	if (rt_cache_size >= RT_CACHE_MAX_SIZE)
+		rt_garbage_collect();
+
+	rt->u.rt_next = rt_hash_table[hash];
+#if RT_CACHE_DEBUG >= 2
+	if (rt->u.rt_next) {
+		struct rtable * trt;
+		printk("rt_cache @%02x: %08x", hash, rt->rt_dst);
+		for (trt=rt->u.rt_next; trt; trt=trt->u.rt_next)
+			printk(" . %08x", trt->rt_dst);
+		printk("\n");
 	}
 #endif
+	rt_hash_table[hash] = rt;
+	atomic_inc(&rt_cache_size);
+
+	if (protocol == ETH_P_IP)
+		rt_ll_bind(rt);
+
+	end_bh_atomic();
+	return rt;
 }
 
-static void rt_garbage_collect_1(void)
+void ip_rt_redirect(u32 old_gw, u32 daddr, u32 new_gw,
+		    u32 saddr, u8 tos, struct device *dev)
 {
 	int i;
-	unsigned expire = RT_CACHE_TIMEOUT>>1;
-	struct rtable * rth, **rthp;
-	unsigned long now = jiffies;
-
-	for (;;)
-	{
-		for (i=0; i<RT_HASH_DIVISOR; i++)
-		{
-			if (!ip_rt_hash_table[i])
+	int  off_link = 0;
+	struct fib_info *fi;
+	struct rtable *rth, **rthp;
+	u32  skeys[2] = { saddr, 0, };
+	struct device *pdev = net_alias_main_dev(dev);
+
+	tos &= IPTOS_TOS_MASK;
+
+	if (new_gw == old_gw || !ipv4_config.accept_redirects
+	    || MULTICAST(new_gw) || BADCLASS(new_gw) || ZERONET(new_gw))
+		goto reject_redirect;
+
+	if ((new_gw^dev->pa_addr)&dev->pa_mask)
+		off_link = 1;
+
+	if (!ipv4_config.rfc1620_redirects) {
+		if (off_link)
+			goto reject_redirect;
+		if (ipv4_config.secure_redirects && ip_fib_chk_default_gw(new_gw, dev))
+			goto reject_redirect;
+	}
+
+	fi = fib_lookup_info(new_gw, 0, 0, &loopback_dev, NULL);
+	if (fi == NULL || fi->fib_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_NAT))
+		goto reject_redirect;
+
+	for (i=0; i<2; i++) {
+		unsigned hash = rt_hash_code(daddr, skeys[i], tos);
+
+		rthp=&rt_hash_table[hash];
+
+		while ( (rth = *rthp) != NULL) {
+			struct rtable *rt;
+
+			if (rth->key.dst != daddr ||
+			    rth->key.src != skeys[i] ||
+			    rth->key.tos != tos ||
+			    rth->key.dst_dev != NULL ||
+			    rth->key.src_dev != NULL) {
+				rthp = &rth->u.rt_next;
 				continue;
-			for (rthp=&ip_rt_hash_table[i]; (rth=*rthp); rthp=&rth->rt_next)
-			{
-				if (rth->rt_lastuse + expire*(rth->rt_refcnt+1) > now)
-					continue;
-				rt_cache_size--;
-				cli();
-				*rthp=rth->rt_next;
-				rth->rt_next = NULL;
-				sti();
-				rt_free(rth);
+			}
+
+			if (rth->rt_dst != daddr ||
+			    rth->rt_src != saddr ||
+			    rth->rt_flags&RTF_REJECT ||
+			    rth->rt_gateway != old_gw ||
+			    rth->u.dst.dev != dev)
 				break;
+
+			rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+			if (rt == NULL)
+				return;
+
+			/*
+			 * Copy all the information.
+			 */
+			rt->u.dst.refcnt = 1;
+			rt->u.dst.dev = dev;
+			rt->u.dst.input = rth->u.dst.input;
+			rt->u.dst.output = rth->u.dst.output;
+			rt->u.dst.pmtu = dev->mtu;
+			rt->u.dst.rtt = TCP_TIMEOUT_INIT;
+			rt->u.dst.window = 0;
+			rt->u.dst.use = 1;
+			rt->u.dst.lastuse = jiffies;
+
+			rt->rt_flags = rth->rt_flags|RTF_DYNAMIC|RTF_MODIFIED;
+			rt->rt_flags &= ~RTF_GATEWAY;
+			if (new_gw != daddr)
+				rt->rt_flags |= RTF_GATEWAY;
+
+			rt->rt_src = rth->rt_src;
+			rt->rt_dst = rth->rt_dst;
+			rt->rt_src_dev = rth->rt_src_dev;
+			rt->rt_spec_dst = rth->rt_spec_dst;
+			rt->key = rth->key;
+
+			/* But gateway is different ... */
+			rt->rt_gateway = new_gw;
+
+			if (off_link) {
+				if (fi->fib_dev != dev &&
+				    net_alias_main_dev(fi->fib_dev) == pdev)
+					rt->u.dst.dev = fi->fib_dev;
 			}
+
+			if (ipv4_config.rfc1620_redirects && !rt_ll_bind(rt)) {
+				ip_rt_put(rt);
+				rt_free(rt);
+				break;
+			}
+
+			*rthp = rth->u.rt_next;
+			rt_free(rth);
+			rt = rt_intern_hash(hash, rt, ETH_P_IP);
+			ip_rt_put(rt);
+			break;
 		}
-		if (rt_cache_size < RT_CACHE_SIZE_MAX)
-			return;
-		expire >>= 1;
 	}
+	return;
+
+reject_redirect:
+	if (ipv4_config.log_martians)
+		printk(KERN_INFO "Redirect from %lX/%s to %lX ignored."
+		       "Path = %lX -> %lX, tos %02x\n",
+		       ntohl(old_gw), dev->name, ntohl(new_gw),
+		       ntohl(saddr), ntohl(daddr), tos);
 }
 
-static __inline__ void rt_req_enqueue(struct rt_req **q, struct rt_req *rtr)
+
+void ip_rt_advice(struct rtable **rp, int advice)
 {
-	unsigned long flags;
-	struct rt_req * tail;
+	struct rtable *rt;
 
-	save_flags(flags);
-	cli();
-	tail = *q;
-	if (!tail)
-		rtr->rtr_next = rtr;
-	else
-	{
-		rtr->rtr_next = tail->rtr_next;
-		tail->rtr_next = rtr;
+	if (advice)
+		return;
+
+	start_bh_atomic();
+	if ((rt = *rp) != NULL && (rt->rt_flags&(RTF_DYNAMIC|RTF_MODIFIED))) {
+#if RT_CACHE_DEBUG >= 1
+		printk(KERN_DEBUG "ip_rt_advice: redirect to %08x/%02x dropped\n", rt->rt_dst, rt->key.tos);
+#endif
+		*rp = NULL;
+		ip_rt_put(rt);
+		rt_cache_flush(0);
 	}
-	*q = rtr;
-	restore_flags(flags);
+	end_bh_atomic();
 	return;
 }
 
 /*
- * Caller should mask interrupts.
+ * Algorithm:
+ *	1. The first RT_REDIRECT_NUMBER redirects are sent
+ *	   with exponential backoff, then we stop sending them at all,
+ *	   assuming that the host ignores our redirects.
+ *	2. If we did not see a packets requiring redirects
+ *	   during RT_REDIRECT_SILENCE, we assume that the host
+ *	   forgot redirected route and start to send redirects again.
+ *
+ * This algorithm is much cheaper and more intelligent than dumb load limiting
+ * in icmp.c.
+ *
+ * NOTE. Do not forget to inhibit load limiting for redirects (redundant)
+ * and "frag. need" (breaks PMTU discovery) in icmp.c.
  */
 
-static __inline__ struct rt_req * rt_req_dequeue(struct rt_req **q)
+void ip_rt_send_redirect(struct sk_buff *skb)
 {
-	struct rt_req * rtr;
+	struct rtable *rt = (struct rtable*)skb->dst;
 
-	if (*q)
-	{
-		rtr = (*q)->rtr_next;
-		(*q)->rtr_next = rtr->rtr_next;
-		if (rtr->rtr_next == rtr)
-			*q = NULL;
-		rtr->rtr_next = NULL;
-		return rtr;
+	/* No redirected packets during RT_REDIRECT_SILENCE;
+	 * reset the algorithm.
+	 */
+	if (jiffies - rt->last_error > RT_REDIRECT_SILENCE)
+		rt->errors = 0;
+
+	/* Too many ignored redirects; do not send anything
+	 * set last_error to the last seen redirected packet.
+	 */
+	if (rt->errors >= RT_REDIRECT_NUMBER) {
+		rt->last_error = jiffies;
+		return;
+	}
+
+	/* Check for load limit; set last_error to the latest sent
+	 * redirect.
+	 */
+	if (jiffies - rt->last_error > (RT_REDIRECT_LOAD<<rt->errors)) {
+		icmp_send(skb, ICMP_REDIRECT, ICMP_REDIR_HOST, rt->rt_gateway);
+		rt->last_error = jiffies;
+		if (ipv4_config.log_martians && ++rt->errors == RT_REDIRECT_NUMBER)
+			printk(KERN_WARNING "host %08x/%s ignores redirects.\n", rt->rt_src, rt->rt_src_dev->name);
 	}
-	return NULL;
 }
 
-/*
-   Called with masked interrupts
- */
+static int ip_error(struct sk_buff *skb)
+{
+	struct rtable *rt = (struct rtable*)skb->dst;
+	int code;
+
+	switch (rt->u.dst.error) {
+	case EINVAL:
+	default:
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	case ENETUNREACH:
+		code = ICMP_NET_UNREACH;
+		break;
+	case EACCES:
+		code = ICMP_PKT_FILTERED;
+		break;
+	}
+	if (jiffies - rt->last_error > RT_ERROR_LOAD) {
+		icmp_send(skb, ICMP_DEST_UNREACH, code, 0);
+		rt->last_error = jiffies;
+	}
+	kfree_skb(skb, FREE_READ);
+	return 0;
+} 
+
 
-static void rt_kick_backlog()
+static __inline__ unsigned short guess_mtu(unsigned short old_mtu)
 {
-	if (!ip_rt_lock)
-	{
-		struct rt_req * rtr;
+	if (old_mtu > 32000)
+		return 32000;
+	else if (old_mtu > 17914)
+		return 17914;
+	else if (old_mtu > 8166)
+		return 8166;
+	else if (old_mtu > 4352)
+		return 4352;
+	else if (old_mtu > 2002)
+		return 2002;
+	else if (old_mtu > 1492)
+		return 1492;
+	else if (old_mtu > 576)
+		return 576;
+	else if (old_mtu > 296)
+		return 296;
+	/*
+	 *	These two are not from the RFC but
+	 *	are needed for AMPRnet AX.25 paths.
+	 */
+	else if (old_mtu > 216)
+		return 216;
+	else if (old_mtu > 128)
+		return 128;
+	return 68;
+}
 
-		ip_rt_fast_lock();
 
-		while ((rtr = rt_req_dequeue(&rt_backlog)) != NULL)
-		{
-			sti();
-			rt_redirect_1(rtr->dst, rtr->gw, rtr->dev);
-			kfree_s(rtr, sizeof(struct rt_req));
-			cli();
+unsigned short ip_rt_frag_needed(struct iphdr *iph, unsigned short new_mtu)
+{
+	int i;
+	unsigned short old_mtu = ntohs(iph->tot_len);
+	struct rtable *rth;
+	u32  skeys[2] = { iph->saddr, 0, };
+	u32  daddr = iph->daddr;
+	u8   tos = iph->tos & IPTOS_TOS_MASK;
+	unsigned short est_mtu = 0;
+
+	if (ipv4_config.no_pmtu_disc)
+		return 0;
+
+	for (i=0; i<2; i++) {
+		unsigned hash = rt_hash_code(daddr, skeys[i], tos);
+
+		for (rth = rt_hash_table[hash]; rth; rth = rth->u.rt_next) {
+			if (rth->key.dst == daddr &&
+			    rth->key.src == skeys[i] &&
+			    rth->rt_dst == daddr &&
+			    rth->rt_src == iph->saddr &&
+			    rth->key.tos == tos &&
+			    !rth->key.src_dev &&
+			    !(rth->rt_flags&RTF_NOPMTUDISC)) {
+				unsigned short mtu = new_mtu;
+
+				if (new_mtu < 68 || new_mtu >= old_mtu) {
+
+					/* BSD 4.2 compatibility hack :-( */
+					if (mtu == 0 && old_mtu >= rth->u.dst.pmtu &&
+					    old_mtu >= 68 + (iph->ihl<<2))
+						old_mtu -= iph->ihl<<2;
+
+					mtu = guess_mtu(old_mtu);
+				}
+				if (mtu < rth->u.dst.pmtu) {
+					rth->u.dst.pmtu = mtu;
+					est_mtu = mtu;
+				}
+			}
 		}
+	}
+	return est_mtu;
+}
 
-		ip_rt_bh_mask &= ~RT_BH_REDIRECT;
 
-		ip_rt_fast_unlock();
+static void ipv4_dst_destroy(struct dst_entry * dst)
+{
+	struct rtable * rt = (struct rtable*)dst;
+	struct hh_cache * hh = rt->u.dst.hh;
+	rt->u.dst.hh = NULL;
+	if (hh && atomic_dec_and_test(&hh->hh_refcnt)) {
+#if RT_CACHE_DEBUG >= 2
+		extern atomic_t hh_count;
+		atomic_dec(&hh_count);
+#endif
+		kfree(hh);
 	}
 }
 
-/*
- * rt_{del|add|flush} called only from USER process. Waiting is OK.
- */
+static struct dst_entry * ipv4_dst_check(struct dst_entry * dst)
+{
+	return NULL;
+}
 
-static int rt_del(__u32 dst, __u32 mask,
-		struct device * dev, __u32 gtw, short rt_flags, short metric)
+static struct dst_entry * ipv4_dst_reroute(struct dst_entry * dst)
 {
-	int retval;
+	return NULL;
+}
+
+int
+ip_check_mc(struct device *dev, u32 mc_addr)
+{
+	struct ip_mc_list *ip_mc;
+
+	if (mc_addr==htonl(INADDR_ALLHOSTS_GROUP))
+		return 1;
+
+	for (ip_mc=dev->ip_mc_list; ip_mc; ip_mc=ip_mc->next)
+		if (ip_mc->multiaddr == mc_addr)
+			return 1;
+	return 0;
+}
 
-	while (ip_rt_lock)
-		sleep_on(&rt_wait);
-	ip_rt_fast_lock();
-	retval = fib_del_1(dst, mask, dev, gtw, rt_flags, metric);
-	ip_rt_unlock();
-	wake_up(&rt_wait);
-	return retval;
-}
-
-static void rt_add(short flags, __u32 dst, __u32 mask,
-	__u32 gw, struct device *dev, unsigned short mss,
-	unsigned long window, unsigned short irtt, short metric)
-{
-	while (ip_rt_lock)
-		sleep_on(&rt_wait);
-	ip_rt_fast_lock();
-	fib_add_1(flags, dst, mask, gw, dev, mss, window, irtt, metric);
-	ip_rt_unlock();
-	wake_up(&rt_wait);
-}
-
-void ip_rt_flush(struct device *dev)
-{
-	while (ip_rt_lock)
-		sleep_on(&rt_wait);
-	ip_rt_fast_lock();
-	fib_flush_1(dev);
-	ip_rt_unlock();
-	wake_up(&rt_wait);
+static int ip_rt_bug(struct sk_buff *skb)
+{
+	kfree_skb(skb, FREE_WRITE);
+	printk(KERN_DEBUG "ip_rt_bug: %08x -> %08x, %s\n", skb->nh.iph->saddr,
+	       skb->nh.iph->daddr, skb->dev ? skb->dev->name : "?");
+	return 0;
 }
 
 /*
-   Called by ICMP module.
+ *	This function is called ONLY FROM NET BH. No locking!
+ *
+ *	NOTE. We drop all the packets that has local source
+ *	addresses, because every properly looped back packet
+ *	must have correct destination already attached by output routine.
+ *
+ *	Such approach solves two big problems:
+ *	1. Not simplex devices (if they exist 8)) are handled properly.
+ *	2. IP spoofing attempts are filtered with 100% of guarantee.
  */
 
-void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev)
-{
-	struct rt_req * rtr;
-	struct rtable * rt;
+int ip_route_input_slow(struct sk_buff *skb, u32 daddr, u32 saddr,
+			u8 tos, struct device *pdev)
+{
+	struct device * dev = pdev;
+	struct fib_info *fi = NULL;
+	struct fib_info *src_fi = NULL;
+	unsigned	flags = 0;
+	struct	device	*devout;
+	struct rtable * rth;
+	unsigned	hash;
+	struct fib_result res;
+	u32	src_key = saddr;
+	u32	dst_key = daddr;
+	int	err = -EINVAL;
+	int	log = 0;
 
-	rt = ip_rt_route(dst, 0);
-	if (!rt)
-		return;
+	hash = rt_hash_code(daddr, saddr^(unsigned long)pdev, tos);
 
-	if (rt->rt_gateway != src ||
-	    rt->rt_dev != dev ||
-	    ((gw^dev->pa_addr)&dev->pa_mask) ||
-	    ip_chk_addr(gw))
-	{
-		ip_rt_put(rt);
-		return;
-	}
-	ip_rt_put(rt);
+	/*	Check for martians... */
 
-	ip_rt_fast_lock();
-	if (ip_rt_lock == 1)
-	{
-		rt_redirect_1(dst, gw, dev);
-		ip_rt_unlock();
-		return;
-	}
+	if (MULTICAST(saddr) || BADCLASS(saddr) || LOOPBACK(saddr))
+		goto martian_source;
+	if (MULTICAST(daddr) || daddr == 0xFFFFFFFF)
+		goto mc_input;
 
-	rtr = kmalloc(sizeof(struct rt_req), GFP_ATOMIC);
-	if (rtr)
-	{
-		rtr->dst = dst;
-		rtr->gw = gw;
-		rtr->dev = dev;
-		rt_req_enqueue(&rt_backlog, rtr);
-		ip_rt_bh_mask |= RT_BH_REDIRECT;
-	}
-	ip_rt_unlock();
-}
+	/* Accept zero addresses only to limited broadcast/multicasts;
+	 * I even do not know to fix it or not.
+	 */
+	if (ZERONET(saddr))
+		goto martian_source;
+	if (BADCLASS(daddr) || ZERONET(daddr) || LOOPBACK(daddr))
+		goto martian_destination;
 
+	/*
+	 * Device is not yet initialized, accept all addresses as ours.
+	 */
+	if (ZERONET(dev->pa_addr))
+		goto promisc_ip;
 
-static __inline__ void rt_garbage_collect(void)
-{
-	if (ip_rt_lock == 1)
-	{
-		rt_garbage_collect_1();
-		return;
+	/*
+	 *	Now we are able to route packet.
+	 */
+	if ((err = fib_lookup(&res, daddr, saddr, tos, pdev, NULL)) < 0) {
+		if (!IS_ROUTER)
+			return -EINVAL;
+		goto no_route;
 	}
-	ip_rt_bh_mask |= RT_BH_GARBAGE_COLLECT;
-}
 
-static void rt_cache_add(unsigned hash, struct rtable * rth)
-{
-	unsigned long	flags;
-	struct rtable	**rthp;
-	__u32		daddr = rth->rt_dst;
-	unsigned long	now = jiffies;
+	fi = res.f->fib_info;
+	flags  = fi->fib_flags;
+	devout = fi->fib_dev;
 
-#if RT_CACHE_DEBUG >= 2
-	if (ip_rt_lock != 1)
-	{
-		printk("rt_cache_add: ip_rt_lock==%d\n", ip_rt_lock);
-		return;
+	if (flags&RTF_NAT) {
+		daddr = htonl((ntohl(daddr)&((1<<res.fm)-1)))|fi->fib_gateway;
+		fi = fib_lookup_info(daddr, saddr, tos, pdev, NULL);
+		if (!fi || fi->fib_flags&(RTF_NAT|RTF_LOCAL|RTF_MULTICAST|RTF_BROADCAST))
+			return -EINVAL;
+		devout = fi->fib_dev;
+		flags = fi->fib_flags|RTCF_NAT|RTF_NAT;
 	}
-#endif
 
-	save_flags(flags);
+	switch (res.fr->cl_action) {
+	case RTP_NAT:
+		/* Packet is from  translated source; remember it */
+		saddr = (saddr&~res.fr->cl_srcmask)|res.fr->cl_srcmap;
+		flags |= RTCF_NAT;
+		break;
+	case RTP_MASQUERADE:
+		/* Packet is from masqueraded source; remember it */
+		flags |= RTCF_MASQ;
+		break;
+	default:
+	}
+	log = res.fr->cl_flags&RTRF_LOG;
 
-	if (rth->rt_dev->header_cache_bind)
-	{
-		struct rtable * rtg = rth;
-
-		if (rth->rt_gateway != daddr)
-		{
-			ip_rt_fast_unlock();
-			rtg = ip_rt_route(rth->rt_gateway, 0);
-			ip_rt_fast_lock();
-		}
+	if (!(flags & RTF_LOCAL)) {
+		if (!IS_ROUTER || flags&RTF_NOFORWARD)
+			return -EINVAL;
+	} else {
+		fi = NULL;
+		devout = &loopback_dev;
+		if (flags&RTF_BROADCAST)
+		    goto mc_input;
+	}
 
-		if (rtg)
-		{
-			if (rtg == rth)
-				rtg->rt_dev->header_cache_bind(&rtg->rt_hh, rtg->rt_dev, ETH_P_IP, rtg->rt_dst);
-			else
-			{
-				if (rtg->rt_hh)
-					atomic_inc(&rtg->rt_hh->hh_refcnt);
-				rth->rt_hh = rtg->rt_hh;
-				ip_rt_put(rtg);
-			}
-		}
+#ifndef CONFIG_IP_LOCAL_RT_POLICY
+	if (flags&RTF_LOCAL)
+		src_fi = fib_lookup_info(src_key, 0, tos, &loopback_dev, NULL);
+	else
+#endif
+	if (fib_lookup(&res, src_key, daddr, tos, net_alias_main_dev(devout), NULL) == 0) {
+		src_fi = res.f->fib_info;
+		/* Destination is on masqueraded network:
+		 * if it is real incoming frame, ip_forward will drop it.
+		 */
+		if (res.fr->cl_flags&RTRF_VALVE)
+			flags |= RTCF_VALVE;
 	}
 
-	if (rt_cache_size >= RT_CACHE_SIZE_MAX)
-		rt_garbage_collect();
+        if (src_fi) {
+		if (src_fi->fib_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTF_NAT))
+			goto martian_source;
+
+		if (!(src_fi->fib_flags&RTF_GATEWAY))
+			flags |= RTCF_DIRECTSRC;
+
+		if (net_alias_main_dev(src_fi->fib_dev) == pdev)
+			skb->dev = dev = src_fi->fib_dev;
+		else {
+			/* Route to packet source goes via
+			   different interface; rfc1812 proposes
+			   to drop them.
+			   It is dangerous on not-stub/transit networks
+			   because of path asymmetry.
+			 */
+			if (ipv4_config.rfc1812_filter >= 2)
+				goto martian_source;
 
-	cli();
-	rth->rt_next = ip_rt_hash_table[hash];
-#if RT_CACHE_DEBUG >= 2
-	if (rth->rt_next)
-	{
-		struct rtable * trth;
-		printk("rt_cache @%02x: %08x", hash, daddr);
-		for (trth=rth->rt_next; trth; trth=trth->rt_next)
-			printk(" . %08x", trth->rt_dst);
-		printk("\n");
+			/* Weaker form of rfc1812 filtering.
+			   If source is on directly connected network,
+			   it can mean either local network configuration error
+			   (the most probable case) or real IP spoofing attempt.
+			 */
+			if (ipv4_config.rfc1812_filter >= 1 && !(flags&RTCF_DIRECTSRC))
+				goto martian_source;
+		}
+	} else if (ipv4_config.rfc1812_filter >= 1)
+		goto martian_source;
+
+make_route:
+	if (skb->protocol != __constant_htons(ETH_P_IP)) {
+		/* ARP request. Do not make route for invalid destination or
+		 * if it is redirected.
+		 */
+		if (flags&(RTF_REJECT|RTF_BROADCAST|RTF_MULTICAST) ||
+		    skb->pkt_type == PACKET_OTHERHOST ||
+		    (devout == dev && !(flags&(RTF_LOCAL|RTCF_NAT))))
+			return -EINVAL;
 	}
-#endif
-	ip_rt_hash_table[hash] = rth;
-	rthp = &rth->rt_next;
-	sti();
-	rt_cache_size++;
 
-	/*
-	 * Cleanup duplicate (and aged off) entries.
-	 */
+	rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+	if (!rth)
+		return -ENOBUFS;
 
-	while ((rth = *rthp) != NULL)
-	{
+	rth->u.dst.output= ip_rt_bug;
 
-		cli();
-		if ((!rth->rt_refcnt && rth->rt_lastuse + RT_CACHE_TIMEOUT < now)
-		    || rth->rt_dst == daddr)
-		{
-			*rthp = rth->rt_next;
-			rt_cache_size--;
-			sti();
-#if RT_CACHE_DEBUG >= 2
-			printk("rt_cache clean %02x@%08x\n", hash, rth->rt_dst);
+	rth->u.dst.use	= 1;
+	rth->key.dst	= dst_key;
+	rth->rt_dst	= dst_key;
+	rth->rt_dst_map	= daddr;
+	rth->key.tos	= tos;
+	rth->key.src	= src_key;
+	rth->rt_src	= src_key;
+	rth->rt_src_map	= saddr;
+	rth->rt_src_dev = dev;
+	rth->key.src_dev= pdev;
+	rth->u.dst.dev	= devout;
+	rth->key.dst_dev= NULL;
+	rth->rt_gateway	= daddr;
+	rth->rt_spec_dst= daddr;
+
+	if (!(flags&RTF_REJECT)) {
+		if (flags&RTF_LOCAL)
+			rth->u.dst.input= ip_local_deliver;
+		if (!(flags&(RTF_NOFORWARD|RTF_BROADCAST))) {
+			if (flags&RTF_MULTICAST) {
+#ifdef CONFIG_IP_MROUTE
+				if (!LOCAL_MCAST(daddr) && ipv4_config.multicast_route) {
+					rth->u.dst.input = ip_mr_input;
+					rth->u.dst.output = ip_output;
+				}
 #endif
-			rt_free(rth);
-			continue;
+			} else if (!(flags&RTF_LOCAL)) {
+				rth->u.dst.input = ip_forward;
+				rth->u.dst.output = ip_output;
+			}
 		}
-		sti();
-		rthp = &rth->rt_next;
-	}
-	restore_flags(flags);
-}
-
-/*
-   RT should be already locked.
-   
-   We could improve this by keeping a chain of say 32 struct rtable's
-   last freed for fast recycling.
-   
- */
+	} else if (IS_ROUTER && !(flags&(RTF_MULTICAST|RTF_BROADCAST))) {
+		rth->u.dst.input= ip_error;
+		rth->u.dst.error= -err;
+	}
+
+	if ((flags&(RTF_BROADCAST|RTF_MULTICAST)) || !(flags&RTF_LOCAL))
+		rth->rt_spec_dst= dev->pa_addr;
+
+	if (fi) {
+		rth->u.dst.pmtu	= fi->fib_mtu;
+		rth->u.dst.window=fi->fib_window;
+		rth->u.dst.rtt	= fi->fib_irtt;
+		if (flags & RTF_GATEWAY)
+			rth->rt_gateway	= fi->fib_gateway;
+	} else {
+		rth->u.dst.pmtu	= devout->mtu;
+		rth->u.dst.window=0;
+		rth->u.dst.rtt	= TCP_TIMEOUT_INIT;
+	}
+
+	if (!(flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTCF_NAT)) &&
+	    flags&RTCF_DIRECTSRC &&
+	    (devout == dev || (ipv4_config.rfc1620_redirects &&
+			       net_alias_main_dev(devout) == pdev)))
+		flags |= RTCF_DOREDIRECT;
 
-struct rtable * ip_rt_slow_route (__u32 daddr, int local)
-{
-	unsigned hash = ip_rt_hash_code(daddr)^local;
-	struct rtable * rth;
-	struct fib_node * f;
-	struct fib_info * fi;
-	__u32 saddr;
+	rth->rt_flags = flags;
 
-#if RT_CACHE_DEBUG >= 2
-	printk("rt_cache miss @%08x\n", daddr);
-#endif
+	if (log)
+		printk(KERN_INFO "installing route %08lX -> %08lX\n", ntohl(rth->rt_src), ntohl(rth->rt_dst));
 
-	rth = kmalloc(sizeof(struct rtable), GFP_ATOMIC);
-	if (!rth)
-	{
-		ip_rt_unlock();
-		return NULL;
+	if (flags&(RTF_LOCAL|RTF_MULTICAST|RTF_BROADCAST|RTF_REJECT)) {
+		skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, 0);
+		return 0;
 	}
+	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, __constant_ntohs(skb->protocol));
+	return 0;
 
-	if (local)
-		f = fib_lookup_local(daddr);
-	else
-		f = fib_lookup (daddr);
+mc_input:
+	if (skb->protocol != __constant_htons(ETH_P_IP))
+		return -EINVAL;
 
-	if (f)
-	{
-		fi = f->fib_info;
-		f->fib_use++;
+	if (ZERONET(saddr)) {
+		if (!ipv4_config.bootp_agent)
+			goto martian_source;
+		flags |= RTF_NOFORWARD|RTF_LOCAL;
+	} else {
+		src_fi = fib_lookup_info(saddr, 0, tos, &loopback_dev, NULL);
+		if (!src_fi)
+			goto martian_source;
+
+		if (src_fi->fib_flags&(RTF_LOCAL|RTF_BROADCAST|RTF_MULTICAST|RTF_NAT))
+			goto martian_source;
+
+		if (!(src_fi->fib_flags&RTF_GATEWAY))
+			flags |= RTCF_DIRECTSRC;
+
+		if (!MULTICAST(daddr) || !ipv4_config.multicast_route ||
+		    LOCAL_MCAST(daddr)) {
+			if (net_alias_main_dev(src_fi->fib_dev) == pdev) {
+				skb->dev = dev = src_fi->fib_dev;
+			} else {
+				/* Fascist not-unicast filtering 8) */
+				goto martian_source;
+			}
+		}
 	}
 
-	if (!f || (fi->fib_flags & RTF_REJECT))
-	{
-#ifdef CONFIG_KERNELD	
-		char wanted_route[20];
-#endif		
-#if RT_CACHE_DEBUG >= 2
-		printk("rt_route failed @%08x\n", daddr);
-#endif
-		ip_rt_unlock();
-		kfree_s(rth, sizeof(struct rtable));
-#ifdef CONFIG_KERNELD		
-		daddr=ntohl(daddr);
-		sprintf(wanted_route, "%d.%d.%d.%d",
-			(int)(daddr >> 24) & 0xff, (int)(daddr >> 16) & 0xff,
-			(int)(daddr >> 8) & 0xff, (int)daddr & 0xff);
-		kerneld_route(wanted_route); 	/* Dynamic route request */
-#endif		
-		return NULL;
-	}
-
-	saddr = fi->fib_dev->pa_addr;
-
-	if (daddr == fi->fib_dev->pa_addr)
-	{
-		f->fib_use--;
-		if ((f = fib_loopback) != NULL)
-		{
-			f->fib_use++;
-			fi = f->fib_info;
-		}
+	if (!MULTICAST(daddr)) {
+		flags |= RTF_LOCAL|RTF_BROADCAST|RTF_NOFORWARD;
+		devout = dev;
+		goto make_route;
 	}
-	
-	if (!f)
-	{
-		ip_rt_unlock();
-		kfree_s(rth, sizeof(struct rtable));
-		return NULL;
+
+	flags |= RTF_MULTICAST|RTF_LOCAL;
+
+	if (ip_check_mc(dev, daddr) == 0) {
+		flags &= ~RTF_LOCAL;
+
+		if (!ipv4_config.multicast_route || !(dev->flags&IFF_ALLMULTI))
+			goto no_route;
 	}
+	devout = dev;
+	goto make_route;
 
-	rth->rt_dst	= daddr;
-	rth->rt_src	= saddr;
-	rth->rt_lastuse	= jiffies;
-	rth->rt_refcnt	= 1;
-	rth->rt_use	= 1;
-	rth->rt_next	= NULL;
-	rth->rt_hh	= NULL;
-	rth->rt_gateway	= fi->fib_gateway;
-	rth->rt_dev	= fi->fib_dev;
-	rth->rt_mtu	= fi->fib_mtu;
-	rth->rt_window	= fi->fib_window;
-	rth->rt_irtt	= fi->fib_irtt;
-	rth->rt_tos	= f->fib_tos;
-	rth->rt_flags   = fi->fib_flags | RTF_HOST;
-	if (local)
-		rth->rt_flags   |= RTF_LOCAL;
+promisc_ip:
+	flags |= RTF_LOCAL|RTF_NOFORWARD;
+	if (MULTICAST(daddr))
+		flags |= RTF_MULTICAST;
+	else
+		flags |= RTF_BROADCAST;
+	devout = dev;
+	goto make_route;
+
+no_route:
+	flags |= RTF_REJECT;
+	devout = dev;
+	goto make_route;
 
-	if (!(rth->rt_flags & RTF_GATEWAY))
-		rth->rt_gateway = rth->rt_dst;
 	/*
-	 *	Multicast or limited broadcast is never gatewayed.
+	 *	Do not cache martian addresses: they should be logged (RFC1812)
 	 */
-	if (MULTICAST(daddr) || daddr == 0xFFFFFFFF)
-		rth->rt_gateway = rth->rt_dst;
+martian_destination:
+	if (ipv4_config.log_martians)
+		printk(KERN_WARNING "martian destination %08x from %08x, dev %s\n", daddr, saddr, dev->name);
+	return -EINVAL;
 
-	if (ip_rt_lock == 1)
-		rt_cache_add(hash, rth);
-	else
-	{
-		rt_free(rth);
-#if RT_CACHE_DEBUG >= 1
-		printk(KERN_DEBUG "rt_cache: route to %08x was born dead\n", daddr);
-#endif
+martian_source:
+	if (ipv4_config.log_martians) {
+		/*
+		 *	RFC1812 recommenadtion, if source is martian,
+		 *	the only hint is MAC header.
+		 */
+		printk(KERN_WARNING "martian source %08x for %08x, dev %s\n", saddr, daddr, dev->name);
+		if (dev->hard_header_len) {
+			int i;
+			unsigned char *p = skb->mac.raw;
+			printk(KERN_WARNING "ll header:");
+			for (i=0; i<dev->hard_header_len; i++, p++)
+				printk(" %02x", *p);
+			printk("\n");
+		}
 	}
-
-	ip_rt_unlock();
-	return rth;
-}
-
-void ip_rt_put(struct rtable * rt)
-{
-	if (rt)
-		atomic_dec(&rt->rt_refcnt);
+	return -EINVAL;
 }
 
-struct rtable * ip_rt_route(__u32 daddr, int local)
+int ip_route_input(struct sk_buff *skb, u32 daddr, u32 saddr,
+		   u8 tos, struct device *dev)
 {
 	struct rtable * rth;
+	unsigned	hash;
 
-	ip_rt_fast_lock();
+	if (skb->dst)
+		return 0;
 
-	for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
-	{
-		if (rth->rt_dst == daddr)
-		{
-			rth->rt_lastuse = jiffies;
-			atomic_inc(&rth->rt_use);
-			atomic_inc(&rth->rt_refcnt);
-			ip_rt_unlock();
-			return rth;
+#if RT_CACHE_DEBUG >= 1
+	if (dev->flags & IFF_LOOPBACK) {
+		printk(KERN_DEBUG "ip_route_input: bug: packet is looped back\n");
+		return -EINVAL;
+	}
+	if (net_alias_main_dev(dev) != dev)
+		printk(KERN_DEBUG "ip_route_input: bug: packet is received on alias %s\n", dev->name);
+#endif
+
+	tos &= IPTOS_TOS_MASK;
+	hash = rt_hash_code(daddr, saddr^(unsigned long)dev, tos);
+	skb->dev = dev;
+
+	for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+		if (rth->key.dst == daddr &&
+		    rth->key.src == saddr &&
+		    rth->key.src_dev == dev &&
+		    rth->key.dst_dev == NULL &&
+		    rth->key.tos == tos) {
+			rth->u.dst.lastuse = jiffies;
+			atomic_inc(&rth->u.dst.use);
+			atomic_inc(&rth->u.dst.refcnt);
+			skb->dst = (struct dst_entry*)rth;
+			skb->dev = rth->rt_src_dev;
+			return 0;
 		}
 	}
-	return ip_rt_slow_route (daddr, local);
+	return ip_route_input_slow(skb, daddr, saddr, tos, dev);
 }
 
+
 /*
- *	Process a route add request from the user, or from a kernel
- *	task.
+ * Major route resolver routine.
  */
- 
-int ip_rt_new(struct rtentry *r)
-{
-	int err;
-	char * devname;
-	struct device * dev = NULL;
-	unsigned long flags;
-	__u32 daddr, mask, gw;
-	short metric;
 
-	/*
-	 *	If a device is specified find it.
-	 */
-	 
-	if ((devname = r->rt_dev) != NULL) 
-	{
-		err = getname(devname, &devname);
-		if (err)
-			return err;
-		dev = dev_get(devname);
-		putname(devname);
-		if (!dev)
-			return -ENODEV;
-	}
-	
-	/*
-	 *	If the device isn't INET, don't allow it
-	 */
+int ip_route_output_slow(struct rtable **rp, u32 daddr, u32 saddr, u8 tos,
+			 struct device *dev_out)
+{
+	u32 src_key = saddr;
+	u32 dst_key = daddr;
+	u32 dst_map;
+	struct device *dst_dev_key = dev_out;
+	unsigned flags = 0;
+	struct fib_info *fi = NULL;
+	struct rtable *rth;
+#ifdef CONFIG_IP_LOCAL_RT_POLICY
+	struct fib_result res;
+#endif
+	unsigned hash;
 
-	if (r->rt_dst.sa_family != AF_INET)
-		return -EAFNOSUPPORT;
+	tos &= IPTOS_TOS_MASK|1;
 
-	/*
-	 *	Make local copies of the important bits
-	 *	We decrement the metric by one for BSD compatibility.
-	 */
-	 
-	flags = r->rt_flags;
-	daddr = (__u32) ((struct sockaddr_in *) &r->rt_dst)->sin_addr.s_addr;
-	mask  = (__u32) ((struct sockaddr_in *) &r->rt_genmask)->sin_addr.s_addr;
-	gw    = (__u32) ((struct sockaddr_in *) &r->rt_gateway)->sin_addr.s_addr;
-	metric = r->rt_metric > 0 ? r->rt_metric - 1 : 0;
+	if (saddr) {
+		if (MULTICAST(saddr) || BADCLASS(saddr) || ZERONET(saddr) ||
+		    __ip_chk_addr(saddr) != IS_MYADDR)
+			return -EINVAL;
+		if (dev_out == NULL && (MULTICAST(daddr) || daddr == 0xFFFFFFFF))
+			dev_out = ip_dev_find(saddr, NULL);
+	}
+	if (!daddr)
+		daddr = saddr;
 
-	/*
-	 *	BSD emulation: Permits route add someroute gw one-of-my-addresses
-	 *	to indicate which iface. Not as clean as the nice Linux dev technique
-	 *	but people keep using it...  (and gated likes it ;))
-	 */
-	 
-	if (!dev && (flags & RTF_GATEWAY)) 
-	{
-		struct device *dev2;
-		for (dev2 = dev_base ; dev2 != NULL ; dev2 = dev2->next) 
-		{
-			if ((dev2->flags & IFF_UP) && dev2->pa_addr == gw) 
-			{
-				flags &= ~RTF_GATEWAY;
-				dev = dev2;
-				break;
-			}
-		}
+	if (dev_out) {
+		if (!saddr) {
+			saddr = dev_out->pa_addr;
+			if (!daddr)
+				daddr = saddr;
+		}
+		dst_map = daddr;
+		if (MULTICAST(daddr) || daddr == 0xFFFFFFFF)
+			goto make_route;
+	}
+
+	if (!daddr)
+		daddr = htonl(INADDR_LOOPBACK);
+
+#ifdef CONFIG_IP_LOCAL_RT_POLICY
+	if (fib_lookup(&res, daddr, saddr, tos, &loopback_dev, dev_out))
+		return -ENETUNREACH;
+	fi = res.f->fib_info;
+	dst_map = daddr;
+
+	if (fi->fib_flags&RTF_NAT) {
+		dst_map = htonl((ntohl(daddr)&((1<<res.fm)-1)))|fi->fib_gateway;
+		fi = fib_lookup_info(dst_map, saddr, tos, &loopback_dev, dev_out);
+		if (!fi || fi->fib_flags&(RTF_NAT|RTF_LOCAL|RTF_MULTICAST|RTF_BROADCAST))
+			return -EINVAL;
+		flags = RTCF_NAT;
 	}
 
-	if (flags & RTF_HOST) 
-		mask = 0xffffffff;
-	else if (mask && r->rt_genmask.sa_family != AF_INET)
-		return -EAFNOSUPPORT;
-
-	if (flags & RTF_GATEWAY) 
-	{
-		if (r->rt_gateway.sa_family != AF_INET)
-			return -EAFNOSUPPORT;
+	if (!saddr) {
+		saddr = fi->fib_dev->pa_addr;
 
 		/*
-		 *	Don't try to add a gateway we can't reach.. 
-		 *	Tunnel devices are exempt from this rule.
+		 * "Stabilization" of route.
+		 * This step is necessary, if locally originated packets
+		 * are subjected to source routing, else we could get
+		 * route flapping.
 		 */
-
-		if (!dev)
-			dev = get_gw_dev(gw);
-		else if (dev != get_gw_dev(gw) && dev->type != ARPHRD_TUNNEL)
-			return -EINVAL;
-		if (!dev)
-			return -ENETUNREACH;
-	} 
-	else
-	{
-		gw = 0;
-		if (!dev)
-			dev = ip_dev_bynet(daddr, mask);
-		if (!dev)
+		fi = fib_lookup_info(dst_map, saddr, tos, &loopback_dev, dev_out);
+		if (!fi)
 			return -ENETUNREACH;
-		if (!mask)
-		{
-			if (((daddr ^ dev->pa_addr) & dev->pa_mask) == 0)
-				mask = dev->pa_mask;
-		}
 	}
+#else
+	fi = fib_lookup_info(daddr, 0, tos, &loopback_dev, dev_out);
+	if (!fi)
+		return -ENETUNREACH;
+
+	if (fi->fib_flags&RTF_NAT)
+		return -EINVAL;
 
-#ifndef CONFIG_IP_CLASSLESS
-	if (!mask)
-		mask = ip_get_mask(daddr);
+	dst_map = daddr;
+	if (!saddr)
+		saddr = fi->fib_dev->pa_addr;
 #endif
-	
-	if (bad_mask(mask, daddr))
+
+	flags |= fi->fib_flags;
+	dev_out = fi->fib_dev;
+
+	if (RT_LOCALADDR(flags)) {
+		dev_out = &loopback_dev;
+		fi = NULL;
+	}
+
+	if (dst_dev_key && dev_out != dst_dev_key)
 		return -EINVAL;
 
-	/*
-	 *	Add the route
-	 */
+make_route:
+	if (LOOPBACK(saddr) && !(dev_out->flags&IFF_LOOPBACK)) {
+		printk(KERN_DEBUG "this guy talks to %08x from loopback\n", daddr);
+		return -EINVAL;
+	}
+
+	if (daddr == 0xFFFFFFFF)
+		flags |= RTF_BROADCAST;
+	else if (MULTICAST(daddr))
+		flags |= RTF_MULTICAST;
+	else if (BADCLASS(daddr) || ZERONET(daddr))
+		return -EINVAL;
+
+	if (flags&RTF_BROADCAST && (dev_out->flags&IFF_LOOPBACK ||
+	    !(dev_out->flags&IFF_BROADCAST)))
+		flags &= ~RTF_LOCAL;
+	else if (flags&RTF_MULTICAST) {
+		if (ip_check_mc(dev_out, daddr))
+			flags |= RTF_LOCAL;
+	}
+	
+	rth = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
+	if (!rth)
+		return -ENOBUFS;
+
+	rth->u.dst.use	= 1;
+	rth->key.dst	= dst_key;
+	rth->key.tos	= tos;
+	rth->key.src	= src_key;
+	rth->key.src_dev= NULL;
+	rth->key.dst_dev= dst_dev_key;
+	rth->rt_dst	= daddr;
+	rth->rt_dst_map	= dst_map;
+	rth->rt_src	= saddr;
+	rth->rt_src_map	= saddr;
+	rth->rt_src_dev = dev_out;
+	rth->u.dst.dev	= dev_out;
+	rth->rt_gateway = dst_map;
+	rth->rt_spec_dst= dev_out->pa_addr;
+
+	rth->u.dst.output=ip_output;
+
+	if (flags&RTF_LOCAL) {
+		rth->u.dst.input = ip_local_deliver;
+		rth->rt_spec_dst = daddr;
+	}
+	if (flags&(RTF_BROADCAST|RTF_MULTICAST)) {
+		rth->rt_spec_dst = dev_out->pa_addr;
+		flags &= ~RTF_GATEWAY;
+		if (flags&RTF_LOCAL)
+			rth->u.dst.output = ip_mc_output;
+		if (flags&RTF_MULTICAST) {
+			if (dev_out->flags&IFF_ALLMULTI)
+				rth->u.dst.output = ip_mc_output;
+#ifdef CONFIG_IP_MROUTE
+			if (ipv4_config.multicast_route && !LOCAL_MCAST(daddr))
+				rth->u.dst.input = ip_mr_input;
+#endif
+		}
+	}
 
-	rt_add(flags, daddr, mask, gw, dev, r->rt_mss, r->rt_window, r->rt_irtt, metric);
+	if (fi) {
+		if (flags&RTF_GATEWAY)
+			rth->rt_gateway = fi->fib_gateway;
+		rth->u.dst.pmtu	= fi->fib_mtu;
+		rth->u.dst.window=fi->fib_window;
+		rth->u.dst.rtt	= fi->fib_irtt;
+	} else {
+		rth->u.dst.pmtu	= dev_out->mtu;
+		rth->u.dst.window=0;
+		rth->u.dst.rtt	= TCP_TIMEOUT_INIT;
+	}
+	rth->rt_flags = flags;
+	hash = rt_hash_code(dst_key, src_key, tos);
+	if (dst_dev_key)
+		hash ^= dev_hash_name(dst_dev_key->name);
+	*rp = rt_intern_hash(hash, rth, ETH_P_IP);
 	return 0;
 }
 
+int ip_route_output(struct rtable **rp, u32 daddr, u32 saddr, u8 tos, struct device *dev_out)
+{
+	unsigned hash;
+	struct rtable *rth;
 
-/*
- *	Remove a route, as requested by the user.
- */
+	hash = rt_hash_code(daddr, saddr, tos);
+	if (dev_out)
+		hash ^= dev_out->hash;
 
-int ip_rt_kill(struct rtentry *r)
-{
-	struct sockaddr_in *trg;
-	struct sockaddr_in *msk;
-	struct sockaddr_in *gtw;
-	char *devname;
-	int err;
-	struct device * dev = NULL;
-
-	trg = (struct sockaddr_in *) &r->rt_dst;
-	msk = (struct sockaddr_in *) &r->rt_genmask;
-	gtw = (struct sockaddr_in *) &r->rt_gateway;
-	if ((devname = r->rt_dev) != NULL) 
-	{
-		err = getname(devname, &devname);
-		if (err)
-			return err;
-		dev = dev_get(devname);
-		putname(devname);
-		if (!dev)
-			return -ENODEV;
+	start_bh_atomic();
+	for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+		if (rth->key.dst == daddr &&
+		    rth->key.src == saddr &&
+		    rth->key.src_dev == NULL &&
+		    rth->key.dst_dev == dev_out &&
+		    rth->key.tos == tos) {
+			rth->u.dst.lastuse = jiffies;
+			atomic_inc(&rth->u.dst.use);
+			atomic_inc(&rth->u.dst.refcnt);
+			end_bh_atomic();
+			*rp = rth;
+			return 0;
+		}
 	}
-	/*
-	 * metric can become negative here if it wasn't filled in
-	 * but that's a fortunate accident; we really use that in rt_del.
-	 */
-	err=rt_del((__u32)trg->sin_addr.s_addr, (__u32)msk->sin_addr.s_addr, dev,
-		(__u32)gtw->sin_addr.s_addr, r->rt_flags, r->rt_metric - 1);
-	return err;
+	end_bh_atomic();
+
+	return ip_route_output_slow(rp, daddr, saddr, tos, dev_out);
 }
 
-/*
- *	Handle IP routing ioctl calls. These are used to manipulate the routing tables
- */
- 
-int ip_rt_ioctl(unsigned int cmd, void *arg)
+int ip_route_output_dev(struct rtable **rp, u32 daddr, u32 saddr, u8 tos, char *devname)
 {
-	int err;
-	struct rtentry rt;
+	unsigned hash;
+	struct rtable *rth;
+	struct device *dev_out;
+
+	hash = rt_hash_code(daddr, saddr, tos)^dev_hash_mc_name(devname);
 
-	switch(cmd) 
-	{
-		case SIOCADDRT:		/* Add a route */
-		case SIOCDELRT:		/* Delete a route */
-			if (!suser())
-				return -EPERM;
-			err = copy_from_user(&rt, arg, sizeof(struct rtentry));
-			if (err)
-				return -EFAULT; 
-			return (cmd == SIOCDELRT) ? ip_rt_kill(&rt) : ip_rt_new(&rt);
+	start_bh_atomic();
+	for (rth=rt_hash_table[hash]; rth; rth=rth->u.rt_next) {
+		if (rth->key.dst == daddr &&
+		    rth->key.src == saddr &&
+		    rth->key.src_dev == NULL &&
+		    rth->key.tos == tos &&
+		    rth->key.dst_dev &&
+		    strcmp(rth->key.dst_dev->name, devname)==0) {
+			rth->u.dst.lastuse = jiffies;
+			atomic_inc(&rth->u.dst.use);
+			atomic_inc(&rth->u.dst.refcnt);
+			end_bh_atomic();
+			*rp = rth;
+			return 0;
+		}
 	}
+	end_bh_atomic();
 
-	return -EINVAL;
+	dev_out = dev_get(devname);
+	if (!dev_out)
+		return -ENODEV;
+	return ip_route_output_slow(rp, daddr, saddr, tos, dev_out);
 }
 
-void ip_rt_advice(struct rtable **rp, int advice)
+void ip_rt_multicast_event(struct device *dev)
 {
-	/* Thanks! */
-	return;
+	rt_cache_flush(0);
 }
 
-void ip_rt_update(int event, struct device *dev)
+void ip_rt_init()
 {
-/*
- *	This causes too much grief to do now.
- */
-#ifdef COMING_IN_2_1
-	if (event == NETDEV_UP)
-		rt_add(RTF_HOST|RTF_UP, dev->pa_addr, ~0, 0, dev, 0, 0, 0, 0);
-	else if (event == NETDEV_DOWN)
-		rt_del(dev->pa_addr, ~0, dev, 0, RTF_HOST|RTF_UP, 0);
-#endif		
+	ip_fib_init();
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&(struct proc_dir_entry) {
+		PROC_NET_RTCACHE, 8, "rt_cache",
+		S_IFREG | S_IRUGO, 1, 0, 0,
+		0, &proc_net_inode_operations,
+		rt_cache_get_info
+	});
+#endif
 }
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.14/linux/net/ipv4/sysctl_net_ipv4.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/sysctl_net_ipv4.c	Thu Dec 12 16:54:24 1996
@@ -1,4 +1,4 @@
-/* -*- linux-c -*-
+/*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
  * Begun April 1, 1996, Mike Shaver.
@@ -7,6 +7,9 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <net/snmp.h>
+#include <net/ip.h>
+#include <net/route.h>
 #include <net/tcp.h>
 
 /*
@@ -35,9 +38,31 @@
 extern int tcp_sysctl_congavoid(ctl_table *ctl, int write, struct file * filp,
 				void *buffer, size_t *lenp);
 
+struct ipv4_config ipv4_config = { 1, 1, 1, 1, };
+struct ipv4_config ipv4_def_router_config = { 0, 1, 1, 1, 1, 1, 1, };
+struct ipv4_config ipv4_def_host_config = { 1, 1, 1, 1, };
+
+int ipv4_sysctl_forwarding(ctl_table *ctl, int write, struct file * filp,
+			   void *buffer, size_t *lenp)
+{
+	int val = IS_ROUTER;
+	int ret;
+
+	ret = proc_dointvec(ctl, write, filp, buffer, lenp);
+
+	if (write && IS_ROUTER != val) {
+		if (IS_ROUTER)
+			ipv4_config = ipv4_def_router_config;
+		else
+			ipv4_config = ipv4_def_host_config;
+		rt_cache_flush(0);
+        }
+        return ret;
+}
+
 ctl_table ipv4_table[] = {
         {NET_IPV4_ARP_RES_TIME, "arp_res_time",
-         &sysctl_arp_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
+	 &sysctl_arp_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
         {NET_IPV4_ARP_DEAD_RES_TIME, "arp_dead_res_time",
          &sysctl_arp_dead_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
         {NET_IPV4_ARP_MAX_TRIES, "arp_max_tries",
@@ -52,15 +77,47 @@
         {NET_IPV4_ARP_CONFIRM_TIMEOUT, "arp_confirm_timeout",
          &sysctl_arp_confirm_timeout, sizeof(int), 0644, NULL,
          &proc_dointvec},
-#if 0
-	{TCP_PMTU_DISC, "tcp_pmtu_discovery",
-	&ipv4_pmtu_discovery, sizeof(int), 644, 
-	NULL, &proc_dointvec, &sysctl_intvec_minmax, 
-	&boolean_min, &boolean_max},
-#endif
-
 	{NET_IPV4_TCP_VEGAS_CONG_AVOID, "tcp_vegas_cong_avoid",
 	 &sysctl_tcp_cong_avoidance, sizeof(int), 0644,
 	 NULL, &tcp_sysctl_congavoid },
+        {NET_IPV4_FORWARDING, "ip_forwarding",
+         &ip_statistics.IpForwarding, sizeof(int), 0644, NULL,
+         &ipv4_sysctl_forwarding},
+        {NET_IPV4_DEFAULT_TTL, "ip_default_ttl",
+         &ip_statistics.IpDefaultTTL, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_RFC1812_FILTER, "ip_rfc1812_filter",
+         &ipv4_config.rfc1812_filter, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_LOG_MARTIANS, "ip_log_martians",
+         &ipv4_config.log_martians, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_SOURCE_ROUTE, "ip_source_route",
+         &ipv4_config.source_route, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_ADDRMASK_AGENT, "ip_addrmask_agent",
+         &ipv4_config.addrmask_agent, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_BOOTP_AGENT, "ip_bootp_agent",
+         &ipv4_config.bootp_agent, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_BOOTP_RELAY, "ip_bootp_relay",
+         &ipv4_config.bootp_relay, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_FIB_MODEL, "ip_fib_model",
+         &ipv4_config.fib_model, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_NO_PMTU_DISC, "ip_no_pmtu_disc",
+         &ipv4_config.no_pmtu_disc, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_ACCEPT_REDIRECTS, "ip_accept_redirects",
+         &ipv4_config.accept_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_SECURE_REDIRECTS, "ip_secure_redirects",
+         &ipv4_config.secure_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
+        {NET_IPV4_RFC1620_REDIRECTS, "ip_rfc1620_redirects",
+         &ipv4_config.rfc1620_redirects, sizeof(int), 0644, NULL,
+         &proc_dointvec},
 	{0}
 };
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.14/linux/net/ipv4/tcp.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/tcp.c	Thu Dec 12 16:54:24 1996
@@ -204,7 +204,7 @@
  *
  *		Rewrite output state machine to use a single queue.
  *		Speed up input assembly algorithm.
- *		RFC1323 - PAWS and window scaling. 
+ *		RFC1323 - PAWS and window scaling.[Required for IPv6]
  *		User settable/learned rtt/max window/mtu
  *
  *		Change the fundamental structure to a single send queue maintained
@@ -604,8 +604,9 @@
  *	take care of normal races (between the test and the event) and we don't
  *	go look at any of the socket buffers directly.
  */
-int tcp_select(struct sock *sk, int sel_type, select_table *wait)
+int tcp_select(struct socket *sock, int sel_type, select_table *wait)
 {
+	struct sock *sk = sock->sk;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 	if (sk->state == TCP_LISTEN)
@@ -802,7 +803,7 @@
  */
 
 int tcp_do_sendmsg(struct sock *sk, int iovlen, struct iovec *iov,
-		   int len, int nonblock, int flags)
+		   int len, int flags)
 {
 	int copied  = 0;
 	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
@@ -826,7 +827,7 @@
 			return -EPIPE;
 		}
 		
-		if (nonblock)
+		if (flags&MSG_DONTWAIT)
 			return -EAGAIN;
 		
 		if (current->signal & ~current->blocked)
@@ -982,7 +983,7 @@
 			if (skb == NULL) 
 			{
 				sk->socket->flags |= SO_NOSPACE;
-				if (nonblock) 
+				if (flags&MSG_DONTWAIT)
 				{
 					if (copied) 
 						return copied;
@@ -1000,10 +1001,6 @@
 				continue;
 			}
 
-			skb->sk = sk;
-			skb->free = 0;
-			skb->localroute = sk->localroute|(flags&MSG_DONTROUTE);
-	
 			/*
 			 * FIXME: we need to optimize this.
 			 * Perhaps some hints here would be good.
@@ -1013,7 +1010,7 @@
 
 			if (tmp < 0)
 			{
-				sock_wfree(sk, skb);
+				kfree_skb(skb, FREE_WRITE);
 				if (copied)
 					return(copied);
 				return(tmp);
@@ -1027,7 +1024,7 @@
 
                         if (tmp < 0) 
                         {
-                                sock_wfree(sk, skb);
+                                kfree_skb(skb, FREE_WRITE);
                                 if (copied) 
                                         return(copied);
                                 return(tmp);
@@ -1045,7 +1042,6 @@
 			from += copy;
 			copied += copy;
 			len -= copy;
-			skb->free = 0;
 			sk->write_seq += copy;
 		
 			tcp_send_skb(sk, skb);
@@ -1167,7 +1163,6 @@
 {
 	sk->ack_backlog++;
 
-	skb->sk = sk;
 	__skb_unlink(skb, &sk->receive_queue);
 	kfree_skb(skb, FREE_READ);
 }
@@ -1184,7 +1179,7 @@
 	 */
 
 	while ((skb=skb_peek(&sk->receive_queue)) != NULL) {
-		if (!skb->used || skb->users)
+		if (!skb->used || skb->users>1)
 			break;
 		tcp_eat_skb(sk, skb);
 	}
@@ -1275,6 +1270,8 @@
 			if (copied)
 				break;
 			copied = -ERESTARTSYS;
+			if (nonblock)
+				copied = -EAGAIN;
 			break;
 		}
 
@@ -1359,7 +1356,7 @@
 		 *	tcp_data.
 		 */
 
-		skb->users++;
+		atomic_inc(&skb->users);
 
 		/*
 		 *	Ok so how much can we use ?
@@ -1412,7 +1409,7 @@
 			 *	exception. bailout!
 			 */
 			*seq -= err;
-			skb->users--;
+			atomic_dec(&skb->users);
 			return -EFAULT;
 		}
 
@@ -1425,7 +1422,7 @@
 		 *	but you'll just have to fix it neatly ;)
 		 */
 
-		skb->users--;
+		atomic_dec(&skb->users);
 
 		if (after(sk->copied_seq,sk->urg_seq))
 			sk->urg_data = 0;
@@ -1442,7 +1439,7 @@
 		if (flags & MSG_PEEK)
 			continue;
 		skb->used = 1;
-		if (!skb->users)
+		if (skb->users == 1)
 			tcp_eat_skb(sk, skb);
 		continue;
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.14/linux/net/ipv4/tcp_input.c	Tue Nov 19 15:54:01 1996
+++ linux/net/ipv4/tcp_input.c	Thu Dec 12 16:54:25 1996
@@ -563,7 +563,6 @@
 		*seq_rtt = now - skb->when;
 				
 		skb_unlink(skb);		
-		skb->free = 1;
 		
 		kfree_skb(skb, FREE_WRITE);
 	}
@@ -1142,7 +1141,8 @@
 			 */
 
 			tcp_write_xmit(sk);
-			wake_up_interruptible(sk->sleep);
+			if(!sk->dead)
+				sk->write_space(sk);
 		}
 		else if (sk->packets_out == 0 && !tp->pending)
  		{
@@ -1278,8 +1278,8 @@
 }
 
 
-void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
-			 struct tcphdr *th, __u16 len)
+int tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
+			struct tcphdr *th, __u16 len)
 {
 	struct tcp_opt *tp;
 	int queued = 0;
@@ -1323,7 +1323,7 @@
 			tcp_data_snd_check(sk);
 
 			kfree_skb(skb, FREE_READ);
-			return;
+			return 0;
 			
 		}
 		else if (skb->ack_seq == tp->snd_una)
@@ -1348,7 +1348,7 @@
 			else
 				tcp_send_ack(sk);
 
-			return;
+			return 0;
 		}
 	}
 
@@ -1365,7 +1365,7 @@
 			}
 			tcp_send_ack(sk);
 			kfree_skb(skb, FREE_READ);
-			return;
+			return 0;
 		}
 	}
 
@@ -1374,14 +1374,14 @@
 		printk(KERN_DEBUG "syn in established state\n");
 		tcp_reset(sk, skb);
 		kfree_skb(skb, FREE_READ);
-		return;
+		return 1;
 	}
 	
 	if(th->rst)
 	{
 		tcp_reset(sk,skb);
 		kfree_skb(skb, FREE_READ);
-		return;
+		return 0;
 	}
 	
 	if(th->ack)
@@ -1426,10 +1426,9 @@
 	 *	And done
 	 */	
 	
-	if (queued)
-		return;
-
-	kfree_skb(skb, FREE_READ);
+	if (!queued)
+		kfree_skb(skb, FREE_READ);
+	return 0;
 }
 		
 
@@ -1559,12 +1558,8 @@
 			tcp_set_state(sk, TCP_ESTABLISHED);
 			rcv_mss = tcp_parse_options(th);
 			
-			if (rcv_mss == 0)
-			{
-				rcv_mss = 536;
-			}
-
-			sk->mss = min(sk->mss, rcv_mss);
+			if (rcv_mss)
+				sk->mss = min(sk->mss, rcv_mss);
 			
 			sk->dummy_th.dest = th->source;
 			sk->copied_seq = tp->rcv_nxt;
@@ -1625,8 +1620,7 @@
 			__u32 isn;
 			int err;
 
-                        atomic_sub(skb->truesize, &sk->rmem_alloc);
-                        skb->sk = NULL;
+			skb_orphan(skb);
                         sk->err = ECONNRESET;
                         tcp_set_state(sk, TCP_CLOSE);
                         sk->shutdown = SHUTDOWN_MASK;
@@ -1638,9 +1632,8 @@
 			if (sk == NULL)
 				goto discard;
 
-			skb->sk = sk;
+			skb_set_owner_r(skb, sk);
 			tp = &sk->tp_pinfo.af_tcp;
-			atomic_add(skb->truesize, &sk->rmem_alloc);
 			
 			err = tp->af_specific->conn_request(sk, skb, opt, isn);
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.14/linux/net/ipv4/tcp_ipv4.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipv4/tcp_ipv4.c	Thu Dec 12 16:54:25 1996
@@ -33,10 +33,7 @@
 
 #include <asm/segment.h>
 
-static void tcp_v4_send_reset(unsigned long saddr, unsigned long daddr, 
-			      struct tcphdr *th, struct proto *prot, 
-			      struct options *opt,
-			      struct device *dev, int tos, int ttl);
+static void tcp_v4_send_reset(struct sk_buff *skb);
 
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
 		       struct sk_buff *skb);
@@ -59,17 +56,15 @@
  *	The cache is not quite right...
  */
 
-static inline struct sock * get_tcp_sock(u32 saddr, u16 sport, 
-					 u32 daddr, u16 dport,
-					 u32 paddr, u16 pport)
+static inline struct sock * get_tcp_sock(u32 saddr, u16 sport,
+					 u32 daddr, u16 dport)
 {
 	struct sock * sk;
 
 	sk = (struct sock *) th_cache_sk;
 	if (!sk || saddr != th_cache_saddr || daddr != th_cache_daddr ||
 	    sport != th_cache_sport || dport != th_cache_dport) {
-		sk = get_sock(&tcp_prot, dport, saddr, sport, daddr, 
-			      paddr, pport);
+		sk = get_sock(&tcp_prot, dport, saddr, sport, daddr);
 		if (sk) {
 			th_cache_saddr=saddr;
 			th_cache_daddr=daddr;
@@ -131,10 +126,8 @@
 {
 	struct sk_buff *buff;
 	struct sk_buff *skb1;
-	struct device *dev=NULL;
 	unsigned char *ptr;
 	int tmp;
-	int atype;
 	struct tcphdr *t1;
 	struct rtable *rt;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
@@ -147,40 +140,49 @@
 	 *	Don't allow a double connect.
 	 */
 	 	
-	if(sk->daddr)
+	if (sk->daddr)
 		return -EINVAL;
 	
 	if (addr_len < sizeof(struct sockaddr_in)) 
 		return(-EINVAL);
 
-	if (usin->sin_family && usin->sin_family != AF_INET) 
-		return(-EAFNOSUPPORT);
+	if (usin->sin_family != AF_INET) {
+		static int complained;
+		if (usin->sin_family)
+			return(-EAFNOSUPPORT);
+		if (!complained++)
+			printk(KERN_DEBUG "%s forgot to set AF_INET in " __FUNCTION__ "\n", current->comm);
+	}
 
-  	/*
-  	 *	connect() to INADDR_ANY means loopback (BSD'ism).
-  	 */
-  	
-  	if (usin->sin_addr.s_addr==INADDR_ANY)
-		usin->sin_addr.s_addr=ip_my_addr();
+	if (sk->dst_cache) {
+		dst_release(sk->dst_cache);
+		sk->dst_cache = NULL;
+	}
 		  
-	/*
-	 *	Don't want a TCP connection going to a broadcast address 
-	 */
+	tmp = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
+			       RT_TOS(sk->ip_tos)|(sk->localroute || 0));
+	if (tmp < 0)
+		return tmp;
 
-	if ((atype=ip_chk_addr(usin->sin_addr.s_addr)) == IS_BROADCAST 
-	    || atype==IS_MULTICAST)
-	{ 
+	if (rt->rt_flags&(RTF_MULTICAST|RTF_BROADCAST)) {
+		ip_rt_put(rt);
 		return -ENETUNREACH;
 	}
 
-	if (!tcp_unique_address(sk->saddr, sk->num, usin->sin_addr.s_addr,
+	if (!tcp_unique_address(rt->rt_src, sk->num, rt->rt_dst,
 				usin->sin_port))
-	{
 		return -EADDRNOTAVAIL;
-	}
   
 	lock_sock(sk);
-	sk->daddr = usin->sin_addr.s_addr;
+	sk->dst_cache = &rt->u.dst;
+	sk->daddr = rt->rt_dst;
+	if (!sk->saddr)
+		sk->saddr = rt->rt_src;
+	sk->rcv_saddr = sk->saddr;
+
+	if (sk->priority == SOPRI_NORMAL)
+		sk->priority = rt->u.dst.priority;
+
 	sk->dummy_th.dest = usin->sin_port;
 	sk->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
 						   sk->dummy_th.source,
@@ -195,34 +197,25 @@
 
 	sk->err = 0;
 	
-	buff = sock_wmalloc(sk,MAX_SYN_SIZE,0, GFP_KERNEL);
+	buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);
 	if (buff == NULL) 
 	{
 		release_sock(sk);
 		return(-ENOBUFS);
 	}
 
-	buff->sk = sk;
-	buff->free = 0;
-	buff->localroute = sk->localroute;
-	
 	/*
 	 *	Put in the IP header and routing stuff.
 	 */
 	
-	tmp = ip_build_header(buff, sk->saddr, sk->daddr, &dev,
-			      IPPROTO_TCP, NULL, MAX_SYN_SIZE, sk->ip_tos, 
-			      sk->ip_ttl,&sk->ip_route_cache);
+	tmp = ip_build_header(buff, sk);
 
 	if (tmp < 0) 
 	{
-		sock_wfree(sk, buff);
+		kfree_skb(buff, FREE_WRITE);
 		release_sock(sk);
 		return(-ENETUNREACH);
 	}
-	if ((rt = sk->ip_route_cache) != NULL && !sk->saddr)
-		sk->saddr = rt->rt_src;
-	sk->rcv_saddr = sk->saddr;
 
 	t1 = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
 	buff->h.th = t1;
@@ -239,17 +232,15 @@
 
 	/* use 512 or whatever user asked for */
 
-	if(rt!=NULL && (rt->rt_flags&RTF_WINDOW))
-		sk->window_clamp=rt->rt_window;
-	else
-		sk->window_clamp=0;
+	sk->window_clamp=rt->u.dst.window;
 
+	sk->mtu = rt->u.dst.pmtu;
+	if ((sk->ip_pmtudisc == IP_PMTUDISC_DONT ||
+	     (sk->ip_pmtudisc == IP_PMTUDISC_WANT &&
+	      rt->rt_flags&RTF_NOPMTUDISC)) &&
+	    rt->u.dst.pmtu > 576)
+		sk->mtu = 576;
 
-	if (rt)
-		sk->mtu = rt->rt_mtu;
-	else
-		sk->mtu = dev->mtu;
-	
 	if(sk->mtu < 64)
 		sk->mtu = 64;	/* Sanity limit */
 
@@ -279,10 +270,7 @@
 	tcp_cache_zap();
 	tcp_set_state(sk,TCP_SYN_SENT);
 
-	if(rt && (rt->rt_flags&RTF_IRTT))
-		tp->rto = rt->rt_irtt;
-	else
-		tp->rto = TCP_TIMEOUT_INIT;
+	tp->rto = rt->u.dst.rtt;
 
 	tcp_init_xmit_timers(sk);
 	
@@ -295,8 +283,7 @@
 	buff->when = jiffies;
 
 	skb1 = skb_clone(buff, GFP_KERNEL);
-	sk->wmem_alloc += skb1->truesize;
-	ip_queue_xmit(sk, dev, skb1, 1);  
+	ip_queue_xmit(skb1);  
 
 	/* Timer for repeating the SYN until an answer  */
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
@@ -307,8 +294,7 @@
 	return(0);
 }
 
-static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg,
-			  int len, int nonblock, int flags)
+static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
 	int retval = -EINVAL;
 
@@ -316,7 +302,7 @@
 	 *	Do sanity checking for sendmsg/sendto/send
 	 */
 
-	if (flags & ~(MSG_OOB|MSG_DONTROUTE))
+	if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT))
 		goto out;
 	if (msg->msg_name) {
 		struct sockaddr_in *addr=(struct sockaddr_in *)msg->msg_name;
@@ -336,8 +322,8 @@
 	}
 
 	lock_sock(sk);
-	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov, 
-				len, nonblock, flags);
+	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
+				len, msg->msg_flags);
 
 	release_sock(sk);
 
@@ -354,18 +340,16 @@
  * to find the appropriate port.
  */
 
-void tcp_v4_err(int type, int code, unsigned char *header, __u32 info,
-		__u32 daddr, __u32 saddr, struct inet_protocol *protocol, int len)
+void tcp_v4_err(struct sk_buff *skb, unsigned char *dp)
 {
-	struct tcphdr *th = (struct tcphdr *)header;
+	struct iphdr *iph = (struct iphdr*)dp;
+	struct tcphdr *th = (struct tcphdr*)(dp+(iph->ihl<<2));
+	int type = skb->h.icmph->type;
+	int code = skb->h.icmph->code;
 	struct tcp_opt *tp;
 	struct sock *sk;
 
-	if(len<8)	/* We use the first 8 bytes only */
-		return;
-		
-	th =(struct tcphdr *)header;
-	sk = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr, 0, 0);
+	sk = get_sock(&tcp_prot, th->source, iph->daddr, th->dest, iph->saddr);
 
 	if (sk == NULL)
 		return;
@@ -394,27 +378,15 @@
 		sk->error_report(sk);
 	}
 
-#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
-	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
-	{
-		struct rtable * rt;
-
-		unsigned short new_mtu = info;
-
-		if ((rt = sk->ip_route_cache) != NULL)
-			if (rt->rt_mtu > new_mtu)
-				rt->rt_mtu = new_mtu;
-
-		if ((sk->mtu > new_mtu) &&
-		    (new_mtu > sizeof(struct iphdr)+sizeof(struct tcphdr)))
-		{
-			sk->mss = (new_mtu - sizeof(struct iphdr) 
-				   - sizeof(struct tcphdr));
+	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
+		if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
+			int new_mtu = sk->dst_cache->pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr);
+			if (new_mtu < sk->mss && new_mtu > 0) {
+				sk->mss = new_mtu;
+			}
 		}
-
 		return;
 	}
-#endif
 
 	/*
 	 * If we've already connected we will keep trying
@@ -474,83 +446,57 @@
 }
 
 /*
- *	This routine will send an RST to the other tcp. 
+ *	This routine will send an RST to the other tcp.
+ *
+ *	Someone asks: why I NEVER use socket parameters (TOS, TTL etc.)
+ *		      for reset.
+ *	Answer: if a packet caused RST, it is not for a socket
+ *		existing in our system, if it is matched to a socket,
+ *		it is just duplicate segment or bug in other side's TCP.
+ *		So that we build reply only basing on parameters
+ *		arrived with segment.
+ *	Exception: precedence violation. We do not implement it in any case.
  */
  
-static void tcp_v4_send_reset(unsigned long saddr, unsigned long daddr, 
-			      struct tcphdr *th, struct proto *prot, 
-			      struct options *opt,
-			      struct device *dev, int tos, int ttl)
+static void tcp_v4_send_reset(struct sk_buff *skb)
 {
-	struct sk_buff *buff;
-	struct tcphdr *t1;
-	int tmp;
-	struct device *ndev=NULL;
+	struct tcphdr  *th = skb->h.th;
+	struct sk_buff *skb1;
+	struct tcphdr  *th1;
 
-	/*
-	 *	Cannot reset a reset (Think about it).
-	 */
-	 
-	if(th->rst)
+	if (th->rst)
 		return;
   
-	/*
-	 * We need to grab some memory, and put together an RST,
-	 * and then put it into the queue to be sent.
-	 */
-
-	buff = alloc_skb(MAX_RESET_SIZE, GFP_ATOMIC);
-	if (buff == NULL) 
-	  	return;
-
-	buff->sk = NULL;
-	buff->dev = dev;
-	buff->localroute = 0;
-
-
-	/*
-	 *	Put in the IP header and routing stuff. 
-	 */
-
-	tmp = ip_build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
-			      sizeof(struct tcphdr),tos,ttl,NULL);
-	if (tmp < 0) 
-	{
-  		buff->free = 1;
-		sock_wfree(NULL, buff);
+	skb1 = ip_reply(skb, sizeof(struct tcphdr));
+	if (skb1 == NULL)
 		return;
-	}
-
-	t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
-	memset(t1, 0, sizeof(*t1));
+ 
+	skb1->h.th = th1 = (struct tcphdr *)skb_put(skb1, sizeof(struct tcphdr));
+	memset(th1, 0, sizeof(*th1));
 
 	/*
 	 *	Swap the send and the receive. 
 	 */
-
-	t1->dest = th->source;
-	t1->source = th->dest;
-	t1->doff = sizeof(*t1)/4;
-	t1->rst = 1;
-  
-	if(th->ack)
-	{
-	  	t1->seq = th->ack_seq;
-	}
-	else
-	{
-		t1->ack = 1;
-	  	if(!th->syn)
-			t1->ack_seq = th->seq;
+ 
+	th1->dest = th->source;
+	th1->source = th->dest;
+	th1->doff = sizeof(*th1)/4;
+	th1->rst = 1;
+   
+	if (th->ack)
+	  	th1->seq = th->ack_seq;
+	else {
+		th1->ack = 1;
+	  	if (!th->syn)
+			th1->ack_seq = th->seq;
 		else
-			t1->ack_seq = htonl(ntohl(th->seq)+1);
+			th1->ack_seq = htonl(ntohl(th->seq)+1);
 	}
-
-
-	buff->csum = csum_partial((u8 *) t1, sizeof(*t1), 0);
-	t1->check = tcp_v4_check(t1, sizeof(*t1), saddr, daddr, buff->csum);
-
-	ip_queue_xmit(NULL, ndev, buff, 1);
+ 
+	skb1->csum = csum_partial((u8 *) th1, sizeof(*th1), 0);
+	th1->check = tcp_v4_check(th1, sizeof(*th1), skb1->nh.iph->saddr,
+				  skb1->nh.iph->daddr, skb1->csum);
+	ip_queue_xmit(skb1);
 	tcp_statistics.TcpOutSegs++;
 }
 
@@ -562,12 +508,11 @@
 
 int tcp_chkaddr(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->h.iph;
-	struct tcphdr *th = (struct tcphdr *)(skb->h.raw + iph->ihl*4);
+	struct iphdr *iph = skb->nh.iph;
+	struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = get_sock(&tcp_prot, th->dest, iph->saddr, th->source, iph->daddr,
-		      0, 0);
+	sk = get_sock(&tcp_prot, th->dest, iph->saddr, th->source, iph->daddr);
 
 	if (!sk)
 		return 0;
@@ -586,8 +531,6 @@
 	struct tcp_v4_open_req *af_req = (struct tcp_v4_open_req *) req;
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 	struct sk_buff * skb;
-	struct device *dev = NULL;
-	struct rtable *rt = NULL;
 	struct tcphdr *th;
 	unsigned char *ptr;
 	int mss;
@@ -600,31 +543,22 @@
 		return;
 	}
 
-	tmp = ip_build_header(skb, af_req->loc_addr, af_req->rmt_addr, &dev,
-			      IPPROTO_TCP, af_req->opt, skb->truesize, 
-			      sk->ip_tos, sk->ip_ttl, &rt);
+	tmp = ip_build_pkt(skb, sk, af_req->loc_addr, af_req->rmt_addr,
+			   af_req->opt);
 
 	if (tmp < 0)
 	{
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 		return;
 	}
 
-	skb->dev = dev;
+	mss = skb->dst->pmtu;
 
-	if (rt)
-		mss = rt->rt_mtu;
-	else
-		mss = dev->mtu;
-	
 	mss -= sizeof(struct iphdr) + sizeof(struct tcphdr);
 	
 	if (sk->user_mss)
 		mss = min(mss, sk->user_mss);
 	
-	ip_rt_put(rt);
-	
 	th =(struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
 	skb->h.th = th;
 	memset(th, 0, sizeof(struct tcphdr));
@@ -651,13 +585,15 @@
 	ptr[3] = mss & 0xff;
 	skb->csum = csum_partial(ptr, TCPOLEN_MSS, 0);
 
-	th->check = tcp_v4_check(th, sizeof(*th) + TCPOLEN_MSS, af_req->loc_addr, 
+	th->check = tcp_v4_check(th, sizeof(*th) + TCPOLEN_MSS,
+				 af_req->loc_addr, 
 				 af_req->rmt_addr,
 				 csum_partial((char *)th, sizeof(*th), skb->csum));
 
-	ip_queue_xmit(sk, dev, skb, 1);
+	ip_queue_xmit(skb);
 	tcp_statistics.TcpOutSegs++;
 					      
+
 }
 
 static void tcp_v4_or_free(struct open_request *req)
@@ -668,9 +604,7 @@
 		return;
 	
 	if (af_req->opt)
-	{
 		kfree_s(af_req->opt, sizeof(struct options) + af_req->opt->optlen);
-	}
 }
 
 static struct or_calltable or_ipv4 = {
@@ -685,12 +619,12 @@
 
 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb, void *ptr, __u32 isn)
 {
-	struct options *opt = (struct options *) ptr;
+	struct ip_options *opt = (struct ip_options *) ptr;
 	struct tcp_v4_open_req *af_req;
 	struct open_request *req;
 	struct tcphdr *th = skb->h.th;
-	__u32 saddr = skb->saddr;
-	__u32 daddr = skb->daddr;
+	__u32 saddr = skb->nh.iph->saddr;
+	__u32 daddr = skb->nh.iph->daddr;
 
 	/* If the socket is dead, don't accept the connection.	*/
 	if (sk->dead)
@@ -751,12 +685,11 @@
 
 	if (opt && opt->optlen)
 	{
-		af_req->opt = (struct options*) kmalloc(sizeof(struct options) +
-							opt->optlen, GFP_ATOMIC);
+		af_req->opt = (struct ip_options*) kmalloc(sizeof(struct ip_options) +
+							   opt->optlen, GFP_ATOMIC);
 		if (af_req->opt) 
 		{
-			if (ip_options_echo(af_req->opt, opt, skb->daddr, 
-					    skb->saddr, skb))
+			if (ip_options_echo(af_req->opt, skb))
 			{
 				kfree_s(af_req->opt, sizeof(struct options) + 
 					opt->optlen);
@@ -771,10 +704,9 @@
 	
 	req->expires = jiffies + TCP_TIMEOUT_INIT;
 	tcp_inc_slow_timer(TCP_SLT_SYNACK);
-	tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);	
+	tcp_synq_queue(&sk->tp_pinfo.af_tcp, req);
 
 	sk->data_ready(sk, 0);
-
   exit:
 	kfree_skb(skb, FREE_READ);
 	return 0;
@@ -797,10 +729,11 @@
 
 	memcpy(newsk, sk, sizeof(*newsk));
 	newsk->opt = NULL;
-	newsk->ip_route_cache  = NULL;
+	newsk->dst_cache  = NULL;
 	skb_queue_head_init(&newsk->write_queue);
 	skb_queue_head_init(&newsk->receive_queue);
 	skb_queue_head_init(&newsk->out_of_order_queue);
+	skb_queue_head_init(&newsk->error_queue);
 	
 	/*
 	 *	Unused
@@ -866,6 +799,7 @@
 
 	newsk->dummy_th.source = sk->dummy_th.source;
 	newsk->dummy_th.dest = req->rmt_port;
+	newsk->users=0;
 	
 	newtp->rcv_nxt = req->rcv_isn + 1;
 	newtp->rcv_wup = req->rcv_isn + 1;
@@ -881,20 +815,18 @@
 	 *	options / mss / route_cache
 	 */
 	newsk->opt = af_req->opt;
-	rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : 
-			 newsk->daddr, 0);
 
-	newsk->ip_route_cache = rt;
-	
-	if(rt != NULL && (rt->rt_flags&RTF_WINDOW))
-		newsk->window_clamp = rt->rt_window;
-	else
-		newsk->window_clamp = 0;
+	if (ip_route_output(&rt,
+			    newsk->opt && newsk->opt->srr ? newsk->opt->faddr : newsk->daddr,
+			    newsk->saddr, newsk->ip_tos, NULL)) {
+		kfree(newsk);
+		return NULL;
+	}
 
-	if (rt)
-		snd_mss = rt->rt_mtu;
-	else
-		snd_mss = skb->dev->mtu;
+	newsk->dst_cache = &rt->u.dst;
+	
+	newsk->window_clamp = rt->u.dst.window;
+	snd_mss = rt->u.dst.pmtu;
 	
 	newsk->mtu = snd_mss;
 	/* sanity check */
@@ -944,8 +876,8 @@
 
 		af_req = (struct tcp_v4_open_req *) req;
 
-		if (af_req->rmt_addr == skb->saddr &&
-		    af_req->loc_addr == skb->daddr &&
+		if (af_req->rmt_addr == skb->nh.iph->saddr &&
+		    af_req->loc_addr == skb->nh.iph->daddr &&
 		    req->rmt_port == skb->h.th->source)
 		{
 			u32 flg;
@@ -975,7 +907,7 @@
 				return NULL;
 			}
 
-			atomic_sub(skb->truesize, &sk->rmem_alloc);
+			skb_orphan(skb);
 			sk = tp->af_specific->syn_recv_sock(sk, skb, req);
 
 			tcp_dec_slow_timer(TCP_SLT_SYNACK);
@@ -985,10 +917,9 @@
 				return NULL;
 			}
 
-			atomic_add(skb->truesize, &sk->rmem_alloc);
+			skb_set_owner_r(skb, sk);
 			req->expires = 0UL;
 			req->sk = sk;
-			skb->sk = sk;
 			break;
 		}
 
@@ -999,94 +930,14 @@
 	return sk;
 }
 
-/*
- *	From tcp_input.c
- */
-
-int tcp_v4_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
-	       __u32 daddr, unsigned short len,
-	       __u32 saddr, int redo, struct inet_protocol * protocol)
+static int __inline__ tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
 {
-	struct tcphdr *th;	
-	struct sock *sk;
-
-	/*
-	 * "redo" is 1 if we have already seen this skb but couldn't
-	 * use it at that time (the socket was locked).  In that case
-	 * we have already done a lot of the work (looked up the socket
-	 * etc).
-	 */
-
-	th = skb->h.th;
-
-	sk = skb->sk;
-
-	if (!redo)
-	{
-
-		if (skb->pkt_type!=PACKET_HOST)
-			goto discard_it;
-
-		/*
-		 *	Pull up the IP header.
-		 */
-	
-		skb_pull(skb, skb->h.raw-skb->data);
-
-		/*
-		 *	Try to use the device checksum if provided.
-		 */
-		
-		switch (skb->ip_summed) 
-		{
-			case CHECKSUM_NONE:
-				skb->csum = csum_partial((char *)th, len, 0);
-			case CHECKSUM_HW:
-				if (tcp_v4_check(th,len,saddr,daddr,skb->csum))
-					goto discard_it;
-			default:
-				/* CHECKSUM_UNNECESSARY */
-		}
-
-		sk = get_tcp_sock(saddr, th->source, daddr, th->dest,
-				  dev->pa_addr, skb->redirport);
-
-		if (!sk)
-			goto no_tcp_socket;
-
-		skb->sk = sk;
-		skb->seq = ntohl(th->seq);
-		skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
-		skb->ack_seq = ntohl(th->ack_seq);
-		
-		skb->acked = 0;
-		skb->used = 0;
-		skb->free = 1;
-		skb->saddr = saddr;
-		skb->daddr = daddr;		
-	}		
-
-	/*
-	 * We may need to add it to the backlog here. 
-	 */
-
-	if (sk->users)
-	{
-		__skb_queue_tail(&sk->back_log, skb);
-		return(0);
-	}
-
-	if (!sk->prot)
-	{
-		printk(KERN_DEBUG "tcp_rcv: sk->prot == NULL\n");
-		return(0);
-	}
-
-	atomic_add(skb->truesize, &sk->rmem_alloc);
+	skb_set_owner_r(skb, sk);
 
 	if (sk->state == TCP_ESTABLISHED)
 	{
-		tcp_rcv_established(sk, skb, th, len);
+		if (tcp_rcv_established(sk, skb, skb->h.th, skb->len))
+			goto reset;
 		return 0;
 	}
 
@@ -1103,138 +954,149 @@
 		}
 	}
 	
-	if (tcp_rcv_state_process(sk, skb, th, opt, len) == 0)
+	if (tcp_rcv_state_process(sk, skb, skb->h.th, NULL, skb->len) == 0)
 		return 0;
 
-no_tcp_socket:
-
-	/*
-	 *	No such TCB. If th->rst is 0 send a reset 
-	 *	(checked in tcp_send_reset)
-	 */
-
-	tcp_v4_send_reset(daddr, saddr, th, &tcp_prot, opt, dev, 
-			  skb->ip_hdr->tos, 255);
+reset:
+	
+	tcp_v4_send_reset(skb);
 
 discard_it:
-
 	/*
 	 *	Discard frame
 	 */
-
 	kfree_skb(skb, FREE_READ);
 	return 0;
 }
 
-int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb)
-{	
-	struct options * opt = (struct options*)skb->proto_priv;
-	struct device * dev;
-	struct rtable *rt;
-	struct iphdr *iph;
-	struct tcphdr *th;
-	int size;
+int __inline__ tcp_v4_backlog_rcv(struct sock *sk, struct sk_buff *skb)
+{
+	return tcp_v4_do_rcv(sk, skb);
+}
 
-	/*
-	 *	Discard the surplus MAC header
-	 */
-	
-	skb_pull(skb, ((unsigned char *)skb->ip_hdr)-skb->data);
+/*
+ *	From tcp_input.c
+ */
 
-	iph = skb->ip_hdr;
-	th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2));
-	size = skb->tail - (unsigned char *) th;
+int tcp_v4_rcv(struct sk_buff *skb, unsigned short len)
+{
+	struct tcphdr *th;	
+	struct sock *sk;
+	u32	saddr = skb->nh.iph->saddr;
+	u32	daddr = skb->nh.iph->daddr;
 
-	dev = skb->dev;
+	th = skb->h.th;
 
-	rt = ip_check_route(&sk->ip_route_cache, 
-			    opt->srr?opt->faddr:iph->daddr, 
-			    skb->localroute);
+	if (skb->pkt_type!=PACKET_HOST)
+		goto discard_it;
 
+	/*
+	 *	Pull up the IP header.
+	 */
+	
+	skb_pull(skb, skb->h.raw-skb->data);
 
-#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
-	if (rt && ntohs(iph->tot_len) > rt->rt_mtu)
-		iph->frag_off &= ~htons(IP_DF);
-#endif
-			
-	if (rt==NULL)	/* Deep poo */
+	/*
+	 *	Try to use the device checksum if provided.
+	 */
+		
+	switch (skb->ip_summed) 
 	{
-		if(skb->sk)
-		{
-			skb->sk->err_soft=ENETUNREACH;
-			skb->sk->error_report(skb->sk);
+	case CHECKSUM_NONE:
+		skb->csum = csum_partial((char *)th, len, 0);
+	case CHECKSUM_HW:
+		if (tcp_v4_check(th,len,saddr,daddr,skb->csum)) {
+			struct iphdr * iph = skb->nh.iph;
+			printk("TCPv4 bad checksum from %08x:%04x to %08x:%04x, len=%d/%d/%d\n",
+			       saddr, ntohs(th->source), daddr,
+			       ntohs(th->dest), len, skb->len, ntohs(iph->tot_len));
+					goto discard_it;
 		}
-		return -1;
+	default:
+		/* CHECKSUM_UNNECESSARY */
 	}
 
-
-	dev=rt->rt_dev;
-	skb->raddr=rt->rt_gateway;
-	skb->dev=dev;
-	skb->arp=1;
-
-	if (rt->rt_hh)
-	{
-		memcpy(skb_push(skb, dev->hard_header_len), 
-		       rt->rt_hh->hh_data, dev->hard_header_len);
-
-		if (!rt->rt_hh->hh_uptodate)
-		{
-			skb->arp = 0;
-#if RT_CACHE_DEBUG >= 2
-			printk("tcp_do_rebuild_header: "
-			       "hh miss %08x via %08x\n", 
-			       iph->daddr, rt->rt_gateway);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	if (IPCB(skb)->redirport)
+		sk = get_sock_proxy(&tcp_prot, th->dest, saddr, th->source, daddr, dev->pa_addr, IPCB(skb)->redirport);
+	else
 #endif
-		}
-	}
-	else if (dev->hard_header)
-	{
-		if(dev->hard_header(skb, dev, ETH_P_IP, NULL, NULL, 
-				    skb->len)<0)
-			skb->arp=0;
-	}
+	sk = get_tcp_sock(saddr, th->source, daddr, th->dest);
 
-	return 0;	
-}
+	if (!sk)
+		goto no_tcp_socket;
 
-int tcp_v4_backlog_rcv(struct sock *sk, struct sk_buff *skb)
-{
-	return tcp_v4_rcv(skb, skb->dev, (struct options *) skb->proto_priv,
-			  skb->daddr, skb->len, skb->saddr, 1,
-			  (struct inet_protocol *) sk->pair);
-}
+	skb->seq = ntohl(th->seq);
+	skb->end_seq = skb->seq + th->syn + th->fin + len - th->doff*4;
+	skb->ack_seq = ntohl(th->ack_seq);
+		
+	skb->acked = 0;
+	skb->used = 0;
 
-static struct sock * tcp_v4_get_sock(struct sk_buff *skb, struct tcphdr *th)
-{
-	struct sock *sk;
+	if (!sk->users)
+		return tcp_v4_do_rcv(sk, skb);
 
-	sk = get_tcp_sock(skb->saddr, th->source, skb->daddr, th->dest, 0, 0);
+	__skb_queue_tail(&sk->back_log, skb);
+	return 0;
 
-	return sk;
+no_tcp_socket:
+	tcp_v4_send_reset(skb);
+
+discard_it:
+	/*
+ 	 *	Discard frame
+	 */
+	kfree_skb(skb, FREE_READ);
+  	return 0;
 }
+  
 
 int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb)
 {
-	struct device *dev = NULL;
-	int tmp;
+	return ip_build_header(skb, sk);
+}
 
-	tmp = ip_build_header(skb, sk->saddr, sk->daddr, &dev,
-			      IPPROTO_TCP, sk->opt, skb->truesize, 
-			      sk->ip_tos, sk->ip_ttl, 
-			      &sk->ip_route_cache);
-	skb->dev = dev;
+int tcp_v4_rebuild_header(struct sock *sk, struct sk_buff *skb)
+{	
+	struct rtable *rt;
+	struct iphdr *iph;
+	struct tcphdr *th;
+	int size;
 
-#ifndef CONFIG_NO_PATH_MTU_DISCOVERY
-	if (tmp > 0)
-	{
-		skb->ip_hdr->frag_off |= htons(IP_DF);
+	/* Check route */
+
+	rt = (struct rtable*)skb->dst;
+	if (rt->u.dst.obsolete) {
+		int err;
+		err = ip_route_output(&rt, rt->rt_dst, rt->rt_src, rt->key.tos, rt->key.dst_dev);
+		if (err) {
+			sk->err_soft=-err;
+			sk->error_report(skb->sk);
+			return -1;
+		}
+		dst_release(skb->dst);
+		skb->dst = &rt->u.dst;
 	}
-#endif
+  
+	/*
+	 *	Discard the surplus MAC header
+	 */
+	
+	skb_pull(skb, skb->nh.raw-skb->data);
+
+	iph = skb->nh.iph;
+	th = skb->h.th;
+	size = skb->tail - skb->h.raw;
 
-	return tmp;
+	return 0;	
 }
 
+static struct sock * tcp_v4_get_sock(struct sk_buff *skb, struct tcphdr *th)
+{
+	struct sock *sk;
+	sk = get_tcp_sock(skb->nh.iph->saddr, th->source, skb->nh.iph->daddr, th->dest);
+	return sk;
+}
 
 static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
 {
@@ -1258,6 +1120,7 @@
 	ip_setsockopt,
 	ip_getsockopt,
 	v4_addr2sockaddr,
+	tcp_v4_send_reset,
 	sizeof(struct sockaddr_in)
 };
 
@@ -1327,7 +1190,6 @@
 	 
   	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		IS_SKB(skb);
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
   	}
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.14/linux/net/ipv4/tcp_output.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipv4/tcp_output.c	Thu Dec 12 16:54:25 1996
@@ -186,9 +186,9 @@
 		skb->when = jiffies;
 		
 		buff = skb_clone(skb, GFP_ATOMIC);
-		atomic_add(buff->truesize, &sk->wmem_alloc);
+		skb_set_owner_w(buff, sk);
 
-		tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
+		tp->af_specific->queue_xmit(buff);
 
 		if (!tcp_timer_is_set(sk, TIME_RETRANS))
 			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
@@ -232,9 +232,6 @@
 	if (buff == NULL)
 		return -1;
 
-	buff->sk = sk;
-	buff->localroute = sk->localroute;
-	    	
 	/*
 	 *	Put headers on the new packet
 	 */
@@ -243,7 +240,7 @@
 
 	if (tmp < 0)
 	{
-		sock_wfree(sk, buff);
+		kfree_skb(buff, FREE_WRITE);
 		return -1;
 	}
 		
@@ -318,8 +315,6 @@
 	update_send_head(sk);
 
 	skb_unlink(skb);	
-	skb->sk = NULL;
-	skb->free = 1;
 	kfree_skb(skb, FREE_WRITE);
 
 	if (!sk->dead)
@@ -393,7 +388,7 @@
 		int size;
 
 		IS_SKB(skb);
-				
+
 		/*
 		 *	See if we really need to send the packet. 
 		 */
@@ -449,10 +444,10 @@
 		clear_delayed_acks(sk);
 
 		buff = skb_clone(skb, GFP_ATOMIC);
-		atomic_add(buff->truesize, &sk->wmem_alloc);
+		skb_set_owner_w(buff, sk);
 
 		sent_pkts = 1;
-		tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
+		tp->af_specific->queue_xmit(buff);
 
 	}
 	
@@ -614,7 +609,6 @@
 	 * ... and off you go.
 	 */
 
-	buff->free = 1;
 	kfree_skb(buff, FREE_WRITE);
 	atomic_dec(&sk->packets_out);
 
@@ -714,11 +708,11 @@
 		
 		skb->when = jiffies;
 		buff = skb_clone(skb, GFP_ATOMIC);
-		atomic_add(buff->truesize, &sk->wmem_alloc);
+		skb_set_owner_w(buff, sk);
 		
 		clear_delayed_acks(sk);
 		
-		tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
+		tp->af_specific->queue_xmit(buff);
 		
 		/*
 		 *	Count retransmissions
@@ -791,8 +785,6 @@
 	 *	Administrivia
 	 */
 	 
-	buff->sk = sk;
-	buff->localroute = sk->localroute;
 	buff->csum = 0;
 
 	/*
@@ -809,8 +801,7 @@
   		 *	(Not good).
   		 */
   		 
-	  	buff->free = 1;
-		sock_wfree(sk,buff);
+		kfree_skb(buff, FREE_WRITE);
 		sk->write_seq++;
 		t=del_timer(&sk->timer);
 		if(t)
@@ -854,9 +845,9 @@
 		buff->when = jiffies;
 
 		skb1 = skb_clone(buff, GFP_KERNEL);
-		atomic_add(skb1->truesize, &sk->wmem_alloc);
+		skb_set_owner_w(skb1, sk);
 
-		tp->af_specific->queue_xmit(sk, skb1->dev, skb1, 1);
+		tp->af_specific->queue_xmit(skb1);
 
                 if (!tcp_timer_is_set(sk, TIME_RETRANS))
 			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
@@ -879,14 +870,10 @@
 		return -ENOMEM;
 	}
 
-	skb->sk = sk;
-	skb->localroute = sk->localroute;
-
 	tmp = tp->af_specific->build_net_header(sk, skb);
 	
 	if (tmp < 0)
 	{
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 		return tmp;
 	}
@@ -926,9 +913,9 @@
 	skb->when = jiffies;
 	buff = skb_clone(skb, GFP_ATOMIC);
 
-	atomic_add(skb->truesize, &sk->wmem_alloc);
+	skb_set_owner_w(skb, sk);
 
-	tp->af_specific->queue_xmit(sk, skb->dev, buff, 1);
+	tp->af_specific->queue_xmit(buff);
 
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, TCP_TIMEOUT_INIT);
 
@@ -1016,8 +1003,6 @@
 	 *	Assemble a suitable TCP frame
 	 */
 	 
-	buff->sk = sk;
-	buff->localroute = sk->localroute;
 	buff->csum = 0;
 
 	/* 
@@ -1028,8 +1013,7 @@
 
 	if (tmp < 0) 
 	{
-  		buff->free = 1;
-		sock_wfree(sk, buff);
+		kfree_skb(buff, FREE_WRITE);
 		return;
 	}
 
@@ -1055,7 +1039,7 @@
   		 printk("\rtcp_send_ack: seq %x ack %x\n", 
 			tp->snd_nxt, tp->rcv_nxt);
 
-	tp->af_specific->queue_xmit(sk, buff->dev, buff, 1);
+	tp->af_specific->queue_xmit(buff);
 
   	tcp_statistics.TcpOutSegs++;
 }
@@ -1123,7 +1107,7 @@
 
 		buff = skb_clone(skb, GFP_ATOMIC);
 
-		atomic_add(buff->truesize, &sk->wmem_alloc);
+		skb_set_owner_w(buff, sk);
 		atomic_inc(&sk->packets_out);
 
 		clear_delayed_acks(sk);
@@ -1143,9 +1127,6 @@
 		if (buff == NULL) 
 			return;
 
-		buff->free = 1;
-		buff->sk = sk;
-		buff->localroute = sk->localroute;
 		buff->csum = 0;
 
 		/*
@@ -1156,7 +1137,7 @@
 
 		if (tmp < 0) 
 		{
-			sock_wfree(sk, buff);
+			kfree_skb(buff, FREE_WRITE);
 			return;
 		}
 
@@ -1180,7 +1161,7 @@
 	 *	Send it.
 	 */
 
-	tp->af_specific->queue_xmit(sk, buff->dev, buff, 1);
+	tp->af_specific->queue_xmit(buff);
 	tcp_statistics.TcpOutSegs++;
 }
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.14/linux/net/ipv4/tcp_timer.c	Fri Nov 22 18:28:23 1996
+++ linux/net/ipv4/tcp_timer.c	Thu Dec 12 16:54:25 1996
@@ -172,7 +172,7 @@
 		 *	Attempt to recover if arp has changed (unlikely!) or
 		 *	a route has shifted (not supported prior to 1.3).
 		 */
-		ip_rt_advice(&sk->ip_route_cache, 0);
+		ip_rt_advice((struct rtable**)&sk->dst_cache, 0);
 	}
 	
 	/*
@@ -303,7 +303,8 @@
 {
 	int res = 0;
 
-	if (sk->state == TCP_ESTABLISHED || sk->state == TCP_CLOSE_WAIT)
+	if (sk->state == TCP_ESTABLISHED || sk->state == TCP_CLOSE_WAIT ||
+	    sk->state == TCP_FIN_WAIT2)
 	{
 		struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 		__u32 elapsed = jiffies - tp->rcv_tstamp;
diff -u --recursive --new-file v2.1.14/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.14/linux/net/ipv4/udp.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipv4/udp.c	Thu Dec 12 16:54:25 1996
@@ -150,25 +150,24 @@
  * to find the appropriate port.
  */
 
-void udp_err(int type, int code, unsigned char *header, __u32 info,
-	     __u32 daddr, __u32 saddr, struct inet_protocol *protocol, int len)
+void udp_err(struct sk_buff *skb, unsigned char *dp)
 {
-	struct udphdr *uh;
+	struct iphdr *iph = (struct iphdr*)dp;
+	struct udphdr *uh = (struct udphdr*)(dp+(iph->ihl<<2));
+	int type = skb->h.icmph->type;
+	int code = skb->h.icmph->code;
 	struct sock *sk;
 
-	/*
-	 *	Find the 8 bytes of post IP header ICMP included for us
-	 */  
-	 
-	if(len<sizeof(struct udphdr))
-		return;
-	
-	uh = (struct udphdr *)header;  
-   
-	sk = get_sock(&udp_prot, uh->source, daddr, uh->dest, saddr, 0, 0);
+	sk = get_sock(&udp_prot, uh->source, iph->daddr, uh->dest, iph->saddr);
 
-	if (sk == NULL) 
+	if (sk == NULL)
 	  	return;	/* No socket for error */
+
+	if (sk->ip_recverr && !sk->users) {
+		struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
+		if (skb2 && sock_queue_err_skb(sk, skb2))
+			kfree_skb(skb2, FREE_READ);
+	}
   	
 	if (type == ICMP_SOURCE_QUENCH) 
 	{	/* Slow down! */
@@ -183,6 +182,15 @@
 		sk->error_report(sk);
 		return;
 	}
+
+	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
+	{
+		if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
+			sk->err = EMSGSIZE;
+			sk->error_report(sk);
+		}
+		return;
+	}
 			
 	/*
 	 *	Various people wanted BSD UDP semantics. Well they've come 
@@ -196,7 +204,7 @@
 	/* 4.1.3.3. */
 	/* After the comment above, that should be no surprise. */
 
-	if(code<=NR_ICMP_UNREACH && icmp_err_convert[code].fatal)
+	if (code < NR_ICMP_UNREACH && icmp_err_convert[code].fatal)
 	{
 		/*
 		 *	4.x BSD compatibility item. Break RFC1122 to
@@ -218,10 +226,11 @@
 struct udpfakehdr 
 {
 	struct udphdr uh;
-	__u32 daddr;
-	__u32 other;
+	u32 saddr;
+	u32 daddr;
+	u32 other;
 	const char *from;
-	__u32 wcheck;
+	u32 wcheck;
 };
 
 /*
@@ -230,31 +239,27 @@
  *	for direct user->board I/O transfers. That one will be fun.
  */
  
-static int udp_getfrag(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen) 
+static int udp_getfrag(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
 {
 	struct udpfakehdr *ufh = (struct udpfakehdr *)p;
 	const char *src;
 	char *dst;
 	unsigned int len;
 
-	if (offset)
-	{
+	if (offset) {
 		len = fraglen;
 	 	src = ufh->from+(offset-sizeof(struct udphdr));
 	 	dst = to;
-	}
-	else 
-	{
+	} else {
 		len = fraglen-sizeof(struct udphdr);
  		src = ufh->from;
 		dst = to+sizeof(struct udphdr);
 	}
 	ufh->wcheck = csum_partial_copy_fromuser(src, dst, len, ufh->wcheck);
-	if (offset == 0) 
-	{
+	if (offset == 0) {
  		ufh->wcheck = csum_partial((char *)ufh, sizeof(struct udphdr),
  				   ufh->wcheck);
-		ufh->uh.check = csum_tcpudp_magic(saddr, ufh->daddr, 
+		ufh->uh.check = csum_tcpudp_magic(ufh->saddr, ufh->daddr, 
 					  ntohs(ufh->uh.len),
 					  IPPROTO_UDP, ufh->wcheck);
 		if (ufh->uh.check == 0)
@@ -271,22 +276,19 @@
  *	this is a valid decision.
  */
  
-static int udp_getfrag_nosum(const void *p, __u32 saddr, char * to, unsigned int offset, unsigned int fraglen) 
+static int udp_getfrag_nosum(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
 {
 	struct udpfakehdr *ufh = (struct udpfakehdr *)p;
 	const char *src;
 	char *dst;
+	int err;
 	unsigned int len;
-	int err; 
 
-	if (offset) 
-	{
+	if (offset) {
 		len = fraglen;
 	 	src = ufh->from+(offset-sizeof(struct udphdr));
 	 	dst = to;
-	}
-	else 
-	{
+	} else {
 		len = fraglen-sizeof(struct udphdr);
  		src = ufh->from;
 		dst = to+sizeof(struct udphdr);
@@ -294,162 +296,144 @@
 	err = copy_from_user(dst,src,len);
 	if (offset == 0) 
 		memcpy(to, ufh, sizeof(struct udphdr));
-	return err; 
+	return err;
 }
 
 
-/*
- *	Send UDP frames.
- */
-
-static int udp_send(struct sock *sk, struct sockaddr_in *sin,
-		      const unsigned char *from, int len, int rt,
-		    __u32 saddr, int noblock) 
+static int udp_sendto(struct sock *sk, const unsigned char *from, int len,
+		      struct msghdr *msg)
 {
 	int ulen = len + sizeof(struct udphdr);
-	int a;
+	struct device *dev = NULL;
+	struct ipcm_cookie ipc;
 	struct udpfakehdr ufh;
-	
-	if(ulen>65535-sizeof(struct iphdr))
+	struct rtable *rt;
+	int free = 0;
+	u32 daddr;
+	u8  tos;
+	int err;
+
+	if (len>65535)
 		return -EMSGSIZE;
 
+	/* 
+	 *	Check the flags.
+	 */
+
+	if (msg->msg_flags&MSG_OOB)		/* Mirror BSD error message compatibility */
+		return -EOPNOTSUPP;
+
+	if (msg->msg_flags&~(MSG_DONTROUTE|MSG_DONTWAIT))
+	  	return -EINVAL;
+
+	/*
+	 *	Get and verify the address. 
+	 */
+	 
+	if (msg->msg_namelen) {
+		struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;
+		if (msg->msg_namelen < sizeof(*usin))
+			return(-EINVAL);
+		if (usin->sin_family != AF_INET) {
+			static int complained;
+			if (!complained++)
+				printk(KERN_WARNING "%s forgot to set AF_INET in udp sendmsg. Fix it!\n", current->comm);
+			if (usin->sin_family)
+				return -EINVAL;
+		}
+		ufh.daddr = usin->sin_addr.s_addr;
+		ufh.uh.dest = usin->sin_port;
+		if (ufh.uh.dest == 0)
+			return -EINVAL;
+	} else {
+		if (sk->state != TCP_ESTABLISHED)
+			return -EINVAL;
+		ufh.daddr = sk->daddr;
+		ufh.uh.dest = sk->dummy_th.dest;
+  	}
+
+	ipc.addr = sk->saddr;
+	ipc.opt = NULL;
+	if (msg->msg_controllen) {
+		err = ip_cmsg_send(msg, &ipc, &dev);
+		if (err)
+			return err;
+		if (ipc.opt)
+			free = 1;
+	}
+	if (!ipc.opt)
+		ipc.opt = sk->opt;
+
+	ufh.saddr = ipc.addr;
+	ipc.addr = daddr = ufh.daddr;
+
+	if (ipc.opt && ipc.opt->srr) {
+		if (!daddr)
+			return -EINVAL;
+		daddr = ipc.opt->faddr;
+	}
+	tos = RT_TOS(sk->ip_tos) | (sk->localroute || (msg->msg_flags&MSG_DONTROUTE) ||
+				    (ipc.opt && ipc.opt->is_strictroute));
+
+	if (MULTICAST(daddr) && sk->ip_mc_name[0] && dev == NULL)
+		err = ip_route_output_dev(&rt, daddr, ufh.saddr, tos, sk->ip_mc_name);
+	else
+		err = ip_route_output(&rt, daddr, ufh.saddr, tos, dev);
+
+	if (err) {
+		if (free) kfree(ipc.opt);
+		return err;
+	}
+
+	if (rt->rt_flags&RTF_BROADCAST && !sk->broadcast) {
+		if (free) kfree(ipc.opt);
+		ip_rt_put(rt);
+		return -EACCES;
+	}
+
+	ufh.saddr = rt->rt_src;
+	if (!ipc.addr)
+		ufh.daddr = ipc.addr = rt->rt_dst;
 	ufh.uh.source = sk->dummy_th.source;
-	ufh.uh.dest = sin->sin_port;
 	ufh.uh.len = htons(ulen);
 	ufh.uh.check = 0;
-	ufh.daddr = sin->sin_addr.s_addr;
 	ufh.other = (htons(ulen) << 16) + IPPROTO_UDP*256;
 	ufh.from = from;
 	ufh.wcheck = 0;
 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-	if (rt&MSG_PROXY)
-	{
-		/*
-		 * We map the first 8 bytes of a second sockaddr_in
-		 * into the last 8 (unused) bytes of a sockaddr_in.
-		 * This _is_ ugly, but it's the only way to do it
-		 * easily,  without adding system calls.
-		 */
-		struct sockaddr_in *sinfrom =
-			(struct sockaddr_in *) sin->sin_zero;
-
-		if (!suser())
-			return(-EPERM);
-		if (sinfrom->sin_family && sinfrom->sin_family != AF_INET)
-			return(-EINVAL);
-		if (sinfrom->sin_port == 0)
-			return(-EINVAL);
-		saddr = sinfrom->sin_addr.s_addr;
-		ufh.uh.source = sinfrom->sin_port;
-	}
-#endif
-
 	/* RFC1122: OK.  Provides the checksumming facility (MUST) as per */
 	/* 4.1.3.4. It's configurable by the application via setsockopt() */
 	/* (MAY) and it defaults to on (MUST).  Almost makes up for the */
 	/* violation above. -- MS */
 
-	if(sk->no_check)
-		a = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen, 
-			sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock);
-	else
-		a = ip_build_xmit(sk, udp_getfrag, &ufh, ulen, 
-			sin->sin_addr.s_addr, saddr, sk->opt, rt, IPPROTO_UDP, noblock);
-	if(a<0)
-		return a;
-	udp_statistics.UdpOutDatagrams++;
-	return len;
-}
-
-
-static int udp_sendto(struct sock *sk, const unsigned char *from, int len, int noblock,
-	   unsigned flags, struct sockaddr_in *usin, int addr_len)
-{
-	struct sockaddr_in sin;
-	int tmp;
-	__u32 saddr=0;
-
-	/* 
-	 *	Check the flags. We support no flags for UDP sending
-	 */
-
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-	if (flags&~(MSG_DONTROUTE|MSG_PROXY))
-#else
-	if (flags&~MSG_DONTROUTE) 
-#endif
-	  	return(-EINVAL);
-	/*
-	 *	Get and verify the address. 
-	 */
-	 
-	if (usin) 
-	{
-		if (addr_len < sizeof(sin)) 
-			return(-EINVAL);
-		if (usin->sin_family && usin->sin_family != AF_INET) 
-			return(-EINVAL);
-		if (usin->sin_port == 0) 
-			return(-EINVAL);
-	} 
-	else 
-	{
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-		/* We need to provide a sockaddr_in when using MSG_PROXY. */
-		if (flags&MSG_PROXY)
-			return(-EINVAL);
-#endif
-		if (sk->state != TCP_ESTABLISHED) 
-			return(-EINVAL);
-		sin.sin_family = AF_INET;
-		sin.sin_port = sk->dummy_th.dest;
-		sin.sin_addr.s_addr = sk->daddr;
-		usin = &sin;
-  	}
-  
-  	/*
-  	 *	BSD socket semantics. You must set SO_BROADCAST to permit
-  	 *	broadcasting of data.
-  	 */
-  	 
-	/* RFC1122: OK.  Allows the application to select the specific */
-	/* source address for an outgoing packet (MUST) as per 4.1.3.5. */
-	/* Optional addition: a mechanism for telling the application what */
-	/* address was used. (4.1.3.5, MAY) -- MS */
-
-	/* RFC1122: MUST ensure that all outgoing packets have one */
-	/* of this host's addresses as a source addr.(4.1.3.6) - bind in  */
-	/* af_inet.c checks these. It does need work to allow BSD style */
-	/* bind to multicast as is done by xntpd		*/
-
-  	if(usin->sin_addr.s_addr==INADDR_ANY)
-  		usin->sin_addr.s_addr=ip_my_addr();
-  		
-  	if(!sk->broadcast && ip_chk_addr(usin->sin_addr.s_addr)==IS_BROADCAST)
-	    	return -EACCES;			/* Must turn broadcast on first */
-
 	lock_sock(sk);
-
-	/* Send the packet. */
-	tmp = udp_send(sk, usin, from, len, flags, saddr, noblock);
-
-	/* The datagram has been sent off.  Release the socket. */
+	if (sk->no_check)
+		err = ip_build_xmit(sk, udp_getfrag_nosum, &ufh, ulen, 
+				    &ipc, rt, msg->msg_flags);
+	else
+		err = ip_build_xmit(sk, udp_getfrag, &ufh, ulen, 
+				    &ipc, rt, msg->msg_flags);
+	ip_rt_put(rt);
 	release_sock(sk);
-	return(tmp);
+
+	if (free)
+		kfree(ipc.opt);
+	if (!err) {
+		udp_statistics.UdpOutDatagrams++;
+		return len;
+	}
+	return err;
 }
 
 /*
  *	Temporary
  */
  
-int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len, int noblock, 
-		int flags)
+int udp_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
 	if(msg->msg_iovlen==1)
-		return udp_sendto(sk,msg->msg_iov[0].iov_base,len, noblock, flags, msg->msg_name, msg->msg_namelen);
-	else
-	{
+		return udp_sendto(sk,msg->msg_iov[0].iov_base,len, msg);
+	else {
 		/*
 		 *	For awkward cases we linearise the buffer first. In theory this is only frames
 		 *	whose iovec's don't split on 4 byte boundaries, and soon encrypted stuff (to keep
@@ -471,7 +455,7 @@
 		{
 			fs=get_fs();
 			set_fs(get_ds());
-			err=udp_sendto(sk,buf,len, noblock, flags, msg->msg_name, msg->msg_namelen);
+			err=udp_sendto(sk,buf,len, msg);
 			set_fs(fs);
 		}
 		kfree_s(buf,len);
@@ -513,6 +497,7 @@
 				amount = skb->len-sizeof(struct udphdr);
 			}
 			return put_user(amount, (int *)arg);
+			return(0);
 		}
 
 		default:
@@ -542,6 +527,17 @@
 	 
   	if (addr_len) 
   		*addr_len=sizeof(*sin);
+
+	if (sk->ip_recverr && (skb = skb_dequeue(&sk->error_queue)) != NULL) {
+		er = sock_error(sk);
+		if (msg->msg_controllen == 0) {
+			skb_free_datagram(sk, skb);
+			return er;
+		}
+		put_cmsg(msg, SOL_IP, IP_RECVERR, skb->len, skb->data);
+		skb_free_datagram(sk, skb);
+		return 0;
+	}
   
 	/*
 	 *	From here the generic datagram does a lot of the work. Come
@@ -553,13 +549,12 @@
   		return er;
   
   	truesize = skb->len - sizeof(struct udphdr);
-  	copied = truesize;
-  	
-  	if(len<truesize)
-  	{
-  		copied=len;
-  		msg->msg_flags|=MSG_TRUNC;
-  	}
+	copied = truesize;
+	if (len < truesize)
+	{
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
 
   	/*
   	 *	FIXME : should use udp header size info value 
@@ -571,11 +566,11 @@
 	sk->stamp=skb->stamp;
 
 	/* Copy the address. */
-	if (sin) 
+	if (sin)
 	{
 		sin->sin_family = AF_INET;
 		sin->sin_port = skb->h.uh->source;
-		sin->sin_addr.s_addr = skb->daddr;
+		sin->sin_addr.s_addr = skb->nh.iph->saddr;
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 		if (flags&MSG_PROXY)
 		{
@@ -590,10 +585,12 @@
 
 			sinto->sin_family = AF_INET;
 			sinto->sin_port = skb->h.uh->dest;
-			sinto->sin_addr.s_addr = skb->saddr;
+			sinto->sin_addr.s_addr = skb->nh.iph->daddr;
 		}
 #endif
   	}
+	if (sk->ip_cmsg_flags)
+		ip_cmsg_recv(msg, skb);
   
   	skb_free_datagram(sk, skb);
   	return(copied);
@@ -603,7 +600,12 @@
 {
 	struct sockaddr_in *usin = (struct sockaddr_in *) uaddr;
 	struct rtable *rt;
+	int err;
+
 	
+	if (addr_len < sizeof(*usin)) 
+	  	return(-EINVAL);
+
 	/*
 	 *	1003.1g - break association.
 	 */
@@ -617,30 +619,27 @@
 		udp_cache_zap();
 		return 0;
 	}
-	
-	if (addr_len < sizeof(*usin)) 
-	  	return(-EINVAL);
 
 	if (usin->sin_family && usin->sin_family != AF_INET) 
 	  	return(-EAFNOSUPPORT);
-	if (usin->sin_addr.s_addr==INADDR_ANY)
-		usin->sin_addr.s_addr=ip_my_addr();
 
-	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);
-  	if (rt==NULL)
-  		return -ENETUNREACH;
+	err = ip_route_connect(&rt, usin->sin_addr.s_addr, sk->saddr,
+			       sk->ip_tos|sk->localroute);
+	if (err)
+		return err;
+	if (rt->rt_flags&RTF_BROADCAST && !sk->broadcast) {
+		ip_rt_put(rt);
+		return -EACCES;
+	}
   	if(!sk->saddr)
 	  	sk->saddr = rt->rt_src;		/* Update source address */
 	if(!sk->rcv_saddr)
 		sk->rcv_saddr = rt->rt_src;
-	sk->daddr = usin->sin_addr.s_addr;
+	sk->daddr = rt->rt_dst;
 	sk->dummy_th.dest = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
 	udp_cache_zap();
-	sk->ip_route_cache = rt;
+	ip_rt_put(rt);
 	return(0);
 }
 
@@ -656,22 +655,18 @@
 	destroy_sock(sk);
 }
 
-static inline int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
+static int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 {
 	/*
 	 *	Charge it to the socket, dropping if the queue is full.
 	 */
 
-	/* I assume this includes the IP options, as per RFC1122 (4.1.3.2). */
-	/* If not, please let me know. -- MS */
-
 	if (__sock_queue_rcv_skb(sk,skb)<0) {
 		udp_statistics.UdpInErrors++;
 		ip_statistics.IpInDiscards++;
 		ip_statistics.IpInDelivers--;
-		skb->sk = NULL;
 		kfree_skb(skb, FREE_WRITE);
-		return 0;
+		return -1;
 	}
 	udp_statistics.UdpInDatagrams++;
 	return 0;
@@ -680,8 +675,6 @@
 
 static inline void udp_deliver(struct sock *sk, struct sk_buff *skb)
 {
-	skb->sk = sk;
-
 	if (sk->users) {
 		__skb_queue_tail(&sk->back_log, skb);
 		return;
@@ -697,11 +690,11 @@
 
 int udp_chkaddr(struct sk_buff *skb)
 {
-	struct iphdr *iph = skb->h.iph;
-	struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4);
+	struct iphdr *iph = skb->nh.iph;
+	struct udphdr *uh = (struct udphdr *)(skb->nh.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = get_sock(&udp_prot, uh->dest, iph->saddr, uh->source, iph->daddr, 0, 0);
+	sk = get_sock(&udp_prot, uh->dest, iph->saddr, uh->source, iph->daddr);
 
 	if (!sk) return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
@@ -710,33 +703,49 @@
 }
 #endif
 
+
+static __inline__ struct sock *
+get_udp_sock(unsigned short dport, unsigned long saddr, unsigned short sport,
+	     unsigned long daddr)
+{
+	struct sock *sk;
+
+	if (saddr==uh_cache_saddr && daddr==uh_cache_daddr &&
+	    dport==uh_cache_dport && sport==uh_cache_sport)
+		 return (struct sock *)uh_cache_sk;
+	sk = get_sock(&udp_prot, dport, saddr, sport, daddr);
+	uh_cache_saddr=saddr;
+	uh_cache_daddr=daddr;
+	uh_cache_dport=dport;
+	uh_cache_sport=sport;
+	uh_cache_sk=sk;
+	return sk;
+}
+
+
 /*
  *	All we need to do is get the socket, and then do a checksum. 
  */
  
-int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
-	__u32 daddr, unsigned short len,
-	__u32 saddr, int redo, struct inet_protocol *protocol)
+int udp_rcv(struct sk_buff *skb, unsigned short len)
 {
   	struct sock *sk;
   	struct udphdr *uh;
 	unsigned short ulen;
-	int addr_type;
+	struct rtable *rt = (struct rtable*)skb->dst;
+	u32 saddr = skb->nh.iph->saddr;
+	u32 daddr = skb->nh.iph->daddr;
 
 	/*
 	 * First time through the loop.. Do all the setup stuff
 	 * (including finding out the socket we go to etc)
 	 */
 
-	addr_type = IS_MYADDR;
-	if(!dev || dev->pa_addr!=daddr)
-		addr_type=ip_chk_addr(daddr);
-		
 	/*
 	 *	Get the header.
 	 */
 	 
-  	uh = (struct udphdr *) skb->h.uh;
+  	uh = skb->h.uh;
   	
   	ip_statistics.IpInDelivers++;
 
@@ -746,8 +755,7 @@
 	 
 	ulen = ntohs(uh->len);
 	
-	if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) 
-	{
+	if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) {
 		NETDEBUG(printk("UDP: short packet: %d/%d\n", ulen, len));
 		udp_statistics.UdpInErrors++;
 		kfree_skb(skb, FREE_WRITE);
@@ -766,8 +774,7 @@
 		( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
 			  /* skip if CHECKSUM_UNNECESSARY */
 		         )
-	   )
-	{
+	   ) {
 		/* <mea@utu.fi> wants to know, who sent it, to
 		   go and stomp on the garbage sender... */
 
@@ -783,31 +790,22 @@
 		return(0);
 	}
 
-	/*
-	 *	These are supposed to be switched. 
-	 */
-	 
-	skb->daddr = saddr;
-	skb->saddr = daddr;
 
-	len=ulen;
+	len = ulen;
 
-	skb->dev = dev;
+	/* Wrong! --ANK */
 	skb_trim(skb,len);
 
-#ifdef CONFIG_IP_MULTICAST
-	if (addr_type==IS_BROADCAST || addr_type==IS_MULTICAST)
-	{
+
+	if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST)) {
 		/*
 		 *	Multicasts and broadcasts go to each listener.
 		 */
 		struct sock *sknext=NULL;
 		sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
 				saddr, uh->source, daddr);
-		if(sk)
-		{		
-			do
-			{
+		if(sk) {		
+			do {
 				struct sk_buff *skb1;
 
 				sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);
@@ -818,38 +816,27 @@
 				if(skb1)
 					udp_deliver(sk, skb1);
 				sk=sknext;
-			}
-			while(sknext!=NULL);
-		}
-		else
+			} while(sknext!=NULL);
+		} else
 			kfree_skb(skb, FREE_READ);
 		return 0;
-	}	
-#endif
-	if(saddr==uh_cache_saddr && daddr==uh_cache_daddr && uh->dest==uh_cache_dport && uh->source==uh_cache_sport)
-		sk=(struct sock *)uh_cache_sk;
-	else
-	{
-	  	sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr, dev->pa_addr, skb->redirport);
-  		uh_cache_saddr=saddr;
-  		uh_cache_daddr=daddr;
-  		uh_cache_dport=uh->dest;
-  		uh_cache_sport=uh->source;
-  		uh_cache_sk=sk;
 	}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+	if (IPCB(skb)->redirport)
+		sk = get_sock_proxy(&udp_prot, uh->dest, saddr, uh->source, daddr, dev->pa_addr, IPCB(skb)->redirport);
+	else
+#endif
+	sk = get_udp_sock(uh->dest, saddr, uh->source, daddr);
 	
-	if (sk == NULL) 
-  	{
+	if (sk == NULL) {
   		udp_statistics.UdpNoPorts++;
-		if (addr_type != IS_BROADCAST && addr_type != IS_MULTICAST) 
-		{
-			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0, dev);
-		}
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
+
 		/*
 		 * Hmm.  We got an UDP broadcast to a port to which we
 		 * don't wanna listen.  Ignore it.
 		 */
-		skb->sk = NULL;
 		kfree_skb(skb, FREE_WRITE);
 		return(0);
   	}
@@ -873,12 +860,11 @@
 	ip_getsockopt,
 	udp_sendmsg,
 	udp_recvmsg,
-	NULL,			/* No special bind function */
-	udp_queue_rcv_skb,     
+	NULL,		/* No special bind function */
+	udp_queue_rcv_skb,
 	128,
 	0,
 	"UDP",
 	0, 0,
-	NULL
+	NULL,
 };
-
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.14/linux/net/ipv6/af_inet6.c	Thu Dec 12 17:02:47 1996
+++ linux/net/ipv6/af_inet6.c	Thu Dec 12 16:54:25 1996
@@ -101,6 +101,9 @@
 
 struct sock * rawv6_sock_array[SOCK_ARRAY_SIZE];
 
+extern struct proto_ops inet6_stream_ops;
+extern struct proto_ops inet6_dgram_ops;
+
 static int inet6_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
@@ -130,6 +133,7 @@
 			protocol = IPPROTO_TCP;
 			sk->no_check = TCP_NO_CHECK;
 			prot = &tcpv6_prot;
+			sock->ops = &inet6_stream_ops;
 			break;
 
 		case SOCK_DGRAM:
@@ -141,6 +145,7 @@
 			protocol = IPPROTO_UDP;
 			sk->no_check = UDP_NO_CHECK;
 			prot=&udpv6_prot;
+			sock->ops = &inet6_dgram_ops;
 			break;
       
 		case SOCK_RAW:
@@ -155,6 +160,7 @@
 				return(-EPROTONOSUPPORT);
 			}
 			prot = &rawv6_prot;
+			sock->ops = &inet6_dgram_ops;
 			sk->reuse = 1;
 			sk->num = protocol;
 			break;
@@ -176,13 +182,14 @@
 	sk->prot = prot;
 	sk->backlog_rcv = prot->backlog_rcv;
 
-	sk->sleep = sock->wait;
-	sock->data =(void *) sk;
+	sk->sleep = &sock->wait;
+	sock->sk = sk;
 
 	sk->state = TCP_CLOSE;
 
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->error_queue);
 	skb_queue_head_init(&sk->back_log);
 
 	sk->timer.data = (unsigned long)sk;
@@ -244,8 +251,7 @@
 
 static int inet6_dup(struct socket *newsock, struct socket *oldsock)
 {
-	return(inet6_create(newsock, 
-			    ((struct sock *)(oldsock->data))->protocol));
+	return(inet6_create(newsock, oldsock->sk->protocol));
 }
 
 
@@ -257,7 +263,7 @@
 		      int addr_len)
 {
 	struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
-	struct sock *sk=(struct sock *)sock->data, *sk2;
+	struct sock *sk = sock->sk, *sk2;
 	__u32 v4addr = 0;
 	unsigned short snum = 0;
 	int addr_type = 0;
@@ -305,7 +311,7 @@
 	{
 		v4addr = addr->sin6_addr.s6_addr32[3];
 
-		if (ip_chk_addr(v4addr) != IS_MYADDR)
+		if (__ip_chk_addr(v4addr) != IS_MYADDR)
 			return(-EADDRNOTAVAIL);
 	}
 	else
@@ -440,7 +446,7 @@
 	struct sock *sk;
   
 	sin->sin6_family = AF_INET6;
-	sk = (struct sock *) sock->data;
+	sk = sock->sk;
 	if (peer) 
 	{
 		if (!tcp_connected(sk->state))
@@ -472,7 +478,7 @@
 
 static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk=(struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 	int err;
 	int pid;
 
@@ -782,14 +788,13 @@
 }
 	
 
-static struct proto_ops inet6_proto_ops = {
+struct proto_ops inet6_stream_ops = {
 	AF_INET6,
 
-	inet6_create,
 	inet6_dup,
 	inet6_release,
 	inet6_bind,
-	inet_connect,			/* ok		*/
+	inet_stream_connect,		/* ok		*/
 	inet6_socketpair,		/* a do nothing	*/
 	inet_accept,			/* ok		*/
 	inet6_getname, 
@@ -804,6 +809,34 @@
 	inet_recvmsg			/* ok		*/
 };
 
+struct proto_ops inet6_dgram_ops = {
+	AF_INET6,
+
+	inet6_dup,
+	inet6_release,
+	inet6_bind,
+	inet_dgram_connect,		/* ok		*/
+	inet6_socketpair,		/* a do nothing	*/
+	inet_accept,			/* ok		*/
+	inet6_getname, 
+	datagram_select,		/* ok		*/
+	inet6_ioctl,			/* must change  */
+	inet_listen,			/* ok		*/
+	inet_shutdown,			/* ok		*/
+	inet_setsockopt,		/* ok		*/
+	inet_getsockopt,		/* ok		*/
+	inet_fcntl,			/* ok		*/
+	inet_sendmsg,			/* ok		*/
+	inet_recvmsg			/* ok		*/
+};
+
+struct net_proto_family inet6_family_ops = {
+	AF_INET6,
+	inet6_create
+};
+
+
+
 #ifdef MODULE
 int init_module(void)
 #else
@@ -814,7 +847,7 @@
 
 	printk(KERN_INFO "IPv6 v0.1 for NET3.037\n");
 
-	sock_register(inet6_proto_ops.family, &inet6_proto_ops);
+  	(void) sock_register(&inet6_family_ops);
 	
 	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
 	{
@@ -842,8 +875,8 @@
 	
 	ipv6_init();
 
-	icmpv6_init(&inet6_proto_ops);
-	ndisc_init(&inet6_proto_ops);
+	icmpv6_init(&inet6_family_ops);
+	ndisc_init(&inet6_family_ops);
 
         addrconf_init();
  
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/datagram.c linux/net/ipv6/datagram.c
--- v2.1.14/linux/net/ipv6/datagram.c	Tue Nov 12 15:56:16 1996
+++ linux/net/ipv6/datagram.c	Thu Dec 12 16:54:25 1996
@@ -30,7 +30,7 @@
 int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
-	struct ipv6_options *opt = (struct ipv6_options *) skb->proto_priv;
+	struct ipv6_options *opt = (struct ipv6_options *) skb->cb;
 	struct cmsghdr *cmsg = msg->msg_control;
 	int len = msg->msg_controllen;
 
@@ -58,7 +58,7 @@
 
 		src_info->ipi6_ifindex = in6_dev->if_index;
 		ipv6_addr_copy(&src_info->ipi6_addr,
-			       &skb->ipv6_hdr->daddr);
+			       &skb->nh.ipv6h->daddr);
 
 		len -= cmsg->cmsg_len;
 		msg->msg_controllen += cmsg->cmsg_len;
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/exthdrs.c linux/net/ipv6/exthdrs.c
--- v2.1.14/linux/net/ipv6/exthdrs.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv6/exthdrs.c	Thu Dec 12 16:54:25 1996
@@ -57,7 +57,7 @@
 	{
 		struct ipv6_options *opt;
 
-		opt = (struct ipv6_options *) skb->proto_priv;
+		opt = (struct ipv6_options *) skb->cb;
 		opt->srcrt = hdr;
 
 		skb->h.raw += (hdr->hdrlen + 1) << 3;
@@ -71,7 +71,7 @@
 		 *	Discard 
 		 */
 		
-		pos = (__u8 *) hdr - (__u8 *) skb->ipv6_hdr + 2;
+		pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
 
 		if (hdr->type)
 			pos += 2;
@@ -92,7 +92,7 @@
 
 	if (hdr->segments_left > n)
 	{
-		pos = (__u8 *) hdr - (__u8 *) skb->ipv6_hdr + 2;
+		pos = (__u8 *) hdr - (__u8 *) skb->nh.ipv6h + 2;
 
 		pos += 3;
 
@@ -116,8 +116,8 @@
 	}
 
 	ipv6_addr_copy(&daddr, addr);
-	ipv6_addr_copy(addr, &skb->ipv6_hdr->daddr);
-	ipv6_addr_copy(&skb->ipv6_hdr->daddr, &daddr);
+	ipv6_addr_copy(addr, &skb->nh.ipv6h->daddr);
+	ipv6_addr_copy(&skb->nh.ipv6h->daddr, &daddr);
 
 	/*
 	 *	Check Strick Source Route
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.1.14/linux/net/ipv6/icmp.c	Fri Nov 15 23:49:12 1996
+++ linux/net/ipv6/icmp.c	Thu Dec 12 16:54:25 1996
@@ -66,7 +66,8 @@
  *	ICMP socket for flow control.
  */
 
-static struct socket icmpv6_socket;
+struct inode icmpv6_inode;
+struct socket *icmpv6_socket=&icmpv6_inode.u.socket_i;
 
 int icmpv6_rcv(struct sk_buff *skb, struct device *dev,
 	       struct in6_addr *saddr, struct in6_addr *daddr,
@@ -145,7 +146,7 @@
  */
 static __inline__ int opt_unrec(struct sk_buff *skb, __u32 offset)
 {
-	char *buff = (char *) skb->ipv6_hdr;
+	char *buff = skb->nh.raw;
 
 	return ( ( *(buff + offset) & 0xC0 ) == 0x80 );
 }
@@ -157,8 +158,8 @@
 void icmpv6_send(struct sk_buff *skb, int type, int code, __u32 info, 
 		 struct device *dev)
 {
-	struct ipv6hdr *hdr = skb->ipv6_hdr;
-	struct sock *sk = (struct sock *) icmpv6_socket.data;
+	struct ipv6hdr *hdr = skb->nh.ipv6h;
+	struct sock *sk = icmpv6_socket->sk;
 	struct in6_addr *saddr = NULL;
 	struct device *src_dev = NULL;
 	struct icmpv6_msg msg;
@@ -239,7 +240,7 @@
 	msg.icmph.checksum = 0;
 	msg.icmph.icmp6_pointer = htonl(info);
 
-	msg.data = (__u8 *) skb->ipv6_hdr;
+	msg.data = skb->nh.raw;
 	msg.csum = 0;
 	msg.daddr = &hdr->saddr;
         /*
@@ -271,8 +272,8 @@
 
 static void icmpv6_echo_reply(struct sk_buff *skb)
 {
-	struct sock *sk = (struct sock *) icmpv6_socket.data;
-	struct ipv6hdr *hdr = skb->ipv6_hdr;
+	struct sock *sk = icmpv6_socket->sk;
+	struct ipv6hdr *hdr = skb->nh.ipv6h;
 	struct icmpv6hdr *icmph = (struct icmpv6hdr *) skb->h.raw;
 	struct in6_addr *saddr;
 	struct icmpv6_msg msg;
@@ -493,21 +494,27 @@
 	return 0;
 }
 
-void icmpv6_init(struct proto_ops *ops)
+void icmpv6_init(struct net_proto_family *ops)
 {
 	struct sock *sk;
 	int err;
 
-	icmpv6_socket.type=SOCK_RAW;
-	icmpv6_socket.ops=ops;
+	icmpv6_inode.i_mode = S_IFSOCK;
+	icmpv6_inode.i_sock = 1;
+	icmpv6_inode.i_uid = 0;
+	icmpv6_inode.i_gid = 0;
+
+	icmpv6_socket->inode = &icmpv6_inode;
+	icmpv6_socket->state = SS_UNCONNECTED;
+	icmpv6_socket->type=SOCK_RAW;
 
-	if((err=ops->create(&icmpv6_socket, IPPROTO_ICMPV6))<0)
+	if((err=ops->create(icmpv6_socket, IPPROTO_ICMPV6))<0)
 		printk(KERN_DEBUG 
 		       "Failed to create the ICMP control socket.\n");
 
 	MOD_DEC_USE_COUNT;
 
-	sk = icmpv6_socket.data;
+	sk = icmpv6_socket->sk;
 	sk->allocation = GFP_ATOMIC;
 	sk->num = 256;			/* Don't receive any data */
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/ipv6_input.c linux/net/ipv6/ipv6_input.c
--- v2.1.14/linux/net/ipv6/ipv6_input.c	Sun Nov 10 20:12:30 1996
+++ linux/net/ipv6/ipv6_input.c	Thu Dec 12 16:54:25 1996
@@ -133,7 +133,7 @@
 			if (curr->type==255)
 			{ 
 				/* unkown type */
-				pos= (__u8 *) skb->h.raw - (__u8 *) skb->ipv6_hdr;
+				pos= (__u8 *) skb->h.raw - (__u8 *) skb->nh.raw;
 				/* I think this is correct please check - IPM */
 
 				switch ((hdr->type & 0xC0) >> 6) {
@@ -154,7 +154,7 @@
 
 					case 3: /* Send ICMP if not a multicast address 
 						   and drop packet */
-						if (!(ipv6_addr_type(&(skb->ipv6_hdr->daddr)) & IPV6_ADDR_MULTICAST) )
+						if (!(ipv6_addr_type(&(skb->nh.ipv6h->daddr)) & IPV6_ADDR_MULTICAST) )
 							icmpv6_send(skb, ICMPV6_PARAMETER_PROB, 2, pos, dev);
 						kfree_skb(skb, FREE_READ);
 						return 0;
@@ -192,7 +192,7 @@
 	struct raw6_opt *opt;
 
 	opt = &sk->tp_pinfo.tp_raw;
-	icmph = (struct icmpv6hdr *) (skb->ipv6_hdr + 1);
+	icmph = (struct icmpv6hdr *) (skb->nh.ipv6h + 1);
 	return test_bit(icmph->type, &opt->filter);
 }
 
@@ -258,7 +258,7 @@
 int ipv6_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
 	struct inet6_ifaddr	*ifp;
-	struct ipv6_options	*opt = (struct ipv6_options *) skb->proto_priv;
+	struct ipv6_options	*opt = (struct ipv6_options *) skb->cb;
 	struct ipv6hdr		*hdr;
 	u8			hash;
 	u8			addr_type;
@@ -269,7 +269,8 @@
 	__u8			*nhptr;
 	int			pkt_len;
 
-	hdr = skb->ipv6_hdr = (struct ipv6hdr *) skb->h.raw;
+	hdr = skb->nh.ipv6h;
+	skb->h.raw = (__u8*)hdr;
 
 	if (skb->len < sizeof(struct ipv6hdr) || hdr->version != 6)
 	{
@@ -342,7 +343,7 @@
 					if ((nexthdr = hdrt->func(&skb, dev, nhptr, opt)))
 					{
 						nhptr = skb->h.raw;
-						hdr = skb->ipv6_hdr;
+						hdr = skb->nh.ipv6h;
 						continue;
 					}
 					return 0;
@@ -401,7 +402,6 @@
 		if (!found)
 		{
 			printk(KERN_DEBUG "proto not found %d\n", nexthdr);
-			skb->sk = NULL;
 			kfree_skb(skb, FREE_READ);
 		}
 			
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/ipv6_output.c linux/net/ipv6/ipv6_output.c
--- v2.1.14/linux/net/ipv6/ipv6_output.c	Fri Nov 15 23:49:12 1996
+++ linux/net/ipv6/ipv6_output.c	Thu Dec 12 16:54:25 1996
@@ -55,23 +55,14 @@
 	skb->arp = 1;
 	skb->nexthop = neigh;
 
+	skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
 
 	if (dev->hard_header_len)
 	{
-		skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
-
-		if (neigh && (neigh->flags & NCF_HHVALID))
-		{
-			/*
-			 *	Cached hardware header
-			 */
-
-			memcpy(skb_push(skb, dev->hard_header_len),
-			       neigh->hh_data, dev->hard_header_len);
-
-			return dev->hard_header_len;
-		}
 
+		/*
+		 *	FIXME: use cached hardware header if availiable
+		 */
 		if (dev->hard_header)
 		{
 			mac = dev->hard_header(skb, dev, ETH_P_IPV6, 
@@ -91,6 +82,7 @@
 			hdrlen = dev->hard_header_len;
 	}
 
+	skb->mac.raw = skb->data;
 	return hdrlen;
 }
 
@@ -103,7 +95,7 @@
 	skb->nexthop = neigh;
 	skb->arp = 1;
 
-	skb_pull(skb, (unsigned char *) skb->ipv6_hdr - skb->data);
+	skb_pull(skb, (unsigned char *) skb->nh.ipv6h - skb->data);
 
 	/*
 	 *	neighbour cache should have the ether address
@@ -112,16 +104,9 @@
 
 	if (dev->hard_header)
 	{
-		if (neigh && (neigh->flags & NCF_HHVALID))
-		{
-			/*
-			 *	Cached hardware header
-			 */
-
-			memcpy(skb_push(skb, dev->hard_header_len),
-			       neigh->hh_data, dev->hard_header_len);
-			return;
-		}
+		/*
+		 *	FIXME: use cached hardware header if availiable
+		 */
 
 		mac = dev->hard_header(skb, dev, ETH_P_IPV6, 
 				       NULL, NULL, len);
@@ -132,6 +117,7 @@
 		}
 
 	}
+	skb->mac.raw = skb->data;
 }
 
 void default_output_method(struct sk_buff *skb, struct rt6_info *rt)
@@ -146,14 +132,7 @@
 		 *	otherwise use NORMAL
 		 */
 
-		if (sk != NULL)
-		{
-			dev_queue_xmit(skb, dev, sk->priority);
-		}
-		else
-		{
-			dev_queue_xmit(skb, dev, SOPRI_NORMAL);
-		}
+		dev_queue_xmit(skb);
 	}
 	else
 	{
@@ -193,12 +172,10 @@
 		rt_flags |= RTI_DEVRT;
 	}
 
-	if (skb->localroute)
-	{
+	if (sk && sk->localroute)
 		rt_flags |= RTI_GATEWAY;
-	}
 
-	hdr = skb->ipv6_hdr;
+	hdr = skb->nh.ipv6h;
 	
 
 	if (sk)
@@ -250,9 +227,7 @@
 	 *	Link Layer headers
 	 */
 
-	skb->sk = sk;
 	skb->protocol = __constant_htons(ETH_P_IPV6);
-	skb->free = 1;
 	skb->dev = dev;
 	
 	ipv6_redo_mac_hdr(skb, dc->dc_nexthop, seg_len);
@@ -344,7 +319,7 @@
 
 	
 	hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
-	skb->ipv6_hdr = hdr;
+	skb->nh.ipv6h = hdr;
 	
 	hdr->version  = 6;
 	hdr->priority = np->priority & 0x0f;
@@ -375,10 +350,8 @@
 	struct ipv6hdr *hdr;
 	u32 seg_len;
 
-	hdr = skb->ipv6_hdr;
-	skb->sk = sk;
+	hdr = skb->nh.ipv6h;
 	skb->protocol = __constant_htons(ETH_P_IPV6);
-	skb->free=1;
 
 	seg_len = skb->tail - ((unsigned char *) hdr);
 
@@ -406,14 +379,7 @@
 		 *	otherwise use NORMAL
 		 */
 
-		if (sk != NULL)
-		{
-			dev_queue_xmit(skb, dev, sk->priority);
-		}
-		else
-		{
-			dev_queue_xmit(skb, dev, SOPRI_NORMAL);
-		}
+		dev_queue_xmit(skb);
 	}
 	else
 	{
@@ -588,20 +554,18 @@
 
 		skb->dev=dev;
 		skb->protocol = htons(ETH_P_IPV6);
-		skb->free=1;
 		skb->when=jiffies;
-		skb->sk=sk;
 		skb->arp=0;
 
 		/* build the mac header... */
 		ipv6_build_mac_header(skb, dev, neigh, pktlength);
 
 		hdr = (struct ipv6hdr *) skb->tail;
+		skb->nh.ipv6h = hdr;
 
 		if (!sk->ip_hdrincl)
 		{
 			skb_put(skb, sizeof(struct ipv6hdr));
-			skb->ipv6_hdr = hdr;
 
 			hdr->version = 6;
 			hdr->priority = np->priority;
@@ -728,9 +692,7 @@
 
 		last_skb->dev=dev;
 		last_skb->protocol = htons(ETH_P_IPV6);
-		last_skb->free=1;
 		last_skb->when=jiffies;
-		last_skb->sk=sk;
 		last_skb->arp=0;
 
 		/* 
@@ -741,7 +703,7 @@
 
 		hdr = (struct ipv6hdr *) skb_put(last_skb, 
 						 sizeof(struct ipv6hdr));
-		last_skb->ipv6_hdr = hdr;
+		last_skb->nh.ipv6h = hdr;
 
 		hdr->version = 6;
 		hdr->priority = np->priority;
@@ -872,7 +834,7 @@
 	int size;
 	int pmtu;
 
-	if (skb->ipv6_hdr->hop_limit <= 1)
+	if (skb->nh.ipv6h->hop_limit <= 1)
 	{
 		icmpv6_send(skb, ICMPV6_TIME_EXCEEDED, ICMPV6_EXC_HOPLIMIT,
 			    0, dev);
@@ -881,9 +843,9 @@
 		return;
 	}
 
-	skb->ipv6_hdr->hop_limit--;
+	skb->nh.ipv6h->hop_limit--;
 
-	if (ipv6_addr_type(&skb->ipv6_hdr->saddr) & IPV6_ADDR_LINKLOCAL)
+	if (ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)
 	{
 		printk(KERN_DEBUG "ipv6_forward: link local source addr\n");
 		icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOT_NEIGHBOUR,
@@ -899,7 +861,7 @@
 		rt_flags |= RTF_GATEWAY;
 	}
 
-	dest = ipv6_dst_route(&skb->ipv6_hdr->daddr, NULL, rt_flags);
+	dest = ipv6_dst_route(&skb->nh.ipv6h->daddr, NULL, rt_flags);
 
 	if (dest == NULL)
 	{
@@ -922,7 +884,8 @@
 	    !(flags & IP6_FW_SRCRT))
 	{
 		struct in6_addr *target = NULL;
-
+		struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+		
 		/* 
 		 *	outgoing device equal to incoming device
 		 *	send a redirect
@@ -930,11 +893,11 @@
 		
 		if ((dest->dc_flags & RTF_GATEWAY))
 		{
-			target = &neigh->addr;
+			target = &ndn->ndn_addr;
 		}
 		else
 		{
-			target = &skb->ipv6_hdr->daddr;
+			target = &skb->nh.ipv6h->daddr;
 		}
 
 		ndisc_send_redirect(skb, neigh, target);
@@ -942,7 +905,7 @@
 
 	pmtu = neigh->dev->mtu;
 
-	size = sizeof(struct ipv6hdr) + ntohs(skb->ipv6_hdr->payload_len);
+	size = sizeof(struct ipv6hdr) + ntohs(skb->nh.ipv6h->payload_len);
 	
 	if (size > pmtu)
 	{
@@ -968,25 +931,25 @@
 		skb_reserve(buff, (neigh->dev->hard_header_len + 15) & ~15);
 
 		buff->protocol = __constant_htons(ETH_P_IPV6);
-		buff->free = 1;
 		buff->h.raw = skb_put(buff, size);
 
-		memcpy(buff->h.raw, skb->ipv6_hdr, size);
-		buff->ipv6_hdr = (struct ipv6hdr *) buff->h.raw;
+		memcpy(buff->h.raw, skb->nh.ipv6h, size);
+		buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
 		kfree_skb(skb, FREE_READ);
 		skb = buff;
 	}
 
 	ipv6_redo_mac_hdr(skb, neigh, size);
 
-	priority = skb->ipv6_hdr->priority;
+	priority = skb->nh.ipv6h->priority;
 
 	priority = (priority & 0x7) >> 1;
 	priority = pri_values[priority];
 
 	if (dev->flags & IFF_UP)
 	{
-		dev_queue_xmit(skb, neigh->dev, priority);
+		skb->dev = neigh->dev;
+		dev_queue_xmit(skb);
 	}
 	else
 	{
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/ipv6_route.c linux/net/ipv6/ipv6_route.c
--- v2.1.14/linux/net/ipv6/ipv6_route.c	Thu Dec 12 17:02:47 1996
+++ linux/net/ipv6/ipv6_route.c	Thu Dec 12 16:54:25 1996
@@ -1233,7 +1233,7 @@
 
 			if (dc->dc_nexthop)
 			{
-				ndisc_dec_neigh(dc->dc_nexthop);
+				neighbour_unlock(dc->dc_nexthop);
 			}
 			
 			if (dc->dc_flags & RTI_DCACHE)
@@ -1406,14 +1406,16 @@
 
 	if (rt->rt_nexthop)
 	{
-		if (ipv6_addr_cmp(&rt->rt_nexthop->addr, target) == 0)
+		struct nd_neigh *ndn = (struct nd_neigh *) rt->rt_nexthop;
+		
+		if (ipv6_addr_cmp(&ndn->ndn_addr, target) == 0)
 		{
-			atomic_inc(&rt->rt_nexthop->refcnt);
+			rt->rt_nexthop = neighbour_clone(rt->rt_nexthop);
 			goto exit;
 		}
 		else
 		{
-			ndisc_dec_neigh(rt->rt_nexthop);
+			neighbour_unlock(rt->rt_nexthop);
 		}
 	}
 	
@@ -1688,8 +1690,6 @@
 	if (skb == NULL)
 		return;
 
-	skb->free = 1;
-	
 	memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg,
 	       sizeof(struct in6_rtmsg));
 	
@@ -1848,8 +1848,11 @@
 		{
 			for (i=0; i<16; i++)
 			{
+				struct nd_neigh *ndn;
+
+				ndn = (struct nd_neigh *) rt->rt_nexthop;
 				sprintf(arg->buffer + arg->len, "%02x",
-					rt->rt_nexthop->addr.s6_addr[i]);
+					ndn->ndn_addr.s6_addr[i]);
 				arg->len += 2;
 			}
 		}
@@ -2011,7 +2014,6 @@
 	if (skb == NULL)
 		return;
 
-	skb->free = 1;
 
 	msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg));
 	
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.14/linux/net/ipv6/ipv6_sockglue.c	Fri Nov 15 23:49:12 1996
+++ linux/net/ipv6/ipv6_sockglue.c	Thu Dec 12 16:54:25 1996
@@ -113,12 +113,13 @@
 				
 				sk->prot = &tcp_prot;
 				tp->af_specific = &ipv4_specific;
+				sk->socket->ops = &inet_stream_ops;
 			}
 			else
 			{
 				sk->prot = &udp_prot;
+				sk->socket->ops = &inet_dgram_ops;
 			}
-			sk->socket->ops = &inet_proto_ops;
 			retv = 0;
 		}
 		else
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.14/linux/net/ipv6/ndisc.c	Thu Dec 12 17:02:48 1996
+++ linux/net/ipv6/ndisc.c	Thu Dec 12 16:54:25 1996
@@ -23,18 +23,7 @@
  *	Janos Farkas			:	kmalloc failure checks
  */
 
-/*
- *	Interface:
- *
- *	ndisc_lookup will be called from eth.c on dev->(re)build_header
- *
- *	ndisc_rcv
- *	ndisc_validate is called by higher layers when they know a neighbour
- *		       is reachable.
- *
- *	Manages neighbour cache
- *
- */
+#define ND_DEBUG 2
 
 #define __NO_VERSION__
 #include <linux/module.h>
@@ -46,7 +35,8 @@
 #include <linux/sched.h>
 #include <linux/net.h>
 #include <linux/in6.h>
-#include <linux/netdevice.h>
+
+#include <net/neighbour.h>
 #include <linux/if_arp.h>
 #include <linux/ipv6.h>
 #include <linux/icmpv6.h>
@@ -61,23 +51,36 @@
 #include <net/addrconf.h>
 
 
+
 #include <net/checksum.h>
 #include <linux/proc_fs.h>
 
 #define NCACHE_NUM_BUCKETS 32
 
-static struct socket ndisc_socket;
+static struct inode ndisc_inode;
+static struct socket *ndisc_socket=&ndisc_inode.u.socket_i;
 
 unsigned long nd_rand_seed = 152L;
 
 struct ndisc_statistics nd_stats;
 
-static struct neighbour *neighbours[NCACHE_NUM_BUCKETS];
+static struct neigh_table nd_tbl;
+
+unsigned int	ndisc_hash(void *primary_key);
+int			ndisc_eth_resolv(unsigned char *h_dest,
+					 struct device *dev,
+					 struct sk_buff *skb);
+
+static struct neigh_ops nd_neigh_ops = {
+	ETH_P_IPV6,
+	ndisc_hash,
+	ndisc_eth_resolv,
+	NULL
+};
+
 static struct timer_list ndisc_timer;
 static struct timer_list ndisc_gc_timer;
 
-static atomic_t	ndisc_lock = 0;
-
 /*
  *	Protocol variables
  */
@@ -99,18 +102,8 @@
 
 int	nd_gc_staletime			= 3 * RECHABLE_TIME;
 
-static struct neighbour ndisc_insert_queue = {
-	{{{0,}}}, 0, 0, NULL, 0,
-	{0,}, NULL, {0,}, 0, 0, 0, 0, 0,
-	&ndisc_insert_queue,
-	&ndisc_insert_queue
-};
-
-static int ndisc_ins_queue_len = 0;
 
-int  ndisc_event_timer(struct neighbour *neigh);
-
-static void ndisc_bh_insert(void);
+static int  ndisc_event_timer(struct nd_neigh *ndn);
 
 int ipv6_random(void)
 {
@@ -133,15 +126,13 @@
 
 void ndisc_verify_reachability(struct neighbour * neigh);
 
-/*
- *	(inline) support functions
- */
-
-static __inline__ __u32 ndisc_hash(struct in6_addr *addr)
+unsigned int ndisc_hash(void *primary_key)
 {
-        
+        struct in6_addr *addr = (struct in6_addr *) primary_key;
         __u32 hash_val;
         
+	addr = (struct in6_addr *) primary_key;
+	
         hash_val = addr->s6_addr32[2] ^ addr->s6_addr32[3];
 
         hash_val ^= hash_val >> 16;
@@ -149,239 +140,122 @@
         return (hash_val & (NCACHE_NUM_BUCKETS - 1));
 }
 
+static int ndisc_gc_func(struct neighbour *neigh, void *arg);
 
-static __inline__ void ndisc_neigh_queue(struct neighbour *neigh)
-{
-	struct neighbour *next = &ndisc_insert_queue;
-
-	ndisc_ins_queue_len++;
-
-	neigh->prev = next->prev;
-	neigh->prev->next = neigh;
-	next->prev = neigh;
-	neigh->next = next;
-}
-
-static __inline__ struct neighbour * ndisc_dequeue(void)
-{
-	struct neighbour *next = &ndisc_insert_queue;
-	struct neighbour *head;
-
-	ndisc_ins_queue_len--;
-
-	head = next->next;
-
-	if (head == next)
-	{
-		return NULL;
-	}
-
-	head->next->prev = head->prev;
-	next->next = head->next;
-
-	head->next = NULL;
-	head->prev = NULL;
-
-	return head;
-}
-
-static __inline__ void ndisc_release_lock(void)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	ndisc_lock--;
-
-	if (ndisc_lock == 0 && ndisc_ins_queue_len)
-	{
-		ndisc_bh_insert();
-	}
-
-	restore_flags(flags);
-}
-
-static void ndisc_insert_neigh(struct neighbour *neigh)
-{
-
-        struct neighbour * bucket;
-        __u32 hash_val = ndisc_hash(&neigh->addr);
-        
-        bucket = neighbours[hash_val];
-        
-        if (!bucket)
-	{
-                neighbours[hash_val] = neigh;
-                return;
-        }
-
-        for (; bucket->next; bucket = bucket->next)
-		;
-	        
-        bucket->next = neigh;
-	neigh->prev = bucket;
-}
-
-static __inline__ struct neighbour * 
-ndisc_retrieve_neigh(struct device *dev, struct in6_addr *addr)
-{
-
-        struct neighbour * iter;
-        iter = neighbours[ndisc_hash(addr)];
-
-        for (; iter; iter = iter->next)
-	{
-		if (dev == iter->dev && ipv6_addr_cmp(addr, &iter->addr) == 0)
-			return iter;
-	}
-        return NULL;
-}
-
-static void ndisc_unlink_neigh(struct neighbour * neigh) 
-{
-	if (neigh->prev)
-		neigh->prev->next = neigh->next;
-	else
-	{
-		int hash = ndisc_hash(&neigh->addr);
-		neighbours[hash] = neigh->next;
-	}
-
-	if (neigh->next)
-		neigh->next->prev = neigh->prev;
-}
-
-static void ndisc_release_neigh(struct neighbour * neigh)
-{
-	struct sk_buff *skb;
-
-	while((skb=skb_dequeue(&neigh->arp_queue)))
-	{
-		dev_kfree_skb(skb, FREE_WRITE);
-	}
-
-	if (neigh->refcnt == 0)
-	{
-		ndisc_unlink_neigh(neigh);
-		kfree(neigh);
-	}
-}
-
-static void ndisc_bh_insert(void)
-{
-	struct neighbour *neigh;
-
-	while((neigh = ndisc_dequeue()))
-	{
-		ndisc_insert_neigh(neigh);
-	}
-}
-
-
-static void ndisc_garbage_collect(unsigned long arg)
+static void ndisc_periodic_timer(unsigned long arg)
 {
-        struct neighbour * neigh;
 	static unsigned long last_rand = 0;
-        unsigned long now = jiffies;
-	unsigned long flags;
-        int i = 0;
-
-
+	unsigned long now = jiffies;
+	
 	/*
 	 *	periodicly compute ReachableTime from random function
 	 */
-	if (now - last_rand > REACH_RANDOM_INTERVAL)
+	
+	if ((now - last_rand) > REACH_RANDOM_INTERVAL)
 	{
 		last_rand = now;
 		nd_reachable_time = rand_reach_time();
 	}
 
-	save_flags(flags);
-	cli();
+	neigh_table_lock(&nd_tbl);
 
-	if (ndisc_lock)
+	start_bh_atomic();
+	if (nd_tbl.tbl_lock == 1)
 	{
-		restore_flags(flags);
-		ndisc_gc_timer.expires = now + HZ;
-		add_timer(&ndisc_gc_timer);
-		return;
+		ntbl_walk_table(&nd_tbl, ndisc_gc_func, 0, 0, NULL);
 	}
-		
-        for (; i < NCACHE_NUM_BUCKETS; i++)
-                for (neigh = neighbours[i]; neigh;)
-		{
-                        /*
-			 *	Release unused entries
-			 */
-                        if (neigh->refcnt == 0 &&
-			    ((neigh->nud_state == NUD_FAILED) ||
-			     ((neigh->nud_state == NUD_REACHABLE) &&
-			      (neigh->tstamp <= (now - nd_gc_staletime))
-			      )
-			     )
-			    )
-			{
-				struct neighbour *prev;
-				
-				prev = neigh;
-				neigh = neigh->next;
-                                ndisc_release_neigh(prev);
-				continue;
-                        }
-			neigh = neigh->next;
-		}
+	end_bh_atomic();
+	
+	neigh_table_unlock(&nd_tbl);
+	
+	ndisc_gc_timer.expires = now + HZ;
+	ndisc_gc_timer.expires = now + nd_gc_interval;
+	add_timer(&ndisc_gc_timer);
+}
 
-	restore_flags(flags);
+static int ndisc_gc_func(struct neighbour *neigh, void *arg)
+{
+	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+        unsigned long now = jiffies;
 
-        ndisc_gc_timer.expires = now + nd_gc_interval;
-        add_timer(&ndisc_gc_timer);
+	if (ndn->ndn_refcnt == 0 &&
+	    ((ndn->ndn_nud_state == NUD_FAILED) ||
+	     ((ndn->ndn_nud_state == NUD_REACHABLE) &&
+	      (ndn->ndn_tstamp <= (now - nd_gc_staletime))
+	      )
+	     )
+	    )
+	{
+		/*
+		 *	Release unused entries
+		 */
+		
+		return 1;
+	}
+	return 0;
 }
 
-static __inline__ void ndisc_add_timer(struct neighbour *neigh, int timer)
+static __inline__ void ndisc_add_timer(struct nd_neigh *ndn, int timer)
 {
 	unsigned long now = jiffies;
 	unsigned long tval;
 
-	neigh->expires = now + timer;
+	ndn->ndn_expires = now + timer;
 	tval = del_timer(&ndisc_timer);
 
 	if (tval)
 	{
-		tval = min(tval, neigh->expires);
+		tval = min(tval, ndn->ndn_expires);
 	}
 	else
-		tval = neigh->expires;
+		tval = ndn->ndn_expires;
 
 	ndisc_timer.expires = tval;
 	add_timer(&ndisc_timer);
 }
         
-static void ndisc_del_timer(struct neighbour *neigh)
+static void ndisc_del_timer(struct nd_neigh *ndn)
 {
 	unsigned long tval;
 
-	if (!(neigh->nud_state & NUD_IN_TIMER))
+	if (!(ndn->ndn_nud_state & NUD_IN_TIMER))
 		return;
 
 	tval = del_timer(&ndisc_timer);
 	
-	if (tval == neigh->expires)
+	if (tval == ndn->ndn_expires)
 	{
 		int i;
 		
 		tval = ~0UL;
 
+		neigh_table_lock(&nd_tbl);
+		
 		/* need to search the entire neighbour cache */
-		for (i=0; i < NCACHE_NUM_BUCKETS; i++)
+		for (i=0; i < nd_tbl.tbl_size; i++)
 		{
-			for (neigh = neighbours[i]; neigh; neigh=neigh->next)
-				if (neigh->nud_state & NUD_IN_TIMER)
+			struct neighbour *neigh, *head;
+			head = nd_tbl.hash_buckets[i];
+				
+			if ((neigh = head) == NULL)
+				continue;
+			
+			do
+			{
+				struct nd_neigh *n;
+
+				n = (struct nd_neigh *) neigh;
+
+				if (n->ndn_nud_state & NUD_IN_TIMER)
 				{
-					tval = min(tval, neigh->expires);
+					tval = min(tval, n->ndn_expires);
 				}
+				
+				neigh = neigh->next;
+				
+			} while (neigh != head);
 		}
-
+		neigh_table_unlock(&nd_tbl);
 	}
 
 	if (tval == ~(0UL))
@@ -391,53 +265,69 @@
 	add_timer(&ndisc_timer);
 }
 
-static struct neighbour * ndisc_new_neigh(struct device *dev,
-					  struct in6_addr *addr)
+static int ndisc_forced_gc(struct neighbour *neigh, void *arg)
 {
-	struct neighbour *neigh;
-	unsigned long flags;
+	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 
-	neigh = (struct neighbour *) kmalloc(sizeof(struct neighbour),
-					     GFP_ATOMIC);
-
-	if (neigh == NULL)
+	if (ndn->ndn_refcnt == 0)
 	{
-		printk(KERN_DEBUG "ndisc: kmalloc failure\n");
-		return NULL;
+		if (ndn->ndn_nud_state & NUD_IN_TIMER)
+		{
+			ndisc_del_timer(ndn);
+		}
+		
+		return 1;
 	}
+	return 0;
+}
 
-	nd_stats.allocs++;
+static struct nd_neigh * ndisc_new_neigh(struct device *dev,
+					 struct in6_addr *addr)
+{
+	struct nd_neigh *ndn;
 
-	memset(neigh, 0, sizeof (struct neighbour));
-	skb_queue_head_init(&neigh->arp_queue);
+	ndn = (struct nd_neigh *) neigh_alloc(sizeof(struct nd_neigh),
+					      GFP_ATOMIC);
+	
+	if (ndn == NULL)
+	{
 
-	ipv6_addr_copy(&neigh->addr, addr);
-	neigh->len = 128;
-	neigh->type = ipv6_addr_type(addr);
-	neigh->dev = dev;
-	neigh->tstamp = jiffies;
+#if ND_DEBUG >= 2
+		printk(KERN_DEBUG "neigh_alloc: out of memory\n");
+#endif
 
-	if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT)
-	{
-		neigh->flags |= NCF_NOARP;
+		start_bh_atomic();
+		if (nd_tbl.tbl_lock == 1)
+		{
+
+#if ND_DEBUG >= 2
+			printk(KERN_DEBUG "ndisc_alloc: forcing gc\n");
+#endif
+			ntbl_walk_table(&nd_tbl, ndisc_forced_gc, 0, 0, NULL);
+		}
+		
+		end_bh_atomic();
+#if ND_DEBUG >= 1
+		printk(KERN_DEBUG "ndisc_alloc failed\n");
+#endif
+		return NULL;
 	}
 
-	save_flags(flags);
-	cli();
+	nd_stats.allocs++;
+
+	ipv6_addr_copy(&ndn->ndn_addr, addr);
+	ndn->ndn_plen = 128;
+	ndn->ndn_type = ipv6_addr_type(addr);
+	ndn->ndn_dev = dev;
+	ndn->ndn_tstamp = jiffies;
 
-	if (ndisc_lock == 0)
-	{
-		/* Add to the cache. */
-		ndisc_insert_neigh(neigh);
-	}
-	else
+	if (dev->type == ARPHRD_LOOPBACK || dev->type == ARPHRD_SIT)
 	{
-		ndisc_neigh_queue(neigh);
+		ndn->ndn_flags |= NCF_NOARP;
 	}
 
-	restore_flags(flags);
-
-	return neigh;
+	neigh_insert(&nd_tbl, (struct neighbour *) ndn);
+	return ndn;
 }
 
 /*
@@ -448,7 +338,7 @@
 
 struct neighbour * ndisc_get_neigh(struct device *dev, struct in6_addr *addr)
 {
-	struct neighbour *neigh;
+	struct nd_neigh *neigh;
 
 	/*
 	 *	neighbour cache:
@@ -457,24 +347,28 @@
 
 	if (dev == NULL)
 	{
+#if ND_DEBUG >= 1
 		printk(KERN_DEBUG "ncache_get_neigh: NULL device\n");
+#endif
 		return NULL;
 	}
 
-	atomic_inc(&ndisc_lock);
+	neigh_table_lock(&nd_tbl);
 
-        neigh = ndisc_retrieve_neigh(dev, addr);
-
-	ndisc_release_lock();
+        neigh = (struct nd_neigh *) neigh_lookup(&nd_tbl, (void *) addr,
+						 sizeof(struct in6_addr), dev);
 
+	
 	if (neigh == NULL)
 	{
 		neigh = ndisc_new_neigh(dev, addr);
 	}
 
-	atomic_inc(&neigh->refcnt);
+	neigh_table_unlock(&nd_tbl);
+
+	atomic_inc(&neigh->ndn_refcnt);
 	
-	return neigh;	
+	return (struct neighbour *) neigh;	
 }
 
 /*
@@ -486,66 +380,64 @@
 int ndisc_eth_resolv(unsigned char *h_dest, struct device *dev,
 		     struct sk_buff *skb)
 {
-	struct neighbour *neigh;
-
-	neigh = skb->nexthop;
+	struct nd_neigh *ndn;
 
-	if (neigh == NULL)
+	ndn = (struct nd_neigh *) skb->nexthop;
+	
+	if (ndn == NULL)
 	{
+		struct in6_addr *daddr;
 		int addr_type;
 
-		addr_type = ipv6_addr_type(&skb->ipv6_hdr->daddr);
+		daddr = &skb->nh.ipv6h->daddr;
+		
+		addr_type = ipv6_addr_type(daddr);
 		
 		if (addr_type & IPV6_ADDR_MULTICAST)
 		{
-			ipv6_mc_map(&skb->ipv6_hdr->daddr, h_dest);
+			ipv6_mc_map(daddr, h_dest);
 			return 0;
 		}
 
+#if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_eth_resolv: nexthop is NULL\n");
+#endif
 		goto discard;
 	}
 
 	if (skb->pkt_type == PACKET_NDISC)
 		goto ndisc_pkt;
 	
-	switch (neigh->nud_state) {	
+	switch (ndn->ndn_nud_state) {	
 	case NUD_FAILED:
 	case NUD_NONE:
-		ndisc_event_send(neigh, skb);
+		ndisc_event_send((struct neighbour *)ndn, skb);
 
 	case NUD_INCOMPLETE:			
-		if (skb_queue_len(&neigh->arp_queue) >= NDISC_QUEUE_LEN)
+		if (skb_queue_len(&ndn->neigh.arp_queue) >= NDISC_QUEUE_LEN)
 		{
 			struct sk_buff *buff;
 			
-			buff = neigh->arp_queue.prev;
+			buff = ndn->neigh.arp_queue.prev;
 			skb_unlink(buff);
 			dev_kfree_skb(buff, FREE_WRITE);
 		}
-		skb_queue_head(&neigh->arp_queue, skb);
+		skb_queue_head(&ndn->neigh.arp_queue, skb);
 		return 1;
 	default:
-		ndisc_event_send(neigh, skb);
+		ndisc_event_send((struct neighbour *)ndn, skb);
 	}
 
   ndisc_pkt:
-
-	if (neigh->h_dest == NULL)
-	{
-		printk(KERN_DEBUG "neigh->h_dest is NULL\n");
-		goto discard;
-	}
-
-	memcpy(h_dest, neigh->h_dest, dev->addr_len);
-
-	if ((neigh->flags & NCF_HHVALID) == 0)
+	
+	if ((ndn->ndn_flags & NTF_COMPLETE) == 0)
 	{
-		/*
-		 * copy header to hh_data and move h_dest pointer
-		 * this is strictly media dependent.
-		 */
+#if ND_DEBUG >=1
+		/* This shouldn't happen */
+		printk(KERN_DEBUG "ND: using incomplete entry\n");
+#endif
 	}
+	memcpy(h_dest, ndn->ndn_ha, dev->addr_len);
 	return 0;
 
   discard:
@@ -555,14 +447,16 @@
 }
 
 
-/* Send the actual Neighbour Advertisement */
+/*
+ *	Send a Neighbour Advertisement
+ */
 
-void ndisc_send_na(struct device *dev, struct neighbour *neigh,
+void ndisc_send_na(struct device *dev, struct nd_neigh *ndn,
 		   struct in6_addr *daddr,
 		   struct in6_addr *solicited_addr,
 		   int router, int solicited, int override, int inc_opt) 
 {
-        struct sock *sk = (struct sock *)ndisc_socket.data;
+        struct sock *sk = ndisc_socket->sk;
         struct nd_msg *msg;
         int len, opt_len;
         struct sk_buff *skb;
@@ -581,13 +475,10 @@
 	if (skb == NULL)
 	{
 		printk(KERN_DEBUG "send_na: alloc skb failed\n");
-		return;
 	}
 
-	skb->free=1;
-
-	if (ipv6_bld_hdr_2(sk, skb, dev, neigh, solicited_addr, daddr,
-			   IPPROTO_ICMPV6, len) < 0)
+	if (ipv6_bld_hdr_2(sk, skb, dev, (struct neighbour *) ndn, 
+			   solicited_addr, daddr, IPPROTO_ICMPV6, len) < 0)
         {
 		kfree_skb(skb, FREE_WRITE);
 		printk(KERN_DEBUG 
@@ -638,7 +529,7 @@
 		   struct in6_addr *solicit,
 		   struct in6_addr *daddr, struct in6_addr *saddr) 
 {
-        struct sock *sk = (struct sock *) ndisc_socket.data;
+        struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct nd_msg *msg;
         int len, opt_len;
@@ -649,14 +540,15 @@
 	len = sizeof(struct icmpv6hdr) + sizeof(struct in6_addr) +
                 (opt_len << 3);
 
-        skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
+	skb = sock_alloc_send_skb(sk, MAX_HEADER + len, 0, 0, &err);
 	if (skb == NULL)
 	{
+#if ND_DEBUG >= 1
 		printk(KERN_DEBUG "send_ns: alloc skb failed\n");
+#endif
 		return;
 	}
 
-	skb->free=1;
 	skb->pkt_type = PACKET_NDISC;
 
 	if (saddr == NULL)
@@ -672,38 +564,38 @@
 		}
 	}
 
-        if(ipv6_addr_type(daddr) == IPV6_ADDR_MULTICAST)
+	if(ipv6_addr_type(daddr) == IPV6_ADDR_MULTICAST)
 	{
-                nd_stats.snt_probes_mcast++;
+		nd_stats.snt_probes_mcast++;
 	}
         else
 	{
-                nd_stats.snt_probes_ucast++;
+		nd_stats.snt_probes_ucast++;
 	}
 
-        if (ipv6_bld_hdr_2(sk, skb, dev, neigh, saddr, daddr, IPPROTO_ICMPV6,
+	if (ipv6_bld_hdr_2(sk, skb, dev, neigh, saddr, daddr, IPPROTO_ICMPV6,
 			   len) < 0 )
 	{
-                kfree_skb(skb, FREE_WRITE);
-                printk(KERN_DEBUG
-                       "ndisc_send_ns: ipv6_build_header returned < 0\n");
-                return;
-        }
+		kfree_skb(skb, FREE_WRITE);
+		printk(KERN_DEBUG
+		       "ndisc_send_ns: ipv6_build_header returned < 0\n");
+		return;
+	}
 	
-        msg = (struct nd_msg *)skb_put(skb, len);
-        msg->icmph.type = NDISC_NEIGHBOUR_SOLICITATION;
-        msg->icmph.code = 0;
-        msg->icmph.checksum = 0;
-        msg->icmph.icmp6_unused = 0;
-
-        /* Set the target address. */
-        ipv6_addr_copy(&msg->target, solicit);
+	msg = (struct nd_msg *)skb_put(skb, len);
+	msg->icmph.type = NDISC_NEIGHBOUR_SOLICITATION;
+	msg->icmph.code = 0;
+	msg->icmph.checksum = 0;
+	msg->icmph.icmp6_unused = 0;
+
+	/* Set the target address. */
+	ipv6_addr_copy(&msg->target, solicit);
+
+	/* Set the source link-layer address option. */
+	msg->opt.opt_type = ND_OPT_SOURCE_LL_ADDR;
+	msg->opt.opt_len = opt_len;
 
-        /* Set the source link-layer address option. */
-        msg->opt.opt_type = ND_OPT_SOURCE_LL_ADDR;
-        msg->opt.opt_len = opt_len;
-
-        memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
+	memcpy(msg->opt.link_addr, dev->dev_addr, dev->addr_len);
 
 	if ((opt_len << 3) - (2 + dev->addr_len))
 	{
@@ -712,7 +604,7 @@
 	}
 
 	/* checksum */
-	msg->icmph.checksum = csum_ipv6_magic(&skb->ipv6_hdr->saddr,
+	msg->icmph.checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr,
 					      daddr, len, 
 					      IPPROTO_ICMPV6,
 					      csum_partial((__u8 *) msg, 
@@ -724,7 +616,7 @@
 void ndisc_send_rs(struct device *dev, struct in6_addr *saddr,
 		   struct in6_addr *daddr)
 {
-	struct sock *sk = (struct sock *) ndisc_socket.data;
+	struct sock *sk = ndisc_socket->sk;
         struct sk_buff *skb;
         struct icmpv6hdr *hdr;
 	__u8 * opt;
@@ -739,11 +631,8 @@
 	if (skb == NULL)
 	{
 		printk(KERN_DEBUG "send_ns: alloc skb failed\n");
-		return;
 	}
 
-	skb->free=1;
-
         if (ipv6_bld_hdr_2(sk, skb, dev, NULL, saddr, daddr, IPPROTO_ICMPV6,
 			   len) < 0 )
 	{
@@ -774,7 +663,7 @@
 	}
 
 	/* checksum */
-	hdr->checksum = csum_ipv6_magic(&skb->ipv6_hdr->saddr, daddr, len,
+	hdr->checksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len,
 					IPPROTO_ICMPV6,
 					csum_partial((__u8 *) hdr, len, 0));
 
@@ -783,8 +672,8 @@
 }
 		   
 
-static int ndisc_store_hwaddr(struct device *dev, __u8 *opt, int opt_len,
-			      __u8 *h_addr, int option)
+static int ndisc_store_hwaddr(struct nd_neigh *ndn, __u8 *opt, int opt_len,
+			      int option)
 {
 	while (*opt != option && opt_len)
 	{
@@ -804,7 +693,7 @@
 
 	if (*opt == option)
 	{
-		memcpy(h_addr, opt + 2, dev->addr_len); 
+		memcpy(ndn->neigh.ha, opt + 2, ndn->ndn_dev->addr_len); 
 		return 0;
 	}
 
@@ -816,51 +705,41 @@
 static void ndisc_timer_handler(unsigned long arg) 
 {
 	unsigned long now = jiffies;
-        struct neighbour * neigh;
 	unsigned long ntimer = ~0UL;
         int i;
 
-	atomic_inc(&ndisc_lock);
-
-	for (i=0; i < NCACHE_NUM_BUCKETS; i++)
+	neigh_table_lock(&nd_tbl);
+	
+	for (i=0; i < nd_tbl.tbl_size; i++)
 	{
-                for (neigh = neighbours[i]; neigh;)
+		struct nd_neigh *ndn, *head;
+
+		head = (struct nd_neigh *) nd_tbl.hash_buckets[i];
+
+		if ((ndn = head) == NULL)
+			continue;
+		
+		do
 		{
-                        if (neigh->nud_state & NUD_IN_TIMER)
+                        if (ndn->ndn_nud_state & NUD_IN_TIMER)
 			{
-				int time;
+				long time;
 
-				if (neigh->expires <= now)
+				if ((ndn->ndn_expires - now) <= 0)
 				{
-					time = ndisc_event_timer(neigh);
+					time = ndisc_event_timer(ndn);
 				}
 				else
-					time = neigh->expires - now;
-
-				if (time == 0)
+					time = ndn->ndn_expires - now;
+				
+				if (time)
 				{
-					unsigned long flags;
-
-					save_flags(flags);
-					cli();
-
-					if (ndisc_lock == 1)
-					{
-						struct neighbour *old = neigh;
-
-						neigh = neigh->next;
-						ndisc_release_neigh(old);
-						restore_flags(flags);
-						continue;
-					}
-
-					restore_flags(flags);
+					ntimer = min(ntimer, time);
 				}
-
-				ntimer = min(ntimer, time);
 			}
-			neigh = neigh->next;
-		}
+			ndn = (struct nd_neigh *) ndn->neigh.next;
+
+		} while (ndn != head);
 	}
 
 	if (ntimer != (~0UL))
@@ -868,11 +747,12 @@
 		ndisc_timer.expires = jiffies + ntimer;
 		add_timer(&ndisc_timer);
 	}
-	ndisc_release_lock();
+	
+	neigh_table_unlock(&nd_tbl);
 }
 
 
-int ndisc_event_timer(struct neighbour *neigh)
+static int ndisc_event_timer(struct nd_neigh *ndn)
 {
 	struct in6_addr *daddr;
 	struct in6_addr *target;
@@ -880,66 +760,67 @@
 	struct device *dev;
 	int max_probes;
 
-	if (neigh->nud_state == NUD_DELAY)
+	if (ndn->ndn_nud_state == NUD_DELAY)
 	{
-		neigh->nud_state = NUD_PROBE;
+		ndn->ndn_nud_state = NUD_PROBE;
 	}
 
-	max_probes = (neigh->nud_state == NUD_PROBE ? nd_max_unicast_solicit:
+	max_probes = (ndn->ndn_nud_state == NUD_PROBE ? nd_max_unicast_solicit:
 		      nd_max_multicast_solicit);
 
-	if (neigh->probes == max_probes)
+	if (ndn->ndn_probes == max_probes)
 	{
 		struct sk_buff *skb;
 
-		neigh->nud_state = NUD_FAILED;
-		neigh->flags |= NCF_INVALID;
+		ndn->ndn_nud_state = NUD_FAILED;
+		ndn->ndn_flags &= ~NTF_COMPLETE;
 		nd_stats.res_failed++;
 
-		while((skb=skb_dequeue(&neigh->arp_queue)))
+		while((skb=skb_dequeue(&ndn->neigh.arp_queue)))
 		{
 			/*
 			 *	"The sender MUST return an ICMP
 			 *	 destination unreachable"
 			 */
 			icmpv6_send(skb, ICMPV6_DEST_UNREACH,
-				    ICMPV6_ADDR_UNREACH, 0, neigh->dev);
+				    ICMPV6_ADDR_UNREACH, 0, ndn->ndn_dev);
 
 			dev_kfree_skb(skb, FREE_WRITE);
 		}
 		return 0;
 	}
-		
-	neigh->probes++;
 
-	dev = neigh->dev;
-	target = &neigh->addr;
+	ndn->ndn_probes++;
 
-	if (neigh->nud_state == NUD_INCOMPLETE)
+	dev = ndn->ndn_dev;
+	target = &ndn->ndn_addr;
+
+	if (ndn->ndn_nud_state == NUD_INCOMPLETE)
 	{
-		addrconf_addr_solict_mult(&neigh->addr, &mcaddr);
-		daddr = &mcaddr;		
-		neigh = NULL;
+		addrconf_addr_solict_mult(&ndn->ndn_addr, &mcaddr);
+		daddr = &mcaddr;
+		ndn = NULL;
 	}
 	else
 	{
-		daddr = &neigh->addr;		
+		daddr = &ndn->ndn_addr;
 	}
 
-	ndisc_send_ns(dev, neigh, target, daddr, NULL);
+	ndisc_send_ns(dev, (struct neighbour *) ndn, target, daddr, NULL);
 
-	return nd_retrans_timer;	
+	return nd_retrans_timer;
 }
 
 void ndisc_event_send(struct neighbour *neigh, struct sk_buff *skb)
 {
-	unsigned long now = jiffies;
+	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 	struct in6_addr daddr;
+	unsigned long now = jiffies;
 	struct in6_addr *saddr = NULL;
-
-	switch (neigh->nud_state) {
+	
+	switch (ndn->ndn_nud_state) {
 	case NUD_FAILED:
-		neigh->probes = 0;
+		ndn->ndn_probes = 0;
 	case NUD_NONE:
 
 		if (skb && !skb->stamp.tv_sec)
@@ -949,42 +830,42 @@
 			 *	originating the skb or forwarding it.
 			 *	(it is set on netif_rx)
 			 */
-			saddr = &skb->ipv6_hdr->saddr;
+			saddr = &skb->nh.ipv6h->saddr;
 		}
 
-		neigh->nud_state = NUD_INCOMPLETE;
-		addrconf_addr_solict_mult(&neigh->addr, &daddr);
-		ndisc_send_ns(neigh->dev, NULL, &neigh->addr, &daddr, saddr);
-		ndisc_add_timer(neigh, nd_retrans_timer);
+		ndn->ndn_nud_state = NUD_INCOMPLETE;
+		addrconf_addr_solict_mult(&ndn->ndn_addr, &daddr);
+		ndisc_send_ns(ndn->ndn_dev, NULL, &ndn->ndn_addr, &daddr,
+			      saddr);
+		ndisc_add_timer(ndn, nd_retrans_timer);
 
 		break;
 
 	case NUD_REACHABLE:
-		if (now - neigh->tstamp < nd_reachable_time)
+		if ((now - ndn->ndn_tstamp) < nd_reachable_time)
 			break;
 
 	case NUD_STALE:
-		neigh->nud_state = NUD_DELAY;
-		ndisc_add_timer(neigh, nd_delay_first_probe);
+		ndn->ndn_nud_state = NUD_DELAY;
+		ndisc_add_timer(ndn, nd_delay_first_probe);
 	}
 }
 
 /*
  *	Received a neighbour announce
  */
-void ndisc_event_na(struct neighbour *neigh, unsigned char * opt, int opt_len,
+void ndisc_event_na(struct nd_neigh *ndn, unsigned char *opt, int opt_len,
 		    int solicited, int override)
 {
 	struct sk_buff *skb;
 
-	if (neigh->nud_state == NUD_NONE)
+	if (ndn->ndn_nud_state == NUD_NONE)
 	{
-		neigh->nud_state = NUD_INCOMPLETE;
+		ndn->ndn_nud_state = NUD_INCOMPLETE;
 	}
 
-	if (neigh->nud_state == NUD_INCOMPLETE || override)
+	if (ndn->ndn_nud_state == NUD_INCOMPLETE || override)
 	{
-
 		if (opt_len == 0)
 		{
 			printk(KERN_DEBUG "no opt on NA\n");
@@ -993,58 +874,53 @@
 		{
 			/* record hardware address */
 
-			neigh->h_dest = neigh->hh_data;
-			neigh->flags &= ~NCF_HHVALID;
+			ndn->ndn_flags |= NTF_COMPLETE;
 
-			if (ndisc_store_hwaddr(neigh->dev, opt, opt_len,
-					       neigh->h_dest, 
+			if (ndisc_store_hwaddr(ndn, opt, opt_len,
 					       ND_OPT_TARGET_LL_ADDR))
 			{
+#if ND_DEBUG >= 2
 				printk(KERN_DEBUG
 				       "event_na: invalid TARGET_LL_ADDR\n");
-				neigh->h_dest = NULL;
-				neigh->nud_state = NUD_NONE;
+#endif
+				ndn->ndn_flags &= ~NTF_COMPLETE;
+				ndn->ndn_nud_state = NUD_NONE;
 				return;
 			}
 		}
 	}
 
 
-	if (solicited || override || neigh->nud_state == NUD_INCOMPLETE)
+	if (solicited || override || ndn->ndn_nud_state == NUD_INCOMPLETE)
 	{
 
-		neigh->probes = 0;
-		neigh->tstamp = jiffies;
+		ndn->ndn_probes = 0;
+		ndn->ndn_tstamp = jiffies;
 
-		if (neigh->nud_state & NUD_IN_TIMER)
+		if (ndn->ndn_nud_state & NUD_IN_TIMER)
 		{
-			ndisc_del_timer(neigh);
+			ndisc_del_timer(ndn);
 		}
 
 		if (solicited)
 		{
-			neigh->nud_state = NUD_REACHABLE;
+			ndn->ndn_nud_state = NUD_REACHABLE;
 		}
 		else
 		{
-			neigh->nud_state = NUD_STALE;
+			ndn->ndn_nud_state = NUD_STALE;
 		}
 	}
 			
-	while ((skb=skb_dequeue(&neigh->arp_queue)))
+	while ((skb=skb_dequeue(&ndn->neigh.arp_queue)))
 	{
-		int priority = SOPRI_NORMAL;
-
-		if (skb->sk)
-			priority = skb->sk->priority;
-		
-		dev_queue_xmit(skb, neigh->dev, priority);
+		dev_queue_xmit(skb);
 	}
 }
 
 static void ndisc_event_ns(struct in6_addr *saddr, struct sk_buff *skb)
 {
-	struct neighbour *neigh;
+	struct nd_neigh *ndn;
 	u8 *opt;
 	int len;
 
@@ -1053,46 +929,53 @@
 
 	len = skb->tail - opt;
 
-	neigh = ndisc_retrieve_neigh(skb->dev, saddr);
+	neigh_table_lock(&nd_tbl);
+	
+	ndn = (struct nd_neigh *) neigh_lookup(&nd_tbl, saddr,
+					       sizeof(struct in6_addr),
+					       skb->dev);
 
-	if (neigh == NULL)
+	if (ndn == NULL)
 	{
-		neigh = ndisc_new_neigh(skb->dev, saddr);
+		ndn = ndisc_new_neigh(skb->dev, saddr);
 	}
-       	
-	switch(neigh->nud_state) {
+
+       	neigh_table_unlock(&nd_tbl);
+
+	switch(ndn->ndn_nud_state) {
 		case NUD_REACHABLE:
 		case NUD_STALE:
 		case NUD_DELAY:
 			if (*opt != ND_OPT_SOURCE_LL_ADDR ||
-			    len != neigh->dev->addr_len ||
-			    memcmp(neigh->h_dest, opt + 2, len))
+			    len != ndn->ndn_dev->addr_len ||
+			    memcmp(ndn->neigh.ha, opt + 2, len))
 			{
 				break;
 			}
 
-			if (neigh->nud_state & NUD_IN_TIMER)
+			if (ndn->ndn_nud_state & NUD_IN_TIMER)
 			{
-				ndisc_del_timer(neigh);
+				ndisc_del_timer(ndn);
 			}
 		default:
-			neigh->flags &= ~NCF_HHVALID;
-			neigh->h_dest = neigh->hh_data;
+			ndn->ndn_flags |= NTF_COMPLETE;
 			
-			if (ndisc_store_hwaddr(neigh->dev, opt, len,
-					       neigh->h_dest,
+			if (ndisc_store_hwaddr(ndn, opt, len,
 					       ND_OPT_SOURCE_LL_ADDR))
 			{
-				printk(KERN_DEBUG 
+#if ND_DEBUG >= 1
+				printk(KERN_DEBUG
 				       "event_ns: invalid SOURCE_LL_ADDR\n");
-				neigh->h_dest = NULL;
-				neigh->nud_state = NUD_NONE;
+#endif
+
+				ndn->ndn_flags &= ~NTF_COMPLETE;
+				ndn->ndn_nud_state = NUD_NONE;
 				return;
 			}
 
-			neigh->nud_state = NUD_STALE;
-			neigh->tstamp = jiffies;
-			neigh->probes = 0;
+			ndn->ndn_nud_state = NUD_STALE;
+			ndn->ndn_tstamp = jiffies;
+			ndn->ndn_probes = 0;
 	}
 
 }
@@ -1172,39 +1055,39 @@
 	default_rt_list = NULL;
 }
 
-static void ndisc_ll_addr_update(struct neighbour *neigh, u8* opt, int len,
+static void ndisc_ll_addr_update(struct nd_neigh *ndn, u8* opt, int len,
 				 int type)
 {
-	switch(neigh->nud_state) {
+	switch(ndn->ndn_nud_state) {
 	case NUD_REACHABLE:
 	case NUD_STALE:
 	case NUD_DELAY:
-		if (len == neigh->dev->addr_len &&
-		    memcmp(neigh->h_dest, opt + 2, len) == 0)
+		if (len == ndn->ndn_dev->addr_len &&
+		    memcmp(ndn->neigh.ha, opt + 2, len) == 0)
 		{
 			break;
 		}
 
-		if (neigh->nud_state & NUD_IN_TIMER)
+		if (ndn->ndn_nud_state & NUD_IN_TIMER)
 		{
-			ndisc_del_timer(neigh);
+			ndisc_del_timer(ndn);
 		}
 	default:
-		neigh->flags &= ~NCF_HHVALID;
-		neigh->h_dest = neigh->hh_data;
+		ndn->ndn_flags |= NTF_COMPLETE;
 		
-		if (ndisc_store_hwaddr(neigh->dev, opt, len, neigh->h_dest,
-				       type))
+		if (ndisc_store_hwaddr(ndn, opt, len, type))
 		{
+#if ND_DEBUG >=1
 			printk(KERN_DEBUG "NDISC: invalid LL_ADDR\n");
-			neigh->h_dest = NULL;
-			neigh->nud_state = NUD_NONE;
+#endif
+			ndn->ndn_flags &= ~NTF_COMPLETE;
+			ndn->ndn_nud_state = NUD_NONE;
 			break;
 		}
 		
-		neigh->nud_state = NUD_STALE;
-		neigh->tstamp = jiffies;
-		neigh->probes = 0;
+		ndn->ndn_nud_state = NUD_STALE;
+		ndn->ndn_tstamp = jiffies;
+		ndn->ndn_probes = 0;
 	}
 	
 }
@@ -1219,6 +1102,7 @@
 	for (rt = default_rt_list; rt; rt=rt->next)
 	{
 		struct neighbour *neigh = rt->rt_nexthop;
+		struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 		
 		if (score < 0)
 		{
@@ -1226,7 +1110,7 @@
 			match = rt;
 		}
 
-		if (neigh->nud_state == NUD_REACHABLE)
+		if (ndn->ndn_nud_state == NUD_REACHABLE)
 		{
 			if (score < 1)
 			{
@@ -1234,7 +1118,7 @@
 				match = rt;
 			}
 
-			if (now  - neigh->tstamp < nd_reachable_time)
+			if (now  - ndn->ndn_tstamp < nd_reachable_time)
 			{
 				return rt;
 			}
@@ -1248,7 +1132,7 @@
 static void ndisc_router_discovery(struct sk_buff *skb)
 {
         struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
-	struct neighbour *neigh;
+	struct nd_neigh *ndn;
 	struct inet6_dev *in6_dev;
 	struct rt6_info *rt;
 	int lifetime;
@@ -1258,9 +1142,9 @@
 
 	optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg);
 
-	if (skb->ipv6_hdr->hop_limit != 255)
+	if (skb->nh.ipv6h->hop_limit != 255)
 	{
-		printk(KERN_WARNING
+		printk(KERN_INFO
 		       "NDISC: fake router advertisment received\n");
 		return;
 	}
@@ -1287,7 +1171,7 @@
 
 	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
 
-	rt = ndisc_get_dflt_router(skb->dev, &skb->ipv6_hdr->saddr);
+	rt = ndisc_get_dflt_router(skb->dev, &skb->nh.ipv6h->saddr);
 
 	if (rt && lifetime == 0)
 	{
@@ -1297,42 +1181,55 @@
 
 	if (rt == NULL && lifetime)
 	{
+		struct in6_addr *saddr;
+		
+#if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_rdisc: new default router\n");
+#endif
 
 		rt = (struct rt6_info *) kmalloc(sizeof(struct rt6_info),
 						 GFP_ATOMIC);
-		if (rt)
-		{
-			neigh = ndisc_retrieve_neigh(skb->dev,
-						     &skb->ipv6_hdr->saddr);
-
-			if (neigh == NULL)
-			{
-				neigh = ndisc_new_neigh(skb->dev,
-							&skb->ipv6_hdr->saddr);
-			}
 
-			if (neigh)
-			{
-				atomic_inc(&neigh->refcnt);
-				neigh->flags |= NCF_ROUTER;
-				
-				memset(rt, 0, sizeof(struct rt6_info));
+		if (rt == NULL)
+		{
+			/* We are out-of-memory. Ignore it */
+			return;
+		}
 
-				ipv6_addr_copy(&rt->rt_dst,
-					       &skb->ipv6_hdr->saddr);
-				rt->rt_metric = 1;
-				rt->rt_flags = RTF_GATEWAY | RTF_DYNAMIC;
-				rt->rt_dev = skb->dev;
-				rt->rt_nexthop = neigh;
+		saddr = &skb->nh.ipv6h->saddr;
+		neigh_table_lock(&nd_tbl);
+		
+		ndn = (struct nd_neigh *) neigh_lookup(&nd_tbl, saddr,
+						       sizeof(struct in6_addr),
+						       skb->dev);
 
-				ndisc_add_dflt_router(rt);
-			}
-			else
+		if (ndn == NULL)
+		{
+			ndn = ndisc_new_neigh(skb->dev, saddr);
+			
+			if (ndn == NULL)
 			{
 				kfree(rt);
+				neigh_table_unlock(&nd_tbl);
+				return;
 			}
 		}
+		
+		neigh_table_unlock(&nd_tbl);
+
+		atomic_inc(&ndn->ndn_refcnt);
+
+		ndn->ndn_flags |= NCF_ROUTER;
+
+		memset(rt, 0, sizeof(struct rt6_info));
+
+		ipv6_addr_copy(&rt->rt_dst, &skb->nh.ipv6h->saddr);
+		rt->rt_metric = 1;
+		rt->rt_flags = RTF_GATEWAY | RTF_DYNAMIC;
+		rt->rt_dev = skb->dev;
+		rt->rt_nexthop = (struct neighbour *) ndn;
+
+		ndisc_add_dflt_router(rt);
 	}
 
 	if (rt)
@@ -1388,9 +1285,9 @@
 			if (rt == NULL)
 				break;
 			
-			neigh = rt->rt_nexthop;
+			ndn = (struct nd_neigh *) rt->rt_nexthop;
 
-			ndisc_ll_addr_update(neigh, opt, len,
+			ndisc_ll_addr_update(ndn, opt, len,
 					     ND_OPT_SOURCE_LL_ADDR);
 			break;
 
@@ -1461,20 +1358,20 @@
 	struct icmpv6hdr *icmph;
 	struct in6_addr *dest;
 	struct in6_addr *target;	/* new first hop to destination */
-	struct neighbour *neigh;
+	struct nd_neigh *ndn;
 	struct rt6_info *rt;
 	int on_link = 0;
 	int optlen;
 	u8 * opt;
 
-	if (skb->ipv6_hdr->hop_limit != 255)
+	if (skb->nh.ipv6h->hop_limit != 255)
 	{
 		printk(KERN_WARNING
 		       "NDISC: fake ICMP redirect received\n");
 		return;
 	}
 
-	if (!(ipv6_addr_type(&skb->ipv6_hdr->saddr) & IPV6_ADDR_LINKLOCAL))
+	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL))
 	{
 		printk(KERN_WARNING
 		       "ICMP redirect: source address is not linklocal\n");
@@ -1521,7 +1418,7 @@
 		return;
 	}
 
-	neigh = rt->rt_nexthop;
+	ndn = (struct nd_neigh *) rt->rt_nexthop;
 
 	opt = (u8 *) (dest + 1);
 
@@ -1533,7 +1430,7 @@
 
 		if (*opt == ND_OPT_TARGET_LL_ADDR)
 		{
-			ndisc_ll_addr_update(neigh, opt, len,
+			ndisc_ll_addr_update(ndn, opt, len,
 					     ND_OPT_TARGET_LL_ADDR);
 		}
 
@@ -1545,9 +1442,10 @@
 void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,
 			 struct in6_addr *target)
 {
-	struct sock *sk = (struct sock *) ndisc_socket.data;
+	struct sock *sk = ndisc_socket->sk;
 	int len = sizeof(struct icmpv6hdr) + 2 * sizeof(struct in6_addr);
 	struct sk_buff *buff;
+	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 	struct inet6_ifaddr *ifp;
 	struct icmpv6hdr *icmph;
 	struct in6_addr *addrp;
@@ -1558,21 +1456,23 @@
 	int err;
 	int hlen;
 
-	rt = fibv6_lookup(&skb->ipv6_hdr->saddr, skb->dev, 0);
+	rt = fibv6_lookup(&skb->nh.ipv6h->saddr, skb->dev, 0);
 	
 	if (rt->rt_flags & RTF_GATEWAY)
 	{
+#if ND_DEBUG >= 1
 		printk(KERN_DEBUG "ndisc_send_redirect: not a neighbour\n");
+#endif
 		return;
 	}
 
-	if (neigh->nud_state == NUD_REACHABLE)
+	if (ndn->ndn_nud_state == NUD_REACHABLE)
 	{
 		ta_len  = ((neigh->dev->addr_len + 1) >> 3) + 1;
 		len += (ta_len << 3);
 	}
 
-	rd_len = min(536 - len, ntohs(skb->ipv6_hdr->payload_len) + 8);
+	rd_len = min(536 - len, ntohs(skb->nh.ipv6h->payload_len) + 8);
 	rd_len &= ~0x7;
 	len += rd_len;
 
@@ -1580,7 +1480,9 @@
 
 	if (ifp == NULL)
 	{
+#if ND_DEBUG >= 1
 		printk(KERN_DEBUG "redirect: no link_local addr for dev\n");
+#endif
 		return;
 	}
 
@@ -1588,10 +1490,11 @@
 
 	if (buff == NULL)
 	{
+#if ND_DEBUG >= 2
 		printk(KERN_DEBUG "ndisc_send_redirect: alloc_skb failed\n");
+#endif
 		return;
 	}
-
 	
 	hlen = 0;
 	if (skb->dev->hard_header_len)
@@ -1613,7 +1516,7 @@
 	addrp = (struct in6_addr *)(icmph + 1);
 	ipv6_addr_copy(addrp, target);
 	addrp++;
-	ipv6_addr_copy(addrp, &skb->ipv6_hdr->daddr);
+	ipv6_addr_copy(addrp, &skb->nh.ipv6h->daddr);
 	
 	opt = (u8*) (addrp + 1);
 		
@@ -1628,7 +1531,7 @@
 		*(opt++) = ND_OPT_TARGET_LL_ADDR;
 		*(opt++) = ta_len;
 
-		memcpy(opt, neigh->h_dest, neigh->dev->addr_len);
+		memcpy(opt, neigh->ha, neigh->dev->addr_len);
 		opt += neigh->dev->addr_len;
 
 		/* 
@@ -1656,30 +1559,33 @@
 	*(opt++) = (rd_len >> 3);
 	opt += 6;
 
-	memcpy(opt, &skb->ipv6_hdr, rd_len - 8);
+	memcpy(opt, &skb->nh.ipv6h, rd_len - 8);
 	
-	icmph->checksum = csum_ipv6_magic(&ifp->addr, &skb->ipv6_hdr->saddr,
+	icmph->checksum = csum_ipv6_magic(&ifp->addr, &skb->nh.ipv6h->saddr,
 					  len, IPPROTO_ICMPV6,
 					  csum_partial((u8 *) icmph, len, 0));
 
-	ipv6_xmit(sk, buff, &ifp->addr, &skb->ipv6_hdr->saddr, NULL, IPPROTO_ICMPV6);
+	ipv6_xmit(sk, buff, &ifp->addr, &skb->nh.ipv6h->saddr, NULL,
+		  IPPROTO_ICMPV6);
 }
 
 /* Called by upper layers to validate neighbour cache entries. */
 
 void ndisc_validate(struct neighbour *neigh)
 {
-        if (neigh->nud_state == NUD_INCOMPLETE)
+	struct nd_neigh *ndn = (struct nd_neigh *) neigh;
+	
+        if (ndn->ndn_nud_state == NUD_INCOMPLETE)
                 return;
 
-        if (neigh->nud_state == NUD_DELAY) 
+        if (ndn->ndn_nud_state == NUD_DELAY) 
 	{
-                ndisc_del_timer(neigh);
+                ndisc_del_timer(ndn);
         }
 
         nd_stats.rcv_upper_conf++;
-        neigh->nud_state = NUD_REACHABLE;
-        neigh->tstamp = jiffies;
+        ndn->ndn_nud_state = NUD_REACHABLE;
+        ndn->ndn_tstamp = jiffies;
 }
 
 int ndisc_rcv(struct sk_buff *skb, struct device *dev,
@@ -1687,7 +1593,7 @@
 	      struct ipv6_options *opt, unsigned short len)
 {
 	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
-	struct neighbour *neigh;
+	struct nd_neigh *ndn;
 	struct inet6_ifaddr *ifp;
 
 	switch (msg->icmph.type) {
@@ -1720,29 +1626,42 @@
 				nd_stats.rcv_probes_ucast++;
 				ndisc_event_ns(saddr, skb);
 
-				/* answer solicitation */
-				neigh = ndisc_retrieve_neigh(dev, saddr);
+				neigh_table_lock(&nd_tbl);
+
+				ndn = (struct nd_neigh *)
+					neigh_lookup(&nd_tbl, saddr,
+						     sizeof(struct in6_addr),
+						     dev);
+
+				neigh_table_unlock(&nd_tbl);
 
 				inc = ipv6_addr_type(daddr);
 				inc &= IPV6_ADDR_MULTICAST;
 
-				ndisc_send_na(dev, neigh, saddr, &ifp->addr, 
+				ndisc_send_na(dev, ndn, saddr, &ifp->addr, 
 					      ifp->idev->router, 1, inc, inc);
 			}
 			else
 			{
+#if ND_DEBUG >= 1
 				/* FIXME */
 				printk(KERN_DEBUG "ns: non unicast saddr\n");
+#endif
 			}
 		}
 		break;
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
+		
+		neigh_table_lock(&nd_tbl);	       
+		ndn = (struct nd_neigh *) 
+			neigh_lookup(&nd_tbl, (void *) &msg->target,
+				     sizeof(struct in6_addr), skb->dev);
+		neigh_table_unlock(&nd_tbl);
 
-		neigh = ndisc_retrieve_neigh(skb->dev, &msg->target);
-		if (neigh)
+		if (ndn)
 		{
-			if (neigh->flags & NCF_ROUTER)
+			if (ndn->ndn_flags & NCF_ROUTER)
 			{
 				if (msg->icmph.icmp6_router == 0)
 				{
@@ -1763,10 +1682,10 @@
 			{
 				if (msg->icmph.icmp6_router)
 				{
-					neigh->flags |= NCF_ROUTER;
+					ndn->ndn_flags |= NCF_ROUTER;
 				}
 			}
-			ndisc_event_na(neigh, (unsigned char *) &msg->opt,
+			ndisc_event_na(ndn, (unsigned char *) &msg->opt,
 				       skb->tail - (u8 *)&msg->opt /*opt_len*/,
 				       msg->icmph.icmp6_solicited,
 				       msg->icmph.icmp6_override);
@@ -1794,53 +1713,59 @@
 int ndisc_get_info(char *buffer, char **start, off_t offset, int length,
 		   int dummy)
 {
-	struct neighbour *neigh;
 	unsigned long now = jiffies;
 	int len = 0;
 	int i;
 
-	atomic_inc(&ndisc_lock);
+	neigh_table_lock(&nd_tbl);
 
-	for (i = 0; i < NCACHE_NUM_BUCKETS; i++)
+	for (i = 0; i < nd_tbl.tbl_size; i++)
 	{
-		for(neigh = neighbours[i]; neigh; neigh=neigh->next)
-		{
+		struct neighbour *neigh, *head;
+		head = nd_tbl.hash_buckets[i];
+		
+		if ((neigh = head) == NULL)
+			continue;
+
+		do {
+			struct nd_neigh *ndn = (struct nd_neigh *) neigh;
 			int j;
 
 			for (j=0; j<16; j++)
 			{
 				sprintf(buffer + len, "%02x",
-					neigh->addr.s6_addr[j]);
+					ndn->ndn_addr.s6_addr[j]);
 				len += 2;
 			}
 
 			len += sprintf(buffer + len,
-				       " %02x %02x %08lx %08lx %04x %04x ",
-				       i,
-				       neigh->nud_state,
-				       neigh->expires - now,
-				       now - neigh->tstamp,
-				       neigh->refcnt,
-				       neigh->flags);
+				       " %02x %02x %08lx %08lx %04x %04lx ", i,
+				       ndn->ndn_nud_state,
+				       ndn->ndn_expires - now,
+				       now - ndn->ndn_tstamp,
+				       ndn->ndn_refcnt,
+				       ndn->ndn_flags);
 
-			if (neigh->h_dest)
+			if ((ndn->ndn_flags & NTF_COMPLETE))
 			{
 				for (j=0; j< neigh->dev->addr_len; j++)
 				{
 					sprintf(buffer + len, "%02x",
-						neigh->h_dest[j]);
+						neigh->ha[j]);
 					len += 2;
 				}
 			}
 			else
                                 len += sprintf(buffer + len, "000000000000");
 			len += sprintf(buffer + len, "\n");
-				       
-		}
-	}
+			
+			neigh = neigh->next;
 
-	ndisc_release_lock();
+		} while (neigh != head);
+	}
 
+	neigh_table_unlock(&nd_tbl);
+	
 	*start = buffer + offset;
 
 	len -= offset;
@@ -1852,42 +1777,43 @@
 
 struct proc_dir_entry ndisc_proc_entry =
 {
-        0, 11, "ndisc_cache",
+        PROC_NET_NDISC, 5, "ndisc",
         S_IFREG | S_IRUGO, 1, 0, 0,
         0, NULL,
         &ndisc_get_info
 };
 
-void ndisc_init(struct proto_ops *ops)
+void ndisc_init(struct net_proto_family *ops)
 {
 	struct sock *sk;
-        int i = 0;
         int err;
 
-	/*
-	 *	Init ndisc_socket
-	 */
-	ndisc_socket.type=SOCK_RAW;
-	ndisc_socket.ops=ops;
+        ndisc_inode.i_mode = S_IFSOCK;
+        ndisc_inode.i_sock = 1;
+        ndisc_inode.i_uid = 0;
+        ndisc_inode.i_gid = 0;
+
+        ndisc_socket->inode = &ndisc_inode;
+        ndisc_socket->state = SS_UNCONNECTED;
+        ndisc_socket->type=SOCK_RAW;
 
-	if((err=ops->create(&ndisc_socket, IPPROTO_ICMPV6))<0)
+	if((err=ops->create(ndisc_socket, IPPROTO_ICMPV6))<0)
 		printk(KERN_DEBUG 
 		       "Failed to create the NDISC control socket.\n");
 
 	MOD_DEC_USE_COUNT;
 
-	sk = ndisc_socket.data;
+	sk = ndisc_socket->sk;
 	sk->allocation = GFP_ATOMIC;
 	sk->net_pinfo.af_inet6.hop_limit = 255;
 	sk->net_pinfo.af_inet6.priority  = 15;
-	sk->num = 256;				/* Don't receive any data */
+	sk->num = 256;
 
         /*
-         * Initialize the neighbours hash buckets.
+         * Initialize the neighbour table
          */
-
-        for (; i < NCACHE_NUM_BUCKETS; i++)
-                neighbours[i] = NULL;
+	
+	neigh_table_init(&nd_tbl, &nd_neigh_ops, NCACHE_NUM_BUCKETS);
  
         /* General ND state machine timer. */
 	init_timer(&ndisc_timer);
@@ -1897,15 +1823,18 @@
 
         /* ND GC timer */
         init_timer(&ndisc_gc_timer);
-        ndisc_gc_timer.function = ndisc_garbage_collect;
+        ndisc_gc_timer.function = ndisc_periodic_timer;
         ndisc_gc_timer.data = 0L;
         ndisc_gc_timer.expires = jiffies + nd_gc_interval;
 
 	add_timer(&ndisc_gc_timer);
 
+#ifdef CONFIG_PROC_FS
 #ifdef CONFIG_IPV6_MODULE
-	ndisc_eth_hook = ndisc_eth_resolv;
         proc_register_dynamic(&proc_net, &ndisc_proc_entry);
+#else
+	proc_net_register(&ndisc_proc_entry);
+#endif
 #endif
 }
 
@@ -1913,7 +1842,14 @@
 void ndisc_cleanup(void)
 {
 	ndisc_eth_hook = NULL;
+
+#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_IPV6_MODULE
         proc_unregister(&proc_net, ndisc_proc_entry.low_ino);
+#else
+	proc_net_unregister(PROC_NET_NDISC);
+#endif
+#endif
 	del_timer(&ndisc_gc_timer);
 	del_timer(&ndisc_timer);
 }
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.14/linux/net/ipv6/raw.c	Thu Dec 12 17:02:48 1996
+++ linux/net/ipv6/raw.c	Thu Dec 12 16:54:25 1996
@@ -56,7 +56,6 @@
 	if (sock_queue_rcv_skb(sk,skb)<0)
 	{
 		/* ip_statistics.IpInDiscards++; */
-		skb->sk=NULL;
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -80,10 +79,20 @@
 
 	sk = skb->sk;
 
+#if 1
+/*
+ *	It was wrong for IPv4. It breaks NRL too [ANK]
+ *	Actually i think this is the option that  does make more 
+ *	sense with IPv6 nested headers. [Pedro]
+ */
+
 	if (sk->ip_hdrincl)
 	{
-		skb->h.raw = (unsigned char *) skb->ipv6_hdr;
+		skb->h.raw = skb->nh.raw;
 	}
+#else
+        skb->h.raw = skb->nh.raw;
+#endif
 
 	if (sk->users) {
 		__skb_queue_tail(&sk->back_log, skb);
@@ -134,7 +143,7 @@
 	if (sin6) 
 	{
 		sin6->sin6_family = AF_INET6;
-		memcpy(&sin6->sin6_addr, &skb->ipv6_hdr->saddr, 
+		memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr, 
 		       sizeof(struct in6_addr));
 
 		*addr_len = sizeof(struct sockaddr_in6);
@@ -227,8 +236,7 @@
 }
 
 
-static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len, 
-			 int noblock, int flags)
+static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
 	struct ipv6_options opt_space;
 	struct sockaddr_in6 * sin6 = (struct sockaddr_in6 *) msg->msg_name;
@@ -244,10 +252,10 @@
 	
 
 	/* Mirror BSD error message compatibility */
-	if (flags & MSG_OOB)		
+	if (msg->msg_flags & MSG_OOB)		
 		return -EOPNOTSUPP;
 			 
-	if (flags & ~MSG_DONTROUTE)
+	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
 		return(-EINVAL);
 	/*
 	 *	Get and verify the address. 
@@ -338,13 +346,13 @@
 		}
 
 		err = ipv6_build_xmit(sk, rawv6_frag_cksum, &hdr, daddr, len,
-				      saddr, dev, opt, proto, noblock);
+				      saddr, dev, opt, proto, msg->msg_flags&MSG_DONTWAIT);
 	}
 	else
 	{
 		err = ipv6_build_xmit(sk, rawv6_getfrag, msg->msg_iov, daddr,
 				      len, saddr, dev, opt, proto,
-				      noblock);
+				      msg->msg_flags&MSG_DONTWAIT);
 	}
 
 	return err<0?err:len;
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- v2.1.14/linux/net/ipv6/reassembly.c	Sun Nov 10 20:12:31 1996
+++ linux/net/ipv6/reassembly.c	Thu Dec 12 16:54:26 1996
@@ -217,8 +217,8 @@
 
 	
 	nfp->offset = ntohs(fhdr->frag_off) & ~0x7;
-	nfp->len = (ntohs(skb->ipv6_hdr->payload_len) -
-		    ((u8 *) (fhdr + 1) - (u8 *) (skb->ipv6_hdr + 1)));
+	nfp->len = (ntohs(skb->nh.ipv6h->payload_len) -
+		    ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
 
 
 	nfp->skb  = skb;
@@ -286,7 +286,7 @@
 	 * this means we have all fragments.
 	 */
 
-	unfrag_len = (u8 *) (tail->fhdr) - (u8 *) (tail->skb->ipv6_hdr + 1);
+	unfrag_len = (u8 *) (tail->fhdr) - (u8 *) (tail->skb->nh.ipv6h + 1);
 
 	payload_len = (unfrag_len + tail->offset + 
 		       (tail->skb->tail - (__u8 *) (tail->fhdr + 1)));
@@ -302,20 +302,19 @@
 
 	copy = unfrag_len + sizeof(struct ipv6hdr);
 
-	skb->ipv6_hdr = (struct ipv6hdr *) skb->data;
+	skb->nh.ipv6h = (struct ipv6hdr *) skb->data;
 
-	skb->free = 1;
 	skb->dev = fq->dev;
 
 	
 	nh = fq->nexthdr;
 
 	*(fq->nhptr) = nh;
-	memcpy(skb_put(skb, copy), tail->skb->ipv6_hdr, copy);
+	memcpy(skb_put(skb, copy), tail->skb->nh.ipv6h, copy);
 
 	skb->h.raw = skb->tail;
 
-	skb->ipv6_hdr->payload_len = ntohs(payload_len);
+	skb->nh.ipv6h->payload_len = ntohs(payload_len);
 
 	*skb_in = skb;
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.14/linux/net/ipv6/sit.c	Fri Nov 22 18:28:23 1996
+++ linux/net/ipv6/sit.c	Thu Dec 12 16:54:26 1996
@@ -51,23 +51,15 @@
 
 static int			sit_xmit(struct sk_buff *skb, 
 					 struct device *dev);
-static int			sit_rcv(struct sk_buff *skb, 
-					struct device *dev, 
-					struct options *opt,
-					__u32 daddr, unsigned short len,
-					__u32 saddr, int redo, 
-					struct inet_protocol * protocol);
+static int			sit_rcv(struct sk_buff *skb, unsigned short len);
+static void 			sit_err(struct sk_buff *skb, unsigned char *dp);
 
 static int			sit_open(struct device *dev);
 static int			sit_close(struct device *dev);
 
 static struct enet_statistics *	sit_get_stats(struct device *dev);
 
-static void			sit_err(int type, int code, 
-					unsigned char *buff, __u32 info,
-					__u32 daddr, __u32 saddr,
-					struct inet_protocol *protocol,
-					int len);
+extern void	udp_err(struct sk_buff *, unsigned char *);
 
 static struct inet_protocol sit_protocol = {
 	sit_rcv,
@@ -208,7 +200,7 @@
 	dev->hard_header	= NULL;
 	dev->rebuild_header 	= NULL;
 	dev->set_mac_address 	= NULL;
-	dev->header_cache_bind 	= NULL;
+	dev->hard_header_cache 	= NULL;
 	dev->header_cache_update= NULL;
 
 	dev->type		= ARPHRD_SIT;
@@ -349,20 +341,20 @@
  *	receive IPv4 ICMP messages
  */
 
-static void sit_err(int type, int code, unsigned char *buff, __u32 info,
-		    __u32 daddr, __u32 saddr, struct inet_protocol *protocol,
-		    int len)
-		    
+static void sit_err(struct sk_buff *skb, unsigned char *dp)
 {
+	struct iphdr *iph = (struct iphdr*)dp;
+	int type = skb->h.icmph->type;
+	int code = skb->h.icmph->code;
+
 	if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
 	{
 		struct sit_mtu_info *minfo;
+		unsigned short info = skb->h.icmph->un.frag.mtu - sizeof(struct iphdr);
 
-		info -= sizeof(struct iphdr);
-
-		minfo = sit_mtu_lookup(daddr);
+		minfo = sit_mtu_lookup(iph->daddr);
 
-		printk(KERN_DEBUG "sit: %08lx pmtu = %ul\n", ntohl(saddr),
+		printk(KERN_DEBUG "sit: %08lx pmtu = %ul\n", ntohl(iph->saddr),
 		       info);
 		if (minfo == NULL)
 		{
@@ -373,7 +365,7 @@
 				return;
 
 			start_bh_atomic();
-			sit_cache_insert(daddr, info);
+			sit_cache_insert(iph->daddr, info);
 			end_bh_atomic();
 		}
 		else
@@ -383,16 +375,15 @@
 	}
 }
 
-static int sit_rcv(struct sk_buff *skb, struct device *idev, 
-		   struct options *opt,
-		   __u32 daddr, unsigned short len,
-		   __u32 saddr, int redo, struct inet_protocol * protocol)
+static int sit_rcv(struct sk_buff *skb, unsigned short len)
 {
 	struct enet_statistics *stats;
 	struct device *dev = NULL;
 	struct sit_vif *vif;	
+	__u32  saddr = skb->nh.iph->saddr;
        
-	skb->h.raw = skb_pull(skb, skb->h.raw - skb->data);
+	skb->h.raw = skb->nh.raw = skb_pull(skb, skb->h.raw - skb->data);
+
 	skb->protocol = __constant_htons(ETH_P_IPV6);
 
 	for (vif = vif_list; vif; vif = vif->next)
@@ -453,21 +444,21 @@
 	daddr = dev->pa_dstaddr;
 	if (daddr == 0)
 	{
-		struct neighbour *neigh;
+		struct nd_neigh *neigh;
 
-		neigh = skb->nexthop;
+		neigh = (struct nd_neigh *) skb->nexthop;
 		if (neigh == NULL)
 		{
 			printk(KERN_DEBUG "sit: nexthop == NULL\n");
 			goto on_error;
 		}
 		
-		addr6 = &neigh->addr;
+		addr6 = &neigh->ndn_addr;
 		addr_type = ipv6_addr_type(addr6);
 
 		if (addr_type == IPV6_ADDR_ANY)
 		{
-			addr6 = &skb->ipv6_hdr->daddr;
+			addr6 = &skb->nh.ipv6h->daddr;
 			addr_type = ipv6_addr_type(addr6);
 		}
 
@@ -481,12 +472,7 @@
 
 	len = skb->tail - (skb->data + sizeof(struct ipv6hdr));
 
-	if (skb->sk)
-	{
-		atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
-	}
-
-	skb->sk = NULL;
+	skb_orphan(skb);
 		
 	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr));
 	
@@ -494,20 +480,18 @@
 
 	/* get route */
 
-	rt = ip_rt_route(daddr, skb->localroute);
-	
-	if (rt == NULL)
-	{
+	if (ip_route_output(&rt, daddr, 0, 0, NULL)) {
 		printk(KERN_DEBUG "sit: no route to host\n");
 		goto on_error;
 	}
 
+	skb->dst = dst_clone(&rt->u.dst);
 	minfo = sit_mtu_lookup(daddr);
 
 	if (minfo)
 		mtu = minfo->mtu;
 	else
-		mtu = rt->rt_dev->mtu;
+		mtu = rt->u.dst.dev->mtu;
 
 	if (mtu > 576 && len > mtu)
 	{
@@ -516,7 +500,7 @@
 	}
 
 	saddr = rt->rt_src;
-	skb->dev = rt->rt_dev;
+	skb->dev = rt->u.dst.dev;
 	raddr = rt->rt_gateway;	
 
 	if (raddr == 0)
@@ -544,7 +528,6 @@
 			if (mac < 0)
 				skb->arp = 0;
 
-			skb->raddr = raddr;		
 		}
 		
 	}
@@ -567,11 +550,11 @@
 	iph->saddr    = saddr;
 	iph->daddr    = daddr;
 	iph->protocol = IPPROTO_IPV6;
-	skb->ip_hdr   = iph;
+	skb->nh.iph   = iph;
 
 	ip_send_check(iph);
 
-	ip_queue_xmit(NULL, skb->dev, skb, 1);
+	ip_queue_xmit(skb);
 
 	stats->tx_packets++;
 	dev->tbusy=0;
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.14/linux/net/ipv6/tcp_ipv6.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipv6/tcp_ipv6.c	Thu Dec 12 16:54:26 1996
@@ -70,13 +70,13 @@
 
 	if (skb->protocol == __constant_htons(ETH_P_IPV6))
 	{
-		si = skb->ipv6_hdr->saddr.s6_addr32[3];
-		di = skb->ipv6_hdr->daddr.s6_addr32[3];
+		si = skb->nh.ipv6h->saddr.s6_addr32[3];
+		di = skb->nh.ipv6h->daddr.s6_addr32[3];
 	}
 	else
 	{
-		si = skb->saddr;
-		di = skb->daddr;
+		si = skb->nh.iph->saddr;
+		di = skb->nh.iph->daddr;
 	}
 
 	return secure_tcp_sequence_number(di, si,
@@ -218,9 +218,6 @@
 		return(-ENOMEM);
 	}
 	lock_sock(sk);
-	buff->sk = sk;
-	buff->free = 0;
-	buff->localroute = sk->localroute;
 	
 	tmp = tcp_v6_build_header(sk, buff);
 
@@ -278,7 +275,7 @@
 	sk->packets_out++;
 	buff->when = jiffies;
 	skb1 = skb_clone(buff, GFP_KERNEL);
-	sk->wmem_alloc += skb1->truesize;
+	skb_set_owner_w(skb1, sk);
 
 	tmp = ipv6_xmit(sk, skb1, &np->saddr, &np->daddr, NULL, IPPROTO_TCP);
 
@@ -293,8 +290,7 @@
 	return(tmp);
 }
 
-static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg,
-			  int len, int nonblock, int flags)
+static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	int retval = -EINVAL;
@@ -303,7 +299,7 @@
 	 *	Do sanity checking for sendmsg/sendto/send
 	 */
 
-	if (flags & ~(MSG_OOB|MSG_DONTROUTE))
+	if (msg->msg_flags & ~(MSG_OOB|MSG_DONTROUTE|MSG_DONTWAIT))
 		goto out;
 	if (msg->msg_name) {
 		struct sockaddr_in6 *addr=(struct sockaddr_in6 *)msg->msg_name;
@@ -326,7 +322,7 @@
 
 	lock_sock(sk);
 	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov, 
-				len, nonblock, flags);
+				len, msg->msg_flags);
 
 	release_sock(sk);
 
@@ -407,7 +403,7 @@
 	}
 
 	skb_reserve(skb, (MAX_HEADER + 15) & ~15);
-	skb->ipv6_hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+	skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
 
 	dc = ipv6_dst_route(&af_req->rmt_addr, af_req->dev, 0);
 
@@ -531,8 +527,8 @@
 
 	req->rmt_port = skb->h.th->source;
 
-	ipv6_addr_copy(&af_req->rmt_addr, &skb->ipv6_hdr->saddr);
-	ipv6_addr_copy(&af_req->loc_addr, &skb->ipv6_hdr->daddr);
+	ipv6_addr_copy(&af_req->rmt_addr, &skb->nh.ipv6h->saddr);
+	ipv6_addr_copy(&af_req->loc_addr, &skb->nh.ipv6h->daddr);
 
 	/* FIXME: options */
 
@@ -610,10 +606,11 @@
 
 	memcpy(newsk, sk, sizeof(*newsk));
 	newsk->opt = NULL;
-	newsk->ip_route_cache  = NULL;
+	newsk->dst_cache  = NULL;
 	skb_queue_head_init(&newsk->write_queue);
 	skb_queue_head_init(&newsk->receive_queue);
 	skb_queue_head_init(&newsk->out_of_order_queue);
+	skb_queue_head_init(&newsk->error_queue);
 	
 	/*
 	 *	Unused
@@ -681,7 +678,8 @@
 
 	newsk->dummy_th.source = sk->dummy_th.source;
 	newsk->dummy_th.dest = req->rmt_port;
-	
+	newsk->users=0;
+
 	newtp->rcv_nxt = req->rcv_isn + 1;
 	newtp->rcv_wup = req->rcv_isn + 1;
 	newsk->copied_seq = req->rcv_isn + 1;
@@ -717,6 +715,10 @@
 
 }
 
+static void tcp_v6_reply_reset(struct sk_buff *skb)
+{
+}
+
 static void tcp_v6_send_reset(struct in6_addr *saddr, struct in6_addr *daddr, 
 			      struct tcphdr *th, struct proto *prot, 
 			      struct ipv6_options *opt,
@@ -737,9 +739,7 @@
 	if (buff == NULL) 
 	  	return;
 
-	buff->sk = NULL;
 	buff->dev = dev;
-	buff->localroute = 0;
 
 	tcp_v6_build_header(NULL, buff);
 
@@ -804,8 +804,8 @@
 
 		af_req = (struct tcp_v6_open_req *) req;
 
-		if (!ipv6_addr_cmp(&af_req->rmt_addr, &skb->ipv6_hdr->saddr) &&
-		    !ipv6_addr_cmp(&af_req->loc_addr, &skb->ipv6_hdr->daddr) &&
+		if (!ipv6_addr_cmp(&af_req->rmt_addr, &skb->nh.ipv6h->saddr) &&
+		    !ipv6_addr_cmp(&af_req->loc_addr, &skb->nh.ipv6h->daddr) &&
 		    req->rmt_port == skb->h.th->source)
 		{
 			u32 flg;
@@ -835,7 +835,7 @@
 				return NULL;
 			}
 
-			atomic_sub(skb->truesize, &sk->rmem_alloc);
+			skb_orphan(skb);
 			sk = tp->af_specific->syn_recv_sock(sk, skb, req);
 
 			tcp_dec_slow_timer(TCP_SLT_SYNACK);
@@ -845,10 +845,9 @@
 				return NULL;
 			}
 
-			atomic_add(skb->truesize, &sk->rmem_alloc);
+			skb_set_owner_r(skb, sk);
 			req->expires = 0UL;
 			req->sk = sk;
-			skb->sk = sk;
 			break;
 		}
 
@@ -925,7 +924,6 @@
 
 		skb->acked = 0;
 		skb->used = 0;
-		skb->free = 1;
 	}		
 
 	/*
@@ -961,11 +959,12 @@
 		return(0);
 	}
 
-	atomic_add(skb->truesize, &sk->rmem_alloc);
+	skb_set_owner_r(skb, sk);
 
 	if (sk->state == TCP_ESTABLISHED)
 	{
-		tcp_rcv_established(sk, skb, th, len);
+		if (tcp_rcv_established(sk, skb, th, len))
+			goto no_tcp_socket;
 		return 0;
 	}
 	
@@ -994,7 +993,7 @@
 	 */
 
 	tcp_v6_send_reset(daddr, saddr, th, &tcpv6_prot, opt, dev, 
-			  skb->ipv6_hdr->priority, 255);
+			  skb->nh.ipv6h->priority, 255);
 
 discard_it:
 
@@ -1034,7 +1033,7 @@
 			 np->dest->rt.fib_node->fn_sernum : 0);
 
 	ipv6_redo_mac_hdr(skb, np->dest->dc_nexthop,
-			  skb->tail - (u8*) skb->ipv6_hdr);
+			  skb->tail - (u8*) skb->nh.ipv6h);
 	return 0;
 }
 
@@ -1043,8 +1042,8 @@
 	int res;
 
 	res = tcp_v6_rcv(skb, skb->dev,
-			 &skb->ipv6_hdr->saddr, &skb->ipv6_hdr->daddr,
-			 (struct ipv6_options *) skb->proto_priv,
+			 &skb->nh.ipv6h->saddr, &skb->nh.ipv6h->daddr,
+			 (struct ipv6_options *) skb->cb,
 			 skb->len, 1, 
 			 (struct inet6_protocol *) sk->pair);
 	return res;
@@ -1056,8 +1055,8 @@
 	struct in6_addr *daddr;
 	struct sock *sk;
 
-	saddr = &skb->ipv6_hdr->saddr;
-	daddr = &skb->ipv6_hdr->daddr;
+	saddr = &skb->nh.ipv6h->saddr;
+	daddr = &skb->nh.ipv6h->daddr;
 
 	sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
 
@@ -1067,7 +1066,7 @@
 static int tcp_v6_build_header(struct sock *sk, struct sk_buff *skb)
 {
 	skb_reserve(skb, (MAX_HEADER + 15) & ~15);
-	skb->ipv6_hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
+	skb->nh.ipv6h = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
 
 	/*
 	 *	FIXME: reserve space for option headers
@@ -1077,9 +1076,9 @@
 	return 0;
 }
 
-static void tcp_v6_xmit(struct sock *sk, struct device *dev, struct sk_buff *skb,
-			int free)
+static void tcp_v6_xmit(struct sk_buff *skb)
 {
+	struct sock *sk = skb->sk;
 	struct ipv6_pinfo * np = &sk->net_pinfo.af_inet6;
 	int err;
 
@@ -1117,6 +1116,7 @@
 	ipv6_setsockopt,
 	ipv6_getsockopt,
 	v6_addr2sockaddr,
+	tcp_v6_reply_reset,
 	sizeof(struct sockaddr_in6)
 };
 
@@ -1136,6 +1136,7 @@
 	ipv6_setsockopt,
 	ipv6_getsockopt,
 	v6_addr2sockaddr,
+	tcp_v6_reply_reset,
 	sizeof(struct sockaddr_in6)
 };
 
@@ -1203,7 +1204,6 @@
 	 
   	while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		IS_SKB(skb);
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
   	}
 
diff -u --recursive --new-file v2.1.14/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.14/linux/net/ipv6/udp.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipv6/udp.c	Thu Dec 12 16:54:26 1996
@@ -224,11 +224,11 @@
 		if (skb->protocol == __constant_htons(ETH_P_IP))
 		{
 			ipv6_addr_set(&sin6->sin6_addr, 0, 0,
-				      __constant_htonl(0xffff), skb->daddr);
+				      __constant_htonl(0xffff), skb->nh.iph->daddr);
 		}
 		else
 		{
-			memcpy(&sin6->sin6_addr, &skb->ipv6_hdr->saddr,
+			memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
 			       sizeof(struct in6_addr));
 
 			if (msg->msg_control)
@@ -492,8 +492,7 @@
 	return 0;
 }
 
-static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen, 
-			 int noblock, int flags)
+static int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, int ulen)
 {
 	
 	struct ipv6_options opt_space;
@@ -510,7 +509,7 @@
 	int err;
 
 	
-	if (flags & ~MSG_DONTROUTE)
+	if (msg->msg_flags & ~(MSG_DONTROUTE|MSG_DONTWAIT))
 		return(-EINVAL);
 
 	if (sin6)
@@ -551,7 +550,7 @@
 		sin.sin_family = AF_INET;
 		sin.sin_addr.s_addr = daddr->s6_addr32[3];
 
-		return udp_sendmsg(sk, msg, len, noblock, flags);
+		return udp_sendmsg(sk, msg, len);
 	}
 
 	udh.daddr = NULL;
@@ -582,7 +581,7 @@
 	udh.pl_len = len;
 	
 	err = ipv6_build_xmit(sk, udpv6_getfrag, &udh, daddr, len,
-			      saddr, dev, opt, IPPROTO_UDP, noblock);
+			      saddr, dev, opt, IPPROTO_UDP, msg->msg_flags&MSG_DONTWAIT);
 	
 	if (err < 0)
 		return err;
diff -u --recursive --new-file v2.1.14/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.14/linux/net/ipx/af_ipx.c	Sat Nov 30 12:03:13 1996
+++ linux/net/ipx/af_ipx.c	Thu Dec 12 16:54:26 1996
@@ -108,6 +108,8 @@
 static struct datalink_proto	*p8023_datalink = NULL;
 static struct datalink_proto	*pSNAP_datalink = NULL;
 
+static struct proto_ops ipx_dgram_ops;
+
 static ipx_route 	*ipx_routes = NULL;
 static ipx_interface	*ipx_interfaces = NULL;
 static ipx_interface	*ipx_primary_net = NULL;
@@ -160,9 +162,9 @@
  */
  
 static void 
-ipx_remove_socket(ipx_socket *sk)
+ipx_remove_socket(struct sock *sk)
 {
-	ipx_socket	*s;
+	struct sock	*s;
 	ipx_interface	*intrfc;
 	unsigned long	flags;
 
@@ -202,7 +204,7 @@
  */
  
 static void 
-ipx_destroy_socket(ipx_socket *sk)
+ipx_destroy_socket(struct sock *sk)
 {
 	struct sk_buff	*skb;
 
@@ -258,9 +260,9 @@
 
 /* Sockets are bound to a particular IPX interface. */
 static void
-ipxitf_insert_socket(ipx_interface *intrfc, ipx_socket *sk)
+ipxitf_insert_socket(ipx_interface *intrfc, struct sock *sk)
 {
-	ipx_socket	*s;
+	struct sock	*s;
 
 	sk->protinfo.af_ipx.intrfc = intrfc;
 	sk->next = NULL;
@@ -273,10 +275,10 @@
 	}
 }
 
-static ipx_socket *
+static struct sock *
 ipxitf_find_socket(ipx_interface *intrfc, unsigned short port)
 {
-	ipx_socket	*s;
+	struct sock	*s;
 
 	for (s=intrfc->if_sklist; 
 		(s != NULL) && (s->protinfo.af_ipx.port != port); 
@@ -288,11 +290,11 @@
 
 #ifdef CONFIG_IPX_INTERN
 
-static ipx_socket *
+static struct sock *
 ipxitf_find_internal_socket(ipx_interface *intrfc,
 			    unsigned char *node, unsigned short port)
 {
-	ipx_socket *s = intrfc->if_sklist;
+	struct sock *s = intrfc->if_sklist;
 
 	while (s != NULL)
 	{
@@ -313,7 +315,7 @@
 ipxitf_down(ipx_interface *intrfc)
 {
 	ipx_interface	*i;
-	ipx_socket	*s, *t;
+	struct sock	*s, *t;
 
 	/* Delete all routes associated with this interface */
 	ipxrtr_del_routes(intrfc);
@@ -402,7 +404,7 @@
 ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
 {
 	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
-	ipx_socket	*s;
+	struct sock	*s;
 
 	int is_broadcast = (memcmp(ipx->ipx_dest.node, ipx_broadcast_node,
 				   IPX_NODE_LEN) == 0);
@@ -424,7 +426,7 @@
 				skb1 = skb_clone(skb, GFP_ATOMIC);
 				if (skb1 != NULL)
 				{
-					skb1->arp = skb1->free = 1;
+					skb1->arp = 1;
 				}
 				else
 				{
@@ -466,7 +468,7 @@
 ipxitf_demux_socket(ipx_interface *intrfc, struct sk_buff *skb, int copy) 
 {
 	ipx_packet	*ipx = (ipx_packet *)(skb->h.raw);
-	ipx_socket	*sock1 = NULL, *sock2 = NULL;
+	struct sock	*sock1 = NULL, *sock2 = NULL;
 	struct sk_buff	*skb1 = NULL, *skb2 = NULL;
 
 	sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
@@ -523,8 +525,8 @@
 	if (copy) 
 	{
 		skb1 = skb_clone(skb, GFP_ATOMIC);
-		if (skb1 != NULL) 
-			skb1->arp = skb1->free = 1;
+		if (skb1) 
+			skb1->arp=1;
 	} 
 	else 
 	{
@@ -542,7 +544,7 @@
 	{
 		skb2 = skb_clone(skb1, GFP_ATOMIC);
 		if (skb2 != NULL) 
-			skb2->arp = skb2->free = 1;
+			skb2->arp = 1;
 	}
 	else 
 		skb2 = skb1;
@@ -570,7 +572,7 @@
 
 	/* Hopefully, most cases */
 	if (in_offset >= out_offset) {
-		skb->arp = skb->free = 1;
+		skb->arp = 1;
 		return skb;
 	}
 
@@ -580,7 +582,6 @@
 	if (skb2 != NULL) {
 		skb_reserve(skb2,out_offset);
 		skb2->h.raw=skb_put(skb2,skb->len);
-		skb2->free=1;
 		skb2->arp=1;
 		memcpy(skb2->h.raw, skb->h.raw, skb->len);
 	}
@@ -622,11 +623,7 @@
 			/*
 			 *	Don't charge sender
 			 */
-			if(skb->sk)
-			{
-				atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
-				skb->sk=NULL;
-			}
+			skb_orphan(skb);
 			/*
 			 *	Will charge receiver
 			 */
@@ -637,11 +634,8 @@
 		 */
 		if (memcmp(ipx_broadcast_node, node, IPX_NODE_LEN) == 0) 
 		{
-			if (!send_to_wire && skb->sk)
-			{
-				atomic_sub(skb->truesize, &skb->sk->wmem_alloc);
-				skb->sk=NULL;
-			}
+			if (!send_to_wire)
+				skb_orphan(skb);
 			ipxitf_demux_socket(intrfc, skb, send_to_wire);
 			if (!send_to_wire) 
 				return 0;
@@ -702,14 +696,14 @@
 	 */
 	 
 	dump_pkt("IPX snd:", (ipx_packet *)skb->h.raw);
-	dump_data("ETH hdr:", skb->data, skb->h.raw - skb->data);
+	dump_data("ETH hdr:", skb->sk, skb->h.raw - skb->sk);
 #endif
 
 	/*
 	 *	Send it out 
 	 */
-	 
-	dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+	skb->priority = SOPRI_NORMAL; 
+	dev_queue_xmit(skb);
 	return 0;
 }
 
@@ -767,6 +761,74 @@
 		}
 	}
 
+#ifdef CONFIG_IPX_PPROP_ROUTING
+	if( ipx->ipx_type == IPX_TYPE_PPROP && ipx->ipx_tctrl < 8 ) {
+	  int i;
+          ipx_interface *ifcs;
+	  struct sk_buff *skb2;
+  	  long *l;
+	  char *c;
+
+#ifdef DEBUG_IPX_PPROP_ROUTING
+	  printk(KERN_INFO "IPX: PPROP packet received\n"
+		 " Src: %8x:%02x:%02x:%02x:%02x:%02x:%02x:%d/%d\n",
+		 htonl(ipx->ipx_source.net),
+		 ipx->ipx_source.node[0], ipx->ipx_source.node[1],
+		 ipx->ipx_source.node[2], ipx->ipx_source.node[3],
+		 ipx->ipx_source.node[4], ipx->ipx_source.node[5],
+		 htons(ipx->ipx_source.sock),
+		 htons(ipx->ipx_dest.sock)
+		 );
+#endif
+
+	  c = (char *) skb->data;
+	  c += sizeof( struct ipx_packet );
+
+	  l = (long *) c;
+
+#ifdef DEBUG_IPX_PPROP_ROUTING
+	  printk( "IPX: Routing PPROP from net num %08x\n", (unsigned int) htonl(intrfc->if_netnum) );
+	  for( i = 0 ; i < ipx->ipx_tctrl ; i++ )
+	    printk( "IPX: Routing PPROP seen net num %08x\n", (unsigned int) htonl(*l++) );
+	  l = (long *) c;
+#endif
+	  i = 0;
+	  /* dump packet if too many hops or already seen this net */
+	  if( ipx->ipx_tctrl < 8 )
+	    for( ; i < ipx->ipx_tctrl ; i++ )
+	      if( *l++ == intrfc->if_netnum )
+		break;
+
+	  if( i == ipx->ipx_tctrl ) { /* < 8 hops && input itfc not in list */
+	    *l = intrfc->if_netnum; /* insert recvd netnum into list */
+
+	    /* xmit on all other interfaces... */
+	    for ( ifcs = ipx_interfaces; ifcs != NULL ; ifcs = ifcs->if_next) {
+	      /* that aren't in the list */
+	      l = (long *) c;
+	      for( i = 0 ; i <= ipx->ipx_tctrl ; i++ )
+		if( ifcs->if_netnum == *l++ )
+		  break;
+	      if( i - 1 == ipx->ipx_tctrl ) {
+		ipx->ipx_dest.net = ifcs->if_netnum;
+#ifdef DEBUG_IPX_PPROP_ROUTING
+		printk( "IPX: Forward PPROP onto net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
+#endif
+		skb2 = skb_clone(skb, GFP_ATOMIC);
+		ipxrtr_route_skb(skb2);
+	      }
+#ifdef DEBUG_IPX_PPROP_ROUTING
+	      else
+		printk( "IPX: Ignoring PPROP for net num %08x\n", (unsigned int) htonl(ifcs->if_netnum) );
+#endif
+	    }
+	    /* reset netnum in packet */
+	    ipx->ipx_dest.net = intrfc->if_netnum;
+	  }
+
+	}
+#endif
+
 	if (ipx->ipx_dest.net == 0L)
 		ipx->ipx_dest.net = intrfc->if_netnum;
 	if (ipx->ipx_source.net == 0L)
@@ -1275,7 +1337,7 @@
  *	Route an outgoing frame from a socket.
  */
 
-static int ipxrtr_route_packet(ipx_socket *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len, int noblock)
+static int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx, struct iovec *iov, int len, int noblock)
 {
 	struct sk_buff *skb;
 	ipx_interface *intrfc;
@@ -1309,7 +1371,6 @@
 		return err;
 
 	skb_reserve(skb,ipx_offset);
-	skb->free=1;
 	skb->arp=1;
 	skb->sk=sk;
 
@@ -1496,7 +1557,7 @@
 static int ipx_get_info(char *buffer, char **start, off_t offset,
 			int length, int dummy)
 {
-	ipx_socket *s;
+	struct sock *s;
 	ipx_interface *i;
 	int len=0;
 	off_t pos=0;
@@ -1626,10 +1687,10 @@
 
 static int ipx_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
 {
-	ipx_socket *sk;
+	struct sock *sk;
 	int err,opt;
 	
-	sk=(ipx_socket *)sock->data;
+	sk=sock->sk;
 	
 	if (optval==NULL)
 		return(-EINVAL);
@@ -1651,9 +1712,6 @@
 			}
 			break;
 			
-		case SOL_SOCKET:
-			return sock_setsockopt(sk,level,optname,optval,optlen);
-
 		default:
 			return -EOPNOTSUPP;
 	}
@@ -1662,11 +1720,11 @@
 static int ipx_getsockopt(struct socket *sock, int level, int optname,
 	char *optval, int *optlen)
 {
-	ipx_socket *sk;
+	struct sock *sk;
 	int val=0;
 	int err;
 	
-	sk=(ipx_socket *)sock->data;
+	sk=sock->sk;
 
 	switch(level)
 	{
@@ -1682,9 +1740,6 @@
 			}
 			break;
 			
-		case SOL_SOCKET:
-			return sock_getsockopt(sk,level,optname,optval,optlen);
-			
 		default:
 			return -EOPNOTSUPP;
 	}
@@ -1716,16 +1771,17 @@
 
 static int ipx_create(struct socket *sock, int protocol)
 {
-	ipx_socket *sk;
-	sk=(ipx_socket *)sk_alloc(GFP_KERNEL);
+	struct sock *sk;
+	sk=sk_alloc(GFP_KERNEL);
 	if(sk==NULL)
 		return(-ENOMEM);
 	switch(sock->type)
 	{
 		case SOCK_DGRAM:
+			sock->ops = &ipx_dgram_ops;
 			break;
 		default:
-			kfree_s((void *)sk,sizeof(*sk));
+			sk_free(sk);
 			return(-ESOCKTNOSUPPORT);
 	}
 	sk->rcvbuf=SK_RMEM_MAX;
@@ -1742,8 +1798,8 @@
 	sk->no_check = 1;		/* Checksum off by default */	
 	if(sock!=NULL)
 	{
-		sock->data=(void *)sk;
-		sk->sleep=sock->wait;
+		sk->sleep=&sock->wait;
+		sock->sk=sk;
 	}
 	
 	sk->state_change=def_callback1;
@@ -1758,13 +1814,13 @@
 
 static int ipx_release(struct socket *sock, struct socket *peer)
 {
-	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	if(sk==NULL)
 		return(0);
 	if(!sk->dead)
 		sk->state_change(sk);
 	sk->dead=1;
-	sock->data=NULL;
+	sock->sk=NULL;
 	ipx_destroy_socket(sk);
 	return(0);
 }
@@ -1794,11 +1850,11 @@
 	
 static int ipx_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
-	ipx_socket *sk;
+	struct sock *sk;
 	ipx_interface *intrfc;
 	struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr;
 	
-	sk=(ipx_socket *)sock->data;
+	sk=sock->sk;
 	
 	if(sk->zapped==0)
 		return -EINVAL;
@@ -1895,7 +1951,7 @@
 static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
 	int addr_len, int flags)
 {
-	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	struct sockaddr_ipx *addr;
 	
 	sk->state = TCP_CLOSE;	
@@ -1941,8 +1997,8 @@
 
 static int ipx_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	if(newsock->data) {
-		kfree_s(newsock->data,sizeof(ipx_socket));
+	if(newsock->sk) {
+		sk_free(newsock->sk);
 		MOD_DEC_USE_COUNT;
 	}
 	return -EOPNOTSUPP;
@@ -1953,9 +2009,9 @@
 {
 	ipx_address *addr;
 	struct sockaddr_ipx sipx;
-	ipx_socket *sk;
+	struct sock *sk;
 	
-	sk=(ipx_socket *)sock->data;
+	sk=sock->sk;
 	
 	*uaddr_len = sizeof(struct sockaddr_ipx);
 		
@@ -2052,7 +2108,6 @@
 	ipx_interface	*intrfc;
 	ipx_packet *ipx;
 	
-	
 	ipx=(ipx_packet *)skb->h.raw;
 	
 	/* Too small */
@@ -2091,19 +2146,20 @@
 	return ipxitf_rcv(intrfc, skb);
 }
 
-static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock,
-	int flags)
+static int ipx_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+	struct scm_cookie *scm)
 {
-	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)msg->msg_name;
 	struct sockaddr_ipx local_sipx;
 	int retval;
+	int flags = msg->msg_flags;
 
 	if (sk->zapped) 
 		return -EIO; /* Socket not bound */
-	if(flags) 
+	if (flags&~MSG_DONTWAIT)
 		return -EINVAL;
-		
+
 	if(usipx) 
 	{
 		if(sk->protinfo.af_ipx.port == 0) 
@@ -2139,7 +2195,7 @@
 		memcpy(usipx->sipx_node,sk->protinfo.af_ipx.dest_addr.node,IPX_NODE_LEN);
 	}
 	
-	retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len, noblock);
+	retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len, flags&MSG_DONTWAIT);
 	if (retval < 0) 
 		return retval;
 
@@ -2147,28 +2203,24 @@
 }
 
 
-static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
-		 int flags, int *addr_len)
+static int ipx_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+		int flags, struct scm_cookie *scm)
 {
-	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	struct sockaddr_ipx *sipx=(struct sockaddr_ipx *)msg->msg_name;
 	struct ipx_packet *ipx = NULL;
 	int copied = 0;
 	int truesize;
 	struct sk_buff *skb;
 	int err;
-	
+
 	if (sk->zapped)
 		return -ENOTCONN;
 
-
-	skb=skb_recv_datagram(sk,flags,noblock,&err);
+	skb=skb_recv_datagram(sk,flags&~MSG_DONTWAIT,flags&MSG_DONTWAIT,&err);
 	if(skb==NULL)
 		return err;
 	
-	if(addr_len)
-		*addr_len=sizeof(*sipx);
-
 	ipx = (ipx_packet *)(skb->h.raw);
 	truesize=ntohs(ipx->ipx_pktsize) - sizeof(ipx_packet);
 	
@@ -2184,6 +2236,8 @@
 	if (err)
 		return err;
 
+	msg->msg_namelen = sizeof(*sipx);
+
 	if(sipx)
 	{
 		sipx->sipx_family=AF_IPX;
@@ -2201,17 +2255,10 @@
 	return -EOPNOTSUPP;
 }
 
-static int ipx_select(struct socket *sock , int sel_type, select_table *wait)
-{
-	ipx_socket *sk=(ipx_socket *)sock->data;
-	
-	return datagram_select(sk,sel_type,wait);
-}
-
 static int ipx_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
 {
 	long amount=0;
-	ipx_socket *sk=(ipx_socket *)sock->data;
+	struct sock *sk=sock->sk;
 	
 	switch(cmd)
 	{
@@ -2271,10 +2318,14 @@
 	return(0);
 }
 
-static struct proto_ops ipx_proto_ops = {
+static struct net_proto_family ipx_family_ops = {
+	AF_IPX,
+	ipx_create
+};
+
+static struct proto_ops ipx_dgram_ops = {
 	AF_IPX,
 	
-	ipx_create,
 	ipx_dup,
 	ipx_release,
 	ipx_bind,
@@ -2282,7 +2333,7 @@
 	ipx_socketpair,
 	ipx_accept,
 	ipx_getname,
-	ipx_select,
+	datagram_select,
 	ipx_ioctl,
 	ipx_listen,
 	ipx_shutdown,
@@ -2347,7 +2398,7 @@
 void
 ipx_proto_init(struct net_proto *pro)
 {
-	(void) sock_register(ipx_proto_ops.family, &ipx_proto_ops);
+	(void) sock_register(&ipx_family_ops);
 
 	pEII_datalink = make_EII_client();
 	ipx_dix_packet_type.type=htons(ETH_P_IPX);
@@ -2427,7 +2478,7 @@
 	destroy_EII_client(pEII_datalink);
 	pEII_datalink = NULL;
 
-	(void) sock_unregister(ipx_proto_ops.family);
+	(void) sock_unregister(ipx_family_ops.family);
 
 	return;
 }
diff -u --recursive --new-file v2.1.14/linux/net/netbeui/README linux/net/netbeui/README
--- v2.1.14/linux/net/netbeui/README	Thu Jan  1 02:00:00 1970
+++ linux/net/netbeui/README	Thu Dec 12 16:54:26 1996
@@ -0,0 +1,19 @@
+
+NetBEUI is a rather weird protocol. There are about three different set
+of connection and name spaces here.
+
+Firstly we have an array of 802.2 LLC links acting as reliable inter node
+links for the nodes we are talking to do. We create and tear these down as
+needed. In effect it goes around pretending ethernet is a set of bits of
+wire and running pseudo X.25 over it. The LLC code is elsewhere (net/802).
+
+Secondly we have the netbios name space. When we sit on multiple networks
+we have fun. Netbios isnt routable, so we have to arse around looking on
+all our devices for names.
+
+Thirdly we have logical netbeui sessions on top of the whole heap. 
+
+			*Don't blame us*
+
+We didn't design the protocol.
+
diff -u --recursive --new-file v2.1.14/linux/net/netbeui/netbeui.c linux/net/netbeui/netbeui.c
--- v2.1.14/linux/net/netbeui/netbeui.c	Thu Jan  1 02:00:00 1970
+++ linux/net/netbeui/netbeui.c	Thu Dec 12 16:54:26 1996
@@ -0,0 +1,966 @@
+ 
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/route.h>
+#include <linux/inet.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/termios.h>	/* For TIOCOUTQ/INQ */
+#include <net/datalink.h>
+#include <net/p8022.h>
+#include <net/psnap.h>
+#include <net/sock.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/firewall.h>
+
+
+#undef NETBEUI_DEBUG
+
+
+#ifdef NETBEUI_DEBUG
+#define DPRINT(x)		print(x)
+#else
+#define DPRINT(x)
+#endif
+
+#define min(a,b)	(((a)<(b))?(a):(b))
+
+/***********************************************************************************************************************\
+*															*
+*						Handlers for the socket list.						*
+*															*
+\***********************************************************************************************************************/
+
+static netbeui_socket *volatile netbeui_socket_list=NULL;
+
+/*
+ *	Note: Sockets may not be removed _during_ an interrupt or inet_bh
+ *	handler using this technique. They can be added although we do not
+ *	use this facility.
+ */
+ 
+static void netbeui_remove_socket(netbeui_socket *sk)
+{
+	unsigned long flags;
+	netbeui_socket *s;
+	
+	save_flags(flags);
+	cli();
+	
+	s=netbeui_socket_list;
+	if(s==sk)
+	{
+		netbeui_socket_list=s->next;
+		restore_flags(flags);
+		return;
+	}
+	while(s && s->next)
+	{
+		if(s->next==sk)
+		{
+			s->next=sk->next;
+			restore_flags(flags);
+			return;
+		}
+		s=s->next;
+	}
+	restore_flags(flags);
+}
+
+static void netbeui_insert_socket(netbeui_socket *sk)
+{
+	unsigned long flags;
+	save_flags(flags);
+	cli();
+	sk->next=netbeui_socket_list;
+	netbeui_socket_list=sk;
+	restore_flags(flags);
+}
+
+/*
+ *	This is only called from user mode. Thus it protects itself against
+ *	interrupt users but doesn't worry about being called during work.
+ *	Once it is removed from the queue no interrupt or bottom half will
+ *	touch it and we are (fairly 8-) ) safe.
+ */
+
+static void netbeui_destroy_socket(netbeui_socket *sk);
+
+/*
+ *	Handler for deferred kills.
+ */
+ 
+static void netbeui_destroy_timer(unsigned long data)
+{
+	netbeui_destroy_socket((netbeui_socket *)data);
+}
+
+static void netbeui_destroy_socket(netbeui_socket *sk)
+{
+	struct sk_buff *skb;
+	netbeui_remove_socket(sk);
+	
+	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+	{
+		kfree_skb(skb,FREE_READ);
+	}
+	
+	if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead)
+	{
+		sk_free(sk);
+		MOD_DEC_USE_COUNT;
+	}
+	else
+	{
+		/*
+		 *	Someone is using our buffers still.. defer
+		 */
+		init_timer(&sk->timer);
+		sk->timer.expires=jiffies+10*HZ;
+		sk->timer.function=netbeui_destroy_timer;
+		sk->timer.data = (unsigned long)sk;
+		add_timer(&sk->timer);
+	}
+}
+
+
+/*
+ *	Called from proc fs 
+ */
+ 
+int netbeui_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	netbeui_socket *s;
+	int len=0;
+	off_t pos=0;
+	off_t begin=0;
+
+	/*
+	 *	Output the netbeui data for the /proc virtual fs.
+	 */
+
+	len += sprintf (buffer,"Type local_addr  remote_addr tx_queue rx_queue st uid\n");
+	for (s = netbeui_socket_list; s != NULL; s = s->next)
+	{
+		len += sprintf (buffer+len,"%02X   ", s->type);
+		len += sprintf (buffer+len,"%04X:%02X:%02X  ",
+			ntohs(s->protinfo.af_at.src_net),
+			s->protinfo.af_at.src_node,
+			s->protinfo.af_at.src_port);
+		len += sprintf (buffer+len,"%04X:%02X:%02X  ",
+			ntohs(s->protinfo.af_at.dest_net),
+			s->protinfo.af_at.dest_node,
+			s->protinfo.af_at.dest_port);
+		len += sprintf (buffer+len,"%08X:%08X ", s->wmem_alloc, s->rmem_alloc);
+		len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid);
+		
+		/* Are we still dumping unwanted data then discard the record */
+		pos=begin+len;
+		
+		if(pos<offset)
+		{
+			len=0;			/* Keep dumping into the buffer start */
+			begin=pos;
+		}
+		if(pos>offset+length)		/* We have dumped enough */
+			break;
+	}
+	
+	/* The data in question runs from begin to begin+len */
+	*start=buffer+(offset-begin);	/* Start of wanted data */
+	len-=(offset-begin);		/* Remove unwanted header data from length */
+	if(len>length)
+		len=length;		/* Remove unwanted tail data from length */
+	
+	return len;
+}
+
+/*
+ *	A device event has occurred. Watch for devices going down and
+ *	delete our use of them (iface and route).
+ */
+
+static int nb_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+	if(event==NETDEV_DOWN)
+	{
+		/* Discard any use of this */
+		netbeui_drop_device((struct device *)ptr);
+	}
+	return NOTIFY_DONE;
+}
+
+/*******************************************************************************************************************\
+*													            *
+*	      Handling for system calls applied via the various interfaces to a netbeui socket object		    *
+*														    *
+\*******************************************************************************************************************/
+
+/*
+ *	Generic fcntl calls are already dealt with. If we don't need funny ones
+ *	this is the all you need. Async I/O is also separate.
+ */
+  
+static int netbeui_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+/*	netbeui_socket *sk=(netbeui_socket *)sock->data;*/
+	switch(cmd)
+	{
+		default:
+			return(-EINVAL);
+	}
+}
+
+/*
+ *	Set 'magic' options for netbeui. If we don't have any this is fine 
+ *	as it is.
+ */
+ 
+static int netbeui_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	netbeui_socket *sk;
+	int err,opt;
+	
+	sk=(netbeui_socket *)sock->data;
+	
+	if(optval==NULL)
+		return(-EINVAL);
+
+	err = get_user(opt, (int *)optval);
+	if (err)
+		return err;
+	
+	switch(level)
+	{
+		case SOL_NETBEUI:
+			switch(optname)
+			{
+				default:
+					return -EOPNOTSUPP;
+			}
+			break;
+			
+		case SOL_SOCKET:
+			return sock_setsockopt(sk,level,optname,optval,optlen);
+
+		default:
+			return -EOPNOTSUPP;
+	}
+}
+
+
+/*
+ *	Get any magic options. Comment above applies.
+ */
+ 
+static int netbeui_getsockopt(struct socket *sock, int level, int optname,
+	char *optval, int *optlen)
+{
+	netbeui_socket *sk;
+	int val=0;
+	int err;
+	
+	sk=(netbeui_socket *)sock->data;
+
+	switch(level)
+	{
+
+		case SOL_NETBEUI:
+			switch(optname)
+			{
+				default:
+					return -ENOPROTOOPT;
+			}
+			break;
+			
+		case SOL_SOCKET:
+			return sock_getsockopt(sk,level,optname,optval,optlen);
+			
+		default:
+			return -EOPNOTSUPP;
+	}
+	err = put_user(sizeof(int),optlen);
+	if (!err)
+		err = put_user(val, (int *) optval);
+	return err;
+}
+
+/*
+ *	Only for connection oriented sockets - ignore
+ */
+ 
+static int netbeui_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk=(netbeui_socket *)sock->data;
+	if(sk->state!=TCP_CLOSED)
+		return -EINVAL;
+	if(backlog<0)
+		return -EINVAL;
+	if(backlog<128)
+		sk->backlog=backlog;
+	else
+		sk->backlog=128;
+	sk->state=TCP_LISTEN;
+	sk->state_change(sk);
+	netbeui_llc_listen(sk);
+	return 0;	
+}
+
+/*
+ *	These are standard.
+ */
+ 
+static void def_callback1(struct sock *sk)
+{
+	if(!sk->dead)
+		wake_up_interruptible(sk->sleep);
+}
+
+static void def_callback2(struct sock *sk, int len)
+{
+	if(!sk->dead)
+	{
+		wake_up_interruptible(sk->sleep);
+		sock_wake_async(sk->socket,0);
+	}
+}
+
+/*
+ *	Create a socket. Initialise the socket, blank the addresses
+ *	set the state.
+ */
+ 
+static int netbeui_create(struct socket *sock, int protocol)
+{
+	netbeui_socket *sk;
+	sk=(netbeui_socket *)sk_alloc(GFP_KERNEL);
+	if(sk==NULL)
+		return(-ENOBUFS);
+	switch(sock->type)
+	{
+		case SOCK_DGRAM:
+			break;
+		case SOCK_SEQPACKET:
+			break;
+		default:
+			sk_free((void *)sk);
+			return(-ESOCKTNOSUPPORT);
+	}
+	
+	sk->llc802=llc_alloc(GFP_KERNEL);
+	if(sk->llc802==NULL)
+	{
+		sk_free((void *)sk);
+		return -ENOBUFS:
+	}
+	
+	MOD_INC_USE_COUNT;
+
+	sk->allocation=GFP_KERNEL;
+	sk->rcvbuf=SK_RMEM_MAX;
+	sk->sndbuf=SK_WMEM_MAX;
+	sk->pair=NULL;
+	sk->priority=SOPRI_NORMAL;
+	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->write_queue);
+	skb_queue_head_init(&sk->back_log);
+	sk->state=TCP_CLOSE;
+	sk->socket=sock;
+	sk->type=sock->type;
+	sk->mtu=1500;
+	
+	if(sock!=NULL)
+	{
+		sock->data=(void *)sk;
+		sk->sleep=sock->wait;
+	}
+	
+	sk->state_change=def_callback1;
+	sk->data_ready=def_callback2;
+	sk->write_space=def_callback1;
+	sk->error_report=def_callback1;
+	sk->zapped=1;
+	return(0);
+}
+
+/*
+ *	Copy a socket. No work needed.
+ */
+ 
+static int netbeui_dup(struct socket *newsock,struct socket *oldsock)
+{
+	return(netbeui_create(newsock,oldsock->type));
+}
+
+/*
+ *	Free a socket. No work needed
+ */
+ 
+static int netbeui_release(struct socket *sock, struct socket *peer)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	if(sk==NULL)
+		return(0);
+	if(!sk->dead)
+		sk->state_change(sk);
+	sk->dead=1;
+	sock->data=NULL;
+	netbeui_destroy_socket(sk);
+	return(0);
+}
+		
+/*
+ *	Set the address 'our end' of the connection.
+ */
+ 
+static int netbeui_bind(struct socket *sock, struct sockaddr *uaddr,size_t addr_len)
+{
+	netbeui_socket *sk;
+	struct sockaddr_netbeui *addr=(struct sockaddr_netbeui *)uaddr;
+	
+	sk=(netbeui_socket *)sock->data;
+	
+	if(sk->zapped==0)
+		return(-EINVAL);
+		
+	if(addr_len!=sizeof(struct sockaddr_at))
+		return -EINVAL;
+
+	if(addr->sat_family!=AF_NETBEUI)
+		return -EAFNOSUPPORT;
+
+	if(netbeui_find_socket(addr)!=NULL)
+		return -EADDRINUSE;	   
+
+	netbeui_insert_socket(sk);
+	sk->zapped=0;
+	return(0);
+}
+
+/*
+ *	Set the address we talk to.
+ */
+ 
+static int netbeui_connect(struct socket *sock, struct sockaddr *uaddr,
+	size_t addr_len, int flags)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	struct sockaddr_netbeui *addr;
+	
+	sk->state = TCP_CLOSE;	
+	sock->state = SS_UNCONNECTED;
+
+	if(addr_len!=sizeof(*addr))
+		return(-EINVAL);
+	addr=(struct sockaddr_netbeui *)uaddr;
+	
+	if(addr->sat_family!=AF_NETBEUI)
+		return -EAFNOSUPPORT;
+
+/* FIXME - Netbios broadcast semantics ?? */
+	if(addr->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
+		return -EACCES;
+
+	if(sk->zapped)
+		return -EINVAL;
+	
+	if(atrtr_get_dev(&addr->sat_addr)==NULL)
+		return -ENETUNREACH;
+		
+}
+
+/*
+ *	Not relevant
+ */
+ 
+static int netbeui_socketpair(struct socket *sock1, struct socket *sock2)
+{
+	return(-EOPNOTSUPP);
+}
+
+/*
+ *	WRITE ME
+ */
+ 
+static int netbeui_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	if(newsock->data)
+		sk_free(newsock->data);
+	return -EOPNOTSUPP;
+}
+
+/*
+ *	Find the name of a netbeui socket. Just copy the right
+ *	fields into the sockaddr.
+ */
+ 
+static int netbeui_getname(struct socket *sock, struct sockaddr *uaddr,
+	size_t *uaddr_len, int peer)
+{
+	struct sockaddr_netbeui snb;
+	netbeui_socket *sk;
+	
+	sk=(netbeui_socket *)sock->data;
+	if(sk->zapped)
+	{
+		return -EINVAL;
+	}	
+	
+	*uaddr_len = sizeof(struct sockaddr_netbeui);
+		
+	if(peer)
+	{
+		if(sk->state!=TCP_ESTABLISHED)
+			return -ENOTCONN;
+	}
+	else
+	{
+	}
+	snb.snb_family = AF_NETBEUI;
+	memcpy(uaddr,&snb,sizeof(snb));
+	return(0);
+}
+
+/*
+ *	Receive a packet (in skb) from device dev.
+ */
+ 
+static int netbeui_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+	netbeui_socket *sock;
+}
+
+static int netbeui_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	struct sockaddr_at *usat=(struct sockaddr_at *)msg->msg_name;
+	struct sockaddr_at local_snetbeui, gsat;
+	struct sk_buff *skb;
+	struct device *dev;
+	struct ddpehdr *ddp;
+	int size;
+	struct netbeui_route *rt;
+	int loopback=0;
+	int err;
+	
+	if(flags)
+		return -EINVAL;
+		
+	if(len>587)
+		return -EMSGSIZE;
+		
+	if(usat)
+	{
+		if(sk->zapped)
+		{
+			if(netbeui_autobind(sk)<0)
+				return -EBUSY;
+		}
+
+		if(msg->msg_namelen <sizeof(*usat))
+			return(-EINVAL);
+		if(usat->sat_family != AF_NETBEUI)
+			return -EINVAL;
+#if 0 	/* netnetbeui doesn't implement this check */
+		if(usat->sat_addr.s_node==ATADDR_BCAST && !sk->broadcast)
+			return -EPERM;
+#endif			
+	}
+	else
+	{
+		if(sk->state!=TCP_ESTABLISHED)
+			return -ENOTCONN;
+		usat=&local_snetbeui;
+		usat->sat_family=AF_NETBEUI;
+		usat->sat_port=sk->protinfo.af_at.dest_port;
+		usat->sat_addr.s_node=sk->protinfo.af_at.dest_node;
+		usat->sat_addr.s_net=sk->protinfo.af_at.dest_net;
+	}
+	
+	/* Build a packet */
+	
+	if(sk->debug)
+		printk("SK %p: Got address.\n",sk);
+	
+	size=sizeof(struct ddpehdr)+len+nb_dl->header_length;	/* For headers */
+
+	if(usat->sat_addr.s_net!=0 || usat->sat_addr.s_node == ATADDR_ANYNODE)
+	{
+		rt=atrtr_find(&usat->sat_addr);
+		if(rt==NULL)
+			return -ENETUNREACH;	
+		dev=rt->dev;
+	}
+	else
+	{
+		struct at_addr at_hint;
+		at_hint.s_node=0;
+		at_hint.s_net=sk->protinfo.af_at.src_net;
+		rt=atrtr_find(&at_hint);
+		if(rt==NULL)
+			return -ENETUNREACH;
+		dev=rt->dev;
+	}
+
+	if(sk->debug)
+		printk("SK %p: Size needed %d, device %s\n", sk, size, dev->name);
+	
+	size += dev->hard_header_len;
+
+	skb = sock_alloc_send_skb(sk, size, 0, 0 , &err);
+	if(skb==NULL)
+		return err;
+
+	skb->sk=sk;
+	skb->free=1;
+	skb->arp=1;
+	skb_reserve(skb,nb_dl->header_length);
+	skb_reserve(skb,dev->hard_header_len);
+
+	skb->dev=dev;
+	
+	if(sk->debug)
+		printk("SK %p: Begin build.\n", sk);
+	
+	ddp=(struct ddpehdr *)skb_put(skb,sizeof(struct ddpehdr));
+	ddp->deh_pad=0;
+	ddp->deh_hops=0;
+	ddp->deh_len=len+sizeof(*ddp);
+	/*
+	 *	Fix up the length field	[Ok this is horrible but otherwise
+	 *	I end up with unions of bit fields and messy bit field order
+	 *	compiler/endian dependencies..
+	 */
+	*((__u16 *)ddp)=ntohs(*((__u16 *)ddp));
+
+	ddp->deh_dnet=usat->sat_addr.s_net;
+	ddp->deh_snet=sk->protinfo.af_at.src_net;
+	ddp->deh_dnode=usat->sat_addr.s_node;
+	ddp->deh_snode=sk->protinfo.af_at.src_node;
+	ddp->deh_dport=usat->sat_port;
+	ddp->deh_sport=sk->protinfo.af_at.src_port;
+
+	if(sk->debug)
+		printk("SK %p: Copy user data (%d bytes).\n", sk, len);
+		
+	err = memcpy_fromiovec(skb_put(skb,len),msg->msg_iov,len);
+	if (err)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return -EFAULT;
+	}
+		
+	if(sk->no_check==1)
+		ddp->deh_sum=0;
+	else
+		ddp->deh_sum=netbeui_checksum(ddp, len+sizeof(*ddp));
+		
+#ifdef CONFIG_FIREWALL
+
+	if(call_out_firewall(AF_NETBEUI, skb->dev, ddp, NULL)!=FW_ACCEPT)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		return -EPERM;
+	}	
+	
+#endif
+	
+	/*
+	 *	Loopback broadcast packets to non gateway targets (ie routes
+	 *	to group we are in)
+	 */
+	 
+	if(ddp->deh_dnode==ATADDR_BCAST)
+	{
+		if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK)))
+		{
+			struct sk_buff *skb2=skb_clone(skb, GFP_KERNEL);
+			if(skb2)
+			{
+				loopback=1;
+				if(sk->debug)
+					printk("SK %p: send out(copy).\n", sk);
+				if(aarp_send_ddp(dev,skb2,&usat->sat_addr, NULL)==-1)
+					kfree_skb(skb2, FREE_WRITE);
+				/* else queued/sent above in the aarp queue */
+			}
+		}
+	}
+
+	if((dev->flags&IFF_LOOPBACK) || loopback) 
+	{
+		if(sk->debug)
+			printk("SK %p: Loop back.\n", sk);
+		/* loop back */
+		atomic_sub(skb->truesize, &sk->wmem_alloc);
+		nb_dl->datalink_header(nb_dl, skb, dev->dev_addr);
+		skb->sk = NULL;
+		skb->mac.raw=skb->data;
+		skb->h.raw = skb->data + nb_dl->header_length + dev->hard_header_len;
+		skb_pull(skb,dev->hard_header_len);
+		skb_pull(skb,nb_dl->header_length);
+		netbeui_rcv(skb,dev,NULL);
+	}
+	else 
+	{
+		if(sk->debug)
+			printk("SK %p: send out.\n", sk);
+
+		if ( rt->flags & RTF_GATEWAY ) {
+		    gsat.sat_addr = rt->gateway;
+		    usat = &gsat;
+		}
+	
+		if(nb_send_low(dev,skb,&usat->sat_addr, NULL)==-1)
+			kfree_skb(skb, FREE_WRITE);
+		/* else queued/sent above in the aarp queue */
+	}
+	if(sk->debug)
+		printk("SK %p: Done write (%d).\n", sk, len);
+	return len;
+}
+
+
+static int netbeui_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	struct sockaddr_at *sat=(struct sockaddr_at *)msg->msg_name;
+	struct ddpehdr	*ddp = NULL;
+	int copied = 0;
+	struct sk_buff *skb;
+	int er = 0;
+	
+	if(addr_len)
+		*addr_len=sizeof(*sat);
+
+	skb=skb_recv_datagram(sk,flags,noblock,&er);
+	if(skb==NULL)
+		return er;
+
+	ddp = (struct ddpehdr *)(skb->h.raw);
+	if(sk->type==SOCK_RAW)
+	{
+		copied=ddp->deh_len;
+		if(copied > size)
+		{
+			copied=size;
+			msg->msg_flags|=MSG_TRUNC;
+		}
+		er = skb_copy_datagram_iovec(skb,0,msg->msg_iov,copied);
+		if (er)
+			goto out;
+	}
+	else
+	{
+		copied=ddp->deh_len - sizeof(*ddp);
+		if (copied > size)
+		{
+			copied = size;
+			msg->msg_flags|=MSG_TRUNC;
+		}
+		er = skb_copy_datagram_iovec(skb,sizeof(*ddp),msg->msg_iov,copied);
+		if (er) 
+			goto out; 
+	}
+	if(sat)
+	{
+		sat->sat_family=AF_NETBEUI;
+		sat->sat_port=ddp->deh_sport;
+		sat->sat_addr.s_node=ddp->deh_snode;
+		sat->sat_addr.s_net=ddp->deh_snet;
+	}
+out:
+	skb_free_datagram(sk, skb);
+	return er ? er : (copied);
+}		
+
+
+static int netbeui_shutdown(struct socket *sk,int how)
+{
+	return -EOPNOTSUPP;
+}
+
+static int netbeui_select(struct socket *sock , int sel_type, select_table *wait)
+{
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	
+	return datagram_select(sk,sel_type,wait);
+}
+
+/*
+ *	Netbeui ioctl calls.
+ */
+
+static int netbeui_ioctl(struct socket *sock,unsigned int cmd, unsigned long arg)
+{
+	long amount=0;
+	netbeui_socket *sk=(netbeui_socket *)sock->data;
+	
+	switch(cmd)
+	{
+		/*
+		 *	Protocol layer
+		 */
+		case TIOCOUTQ:
+			amount=sk->sndbuf-sk->wmem_alloc;
+			if(amount<0)
+				amount=0;
+			break;
+		case TIOCINQ:
+		{
+			struct sk_buff *skb;
+			/* These two are safe on a single CPU system as only user tasks fiddle here */
+			if((skb=skb_peek(&sk->receive_queue))!=NULL)
+				amount=skb->len-sizeof(struct ddpehdr);
+			break;
+		}
+		case SIOCGSTAMP:
+			if (sk)
+			{
+				if(sk->stamp.tv_sec==0)
+					return -ENOENT;
+				return copy_to_user((void *)arg,&sk->stamp,sizeof(struct timeval)) ? -EFAULT : 0;
+			}
+			return -EINVAL;
+		/*
+		 *	Routing
+		 */
+		case SIOCADDRT:
+		case SIOCDELRT:
+			if(!suser())
+				return -EPERM;
+			return(atrtr_ioctl(cmd,(void *)arg));
+		/*
+		 *	Interface
+		 */			
+		case SIOCGIFADDR:
+		case SIOCSIFADDR:
+		case SIOCGIFBRDADDR:
+			return atif_ioctl(cmd,(void *)arg);
+		/*
+		 *	Physical layer ioctl calls
+		 */
+		case SIOCSIFLINK:
+		case SIOCGIFHWADDR:
+		case SIOCSIFHWADDR:
+		case SIOCGIFFLAGS:
+		case SIOCSIFFLAGS:
+		case SIOCGIFMTU:
+		case SIOCGIFCONF:
+		case SIOCADDMULTI:
+		case SIOCDELMULTI:
+
+			return(dev_ioctl(cmd,(void *) arg));
+
+		case SIOCSIFMETRIC:
+		case SIOCSIFBRDADDR:
+		case SIOCGIFNETMASK:
+		case SIOCSIFNETMASK:
+		case SIOCGIFMEM:
+		case SIOCSIFMEM:
+		case SIOCGIFDSTADDR:
+		case SIOCSIFDSTADDR:
+			return -EINVAL;
+
+		default:
+			return -EINVAL;
+	}
+	return put_user(amount, (int *)arg);
+}
+
+static struct proto_ops netbeui_proto_ops = {
+	AF_NETBEUI,
+	
+	netbeui_create,
+	netbeui_dup,
+	netbeui_release,
+	netbeui_bind,
+	netbeui_connect,
+	netbeui_socketpair,
+	netbeui_accept,
+	netbeui_getname,
+	netbeui_select,
+	netbeui_ioctl,
+	netbeui_listen,
+	netbeui_shutdown,
+	netbeui_setsockopt,
+	netbeui_getsockopt,
+	netbeui_fcntl,
+	netbeui_sendmsg,
+	netbeui_recvmsg
+};
+
+static struct notifier_block nb_notifier={
+	nb_device_event,
+	NULL,
+	0
+};
+
+static char nb_snap_id[]={0x08,0x00,0x07,0x80,0x9B};
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry proc_netbeui = {
+	PROC_NET_NETBEUI, 9, "netbeui",
+	S_IFREG | S_IRUGO, 1, 0, 0
+	0, &proc_net_inode_operations,
+	netbeui_get_info
+};
+#endif
+
+/* Called by proto.c on kernel start up */
+
+void netbeui_proto_init(struct net_proto *pro)
+{
+	(void) sock_register(netbeui_proto_ops.family, &netbeui_proto_ops);
+/* ddp?  isn't it atalk too? 8) */
+	if ((nb_dl = register_snap_client(nb_snap_id, netbeui_rcv)) == NULL)
+		printk(KERN_CRIT "Unable to register DDP with SNAP.\n");
+	
+	register_netdevice_notifier(&nb_notifier);
+
+#ifdef CONFIG_PROC_FS
+	proc_net_register(&proc_netbeui);
+#endif	
+
+	printk(KERN_INFO "NetBEUI 0.02 for Linux NET3.037\n");
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+	netbeui_proto_init(NULL);
+	register_symtab(0);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	unsigned long flags;
+#ifdef CONFIG_PROC_FS
+	proc_net_unregister(PROC_NET_NETBEUI);
+#endif	
+	unregister_netdevice_notifier(&nb_notifier);
+	unregister_snap_client(nb_snap_id);
+	sock_unregister(netbeui_proto_ops.family);
+}
+
+#endif  /* MODULE */
diff -u --recursive --new-file v2.1.14/linux/net/netbeui/netbeui_llc.c linux/net/netbeui/netbeui_llc.c
--- v2.1.14/linux/net/netbeui/netbeui_llc.c	Thu Jan  1 02:00:00 1970
+++ linux/net/netbeui/netbeui_llc.c	Thu Dec 12 16:54:26 1996
@@ -0,0 +1,186 @@
+/*
+ *	Maintain 802.2 LLC logical channels being used by NetBEUI
+ */
+
+
+
+void netbeui_disc_indication(llcptr llc)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	if(nb->users>0)
+		llc_connect_request(&nb->llc);
+}
+
+void netbeui_disc_confirm(llcptr llc)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	if(nb->users>0)
+		llc_connect_request(&nb->llc);
+	else
+	{
+		netbeui_destroy_channel(nb);
+	}
+}
+
+void netbeui_connect_confirm(llcptr llc)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	nb->status=SS_CONNECTED;
+	wakeup(&nb->wait_queue);
+}
+
+void netbeui_connect_indication(llcptr llc)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	nb->status=SS_CONNECTED;
+	wakeup(&nb->wait_queue);
+}
+
+void netbeui_reset_confirm(llcptr llc)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	/* Good question .. */
+}
+
+void netbeui_reset_indication(llcptr llc, char lr)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	printk("netbeui: 802.2LLC reset.\n");
+	/* Good question too */
+}
+	 
+void netbeui_data_indication(llcptr llc, struct sk_buff *skb)
+{
+	netbeui_rcv_seq(LLC_TO_NB(llc),skb);
+}
+
+int netbeui_unit_data_indication(llcptr llc, struct sk_buff *skb)
+{
+	return netbeui_rcv_dgram(LLC_TO_NB(llc),skb);
+}
+
+int netbeui_xid_indication(llcptr llc, int ll, char *data)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	/* No action needed */
+}
+
+int netbeui_test_indication(llcptr llc, int ll, char *data)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	/* No action needed */
+}
+
+void netbeui_report_status(llcptr llc, char status)
+{
+	struct nb_link *nb=LLC_TO_NB(llc);
+	switch(status)
+	{
+		case FRMR_RECEIVED:
+		case FRMR_SENT:
+			printk("netbeui: FRMR event %d\n",status);
+			break;	/* FRMR's - shouldnt occur - debug log */
+		case REMOTE_BUSY:
+			nb->busy=1;
+			break;
+		case REMOTE_NOT_BUSY:
+			nb->busy=0;
+			wakeup(&nb->wakeup);
+			break;
+		default:
+			printk("LLC passed netbeui bogus state %d\n",status);
+			break;
+	}
+}
+
+struct llc_ops netbeui_ops=
+{
+	netbeui_data_indication,		/* Sequenced frame */
+	netbeui_unit_data_indication,		/* Datagrams */
+	netbeui_connect_indication,		/* They called us */
+	netbeui_connect_confirm,		/* We called them, they OK'd */
+	netbeui_data_connect_indication,	/* Erm ?????? */
+	netbeui_data_connect_confirm,		/* Erm ?????? */
+	netbeui_disc_indication,		/* They closed */
+	netbeui_disc_confirm,			/* We closed they OK'd */
+	netbeui_reset_confirm,			/* Our reset worked */
+	netbeui_reset_indication,		/* They reset on us */
+	netbeui_xid_indication,			/* An XID frame */
+	netbeui_test_indication,		/* A TEST frame */
+	netbeui_report_status			/* Link state change */
+};
+
+/*
+ *	Create a new outgoing session
+ */
+ 
+struct nb_link *netbeui_create_channel(struct device *dev, u8 *remote_mac, int pri)
+{
+	struct nb_link *nb=netbeui_find_channel(dev,remote_mac);
+	if(nb)
+	{
+		nb->users++;
+		return nb;
+	}
+	nb=netbeui_alloc_link(pri);
+	if(nb==NULL)
+		return NULL;
+	
+	/*
+	 *	Internal book keeping
+	 */
+	 
+	nb->dev=dev;
+	init_timer(&nb->timer);
+	nb->timer.function=netbeui_link_timer;
+	nb->users=1;
+	nb->busy=0;
+	nb->wakeup=NULL;
+	nb->status=SS_CONNECTING;
+	memcpy(nb->remote_mac, remote_mac, ETH_ALEN);
+	
+	/*
+	 *	Now try and attach an LLC.
+	 */
+	
+	if(register_cl2llc_client(&nb->llc,dev->name,&nebeui_llcops,
+		remote_mac, NETBEUI_SAP, NETBEUI_SAP)<0)
+	{
+		netbeui_free_link(nb);
+		return NULL;
+	}
+	
+	/*
+	 *	Commence connection establishment.
+	 */
+	 
+	llc_connect_request(&nb->llc);
+	
+	/*
+	 *	Done
+	 */
+
+	nb->next=nb_link_list;
+	nb_link_list=nb;
+	 
+	return nb;
+}
+	
+int netbeui_delete_channel(struct nb_link *nb)
+{
+	nb->users--;
+	if(nb->users)
+		return 0;
+		
+	llc_disconnect_request(lp);
+	/*
+	 *	Ensure we drop soon. The disconnect confirm will let
+	 *	us fix the deletion
+	 */
+	nb->state = SS_DISCONNECTING;
+	nb->timer.expires=jiffies+NB_DROP_TIMEOUT;
+	add_timer(&nb->timer);
+	return 0;
+}
+
+
diff -u --recursive --new-file v2.1.14/linux/net/netbeui/netbeui_name.c linux/net/netbeui/netbeui_name.c
--- v2.1.14/linux/net/netbeui/netbeui_name.c	Thu Jan  1 02:00:00 1970
+++ linux/net/netbeui/netbeui_name.c	Thu Dec 12 16:54:26 1996
@@ -0,0 +1,159 @@
+/*
+ *	NetBIOS name handler
+ */
+
+/*
+ *	You must hold the netbios name lock before using these.
+ */
+  
+struct nb_name *nb_name_find(struct device *dev,const char * name)
+{
+	struct nb_name *nb=nb_name_list;
+	while(nb!=NULL)
+	{
+		if((dev==NULL || dev==nb->dev) && 
+			strncmp(name,nb->name, NB_NAME_LEN)==0)
+			return nb;
+		nb=nb->next;
+	}
+	return NULL;
+}
+
+int nb_name_add(struct device *dev, const char *name, int ours, int pri)
+{
+	struct nb_name *nb=kmalloc(sizeof(*nb), pri);
+	if(nb==NULL)
+		return NULL;
+	nb->dev=dev;
+	strncpy(nb->name,name,NB_NAME_LEN);
+	nb->name[NB_NAME_LEN-1]=0;
+	nb->next=nb_name_list;
+	nb->ours=ours;
+	nb_name_list=nb;
+}
+
+void nb_name_delete(struct nb_name *nb)
+{
+	struct nb_name *i=&nb_name_list;
+	while((*i)!=NULL)
+	{
+		if(*i==nb)
+		{
+			*i=nb->next;
+			kfree_s(nb,sizeof(*nb));
+			return;
+		}
+		i=&((*i)->next);
+	}
+	printk(KERN_ERR "nb_name_delete: bad name pointer!\n");
+}
+
+/*
+ *	NETBIOS name handlers
+ */
+
+static void nb_defend(struct device *dev, const char *name)
+{
+	struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC);
+	if(nskb==NULL)
+		return;
+	/* Build a name defence packet */
+	dev_queue_xmit(nskb,dev,SOPRI_INTERACTIVE);
+}
+
+void netbeui_heard_name(struct device *dev, struct sk_buff *skb)
+{
+	struct nb_name *nb;
+	name=...
+	
+	if((nb=nb_name_find(dev,name))!=NULL)
+	{
+		/*
+		 *	If we own the name then defend it
+		 */
+		if(nb->our && !nb->state==NB_ACQUIRE)
+			nb_defend(dev,name);
+		/*
+		 *	A name has been resolved. Wake up pending
+		 *	connectors.
+		 */
+		if(nb->state==NB_QUERY)
+		{
+			nb->state=NB_OTHER;
+			nb_complete(nb,skb);
+		}
+	}
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+/*
+ *	Handle incoming name defences
+ */
+ 
+void netbeui_name_defence(struct dev *dev, struct sk_buff *skb)
+{
+	struct nb_name *name;
+	name=
+	
+	if((nb=nb_name_find(dev,name))!=NULL)
+	{
+		if(nb->ours)
+		{
+			/*	
+			 *	We wanted it, we got told its used
+			 */
+			if(nb->state==NB_ACQUIRE)
+			{
+				/*
+				 *	Fill in the record for its true
+				 *	owner. Set the state first as
+				 *	nb_complete may well delete the
+				 *	record.
+				 */
+				nb->state=NB_OTHER;
+				nb_complete(nb,skb);
+				nb_wakeup();
+			}
+			/*
+			 *	We own it we got told its used. This is
+			 *	a deep cack even that can only occur when
+			 *	a bridge comes back and the net was split.
+			 *	Make sure both sides lose.
+			 */
+			if(nb->state==NB_OURS || nb->state==NB_COLLIDE)
+			{
+				nb->state=NR_COLLIDE;
+				nb_wakeup();
+				/*
+				 *	Kill the other copy too
+				 */
+				nb_defend(dev,name);	
+				/*
+				 *	Timer expiry will delete our
+				 *	record.
+				 */	
+				nb_start_timer(nb, NB_TIME_COLLIDED);
+			}		
+		}
+	}
+	kfree_skb(skb, FREE_READ);
+}
+
+void netbeui_name_query(struct dev *dev, struct sk_buff *skb)
+{
+	char *name=...
+	struct nb_name *nb=nb_find_name(dev,name);
+	
+	if(nb!=NULL && nb->ours)
+	{
+		struct sk_buff *nskb=nb_alloc_skb(NB_CONTROL_LEN, GFP_ATOMIC);
+		if(nskb!=NULL)
+		{
+			/* Build a name reply packet */
+			dev_queue_xmit(nskb,dev,SOPRI_INTERACTIVE);
+		}
+	}
+	kfree_skb(skb, FREE_READ);
+}
+
diff -u --recursive --new-file v2.1.14/linux/net/netlink.c linux/net/netlink.c
--- v2.1.14/linux/net/netlink.c	Tue Nov 19 15:54:02 1996
+++ linux/net/netlink.c	Thu Dec 12 16:54:26 1996
@@ -1,5 +1,5 @@
 /*
- * SKIPLINK	An implementation of a loadable kernel mode driver providing
+ * NETLINK	An implementation of a loadable kernel mode driver providing
  *		multiple kernel/user space bidirectional communications links.
  *
  * 		Author: 	Alan Cox <alan@cymru.net>
@@ -17,11 +17,11 @@
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/sched.h>
-#include <linux/lp.h>
 #include <linux/malloc.h>
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
+#include <linux/interrupt.h>
 #include <linux/skbuff.h>
 
 #include <net/netlink.h>
@@ -35,8 +35,8 @@
 static int rdq_size[MAX_LINKS];
 static struct wait_queue *read_space_wait[MAX_LINKS];
 
-static int active_map = 0;
-static int open_map = 0;
+static unsigned active_map = 0;
+static unsigned open_map = 0;
 
 /*
  *	Device operations
@@ -89,7 +89,6 @@
 	unsigned int minor = MINOR(inode->i_rdev);
 	struct sk_buff *skb;
 	skb=alloc_skb(count, GFP_KERNEL);
-	skb->free=1;
 	err = copy_from_user(skb_put(skb,count),buf, count);
 	return err ? -EFAULT : (netlink_handler[minor])(minor,skb);
 }
@@ -140,11 +139,14 @@
 	
 	if(minor>=MAX_LINKS)
 		return -ENODEV;
-	if(open_map&(1<<minor))
-		return -EBUSY;
 	if(active_map&(1<<minor))
 	{
-		open_map|=(1<<minor);
+		if (file->f_mode & FMODE_READ)
+		{
+			if (open_map&(1<<minor))
+				return -EBUSY;
+			open_map|=(1<<minor);
+		}
 		MOD_INC_USE_COUNT;
 		return 0;
 	}
@@ -154,7 +156,8 @@
 static void netlink_release(struct inode * inode, struct file * file)
 {
 	unsigned int minor = MINOR(inode->i_rdev);
-	open_map&=~(1<<minor);	
+	if (file->f_mode & FMODE_READ)
+		open_map&=~(1<<minor);
 	MOD_DEC_USE_COUNT;
 }
 
@@ -231,6 +234,215 @@
 	}
 	return ret;
 }
+
+
+/*
+ *	"High" level netlink interface. (ANK)
+ *	
+ *	Features:
+ *		- standard message format.
+ *		- pseudo-reliable delivery. Messages can be still lost, but
+ *		  user level will know that they were lost and can
+ *		  recover (f.e. gated could reread FIB and device list)
+ *		- messages are batched.
+ *		- if user is not attached, we do not make useless work.
+ *
+ *	Examples:
+ *		- netlink_post equivalent (but with pseudo-reliable delivery)
+ *			ctl.nlmsg_delay = 0;
+ *			ctl.nlmsg_maxsize = <one message size>;
+ *			....
+ *			msg = nlmsg_send(&ctl, ...);
+ *			if (msg) {
+ *				... make it ...
+ *				nlmsg_transmit(&ctl);
+ *			}
+ *
+ *		- batched messages.
+ *		  	if nlmsg_delay==0, messages are delivered only
+ *			by nlmsg_transmit, or when batch is completed,
+ *			otherwise nlmsg_transmit is noop (only starts
+ *			timer)
+ *
+ *			ctl.nlmsg_delay = ...;
+ *			ctl.nlmsg_maxsize = <one batch size>;
+ *			....
+ *			msg = nlmsg_send(&ctl, ...);
+ *			if (msg)
+ *				... make it ...
+ *			....
+ *			msg = nlmsg_send(&ctl, ...);
+ *			if (msg)
+ *				... make it ...
+ *			....
+ *			if (ctl.nlmsg_skb)
+ *				nlmsg_transmit(&ctl);
+ *
+ */
+
+/*
+ *	Try to deliver queued messages.
+ *	If the delivery fails (netlink is not attached or congested),
+ *	do not free skb to avoid useless new message creation.
+ *
+ *	Notes:
+ *		- timer should be already stopped.
+ *		- NET SPL.
+ */
+
+void nlmsg_flush(struct nlmsg_ctl *ctl)
+{
+	if (ctl->nlmsg_skb == NULL)
+		return;
+
+	if (netlink_post(ctl->nlmsg_unit, ctl->nlmsg_skb) == 0)
+	{
+		ctl->nlmsg_skb = NULL;
+		return;
+	}
+
+	ctl->nlmsg_timer.expires = jiffies + NLMSG_RECOVERY_TIMEO;
+	ctl->nlmsg_timer.data = (unsigned long)ctl;
+	ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+	add_timer(&ctl->nlmsg_timer);
+	return;
+}
+
+
+/*
+ *	Allocate room for new message. If it is impossible,
+ *	start "overrun" mode and return NULL.
+ *
+ *	Notes:
+ *		- NET SPL.
+ */
+
+void* nlmsg_send(struct nlmsg_ctl *ctl, unsigned long type, int len,
+		 unsigned long seq, unsigned long pid)
+{
+	struct nlmsghdr *nlh;
+	struct sk_buff *skb;
+	int	rlen;
+
+	static __inline__ void nlmsg_lost(struct nlmsg_ctl *ctl,
+					  unsigned long seq)
+	{
+		if (!ctl->nlmsg_overrun)
+		{
+			ctl->nlmsg_overrun_start = seq;
+			ctl->nlmsg_overrun_end = seq;
+			ctl->nlmsg_overrun = 1;
+			return;
+		}
+		if (!ctl->nlmsg_overrun_start)
+			ctl->nlmsg_overrun_start = seq;
+		if (seq)
+			ctl->nlmsg_overrun_end = seq;
+	}
+
+	if (!(open_map&(1<<ctl->nlmsg_unit)))
+	{
+		nlmsg_lost(ctl, seq);
+		return NULL;
+	}
+
+	rlen = NLMSG_ALIGN(len + sizeof(struct nlmsghdr));
+
+	if (rlen > ctl->nlmsg_maxsize)
+	{
+		printk(KERN_ERR "nlmsg_send: too big message\n");
+		return NULL;
+	}
+
+	if ((skb=ctl->nlmsg_skb) == NULL || skb_tailroom(skb) < rlen)
+	{
+		if (skb)
+		{
+			ctl->nlmsg_force++;
+			nlmsg_flush(ctl);
+			ctl->nlmsg_force--;
+		}
+
+		if (ctl->nlmsg_skb ||
+		    (skb=alloc_skb(ctl->nlmsg_maxsize, GFP_ATOMIC)) == NULL)
+		{
+			printk (KERN_WARNING "nlmsg at unit %d overrunned\n", ctl->nlmsg_unit);
+			nlmsg_lost(ctl, seq);
+			return NULL;
+		}
+
+		ctl->nlmsg_skb = skb;
+
+		if (ctl->nlmsg_overrun)
+		{
+			int *seqp;
+			nlh = (struct nlmsghdr*)skb_put(skb, sizeof(struct nlmsghdr) + 2*sizeof(unsigned long));
+			nlh->nlmsg_type = NLMSG_OVERRUN;
+			nlh->nlmsg_len = sizeof(struct nlmsghdr) + 2*sizeof(unsigned long);
+			nlh->nlmsg_seq = 0;
+			nlh->nlmsg_pid = 0;
+			seqp = (int*)nlh->nlmsg_data;
+			seqp[0] = ctl->nlmsg_overrun_start;
+			seqp[1] = ctl->nlmsg_overrun_end;
+			ctl->nlmsg_overrun = 0;
+		}
+		if (ctl->nlmsg_timer.function)
+		{
+			del_timer(&ctl->nlmsg_timer);
+			ctl->nlmsg_timer.function = NULL;
+		}
+		if (ctl->nlmsg_delay)
+		{
+			ctl->nlmsg_timer.expires = jiffies + ctl->nlmsg_delay;
+			ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+			ctl->nlmsg_timer.data = (unsigned long)ctl;
+			add_timer(&ctl->nlmsg_timer);
+		}
+	}
+
+	nlh = (struct nlmsghdr*)skb_put(skb, rlen);
+	nlh->nlmsg_type = type;
+	nlh->nlmsg_len = sizeof(struct nlmsghdr) + len;
+	nlh->nlmsg_seq = seq;
+	nlh->nlmsg_pid = pid;
+	return nlh->nlmsg_data;
+}
+
+/*
+ *	Kick message queue.
+ *	Two modes:
+ *		- synchronous (delay==0). Messages are delivered immediately.
+ *		- delayed. Do not deliver, but start delivery timer.
+ */
+
+void nlmsg_transmit(struct nlmsg_ctl *ctl)
+{
+	start_bh_atomic();
+
+	if (!ctl->nlmsg_delay)
+	{
+		if (ctl->nlmsg_timer.function)
+		{
+			del_timer(&ctl->nlmsg_timer);
+			ctl->nlmsg_timer.function = NULL;
+		}
+		ctl->nlmsg_force++;
+		nlmsg_flush(ctl);
+		ctl->nlmsg_force--;
+		end_bh_atomic();
+		return;
+	}
+	if (!ctl->nlmsg_timer.function)
+	{
+		ctl->nlmsg_timer.expires = jiffies + ctl->nlmsg_delay;
+		ctl->nlmsg_timer.function = (void (*)(unsigned long))nlmsg_flush;
+		ctl->nlmsg_timer.data = (unsigned long)ctl;
+		add_timer(&ctl->nlmsg_timer);
+	}
+
+	end_bh_atomic();
+}
+
 
 int init_netlink(void)
 {
diff -u --recursive --new-file v2.1.14/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.14/linux/net/netrom/af_netrom.c	Sat Nov 30 12:03:14 1996
+++ linux/net/netrom/af_netrom.c	Thu Dec 12 16:54:26 1996
@@ -1,10 +1,10 @@
 /*
- *	NET/ROM release 005
+ *	NET/ROM release 006
  *
  *	This is ALPHA test software. This code may break your machine, randomly fail to work with new 
  *	releases, misbehave and/or generally screw up. It might even work. 
  *
- *	This code REQUIRES 2.1.0 or higher/ NET3.037
+ *	This code REQUIRES 2.1.15 or higher/ NET3.039
  *
  *	This module:
  *		This module is free software; you can redistribute it and/or
@@ -29,6 +29,7 @@
  *	NET/ROM 004	Jonathan(G4KLX)	Converted to module.
  *	NET/ROM 005	Jonathan(G4KLX) Linux 2.1
  *			Alan(GW4PTS)	Started POSIXisms
+ *	NET/ROM 006	Alan(GW4PTS)	Brought in line with the ANK changes
  */
   
 #include <linux/config.h>
@@ -79,6 +80,9 @@
 
 static struct sock *volatile nr_list = NULL;
 
+static struct proto_ops nr_proto_ops;
+
+
 /*
  *	Socket removal during an interrupt is now safe.
  */
@@ -233,7 +237,7 @@
 /*
  *	Deferred destroy.
  */
-void nr_destroy_socket(struct sock *);
+void nr_destroy_socket(struct sock * sk);
 
 /*
  *	Handler for deferred kills.
@@ -399,11 +403,8 @@
 	struct sock *sk;
 	int err, opt;
 
-	sk = (struct sock *)sock->data;
+	sk = sock->sk;
 	
-	if (level == SOL_SOCKET)
-		return sock_setsockopt(sk, level, optname, optval, optlen);
-
 	if (level != SOL_NETROM)
 		return -EOPNOTSUPP;
 
@@ -468,10 +469,7 @@
 	int val = 0;
 	int err; 
 
-	sk = (struct sock *)sock->data;
-	
-	if (level == SOL_SOCKET)
-		return sock_getsockopt(sk, level, optname, optval, optlen);
+	sk = sock->sk;
 	
 	if (level != SOL_NETROM)
 		return -EOPNOTSUPP;
@@ -524,7 +522,7 @@
 
 static int nr_listen(struct socket *sock, int backlog)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 
 	if (sk->state != TCP_LISTEN) {
 		memset(&sk->protinfo.nr->user_addr, '\0', AX25_ADDR_LEN);
@@ -569,6 +567,8 @@
 	skb_queue_head_init(&sk->back_log);
 
 	init_timer(&sk->timer);
+	
+	sock->ops	  = &nr_proto_ops;
 
 	sk->socket        = sock;
 	sk->type          = sock->type;
@@ -588,8 +588,8 @@
 	sk->error_report = def_callback1;
 
 	if (sock != NULL) {
-		sock->data = (void *)sk;
-		sk->sleep  = sock->wait;
+		sock->sk = sk;
+		sk->sleep  = &sock->wait;
 	}
 
 	skb_queue_head_init(&nr->ack_queue);
@@ -647,7 +647,7 @@
 	if (osk->type != SOCK_SEQPACKET)
 		return NULL;
 
-	if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
@@ -715,14 +715,14 @@
 
 static int nr_dup(struct socket *newsock, struct socket *oldsock)
 {
-	struct sock *sk = (struct sock *)oldsock->data;
+	struct sock *sk = oldsock->sk;
 
 	return nr_create(newsock, sk->protocol);
 }
 
 static int nr_release(struct socket *sock, struct socket *peer)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 
 	if (sk == NULL) return 0;
 
@@ -774,7 +774,7 @@
 			break;
 	}
 
-	sock->data = NULL;	
+	sock->sk = NULL;	
 	sk->socket = NULL;	/* Not used, but we should do this. **/
 
 	return 0;
@@ -787,7 +787,7 @@
 	struct device *dev;
 	ax25_address *user, *source;
 	
-	sk = (struct sock *)sock->data;
+	sk = sock->sk;
 
 	if (sk->zapped == 0)
 		return -EINVAL;
@@ -836,7 +836,7 @@
 static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
 	int addr_len, int flags)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 	struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
 	ax25_address *user, *source = NULL;
 	struct device *dev;
@@ -939,12 +939,12 @@
 	struct sock *newsk;
 	struct sk_buff *skb;
 
-	if (newsock->data)
-		sk_free(newsock->data);
+	if (newsock->sk)
+		sk_free(newsock->sk);
 
-	newsock->data = NULL;
+	newsock->sk = NULL;
 	
-	sk = (struct sock *)sock->data;
+	sk = sock->sk;
 
 	if (sk->type != SOCK_SEQPACKET)
 		return -EOPNOTSUPP;
@@ -979,7 +979,7 @@
 	skb->sk = NULL;
 	kfree_skb(skb, FREE_READ);
 	sk->ack_backlog--;
-	newsock->data = newsk;
+	newsock->sk = newsk;
 
 	return 0;
 }
@@ -990,7 +990,7 @@
 	struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
 	struct sock *sk;
 	
-	sk = (struct sock *)sock->data;
+	sk = sock->sk;
 	
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
@@ -1126,9 +1126,9 @@
 	return 1;
 }
 
-static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
+static int nr_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 	struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
 	int err;
 	struct sockaddr_ax25 sax;
@@ -1136,10 +1136,7 @@
 	unsigned char *asmptr;
 	int size;
 	
-	if (sk->err)
-		return sock_error(sk);
-
-	if (flags)
+	if (msg->msg_flags&~MSG_DONTWAIT)
 		return -EINVAL;
 
 	if (sk->zapped)
@@ -1177,11 +1174,10 @@
 
 	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + NR_NETWORK_LEN + NR_TRANSPORT_LEN;
 
-	if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags&MSG_DONTWAIT, &err)) == NULL)
 		return err;
 
 	skb->sk   = sk;
-	skb->free = 1;
 	skb->arp  = 1;
 
 	skb_reserve(skb, size - len);
@@ -1234,18 +1230,15 @@
 }
 
 
-static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
-		   int flags, int *addr_len)
+static int nr_recvmsg(struct socket *sock, struct msghdr *msg, int size, 
+	int flags, struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 	struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
 	int copied;
 	struct sk_buff *skb;
 	int er;
 
-	if (addr_len != NULL)
-		*addr_len = sizeof(*sax);
-
 	/*
 	 * This works for seqpacket too. The receiver has ordered the queue for
 	 * us! We do one quick check first though
@@ -1255,7 +1248,7 @@
 		return -ENOTCONN;
 
 	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+	if ((skb = skb_recv_datagram(sk, flags, msg->msg_flags&MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
 	if (!sk->protinfo.nr->hdrincl) {
@@ -1280,11 +1273,11 @@
 
 		*sax = addr;
 
-		*addr_len = sizeof(*sax);
 	}
 
-	skb_free_datagram(sk, skb);
+	msg->msg_namelen=sizeof(*sax);
 
+	skb_free_datagram(sk, skb);
 	return copied;
 }
 
@@ -1293,16 +1286,9 @@
 	return -EOPNOTSUPP;
 }
 
-static int nr_select(struct socket *sock , int sel_type, select_table *wait)
-{
-	struct sock *sk = (struct sock *)sock->data;
-
-	return datagram_select(sk, sel_type, wait);
-}
-
 static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = sock->sk;
 	int err;
 	long amount = 0;
 
@@ -1427,10 +1413,15 @@
 	return(len);
 } 
 
-struct proto_ops nr_proto_ops = {
+static struct net_proto_family netrom_family_ops = 
+{
+	AF_NETROM,
+	nr_create
+};
+
+static struct proto_ops nr_proto_ops = {
 	AF_NETROM,
 	
-	nr_create,
 	nr_dup,
 	nr_release,
 	nr_bind,
@@ -1438,7 +1429,7 @@
 	nr_socketpair,
 	nr_accept,
 	nr_getname,
-	nr_select,
+	datagram_select,
 	nr_ioctl,
 	nr_listen,
 	nr_shutdown,
@@ -1477,9 +1468,9 @@
 
 void nr_proto_init(struct net_proto *pro)
 {
-	sock_register(nr_proto_ops.family, &nr_proto_ops);
+	sock_register(&netrom_family_ops);
 	register_netdevice_notifier(&nr_dev_notifier);
-	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.5 for AX25.034 Linux 2.1\n");
+	printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.034 Linux 2.1\n");
 
 	if (!ax25_protocol_register(AX25_P_NETROM, nr_route_frame))
 		printk(KERN_ERR "NET/ROM unable to register protocol with AX.25\n");
diff -u --recursive --new-file v2.1.14/linux/net/netrom/nr_dev.c linux/net/netrom/nr_dev.c
--- v2.1.14/linux/net/netrom/nr_dev.c	Tue Nov 12 15:56:16 1996
+++ linux/net/netrom/nr_dev.c	Thu Dec 12 16:54:26 1996
@@ -110,15 +110,14 @@
 	return -37;	
 }
 
-static int nr_rebuild_header(void *buff, struct device *dev,
-	unsigned long raddr, struct sk_buff *skb)
+static int nr_rebuild_header(struct sk_buff *skb)
 {
+	struct device *dev=skb->dev;
 	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
-	unsigned char *bp = (unsigned char *)buff;
 	struct sk_buff *skbn;
-
-	if (!arp_query(bp + 7, raddr, dev)) {
-		dev_kfree_skb(skb, FREE_WRITE);
+	unsigned char *bp=skb->data;
+	if (!arp_find(bp + 7, skb)) {
+		kfree_skb(skb, FREE_WRITE);
 		return 1;
 	}
 
@@ -132,19 +131,17 @@
 	bp[6] |= SSSID_SPARE;
 
 	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 		return 1;
 	}
 
-	skbn->sk = skb->sk;
-	
 	if (skbn->sk != NULL)
-		atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+		skb_set_owner_w(skbn, skb->sk);
 
-	dev_kfree_skb(skb, FREE_WRITE);
+	kfree_skb(skb, FREE_WRITE);
 
 	if (!nr_route_frame(skbn, NULL)) {
-		dev_kfree_skb(skbn, FREE_WRITE);
+		kfree_skb(skbn, FREE_WRITE);
 		stats->tx_errors++;
 	}
 
@@ -214,7 +211,7 @@
 
 	sti();
 
-	dev_kfree_skb(skb, FREE_WRITE);
+	kfree_skb(skb, FREE_WRITE);
 
 	stats->tx_errors++;
 
diff -u --recursive --new-file v2.1.14/linux/net/netrom/nr_in.c linux/net/netrom/nr_in.c
--- v2.1.14/linux/net/netrom/nr_in.c	Thu Dec 12 17:02:48 1996
+++ linux/net/netrom/nr_in.c	Thu Dec 12 16:54:26 1996
@@ -67,10 +67,8 @@
 		if ((skbn = alloc_skb(sk->protinfo.nr->fraglen, GFP_ATOMIC)) == NULL)
 			return 1;
 
-		skbn->free = 1;
 		skbn->arp  = 1;
-		skbn->sk   = sk;
-		sk->rmem_alloc += skbn->truesize;
+		skb_set_owner_r(skbn, sk);
 		skbn->h.raw = skbn->data;
 
 		skbo = skb_dequeue(&sk->protinfo.nr->frag_queue);
@@ -282,7 +280,6 @@
 					} else if (nr_in_rx_window(sk, ns)) {
 						skb_queue_tail(&temp_queue, skbn);
 					} else {
-						skbn->free = 1;
 						kfree_skb(skbn, FREE_READ);
 					}
 				}
diff -u --recursive --new-file v2.1.14/linux/net/netrom/nr_out.c linux/net/netrom/nr_out.c
--- v2.1.14/linux/net/netrom/nr_out.c	Fri Nov 22 18:28:24 1996
+++ linux/net/netrom/nr_out.c	Thu Dec 12 16:54:26 1996
@@ -66,7 +66,6 @@
 				return;
 
 			skbn->sk   = sk;
-			skbn->free = 1;
 			skbn->arp  = 1;
 
 			skb_reserve(skbn, frontlen);
@@ -87,7 +86,6 @@
 			skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
 		}
 		
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	} else {
 		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
diff -u --recursive --new-file v2.1.14/linux/net/netrom/nr_subr.c linux/net/netrom/nr_subr.c
--- v2.1.14/linux/net/netrom/nr_subr.c	Tue Nov 12 15:56:16 1996
+++ linux/net/netrom/nr_subr.c	Thu Dec 12 16:54:26 1996
@@ -50,13 +50,11 @@
 
 	while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		skb->sk   = sk;
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	}
 
 	while ((skb = skb_dequeue(&sk->protinfo.nr->ack_queue)) != NULL) {
 		skb->sk   = sk;
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	}
 
@@ -85,7 +83,6 @@
 		while (skb_peek(&sk->protinfo.nr->ack_queue) != NULL && sk->protinfo.nr->va != nr) {
 		        skb = skb_dequeue(&sk->protinfo.nr->ack_queue);
 		        skb->sk   = sk;
-			skb->free = 1;
 			kfree_skb(skb, FREE_WRITE);
 			sk->protinfo.nr->va = (sk->protinfo.nr->va + 1) % NR_MODULUS;
 		}
@@ -234,8 +231,6 @@
 			break;
 	}
 
-	skb->free = 1;
-
 	nr_transmit_buffer(sk, skb);
 }
 
@@ -279,7 +274,6 @@
 	*dptr++ = NR_CONNACK | NR_CHOKE_FLAG;
 	*dptr++ = 0;
 
-	skbn->free = 1;
 	skbn->sk   = NULL;
 
 	if (!nr_route_frame(skbn, NULL))
diff -u --recursive --new-file v2.1.14/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.14/linux/net/netsyms.c	Fri Nov 22 18:28:24 1996
+++ linux/net/netsyms.c	Thu Dec 12 16:54:26 1996
@@ -25,9 +25,12 @@
 #include <net/tcp.h>
 #include <net/icmp.h>
 #include <net/route.h>
+#include <net/scm.h>
 #include <net/inet_common.h>
 #include <linux/net_alias.h>
 
+extern struct net_proto_family inet_family_ops;
+
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
 #include <linux/in6.h>
 #include <net/ndisc.h>
@@ -44,10 +47,13 @@
 #include <linux/net_alias.h>
 #endif
 
-#if     defined(CONFIG_ULTRA)   ||      defined(CONFIG_WD80x3)          || \
-        defined(CONFIG_EL2)     ||      defined(CONFIG_NE2000)          || \
-        defined(CONFIG_E2100)   ||      defined(CONFIG_HPLAN_PLUS)      || \
-        defined(CONFIG_HPLAN)   ||      defined(CONFIG_AC3200)
+#include <net/scm.h>
+
+#if	defined(CONFIG_ULTRA)	||	defined(CONFIG_WD80x3)		|| \
+	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
+	defined(CONFIG_E2100)	||	defined(CONFIG_HPLAN_PLUS)	|| \
+	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		|| \
+	defined(CONFIG_ES3210)
 #include "../drivers/net/8390.h"
 #endif
 
@@ -88,6 +94,10 @@
 	X(skb_copy_datagram_iovec),
 	X(datagram_select),
 
+	/* ?? needed by smbfs.o */
+	X(__scm_destroy),
+	X(__scm_send),
+
 #ifdef CONFIG_IPX_MODULE
 	X(make_8023_client),
 	X(destroy_8023_client),
@@ -102,23 +112,30 @@
 	X(inet_del_protocol),
 	X(rarp_ioctl_hook),
 	X(init_etherdev),
-	X(ip_rt_route),
+	X(ip_route_output),
 	X(icmp_send),
 	X(ip_options_compile),
 	X(ip_rt_put),
 	X(arp_send),
 	X(ip_id_count),
 	X(ip_send_check),
+	X(inet_family_ops),
+
+	X(__scm_send),
+	X(__scm_destroy),
+
 #ifdef CONFIG_IP_FORWARD
 	X(ip_forward),
 #endif
 
 #ifdef CONFIG_IPV6_MODULE
 	/* inet functions common to v4 and v6 */
-	X(inet_proto_ops),
+	X(inet_stream_ops),
+	X(inet_dgram_ops),
 	X(inet_remove_sock),
 	X(inet_release),
-	X(inet_connect),
+	X(inet_stream_connect),
+	X(inet_dgram_connect),
 	X(inet_accept),
 	X(inet_select),
 	X(inet_listen),
@@ -133,7 +150,6 @@
 	X(destroy_sock),
 	X(ip_queue_xmit),
 	X(csum_partial),
-	X(ip_my_addr),
 	X(skb_copy),
 	X(dev_lockct),
 	X(ndisc_eth_hook),
@@ -159,7 +175,6 @@
 	X(tcp_getsockopt),
 	X(tcp_recvmsg),
 	X(tcp_send_synack),
-	X(sock_wfree),
 	X(sock_wmalloc),
 	X(tcp_reset_xmit_timer),
 	X(tcp_parse_options),
@@ -178,7 +193,7 @@
 	X(tcp_v4_syn_recv_sock),
 	X(tcp_v4_backlog_rcv),
 	X(tcp_v4_connect),
-	X(ip_chk_addr),
+	X(__ip_chk_addr),
 	X(net_reset_timer),
 	X(net_delete_timer),
 	X(udp_prot),
@@ -189,7 +204,8 @@
 #if	defined(CONFIG_ULTRA)	||	defined(CONFIG_WD80x3)		|| \
 	defined(CONFIG_EL2)	||	defined(CONFIG_NE2000)		|| \
 	defined(CONFIG_E2100)	||	defined(CONFIG_HPLAN_PLUS)	|| \
-	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)
+	defined(CONFIG_HPLAN)	||	defined(CONFIG_AC3200)		|| \
+	defined(CONFIG_ES3210)
 	/* If 8390 NIC support is built in, we will need these. */
 	X(ei_open),
 	X(ei_close),
@@ -228,12 +244,10 @@
 	X(eth_copy_and_sum),
 	X(arp_query),
 	X(alloc_skb),
-	X(kfree_skb),
+	X(__kfree_skb),
 	X(skb_clone),
+	X(skb_copy),
 	X(dev_alloc_skb),
-	X(dev_kfree_skb),
-	X(skb_device_unlock),
-	X(skb_device_locked),
 	X(netif_rx),
 	X(dev_tint),
 	X(irq2dev_map),
diff -u --recursive --new-file v2.1.14/linux/net/protocols.c linux/net/protocols.c
--- v2.1.14/linux/net/protocols.c	Fri Nov 22 18:28:24 1996
+++ linux/net/protocols.c	Thu Dec 12 16:54:26 1996
@@ -13,6 +13,7 @@
 #define CONFIG_UNIX		/* always present...	*/
 
 #ifdef	CONFIG_UNIX
+#include <linux/un.h>
 #include <net/af_unix.h>
 #endif
 
@@ -24,13 +25,14 @@
 #endif	/* INET */
 
 #if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
+#define NEED_802
 #include <net/ipxcall.h>
-#include <net/p8022call.h>
-#include <net/p8022trcall.h>
 #endif
+
 #ifdef CONFIG_X25
 #include <net/x25call.h>
 #endif
+
 #ifdef CONFIG_AX25
 #include <net/ax25call.h>
 #ifdef CONFIG_NETROM
@@ -40,19 +42,35 @@
 #include <net/rosecall.h>
 #endif
 #endif
+
 #if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
-#if ! ( defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE) )
-#include <net/p8022call.h>
-#include <net/p8022trcall.h>
-#endif
+#define NEED_802
 #include <net/atalkcall.h>
 #endif
+
+#if defined(CONFIG_NETBEUI)
+#define NEED_LLC
+#include <net/netbeuicall.h>
+#endif
+
 #include <net/psnapcall.h>
+
 #ifdef CONFIG_TR
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
 extern void rif_init(struct net_proto *);
 #endif
+
+#ifdef NEED_LLC
+#define NEED_802
+#include <net/llccall.h>
+#endif
+
+#ifdef NEED_802
+#include <net/p8022call.h>
+#include <net/p8022trcall.h>
+#endif
+
 /*
  *	Protocol Table
  */
@@ -61,15 +79,21 @@
 #ifdef	CONFIG_UNIX
   { "UNIX",	unix_proto_init	},			/* Unix domain socket family 	*/
 #endif
-#if defined(CONFIG_IPX)   || defined(CONFIG_IPX_MODULE) || \
-    defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
+
+#ifdef NEED_802
   { "802.2",	p8022_proto_init },			/* 802.2 demultiplexor		*/
   { "802.2TR",	p8022tr_proto_init },			/* 802.2 demultiplexor		*/
   { "SNAP",	snap_proto_init },			/* SNAP demultiplexor		*/
 #endif
+
 #ifdef CONFIG_TR
   { "RIF",	rif_init },				/* RIF for Token ring		*/
 #endif  
+
+#ifdef NEED_LLC
+  { "802.2LLC", llc_init },				/* 802.2 LLC */
+#endif  
+
 #ifdef CONFIG_AX25  
   { "AX.25",	ax25_proto_init },			/* Amateur Radio AX.25 */
 #ifdef CONFIG_NETROM
@@ -79,20 +103,25 @@
   { "Rose",	rose_proto_init },			/* Amateur Radio X.25 PLP */
 #endif
 #endif  
+
 #ifdef	CONFIG_INET
   { "INET",	inet_proto_init	},			/* TCP/IP			*/
 #ifdef	CONFIG_IPV6
   { "INET6",	inet6_proto_init},			/* IPv6	*/
 #endif
 #endif
+
 #ifdef  CONFIG_IPX
   { "IPX",	ipx_proto_init },			/* IPX				*/
 #endif
+
 #ifdef CONFIG_ATALK
   { "DDP",	atalk_proto_init },			/* Netatalk Appletalk driver	*/
 #endif
+
 #ifdef CONFIG_X25
   { "X.25",	x25_proto_init },			/* CCITT X.25 Packet Layer */
 #endif
+
   { NULL,	NULL		}			/* End marker			*/
 };
diff -u --recursive --new-file v2.1.14/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.14/linux/net/rose/af_rose.c	Thu Dec 12 17:02:48 1996
+++ linux/net/rose/af_rose.c	Thu Dec 12 16:54:26 1996
@@ -14,6 +14,7 @@
  *
  *	History
  *	Rose 001	Jonathan(G4KLX)	Cloned from af_netrom.c.
+ *			Alan(GW4PTS)	Hacked up for newer API stuff
  */
   
 #include <linux/config.h>
@@ -58,6 +59,8 @@
 
 static unsigned int lci = 1;
 
+struct proto_ops rose_proto_ops;
+
 static struct sock *volatile rose_list = NULL;
 
 /*
@@ -407,11 +410,8 @@
 	struct sock *sk;
 	int err, opt;
 
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 	
-	if (level == SOL_SOCKET)
-		return sock_setsockopt(sk, level, optname, optval, optlen);
-
 	if (level != SOL_ROSE)
 		return -EOPNOTSUPP;
 
@@ -471,10 +471,7 @@
 	int val = 0;
 	int err; 
 
-	sk = (struct sock *)sock->data;
-	
-	if (level == SOL_SOCKET)
-		return sock_getsockopt(sk, level, optname, optval, optlen);
+	sk = (struct sock *)sock->sk;
 	
 	if (level != SOL_ROSE)
 		return -EOPNOTSUPP;
@@ -526,7 +523,7 @@
 
 static int rose_listen(struct socket *sock, int backlog)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 
 	if (sk->state != TCP_LISTEN) {
 		sk->protinfo.rose->dest_ndigis = 0;
@@ -575,6 +572,7 @@
 
 	init_timer(&sk->timer);
 
+	sock->ops	  = &rose_proto_ops;
 	sk->socket        = sock;
 	sk->type          = sock->type;
 	sk->protocol      = protocol;
@@ -593,8 +591,8 @@
 	sk->error_report = def_callback1;
 
 	if (sock != NULL) {
-		sock->data = (void *)sk;
-		sk->sleep  = sock->wait;
+		sock->sk = sk;
+		sk->sleep = &sock->wait;
 	}
 
 	skb_queue_head_init(&rose->ack_queue);
@@ -703,14 +701,14 @@
 
 static int rose_dup(struct socket *newsock, struct socket *oldsock)
 {
-	struct sock *sk = (struct sock *)oldsock->data;
+	struct sock *sk = (struct sock *)oldsock->sk;
 
 	return rose_create(newsock, sk->protocol);
 }
 
 static int rose_release(struct socket *sock, struct socket *peer)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 
 	if (sk == NULL) return 0;
 
@@ -759,7 +757,7 @@
 			break;
 	}
 
-	sock->data = NULL;	
+	sock->sk = NULL;	
 	sk->socket = NULL;	/* Not used, but we should do this. **/
 
 	return 0;
@@ -772,7 +770,7 @@
 	struct device *dev;
 	ax25_address *user, *source;
 	
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 
 	if (sk->zapped == 0)
 		return -EINVAL;
@@ -815,7 +813,7 @@
 
 static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
 	ax25_address *user;
 	struct device *dev;
@@ -917,12 +915,12 @@
 	struct sock *newsk;
 	struct sk_buff *skb;
 
-	if (newsock->data)
-		sk_free(newsock->data);
+	if (newsock->sk)
+		sk_free(newsock->sk);
 
-	newsock->data = NULL;
+	newsock->sk = NULL;
 	
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 
 	if (sk->type != SOCK_SEQPACKET)
 		return -EOPNOTSUPP;
@@ -957,7 +955,7 @@
 	skb->sk = NULL;
 	kfree_skb(skb, FREE_READ);
 	sk->ack_backlog--;
-	newsock->data = newsk;
+	newsock->sk = newsk;
 
 	return 0;
 }
@@ -968,7 +966,7 @@
 	struct sockaddr_rose *srose = (struct sockaddr_rose *)uaddr;
 	struct sock *sk;
 	
-	sk = (struct sock *)sock->data;
+	sk = (struct sock *)sock->sk;
 	
 	if (peer != 0) {
 		if (sk->state != TCP_ESTABLISHED)
@@ -1064,9 +1062,10 @@
 	return 1;
 }
 
-static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, int noblock, int flags)
+static int rose_sendmsg(struct socket *sock, struct msghdr *msg, int len, 
+				struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	struct sockaddr_rose *usrose = (struct sockaddr_rose *)msg->msg_name;
 	int err;
 	struct sockaddr_rose srose;
@@ -1074,10 +1073,7 @@
 	unsigned char *asmptr;
 	int size;
 	
-	if (sk->err)
-		return sock_error(sk);
-
-	if (flags)
+	if (msg->msg_flags&~MSG_DONTWAIT)
 		return -EINVAL;
 
 	if (sk->zapped)
@@ -1128,11 +1124,11 @@
 
 	size = len + AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN;
 
-	if ((skb = sock_alloc_send_skb(sk, size, 0, 0, &err)) == NULL)
+	if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags&MSG_DONTWAIT,
+			&err)) == NULL)
 		return err;
 
 	skb->sk   = sk;
-	skb->free = 1;
 	skb->arp  = 1;
 
 	skb_reserve(skb, size - len);
@@ -1183,18 +1179,15 @@
 }
 
 
-static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock,
-		   int flags, int *addr_len)
+static int rose_recvmsg(struct socket *sock, struct msghdr *msg, int size, 
+		   int flags, struct scm_cookie *scm)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	struct sockaddr_rose *srose = (struct sockaddr_rose *)msg->msg_name;
 	int copied;
 	struct sk_buff *skb;
 	int er;
 
-	if (addr_len != NULL)
-		*addr_len = sizeof(*srose);
-
 	/*
 	 * This works for seqpacket too. The receiver has ordered the queue for
 	 * us! We do one quick check first though
@@ -1203,7 +1196,7 @@
 		return -ENOTCONN;
 
 	/* Now we can treat all alike */
-	if ((skb = skb_recv_datagram(sk, flags, noblock, &er)) == NULL)
+	if ((skb = skb_recv_datagram(sk, flags, flags&MSG_DONTWAIT, &er)) == NULL)
 		return er;
 
 	if (!sk->protinfo.rose->hdrincl) {
@@ -1235,8 +1228,8 @@
 
 		*srose = addr;
 
-		*addr_len = sizeof(*srose);
 	}
+	msg->msg_namelen=sizeof(*srose);
 
 	skb_free_datagram(sk, skb);
 
@@ -1248,16 +1241,9 @@
 	return -EOPNOTSUPP;
 }
 
-static int rose_select(struct socket *sock , int sel_type, select_table *wait)
-{
-	struct sock *sk = (struct sock *)sock->data;
-
-	return datagram_select(sk, sel_type, wait);
-}
-
 static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk = (struct sock *)sock->data;
+	struct sock *sk = (struct sock *)sock->sk;
 	int err;
 	long amount = 0;
 
@@ -1387,10 +1373,14 @@
 	return(len);
 } 
 
+struct net_proto_family rose_family_ops = {
+	AF_ROSE,
+	rose_create
+};
+
 struct proto_ops rose_proto_ops = {
 	AF_ROSE,
 	
-	rose_create,
 	rose_dup,
 	rose_release,
 	rose_bind,
@@ -1398,7 +1388,7 @@
 	rose_socketpair,
 	rose_accept,
 	rose_getname,
-	rose_select,
+	datagram_select,
 	rose_ioctl,
 	rose_listen,
 	rose_shutdown,
@@ -1416,7 +1406,7 @@
 
 void rose_proto_init(struct net_proto *pro)
 {
-	sock_register(rose_proto_ops.family, &rose_proto_ops);
+	sock_register(&rose_family_ops);
 	register_netdevice_notifier(&rose_dev_notifier);
 	printk(KERN_INFO "G4KLX Rose for Linux. Version 0.1 for AX25.034 Linux 2.1\n");
 
diff -u --recursive --new-file v2.1.14/linux/net/rose/rose_dev.c linux/net/rose/rose_dev.c
--- v2.1.14/linux/net/rose/rose_dev.c	Tue Nov 12 15:56:17 1996
+++ linux/net/rose/rose_dev.c	Thu Dec 12 16:54:26 1996
@@ -91,32 +91,30 @@
 	return -37;	
 }
 
-static int rose_rebuild_header(void *buff, struct device *dev,
-	unsigned long raddr, struct sk_buff *skb)
+static int rose_rebuild_header(struct sk_buff *skb)
 {
+	struct device *dev=skb->dev;
 	struct enet_statistics *stats = (struct enet_statistics *)dev->priv;
-	unsigned char *bp = (unsigned char *)buff;
+	unsigned char *bp = (unsigned char *)skb->data;
 	struct sk_buff *skbn;
 
-	if (!arp_query(bp + 7, raddr, dev)) {
-		dev_kfree_skb(skb, FREE_WRITE);
+	if (!arp_find(bp + 7, skb)) {
+		kfree_skb(skb, FREE_WRITE);
 		return 1;
 	}
 
 	if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
-		dev_kfree_skb(skb, FREE_WRITE);
+		kfree_skb(skb, FREE_WRITE);
 		return 1;
 	}
 
-	skbn->sk = skb->sk;
-	
 	if (skbn->sk != NULL)
-		atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+		skb_set_owner_w(skbn, skb->sk);
 
-	dev_kfree_skb(skb, FREE_WRITE);
+	kfree_skb(skb, FREE_WRITE);
 
 	if (!rose_route_frame(skbn, NULL)) {
-		dev_kfree_skb(skbn, FREE_WRITE);
+		kfree_skb(skbn, FREE_WRITE);
 		stats->tx_errors++;
 	}
 
@@ -186,7 +184,7 @@
 
 	sti();
 
-	dev_kfree_skb(skb, FREE_WRITE);
+	kfree_skb(skb, FREE_WRITE);
 
 	stats->tx_errors++;
 
diff -u --recursive --new-file v2.1.14/linux/net/rose/rose_in.c linux/net/rose/rose_in.c
--- v2.1.14/linux/net/rose/rose_in.c	Thu Dec 12 17:02:48 1996
+++ linux/net/rose/rose_in.c	Thu Dec 12 16:54:26 1996
@@ -64,10 +64,8 @@
 		if ((skbn = alloc_skb(sk->protinfo.rose->fraglen, GFP_ATOMIC)) == NULL)
 			return 1;
 
-		skbn->free = 1;
 		skbn->arp  = 1;
-		skbn->sk   = sk;
-		sk->rmem_alloc += skbn->truesize;
+		skb_set_owner_r(skbn, sk);
 		skbn->h.raw = skbn->data;
 
 		skbo = skb_dequeue(&sk->protinfo.rose->frag_queue);
diff -u --recursive --new-file v2.1.14/linux/net/rose/rose_link.c linux/net/rose/rose_link.c
--- v2.1.14/linux/net/rose/rose_link.c	Thu Dec 12 17:02:48 1996
+++ linux/net/rose/rose_link.c	Thu Dec 12 16:54:26 1996
@@ -165,7 +165,6 @@
 	*dptr++ = 0x00;
 	*dptr++ = 0;
 
-	skb->free = 1;
 	skb->sk   = NULL;
 
 	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
@@ -195,7 +194,6 @@
 	*dptr++ = 0x00;
 	*dptr++ = ROSE_RESTART_CONFIRMATION;
 
-	skb->free = 1;
 	skb->sk   = NULL;
 
 	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
@@ -226,7 +224,6 @@
 	*dptr++ = ROSE_DIAGNOSTIC;
 	*dptr++ = diag;
 
-	skb->free = 1;
 	skb->sk   = NULL;
 
 	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
@@ -259,7 +256,6 @@
 	*dptr++ = cause;
 	*dptr++ = 0x00;
 
-	skb->free = 1;
 	skb->sk   = NULL;
 
 	if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
@@ -282,7 +278,6 @@
 	*dptr++ = AX25_P_ROSE;
 
 	skb->arp  = 1;
-	skb->free = 1;
 
 	if (neigh->restarted) {
 		if (!ax25_send_frame(skb, (ax25_address *)neigh->dev->dev_addr, &neigh->callsign, neigh->digipeat, neigh->dev))
diff -u --recursive --new-file v2.1.14/linux/net/rose/rose_out.c linux/net/rose/rose_out.c
--- v2.1.14/linux/net/rose/rose_out.c	Tue Nov 12 15:56:17 1996
+++ linux/net/rose/rose_out.c	Thu Dec 12 16:54:26 1996
@@ -61,7 +61,6 @@
 				return;
 
 			skbn->sk   = sk;
-			skbn->free = 1;
 			skbn->arp  = 1;
 
 			skb_reserve(skbn, frontlen);
@@ -82,7 +81,6 @@
 			skb_queue_tail(&sk->write_queue, skbn); /* Throw it on the queue */
 		}
 		
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	} else {
 		skb_queue_tail(&sk->write_queue, skb);		/* Throw it on the queue */
diff -u --recursive --new-file v2.1.14/linux/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- v2.1.14/linux/net/rose/rose_subr.c	Tue Nov 12 15:56:17 1996
+++ linux/net/rose/rose_subr.c	Thu Dec 12 16:54:26 1996
@@ -49,13 +49,11 @@
 
 	while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
 		skb->sk   = sk;
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	}
 
 	while ((skb = skb_dequeue(&sk->protinfo.rose->ack_queue)) != NULL) {
 		skb->sk   = sk;
-		skb->free = 1;
 		kfree_skb(skb, FREE_WRITE);
 	}
 
@@ -80,7 +78,6 @@
 		while (skb_peek(&sk->protinfo.rose->ack_queue) != NULL && sk->protinfo.rose->va != nr) {
 		        skb = skb_dequeue(&sk->protinfo.rose->ack_queue);
 		        skb->sk   = sk;
-			skb->free = 1;
 			kfree_skb(skb, FREE_WRITE);
 			sk->protinfo.rose->va = (sk->protinfo.rose->va + 1) % ROSE_MODULUS;
 		}
diff -u --recursive --new-file v2.1.14/linux/net/socket.c linux/net/socket.c
--- v2.1.14/linux/net/socket.c	Sat Nov 30 12:03:14 1996
+++ linux/net/socket.c	Thu Dec 12 16:54:26 1996
@@ -49,6 +49,12 @@
  *
  *	In addition it lacks an effective kernel -> kernel interface to go with
  *	the user one.
+ *
+ * PROBLEMS:
+ *	- CLONE_FILES. Big problem, cloned thread can close file,
+ *	  while other thread sleeps in kernel. It can be solved
+ *	  by increasing f_count and releasing it on exit from syscall.
+ *
  */
 
 #include <linux/config.h>
@@ -76,6 +82,18 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <net/ip.h>
+#include <net/protocol.h>
+#include <net/rarp.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/scm.h>
+
+
 #if defined(CONFIG_MODULES) && defined(CONFIG_NET)
 extern void export_net_symbols(void);
 #endif
@@ -116,7 +134,8 @@
 /*
  *	The protocol list. Each protocol is registered in here.
  */
-static struct proto_ops *pops[NPROTO];
+static struct net_proto_family *net_families[NPROTO];
+
 /*
  *	Statistics counters of the socket lists
  */
@@ -147,7 +166,7 @@
 {
 	int err;
 	int len;
-		
+
 	if((err=get_user(len, ulen)))
 		return err;
 	if(len>klen)
@@ -200,15 +219,7 @@
 	return fd;
 }
 
-
-/*
- *	Go from an inode to its socket slot.
- *
- * The original socket implementation wasn't very clever, which is
- * why this exists at all..
- */
-
-__inline struct socket *socki_lookup(struct inode *inode)
+static __inline__ struct socket *socki_lookup(struct inode *inode)
 {
 	return &inode->u.socket_i;
 }
@@ -217,20 +228,23 @@
  *	Go from a file number to its socket slot.
  */
 
-extern __inline struct socket *sockfd_lookup(int fd, struct file **pfile)
+extern __inline__ struct socket *sockfd_lookup(int fd, int *err)
 {
 	struct file *file;
 	struct inode *inode;
 
-	if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd])) 
+	if (fd < 0 || fd >= NR_OPEN || !(file = current->files->fd[fd]))
+	{
+		*err = -EBADF;
 		return NULL;
+	}
 
 	inode = file->f_inode;
-	if (!inode || !inode->i_sock)
+	if (!inode || !inode->i_sock || !socki_lookup(inode))
+	{
+		*err = -ENOTSOCK;
 		return NULL;
-
-	if (pfile) 
-		*pfile = file;
+	}
 
 	return socki_lookup(inode);
 }
@@ -247,78 +261,87 @@
 	inode = get_empty_inode();
 	if (!inode)
 		return NULL;
+	sock = socki_lookup(inode);
 
 	inode->i_mode = S_IFSOCK;
 	inode->i_sock = 1;
 	inode->i_uid = current->uid;
 	inode->i_gid = current->gid;
 
-	sock = &inode->u.socket_i;
+	sock->inode = inode;
+	sock->fasync_list = NULL;
 	sock->state = SS_UNCONNECTED;
 	sock->flags = 0;
 	sock->ops = NULL;
-	sock->data = NULL;
-	sock->conn = NULL;
-	sock->iconn = NULL;
-	sock->next = NULL;
+	sock->sk = NULL;
 	sock->file = NULL;
-	sock->wait = &inode->i_wait;
-	sock->inode = inode;		/* "backlink": we could use pointer arithmetic instead */
-	sock->fasync_list = NULL;
+
 	sockets_in_use++;
 	return sock;
 }
 
-/*
- *	Release a socket.
- */
-
-static inline void sock_release_peer(struct socket *peer)
-{
-	peer->state = SS_DISCONNECTING;
-	wake_up_interruptible(peer->wait);
-	sock_wake_async(peer, 1);
-}
-
 void sock_release(struct socket *sock)
 {
 	int oldstate;
-	struct socket *peersock, *nextsock;
 
 	if ((oldstate = sock->state) != SS_UNCONNECTED)
 		sock->state = SS_DISCONNECTING;
 
-	/*
-	 *	Wake up anyone waiting for connections. 
-	 */
-
-	for (peersock = sock->iconn; peersock; peersock = nextsock) 
-	{
-		nextsock = peersock->next;
-		sock_release_peer(peersock);
-	}
-
-	/*
-	 * Wake up anyone we're connected to. First, we release the
-	 * protocol, to give it a chance to flush data, etc.
-	 */
-
-	peersock = (oldstate == SS_CONNECTED) ? sock->conn : NULL;
 	if (sock->ops) 
-		sock->ops->release(sock, peersock);
-	if (peersock)
-		sock_release_peer(peersock);
+		sock->ops->release(sock, NULL);
+
 	--sockets_in_use;	/* Bookkeeping.. */
 	sock->file=NULL;
-	iput(SOCK_INODE(sock));
+	iput(sock->inode);
+}
+
+int
+sock_sendmsg(struct socket *sock, struct msghdr *msg, int size)
+{
+	int err;
+	struct scm_cookie scm;
+
+	if (!sock->ops->sendmsg)
+		return -EOPNOTSUPP;
+
+	err = scm_send(sock, msg, &scm);
+	if (err < 0)
+		return err;
+
+	err = sock->ops->sendmsg(sock, msg, size, &scm);
+
+	scm_destroy(&scm);
+
+	return err;
+}
+
+int
+sock_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags)
+{
+	struct scm_cookie scm;
+
+	if (!sock->ops->recvmsg)
+		return -EOPNOTSUPP;
+
+	memset(&scm, 0, sizeof(scm));
+
+	size = sock->ops->recvmsg(sock, msg, size, flags, &scm);
+
+	if (size < 0)
+		return size;
+
+	scm_recv(sock, msg, &scm, flags);
+
+	return size;
 }
 
+
 /*
  *	Sockets are not seekable.
  */
 
 static long long sock_lseek(struct inode *inode, struct file *file,
-	long long offset, int whence)
+			    long long offset, int whence)
 {
 	return -ESPIPE;
 }
@@ -329,40 +352,42 @@
  */
 
 static long sock_read(struct inode *inode, struct file *file,
-	char *ubuf, unsigned long size)
+		      char *ubuf, unsigned long size)
 {
 	struct socket *sock;
 	int err;
 	struct iovec iov;
 	struct msghdr msg;
-  
-	sock = socki_lookup(inode); 
-	if (sock->flags & SO_ACCEPTCON) 
-		return(-EINVAL);
 
-	if(size<0)
+	sock = socki_lookup(inode); 
+  
+	if (size<0)
 		return -EINVAL;
-	if(size==0)		/* Match SYS5 behaviour */
+	if (size==0)		/* Match SYS5 behaviour */
 		return 0;
 	if ((err=verify_area(VERIFY_WRITE,ubuf,size))<0)
 	  	return err;
 	msg.msg_name=NULL;
+	msg.msg_namelen=0;
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_control=NULL;
+	msg.msg_controllen=0;
 	iov.iov_base=ubuf;
 	iov.iov_len=size;
 
-	return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK), 0,&msg.msg_namelen));
+	return sock_recvmsg(sock, &msg, size,
+			    !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT);
 }
 
+
 /*
  *	Write data to a socket. We verify that the user area ubuf..ubuf+size-1 is
  *	readable by the user process.
  */
 
 static long sock_write(struct inode *inode, struct file *file,
-	const char *ubuf, unsigned long size)
+		       const char *ubuf, unsigned long size)
 {
 	struct socket *sock;
 	int err;
@@ -371,9 +396,6 @@
 	
 	sock = socki_lookup(inode); 
 
-	if (sock->flags & SO_ACCEPTCON) 
-		return(-EINVAL);
-	
 	if(size<0)
 		return -EINVAL;
 	if(size==0)		/* Match SYS5 behaviour */
@@ -383,15 +405,41 @@
 	  	return err;
 	
 	msg.msg_name=NULL;
+	msg.msg_namelen=0;
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_control=NULL;
+	msg.msg_controllen=0;
+	msg.msg_flags=!(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
 	iov.iov_base=(void *)ubuf;
 	iov.iov_len=size;
 	
-	return(sock->ops->sendmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK),0));
+	return sock_sendmsg(sock, &msg, size);
+}
+
+int sock_readv_writev(int type, struct inode * inode, struct file * file,
+		      const struct iovec * iov, long count, long size)
+{
+	struct msghdr msg;
+	struct socket *sock;
+
+	sock = socki_lookup(inode);
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+	msg.msg_control = NULL;
+	msg.msg_controllen = 0;
+	msg.msg_iov = (struct iovec *) iov;
+	msg.msg_iovlen = count;
+	msg.msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
+
+	/* read() does a VERIFY_WRITE */
+	if (type == VERIFY_WRITE)
+		return sock_recvmsg(sock, &msg, size, msg.msg_flags);
+	return sock_sendmsg(sock, &msg, size);
 }
 
+
 /*
  *	With an ioctl arg may well be a user mode pointer, but we don't know what to do
  *	with it - that's up to the protocol still.
@@ -400,9 +448,8 @@
 int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	   unsigned long arg)
 {
-	struct socket *sock;
-	sock = socki_lookup(inode); 
-  	return(sock->ops->ioctl(sock, cmd, arg));
+	struct socket *sock = socki_lookup(inode);
+  	return sock->ops->ioctl(sock, cmd, arg);
 }
 
 
@@ -417,7 +464,7 @@
 	 */
 
 	if (sock->ops->select)
-		return(sock->ops->select(sock, sel_type, wait));
+		return sock->ops->select(sock, sel_type, wait);
 	return(0);
 }
 
@@ -425,11 +472,14 @@
 void sock_close(struct inode *inode, struct file *filp)
 {
 	/*
-	 *	It's possible the inode is NULL if we're closing an unfinished socket. 
+	 * It's possible the inode is NULL if we're closing an unfinished socket. 
 	 */
 
-	if (!inode) 
+	if (!inode)
+	{
+		printk(KERN_DEBUG "Nope. It is impossible!\n");
 		return;
+	}
 	sock_fasync(inode, filp, 0);
 	sock_release(socki_lookup(inode));
 }
@@ -458,8 +508,8 @@
 	save_flags(flags);
 	cli();
 	
-	for(fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev)
-		if(fa->fa_file==filp)
+	for (fa=*prev; fa!=NULL; prev=&fa->fa_next,fa=*prev)
+		if (fa->fa_file==filp)
 			break;
 	
 	if(on)
@@ -477,7 +527,7 @@
 	}
 	else
 	{
-		if(fa!=NULL)
+		if (fa!=NULL)
 		{
 			*prev=fa->fa_next;
 			kfree_s(fa,sizeof(struct fasync_struct));
@@ -522,9 +572,9 @@
 	register int i;
 	for (i = 0; i < NPROTO; i++)
 	{
-		if (pops[i] == NULL)
+		if (net_families[i] == NULL)
 			continue;
-		if (pops[i]->family == family)
+		if (net_families[i]->family == family)
 			return i;
 	}
 	return -1;
@@ -534,7 +584,6 @@
 {
 	int i, fd;
 	struct socket *sock;
-	struct proto_ops *ops;
 
 	/* Locate the correct protocol family. */
 	i = find_protocol_family(family);
@@ -556,11 +605,7 @@
 #endif
 
 	if (i < 0)
-	{
   		return -EINVAL;
-	}
-
-	ops = pops[i];
 
 /*
  *	Check that this is a type that we know how to manipulate and
@@ -569,8 +614,8 @@
  */
   
 	if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
-		type != SOCK_SEQPACKET && type != SOCK_RAW &&
-		type != SOCK_PACKET) || protocol < 0)
+	     type != SOCK_SEQPACKET && type != SOCK_RAW &&
+	     type != SOCK_PACKET) || protocol < 0)
 			return(-EINVAL);
 
 /*
@@ -587,20 +632,20 @@
 	}
 
 	sock->type = type;
-	sock->ops = ops;
-	if ((i = sock->ops->create(sock, protocol)) < 0) 
+
+	if ((i = net_families[i]->create(sock, protocol)) < 0) 
 	{
 		sock_release(sock);
 		return(i);
 	}
 
-	if ((fd = get_fd(SOCK_INODE(sock))) < 0) 
+	if ((fd = get_fd(sock->inode)) < 0) 
 	{
 		sock_release(sock);
 		return(-EINVAL);
 	}
 
-	sock->file=current->files->fd[fd];
+	sock->file = current->files->fd[fd];
 
 	return(fd);
 }
@@ -613,7 +658,7 @@
 {
 	int fd1, fd2, i;
 	struct socket *sock1, *sock2;
-	int er;
+	int err;
 
 	/*
 	 * Obtain the first socket and check if the underlying protocol
@@ -622,11 +667,14 @@
 
 	if ((fd1 = sys_socket(family, type, protocol)) < 0) 
 		return(fd1);
-	sock1 = sockfd_lookup(fd1, NULL);
+
+	sock1 = sockfd_lookup(fd1, &err);
+	if (!sock1)
+		return err;
 	if (!sock1->ops->socketpair) 
 	{
 		sys_close(fd1);
-		return(-EINVAL);
+		return -EOPNOTSUPP;
 	}
 
 	/*
@@ -639,7 +687,9 @@
 		return(-EINVAL);
 	}
 
-	sock2 = sockfd_lookup(fd2, NULL);
+	sock2 = sockfd_lookup(fd2,&err);
+	if (!sock2)
+		return err;
 	if ((i = sock1->ops->socketpair(sock1, sock2)) < 0) 
 	{
 		sys_close(fd1);
@@ -647,19 +697,14 @@
 		return(i);
 	}
 
-	sock1->conn = sock2;
-	sock2->conn = sock1;
-	sock1->state = SS_CONNECTED;
-	sock2->state = SS_CONNECTED;
-
-	er = put_user(fd1, &usockvec[0]); 
-	if (!er) 
-		er = put_user(fd2, &usockvec[1]);
-	if (er) {
+	err = put_user(fd1, &usockvec[0]); 
+	if (!err) 
+		err = put_user(fd2, &usockvec[1]);
+	if (err) {
 		sys_close(fd1);
 		sys_close(fd2);
 	}
-	return er;
+	return err;
 }
 
 
@@ -678,19 +723,14 @@
 	char address[MAX_SOCK_ADDR];
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd,&err))) 
+		return err;
   
 	if((err=move_addr_to_kernel(umyaddr,addrlen,address))<0)
 	  	return err;
   
 	if ((i = sock->ops->bind(sock, (struct sockaddr *)address, addrlen)) < 0) 
-	{
 		return(i);
-	}
 	return(0);
 }
 
@@ -706,21 +746,12 @@
 	struct socket *sock;
 	int err=-EOPNOTSUPP;
 	
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
-
-	if (sock->state != SS_UNCONNECTED) 
-		return(-EINVAL);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 
 	if (sock->ops && sock->ops->listen)
-	{
 		err=sock->ops->listen(sock, backlog);
-		if(!err)
-			sock->flags |= SO_ACCEPTCON;
-	}
-	return(err);
+	return err;
 }
 
 
@@ -738,24 +769,17 @@
 
 asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen)
 {
-	struct file *file;
+	struct inode *inode;
 	struct socket *sock, *newsock;
 	int i;
+	int err;
 	char address[MAX_SOCK_ADDR];
 	int len;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-  	if (!(sock = sockfd_lookup(fd, &file))) 
-		return(-ENOTSOCK);
-	if (sock->state != SS_UNCONNECTED) 
-	{
-		return(-EINVAL);
-	}
-	if (!(sock->flags & SO_ACCEPTCON)) 
-	{
-		return(-EINVAL);
-	}
+  	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+	if (!sock->ops->accept)
+		return -EOPNOTSUPP;
 
 	if (!(newsock = sock_alloc())) 
 	{
@@ -763,34 +787,39 @@
 		return(-ENOSR);	/* Was: EAGAIN, but we are out of system
 				   resources! */
 	}
+
+	inode = newsock->inode;
 	newsock->type = sock->type;
-	newsock->ops = sock->ops;
+
 	if ((i = sock->ops->dup(newsock, sock)) < 0) 
 	{
 		sock_release(newsock);
 		return(i);
 	}
 
-	i = newsock->ops->accept(sock, newsock, file->f_flags);
-	if ( i < 0) 
+	i = newsock->ops->accept(sock, newsock, current->files->fd[fd]->f_flags);
+
+	if (i < 0)
 	{
 		sock_release(newsock);
 		return(i);
 	}
+	newsock = socki_lookup(inode);
 
-	if ((fd = get_fd(SOCK_INODE(newsock))) < 0) 
+	if ((fd = get_fd(inode)) < 0) 
 	{
 		sock_release(newsock);
 		return(-EINVAL);
 	}
-	newsock->file=current->files->fd[fd];
+
+	newsock->file = current->files->fd[fd];
 	
 	if (upeer_sockaddr)
 	{
 		newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 1);
 		move_addr_to_user(address,len, upeer_sockaddr, upeer_addrlen);
 	}
-	return(fd);
+	return fd;
 }
 
 
@@ -809,46 +838,20 @@
 asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen)
 {
 	struct socket *sock;
-	struct file *file;
 	int i;
 	char address[MAX_SOCK_ADDR];
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || (file=current->files->fd[fd]) == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, &file)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd,&err)))
+		return err;
 
 	if((err=move_addr_to_kernel(uservaddr,addrlen,address))<0)
 	  	return err;
   
-	switch(sock->state) 
-	{
-		case SS_UNCONNECTED:
-			/* This is ok... continue with connect */
-			break;
-		case SS_CONNECTED:
-			/* Socket is already connected */
-			if(sock->type == SOCK_DGRAM) /* Hack for now - move this all into the protocol */
-				break;
-			return -EISCONN;
-		case SS_CONNECTING:
-			/* Not yet connected... we will check this. */
-		
-			/*
-			 *	FIXME:  for all protocols what happens if you start
-			 *	an async connect fork and both children connect. Clean
-			 *	this up in the protocols!
-			 */
-			break;
-		default:
-			return(-EINVAL);
-	}
-	i = sock->ops->connect(sock, (struct sockaddr *)address, addrlen, file->f_flags);
-	if (i < 0) 
-	{
+	i = sock->ops->connect(sock, (struct sockaddr *)address, addrlen,
+			     current->files->fd[fd]->f_flags);
+	if (i < 0)
 		return(i);
-	}
 	return(0);
 }
 
@@ -864,10 +867,8 @@
 	int len;
 	int err;
 	
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 0);
 	if(err)
@@ -889,10 +890,8 @@
 	int len;
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || current->files->fd[fd] == NULL)
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 
 	err=sock->ops->getname(sock, (struct sockaddr *)address, &len, 1);
 	if(err)
@@ -910,15 +909,12 @@
 asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags)
 {
 	struct socket *sock;
-	struct file *file;
 	int err;
 	struct msghdr msg;
 	struct iovec iov;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 
 	if(len<0)
 		return -EINVAL;
@@ -929,10 +925,16 @@
 	iov.iov_base=buff;
 	iov.iov_len=len;
 	msg.msg_name=NULL;
+	msg.msg_namelen=0;
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_control=NULL;
-	return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK), flags));
+	msg.msg_controllen=0;
+	if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	msg.msg_flags=flags;
+
+	return sock_sendmsg(sock, &msg, len);
 }
 
 /*
@@ -945,31 +947,29 @@
 	   struct sockaddr *addr, int addr_len)
 {
 	struct socket *sock;
-	struct file *file;
 	char address[MAX_SOCK_ADDR];
 	int err;
 	struct msghdr msg;
 	struct iovec iov;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd,&err)))
+		return err;
 
 	if(len<0)
 		return -EINVAL;
 	err=verify_area(VERIFY_READ,buff,len);
 	if(err)
 	  	return err;
-
+  	
 	iov.iov_base=buff;
 	iov.iov_len=len;
-	msg.msg_name = NULL;
-	msg.msg_namelen = 0;
+	msg.msg_name=NULL;
+	msg.msg_namelen=0;
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_control=NULL;
-	if (addr && addr_len) {
+	msg.msg_controllen=0;
+	if (addr_len) {
 		err=move_addr_to_kernel(addr,addr_len,address);
 		if (err < 0)
 			return err;
@@ -977,8 +977,11 @@
 		msg.msg_namelen=addr_len;
 	}
 	  	
-	return(sock->ops->sendmsg(sock, &msg, len, (file->f_flags & O_NONBLOCK),
-		flags));
+	if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+	msg.msg_flags=flags;
+
+	return sock_sendmsg(sock, &msg, len);
 }
 
 
@@ -991,14 +994,10 @@
 	struct iovec iov;
 	struct msghdr msg;
 	struct socket *sock;
-	struct file *file;
 	int err;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 		
 	if(size<0)
 		return -EINVAL;
@@ -1012,10 +1011,12 @@
 	msg.msg_iov=&iov;
 	msg.msg_iovlen=1;
 	msg.msg_control=NULL;
+	msg.msg_controllen=0;
 	iov.iov_base=ubuf;
 	iov.iov_len=size;
 
-	return(sock->ops->recvmsg(sock, &msg, size,(file->f_flags & O_NONBLOCK), flags,&msg.msg_namelen));
+	return sock_recvmsg(sock, &msg, size,
+			    (current->files->fd[fd]->f_flags & O_NONBLOCK) ? flags | MSG_DONTWAIT : flags);
 }
 
 /*
@@ -1028,19 +1029,16 @@
 	     struct sockaddr *addr, int *addr_len)
 {
 	struct socket *sock;
-	struct file *file;
 	struct iovec iov;
 	struct msghdr msg;
 	char address[MAX_SOCK_ADDR];
 	int err;
-	int alen;
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-	  	return(-ENOTSOCK);
-	if(size<0)
+
+	if (!(sock = sockfd_lookup(fd, &err)))
+	  	return err;
+	if (size<0)
 		return -EINVAL;
-	if(size==0)
+	if (size==0)
 		return 0;
 
 	err=verify_area(VERIFY_WRITE,ubuf,size);
@@ -1048,18 +1046,19 @@
 	  	return err;
   
   	msg.msg_control=NULL;
+  	msg.msg_controllen=0;
   	msg.msg_iovlen=1;
   	msg.msg_iov=&iov;
   	iov.iov_len=size;
   	iov.iov_base=ubuf;
   	msg.msg_name=address;
   	msg.msg_namelen=MAX_SOCK_ADDR;
-	size=sock->ops->recvmsg(sock, &msg, size, (file->f_flags & O_NONBLOCK),
-		     flags, &alen);
+	size=sock_recvmsg(sock, &msg, size,
+			  (current->files->fd[fd]->f_flags & O_NONBLOCK) ? (flags | MSG_DONTWAIT) : flags);
 
 	if(size<0)
 	 	return size;
-	if(addr!=NULL && (err=move_addr_to_user(address,alen, addr, addr_len))<0)
+	if(addr!=NULL && (err=move_addr_to_user(address, msg.msg_namelen, addr, addr_len))<0)
 	  	return err;
 
 	return size;
@@ -1072,15 +1071,19 @@
  
 asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen)
 {
+	int err;
 	struct socket *sock;
-	struct file *file;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
+
+	if (level == SOL_SOCKET)
+		return sock_setsockopt(sock,level,optname,optval,optlen);
+
+	if (sock->ops->setsockopt)
+		return sock->ops->setsockopt(sock, level, optname, optval, optlen);
 
-	return(sock->ops->setsockopt(sock, level, optname, optval, optlen));
+	return -EOPNOTSUPP;
 }
 
 /*
@@ -1090,17 +1093,19 @@
 
 asmlinkage int sys_getsockopt(int fd, int level, int optname, char *optval, int *optlen)
 {
+	int err;
 	struct socket *sock;
-	struct file *file;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
 	    
-	if (!sock->ops->getsockopt) 
-		return(0);
-	return(sock->ops->getsockopt(sock, level, optname, optval, optlen));
+	if (level == SOL_SOCKET)
+		return sock_getsockopt(sock,level,optname,optval,optlen);
+
+	if (sock->ops->getsockopt)
+		return sock->ops->getsockopt(sock, level, optname, optval, optlen);
+
+	return -EOPNOTSUPP;
 }
 
 
@@ -1110,45 +1115,40 @@
  
 asmlinkage int sys_shutdown(int fd, int how)
 {
+	int err;
 	struct socket *sock;
-	struct file *file;
 
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL))) 
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd, &err))) 
+		return err;
 
-	return(sock->ops->shutdown(sock, how));
+	return sock->ops->shutdown(sock, how);
 }
 
 /*
  *	BSD sendmsg interface
  */
  
-asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned int flags)
+asmlinkage int sys_sendmsg(int fd, struct msghdr *msg, unsigned flags)
 {
 	struct socket *sock;
-	struct file *file;
 	char address[MAX_SOCK_ADDR];
-	struct iovec iov[UIO_MAXIOV];
+	struct iovec iov[UIO_FASTIOV];
 	struct msghdr msg_sys;
 	void * krn_msg_ctl = NULL;
 	int err;
 	int total_len;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
+	if (!(sock = sockfd_lookup(fd,&err)))
+		return err;
 	
-	if(sock->ops->sendmsg==NULL)
+	if (sock->ops->sendmsg==NULL)
 		return -EOPNOTSUPP;
 
 	if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
-		return -EFAULT; 
+		return -EFAULT;
 
 	/* do not move before msg_sys is valid */
-	if(msg_sys.msg_iovlen>UIO_MAXIOV)
+	if (msg_sys.msg_iovlen>UIO_MAXIOV)
 		return -EINVAL;
 
 	/* This will also move the address data into kernel space */
@@ -1156,21 +1156,37 @@
 	if (err < 0)
 		return err;
 	total_len=err;
-		
-	if (msg_sys.msg_control)
+
+	if (msg_sys.msg_control==NULL)
+		msg_sys.msg_controllen = 0;
+
+	if (msg_sys.msg_controllen)
 	{
 		krn_msg_ctl = kmalloc(msg_sys.msg_controllen, GFP_KERNEL);
+		
+		if (!krn_msg_ctl)
+		{
+			err = -ENOBUFS;
+			goto flush_it;
+		}		
 		err = copy_from_user(krn_msg_ctl, msg_sys.msg_control,
 				     msg_sys.msg_controllen);
 		if (err)
-			return -EFAULT;
+			goto flush_it;
 		msg_sys.msg_control = krn_msg_ctl;
 	}
 
-	err = sock->ops->sendmsg(sock, &msg_sys, total_len,
-				 (file->f_flags&O_NONBLOCK), flags);
+	msg_sys.msg_flags = flags;
+	if (current->files->fd[fd]->f_flags & O_NONBLOCK)
+		msg_sys.msg_flags |= MSG_DONTWAIT;
 
-	if (msg_sys.msg_control)
+	err = sock_sendmsg(sock, &msg_sys, total_len);
+
+flush_it:
+	if (msg_sys.msg_iov != iov)
+		kfree(iov);
+
+	if (krn_msg_ctl)
 	{
 		kfree(krn_msg_ctl);
 	}
@@ -1185,32 +1201,28 @@
 asmlinkage int sys_recvmsg(int fd, struct msghdr *msg, unsigned int flags)
 {
 	struct socket *sock;
-	struct file *file;
-	struct iovec iov[UIO_MAXIOV];
+	struct iovec iovstack[UIO_FASTIOV];
+	struct iovec *iov=iovstack;
 	struct msghdr msg_sys;
-	void *usr_msg_ctl = NULL;
-	void *krn_msg_ctl = NULL;
+	void * krn_msg_ctl = NULL;
 	int err;
 	int total_len;
 	int len;
 
 	/* kernel mode address */
 	char addr[MAX_SOCK_ADDR];
-	int addr_len;
 
 	/* user mode address pointers */
 	struct sockaddr *uaddr;
 	int *uaddr_len;
 	
-	if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
-		return(-EBADF);
-	if (!(sock = sockfd_lookup(fd, NULL)))
-		return(-ENOTSOCK);
-	
+	if (!(sock = sockfd_lookup(fd, &err)))
+		return err;
+
 	if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr)))
 		return -EFAULT; 
 
-	if(msg_sys.msg_iovlen>UIO_MAXIOV)
+	if (msg_sys.msg_iovlen>UIO_MAXIOV)
 		return -EINVAL;
 
 	/*
@@ -1219,49 +1231,77 @@
 	 */
 	uaddr = msg_sys.msg_name;
 	uaddr_len = &msg->msg_namelen;
-	err=verify_iovec(&msg_sys,iov,addr, VERIFY_WRITE);
-	if(err<0)
+	err=verify_iovec(&msg_sys, iov, addr, VERIFY_WRITE);
+	if (err<0)
 		return err;
 
 	total_len=err;
 
+	msg_sys.msg_flags = 0;
 	
+	if (msg_sys.msg_control==NULL)
+		msg_sys.msg_controllen = 0;
 
-	if (msg_sys.msg_control)
+	if (msg_sys.msg_controllen)
 	{
-		usr_msg_ctl = msg_sys.msg_control;
 		krn_msg_ctl = kmalloc(msg_sys.msg_controllen, GFP_KERNEL);
-		err = copy_from_user(krn_msg_ctl, usr_msg_ctl,
+		
+		if (!krn_msg_ctl)
+		{
+			err=-ENOBUFS;
+			goto flush_it;		
+		}
+		err = copy_from_user(krn_msg_ctl, msg_sys.msg_control,
 				     msg_sys.msg_controllen);
 		if (err)
-			return -EFAULT;
+			goto flush_it;
 		msg_sys.msg_control = krn_msg_ctl;
 	}
-	
-	if(sock->ops->recvmsg==NULL)
-		return -EOPNOTSUPP;
-	len=sock->ops->recvmsg(sock, &msg_sys, total_len, (file->f_flags&O_NONBLOCK), flags, &addr_len);
-	if(len<0)
-		return len;
 
+	if (current->files->fd[fd]->f_flags&O_NONBLOCK)
+		flags |= MSG_DONTWAIT;
+
+	len=sock_recvmsg(sock, &msg_sys, total_len, flags);
+	if (msg_sys.msg_iov != iov)
+		kfree(iov);
+	if (len<0)
+	{
+		err=len;
+		goto flush_it;
+	}
+	
 	if (uaddr != NULL)
 	{
-		err = move_addr_to_user(addr, addr_len, uaddr, uaddr_len);
+		err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr,
+					uaddr_len);
+
 	}
+	
+	if (!err && msg_sys.msg_controllen)
+	{
+		err = copy_to_user(msg_sys.msg_control, krn_msg_ctl,
+				   msg_sys.msg_controllen);
+	}
+
+flush_it:
+	if (msg_sys.msg_iov != iov)
+		kfree(iov);
 
-	if (msg_sys.msg_control)
+	if (krn_msg_ctl)
 	{
-		if (!err)
-		{
-			err = copy_to_user(usr_msg_ctl, krn_msg_ctl,
-					   msg_sys.msg_controllen);
-			if (err)
-			    err = -EFAULT; 
-		}
-		kfree(krn_msg_ctl);		
+		kfree(krn_msg_ctl);
 	}
 
-	return err ? err : len;
+	if (err)
+		return err;
+
+	if (put_user(msg_sys.msg_flags, &msg->msg_flags))
+		return -EFAULT;
+
+	if (put_user(msg_sys.msg_controllen, &msg->msg_controllen))
+		return -EFAULT;
+
+	return len;
 }
 
 
@@ -1274,8 +1314,8 @@
 	struct socket *sock;
 
 	sock = socki_lookup (filp->f_inode);
-	if (sock != NULL && sock->ops != NULL && sock->ops->fcntl != NULL)
-		return(sock->ops->fcntl(sock, cmd, arg));
+	if (sock && sock->ops && sock->ops->fcntl)
+		return sock->ops->fcntl(sock, cmd, arg);
 	return(-EINVAL);
 }
 
@@ -1383,23 +1423,23 @@
 	return -EINVAL; /* to keep gcc happy */
 }
 
+
 /*
  *	This function is called by a protocol handler that wants to
  *	advertise its address family, and have it linked into the
  *	SOCKET module.
  */
  
-int sock_register(int family, struct proto_ops *ops)
+int sock_register(struct net_proto_family *ops)
 {
 	int i;
 
 	cli();
 	for(i = 0; i < NPROTO; i++) 
 	{
-		if (pops[i] != NULL) 
+		if (net_families[i] != NULL) 
 			continue;
-		pops[i] = ops;
-		pops[i]->family = family;
+		net_families[i] = ops;
 		sti();
 		return(i);
 	}
@@ -1420,11 +1460,11 @@
 	cli();
 	for(i = 0; i < NPROTO; i++) 
 	{
-		if (pops[i] == NULL) 
+		if (net_families[i] == NULL) 
 			continue;
-		if (pops[i]->family == family)
+		if (net_families[i]->family == family)
 		{
-			pops[i]=NULL;
+			net_families[i]=NULL;
 			sti();
 			return(i);
 		}
@@ -1453,13 +1493,13 @@
 {
 	int i;
 
-	printk(KERN_INFO "Swansea University Computer Society NET3.037 for Linux 2.1\n");
+	printk(KERN_INFO "Swansea University Computer Society NET3.038 for Linux 2.1\n");
 
 	/*
 	 *	Initialize all address (protocol) families. 
 	 */
 	 
-	for (i = 0; i < NPROTO; ++i) pops[i] = NULL;
+	for (i = 0; i < NPROTO; i++) net_families[i] = NULL;
 	
 	/*
 	 *	The netlink device handler may be needed early.
@@ -1467,13 +1507,6 @@
 
 #ifdef CONFIG_NETLINK
 	init_netlink();
-#endif		 
-	/*
-	 *	Attach the routing/device information port.
-	 */
-
-#if defined(CONFIG_RTNETLINK)
-	netlink_attach(NETLINK_ROUTE, netlink_donothing);
 #endif
 
 	/*
diff -u --recursive --new-file v2.1.14/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.14/linux/net/unix/af_unix.c	Sat Nov 30 12:03:14 1996
+++ linux/net/unix/af_unix.c	Thu Dec 12 16:54:26 1996
@@ -45,6 +45,21 @@
  *	socketpair(...SOCK_RAW..) doesn't panic the kernel.
  *	BSD af_unix apparently has connect forgetting to block properly.
  *		(need to check this with the POSIX spec in detail)
+ *
+ * Differences from 2.0.0-11-... (ANK)
+ *	Bug fixes and improvements.
+ *		- client shutdown killed server socket.
+ *		- removed all useless cli/sti pairs.
+ *		- (suspicious!) not allow connect/send to connected not to us
+ *		  socket, return EPERM.
+ *
+ *	Semantic changes/extensions.
+ *		- generic control message passing.
+ *		- SCM_CREDENTIALS control message.
+ *		- "Abstract" (not FS based) socket bindings.
+ *		  Abstract names are sequences of bytes (not zero terminated)
+ *		  started by 0, so that this name space does not intersect
+ *		  with BSD names.
  */
 
 #include <linux/config.h>
@@ -72,71 +87,144 @@
 #include <net/tcp.h>
 #include <net/af_unix.h>
 #include <linux/proc_fs.h>
+#include <net/scm.h>
 
-unix_socket *unix_socket_list=NULL;
+#include <asm/checksum.h>
 
 #define min(a,b)	(((a)<(b))?(a):(b))
 
-/*
- *	Make sure the unix name is null-terminated.
- */
- 
-static inline void unix_mkname(struct sockaddr_un * sunaddr, unsigned long len)
+
+unix_socket *unix_socket_table[UNIX_HASH_SIZE+1];
+
+#define unix_sockets_unbound	(unix_socket_table[UNIX_HASH_SIZE])
+
+#define UNIX_ABSTRACT(sk)	((sk)->protinfo.af_unix.addr->hash!=UNIX_HASH_SIZE)
+
+static __inline__ unsigned unix_hash_fold(unsigned hash)
+{
+	hash ^= hash>>16;
+	hash ^= hash>>8;
+	hash ^= hash>>4;
+	return hash;
+}
+
+#define unix_peer(sk) ((sk)->pair)
+
+static __inline__ int unix_our_peer(unix_socket *sk, unix_socket *osk)
+{
+	return unix_peer(osk) == sk;
+}
+
+static __inline__ int unix_may_send(unix_socket *sk, unix_socket *osk)
 {
-	if (len >= sizeof(*sunaddr))
-		len = sizeof(*sunaddr)-1;
-	((char *)sunaddr)[len]=0;
+	return !unix_peer(osk) || unix_peer(osk) == sk;
+}
+
+static __inline__ void unix_lock(unix_socket *sk)
+{
+	sk->users++;
+}
+
+static __inline__ int unix_unlock(unix_socket *sk)
+{
+	return sk->users--;
+}
+
+static __inline__ int unix_locked(unix_socket *sk)
+{
+	return sk->users;
+}
+
+static __inline__ void unix_release_addr(struct unix_address *addr)
+{
+	if (addr)
+	{
+		if (atomic_dec_and_test(&addr->refcnt))
+			kfree(addr);
+	}
 }
 
+
 /*
- *	Note: Sockets may not be removed _during_ an interrupt or net_bh
- *	handler using this technique. They can be added although we do not
- *	use this facility.
+ *	Check unix socket name:
+ *		- should be not zero length.
+ *	        - if started by not zero, should be NULL terminated (FS object)
+ *		- if started by zero, it is abstract name.
  */
  
-static void unix_remove_socket(unix_socket *sk)
+static int unix_mkname(struct sockaddr_un * sunaddr, int len, unsigned *hashp)
 {
-	unix_socket **s;
-	
-	cli();
-	s=&unix_socket_list;
-
-	while(*s!=NULL)
+	if (len <= sizeof(short) || len > sizeof(*sunaddr))
+		return -EINVAL;
+	if (!sunaddr || sunaddr->sun_family != AF_UNIX)
+		return -EINVAL;
+	if (sunaddr->sun_path[0])
 	{
-		if(*s==sk)
-		{
-			*s=sk->next;
-			sti();
-			return;
-		}
-		s=&((*s)->next);
+		if (len >= sizeof(*sunaddr))
+			len = sizeof(*sunaddr)-1;
+		((char *)sunaddr)[len]=0;
+		len = strlen(sunaddr->sun_path)+1+sizeof(short);
+		return len;
 	}
-	sti();
+
+	*hashp = unix_hash_fold(csum_partial((char*)sunaddr, len, 0));
+	return len;
+}
+
+static void unix_remove_socket(unix_socket *sk)
+{
+	unix_socket **list = sk->protinfo.af_unix.list;
+	if (sk->next)
+		sk->next->prev = sk->prev;
+	if (sk->prev)
+		sk->prev->next = sk->next;
+	if (*list == sk)
+		*list = sk->next;
+	sk->protinfo.af_unix.list = NULL;
+	sk->prev = NULL;
+	sk->next = NULL;
 }
 
 static void unix_insert_socket(unix_socket *sk)
 {
-	cli();
-	sk->next=unix_socket_list;
-	unix_socket_list=sk;
-	sti();
+	unix_socket **list = sk->protinfo.af_unix.list;
+	sk->prev = NULL;
+	sk->next = *list;
+	if (*list)
+		(*list)->prev = sk;
+	*list=sk;
 }
 
-static unix_socket *unix_find_socket(struct inode *i)
+static unix_socket *unix_find_socket_byname(struct sockaddr_un *sunname,
+					    int len, int type, unsigned hash)
 {
 	unix_socket *s;
-	cli();
-	s=unix_socket_list;
-	while(s)
+
+	for (s=unix_socket_table[(hash^type)&0xF]; s; s=s->next)
+	{
+		if(s->protinfo.af_unix.addr->len==len &&
+		   memcmp(s->protinfo.af_unix.addr->name, sunname, len) == 0 &&
+		   s->type == type)
+		{
+			unix_lock(s);
+			return(s);
+		}
+	}
+	return(NULL);
+}
+
+static unix_socket *unix_find_socket_byinode(struct inode *i)
+{
+	unix_socket *s;
+
+	for (s=unix_socket_table[i->i_ino & 0xF]; s; s=s->next)
 	{
 		if(s->protinfo.af_unix.inode==i)
 		{
-			sti();
+			unix_lock(s);
 			return(s);
 		}
-		s=s->next;
 	}
-	sti();
 	return(NULL);
 }
 
@@ -147,10 +235,9 @@
 static void unix_destroy_timer(unsigned long data)
 {
 	unix_socket *sk=(unix_socket *)data;
-	if(sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0)
+	if(!unix_locked(sk) && sk->wmem_alloc==0)
 	{
-		if(sk->protinfo.af_unix.name)
-			kfree(sk->protinfo.af_unix.name);
+		unix_release_addr(sk->protinfo.af_unix.addr);
 		sk_free(sk);
 		return;
 	}
@@ -201,10 +288,9 @@
 		sk->protinfo.af_unix.inode=NULL;
 	}
 	
-	if(--sk->protinfo.af_unix.locks==0 && sk->wmem_alloc==0)
+	if(!unix_unlock(sk) && sk->wmem_alloc==0)
 	{
-		if(sk->protinfo.af_unix.name)
-			kfree(sk->protinfo.af_unix.name);
+		unix_release_addr(sk->protinfo.af_unix.addr);
 		sk_free(sk);
 	}
 	else
@@ -223,35 +309,21 @@
 	return -EINVAL;
 }
 
-/*
- *	Yes socket options work with the new unix domain socketry!!!!!!!
- */
- 
-static int unix_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
-{
-	unix_socket *sk=sock->data;
-	if(level!=SOL_SOCKET)
-		return -EOPNOTSUPP;
-	return sock_setsockopt(sk,level,optname,optval,optlen);	
-}
-
-static int unix_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
-{
-	unix_socket *sk=sock->data;
-	if(level!=SOL_SOCKET)
-		return -EOPNOTSUPP;
-	return sock_getsockopt(sk,level,optname,optval,optlen);
-}
-
 static int unix_listen(struct socket *sock, int backlog)
 {
-	unix_socket *sk=sock->data;
-	if(sk->type!=SOCK_STREAM)
+	struct sock *sk = sock->sk;
+
+	if (sock->state != SS_UNCONNECTED) 
+		return(-EINVAL);
+	if (sock->type!=SOCK_STREAM)
 		return -EOPNOTSUPP;		/* Only stream sockets accept */
-	if(sk->protinfo.af_unix.name==NULL)
+	if (!sk->protinfo.af_unix.addr)
 		return -EINVAL;			/* No listens on an unbound socket */
 	sk->max_ack_backlog=backlog;
+	if (sk->ack_backlog < backlog)
+		sk->state_change(sk);
 	sk->state=TCP_LISTEN;
+	sock->flags |= SO_ACCEPTCON;
 	return 0;
 }
 
@@ -279,17 +351,22 @@
 	}
 }
 
+extern struct proto_ops unix_stream_ops;
+extern struct proto_ops unix_dgram_ops;
+
 static int unix_create(struct socket *sock, int protocol)
 {
-	unix_socket *sk;
-	if(protocol && protocol != PF_UNIX)
+	struct sock *sk;
+
+	sock->state = SS_UNCONNECTED;
+
+	if (protocol && protocol != PF_UNIX)
 		return -EPROTONOSUPPORT;
-	sk=(unix_socket *)sk_alloc(GFP_KERNEL);
-	if(sk==NULL)
-		return -ENOMEM;
-	switch(sock->type)
+
+	switch (sock->type)
 	{
 		case SOCK_STREAM:
+			sock->ops = &unix_stream_ops;
 			break;
 		/*
 		 *	Believe it or not BSD has AF_UNIX, SOCK_RAW though
@@ -298,19 +375,31 @@
 		case SOCK_RAW:
 			sock->type=SOCK_DGRAM;
 		case SOCK_DGRAM:
+			sock->ops = &unix_dgram_ops;
 			break;
 		default:
-			sk_free(sk);
 			return -ESOCKTNOSUPPORT;
 	}
-	sk->type=sock->type;
+	sk = sk_alloc(GFP_KERNEL);
+	if (!sk)
+		return -ENOMEM;
+	sock->sk = sk;
+	sk->type = sock->type;
+	sk->socket=sock;
+	sk->sleep= &sock->wait;
+
+	sk->peercred.pid = 0;
+	sk->peercred.uid = -1;
+	sk->peercred.gid = -1;
+	
 	init_timer(&sk->timer);
 	skb_queue_head_init(&sk->write_queue);
 	skb_queue_head_init(&sk->receive_queue);
+	skb_queue_head_init(&sk->error_queue);
 	skb_queue_head_init(&sk->back_log);
 	sk->protinfo.af_unix.family=AF_UNIX;
 	sk->protinfo.af_unix.inode=NULL;
-	sk->protinfo.af_unix.locks=1;		/* Us */
+	sk->users=1;				/* Us */
 	sk->protinfo.af_unix.readsem=MUTEX;	/* single task reading lock */
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
@@ -322,39 +411,40 @@
 	sk->write_space=def_callback3;
 	sk->error_report=def_callback1;
 	sk->mtu=4096;
-	sk->socket=sock;
-	sock->data=(void *)sk;
-	sk->sleep=sock->wait;
+	sk->protinfo.af_unix.list=&unix_sockets_unbound;
 	unix_insert_socket(sk);
 	return 0;
 }
 
 static int unix_dup(struct socket *newsock, struct socket *oldsock)
 {
-	return unix_create(newsock,0);
+	return unix_create(newsock, 0);
 }
 
 static int unix_release(struct socket *sock, struct socket *peer)
 {
-	unix_socket *sk=sock->data;
+	unix_socket *sk = sock->sk;
 	unix_socket *skpair;
-	
-	/* May not have data attached */
-	
-	if(sk==NULL)
+
+	if (!sk)
 		return 0;
-		
+	
+	if (sock->state != SS_UNCONNECTED)
+		sock->state = SS_DISCONNECTING;
+
 	sk->state_change(sk);
 	sk->dead=1;
-	skpair=(unix_socket *)sk->protinfo.af_unix.other;	/* Person we send to (default) */
-	if(sk->type==SOCK_STREAM && skpair!=NULL && skpair->state!=TCP_LISTEN)
+	skpair=unix_peer(sk);
+	if (sock->type==SOCK_STREAM && skpair)
 	{
-		skpair->shutdown=SHUTDOWN_MASK;		/* No more writes */
-		skpair->state_change(skpair);		/* Wake any blocked writes */
-	}
-	if(skpair!=NULL)
-		skpair->protinfo.af_unix.locks--;	/* It may now die */
-	sk->protinfo.af_unix.other=NULL;		/* No pair */
+		if (unix_our_peer(sk, skpair))
+			skpair->shutdown=SHUTDOWN_MASK;	/* No more writes */
+		if (skpair->state!=TCP_LISTEN)
+			skpair->state_change(skpair);	/* Wake any blocked writes */
+	}
+	if (skpair!=NULL)
+		unix_unlock(skpair);			/* It may now die */
+	unix_peer(sk)=NULL;				/* No pair */
 	unix_destroy_socket(sk);			/* Try to flush out this socket. Throw out buffers at least */
 	unix_gc();					/* Garbage collect fds */	
 
@@ -362,33 +452,85 @@
 	 *	FIXME: BSD difference: In BSD all sockets connected to use get ECONNRESET and we die on the spot. In
 	 *	Linux we behave like files and pipes do and wait for the last dereference.
 	 */
+	if (sk->socket)
+	{
+		sk->socket = NULL;
+		sock->sk = NULL;
+	}
 	 
-	sock->data = NULL;
-	sk->socket = NULL;
-	
 	return 0;
 }
 
+static int unix_autobind(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	static u32 ordernum = 1;
+	struct unix_address * addr;
+	unix_socket *osk;
+
+	addr = kmalloc(sizeof(*addr) + sizeof(short) + 16, GFP_KERNEL);
+	if (!addr)
+		return -ENOBUFS;
+	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode)
+	{
+		kfree(addr);
+		return -EINVAL;
+	}
+	memset(addr, 0, sizeof(*addr) + sizeof(short) + 16);
+	addr->name->sun_family = AF_UNIX;
+	addr->refcnt = 1;
+
+retry:
+	addr->len = sprintf(addr->name->sun_path+1, "%08x", ordernum) + 1 + sizeof(short);
+	addr->hash = unix_hash_fold(csum_partial((void*)addr->name, addr->len, 0));
+	ordernum++;
+
+	if ((osk=unix_find_socket_byname(addr->name, addr->len, sock->type,
+					 addr->hash)) != NULL)
+	{
+		unix_unlock(osk);
+		goto retry;
+	}
+
+	sk->protinfo.af_unix.addr = addr;
+	unix_remove_socket(sk);
+	sk->protinfo.af_unix.list = &unix_socket_table[(addr->hash ^ sk->type)&0xF];
+	unix_insert_socket(sk);
+	return 0;
+}
 
-static unix_socket *unix_find_other(char *path, int *error)
+static unix_socket *unix_find_other(struct sockaddr_un *sunname, int len,
+				    int type, unsigned hash, int *error)
 {
 	int old_fs;
 	int err;
 	struct inode *inode;
 	unix_socket *u;
 	
-	old_fs=get_fs();
-	set_fs(get_ds());
-	err = open_namei(path, 2, S_IFSOCK, &inode, NULL);
-	set_fs(old_fs);
-	if(err<0)
+	if (sunname->sun_path[0])
 	{
-		*error=err;
-		return NULL;
+		old_fs=get_fs();
+		set_fs(get_ds());
+		err = open_namei(sunname->sun_path, 2, S_IFSOCK, &inode, NULL);
+		set_fs(old_fs);
+		if(err<0)
+		{
+			*error=err;
+			return NULL;
+		}
+		u=unix_find_socket_byinode(inode);
+		iput(inode);
+		if (u && u->type != type)
+		{
+			*error=-EPROTOTYPE;
+			unix_unlock(u);
+			return NULL;
+		}
 	}
-	u=unix_find_socket(inode);
-	iput(inode);
-	if(u==NULL)
+	else
+		u=unix_find_socket_byname(sunname, len, type, hash);
+
+	if (u==NULL)
 	{
 		*error=-ECONNREFUSED;
 		return NULL;
@@ -399,173 +541,230 @@
 
 static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
+	struct sock *sk = sock->sk;
 	struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
-	unix_socket *sk=sock->data;
+	struct inode * inode;
 	int old_fs;
 	int err;
+	unsigned hash;
+	struct unix_address *addr;
 	
-	if(sk->protinfo.af_unix.name)
-		return -EINVAL;		/* Already bound */
-	
-	if(addr_len>sizeof(struct sockaddr_un) || addr_len<3 || sunaddr->sun_family!=AF_UNIX)
+	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode ||
+	    sunaddr->sun_family != AF_UNIX)
 		return -EINVAL;
-	unix_mkname(sunaddr, addr_len);
-	/*
-	 *	Put ourselves in the filesystem
-	 */
-	if(sk->protinfo.af_unix.inode!=NULL)
-		return -EINVAL;
-	
-	sk->protinfo.af_unix.name=kmalloc(addr_len+1, GFP_KERNEL);
-	if(sk->protinfo.af_unix.name==NULL)
+
+	if (addr_len==sizeof(short))
+		return unix_autobind(sock);
+
+	addr_len = unix_mkname(sunaddr, addr_len, &hash);
+	if (addr_len < 0)
+		return addr_len;
+
+	addr = kmalloc(sizeof(*addr)+addr_len, GFP_KERNEL);
+	if (!addr)
 		return -ENOBUFS;
-	memcpy(sk->protinfo.af_unix.name, sunaddr->sun_path, addr_len+1);
+
+	/* We sleeped; recheck ... */
+
+	if (sk->protinfo.af_unix.addr || sk->protinfo.af_unix.inode)
+	{
+		kfree(addr);
+		return -EINVAL;		/* Already bound */
+	}
+
+	memcpy(addr->name, sunaddr, addr_len);
+	addr->len = addr_len;
+	addr->hash = hash;
+	addr->refcnt = 1;
+
+	if (!sunaddr->sun_path[0])
+	{
+		unix_socket *osk = unix_find_socket_byname(sunaddr, addr_len,
+							   sk->type, hash);
+		if (osk)
+		{
+			unix_unlock(osk);
+			kfree(addr);
+			return -EADDRINUSE;
+		}
+		unix_remove_socket(sk);
+		sk->protinfo.af_unix.addr = addr;
+		sk->protinfo.af_unix.list = &unix_socket_table[(hash^sk->type)&0xF];
+		unix_insert_socket(sk);
+		return 0;
+	}
+
+	addr->hash = UNIX_HASH_SIZE;
+	sk->protinfo.af_unix.addr = addr;
 	
 	old_fs=get_fs();
 	set_fs(get_ds());
-	
-	err=do_mknod(sk->protinfo.af_unix.name,S_IFSOCK|S_IRWXUGO,0);
-	if(err==0)
-		err=open_namei(sk->protinfo.af_unix.name, 2, S_IFSOCK, &sk->protinfo.af_unix.inode, NULL);
+
+	err=do_mknod(sunaddr->sun_path, S_IFSOCK|S_IRWXUGO, 0);
+	if (!err)
+		err=open_namei(sunaddr->sun_path, 2, S_IFSOCK, &inode, NULL);
 	
 	set_fs(old_fs);
 	
 	if(err<0)
 	{
-		kfree_s(sk->protinfo.af_unix.name,addr_len+1);
-		sk->protinfo.af_unix.name=NULL;
-		if(err==-EEXIST)
+		unix_release_addr(addr);
+		sk->protinfo.af_unix.addr = NULL;
+		if (err==-EEXIST)
 			return -EADDRINUSE;
 		else
 			return err;
 	}
-	
+	unix_remove_socket(sk);
+	sk->protinfo.af_unix.list = &unix_socket_table[inode->i_ino & 0xF];
+	sk->protinfo.af_unix.inode = inode;
+	unix_insert_socket(sk);
+
 	return 0;
-	
 }
 
-static int unix_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
+			      int alen, int flags)
 {
-	unix_socket *sk=sock->data;
-	struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
-	unix_socket *other;
-	struct sk_buff *skb;
+	struct sock *sk = sock->sk;
+	struct sockaddr_un *sun=(struct sockaddr_un*)addr;
+	struct sock *other;
+	unsigned hash;
 	int err;
-	
+
 	/*
 	 *	1003.1g breaking connected state with AF_UNSPEC
 	 */
 
-	if(sunaddr->sun_family==AF_UNSPEC)
+	if(addr->sa_family==AF_UNSPEC)
 	{
-		if(sk->protinfo.af_unix.other)
+		if(unix_peer(sk))
 		{
-			sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
-			sk->protinfo.af_unix.other=NULL;
+			unix_unlock(unix_peer(sk));
+			unix_peer(sk) = NULL;
 			sock->state=SS_UNCONNECTED;
 		}
 		return 0;
 	}
 		
-	if(sk->type==SOCK_STREAM && sk->protinfo.af_unix.other)
+	alen = unix_mkname(sun, alen, &hash);
+	if (alen < 0)
+		return alen;
+
+	other=unix_find_other(sun, alen, sock->type, hash, &err);
+	if (!other)
+		return err;
+	if (!unix_may_send(sk, other))
+	{
+		unix_unlock(other);
+		return -EPERM;
+	}
+
+	/*
+	 * If it was connected, reconnect.
+	 */
+	if (unix_peer(sk))
+	{
+		unix_unlock(unix_peer(sk));
+		unix_peer(sk)=NULL;
+	}
+	unix_peer(sk)=other;
+	if (sock->passcred && !sk->protinfo.af_unix.addr)
+		unix_autobind(sock);
+	return 0;
+}
+
+static int unix_stream_connect1(struct socket *sock, struct msghdr *msg,
+				int len, struct unix_skb_parms *cmsg, int nonblock)
+{
+	struct sockaddr_un *sunaddr=(struct sockaddr_un *)msg->msg_name;
+	struct sock *sk = sock->sk;
+	unix_socket *other;
+	struct sk_buff *skb;
+	int err;
+	unsigned hash;
+	int addr_len;
+
+	addr_len = unix_mkname(sunaddr, msg->msg_namelen, &hash);
+	if (addr_len < 0)
+		return addr_len;
+
+	switch (sock->state) 
+	{
+		case SS_UNCONNECTED:
+			/* This is ok... continue with connect */
+			break;
+		case SS_CONNECTED:
+			/* Socket is already connected */
+			return -EISCONN;
+		case SS_CONNECTING:
+			/* Not yet connected... we will check this. */
+			break;
+		default:
+			return(-EINVAL);
+	}
+
+
+	if (unix_peer(sk))
 	{
-		if(sock->state==SS_CONNECTING && sk->state==TCP_ESTABLISHED)
+		if (sock->state==SS_CONNECTING && sk->state==TCP_ESTABLISHED)
 		{
 			sock->state=SS_CONNECTED;
+			if (!sk->protinfo.af_unix.addr)
+				unix_autobind(sock);
 			return 0;
 		}
-		if(sock->state==SS_CONNECTING && sk->state == TCP_CLOSE)
+		if (sock->state==SS_CONNECTING && sk->state == TCP_CLOSE)
 		{
 			sock->state=SS_UNCONNECTED;
 			return -ECONNREFUSED;
 		}
-		if(sock->state!=SS_CONNECTING)
+		if (sock->state!=SS_CONNECTING)
 			return -EISCONN;
-		if(flags&O_NONBLOCK)
+		if (nonblock)
 			return -EALREADY;
 		/*
 		 *	Drop through the connect up logic to the wait.
 		 */
 	}
-	
-	if(addr_len < sizeof(sunaddr->sun_family)+1 || sunaddr->sun_family!=AF_UNIX)
-		return -EINVAL;
-		
-	unix_mkname(sunaddr, addr_len);
-		
-	if(sk->type==SOCK_DGRAM)
-	{
-		if(sk->protinfo.af_unix.other)
-		{
-			sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
-			sk->protinfo.af_unix.other=NULL;
-			sock->state=SS_UNCONNECTED;
-		}
-		other=unix_find_other(sunaddr->sun_path, &err);
-		if(other==NULL)
-			return err;
-		if(other->type!=sk->type)
-			return -EPROTOTYPE;
-		other->protinfo.af_unix.locks++;
-		sk->protinfo.af_unix.other=other;
-		sock->state=SS_CONNECTED;
-		sk->state=TCP_ESTABLISHED;
-		return 0;			/* Done */
-	}
-	
 
-	if(sock->state==SS_UNCONNECTED)
+	if (sock->state==SS_UNCONNECTED)
 	{
 		/*
 		 *	Now ready to connect
 		 */
 	 
-		skb=sock_alloc_send_skb(sk, 0, 0, 0, &err); /* Marker object */
+		skb=sock_alloc_send_skb(sk, len, 0, nonblock, &err); /* Marker object */
 		if(skb==NULL)
 			return err;
-		skb->sk=sk;				/* So they know it is us */
-		skb->free=1;
-		skb->h.filp=NULL;
+		memcpy(&UNIXCB(skb), cmsg, sizeof(*cmsg));
+		if (len)
+			memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
 		sk->state=TCP_CLOSE;
-		unix_mkname(sunaddr, addr_len);
-		other=unix_find_other(sunaddr->sun_path, &err);
+		other=unix_find_other(sunaddr, addr_len, sk->type, hash, &err);
 		if(other==NULL)
 		{
 			kfree_skb(skb, FREE_WRITE);
 			return err;
 		}
-		if(other->type!=sk->type)
-		{
-			kfree_skb(skb, FREE_WRITE);
-			return -EPROTOTYPE;
-		}
-		other->protinfo.af_unix.locks++;		/* Lock the other socket so it doesn't run off for a moment */
 		other->ack_backlog++;
-		sk->protinfo.af_unix.other=other;
+		unix_peer(sk)=other;
 		skb_queue_tail(&other->receive_queue,skb);
 		sk->state=TCP_SYN_SENT;
 		sock->state=SS_CONNECTING;
-		sti();
 		other->data_ready(other,0);		/* Wake up ! */		
 	}
 			
 	
 	/* Wait for an accept */
 	
-	cli();
 	while(sk->state==TCP_SYN_SENT)
 	{
-		if(flags&O_NONBLOCK)
-		{
-			sti();
+		if(nonblock)
 			return -EINPROGRESS;
-		}
 		interruptible_sleep_on(sk->sleep);
 		if(current->signal & ~current->blocked)
-		{
-			sti();
 			return -ERESTARTSYS;
-		}
 	}
 	
 	/*
@@ -574,10 +773,9 @@
 	 
 	if(sk->state==TCP_CLOSE)
 	{
-		sk->protinfo.af_unix.other->protinfo.af_unix.locks--;
-		sk->protinfo.af_unix.other=NULL;
+		unix_unlock(unix_peer(sk));
+		unix_peer(sk)=NULL;
 		sock->state=SS_UNCONNECTED;
-		sti();
 		return -ECONNREFUSED;
 	}
 	
@@ -586,315 +784,302 @@
 	 */
 	 
 	sock->state=SS_CONNECTED;
-	sti();
+	if (!sk->protinfo.af_unix.addr)
+		unix_autobind(sock);
 	return 0;
-	
 }
 
-static int unix_socketpair(struct socket *a, struct socket *b)
+
+static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
+			       int addr_len, int flags)
+{
+	struct msghdr msg;
+	struct unix_skb_parms cmsg;
+
+	msg.msg_name = uaddr;
+	msg.msg_namelen = addr_len;
+	cmsg.fp = NULL;
+	cmsg.attr = MSG_SYN;
+	cmsg.creds.pid = current->pid;
+	cmsg.creds.uid = current->euid;
+	cmsg.creds.gid = current->egid;
+
+	return unix_stream_connect1(sock, &msg, 0, &cmsg, flags&O_NONBLOCK);
+}
+
+static int unix_socketpair(struct socket *socka, struct socket *sockb)
 {
-	unix_socket *ska,*skb;	
-	
-	ska=a->data;
-	skb=b->data;
+	struct sock *ska=socka->sk, *skb = sockb->sk;
 
 	/* Join our sockets back to back */
-	ska->protinfo.af_unix.locks++;
-	skb->protinfo.af_unix.locks++;
-	ska->protinfo.af_unix.other=skb;
-	skb->protinfo.af_unix.other=ska;
-	ska->state=TCP_ESTABLISHED;
-	skb->state=TCP_ESTABLISHED;
+	unix_lock(ska);
+	unix_lock(skb);
+	unix_peer(ska)=skb;
+	unix_peer(skb)=ska;
+
+	if (ska->type != SOCK_DGRAM)
+	{
+		ska->state=TCP_ESTABLISHED;
+		skb->state=TCP_ESTABLISHED;
+		socka->state=SS_CONNECTED;
+		sockb->state=SS_CONNECTED;
+	}
 	return 0;
 }
 
 static int unix_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	unix_socket *sk=sock->data;
-	unix_socket *newsk, *tsk;
+	unix_socket *sk = sock->sk;
+	unix_socket *newsk = newsock->sk;
+	unix_socket *tsk;
 	struct sk_buff *skb;
 	
-	if(sk->type!=SOCK_STREAM)
-	{
+	if (sock->state != SS_UNCONNECTED)
+		return(-EINVAL);
+	if (!(sock->flags & SO_ACCEPTCON)) 
+		return(-EINVAL);
+
+	if (sock->type!=SOCK_STREAM)
 		return -EOPNOTSUPP;
-	}
-	if(sk->state!=TCP_LISTEN)
-	{
+	if (sk->state!=TCP_LISTEN)
 		return -EINVAL;
-	}
 		
-	newsk=newsock->data;
-	if(sk->protinfo.af_unix.name!=NULL)
+	if (sk->protinfo.af_unix.addr)
+	{
+		atomic_inc(&sk->protinfo.af_unix.addr->refcnt);
+		newsk->protinfo.af_unix.addr=sk->protinfo.af_unix.addr;
+	}
+	if (sk->protinfo.af_unix.inode)
 	{
-		newsk->protinfo.af_unix.name=kmalloc(strlen(sk->protinfo.af_unix.name)+1, GFP_KERNEL);
-		if(newsk->protinfo.af_unix.name==NULL)
-			return -ENOMEM;
-		strcpy(newsk->protinfo.af_unix.name, sk->protinfo.af_unix.name);
+		sk->protinfo.af_unix.inode->i_count++;
+		newsk->protinfo.af_unix.inode=sk->protinfo.af_unix.inode;
 	}
 		
 	do
 	{
-		cli();
 		skb=skb_dequeue(&sk->receive_queue);
 		if(skb==NULL)
 		{
 			if(flags&O_NONBLOCK)
-			{
-				sti();
 				return -EAGAIN;
-			}
 			interruptible_sleep_on(sk->sleep);
 			if(current->signal & ~current->blocked)
-			{
-				sti();
 				return -ERESTARTSYS;
-			}
-			sti();
 		}
-	}
-	while(skb==NULL);
+		if (!(UNIXCB(skb).attr & MSG_SYN))
+		{
+			tsk=skb->sk;
+			tsk->state_change(tsk);
+			kfree_skb(skb, FREE_WRITE);
+			skb = NULL;
+		}
+	} while(skb==NULL);
+
 	tsk=skb->sk;
-	kfree_skb(skb, FREE_WRITE);	/* The buffer is just used as a tag */
 	sk->ack_backlog--;
-	newsk->protinfo.af_unix.other=tsk;
-	tsk->protinfo.af_unix.other=newsk;
+	unix_peer(newsk)=tsk;
+	unix_peer(tsk)=newsk;
 	tsk->state=TCP_ESTABLISHED;
 	newsk->state=TCP_ESTABLISHED;
-	newsk->protinfo.af_unix.locks++;	/* Swap lock over */
-	sk->protinfo.af_unix.locks--;	/* Locked to child socket not master */
-	tsk->protinfo.af_unix.locks++;	/* Back lock */
-	sti();
+	memcpy(&newsk->peercred, UNIXCREDS(skb), sizeof(struct ucred));
+	tsk->peercred.pid = current->pid;
+	tsk->peercred.uid = current->euid;
+	tsk->peercred.gid = current->egid;
+	unix_lock(newsk);		/* Swap lock over */
+	unix_unlock(sk);		/* Locked to child socket not master */
+	unix_lock(tsk);			/* Back lock */
+	kfree_skb(skb, FREE_WRITE);	/* The buffer is just used as a tag */
 	tsk->state_change(tsk);		/* Wake up any sleeping connect */
 	sock_wake_async(tsk->socket, 0);
 	return 0;
 }
 
+
 static int unix_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
 {
-	unix_socket *sk=sock->data;
+	struct sock *sk = sock->sk;
 	struct sockaddr_un *sunaddr=(struct sockaddr_un *)uaddr;
 	
-	if(peer)
+	if (peer)
 	{
-		if(sk->protinfo.af_unix.other==NULL)
+		if (!unix_peer(sk))
 			return -ENOTCONN;
-		sk=sk->protinfo.af_unix.other;
+		sk=unix_peer(sk);
 	}
-	sunaddr->sun_family=AF_UNIX;
-	if(sk->protinfo.af_unix.name==NULL)
+	if (!sk->protinfo.af_unix.addr)
 	{
-		*sunaddr->sun_path=0;
-		*uaddr_len=sizeof(sunaddr->sun_family)+1;
+		sunaddr->sun_family = AF_UNIX;
+		sunaddr->sun_path[0] = 0;
+		*uaddr_len = sizeof(short);
 		return 0;		/* Not bound */
 	}
-	*uaddr_len=sizeof(sunaddr->sun_family)+strlen(sk->protinfo.af_unix.name)+1;
-	strcpy(sunaddr->sun_path,sk->protinfo.af_unix.name);		/* 108 byte limited */
+	*uaddr_len = sk->protinfo.af_unix.addr->len;
+	memcpy(sunaddr, sk->protinfo.af_unix.addr->name, *uaddr_len);
 	return 0;
 }
 
-/*
- *	Copy file descriptors into system space.
- *	Return number copied or negative error code
- */
- 
-static int unix_fd_copy(struct sock *sk, struct cmsghdr *cmsg, struct file **fp)
+static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
 {
-	int num=cmsg->cmsg_len-sizeof(struct cmsghdr);
 	int i;
-	int *fdp=(int *)cmsg->cmsg_data;
 
-	num /= sizeof(int);	/* Odd bytes are forgotten in BSD not errored */
-	if (num >= UNIX_MAX_FD)
-		return -EINVAL;
-	
-	/*
-	 *	Verify the descriptors.
-	 */
-	 
-	for(i=0; i< num; i++)
-	{
-		int fd;
-		
-		fd = fdp[i];	
-		if (fd < 0 || fd >= NR_OPEN)
-			return -EBADF;
-		if (current->files->fd[fd]==NULL)
-			return -EBADF;
-	}
-	
-        /* add another reference to these files */
-	for(i=0; i< num; i++)
-	{
-		fp[i]=current->files->fd[fdp[i]];
-		fp[i]->f_count++;
-		unix_inflight(fp[i]);
-	}
-	
-	return num;
+	scm->fp = UNIXCB(skb).fp;
+	skb->destructor = sock_wfree;
+	UNIXCB(skb).fp = NULL;
+
+	for (i=scm->fp->count-1; i>=0; i--)
+		unix_notinflight(scm->fp->fp[i]);
 }
 
-/*
- *	Free the descriptors in the array
- */
+static void unix_destruct_fds(struct sk_buff *skb)
+{
+	struct scm_cookie scm;
+	memset(&scm, 0, sizeof(scm));
+	unix_detach_fds(&scm, skb);
+	scm_destroy(&scm);
+	sock_wfree(skb);
+}
 
-static void unix_fd_free(struct sock *sk, struct file **fp, int num)
+static void unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
 {
 	int i;
-	for(i=0;i<num;i++)
-	{
-		close_fp(fp[i]);
-		unix_notinflight(fp[i]);
-	}
+	for (i=scm->fp->count-1; i>=0; i--)
+		unix_inflight(scm->fp->fp[i]);
+	UNIXCB(skb).fp = scm->fp;
+	skb->destructor = unix_destruct_fds;
+	scm->fp = NULL;
 }
 
 
 /*
- *	Perform the AF_UNIX file descriptor pass out functionality. This
- *	is nasty and messy as is the whole design of BSD file passing.
+ *	Send AF_UNIX data.
  */
 
-static void unix_detach_fds(struct sk_buff *skb, struct cmsghdr *cmsg)
+static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+			      struct scm_cookie *scm)
 {
-	int i;
-	/* count of space in parent for fds */
-	int cmnum;
-	struct file **fp;
-	int *cmfptr;
-	int fdnum;
+	struct sock *sk = sock->sk;
+	unix_socket *other;
+	struct sockaddr_un *sunaddr=msg->msg_name;
+	int namelen = 0; /* fake GCC */
+	int err;
+	unsigned hash;
+	struct sk_buff *skb;
 
-	cmfptr = NULL;
-	cmnum = 0;
-	if (cmsg)
-	{
-		cmnum = (cmsg->cmsg_len-sizeof(struct cmsghdr)) / sizeof(int);
-		cmfptr = (int *)&cmsg->cmsg_data;
+	if (msg->msg_flags&MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (msg->msg_flags&~MSG_DONTWAIT)
+		return -EINVAL;
+
+	if (msg->msg_namelen) {
+		namelen = unix_mkname(sunaddr, msg->msg_namelen, &hash);
+		if (namelen < 0)
+			return namelen;
+	} else {
+		sunaddr = NULL;
+		if (!unix_peer(sk))
+			return -ENOTCONN;
 	}
-	
-	fdnum = *(int *)skb->h.filp;
-	fp = (struct file **)(skb->h.filp+sizeof(long));
 
-	if (cmnum > fdnum)
-		cmnum = fdnum;
+	if (sock->passcred && !sk->protinfo.af_unix.addr)
+		unix_autobind(sock);
 
-	/*
-	 *	Copy those that fit
-	 */
-	for (i = 0 ; i < cmnum ; i++)
-	{
-		int new_fd = get_unused_fd();
-		if (new_fd < 0)
-			break;
-		current->files->fd[new_fd]=fp[i];
-		*cmfptr++ = new_fd;
-		unix_notinflight(fp[i]);
+	skb = sock_alloc_send_skb(sk, len, 0, msg->msg_flags&MSG_DONTWAIT, &err);
+		
+	if (skb==NULL)
+		return err;
+
+	memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
+	UNIXCB(skb).attr = msg->msg_flags;
+	if (scm->fp)
+		unix_attach_fds(scm, skb);
+
+	memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);
+
+	other = unix_peer(sk);
+	if (other && other->dead)
+	{
+		/* Alan said:
+		 *	Check with 1003.1g - what should
+		 *	datagram error
+		 * 
+		 * Pardon, if POSIX says, that we should return
+		 * error here, it is wrong.
+		 * It is the main idea of SOCK_DGRAM sockets,
+		 * they could die and be borned, and clients
+		 * should not care about it.
+		 * If you want SOCK_STREAM semantics,
+		 * use SOCK_STREAM.
+		 *				--ANK
+		 */
+		unix_unlock(other);
+		unix_peer(sk)=NULL;
+		other = NULL;
+		if (sunaddr == NULL) {
+			kfree_skb(skb, FREE_WRITE);
+			return -ECONNRESET;
+		}
 	}
-	/*
-	 *	Dump those that don't
-	 */
-	for( ; i < fdnum ; i++)
+	if (!other)
 	{
-		close_fp(fp[i]);
-		unix_notinflight(fp[i]);
+		other = unix_find_other(sunaddr, namelen, sk->type, hash, &err);
+		
+		if (other==NULL)
+		{
+			kfree_skb(skb, FREE_WRITE);
+			return err;
+		}
+		if (!unix_may_send(sk, other))
+		{
+			unix_unlock(other);
+			kfree_skb(skb, FREE_WRITE);
+			return -EPERM;
+		}
 	}
-	kfree(skb->h.filp);
-	skb->h.filp=NULL;
-
-	/* no need to use destructor */
-	skb->destructor = NULL;
-}
 
-static void unix_destruct_fds(struct sk_buff *skb)
-{
-	unix_detach_fds(skb,NULL);
-}
+	skb_queue_tail(&other->receive_queue, skb);
+	other->data_ready(other,len);
 	
-/*
- *	Attach the file descriptor array to an sk_buff
- */
-static void unix_attach_fds(int fpnum,struct file **fp,struct sk_buff *skb)
-{
-
-	skb->h.filp = kmalloc(sizeof(long)+fpnum*sizeof(struct file *), 
-							GFP_KERNEL);
-	/* number of descriptors starts block */
-	*(int *)skb->h.filp = fpnum;
-	/* actual  descriptors */
-	memcpy(skb->h.filp+sizeof(long),fp,fpnum*sizeof(struct file *));
-	skb->destructor = unix_destruct_fds;
+	if (!unix_peer(sk))
+		unix_unlock(other);
+	return len;
 }
 
-/*
- *	Send AF_UNIX data.
- */
 		
-static int unix_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
+static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+			       struct scm_cookie *scm)
 {
-	unix_socket *sk=sock->data;
+	struct sock *sk = sock->sk;
 	unix_socket *other;
 	struct sockaddr_un *sunaddr=msg->msg_name;
 	int err,size;
 	struct sk_buff *skb;
 	int limit=0;
 	int sent=0;
-	struct file *fp[UNIX_MAX_FD];
-	/* number of fds waiting to be passed, 0 means either
-	 * no fds to pass or they've already been passed 
-	 */
-	int fpnum=0;
 
-	if(sk->err)
-		return sock_error(sk);
+	if (sock->flags & SO_ACCEPTCON) 
+		return(-EINVAL);
 
-	if(flags&MSG_OOB)
+	if (msg->msg_flags&MSG_OOB)
 		return -EOPNOTSUPP;
-			
-	if(flags)	/* For now */ {
+
+	if (msg->msg_flags&~MSG_DONTWAIT)
 		return -EINVAL;
-	}
-		
-	if(sk->shutdown&SEND_SHUTDOWN)
-	{
-		send_sig(SIGPIPE,current,0);
-		return -EPIPE;
-	}
-	
-	if(sunaddr!=NULL)
-	{
-		if(sock->type==SOCK_STREAM)
-		{
-			if(sk->state==TCP_ESTABLISHED)
-				return -EISCONN;
-			else
-				return -EOPNOTSUPP;
-		}
-	}
 
-	if(sunaddr==NULL)
-	{
-		if(sk->protinfo.af_unix.other==NULL)
+	if (msg->msg_namelen) {
+		if (sk->state==TCP_ESTABLISHED)
+			return -EISCONN;
+		else
+			return -EOPNOTSUPP;
+	} else {
+		sunaddr = NULL;
+		if (!unix_peer(sk))
 			return -ENOTCONN;
 	}
 
-	/*
-	 *	A control message has been attached.
-	 */
-	if(msg->msg_control) 
-	{
-		struct cmsghdr *cm = msg->msg_control;
-
-		if(cm==NULL || msg->msg_controllen<sizeof(struct cmsghdr) ||
-		   cm->cmsg_type!=SCM_RIGHTS ||
-		   cm->cmsg_level!=SOL_SOCKET ||
-		   msg->msg_controllen!=cm->cmsg_len)
-		{
-		   	return -EINVAL;
-		}
-
-		fpnum = unix_fd_copy(sk, cm, fp);
-
-		if(fpnum<0) {
-			return fpnum;
-		}
+	if (sk->shutdown&SEND_SHUTDOWN) {
+		send_sig(SIGPIPE,current,0);
+		return -EPIPE;
 	}
 
 	while(sent < len)
@@ -906,23 +1091,17 @@
 		 
 		size=len-sent;
 
-		if(size>(sk->sndbuf-sizeof(struct sk_buff))/2)	/* Keep two messages in the pipe so it schedules better */
-		{
-			if(sock->type==SOCK_DGRAM)
-			{
-				unix_fd_free(sk,fp,fpnum);
-				return -EMSGSIZE;
-			}
+		if (size>(sk->sndbuf-sizeof(struct sk_buff))/2)	/* Keep two messages in the pipe so it schedules better */
 			size=(sk->sndbuf-sizeof(struct sk_buff))/2;
-		}
+
 		/*
 		 *	Keep to page sized kmalloc()'s as various people
 		 *	have suggested. Big mallocs stress the vm too
 		 *	much.
 		 */
-#define MAX_ALLOC (PAGE_SIZE*7/8)
-		if(size > MAX_ALLOC && sock->type!=SOCK_DGRAM)
-			limit = MAX_ALLOC;	/* Fall back to 4K if we can't grab a big buffer this instant */
+
+		if (size > 4000)
+			limit = 4000;	/* Fall back to 4K if we can't grab a big buffer this instant */
 		else
 			limit = 0;	/* Otherwise just grab and wait */
 
@@ -930,16 +1109,12 @@
 		 *	Grab a buffer
 		 */
 		 
-		skb=sock_alloc_send_skb(sk,size,limit,nonblock, &err);
+		skb=sock_alloc_send_skb(sk,size,limit,msg->msg_flags&MSG_DONTWAIT, &err);
 		
-		if(skb==NULL)
+		if (skb==NULL)
 		{
-			unix_fd_free(sk,fp,fpnum);
-			if(sent)
-			{
-				sk->err=-err;
+			if (sent)
 				return sent;
-			}
 			return err;
 		}
 
@@ -952,70 +1127,25 @@
 		 */
 		size = min(size, skb_tailroom(skb));
 
-		skb->sk=sk;
-		skb->free=1;
-		
-		if(fpnum)
-		{
-			unix_attach_fds(fpnum,fp,skb);
-			fpnum=0;
-		}
-		else
-			skb->h.filp=NULL;
+		memcpy(UNIXCREDS(skb), &scm->creds, sizeof(struct ucred));
+		UNIXCB(skb).attr = msg->msg_flags;
+		if (scm->fp)
+			unix_attach_fds(scm, skb);
+
+		memcpy_fromiovec(skb_put(skb,size), msg->msg_iov, size);
 
-		memcpy_fromiovec(skb_put(skb,size),msg->msg_iov, size);
+		other=unix_peer(sk);
 
-		cli();
-		if(sunaddr==NULL)
+		if (other->dead || (sk->shutdown & SEND_SHUTDOWN))
 		{
-			other=sk->protinfo.af_unix.other;
-			if(sock->type==SOCK_DGRAM && other->dead)
-			{
-				other->protinfo.af_unix.locks--;
-				sk->protinfo.af_unix.other=NULL;
-				sock->state=SS_UNCONNECTED;
-				sti();
-				kfree_skb(skb, FREE_WRITE);
-				/*
-				 *	Check with 1003.1g - what should
-				 *	datagram error
-				 */
-				if (!sent)
-					sent = -ECONNRESET;
-				return sent;
-			}
-			/*
-			 *	Stream sockets SIGPIPE
-			 */
-			if(sock->type==SOCK_STREAM && other->dead)
-			{
-				kfree_skb(skb, FREE_WRITE);
-				sti();
-				if(!sent)
-				{
-					send_sig(SIGPIPE,current,0);
-					sent = -EPIPE;
-				}
+			kfree_skb(skb, FREE_WRITE);
+			if(sent)
 				return sent;
-			}
-		}
-		else
-		{
-			unix_mkname(sunaddr, msg->msg_namelen);
-			other=unix_find_other(sunaddr->sun_path, &err);
-			if(other==NULL)
-			{
-				sti();
-				kfree_skb(skb, FREE_WRITE);
-				if(sent)
-					return sent;
-				else
-					return err;
-			}
+			send_sig(SIGPIPE,current,0);
+			return -EPIPE;
 		}
+
 		skb_queue_tail(&other->receive_queue, skb);
-		sti();
-		/* if we sent an fd, only do it once */
 		other->data_ready(other,size);
 		sent+=size;
 	}
@@ -1028,173 +1158,235 @@
  
 static void unix_data_wait(unix_socket * sk)
 {
-	/*
-	 *	AF_UNIX sockets get no messages during interrupts, so this
-	 *	is safe without cli/sti.
-	 */
-	if (!skb_peek(&sk->receive_queue)) {
+	if (!skb_peek(&sk->receive_queue))
+	{
 		sk->socket->flags |= SO_WAITDATA;
 		interruptible_sleep_on(sk->sleep);
 		sk->socket->flags &= ~SO_WAITDATA;
 	}
 }
 
-static int unix_recvmsg(struct socket *sock, struct msghdr *msg, int size, int noblock, int flags, int *addr_len)
+static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+			      int flags, struct scm_cookie *scm)
 {
-	unix_socket *sk=sock->data;
-	struct sockaddr_un *sunaddr=msg->msg_name;
+	struct sock *sk = sock->sk;
+	int noblock = flags & MSG_DONTWAIT;
 	struct sk_buff *skb;
-	int copied=0;
-	unsigned char *sp;
-	int len;
-	int num;
-	struct iovec *iov=msg->msg_iov;
-	struct cmsghdr *cm=NULL;
-	int ct=msg->msg_iovlen;
-	int err = 0;
+
+	if (flags&MSG_OOB)
+		return -EOPNOTSUPP;
+
+	msg->msg_namelen = 0;
+
+retry:
+	skb=skb_dequeue(&sk->receive_queue);
+
+	if (skb==NULL)
+	{
+		if (sk->shutdown & RCV_SHUTDOWN)
+			return 0;
+		if (noblock)
+			return -EAGAIN;
+		unix_data_wait(sk);
+		if (current->signal & ~current->blocked)
+			return -ERESTARTSYS;
+		goto retry;
+	}
+
+	if (msg->msg_name)
+	{
+		if (skb->sk->protinfo.af_unix.addr)
+		{
+			memcpy(msg->msg_name, skb->sk->protinfo.af_unix.addr->name,
+			       skb->sk->protinfo.af_unix.addr->len);
+			msg->msg_namelen=skb->sk->protinfo.af_unix.addr->len;
+		}
+		else
+			msg->msg_namelen=sizeof(short);
+	}
+
+	if (size > skb->len)
+		size = skb->len;
+	else if (size < skb->len)
+		msg->msg_flags |= MSG_TRUNC;
+
+	if (memcpy_toiovec(msg->msg_iov, skb->data, size)) {
+		skb_queue_head(&sk->receive_queue, skb);
+		return -EFAULT;
+	}
+
+	scm->creds = *UNIXCREDS(skb);
+
+	if (!(flags & MSG_PEEK))
+	{
+		if (UNIXCB(skb).fp)
+			unix_detach_fds(scm, skb);
+		kfree_skb(skb, FREE_WRITE);
+		return size;
+	} else 
+		/* It is questionable: on PEEK we could:
+		   - do not return fds - good, but too simple 8)
+		   - return fds, and do not return them on read (old strategy,
+		     apparently wrong)
+		   - clone fds (I choosed it for now, it is the most universal
+		     solution)
+		*/
+		if (UNIXCB(skb).fp)
+			scm->fp = scm_fp_dup(UNIXCB(skb).fp);
+
+	skb_queue_head(&sk->receive_queue, skb);
+	return size;
+}
+
+
+static int unix_stream_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+			       int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	int noblock = flags & MSG_DONTWAIT;
+	struct sockaddr_un *sunaddr=msg->msg_name;
+	int copied = 0;
+	int check_creds = 0;
 	int target = 1;
 
-	if(flags&MSG_OOB)
+	if (sock->flags & SO_ACCEPTCON) 
+		return(-EINVAL);
+
+	if (flags&MSG_OOB)
 		return -EOPNOTSUPP;
 	if(flags&MSG_WAITALL)
 		target = size;
 		
 		
-	if(addr_len)
-		*addr_len=0;
-		
-	if(msg->msg_control) 
+	msg->msg_namelen = 0;
+
+	/* Lock the socket to prevent queue disordering
+	 * while sleeps in memcpy_tomsg
+	 */
+
+	down(&sk->protinfo.af_unix.readsem);
+
+	do
 	{
-		cm=msg->msg_control;
+		int chunk;
+		struct sk_buff *skb;
 
-		if(msg->msg_controllen<sizeof(struct cmsghdr)
+		skb=skb_dequeue(&sk->receive_queue);
+		if (skb==NULL)
+		{
+			if (copied >= target)
+				break;
 #if 0
-/*		investigate this further -- Stevens example doesn't seem to care */
-		||
-		   cm->cmsg_type!=SCM_RIGHTS ||
-		   cm->cmsg_level!=SOL_SOCKET ||
-		   msg->msg_controllen!=cm->cmsg_len
+			/* ANK: sk->err is never set for UNIX */
+			if (sk->err) 
+				return sock_error(sk);
 #endif
-		)
+			if (sk->shutdown & RCV_SHUTDOWN)
+				break;
+			up(&sk->protinfo.af_unix.readsem);
+			if (noblock)
+				return -EAGAIN;
+			unix_data_wait(sk);
+			if (current->signal & ~current->blocked)
+				return -ERESTARTSYS;
+			down(&sk->protinfo.af_unix.readsem);
+			continue;
+		}
+
+		/* Never glue messages from different writers */
+		if (check_creds &&
+		    memcmp(UNIXCREDS(skb), &scm->creds, sizeof(scm->creds)) != 0)
 		{
-			printk(KERN_DEBUG "unix_recvmsg: Bad msg_control\n");
-		   	return -EINVAL;
+			skb_queue_head(&sk->receive_queue, skb);
+			break;
 		}
-	}
-	
-	down(&sk->protinfo.af_unix.readsem);		/* Lock the socket */
-	while(ct--)
-	{
-		int done=0;
-		sp=iov->iov_base;
-		len=iov->iov_len;
-		iov++;
-		
-		while(done<len)
+
+		/* Copy address just once */
+		if (sunaddr)
 		{
-			if (copied && (flags & MSG_PEEK))
-				goto out;
-			if (copied == size)
-				goto out;
-			skb=skb_dequeue(&sk->receive_queue);
-			if(skb==NULL)
+			if (skb->sk->protinfo.af_unix.addr)
 			{
-				up(&sk->protinfo.af_unix.readsem);
+				memcpy(sunaddr, skb->sk->protinfo.af_unix.addr->name,
+				       skb->sk->protinfo.af_unix.addr->len);
+				msg->msg_namelen=skb->sk->protinfo.af_unix.addr->len;
+			}
+			else
+				msg->msg_namelen=sizeof(short);
+			sunaddr = NULL;
+		}
 
-				if(copied >= target)
-					return copied;
+		chunk = min(skb->len, size);
+		memcpy_toiovec(msg->msg_iov, skb->data, chunk);
+		copied += chunk;
+		size -= chunk;
 
-				/*
-				 *	POSIX checking order...
-				 */
-				
-				if(sk->err)
-					return sock_error(sk);
-				if(sk->shutdown & RCV_SHUTDOWN)
-					return copied;
-				
-				if(current->signal & ~current->blocked)
-					return -ERESTARTSYS;
-				if(noblock)
-					return -EAGAIN;
-
-				unix_data_wait(sk);
-				down(&sk->protinfo.af_unix.readsem);
-				continue;
-			}
-			if(msg->msg_name!=NULL)
-			{
-				sunaddr->sun_family=AF_UNIX;
-				if(skb->sk->protinfo.af_unix.name)
-				{
-					memcpy(sunaddr->sun_path, skb->sk->protinfo.af_unix.name, 108);
-					if(addr_len)
-						*addr_len=strlen(sunaddr->sun_path)+sizeof(short);
-				}
-				else
-					if(addr_len)
-						*addr_len=sizeof(short);
-			}
+		/* Copy credentials */
+		scm->creds = *UNIXCREDS(skb);
+		check_creds = 1;
 
-			num=skb->len;
-			if(num>len-done)
-			{
-				num=len-done;
-				msg->msg_flags|=MSG_TRUNC;
-			}
-			err = copy_to_user(sp, skb->data, num);
+		/* Mark read part of skb as used */
+		if (!(flags & MSG_PEEK))
+		{
+			skb_pull(skb, chunk);
 
-			if (err)
-			{
-				goto out;
-			}
-			
-			if (skb->h.filp!=NULL)
-				unix_detach_fds(skb,cm);
+			if (UNIXCB(skb).fp)
+				unix_detach_fds(scm, skb);
 
-			copied+=num;
-			done+=num;
-			sp+=num;
-			if (!(flags & MSG_PEEK))
-				skb_pull(skb, num);
 			/* put the skb back if we didn't use it up.. */
-			if (skb->len) {
+			if (skb->len)
+			{
 				skb_queue_head(&sk->receive_queue, skb);
-				continue;
+				break;
 			}
+
 			kfree_skb(skb, FREE_WRITE);
-			if(sock->type==SOCK_DGRAM || cm)
-				goto out;
+
+			if (scm->fp)
+				break;
 		}
-	}
-out:
-	up(&sk->protinfo.af_unix.readsem);
+		else
+		{
+			/* It is questionable,
+			   see note in unix_dgram_recvmsg.
+			 */
+			if (UNIXCB(skb).fp)
+				scm->fp = scm_fp_dup(UNIXCB(skb).fp);
+
+			/* put message back and return */
+			skb_queue_head(&sk->receive_queue, skb);
+			break;
+		}
+	} while (size);
 
-	return err ? -EFAULT : copied;
+	up(&sk->protinfo.af_unix.readsem);
+	return copied;
 }
 
 static int unix_shutdown(struct socket *sock, int mode)
 {
-	unix_socket *sk=(unix_socket *)sock->data;
-	unix_socket *other=sk->protinfo.af_unix.other;
-	if(mode&SEND_SHUTDOWN)
+	struct sock *sk = sock->sk;
+	unix_socket *other=unix_peer(sk);
+
+	if (mode&SEND_SHUTDOWN)
 	{
 		sk->shutdown|=SEND_SHUTDOWN;
 		sk->state_change(sk);
-		if(other)
+		if(other && sk->type == SOCK_STREAM && other->state != TCP_LISTEN)
 		{
-			other->shutdown|=RCV_SHUTDOWN;
+			if (unix_our_peer(sk, other))
+				other->shutdown|=RCV_SHUTDOWN;
 			other->state_change(other);
 		}
 	}
-	other=sk->protinfo.af_unix.other;
+	other=unix_peer(sk);
 	if(mode&RCV_SHUTDOWN)
 	{
 		sk->shutdown|=RCV_SHUTDOWN;
 		sk->state_change(sk);
-		if(other)
+		if(other && sk->type != SOCK_DGRAM && other->state != TCP_LISTEN)
 		{
-			other->shutdown|=SEND_SHUTDOWN;
+			if (unix_our_peer(sk, other))
+				other->shutdown|=SEND_SHUTDOWN;
 			other->state_change(other);
 		}
 	}
@@ -1202,14 +1394,9 @@
 }
 
 		
-static int unix_select(struct socket *sock,  int sel_type, select_table *wait)
-{
-	return datagram_select(sock->data,sel_type,wait);
-}
-
 static int unix_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	unix_socket *sk=sock->data;
+	struct sock *sk = sock->sk;
 	long amount=0;
 			
 	switch(cmd)
@@ -1247,25 +1434,35 @@
 	off_t pos=0;
 	off_t begin=0;
 	int len=0;
-	unix_socket *s=unix_socket_list;
+	int i;
+	unix_socket *s;
 	
 	len+= sprintf(buffer,"Num       RefCount Protocol Flags    Type St "
 	    "Inode Path\n");
 	
-	while(s!=NULL)
+	forall_unix_sockets (i,s)
 	{
 		len+=sprintf(buffer+len,"%p: %08X %08X %08lX %04X %02X %5ld",
 			s,
-			s->protinfo.af_unix.locks,
+			s->users,
 			0,
-			s->socket->flags,
-			s->socket->type,
-			s->socket->state,
-			s->socket->inode ? s->socket->inode->i_ino : 0);
-		if(s->protinfo.af_unix.name!=NULL)
-			len+=sprintf(buffer+len, " %s\n", s->protinfo.af_unix.name);
-		else
-			buffer[len++]='\n';
+			s->socket ? s->socket->flags : 0,
+			s->type,
+			s->socket ? s->socket->state : 0,
+			s->socket ? s->socket->inode->i_ino : 0);
+
+		if (s->protinfo.af_unix.addr)
+		{
+			buffer[len++] = ' ';
+			memcpy(buffer+len, s->protinfo.af_unix.addr->name->sun_path,
+			       s->protinfo.af_unix.addr->len-sizeof(short));
+			if (!UNIX_ABSTRACT(s))
+				len--;
+			else
+				buffer[len] = '@';
+			len += s->protinfo.af_unix.addr->len - sizeof(short);
+		}
+		buffer[len++]='\n';
 		
 		pos=begin+len;
 		if(pos<offset)
@@ -1274,9 +1471,9 @@
 			begin=pos;
 		}
 		if(pos>offset+length)
-			break;
-		s=s->next;
+			goto done;
 	}
+done:
 	*start=buffer+(offset-begin);
 	len-=(offset-begin);
 	if(len>length)
@@ -1285,26 +1482,51 @@
 }
 #endif
 
-struct proto_ops unix_proto_ops = {
+struct proto_ops unix_stream_ops = {
 	AF_UNIX,
 	
-	unix_create,
 	unix_dup,
 	unix_release,
 	unix_bind,
-	unix_connect,
+	unix_stream_connect,
 	unix_socketpair,
 	unix_accept,
 	unix_getname,
-	unix_select,
+	datagram_select,
 	unix_ioctl,
 	unix_listen,
 	unix_shutdown,
-	unix_setsockopt,
-	unix_getsockopt,
+	NULL,
+	NULL,
 	unix_fcntl,
-	unix_sendmsg,
-	unix_recvmsg
+	unix_stream_sendmsg,
+	unix_stream_recvmsg
+};
+
+struct proto_ops unix_dgram_ops = {
+	AF_UNIX,
+	
+	unix_dup,
+	unix_release,
+	unix_bind,
+	unix_dgram_connect,
+	unix_socketpair,
+	NULL,
+	unix_getname,
+	datagram_select,
+	unix_ioctl,
+	NULL,
+	unix_shutdown,
+	NULL,
+	NULL,
+	unix_fcntl,
+	unix_dgram_sendmsg,
+	unix_dgram_recvmsg
+};
+
+struct net_proto_family unix_family_ops = {
+	AF_UNIX,
+	unix_create
 };
 
 #ifdef CONFIG_PROC_FS
@@ -1316,10 +1538,17 @@
 };
 #endif
 
+
 void unix_proto_init(struct net_proto *pro)
 {
+	struct sk_buff *dummy_skb;
 	printk(KERN_INFO "NET3: Unix domain sockets 0.14 for Linux NET3.037.\n");
-	sock_register(unix_proto_ops.family, &unix_proto_ops);
+	if (sizeof(struct unix_skb_parms) > sizeof(dummy_skb->cb))
+	{
+		printk(KERN_CRIT "unix_proto_init: panic\n");
+		return;
+	}
+	sock_register(&unix_family_ops);
 #ifdef CONFIG_PROC_FS
 	proc_net_register(&proc_net_unix);
 #endif
diff -u --recursive --new-file v2.1.14/linux/net/unix/garbage.c linux/net/unix/garbage.c
--- v2.1.14/linux/net/unix/garbage.c	Tue Oct 29 19:58:51 1996
+++ linux/net/unix/garbage.c	Thu Dec 12 16:54:26 1996
@@ -56,6 +56,7 @@
 #include <net/tcp.h>
 #include <net/af_unix.h>
 #include <linux/proc_fs.h>
+#include <net/scm.h>
 
 /* Internal data structures and random procedures: */
 
@@ -73,13 +74,14 @@
 	 *	Socket ?
 	 */
 	if (inode && inode->i_sock) {
-		struct socket * s = &inode->u.socket_i;
+		struct socket * sock = &inode->u.socket_i;
+		struct sock * s = sock->sk;
 
 		/*
 		 *	AF_UNIX ?
 		 */
-		if (s->ops == &unix_proto_ops)
-			u_sock = s->data;
+		if (s && sock->ops && sock->ops->family == AF_UNIX)
+			u_sock = s;
 	}
 	return u_sock;
 }
@@ -141,6 +143,7 @@
 void unix_gc(void)
 {
 	static int in_unix_gc=0;
+	int i;
 	unix_socket *s;
 	unix_socket *next;
 	
@@ -171,7 +174,7 @@
 	 *	Push root set
 	 */
 
-	for(s=unix_socket_list;s!=NULL;s=s->next)
+	forall_unix_sockets(i, s)
 	{
 		/*
 		 *	If all instances of the descriptor are not
@@ -202,13 +205,13 @@
 			/*
 			 *	Do we have file descriptors ?
 			 */
-			if(skb->h.filp)
+			if(UNIXCB(skb).fp)
 			{
 				/*
 				 *	Process the descriptors of this socket
 				 */
-				int nfd=*(int *)skb->h.filp;
-				struct file **fp=(struct file **)(skb->h.filp+sizeof(int));
+				int nfd=UNIXCB(skb).fp->count;
+				struct file **fp = UNIXCB(skb).fp->fp;
 				while(nfd--)
 				{
 					/*
@@ -250,7 +253,7 @@
 	 *	Sweep phase.  NOTE: this part dominates the time complexity 
 	 */
 
-	for(s=unix_socket_list;s!=NULL;s=next)
+	forall_unix_sockets(i, s)
 	{
 		next=s->next;
 		if (!(s->protinfo.af_unix.marksweep&MARKED))
diff -u --recursive --new-file v2.1.14/linux/scripts/mkdep.c linux/scripts/mkdep.c
--- v2.1.14/linux/scripts/mkdep.c	Thu Dec 12 17:02:48 1996
+++ linux/scripts/mkdep.c	Wed Dec 11 16:01:13 1996
@@ -26,20 +26,15 @@
 	int plen;
 	struct path_struct *path = path_array+type;
 
-	if (!type) {
-		if (memcmp(name, "linux/", 6) &&
-		    memcmp(name, "asm/", 4) &&
-		    memcmp(name, "net/", 4) &&
-		    memcmp(name, "scsi/", 5))
-			return;
-		if (len == 14 && !memcmp(name, "linux/config.h", len))
-			hasconfig = 1;
-	}
+	if (len == 14 && !memcmp(name, "linux/config.h", len))
+		hasconfig = 1;
 
 	plen = path->len;
 	memcpy(path->buffer+plen, name, len);
 	len += plen;
 	path->buffer[len] = '\0';
+	if (access(path->buffer, F_OK))
+		return;
 
 	if (!hasdep) {
 		hasdep = 1;
