diff -u --recursive --new-file v2.1.89/linux/CREDITS linux/CREDITS
--- v2.1.89/linux/CREDITS	Tue Mar 10 10:03:29 1998
+++ linux/CREDITS	Mon Mar 16 14:27:03 1998
@@ -62,9 +62,10 @@
 
 N: Andrea Arcangeli
 E: arcangeli@mbox.queen.it
-W: http://www-linux.deis.unibo.it/~mirror/
+W: http://www.cs.unibo.it/~arcangel/
 P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC  C0 4B 81 1D 8C 15 C8 E5
-D: parport sharing fix. Various other kernel hacks.
+D: Parport sharing hacker.
+D: Various other kernel hacks.
 S: Via Ciaclini 26
 S: Imola 40026
 S: Italy
@@ -395,6 +396,11 @@
 S: Blacksburg, Virginia 24061
 S: USA
 
+N: Cyrus Durgin
+E: cider@speakeasy.org
+W: http://www.speakeasy.org/~cider/
+D: implemented kmod
+
 N: Torsten Duwe
 E: Torsten.Duwe@informatik.uni-erlangen.de
 D: Part-time kernel hacker
@@ -1337,6 +1343,7 @@
 N: Kirk Petersen
 E: kirk@speakeasy.org
 W: http://www.speakeasy.org/~kirk/
+D: implemented kmod
 D: modularized BSD Unix domain sockets
 
 N: Kai Petzke
@@ -1412,6 +1419,15 @@
 S: 6, rue Augustin Thierry
 S: 75019 Paris
 S: France
+
+N: Rik van Riel
+E: H.H.vanRiel@fys.ruu.nl
+W: http://www.fys.ruu.nl/~riel/
+D: Maintainer of the mm-patches page (see www.linuxhq.com)
+D: Documentation/sysctl/*, kswapd fixes, random kernel hacker
+S: Vorenkampsweg 1
+S: NL-9488 TG  Zeijerveld
+S: The Netherlands
 
 N: William E. Roadcap
 E: roadcapw@cfw.com
diff -u --recursive --new-file v2.1.89/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.89/linux/Documentation/Changes	Tue Mar 10 10:03:29 1998
+++ linux/Documentation/Changes	Tue Mar 17 21:22:25 1998
@@ -32,7 +32,7 @@
    Also, don't forget http://www.linuxhq.com/ for all your Linux kernel
 needs.
 
-Last updated: February 16. 1998
+Last updated: March 16. 1998
 Current Author: Chris Ricker (kaboom@gatech.edu).
 
 Current Minimal Requirements
@@ -45,11 +45,11 @@
 - Kernel modules	 modutils-2.1.85         ; insmod -V
 - Gnu C 		 2.7.2.3                 ; gcc --version
 - Binutils		 2.8.1.0.1               ; ld -v
-- Linux C Library	 5.4.38                  ; ls -l /lib/libc.so.*
+- Linux C Library	 5.4.44                  ; ls -l /lib/libc.so.*
 - Dynamic Linker (ld.so) 1.9.5                   ; ldd -v
 - Linux C++ Library	 2.7.2.8                 ; ls -l /usr/lib/libg++.so.*
 - Procps		 1.2.5                   ; ps --version
-- Procinfo               0.11                    ; procinfo -v
+- Procinfo               0.13                    ; procinfo -v
 - Mount                  2.7l                    ; mount --version
 - Net-tools              1.41                    ; hostname -V
 - Loadlin                1.6a
@@ -58,7 +58,7 @@
 - NFS                    0.4.21                  ; showmount --version
 - Bash                   1.14.7                  ; bash -version
 - Ncpfs                  2.1.1                   ; ncpmount -v
-- Pcmcia-cs              2.9.12
+- Pcmcia-cs              3.0.0
 - PPP                    2.3.3                   ; pppd -v
 
 Upgrade notes
@@ -73,6 +73,10 @@
 in Lilo.  A small number of machines need "reboot=bios" to reboot via
 the BIOS.
 
+   Also, please remember that cua* devices are now obsolete.  Switch to
+the corresponding ttyS* device instead (e.g., cua0 -> ttyS0, cua1 ->
+ttyS1, etc.).
+
 Libc
 ====
 
@@ -109,6 +113,12 @@
    You need to upgrade to modutils-2.1.85 for kernels 2.1.85 and later.
 This version will also work with 2.0.x kernels.
 
+   As of 2.1.90-pre1, kerneld has been replaced by a kernel thread,
+kmod.  See Documentation/kmod.txt for more information.  The main
+user-level change this requires is modification to your init scripts to
+check for the absence of /proc/sys/kernel/modprobe before starting
+kerneld.
+
 Binutils
 ========
 
@@ -148,7 +158,7 @@
 presence.
 
    To turn on IP forwarding, issue the following command:   echo 1 >
-/proc/sys/net/ipv4/ip_forwarding
+/proc/sys/net/ipv4/ip_forward
 
    To run bootpd, you'll need to issue the following command:   echo 1
 >/proc/sys/net/ipv4/ip_boot_agent
@@ -266,12 +276,12 @@
 Linux C Library
 ===============
 
-The 5.4.38 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.38.bin.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.38.bin.tar.gz
+The 5.4.44 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.44.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.44.bin.tar.gz
 Installation notes for 5.4.38:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.38
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.38
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.44
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.44
 
 Linux C++ Library
 =================
@@ -308,7 +318,7 @@
 ==================
 
 The 0.11 release:
-ftp://ftp.cistron.nl/pub/people/svm/procinfo-0.11.tar.gz
+ftp://ftp.cistron.nl/pub/people/svm/procinfo-0.13.tar.gz
 
 RPM utilities
 =============
@@ -391,8 +401,8 @@
 Pcmcia-cs
 =========
 
-The 1.9.12 release:
-ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs-2.9.12.tar.gz
+The 3.0.0 release:
+ftp://hyper.stanford.edu/pub/pcmcia/pcmcia-cs-3.0.0.tar.gz
 
 PPP
 ===
@@ -415,11 +425,12 @@
 your favorite Red Hat mirror site before installing the non-RPM
 version.  Remember, you might need to use the -force option to get the
 upgrade to install.  ftp://ftp.redhat.com/pub/contrib/ will have almost
-everything you need.
+everything you need, as does RedHat 5.0.
 
    Those of you running Debian (or a different distribution that
 supports .deb packages) can look in the "unstable" and
-"project/experimental" directories of your favorite Debian mirror.
+"project/experimental" directories of your favorite Debian mirror.  The
+Debian 2.0 release should have most packages you need as well.
 
    For others, David Bourgin has put together a package of everything
 necessary to quickly and easily upgrade to 2.1.x.  See
diff -u --recursive --new-file v2.1.89/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.89/linux/Documentation/Configure.help	Tue Mar 10 10:03:30 1998
+++ linux/Documentation/Configure.help	Sat Mar 14 11:50:32 1998
@@ -103,6 +103,17 @@
   you are not sure, say Y; apart from resulting in a 45kB bigger
   kernel, it won't hurt.
 
+Max physical memory
+CONFIG_MAX_MEMSIZE
+  Linux/x86 can use up to 3.8 gigabytes of physical memory. Default
+  is max 1 gigabyte physical memory (1024 MB), this is enough for
+  most systems.
+  A system with 2G physical memory should use a value of ~2400, a
+  system with 3.8G memory should use something like 3900. A bit of
+  experimentation with the limit wont hurt, the kernel needs a ~128M
+  window for vmalloc() plus PCI space uses up some memory too, thus
+  addresses above FD000000 should rather be kept free.
+
 Normal floppy disk support
 CONFIG_BLK_DEV_FD
   If you want to use your floppy disk drive(s) under Linux, say
@@ -1326,21 +1337,12 @@
   non-kernel sources, you would benefit from this option. Otherwise
   it's not that important. So, N ought to be a safe bet.
 
-Kernel daemon support
-CONFIG_KERNELD
-  Normally when you have selected some drivers and/or filesystems to
-  be created as loadable modules, you also have the responsibility to
-  load the corresponding module (via insmod/modprobe) before you can
-  use it.  If you select Y here, the kernel will take care of this all
-  by itself, together with the user level daemon "kerneld".  Note that
-  "kerneld" will also automatically unload all unused modules, so you
-  don't have to use "rmmod" either.  kerneld will also provide support
-  for different user-level beeper and screen blanker programs later
-  on.  The "kerneld" daemon is included in the modutils package (check
-  Documentation/Changes for latest version and location). You will
-  probably want to read the kerneld mini-HOWTO, available via ftp
-  (user: anonymous) from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If unsure, say Y.
+Kernel module loader support
+CONFIG_KMOD
+  This feature allows the kernel to load modules for itself.  When
+  a part of the kernel needs a module, it runs modprobe with the
+  appropriate arguments.  Say Y here and read about configuring it
+  in Documentation/kmod.txt.  (this is a replacement of kerneld)
 
 ARP daemon support (EXPERIMENTAL)
 CONFIG_ARPD
@@ -6109,9 +6111,9 @@
   loadable module called `zft-compressor.o' which contains code to
   support user transparent on-the-fly compression based on Ross
   William's lzrw3 algorithm will be produced. If you have enabled
-  auto-loading of kernel modules via `kerneld' (i.e. have said `Y' to
-  CONFIG_KERNELD) then `zft-compressor.o' will be loaded automatically
-  by zftape when needed.
+  the kernel module loader (i.e. have said `Y' to CONFIG_KMOD) then
+  `zft-compressor.o' will be loaded automatically by zftape when
+  needed.
   Despite of its name zftape does NOT use compression by default. The
   file Documentation/ftape.txt contains a short description of the
   most important changes in the file system interface compared to
@@ -6569,12 +6571,6 @@
   be called joystick.o. If you want to compile it as a module, say M
   here and read Documentation/modules.txt.
 
-ARC console time
-CONFIG_RTC_ARC
-  If you boot your Alpha using the ARC firmware, say Y here. This option
-  adjusts the RTC clock to take into account the different starting epoch
-  used by ARC.
-
 Sound card support
 CONFIG_SOUND
   If you have a Sound Card in your Computer, i.e. if it can say more
@@ -6609,6 +6605,16 @@
   drivers/sound/Readme.cards file before answering this question. For
   an unknown card you may answer Y if the card claims to be
   SoundBlaster compatible.
+
+Are you using the IBM Mwave "emulation" of SB ?
+CONFIG_SB_MWAVE
+  The IBM Mwave can do whats loosely describable as emulation of an 8bit
+  soundblaster if you load the right firmware from DOS warm boot and pray
+  and your machine happens to like you. Say Y if you are doing this as the
+  IRQ test normally fails on the mwave emulation. If you'd like real MWAVE
+  support phone IBM (425-556-8822) and ask them why they still haven't 
+  released any documentation.
+  [http://204.200.238.31/cgi-bin/link.pl?co=i&cl=/ts/ibm/contact.html]
 
 Generic OPL2/OPL3 FM synthesizer support
 CONFIG_ADLIB
diff -u --recursive --new-file v2.1.89/linux/Documentation/cdrom/ide-cd linux/Documentation/cdrom/ide-cd
--- v2.1.89/linux/Documentation/cdrom/ide-cd	Fri Jan 30 11:28:05 1998
+++ linux/Documentation/cdrom/ide-cd	Tue Mar 17 21:39:11 1998
@@ -224,10 +224,8 @@
    - If the autoprobing is not finding your drive, you can tell the
      driver to assume that one exists by using a lilo option of the
      form `hdX=cdrom', where X is the drive letter corresponding to
-     where your drive is installed (see section 2). This is required
-     for CDROM drives such as the Pioneer DR-A24X, which do not properly
-     identify themselves as ATAPI CDROM drives.  Note that if you
-     do this and you see a boot message like
+     where your drive is installed.  Note that if you do this and you 
+     see a boot message like
 
        hdX: ATAPI cdrom (?)
 
@@ -281,7 +279,16 @@
     there are hardware problems with the interrupt setup; they
     apparently don't use interrupts.
 
-
+  - If you own a Pioneer DR-A24X, you _will_ get nasty error messages 
+    on boot such as "irq timeout: status=0x50 { DriveReady SeekComplete }"
+    The Pioneer DR-A24X cdrom drives are fairly popular these days.
+    Unfortunatly, these drives seem to become very confused when we perform
+    the standard Linux ATA disk drive probe. If you own one of these drives,
+    you can bypass the ATA probing which confuses these cdrom drives, by 
+    adding `append="hdX=noprobe hdX=cdrom"' to your lilo.conf file and runing 
+    lilo (again where X is the drive letter corresponding to where your drive 
+    is installed.)
+    
 c. System hangups.
 
   - If the system locks up when you try to access the cdrom, the most
diff -u --recursive --new-file v2.1.89/linux/Documentation/kmod.txt linux/Documentation/kmod.txt
--- v2.1.89/linux/Documentation/kmod.txt	Wed Dec 31 16:00:00 1969
+++ linux/Documentation/kmod.txt	Tue Mar 10 14:43:13 1998
@@ -0,0 +1,47 @@
+Kmod: The Kernel Module Loader
+Kirk Petersen
+
+Kmod is a simple replacement for kerneld.  It consists of a 
+request_module() replacement and a kernel thread called kmod.  When the
+kernel requests a module, the kmod wakes up and execve()s modprobe,
+passing it the name that was requested.  After a configurable period of
+time, kmod will have delete_module() remove any unused modules.
+
+Kmod is configurable through two entries in /proc/sys/kernel.  You can
+set the path of modprobe (where the kernel looks for it) by doing:
+
+	echo "/sbin/modprobe" > /proc/sys/kernel/modprobe
+
+To tell kmod when to unload unused modules, do something like:
+
+	echo "120" > /proc/sys/kernel/kmod_unload_delay
+
+Kmod only loads and unloads modules.  Kerneld could do more (although
+nothing in the standard kernel used its other features).  If you
+require features such as request_route, we suggest that you take
+a similar approach.  A simple request_route function could be called,
+and a kroute kernel thread could be sent off to do the work.  But
+we should probably keep this to a minimum.
+
+Kerneld also had a mechanism for storing device driver settings.  This
+can easily be done with modprobe.  When a module is unloaded, modprobe
+could look at a per-driver-configurable location (/proc/sys/drivers/blah)
+for device driver settings and save them to a file.  When a module
+is loaded, simply cat that file back to that location in the proc
+filesystem.  Or perhaps a script could be a setting in /etc/modules.conf.
+There are many user-land methods that will work (I prefer using /proc,
+myself).
+
+If kerneld worked, why replace it?
+
+- kerneld used sysv ipc, which can now be made into a module.  Besides,
+  sysv ipc is ugly and should therefore be avoided (well, certainly for
+  kernel level stuff)
+
+- both kmod and kerneld end up doing the same thing (calling modprobe),
+  so why not skip the middle man?
+
+- removing kerneld related stuff from ipc/msg.c made it 40% smaller
+
+- kmod reports errors through the normal kernel mechanisms, which avoids
+  the chicken and egg problem of kerneld and modular unix domain sockets
diff -u --recursive --new-file v2.1.89/linux/Documentation/modules.txt linux/Documentation/modules.txt
--- v2.1.89/linux/Documentation/modules.txt	Sun Dec 28 12:05:44 1997
+++ linux/Documentation/modules.txt	Tue Mar 10 14:43:13 1998
@@ -10,6 +10,12 @@
 features that the kernel now supports.  The current required version
 is listed in the file linux/Documentation/Changes.
 
+* * * NOTE * * *
+The kernel has been changed to remove kerneld support and use
+the new kmod support.  Keep this in mind when reading this file.  Kmod
+does the exact same thing as kerneld, but doesn't require an external
+program (see Documentation/kmod.txt)
+
 In the beginning...
 -------------------
 
diff -u --recursive --new-file v2.1.89/linux/Documentation/sysctl/vm.txt linux/Documentation/sysctl/vm.txt
--- v2.1.89/linux/Documentation/sysctl/vm.txt	Tue Mar 10 10:03:30 1998
+++ linux/Documentation/sysctl/vm.txt	Thu Mar 12 10:56:01 1998
@@ -16,6 +16,7 @@
 
 Currently, these files are in /proc/sys/vm:
 - bdflush
+- buffermem
 - freepages
 - overcommit_memory
 - swapctl
@@ -88,11 +89,27 @@
 age_super is for filesystem metadata.
 
 ==============================================================
+buffermem:
 
+The three values in this file correspond to the values in
+the struct buffer_mem. It controls how much memory should
+be used for buffer and cache memory. Note that memorymapped
+files are also counted as cache memory...
+
+The values are:
+min_percent	-- this is the minumum percentage of memory
+		   that should be spent on buffer + page cache
+borrow_percent  -- when Linux is short on memory, and buffer
+                   and cache use more than this percentage of
+                   memory, free pages are stolen from them
+max_percent     -- this is the maximum amount of memory that
+                   can be used for buffer and cache memory 
+
+==============================================================
 freepages:
 
-This file contains three values: min_free_pages, free_pages_low
-and free_pages_high in order.
+This file contains the values in the struct freepages. That
+struct contains three members: min, low and high.
 
 These numbers are used by the VM subsystem to keep a reasonable
 number of pages on the free page list, so that programs can
@@ -100,25 +117,23 @@
 free used pages first. The actual freeing of pages is done
 by kswapd, a kernel daemon.
 
-min_free_pages  -- when the number of free pages reaches this
-                   level, only the kernel can allocate memory
-                   for _critical_ tasks only
-free_pages_low  -- when the number of free pages drops below
-                   this level, kswapd is woken up immediately
-free_pages_high -- this is kswapd's target, when more than
-                   free_pages_high pages are free, kswapd will
-                   stop swapping.
-
-When the number of free pages is between free_pages_low and
-free_pages_high, and kswapd hasn't run for swapout_interval
-jiffies, then kswapd is woken up too. See swapout_interval
-for more info.
+min  -- when the number of free pages reaches this
+        level, only the kernel can allocate memory
+        for _critical_ tasks only
+low  -- when the number of free pages drops below
+        this level, kswapd is woken up immediately
+high -- this is kswapd's target, when more than <high>
+        pages are free, kswapd will stop swapping.
+
+When the number of free pages is between low and high,
+and kswapd hasn't run for swapout_interval jiffies, then
+kswapd is woken up too. See swapout_interval for more info.
 
 When free memory is always low on your system, and kswapd has
 trouble keeping up with allocations, you might want to
-increase these values, especially free_pages_high and perhaps
-free_pages_low. I've found that a 1:2:4 relation for these
-values tend to work rather well in a heavily loaded system.
+increase these values, especially high and perhaps low.
+I've found that a 1:2:4 relation for these values tend to work
+rather well in a heavily loaded system.
 
 ==============================================================
 
@@ -163,9 +178,7 @@
 
 swapctl:
 
-This file contains no less than 16 variables, of which about
-half is actually used :-) In the listing below, the unused
-variables are marked as such.
+This file contains no less than 8 variables.
 All of these values are used by kswapd, and the usage can be
 found in linux/mm/vmscan.c.
 
@@ -177,18 +190,10 @@
     unsigned int    sc_page_advance;
     unsigned int    sc_page_decline;
     unsigned int    sc_page_initial_age;
-    unsigned int    sc_max_buff_age;      /* unused */
-    unsigned int    sc_buff_advance;      /* unused */
-    unsigned int    sc_buff_decline;      /* unused */
-    unsigned int    sc_buff_initial_age;  /* unused */
     unsigned int    sc_age_cluster_fract;
     unsigned int    sc_age_cluster_min;
     unsigned int    sc_pageout_weight;
     unsigned int    sc_bufferout_weight;
-    unsigned int    sc_buffer_grace;      /* unused */
-    unsigned int    sc_nr_buffs_to_free;  /* unused */
-    unsigned int    sc_nr_pages_to_free;  /* unused */
-    enum RCL_POLICY sc_policy;            /* RCL_PERSIST hardcoded */
 } swap_control_v5;
 --------------------------------------------------------------
 
@@ -207,9 +212,8 @@
   (default 1)
 And when a page reaches age 0, it's ready to be swapped out.
 
-The variables sc_age_cluster_fract till sc_bufferout_weight
-have to do with the amount of scanning kswapd is doing on
-each call to try_to_swap_out().
+The next four variables can be used to control kswapd's
+agressiveness in swapping out pages.
 
 sc_age_cluster_fract is used to calculate how many pages from
 a process are to be scanned by kswapd. The formula used is
@@ -221,9 +225,12 @@
 
 The values of sc_pageout_weight and sc_bufferout_weight are
 used to control the how many tries kswapd will do in order
-to swapout one page / buffer. As with sc_age_cluster_fract,
-the actual value is calculated by several more or less complex
-formulae and the default value is good for every purpose.
+to swapout one page / buffer. These values can be used to
+finetune the ratio between user pages and buffer/cache memory.
+When you find that your Linux system is swapping out too much
+process pages in order to satisfy buffer memory demands, you
+might want to either increase sc_bufferout_weight, or decrease
+the value of sc_pageout_weight.
 
 ==============================================================
 
diff -u --recursive --new-file v2.1.89/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.89/linux/MAINTAINERS	Tue Mar 10 10:03:30 1998
+++ linux/MAINTAINERS	Tue Mar 17 21:21:04 1998
@@ -275,8 +275,8 @@
 L:	linux-hams@vger.rutgers.edu
 S:	Maintained
 
-HP100: Driver for HP 10/100 Mbit/s Network Adapter Series
-P:	Jarsolav Kysela
+HP100:	Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series
+P:	Jaroslav Kysela
 M:	perex@jcu.cz
 S:	Maintained
 
@@ -602,6 +602,12 @@
 WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS
 P:	Jean Tourrilhes
 M:	jt@hplb.hpl.hp.com
+S:	Maintained
+
+WD7000 SCSI DRIVER
+P:	Miroslav Zagorac
+M:	zaga@fly.cc.fer.hr
+L:	linux-scsi@vger.rutgers.edu
 S:	Maintained
 
 Z8530 DRIVER FOR AX.25
diff -u --recursive --new-file v2.1.89/linux/Makefile linux/Makefile
--- v2.1.89/linux/Makefile	Tue Mar 10 10:03:30 1998
+++ linux/Makefile	Tue Mar 10 16:54:08 1998
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 89
+SUBLEVEL = 90
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/)
 
@@ -9,7 +9,7 @@
 # because it makes re-config very ugly and too many fundamental files depend
 # on "CONFIG_SMP"
 #
-# NOTE! SMP is experimental. See the file Documentation/SMP.txt
+# For UP operations COMMENT THIS OUT, simply setting SMP = 0 won't work
 #
 SMP = 1
 
@@ -262,6 +262,7 @@
 include/linux/version.h: ./Makefile
 	@echo \#define UTS_RELEASE \"$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)\" > .ver
 	@echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver
+	@echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
 	@mv -f .ver $@
 
 init/version.o: init/version.c include/linux/compile.h
diff -u --recursive --new-file v2.1.89/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v2.1.89/linux/arch/alpha/config.in	Tue Mar 10 10:03:30 1998
+++ linux/arch/alpha/config.in	Tue Mar 10 14:43:13 1998
@@ -23,7 +23,7 @@
 if [ "$CONFIG_MODULES" = "y" ]; then
   MODULES=y
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
diff -u --recursive --new-file v2.1.89/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v2.1.89/linux/arch/alpha/defconfig	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/defconfig	Tue Mar 10 14:43:13 1998
@@ -12,7 +12,7 @@
 #
 CONFIG_MODULES=y
 # CONFIG_MODVERSIONS is not set
-# CONFIG_KERNELD is not set
+# CONFIG_KMOD is not set
 
 #
 # General setup
diff -u --recursive --new-file v2.1.89/linux/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- v2.1.89/linux/arch/alpha/kernel/alpha_ksyms.c	Tue Feb 17 13:12:44 1998
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -13,6 +13,7 @@
 #include <linux/socket.h>
 #include <linux/in.h>
 #include <linux/in6.h>
+#include <linux/pci.h>
 
 #include <asm/io.h>
 #include <asm/hwrpb.h>
@@ -146,3 +147,7 @@
 EXPORT_SYMBOL_NOVERS(__remqu);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memset);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/alpha/kernel/process.c linux/arch/alpha/kernel/process.c
--- v2.1.89/linux/arch/alpha/kernel/process.c	Mon Jan 12 14:51:14 1998
+++ linux/arch/alpha/kernel/process.c	Tue Mar 17 20:56:44 1998
@@ -39,6 +39,7 @@
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/hwrpb.h>
+#include <asm/fpu.h>
 
 /*
  * Initial task structure. Make this a per-architecture thing,
@@ -50,6 +51,7 @@
 unsigned long init_user_stack[1024] = { STACK_MAGIC, };
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
@@ -198,6 +200,10 @@
 
 void flush_thread(void)
 {
+	/* Arrange for each exec'ed process to start off with a 
+	   clean slate wrt the fpu.  */
+	current->tss.flags &= ~IEEE_SW_MASK;
+	wrfpcr(FPCR_DYN_NORMAL);
 }
 
 void release_thread(struct task_struct *dead_task)
diff -u --recursive --new-file v2.1.89/linux/arch/arm/config.in linux/arch/arm/config.in
--- v2.1.89/linux/arch/arm/config.in	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/config.in	Wed Mar 11 15:13:49 1998
@@ -16,7 +16,7 @@
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
diff -u --recursive --new-file v2.1.89/linux/arch/arm/defconfig linux/arch/arm/defconfig
--- v2.1.89/linux/arch/arm/defconfig	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/defconfig	Wed Mar 11 15:13:49 1998
@@ -13,7 +13,7 @@
 #
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
 
 #
 # General setup
diff -u --recursive --new-file v2.1.89/linux/arch/arm/kernel/armksyms.c linux/arch/arm/kernel/armksyms.c
--- v2.1.89/linux/arch/arm/kernel/armksyms.c	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/kernel/armksyms.c	Tue Mar 17 21:15:40 1998
@@ -4,6 +4,7 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/mman.h>
+#include <linux/pci.h>
 
 #include <asm/ecard.h>
 #include <asm/io.h>
@@ -176,3 +177,7 @@
 EXPORT_SYMBOL(test_and_change_bit);
 EXPORT_SYMBOL(find_first_zero_bit);
 EXPORT_SYMBOL(find_next_zero_bit);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/arm/kernel/init_task.c linux/arch/arm/kernel/init_task.c
--- v2.1.89/linux/arch/arm/kernel/init_task.c	Tue Jan 20 16:39:41 1998
+++ linux/arch/arm/kernel/init_task.c	Tue Mar 10 13:28:59 1998
@@ -6,6 +6,7 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct files * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.1.89/linux/arch/i386/.kernel_offset.lds linux/arch/i386/.kernel_offset.lds
--- v2.1.89/linux/arch/i386/.kernel_offset.lds	Wed Dec 31 16:00:00 1969
+++ linux/arch/i386/.kernel_offset.lds	Tue Mar 17 21:42:20 1998
@@ -0,0 +1 @@
+__kernel_offset__ = (0x1000-1024)*1024*1024;
diff -u --recursive --new-file v2.1.89/linux/arch/i386/Makefile linux/arch/i386/Makefile
--- v2.1.89/linux/arch/i386/Makefile	Wed Feb  4 11:35:59 1998
+++ linux/arch/i386/Makefile	Wed Mar 11 15:10:39 1998
@@ -58,6 +58,9 @@
 DRIVERS := $(DRIVERS) arch/i386/math-emu/math.a
 endif
 
+memsize: dummy
+	@echo "__kernel_offset__ = (0x1000-$(CONFIG_MAX_MEMSIZE))*1024*1024;" > arch/i386/.kernel_offset.lds
+
 arch/i386/kernel: dummy
 	$(MAKE) linuxsubdirs SUBDIRS=arch/i386/kernel
 
@@ -66,27 +69,27 @@
 
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
 
-zImage: vmlinux
+zImage: memsize vmlinux
 	@$(MAKEBOOT) zImage
 
-bzImage: vmlinux
+bzImage: memsize vmlinux
 	@$(MAKEBOOT) bzImage
 
 compressed: zImage
 
-zlilo: vmlinux
+zlilo: memsize vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=zImage zlilo
 
-bzlilo: vmlinux
+bzlilo: memsize vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
 
-zdisk: vmlinux
+zdisk: memsize vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=zImage zdisk
 
-bzdisk: vmlinux
+bzdisk: memsize vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=bzImage zdisk
 
-install: vmlinux
+install: memsize vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=bzImage install
 
 archclean:
diff -u --recursive --new-file v2.1.89/linux/arch/i386/config.in linux/arch/i386/config.in
--- v2.1.89/linux/arch/i386/config.in	Wed Feb  4 11:35:59 1998
+++ linux/arch/i386/config.in	Wed Mar 11 15:10:39 1998
@@ -17,6 +17,7 @@
 	 Pentium/K5/5x86/6x86	CONFIG_M586	\
 	 PPro/K6/6x86MX		CONFIG_M686" Pentium
 bool 'Math emulation' CONFIG_MATH_EMULATION
+int ' Max physical memory in MB' CONFIG_MAX_MEMSIZE 1024
 endmenu
 
 mainmenu_option next_comment
@@ -24,7 +25,7 @@
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
diff -u --recursive --new-file v2.1.89/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.89/linux/arch/i386/defconfig	Fri Jan 30 11:28:05 1998
+++ linux/arch/i386/defconfig	Wed Mar 11 16:07:55 1998
@@ -15,13 +15,14 @@
 CONFIG_M586=y
 # CONFIG_M686 is not set
 # CONFIG_MATH_EMULATION is not set
+CONFIG_MAX_MEMSIZE=1024
 
 #
 # Loadable module support
 #
 CONFIG_MODULES=y
 # CONFIG_MODVERSIONS is not set
-# CONFIG_KERNELD is not set
+# CONFIG_KMOD is not set
 
 #
 # General setup
@@ -30,7 +31,6 @@
 CONFIG_PCI=y
 CONFIG_PCI_BIOS=y
 CONFIG_PCI_DIRECT=y
-# CONFIG_PCI_OPTIMIZE is not set
 CONFIG_PCI_OLD_PROC=y
 # CONFIG_MCA is not set
 CONFIG_SYSVIPC=y
@@ -94,7 +94,6 @@
 # CONFIG_IP_ADVANCED_ROUTER is not set
 # CONFIG_IP_PNP is not set
 # CONFIG_IP_ACCT is not set
-# CONFIG_IP_MASQUERADE is not set
 # CONFIG_IP_ROUTER is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- v2.1.89/linux/arch/i386/kernel/head.S	Tue Feb 17 13:12:44 1998
+++ linux/arch/i386/kernel/head.S	Thu Mar 12 10:33:21 1998
@@ -11,6 +11,9 @@
 #include <linux/tasks.h>
 #include <linux/linkage.h>
 #include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
 
 #define CL_MAGIC_ADDR	0x90020
 #define CL_MAGIC	0xA33F
@@ -54,9 +57,9 @@
  *	be using the global pages. 
  *
  *	NOTE! We have to correct for the fact that we're
- *	not yet offset 0xC0000000..
+ *	not yet offset PAGE_OFFSET..
  */
-#define cr4_bits mmu_cr4_features-0xC0000000
+#define cr4_bits mmu_cr4_features-__PAGE_OFFSET
 	movl %cr4,%eax		# Turn on 4Mb pages
 	orl cr4_bits,%eax
 	movl %eax,%cr4
@@ -368,7 +371,7 @@
  *  sets up a idt with 256 entries pointing to
  *  ignore_int, interrupt gates. It doesn't actually load
  *  idt - that can be done only after paging has been enabled
- *  and the kernel moved to 0xC0000000. Interrupts
+ *  and the kernel moved to PAGE_OFFSET. Interrupts
  *  are enabled elsewhere, when we can be relatively
  *  sure everything is ok.
  */
@@ -444,14 +447,16 @@
 /*
  * This is initialized to create a identity-mapping at 0-4M (for bootup
  * purposes) and another mapping of the 0-4M area at virtual address
- * 0xC0000000.
+ * PAGE_OFFSET.
  */
 .org 0x1000
 ENTRY(swapper_pg_dir)
 	.long 0x00102007
-	.fill 767,4,0
+	.fill __USER_PGD_PTRS-1,4,0
+	/* default: 767 entries */
 	.long 0x00102007
-	.fill 255,4,0
+	/* default: 255 entries */
+	.fill __KERNEL_PGD_PTRS-1,4,0
 
 /*
  * The page tables are initialized to only 4MB here - the final page
@@ -614,8 +619,8 @@
 	.fill 256,8,0		# idt is uninitialized
 
 /*
- * This gdt setup gives the kernel a 1GB address space at virtual
- * address 0xC0000000 - space enough for expansion, I hope.
+ * This gdt setup gives the kernel a CONFIG_MAX_MEMSIZE sized address space at
+ * virtual address PAGE_OFFSET.
  *
  * This contains up to 8192 quadwords depending on NR_TASKS - 64kB of
  * gdt entries.  Ugh. 
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- v2.1.89/linux/arch/i386/kernel/i386_ksyms.c	Fri Jan 30 15:50:56 1998
+++ linux/arch/i386/kernel/i386_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -8,6 +8,7 @@
 #include <linux/in6.h>
 #include <linux/interrupt.h>
 #include <linux/smp_lock.h>
+#include <linux/pci.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
@@ -97,3 +98,6 @@
 EXPORT_SYMBOL(mca_isadapter);
 #endif
 
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/init_task.c linux/arch/i386/kernel/init_task.c
--- v2.1.89/linux/arch/i386/kernel/init_task.c	Mon Dec  1 10:34:10 1997
+++ linux/arch/i386/kernel/init_task.c	Mon Mar 16 14:16:56 1998
@@ -6,6 +6,7 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/io_apic.c linux/arch/i386/kernel/io_apic.c
--- v2.1.89/linux/arch/i386/kernel/io_apic.c	Thu Feb 12 20:56:04 1998
+++ linux/arch/i386/kernel/io_apic.c	Thu Mar 12 10:44:00 1998
@@ -271,7 +271,7 @@
 		if (!IO_APIC_IRQ(irq))
 			continue;
 
-		entry.vector = IO_APIC_GATE_OFFSET + (irq<<3);
+		entry.vector = IO_APIC_VECTOR(irq);
 
 		/*
 		 * Determine IRQ line polarity (high active or low active):
@@ -383,7 +383,7 @@
 	entry.mask = 1;					/* unmask IRQ now */
 	entry.dest.logical.logical_dest = 0xff;		/* all CPUs */
 
-	entry.vector = IO_APIC_GATE_OFFSET + (irq<<3);
+	entry.vector = IO_APIC_VECTOR(irq);
 
 	entry.polarity=0;
 	entry.trigger=0;
@@ -513,11 +513,19 @@
 	return;
 }
 
-void init_sym_mode (void)
+static void init_sym_mode (void)
 {
 	printk("enabling Symmetric IO mode ... ");
 		outb (0x70, 0x22);
 		outb (0x01, 0x23);
+	printk("...done.\n");
+}
+
+void init_pic_mode (void)
+{
+	printk("disabling Symmetric IO mode ... ");
+		outb (0x70, 0x22);
+		outb (0x00, 0x23);
 	printk("...done.\n");
 }
 
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/ioport.c linux/arch/i386/kernel/ioport.c
--- v2.1.89/linux/arch/i386/kernel/ioport.c	Wed Nov  5 08:57:51 1997
+++ linux/arch/i386/kernel/ioport.c	Thu Mar 12 10:43:06 1998
@@ -13,6 +13,7 @@
 #include <linux/mm.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
+#include <linux/stddef.h>
 
 /* Set EXTENT bits starting at BASE in BITMAP to value TURN_ON. */
 static void set_bitmap(unsigned long *bitmap, short base, short extent, int new_value)
@@ -53,12 +54,25 @@
  */
 asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int turn_on)
 {
+	struct thread_struct * t = &current->tss;
+
 	if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32))
 		return -EINVAL;
 	if (!suser())
 		return -EPERM;
+	/*
+	 * If it's the first ioperm() call in this thread's lifetime, set the
+	 * IO bitmap up. ioperm() is much less timing critical than clone(),
+	 * this is why we delay this operation until now:
+	 */
+#define IO_BITMAP_OFFSET offsetof(struct thread_struct,io_bitmap)
+
+	if (t->bitmap != IO_BITMAP_OFFSET) {
+		t->bitmap = IO_BITMAP_OFFSET;
+		memset(t->io_bitmap,0xff,(IO_BITMAP_SIZE+1)*4);
+	}
 		
-	set_bitmap((unsigned long *)current->tss.io_bitmap, from, num, !turn_on);
+	set_bitmap((unsigned long *)t->io_bitmap, from, num, !turn_on);
 	return 0;
 }
 
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.89/linux/arch/i386/kernel/irq.c	Tue Mar 10 10:03:30 1998
+++ linux/arch/i386/kernel/irq.c	Wed Mar 11 15:09:25 1998
@@ -70,6 +70,7 @@
 
 static unsigned int irq_events [NR_IRQS] = { -1, };
 static int disabled_irq [NR_IRQS] = { 0, };
+static int ipi_pending [NR_IRQS] = { 0, };
 
 /*
  * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
@@ -489,12 +490,8 @@
  */
 void synchronize_bh(void)
 {
-	if (atomic_read(&global_bh_count)) {
-		int cpu = smp_processor_id();
-		if (!local_irq_count[cpu] && !local_bh_count[cpu]) {
+	if (atomic_read(&global_bh_count) && !in_interrupt())
 			wait_on_bh();
-		}
-	}
 }
 
 /*
@@ -672,8 +669,8 @@
 #ifdef __SMP__
 void enable_ioapic_irq (unsigned int irq)
 {
-	unsigned long flags;
-	int cpu = smp_processor_id(), should_handle_irq;
+	unsigned long flags, should_handle_irq;
+	int cpu = smp_processor_id();
 
 	spin_lock_irqsave(&irq_controller_lock, flags);
 	if (disabled_irq[irq])
@@ -682,18 +679,32 @@
 		spin_unlock_irqrestore(&irq_controller_lock, flags);
 		return;
 	}
+#if 0
 	/*
 	 * In the SMP+IOAPIC case it might happen that there are an unspecified
-	 * number of pending IRQ events unhandled. We protect against multiple
-	 * enable_irq()'s executing them via disable_irq[irq]++
+	 * number of pending IRQ events unhandled. These cases are very rare,
+	 * so we 'resend' these IRQs via IPIs, to the same CPU. It's much
+	 * better to do it this way as thus we dont have to be aware of
+	 * 'pending' interrupts in the IRQ path, except at this point.
 	 */
 	if (!disabled_irq[irq] && irq_events[irq]) {
+		if (!ipi_pending[irq]) {
+			ipi_pending[irq] = 1;
+			--irq_events[irq];
+			send_IPI(cpu,IO_APIC_VECTOR(irq));
+		}
+	}
+	spin_unlock_irqrestore(&irq_controller_lock, flags);
+#else
+	if (!disabled_irq[irq] && irq_events[irq]) {
 		struct pt_regs regs; /* FIXME: these are fake currently */
 
 		disabled_irq[irq]++;
+		hardirq_enter(cpu);
 		spin_unlock(&irq_controller_lock);
+
 		release_irqlock(cpu);
-		irq_enter(cpu, irq);
+		while (test_bit(0,&global_irq_lock)) mb();
 again:
 		handle_IRQ_event(irq, &regs);
 
@@ -713,6 +724,7 @@
 		__restore_flags(flags);
 	} else
 		spin_unlock_irqrestore(&irq_controller_lock, flags);
+#endif
 }
 #endif
 
@@ -775,15 +787,16 @@
 	ack_APIC_irq();
 
 	spin_lock(&irq_controller_lock);
+	if (ipi_pending[irq])
+		ipi_pending[irq] = 0;
 
 	if (!irq_events[irq]++ && !disabled_irq[irq])
 		should_handle_irq = 1;
-
+	hardirq_enter(cpu);
 	spin_unlock(&irq_controller_lock);
 
-	irq_enter(cpu, irq);
-
 	if (should_handle_irq) {
+		while (test_bit(0,&global_irq_lock)) mb();
 again:
 		handle_IRQ_event(irq, regs);
 
@@ -797,7 +810,8 @@
 			goto again;
 	}
 
-	irq_exit(cpu, irq);
+	hardirq_exit(cpu);
+	release_irqlock(cpu);
 }
 #endif
 
@@ -1034,7 +1048,7 @@
 	 * 0x80, because int 0x80 is hm, kindof importantish ;)
 	 */
 	for (i = 0; i < NR_IRQS ; i++)
-		if (IO_APIC_GATE_OFFSET+(i<<3) <= 0xfe)  /* HACK */ {
+		if (IO_APIC_VECTOR(i) <= 0xfe)  /* HACK */ {
 			if (IO_APIC_IRQ(i)) {
 				irq_handles[i] = &ioapic_irq_type;
 				/*
@@ -1071,8 +1085,8 @@
 #ifdef __SMP__	
 
 	for (i = 0; i < NR_IRQS ; i++)
-		if (IO_APIC_GATE_OFFSET+(i<<3) <= 0xfe)  /* hack -- mingo */
-			set_intr_gate(IO_APIC_GATE_OFFSET+(i<<3),interrupt[i]);
+		if (IO_APIC_VECTOR(i) <= 0xfe)  /* hack -- mingo */
+			set_intr_gate(IO_APIC_VECTOR(i),interrupt[i]);
 
 	/*
 	 * The reschedule interrupt slowly changes it's functionality,
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/irq.h linux/arch/i386/kernel/irq.h
--- v2.1.89/linux/arch/i386/kernel/irq.h	Tue Mar 10 10:03:30 1998
+++ linux/arch/i386/kernel/irq.h	Thu Mar 12 10:44:00 1998
@@ -2,13 +2,12 @@
 #define __irq_h
 
 /*
- * Various low-level irq details needed by irq.c and smp.c
+ * Various low-level irq details needed by irq.c, process.c,
+ * time.c, io_apic.c and smp.c
  *
  * Interrupt entry/exit code at both C and assembly level
  */
 
-#define IO_APIC_GATE_OFFSET 0x51
-
 void mask_irq(unsigned int irq);
 void unmask_irq(unsigned int irq);
 void enable_IO_APIC_irq (unsigned int irq);
@@ -19,9 +18,16 @@
 void init_IO_APIC_traps(void);
 int IO_APIC_get_PCI_irq_vector (int bus, int slot, int fn);
 void make_8259A_irq (unsigned int irq);
+void send_IPI (int dest, int vector);
+void init_pic_mode (void);
 
 extern unsigned int io_apic_irqs;
 
+extern inline int IO_APIC_VECTOR (int irq)
+{
+	return (0x51+(irq<<3));
+}
+
 #define MAX_IRQ_SOURCES 128
 #define MAX_MP_BUSSES 32
 enum mp_bustype {
@@ -37,6 +43,7 @@
 					* Protects both the 8259 and the
 					* IO-APIC
 					*/
+
 
 #ifdef __SMP__
 
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- v2.1.89/linux/arch/i386/kernel/process.c	Sun Dec 21 17:27:18 1997
+++ linux/arch/i386/kernel/process.c	Sun Mar 15 10:25:42 1998
@@ -44,6 +44,7 @@
 #ifdef CONFIG_MATH_EMULATION
 #include <asm/math_emu.h>
 #endif
+#include "irq.h"
 
 #ifdef __SMP__
 asmlinkage void ret_from_smpfork(void) __asm__("ret_from_smpfork");
@@ -280,6 +281,12 @@
 
 void machine_restart(char * __unused)
 {
+#if __SMP__
+	/*
+	 * turn off the IO-APIC, so we can do a clean reboot
+	 */
+	init_pic_mode();
+#endif
 
 	if(!reboot_thru_bios) {
 		/* rebooting needs to touch the page at absolute addr 0 */
@@ -314,10 +321,10 @@
 
 	/* Remap the kernel at virtual address zero, as well as offset zero
 	   from the kernel segment.  This assumes the kernel segment starts at
-	   virtual address 0xc0000000. */
+	   virtual address PAGE_OFFSET. */
 
-	memcpy (swapper_pg_dir, swapper_pg_dir + 768,
-		sizeof (swapper_pg_dir [0]) * 256);
+	memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
+		sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
 
 	/* Make sure the first page is mapped to the start of physical memory.
 	   It is normally not mapped, to trap kernel NULL pointer dereferences. */
@@ -473,7 +480,6 @@
 int copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
 	struct task_struct * p, struct pt_regs * regs)
 {
-	int i;
 	struct pt_regs * childregs;
 
 	p->tss.tr = _TSS(nr);
@@ -510,9 +516,13 @@
 		set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,p->ldt, 512);
 	else
 		set_ldt_desc(gdt+(nr<<1)+FIRST_LDT_ENTRY,&default_ldt, 1);
-	p->tss.bitmap = offsetof(struct thread_struct,io_bitmap);
-	for (i = 0; i < IO_BITMAP_SIZE+1 ; i++) /* IO bitmap is actually SIZE+1 */
-		p->tss.io_bitmap[i] = ~0;
+	/*
+	 * a bitmap offset pointing outside of the TSS limit causes a nicely
+	 * controllable SIGSEGV. The first sys_ioperm() call sets up the
+	 * bitmap properly.
+	 */
+	p->tss.bitmap = sizeof(struct thread_struct);
+
 	if (last_task_used_math == current)
 		__asm__("clts ; fnsave %0 ; frstor %0":"=m" (p->tss.i387));
 
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.1.89/linux/arch/i386/kernel/ptrace.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/i386/kernel/ptrace.c	Wed Mar 11 16:07:20 1998
@@ -371,7 +371,10 @@
 	if (pid == 1)		/* you may not mess with init */
 		goto out;
 	ret = -ESRCH;
-	if (!(child = find_task_by_pid(pid)))
+	read_lock(&tasklist_lock);
+	child = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!!! */
+	if (!child)
 		goto out;
 	ret = -EPERM;
 	if (request == PTRACE_ATTACH) {
@@ -390,9 +393,13 @@
 			goto out;
 		child->flags |= PF_PTRACED;
 		if (child->p_pptr != current) {
+			unsigned long flags;
+
+			write_lock_irqsave(&tasklist_lock, flags);
 			REMOVE_LINKS(child);
 			child->p_pptr = current;
 			SET_LINKS(child);
+			write_unlock_irqrestore(&tasklist_lock, flags);
 		}
 		send_sig(SIGSTOP, child, 1);
 		ret = 0;
@@ -545,6 +552,7 @@
 		}
 
 		case PTRACE_DETACH: { /* detach a process that was attached. */
+			unsigned long flags;
 			long tmp;
 
 			ret = -EIO;
@@ -553,9 +561,11 @@
 			child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 			wake_up_process(child);
 			child->exit_code = data;
+			write_lock_irqsave(&tasklist_lock, flags);
 			REMOVE_LINKS(child);
 			child->p_pptr = child->p_opptr;
 			SET_LINKS(child);
+			write_unlock_irqrestore(&tasklist_lock, flags);
 			/* make sure the single step bit is not set. */
 			tmp = get_stack_long(child, EFL_OFFSET) & ~TRAP_FLAG;
 			put_stack_long(child, EFL_OFFSET, tmp);
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v2.1.89/linux/arch/i386/kernel/smp.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/i386/kernel/smp.c	Wed Mar 11 15:09:25 1998
@@ -1108,23 +1108,59 @@
 	setup_IO_APIC();
 }
 
+
+void send_IPI (int dest, int vector)
+{
+	unsigned long cfg;
+	unsigned long flags;
+
+	__save_flags(flags);
+	__cli();
+
+	/*
+	 * prepare target chip field
+	 */
+
+	cfg = apic_read(APIC_ICR2) & 0x00FFFFFF;
+	apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(dest));
+
+	cfg = apic_read(APIC_ICR);
+	cfg &= ~0xFDFFF;
+	cfg |= APIC_DEST_FIELD|APIC_DEST_DM_FIXED|vector;
+	cfg |= dest;
+	
+	/*
+	 * Send the IPI. The write to APIC_ICR fires this off.
+	 */
+	
+	apic_write(APIC_ICR, cfg);
+	__restore_flags(flags);
+}
+
+void funny (void)
+{
+	send_IPI(APIC_DEST_ALLBUT,0x30 /*IO_APIC_VECTOR(11)*/);
+	for(;;)__cli();
+}
+
 /*
- *	A non wait message cannot pass data or cpu source info. This current setup
- *	is only safe because the kernel lock owner is the only person who can send a message.
+ * A non wait message cannot pass data or cpu source info. This current setup
+ * is only safe because the kernel lock owner is the only person who can send
+ * a message.
  *
- *	Wrapping this whole block in a spinlock is not the safe answer either. A processor may
- *	get stuck with irq's off waiting to send a message and thus not replying to the person
- *	spinning for a reply....
+ * Wrapping this whole block in a spinlock is not the safe answer either. A
+ * processor may get stuck with irq's off waiting to send a message and thus
+ * not replying to the person spinning for a reply....
  *
- *	In the end flush tlb ought to be the NMI and a very very short function (to avoid the old
- *	IDE disk problems), and other messages sent with IRQ's enabled in a civilised fashion. That
- *	will also boost performance.
+ * In the end flush tlb ought to be the NMI and a very very short function
+ * (to avoid the old IDE disk problems), and other messages sent with IRQ's
+ * enabled in a civilised fashion. That will also boost performance.
  */
 
 void smp_message_pass(int target, int msg, unsigned long data, int wait)
 {
-	unsigned long flags;
 	unsigned long cfg;
+	unsigned long dest = 0;
 	unsigned long target_map;
 	int p=smp_processor_id();
 	int irq;
@@ -1166,11 +1202,11 @@
 	}
 
 	/*
-	 *	Sanity check we don't re-enter this across CPU's. Only the kernel
-	 *	lock holder may send messages. For a STOP_CPU we are bringing the
-	 *	entire box to the fastest halt we can.. A reschedule carries
-	 *	no data and can occur during a flush.. guess what panic
-	 *	I got to notice this bug...
+	 * Sanity check we don't re-enter this across CPU's. Only the kernel
+	 * lock holder may send messages. For a STOP_CPU we are bringing the
+	 * entire box to the fastest halt we can.. A reschedule carries
+	 * no data and can occur during a flush.. guess what panic
+	 * I got to notice this bug...
 	 */
 	
 	/*
@@ -1183,11 +1219,11 @@
 		p, msg, target);*/
 
 	/*
-	 *	Wait for the APIC to become ready - this should never occur. Its
-	 *	a debugging check really.
+	 * Wait for the APIC to become ready - this should never occur. Its
+	 * a debugging check really.
 	 */
 	
-	while(ct<1000)
+	while (ct<1000)
 	{
 		cfg=apic_read(APIC_ICR);
 		if(!(cfg&(1<<12)))
@@ -1204,49 +1240,32 @@
 		printk("CPU #%d: previous IPI still not cleared after 10mS\n", p);
 
 	/*
-	 *	Program the APIC to deliver the IPI
-	 */
-
-	__save_flags(flags);
-	__cli();
-	cfg=apic_read(APIC_ICR2);
-	cfg&=0x00FFFFFF;
-	apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(target));			/* Target chip     		*/
-	cfg=apic_read(APIC_ICR);
-	cfg&=~0xFDFFF;								/* Clear bits 			*/
-	cfg|=APIC_DEST_FIELD|APIC_DEST_DM_FIXED|irq;				/* Send an IRQ 13		*/
-
-	/*
 	 *	Set the target requirement
 	 */
 	
 	if(target==MSG_ALL_BUT_SELF)
 	{
-		cfg|=APIC_DEST_ALLBUT;
+		dest=APIC_DEST_ALLBUT;
 		target_map=cpu_present_map;
 		cpu_callin_map[0]=(1<<p);
 	}
 	else if(target==MSG_ALL)
 	{
-		cfg|=APIC_DEST_ALLINC;
+		dest=APIC_DEST_ALLINC;
 		target_map=cpu_present_map;
 		cpu_callin_map[0]=0;
 	}
 	else
-	{
-		target_map=(1<<target);
-		cpu_callin_map[0]=0;
-	}
+		panic("huh?");
 
 	/*
-	 *	Send the IPI. The write to APIC_ICR fires this off.
+	 * Program the APIC to deliver the IPI
 	 */
-	
-	apic_write(APIC_ICR, cfg);
-	__restore_flags(flags);
+
+	send_IPI(dest,irq);
 
 	/*
-	 *	Spin waiting for completion
+	 * Spin waiting for completion
 	 */
 	
 	switch(wait)
@@ -1443,6 +1462,7 @@
 	int cpu = smp_processor_id();
 
 	ack_APIC_irq();
+	for (;;) __cli();
 	/*
 	 * This looks silly, but we actually do need to wait
 	 * for the global interrupt lock.
@@ -1694,7 +1714,9 @@
 	/*
 	 * We ACK the APIC, just in case there is something pending.
 	 */
+
 	ack_APIC_irq ();
+
 
 	restore_flags(flags);
 }
diff -u --recursive --new-file v2.1.89/linux/arch/i386/kernel/trampoline.S linux/arch/i386/kernel/trampoline.S
--- v2.1.89/linux/arch/i386/kernel/trampoline.S	Mon Dec  1 10:34:10 1997
+++ linux/arch/i386/kernel/trampoline.S	Thu Mar 12 10:33:21 1998
@@ -28,6 +28,7 @@
 
 #include <linux/linkage.h>
 #include <asm/segment.h>
+#include <asm/page.h>
 
 .data
 
@@ -62,7 +63,7 @@
 
 gdt_48:
 	.word	0x0800			# gdt limit = 2048, 256 GDT entries
-	.long	gdt_table-0xc0000000	# gdt base = gdt (first SMP CPU)
+	.long	gdt_table-__PAGE_OFFSET	# gdt base = gdt (first SMP CPU)
 
 .globl SYMBOL_NAME(trampoline_end)
 SYMBOL_NAME_LABEL(trampoline_end)
diff -u --recursive --new-file v2.1.89/linux/arch/i386/mm/init.c linux/arch/i386/mm/init.c
--- v2.1.89/linux/arch/i386/mm/init.c	Mon Jan 12 14:33:20 1998
+++ linux/arch/i386/mm/init.c	Wed Mar 11 15:10:39 1998
@@ -204,8 +204,8 @@
 	/* unmap the original low memory mappings */
 	pgd_val(pg_dir[0]) = 0;
 
-	/* Map whole memory from 0xC0000000 */
-	pg_dir += 768;
+	/* Map whole memory from PAGE_OFFSET */
+	pg_dir += USER_PGD_PTRS;
 	while (address < end_mem) {
 		/*
 		 * If we're running on a Pentium CPU, we can use the 4MB
diff -u --recursive --new-file v2.1.89/linux/arch/i386/vmlinux.lds linux/arch/i386/vmlinux.lds
--- v2.1.89/linux/arch/i386/vmlinux.lds	Tue May 13 22:41:01 1997
+++ linux/arch/i386/vmlinux.lds	Wed Mar 11 15:10:39 1998
@@ -1,12 +1,13 @@
 /* ld script to make i386 Linux kernel
  * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
  */
+INCLUDE arch/i386/.kernel_offset.lds
 OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
 OUTPUT_ARCH(i386)
 ENTRY(_start)
 SECTIONS
 {
-  . = 0xc0100000;
+  . = __kernel_offset__ + 0x100000;
   _text = .;			/* Text and read-only data */
   .text : {
 	*(.text)
diff -u --recursive --new-file v2.1.89/linux/arch/m68k/amiga/amiga_ksyms.c linux/arch/m68k/amiga/amiga_ksyms.c
--- v2.1.89/linux/arch/m68k/amiga/amiga_ksyms.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/m68k/amiga/amiga_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -2,6 +2,7 @@
 #include <linux/types.h>
 #include <linux/zorro.h>
 #include <asm/amigahw.h>
+#include <linux/pci.h>
 
 extern volatile u_short amiga_audio_min_period;
 extern u_short amiga_audio_period;
@@ -24,3 +25,7 @@
 EXPORT_SYMBOL(zorro_config_board);
 EXPORT_SYMBOL(zorro_unconfig_board);
 EXPORT_SYMBOL(zorro_unused_z2ram);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/m68k/atari/atari_ksyms.c linux/arch/m68k/atari/atari_ksyms.c
--- v2.1.89/linux/arch/m68k/atari/atari_ksyms.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/m68k/atari/atari_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -1,4 +1,6 @@
 #include <linux/module.h>
+#include <linux/pci.h>
+
 #include <asm/ptrace.h>
 #include <asm/traps.h>
 #include <asm/atarihw.h>
@@ -40,3 +42,7 @@
 EXPORT_SYMBOL(ikbd_mouse_disable);
 
 EXPORT_SYMBOL(atari_microwire_cmd);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v2.1.89/linux/arch/m68k/config.in	Tue Feb 17 13:12:44 1998
+++ linux/arch/m68k/config.in	Tue Mar 10 14:43:13 1998
@@ -14,7 +14,7 @@
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
diff -u --recursive --new-file v2.1.89/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v2.1.89/linux/arch/m68k/defconfig	Tue Mar 10 10:03:30 1998
+++ linux/arch/m68k/defconfig	Tue Mar 10 14:43:13 1998
@@ -12,7 +12,7 @@
 #
 # CONFIG_MODULES is not set
 # CONFIG_MODVERSIONS is not set
-# CONFIG_KERNELD is not set
+# CONFIG_KMOD is not set
 
 #
 # Platform-dependent setup
diff -u --recursive --new-file v2.1.89/linux/arch/m68k/kernel/m68k_ksyms.c linux/arch/m68k/kernel/m68k_ksyms.c
--- v2.1.89/linux/arch/m68k/kernel/m68k_ksyms.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/m68k/kernel/m68k_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -7,6 +7,7 @@
 #include <linux/elfcore.h>
 #include <linux/in6.h>
 #include <linux/interrupt.h>
+#include <linux/pci.h>
 
 #include <asm/setup.h>
 #include <asm/machdep.h>
@@ -58,3 +59,7 @@
 EXPORT_SYMBOL_NOVERS(__down_failed);
 EXPORT_SYMBOL_NOVERS(__down_failed_interruptible);
 EXPORT_SYMBOL_NOVERS(__up_wakeup);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/m68k/kernel/process.c linux/arch/m68k/kernel/process.c
--- v2.1.89/linux/arch/m68k/kernel/process.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/kernel/process.c	Tue Mar 17 20:56:44 1998
@@ -40,6 +40,7 @@
  */
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.1.89/linux/arch/m68k/mac/ksyms.c linux/arch/m68k/mac/ksyms.c
--- v2.1.89/linux/arch/m68k/mac/ksyms.c	Tue Feb 17 13:12:45 1998
+++ linux/arch/m68k/mac/ksyms.c	Tue Mar 17 21:15:40 1998
@@ -1,7 +1,12 @@
 #include <linux/module.h>
+#include <linux/pci.h>
 #include <asm/ptrace.h>
 #include <asm/traps.h>
 /* Hook for mouse driver */
 extern void (*mac_mouse_interrupt_hook) (char *);
 
 EXPORT_SYMBOL(mac_mouse_interrupt_hook);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/mips/config.in linux/arch/mips/config.in
--- v2.1.89/linux/arch/mips/config.in	Wed Dec 10 10:31:09 1997
+++ linux/arch/mips/config.in	Tue Mar 10 14:43:13 1998
@@ -109,7 +109,7 @@
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 
 source drivers/block/Config.in
diff -u --recursive --new-file v2.1.89/linux/arch/mips/kernel/init_task.c linux/arch/mips/kernel/init_task.c
--- v2.1.89/linux/arch/mips/kernel/init_task.c	Thu Jun 26 12:33:37 1997
+++ linux/arch/mips/kernel/init_task.c	Tue Mar 10 13:28:59 1998
@@ -5,6 +5,7 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct files * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.1.89/linux/arch/mips/kernel/mips_ksyms.c linux/arch/mips/kernel/mips_ksyms.c
--- v2.1.89/linux/arch/mips/kernel/mips_ksyms.c	Wed Dec 10 10:31:09 1997
+++ linux/arch/mips/kernel/mips_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -15,6 +15,7 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/in6.h>
+#include <linux/pci.h>
 
 #include <asm/checksum.h>
 #include <asm/dma.h>
@@ -96,4 +97,8 @@
 EXPORT_SYMBOL(__compute_return_epc);
 EXPORT_SYMBOL(register_fpe);
 EXPORT_SYMBOL(unregister_fpe);
+#endif
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
 #endif
diff -u --recursive --new-file v2.1.89/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v2.1.89/linux/arch/ppc/config.in	Wed Feb  4 11:36:00 1998
+++ linux/arch/ppc/config.in	Tue Mar 10 14:43:13 1998
@@ -32,7 +32,7 @@
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 
 define_bool CONFIG_PCI y
diff -u --recursive --new-file v2.1.89/linux/arch/ppc/defconfig linux/arch/ppc/defconfig
--- v2.1.89/linux/arch/ppc/defconfig	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/defconfig	Tue Mar 10 14:43:13 1998
@@ -19,7 +19,7 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
 CONFIG_PCI=y
 # CONFIG_PCI_OPTIMIZE is not set
 CONFIG_PCI_OLD_PROC=y
diff -u --recursive --new-file v2.1.89/linux/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- v2.1.89/linux/arch/ppc/kernel/ppc_ksyms.c	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -6,6 +6,7 @@
 #include <linux/string.h>
 #include <linux/bios32.h>
 #include <linux/interrupt.h>
+#include <linux/pci.h>
 
 #include <asm/semaphore.h>
 #include <asm/processor.h>
@@ -147,3 +148,7 @@
 EXPORT_SYMBOL(pci_io_base);
 EXPORT_SYMBOL(pci_device_loc);
 EXPORT_SYMBOL(note_scsi_host);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/ppc/kernel/process.c linux/arch/ppc/kernel/process.c
--- v2.1.89/linux/arch/ppc/kernel/process.c	Mon Feb 23 18:12:02 1998
+++ linux/arch/ppc/kernel/process.c	Tue Mar 17 20:56:44 1998
@@ -64,6 +64,7 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct file * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 
diff -u --recursive --new-file v2.1.89/linux/arch/ppc/pmac_defconfig linux/arch/ppc/pmac_defconfig
--- v2.1.89/linux/arch/ppc/pmac_defconfig	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/pmac_defconfig	Wed Mar 11 15:13:49 1998
@@ -19,7 +19,7 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
 # CONFIG_MODVERSIONS is not set
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
 CONFIG_PCI=y
 CONFIG_NET=y
 CONFIG_SYSCTL=y
diff -u --recursive --new-file v2.1.89/linux/arch/ppc/prep_defconfig linux/arch/ppc/prep_defconfig
--- v2.1.89/linux/arch/ppc/prep_defconfig	Mon Jan 12 15:18:13 1998
+++ linux/arch/ppc/prep_defconfig	Wed Mar 11 15:13:49 1998
@@ -19,7 +19,7 @@
 CONFIG_EXPERIMENTAL=y
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
 CONFIG_PCI=y
 # CONFIG_PCI_OPTIMIZE is not set
 CONFIG_NET=y
diff -u --recursive --new-file v2.1.89/linux/arch/sparc/config.in linux/arch/sparc/config.in
--- v2.1.89/linux/arch/sparc/config.in	Thu Feb 12 20:56:04 1998
+++ linux/arch/sparc/config.in	Tue Mar 10 14:43:13 1998
@@ -14,7 +14,7 @@
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
diff -u --recursive --new-file v2.1.89/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
--- v2.1.89/linux/arch/sparc/defconfig	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/defconfig	Tue Mar 10 14:43:13 1998
@@ -12,7 +12,7 @@
 #
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
 
 #
 # General setup
diff -u --recursive --new-file v2.1.89/linux/arch/sparc/kernel/init_task.c linux/arch/sparc/kernel/init_task.c
--- v2.1.89/linux/arch/sparc/kernel/init_task.c	Tue May 13 22:41:03 1997
+++ linux/arch/sparc/kernel/init_task.c	Tue Mar 10 13:28:59 1998
@@ -6,6 +6,7 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct files * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.1.89/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c
--- v2.1.89/linux/arch/sparc/kernel/ptrace.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/ptrace.c	Wed Mar 11 15:44:14 1998
@@ -550,9 +550,12 @@
 		}
 		child->flags |= PF_PTRACED;
 		if(child->p_pptr != current) {
+			unsigned long flags;
+			write_lock_irqsave(&tasklist_lock, flags);
 			REMOVE_LINKS(child);
 			child->p_pptr = current;
 			SET_LINKS(child);
+			write_unlock_irqrestore(&tasklist_lock, flags);
 		}
 		send_sig(SIGSTOP, child, 1);
 		pt_succ_return(regs, 0);
@@ -851,6 +854,7 @@
 	}
 
 	case PTRACE_SUNDETACH: { /* detach a process that was attached. */
+		unsigned long flags;
 		if ((unsigned long) data > _NSIG) {
 			pt_error_return(regs, EIO);
 			goto out;
@@ -858,9 +862,11 @@
 		child->flags &= ~(PF_PTRACED|PF_TRACESYS);
 		wake_up_process(child);
 		child->exit_code = data;
+		write_lock_irqsave(&tasklist_lock, flags);
 		REMOVE_LINKS(child);
 		child->p_pptr = child->p_opptr;
 		SET_LINKS(child);
+		write_unlock_irqrestore(&tasklist_lock, flags);
 		pt_succ_return(regs, 0);
 		goto out;
 	}
diff -u --recursive --new-file v2.1.89/linux/arch/sparc/kernel/sparc_ksyms.c linux/arch/sparc/kernel/sparc_ksyms.c
--- v2.1.89/linux/arch/sparc/kernel/sparc_ksyms.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/kernel/sparc_ksyms.c	Tue Mar 17 21:15:40 1998
@@ -15,6 +15,7 @@
 #include <linux/string.h>
 #include <linux/interrupt.h>
 #include <linux/in6.h>
+#include <linux/pci.h>
 
 #include <asm/oplib.h>
 #include <asm/delay.h>
@@ -271,3 +272,7 @@
 EXPORT_SYMBOL_DOT(umul);
 EXPORT_SYMBOL_DOT(div);
 EXPORT_SYMBOL_DOT(udiv);
+
+#if CONFIG_PCI
+EXPORT_SYMBOL(pci_devices);
+#endif
diff -u --recursive --new-file v2.1.89/linux/arch/sparc/mm/init.c linux/arch/sparc/mm/init.c
--- v2.1.89/linux/arch/sparc/mm/init.c	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc/mm/init.c	Thu Mar 12 10:56:00 1998
@@ -17,6 +17,7 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/swap.h>
+#include <linux/swapctl.h>
 #ifdef CONFIG_BLK_DEV_INITRD
 #include <linux/blk.h>
 #endif
@@ -172,9 +173,6 @@
 
 struct cache_palias *sparc_aliases;
 
-extern int min_free_pages;
-extern int free_pages_low;
-extern int free_pages_high;
 extern void srmmu_frob_mem_map(unsigned long);
 
 int physmem_mapped_contig = 1;
@@ -265,11 +263,11 @@
 	       initpages << (PAGE_SHIFT-10),
 	       PAGE_OFFSET, end_mem);
 
-	min_free_pages = nr_free_pages >> 7;
-	if(min_free_pages < 16)
-		min_free_pages = 16;
-	free_pages_low = min_free_pages + (min_free_pages >> 1);
-	free_pages_high = min_free_pages + min_free_pages;
+	freepages.min = nr_free_pages >> 7;
+	if(freepages.min < 16)
+		freepages.min = 16;
+	freepages.low = freepages.min + (freepages.min >> 1);
+	freepages.high = freepages.min + freepages.min;
 }
 
 void free_initmem (void)
diff -u --recursive --new-file v2.1.89/linux/arch/sparc64/config.in linux/arch/sparc64/config.in
--- v2.1.89/linux/arch/sparc64/config.in	Tue Mar 10 10:03:30 1998
+++ linux/arch/sparc64/config.in	Tue Mar 10 14:43:13 1998
@@ -14,7 +14,7 @@
 bool 'Enable loadable module support' CONFIG_MODULES
 if [ "$CONFIG_MODULES" = "y" ]; then
   bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS
-  bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD
+  bool 'Kernel module loader' CONFIG_KMOD
 fi
 endmenu
 
diff -u --recursive --new-file v2.1.89/linux/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- v2.1.89/linux/arch/sparc64/defconfig	Mon Jan 12 15:15:43 1998
+++ linux/arch/sparc64/defconfig	Wed Mar 11 15:13:49 1998
@@ -12,7 +12,7 @@
 #
 CONFIG_MODULES=y
 CONFIG_MODVERSIONS=y
-CONFIG_KERNELD=y
+CONFIG_KMOD=y
 
 #
 # General setup
diff -u --recursive --new-file v2.1.89/linux/arch/sparc64/kernel/init_task.c linux/arch/sparc64/kernel/init_task.c
--- v2.1.89/linux/arch/sparc64/kernel/init_task.c	Thu May 15 16:48:02 1997
+++ linux/arch/sparc64/kernel/init_task.c	Tue Mar 10 13:28:59 1998
@@ -6,6 +6,7 @@
 
 static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
+static struct files * init_fd_array[NR_OPEN] = { NULL, };
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
 struct mm_struct init_mm = INIT_MM;
diff -u --recursive --new-file v2.1.89/linux/arch/sparc64/kernel/traps.c linux/arch/sparc64/kernel/traps.c
--- v2.1.89/linux/arch/sparc64/kernel/traps.c	Mon Jan 12 15:15:44 1998
+++ linux/arch/sparc64/kernel/traps.c	Thu Mar 12 10:33:21 1998
@@ -26,8 +26,8 @@
 #include <asm/uaccess.h>
 #include <asm/fpumacro.h>
 #include <asm/lsu.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 /* #define SYSCALL_TRACING */
@@ -327,7 +327,7 @@
 	case (2 << 14): /* unfinished_FPop */
 	case (3 << 14): /* unimplemented_FPop */
 #ifdef CONFIG_MATHEMU_MODULE
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 		if (!handle_mathemu)
 			request_module("math-emu");
 #endif
diff -u --recursive --new-file v2.1.89/linux/arch/sparc64/mm/init.c linux/arch/sparc64/mm/init.c
--- v2.1.89/linux/arch/sparc64/mm/init.c	Fri Jan 16 20:34:00 1998
+++ linux/arch/sparc64/mm/init.c	Thu Mar 12 10:56:00 1998
@@ -10,6 +10,7 @@
 #include <linux/init.h>
 #include <linux/blk.h>
 #include <linux/swap.h>
+#include <linux/swapctl.h>
 
 #include <asm/head.h>
 #include <asm/system.h>
@@ -864,10 +865,6 @@
 	return device_scan (PAGE_ALIGN (start_mem));
 }
 
-extern int min_free_pages;
-extern int free_pages_low;
-extern int free_pages_high;
-
 __initfunc(static void taint_real_pages(unsigned long start_mem, unsigned long end_mem))
 {
 	unsigned long addr, tmp2 = 0;
@@ -946,11 +943,11 @@
 	       initpages << (PAGE_SHIFT-10), 
 	       PAGE_OFFSET, end_mem);
 
-	min_free_pages = nr_free_pages >> 7;
-	if(min_free_pages < 16)
-		min_free_pages = 16;
-	free_pages_low = min_free_pages + (min_free_pages >> 1);
-	free_pages_high = min_free_pages + min_free_pages;
+	freepages.low = nr_free_pages >> 7;
+	if(freepages.low < 16)
+		freepages.low = 16;
+	freepages.low = freepages.low + (freepages.low >> 1);
+	freepages.high = freepages.low + freepages.low;
 }
 
 void free_initmem (void)
diff -u --recursive --new-file v2.1.89/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v2.1.89/linux/drivers/block/floppy.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/block/floppy.c	Wed Mar 11 15:25:12 1998
@@ -534,6 +534,8 @@
 static unsigned char current_drive = 0;
 static long current_count_sectors = 0;
 static unsigned char sector_t; /* sector in track */
+static unsigned char in_sector_offset; /* offset within physical sector,
+										* expressed in units of 512 bytes */
 
 #ifndef fd_eject
 #define fd_eject(x) -EINVAL
@@ -2251,7 +2253,7 @@
 /* Interrupt handler evaluating the result of the r/w operation */
 static void rw_interrupt(void)
 {
-	int nr_sectors, ssize, eoc;
+	int nr_sectors, ssize, eoc, heads;
 
 	if (!DRS->first_read_date)
 		DRS->first_read_date = jiffies;
@@ -2263,23 +2265,32 @@
 		eoc = 1;
 	else
 		eoc = 0;
-	nr_sectors = ((R_TRACK-TRACK)*_floppy->head+R_HEAD-HEAD) *
-		_floppy->sect + ((R_SECTOR-SECTOR+eoc) <<  SIZECODE >> 2) -
-		(sector_t % _floppy->sect) % ssize;
+
+	if(COMMAND & 0x80)
+		heads = 2;
+	else
+		heads = 1;
+
+	nr_sectors = (((R_TRACK-TRACK) * heads + 
+				   R_HEAD-HEAD) * SECT_PER_TRACK +
+				  R_SECTOR-SECTOR + eoc) << SIZECODE >> 2;
 
 #ifdef FLOPPY_SANITY_CHECK
-	if (nr_sectors > current_count_sectors + ssize -
-	     (current_count_sectors + sector_t) % ssize +
-	     sector_t % ssize){
+	if (nr_sectors / ssize > 
+		(in_sector_offset + current_count_sectors + ssize - 1)/ssize) {
 		DPRINT("long rw: %x instead of %lx\n",
 			nr_sectors, current_count_sectors);
 		printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
 		printk("rh=%d h=%d\n", R_HEAD, HEAD);
 		printk("rt=%d t=%d\n", R_TRACK, TRACK);
-		printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
-		       sector_t, ssize);
+		printk("heads=%d eoc=%d\n", heads, eoc);
+		printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, 
+			   sector_t, ssize);
+		printk("in_sector_offset=%d\n", in_sector_offset);
 	}
 #endif
+
+	nr_sectors -= in_sector_offset;
 	INFBOUND(nr_sectors,0);
 	SUPBOUND(current_count_sectors, nr_sectors);
 
@@ -2472,7 +2483,7 @@
  * does not work with MT, hence we can only transfer one head at
  * a time
  */
-static int virtualdmabug_workaround() {
+static void virtualdmabug_workaround(void) {
 	int hard_sectors, end_sector;
 	if(CT(COMMAND) == FD_WRITE) {
 		COMMAND &= ~0x80; /* switch off multiple track mode */
@@ -2483,7 +2494,7 @@
 		if(end_sector > SECT_PER_TRACK) {
 			printk("too many sectors %d > %d\n", 
 				   end_sector, SECT_PER_TRACK);
-			return 0;
+			return;
 		}
 #endif
 		SECT_PER_TRACK = end_sector; /* make sure SECT_PER_TRACK points
@@ -2594,7 +2605,8 @@
 		max_sector = _floppy->sect;
 	}
 
-	aligned_sector_t = sector_t - (sector_t % _floppy->sect) % ssize;
+	in_sector_offset = (sector_t % _floppy->sect) % ssize;
+	aligned_sector_t = sector_t - in_sector_offset;
 	max_size = CURRENT->nr_sectors;
 	if ((raw_cmd->track == buffer_track) && 
 	    (current_drive == buffer_drive) &&
@@ -2604,7 +2616,7 @@
 			copy_buffer(1, max_sector, buffer_max);
 			return 1;
 		}
-	} else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
+	} else if (in_sector_offset || CURRENT->nr_sectors < ssize){
 		if (CT(COMMAND) == FD_WRITE){
 			if (sector_t + CURRENT->nr_sectors > ssize &&
 			    sector_t + CURRENT->nr_sectors < ssize + ssize)
@@ -2676,7 +2688,7 @@
 	    sector_t > buffer_max ||
 	    sector_t < buffer_min ||
 	    ((CT(COMMAND) == FD_READ ||
-	      (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize))&&
+	      (!in_sector_offset && CURRENT->nr_sectors >= ssize))&&
 	     max_sector > 2 * max_buffer_sectors + buffer_min &&
 	     max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
 	    /* not enough space */){
@@ -2693,7 +2705,7 @@
 		 * is either aligned or the data already in the buffer
 		 * (buffer will be overwritten) */
 #ifdef FLOPPY_SANITY_CHECK
-		if (sector_t != aligned_sector_t && buffer_track == -1)
+		if (in_sector_offset && buffer_track == -1)
 			DPRINT("internal error offset !=0 on write\n");
 #endif
 		buffer_track = raw_cmd->track;
@@ -2704,7 +2716,7 @@
 			      2*max_buffer_sectors+buffer_min-aligned_sector_t);
 
 	/* round up current_count_sectors to get dma xfer size */
-	raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
+	raw_cmd->length = in_sector_offset+current_count_sectors;
 	raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
 	raw_cmd->length <<= 9;
 #ifdef FLOPPY_SANITY_CHECK
diff -u --recursive --new-file v2.1.89/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.1.89/linux/drivers/block/ide-cd.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/block/ide-cd.c	Tue Mar 17 21:39:11 1998
@@ -26,7 +26,6 @@
  *    (If you are using a cd changer, you may get errors in the kernel
  *     logs that are completly expected.  Don't complain to me about this,
  *     unless you have a patch to fix it.  I am working on it...)
- * -Implement ide_cdrom_select_speed using the generic cdrom interface
  * -Fix ide_cdrom_reset so that it works (it does nothing right now)
  * -Query the drive to find what features are available before trying to
  *   use them (like trying to close the tray in drives that can't).
@@ -189,10 +188,19 @@
  *                         malloc'ed but never free'd when closing the device.
  *                     -- Cleaned up the global namespace a bit by making more
  *                         functions static that should already have been.
+ * 4.11  Mar 12, 1998  -- Added support for the CDROM_SELECT_SPEED ioctl
+ *                         based on a patch for 2.0.33 by Jelle Foks 
+ *                         <jelle@scintilla.utwente.nl>, a patch for 2.0.33
+ *                         by Toni Giorgino <toni@pcape2.pi.infn.it>, the SCSI
+ *                         version, and my own efforts.  -erik
+ *                     -- Fixed a stupid bug which egcs was kind enough to
+ *                         inform me of where "Illegal mode for this track"
+ *                         was never returned due to a comparison on data
+ *                         types of limited range.
  *
  *************************************************************************/
 
-#define IDECD_VERSION "4.10"
+#define IDECD_VERSION "4.11"
 
 #include <linux/module.h>
 #include <linux/types.h>
@@ -271,8 +279,7 @@
 
 		printk ("  Error code: 0x%02x\n", reqbuf->error_code);
 
-		if (reqbuf->sense_key >= 0 &&
-		    reqbuf->sense_key < ARY_LEN (sense_key_texts))
+		if ( reqbuf->sense_key < ARY_LEN (sense_key_texts))
 			s = sense_key_texts[reqbuf->sense_key];
 		else
 			s = "(bad sense key)";
@@ -285,7 +292,7 @@
 			s = buf;
 		} else {
 			int lo, hi;
-			int key = (reqbuf->asc << 8);
+			unsigned short key = (reqbuf->asc << 8);
 			if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
 				key |= reqbuf->ascq;
 
@@ -496,7 +503,7 @@
 			} else if (sense_key == UNIT_ATTENTION) {
 				/* Check for media change. */
 				cdrom_saw_media_change (drive);
-				printk ("%s: media changed\n", drive->name);
+				/*printk("%s: media changed\n",drive->name);*/
 				return 0;
 			} else {
 				/* Otherwise, print an error. */
@@ -1518,7 +1525,6 @@
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
-
 static int
 cdrom_read_capacity (ide_drive_t *drive, unsigned *capacity,
 		     struct atapi_request_sense *reqbuf)
@@ -1721,7 +1727,6 @@
 	return cdrom_queue_packet_command (drive, &pc);
 }
 
-
 static int
 cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen,
 		   struct atapi_request_sense *reqbuf)
@@ -1742,6 +1747,43 @@
 }
 
 
+/* Note that this takes speed in kbytes/second, so don't try requesting
+   silly speeds like 2 here. Common speeds include:
+     176 kbytes/second  --  1x
+     353 kbytes/second  --  2x
+     387 kbytes/second  --  2.2x
+     528 kbytes/second  --  3x
+     706 kbytes/second  --  4x
+     1400 kbytes/second --  8x
+     2800 kbytes/second --  16x
+   ATAPI drives are free to select the speed you request or any slower
+   rate :-( Requesting too fast a speed will _not_ produce an error. */
+static int
+cdrom_select_speed (ide_drive_t *drive, int speed,
+                   struct atapi_request_sense *reqbuf)
+{
+	struct packet_command pc;
+	memset (&pc, 0, sizeof (pc));
+	pc.sense_data = reqbuf;
+
+	if (speed < 1)
+	    speed = 0xffff; /* set to max */
+	else
+	    speed *= 177;   /* Nx to kbytes/s */
+
+	pc.c[0] = SET_CD_SPEED;
+	/* Read Drive speed in kbytes/second MSB */
+	pc.c[2] = (speed >> 8) & 0xff;	
+	/* Read Drive speed in kbytes/second LSB */
+	pc.c[3] = speed & 0xff;
+	/* Write Drive speed in kbytes/second MSB */
+	//pc.c[4] = (speed >> 8) & 0xff;	
+	/* Write Drive speed in kbytes/second LSB */
+	//pc.c[5] = speed & 0xff;
+
+	return cdrom_queue_packet_command (drive, &pc);
+}
+
 static int
 cdrom_play_lba_range_1 (ide_drive_t *drive, int lba_start, int lba_end,
 			    struct atapi_request_sense *reqbuf)
@@ -2429,6 +2471,44 @@
 	return cdrom_lockdoor (drive, lock, NULL);
 }
 
+static
+int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed)
+{
+        int stat, attempts = 3;
+        struct {
+                char pad[8];
+                struct atapi_capabilities_page cap;
+        } buf;
+	ide_drive_t *drive = (ide_drive_t*) cdi->handle;
+	struct atapi_request_sense reqbuf;
+	stat=cdrom_select_speed (drive, speed, &reqbuf);
+	if (stat<0)
+		return stat;
+
+	/* Now that that is done, update the speed fields */
+        do {    /* we seem to get stat=0x01,err=0x00 the first time (??) */
+                if (attempts-- <= 0)
+                        return 0;
+                stat = cdrom_mode_sense (drive, PAGE_CAPABILITIES, 0,
+                                        (char *)&buf, sizeof (buf), NULL);
+        } while (stat);
+
+        /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
+        if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
+                CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+                CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+        } else {
+                CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
+                CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+        }
+        cdi->speed = CDROM_STATE_FLAGS (drive)->current_speed;
+        return 0;
+}
+
 
 static
 int ide_cdrom_select_disc (struct cdrom_device_info *cdi, int slot)
@@ -2668,14 +2748,14 @@
 	ide_cdrom_check_media_change_real, /* media_changed */
 	ide_cdrom_tray_move,    /* tray_move */
 	ide_cdrom_lock_door,    /* lock_door */
-	NULL, /* select_speed */
+	ide_cdrom_select_speed, /* select_speed */
 	ide_cdrom_select_disc, /* select_disc */
 	ide_cdrom_get_last_session, /* get_last_session */
 	ide_cdrom_get_mcn, /* get_mcn */
 	ide_cdrom_reset, /* reset */
 	ide_cdrom_audio_ioctl, /* audio_ioctl */
 	ide_cdrom_dev_ioctl,   /* dev_ioctl */
-	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK
+	CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | CDC_SELECT_SPEED
 	| CDC_SELECT_DISC | CDC_MULTI_SESSION | CDC_MCN
 	| CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET 
 	| CDC_IOCTLS | CDC_DRIVE_STATUS,  /* capability */
@@ -2691,7 +2771,7 @@
 	devinfo->dev = MKDEV (HWIF(drive)->major, minor);
 	devinfo->ops = &ide_cdrom_dops;
 	devinfo->mask = 0;
-	*(int *)&devinfo->speed = CDROM_CONFIG_FLAGS (drive)->max_speed;
+	*(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed;
 	*(int *)&devinfo->capacity = nslots;
 	devinfo->handle = (void *) drive;
 	strcpy(devinfo->name, drive->name);
@@ -2750,11 +2830,15 @@
 
 	/* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */
 	if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) {
-		CDROM_STATE_FLAGS (drive)->current_speed  = (((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
-		CDROM_CONFIG_FLAGS (drive)->max_speed = (((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
+		CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(((unsigned int)buf.cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(((unsigned int)buf.cap.maxspeed) + (176/2)) / 176;
 	} else {
-		CDROM_STATE_FLAGS (drive)->current_speed  = (ntohs(buf.cap.curspeed) + (176/2)) / 176;
-		CDROM_CONFIG_FLAGS (drive)->max_speed = (ntohs(buf.cap.maxspeed) + (176/2)) / 176;
+		CDROM_STATE_FLAGS (drive)->current_speed  = 
+			(ntohs(buf.cap.curspeed) + (176/2)) / 176;
+		CDROM_CONFIG_FLAGS (drive)->max_speed = 
+			(ntohs(buf.cap.maxspeed) + (176/2)) / 176;
 	}
 
         printk ("%s: ATAPI %dX CDROM", 
diff -u --recursive --new-file v2.1.89/linux/drivers/block/ide-cd.h linux/drivers/block/ide-cd.h
--- v2.1.89/linux/drivers/block/ide-cd.h	Wed Dec 17 11:11:51 1997
+++ linux/drivers/block/ide-cd.h	Tue Mar 17 21:39:11 1998
@@ -71,7 +71,7 @@
 #define MODE_SENSE_10           0x5a
 #define MODE_SELECT_10          0x55
 #define READ_CD                 0xbe
-
+#define SET_CD_SPEED            0xbb
 #define LOAD_UNLOAD             0xa6
 #define MECHANISM_STATUS        0xbd
 
@@ -431,7 +431,7 @@
    with additions from Tables 141 and 142 of the ATAPI 2.6 draft standard. */
 
 struct {
-	short asc_ascq;
+	unsigned short asc_ascq;
 	char *text;
 } sense_data_texts[] = {
 	{ 0x0000, "No additional sense information" },
diff -u --recursive --new-file v2.1.89/linux/drivers/block/ide-disk.c linux/drivers/block/ide-disk.c
--- v2.1.89/linux/drivers/block/ide-disk.c	Mon Feb 23 18:12:04 1998
+++ linux/drivers/block/ide-disk.c	Tue Mar 10 10:48:27 1998
@@ -16,9 +16,11 @@
  * Version 1.02		remove ", LBA" from drive identification msgs
  * Version 1.03		fix display of id->buf_size for big-endian
  * Version 1.04		add /proc configurable settings and S.M.A.R.T support
+ * Version 1.05		add capacity support for ATA3 >= 8GB
+ * Version 1.06		get boot-up messages to show full cyl count
  */
 
-#define IDEDISK_VERSION	"1.04"
+#define IDEDISK_VERSION	"1.06"
 
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
@@ -83,6 +85,11 @@
 	unsigned long chs_sects   = id->cyls * id->heads * id->sectors;
 	unsigned long _10_percent = chs_sects / 10;
 
+	/* very large drives (8GB+) may lie about the number of cylinders */
+	if (id->cyls == 16383 && id->heads == 16 && id->sectors == 63 && lba_sects > chs_sects) {
+		id->cyls = lba_sects / (16 * 63); /* correct cyls */
+		return 1;	/* lba_capacity is our only option */
+	}
 	/* perform a rough sanity check on lba_sects:  within 10% is "okay" */
 	if ((lba_sects - chs_sects) < _10_percent)
 		return 1;	/* lba_capacity is good */
@@ -439,6 +446,7 @@
 	/* Determine capacity, and use LBA if the drive properly supports it */
 	if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
 		if (id->lba_capacity >= capacity) {
+			drive->cyl = id->lba_capacity / (drive->head * drive->sect);
 			capacity = id->lba_capacity;
 			drive->select.b.lba = 1;
 		}
@@ -698,6 +706,10 @@
 		drive->head = id->heads;
 		drive->sect = id->sectors;
 	}
+
+	/* calculate drive capacity, and select LBA if possible */
+	(void) idedisk_capacity (drive);
+
 	/* Correct the number of cyls if the bios value is too small */
 	if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
 		if (drive->cyl > drive->bios_cyl)
@@ -705,8 +717,6 @@
 	}
 	/* fix byte-ordering of buffer size field */
 	id->buf_size = le16_to_cpu(id->buf_size);
-
-	(void) idedisk_capacity (drive); /* initialize LBA selection */
 
 	printk (KERN_INFO "%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
 	 drive->name, id->model, idedisk_capacity(drive)/2048L, id->buf_size/2,
diff -u --recursive --new-file v2.1.89/linux/drivers/block/ide-pci.c linux/drivers/block/ide-pci.c
--- v2.1.89/linux/drivers/block/ide-pci.c	Wed Dec 31 16:42:56 1997
+++ linux/drivers/block/ide-pci.c	Wed Mar 11 16:18:29 1998
@@ -24,7 +24,8 @@
 
 #include "ide.h"
 
-#define DEVID_PIIX	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371FB_1})
+#define DEVID_PIIXa	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371FB_0})
+#define DEVID_PIIXb	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371FB_1})
 #define DEVID_PIIX3	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371SB_1})
 #define DEVID_PIIX4	((ide_pci_devid_t){PCI_VENDOR_ID_INTEL,   PCI_DEVICE_ID_INTEL_82371AB})
 #define DEVID_VP_IDE	((ide_pci_devid_t){PCI_VENDOR_ID_VIA,     PCI_DEVICE_ID_VIA_82C586_1})
@@ -87,7 +88,8 @@
 } ide_pci_device_t;
 
 static ide_pci_device_t ide_pci_chipsets[] __initdata = {
-	{DEVID_PIIX,	"PIIX",		NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+	{DEVID_PIIXa,	"PIIX",		NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
+	{DEVID_PIIXb,	"PIIX",		NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
 	{DEVID_PIIX3,	"PIIX3",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
 	{DEVID_PIIX4,	"PIIX4",	NULL,		{{0x41,0x80,0x80}, {0x43,0x80,0x80}} },
 	{DEVID_VP_IDE,	"VP_IDE",	NULL,		{{0x40,0x02,0x02}, {0x40,0x01,0x01}} },
@@ -360,6 +362,13 @@
 		 || IDE_PCI_DEVID_EQ(devid, IDE_PCI_DEVID_NULL)
 		 || pcibios_read_config_dword(bus, fn, 0x08, &ccode))
 			return;
+		/* 
+		 * workaround Intel Advanced/ZP with bios <= 1.04;
+		 * these appear in some Dell Dimension XPS's 
+		 */
+		if (!hedt && IDE_PCI_DEVID_EQ(devid, DEVID_PIIXa))
+		        hedt = 0x80;
+
 		for (d = ide_pci_chipsets; d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d);
 		if (d->init_hwif == IDE_IGNORE)
 			printk("%s: ignored by ide_scan_pci_device() (uses own driver)\n", d->name);
diff -u --recursive --new-file v2.1.89/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.1.89/linux/drivers/block/ide.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/block/ide.c	Tue Mar 10 14:43:13 1998
@@ -134,9 +134,9 @@
 #include "ide.h"
 #include "ide_modes.h"
 
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif /* CONFIG_KERNELD */
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif /* CONFIG_KMOD */
 
 static const byte	ide_hwif_to_major[] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
 
@@ -1505,10 +1505,10 @@
 		module = module->next;
 	}
 	revalidate_drives();
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (!found && type == IDE_PROBE_MODULE)
 		(void) request_module("ide-probe");
-#endif /* CONFIG_KERNELD */
+#endif /* CONFIG_KMOD */
 }
 
 static int ide_open(struct inode * inode, struct file * filp)
@@ -1521,7 +1521,7 @@
 	MOD_INC_USE_COUNT;
 	if (drive->driver == NULL)
 		ide_init_module(IDE_DRIVER_MODULE);
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (drive->driver == NULL) {
 		if (drive->media == ide_disk)
 			(void) request_module("ide-disk");
@@ -1532,7 +1532,7 @@
 		if (drive->media == ide_floppy)
 			(void) request_module("ide-floppy");
 	}
-#endif /* CONFIG_KERNELD */
+#endif /* CONFIG_KMOD */
 	while (drive->busy)
 		sleep_on(&drive->wqueue);
 	drive->usage++;
diff -u --recursive --new-file v2.1.89/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v2.1.89/linux/drivers/block/ll_rw_blk.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/block/ll_rw_blk.c	Wed Mar 11 15:07:43 1998
@@ -24,9 +24,6 @@
 
 #include <linux/module.h>
 
-#define ATOMIC_ON()	do { } while (0)
-#define ATOMIC_OFF()	do { } while (0)
-
 /*
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
@@ -419,8 +416,6 @@
 	 * not to schedule or do something nonatomic
 	 */
 	spin_lock_irqsave(&io_request_lock,flags);
-	ATOMIC_ON();
-
 	req = *get_queue(bh->b_rdev);
 	if (!req) {
 		/* MD and loop can't handle plugging without deadlocking */
@@ -480,7 +475,6 @@
 				continue;
 
 			mark_buffer_clean(bh);
-			ATOMIC_OFF();
 			spin_unlock_irqrestore(&io_request_lock,flags);
 		    	return;
 
@@ -490,7 +484,6 @@
 /* find an unused request. */
 	req = get_request(max_req, bh->b_rdev);
 
-	ATOMIC_OFF();
 	spin_unlock_irqrestore(&io_request_lock,flags);
 
 /* if no request available: if rw_ahead, forget it; otherwise try again blocking.. */
@@ -665,9 +658,7 @@
 			} else {
 				unsigned long flags;
 				spin_lock_irqsave(&io_request_lock,flags);
-				ATOMIC_ON();
 				req[j] = get_request(max_req, rdev);
-				ATOMIC_OFF();
 				spin_unlock_irqrestore(&io_request_lock,flags);
 				if (req[j] == NULL)
 					break;
diff -u --recursive --new-file v2.1.89/linux/drivers/block/md.c linux/drivers/block/md.c
--- v2.1.89/linux/drivers/block/md.c	Thu Feb 12 20:56:05 1998
+++ linux/drivers/block/md.c	Tue Mar 10 14:43:13 1998
@@ -12,6 +12,9 @@
 
    RAID-1/RAID-5 extensions by:
         Ingo Molnar, Miguel de Icaza, Gadi Oxman
+
+   Changes for kmod by:
+   	Cyrus Durgin
    
    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
@@ -43,8 +46,8 @@
 #include <linux/blkdev.h>
 #include <linux/genhd.h>
 #include <linux/smp_lock.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -431,7 +434,7 @@
   }
   if (!pers[pnum])
   {
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
     char module_name[80];
     sprintf (module_name, "md-personality-%d", pnum);
     request_module (module_name);
diff -u --recursive --new-file v2.1.89/linux/drivers/block/raid5.c linux/drivers/block/raid5.c
--- v2.1.89/linux/drivers/block/raid5.c	Fri Jan 16 20:34:51 1998
+++ linux/drivers/block/raid5.c	Wed Mar 11 21:01:56 1998
@@ -837,8 +837,11 @@
 	struct raid5_data *raid_conf = sh->raid_conf;
 	struct buffer_head *bh_req;
 
-	if (sh->bh_new[dd_idx])
+	if (sh->bh_new[dd_idx]) {
 		printk("raid5: bug: stripe->bh_new[%d], sector %lu exists\n", dd_idx, sh->sector);
+		printk("forcing oops.\n");
+		*(int*)0=0;
+	}
 
 	set_bit(BH_Lock, &bh->b_state);
 
diff -u --recursive --new-file v2.1.89/linux/drivers/block/rd.c linux/drivers/block/rd.c
--- v2.1.89/linux/drivers/block/rd.c	Mon Dec  1 10:34:11 1997
+++ linux/drivers/block/rd.c	Wed Mar 11 11:42:30 1998
@@ -35,6 +35,9 @@
  *
  * 4/25/96 : Made ramdisk size a parameter (default is now 4MB) 
  *		- Chad Page
+ *
+ * Add support for fs images split across >1 disk, Paul Gortmaker, Mar '98
+ *
  */
 
 #include <linux/config.h>
@@ -344,7 +347,6 @@
 	struct ext2_super_block *ext2sb;
 	struct romfs_super_block *romfsb;
 	int nblocks = -1;
-	int max_blocks;
 	unsigned char *buf;
 
 	buf = kmalloc(size, GFP_KERNEL);
@@ -423,17 +425,6 @@
 		fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0);
 	fp->f_pos = start_block * BLOCK_SIZE;	
 
-	if ((nblocks > 0) && blk_size[MAJOR(device)]) {
-		max_blocks = blk_size[MAJOR(device)][MINOR(device)];
-		max_blocks -= start_block;
-		if (nblocks > max_blocks) {
-			printk(KERN_NOTICE
-			       "RAMDISK: Restricting filesystem size "
-			       "from %d to %d blocks.\n",
-			       nblocks, max_blocks);
-			nblocks = max_blocks;
-		}
-	}
 	kfree(buf);
 	return nblocks;
 }
@@ -451,6 +442,7 @@
 	int nblocks, i;
 	char *buf;
 	unsigned short rotate = 0;
+	unsigned short devblocks = 0;
 	char rotator[4] = { '|' , '/' , '-' , '\\' };
 
 	ram_device = MKDEV(MAJOR_NR, 0);
@@ -508,8 +500,31 @@
 		goto done;
 	}
 
-	printk(KERN_NOTICE "RAMDISK: Loading %d blocks into ram disk... ", nblocks);
+	if (blk_size[MAJOR(device)])
+		devblocks = blk_size[MAJOR(device)][MINOR(device)];
+
+	if (devblocks == 0) {
+		printk(KERN_ERR "RAMDISK: could not determine device size\n");
+		goto done;
+	}
+
+	printk(KERN_NOTICE "RAMDISK: Loading %d blocks [%d disk(s)] into ram disk... ", nblocks, nblocks/devblocks+1);
 	for (i=0; i < nblocks; i++) {
+		if (i && (i % devblocks == 0)) {
+			printk("done.\n");
+			rotate = 0;
+			invalidate_buffers(device);
+			if (infile.f_op->release)
+				infile.f_op->release(&inode, &infile);
+			printk("Please insert disk #%d and press ENTER\n", i/devblocks+1);
+			wait_for_keypress();
+			if (blkdev_open(&inode, &infile) != 0)  {
+				printk("Error opening disk.\n");
+				goto done;
+			}
+			infile.f_pos = 0;
+			printk("Loading disk #%d... ", i/devblocks+1);
+		}
 		infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos);
 		outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos);
 		if (!(i % 16)) {
diff -u --recursive --new-file v2.1.89/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v2.1.89/linux/drivers/char/ChangeLog	Mon Dec  1 09:45:43 1997
+++ linux/drivers/char/ChangeLog	Wed Mar 11 17:37:13 1998
@@ -1,3 +1,11 @@
+Thu Feb 19 14:24:08 1998  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* tty_io.c (tty_name): Remove the non-reentrant (and non-SMP safe)
+		version of tty_name, and rename the reentrant _tty_name
+		function to be tty_name.
+		(tty_open): Add a warning message stating callout devices
+		are deprecated.
+
 Mon Dec  1 08:24:15 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
 	* tty_io.c (tty_get_baud_rate): Print a warning syslog if the
diff -u --recursive --new-file v2.1.89/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.89/linux/drivers/char/Config.in	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/Config.in	Sat Mar 14 11:50:31 1998
@@ -106,11 +106,6 @@
   tristate '   Acquire SBC Watchdog Timer' CONFIG_ACQUIRE_WDT
 fi
 bool 'Enhanced Real Time Clock Support' CONFIG_RTC
-if [ "$CONFIG_RTC" = "y" ]; then
-  if [ "$ARCH" = "alpha" ]; then
-    bool '  ARC console time' CONFIG_RTC_ARC y
-  fi
-fi
 if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then
   bool 'Tadpole ANA H8 Support'  CONFIG_H8
 fi
diff -u --recursive --new-file v2.1.89/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v2.1.89/linux/drivers/char/apm_bios.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/apm_bios.c	Wed Mar 11 15:10:39 1998
@@ -1203,17 +1203,17 @@
 	 * even though they are called in protected mode.
 	 */
 	set_base(gdt[APM_40 >> 3],
-		 0xc0000000 + ((unsigned long)0x40 << 4));
+		 __va((unsigned long)0x40 << 4));
 	set_limit(gdt[APM_40 >> 3], 4096 - (0x40 << 4));
 
 	apm_bios_entry.offset = apm_bios_info.offset;
 	apm_bios_entry.segment = APM_CS;
 	set_base(gdt[APM_CS >> 3],
-		 0xc0000000 + ((unsigned long)apm_bios_info.cseg << 4));
+		 __va((unsigned long)apm_bios_info.cseg << 4));
 	set_base(gdt[APM_CS_16 >> 3],
-		 0xc0000000 + ((unsigned long)apm_bios_info.cseg_16 << 4));
+		 __va((unsigned long)apm_bios_info.cseg_16 << 4));
 	set_base(gdt[APM_DS >> 3],
-		 0xc0000000 + ((unsigned long)apm_bios_info.dseg << 4));
+		 __va((unsigned long)apm_bios_info.dseg << 4));
 	if (apm_bios_info.version == 0x100) {
 		set_limit(gdt[APM_CS >> 3], 64 * 1024);
 		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
diff -u --recursive --new-file v2.1.89/linux/drivers/char/esp.c linux/drivers/char/esp.c
--- v2.1.89/linux/drivers/char/esp.c	Tue Feb 17 13:12:46 1998
+++ linux/drivers/char/esp.c	Wed Mar 11 17:37:13 1998
@@ -1384,7 +1384,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 	
-	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	printk("throttle %s: %d....\n", tty_name(tty, buf),
 	       tty->ldisc.chars_in_buffer(tty));
 #endif
 
@@ -1406,7 +1406,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 	
-	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	printk("unthrottle %s: %d....\n", tty_name(tty, buf),
 	       tty->ldisc.chars_in_buffer(tty));
 #endif
 
diff -u --recursive --new-file v2.1.89/linux/drivers/char/ftape/zftape/zftape-ctl.c linux/drivers/char/ftape/zftape/zftape-ctl.c
--- v2.1.89/linux/drivers/char/ftape/zftape/zftape-ctl.c	Tue Nov 25 14:45:28 1997
+++ linux/drivers/char/ftape/zftape/zftape-ctl.c	Tue Mar 10 14:43:13 1998
@@ -29,9 +29,6 @@
 #include <linux/mm.h>
 #define __NO_VERSION__
 #include <linux/module.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 #include <linux/fcntl.h>
 
 #include <linux/zftape.h>
diff -u --recursive --new-file v2.1.89/linux/drivers/char/ftape/zftape/zftape-init.c linux/drivers/char/ftape/zftape/zftape-init.c
--- v2.1.89/linux/drivers/char/ftape/zftape/zftape-init.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/ftape/zftape/zftape-init.c	Tue Mar 10 14:43:13 1998
@@ -30,8 +30,8 @@
 #include <linux/signal.h>
 #include <linux/major.h>
 #include <linux/malloc.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 #include <linux/fcntl.h>
 #include <linux/wrapper.h>
@@ -391,7 +391,7 @@
 int zft_cmpr_lock(int try_to_load)
 {
 	if (zft_cmpr_ops == NULL) {
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 		if (try_to_load) {
 			request_module("zft-compressor");
 			if (zft_cmpr_ops == NULL) {
diff -u --recursive --new-file v2.1.89/linux/drivers/char/ftape/zftape/zftape-read.c linux/drivers/char/ftape/zftape/zftape-read.c
--- v2.1.89/linux/drivers/char/ftape/zftape/zftape-read.c	Tue Nov 25 14:45:28 1997
+++ linux/drivers/char/ftape/zftape/zftape-read.c	Thu Mar 12 10:33:21 1998
@@ -24,12 +24,8 @@
  *      for the QIC-117 floppy-tape driver for Linux.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 
 #include <linux/zftape.h>
 
diff -u --recursive --new-file v2.1.89/linux/drivers/char/ftape/zftape/zftape-write.c linux/drivers/char/ftape/zftape/zftape-write.c
--- v2.1.89/linux/drivers/char/ftape/zftape/zftape-write.c	Tue Nov 25 14:45:28 1997
+++ linux/drivers/char/ftape/zftape/zftape-write.c	Thu Mar 12 10:33:21 1998
@@ -24,12 +24,8 @@
  *      for the QIC-117 floppy-tape driver for Linux.
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 
 #include <linux/zftape.h>
 
diff -u --recursive --new-file v2.1.89/linux/drivers/char/lp_m68k.c linux/drivers/char/lp_m68k.c
--- v2.1.89/linux/drivers/char/lp_m68k.c	Sat Sep 20 14:51:54 1997
+++ linux/drivers/char/lp_m68k.c	Tue Mar 10 14:43:13 1998
@@ -43,8 +43,8 @@
 #include <linux/string.h>
 #include <linux/init.h>
 #include <asm/irq.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 #ifdef CONFIG_AMIGA
@@ -365,7 +365,7 @@
 
 	if (dev >= MAX_LP)
 		return -ENODEV;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (!lp_table[dev]) {
 		char modname[30];
 
diff -u --recursive --new-file v2.1.89/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v2.1.89/linux/drivers/char/misc.c	Fri Jan 23 18:10:31 1998
+++ linux/drivers/char/misc.c	Tue Mar 10 14:43:13 1998
@@ -26,6 +26,9 @@
  *  Idea by Jacques Gelinas <jack@solucorp.qc.ca>,
  *  adapted by Bjorn Ekwall <bj0rn@blox.se>
  *  corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ * Changes for kmod (from kerneld):
+ 	Cyrus Durgin <cider@speakeasy.org>
  */
 
 #include <linux/config.h>
@@ -46,8 +49,8 @@
 
 #include <linux/tty.h>
 #include <linux/selection.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 /*
@@ -107,7 +110,7 @@
 	while ((c != &misc_list) && (c->minor != minor))
 		c = c->next;
 	if (c == &misc_list) {
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 		char modname[20];
 		sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor);
 		request_module(modname);
diff -u --recursive --new-file v2.1.89/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.1.89/linux/drivers/char/n_tty.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/n_tty.c	Wed Mar 11 17:37:13 1998
@@ -420,7 +420,7 @@
 
 	tty->num_overrun++;
 	if (tty->overrun_time < (jiffies - HZ)) {
-		printk("%s: %d input overrun(s)\n", _tty_name(tty, buf),
+		printk("%s: %d input overrun(s)\n", tty_name(tty, buf),
 		       tty->num_overrun);
 		tty->overrun_time = jiffies;
 		tty->num_overrun = 0;
@@ -656,6 +656,7 @@
 	const unsigned char *p;
 	char *f, flags = TTY_NORMAL;
 	int	i;
+	char	buf[64];
 
 	if (!tty->read_buf)
 		return;
@@ -693,8 +694,8 @@
 				n_tty_receive_overrun(tty);
 				break;
 			default:
-				printk("%s: unknown flag %d\n", tty_name(tty),
-				       flags);
+				printk("%s: unknown flag %d\n",
+				       tty_name(tty, buf), flags);
 				break;
 			}
 		}
diff -u --recursive --new-file v2.1.89/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
--- v2.1.89/linux/drivers/char/rtc.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/rtc.c	Sun Mar 15 10:25:42 1998
@@ -48,7 +48,6 @@
  *	this driver.)
  */
 
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/miscdevice.h>
@@ -63,13 +62,6 @@
 #include <asm/uaccess.h>
 #include <asm/system.h>
 
-/* Adjust starting epoch if ARC console time is being used */
-#ifdef CONFIG_RTC_ARC
-#define ARCFUDGE 20 
-#else
-#define ARCFUDGE 0
-#endif
-
 /*
  *	We sponge a minor off of the misc major. No need slurping
  *	up another valuable major dev number for this. If you add
@@ -126,12 +118,8 @@
  *	so that there is no possibility of conflicting with the
  *	set_rtc_mmss() call that happens during some timer interrupts.
  *	(See ./arch/XXXX/kernel/time.c for the set_rtc_mmss() function.)
- *
- *	On Alpha we won't get any interrupts anyway, as they all end up
- *	in the system timer code.
  */
 
-#ifndef __alpha__
 static void rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	/*
@@ -152,11 +140,9 @@
 		add_timer(&rtc_irq_timer);
 	}
 }
-#endif
 
 /*
  *	Now all the various file operations that we export.
- *      They are all useless on Alpha...  *sigh*.
  */
 
 static long long rtc_llseek(struct file *file, loff_t offset, int origin)
@@ -167,9 +153,6 @@
 static ssize_t rtc_read(struct file *file, char *buf,
 			size_t count, loff_t *ppos)
 {
-#ifdef __alpha__
-	return -EIO;
-#else
 	struct wait_queue wait = { current, NULL };
 	unsigned long data;
 	ssize_t retval;
@@ -201,7 +184,6 @@
 	remove_wait_queue(&rtc_wait, &wait);
 
 	return retval;
-#endif
 }
 
 static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
@@ -212,7 +194,6 @@
 	struct rtc_time wtime; 
 
 	switch (cmd) {
-#ifndef __alpha__
 	case RTC_AIE_OFF:	/* Mask alarm int. enab. bit	*/
 	{
 		mask_rtc_irq_bit(RTC_AIE);
@@ -260,7 +241,6 @@
 		set_rtc_irq_bit(RTC_UIE);
 		return 0;
 	}
-#endif
 	case RTC_ALM_READ:	/* Read the present alarm time */
 	{
 		/*
@@ -335,7 +315,7 @@
 				   sizeof(struct rtc_time)))
 			return -EFAULT;
 
-		yrs = rtc_tm.tm_year + 1900 + ARCFUDGE;
+		yrs = rtc_tm.tm_year + 1900;
 		mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
 		day = rtc_tm.tm_mday;
 		hrs = rtc_tm.tm_hour;
@@ -400,7 +380,6 @@
 	{
 		return put_user(rtc_freq, (unsigned long *)arg);
 	}
-#ifndef __alpha__
 	case RTC_IRQP_SET:	/* Set periodic IRQ rate.	*/
 	{
 		int tmp = 0;
@@ -437,7 +416,7 @@
 		restore_flags(flags);
 		return 0;
 	}
-#else /* __alpha__ */
+#ifdef __alpha__
 	case RTC_EPOCH_READ:	/* Read the epoch.	*/
 	{
 		return put_user (epoch, (unsigned long *)arg);
@@ -467,18 +446,15 @@
  *	We enforce only one user at a time here with the open/close.
  *	Also clear the previous interrupt data on an open, and clean
  *	up things on a close.
- *	On Alpha we just open, for we don't mess with interrups anyway.
  */
 
 static int rtc_open(struct inode *inode, struct file *file)
 {
-#ifndef __alpha__
 	if(rtc_status & RTC_IS_OPEN)
 		return -EBUSY;
 
 	rtc_status |= RTC_IS_OPEN;
 	rtc_irq_data = 0;
-#endif
 	return 0;
 }
 
@@ -489,7 +465,6 @@
 	 * in use, and clear the data.
 	 */
 
-#ifndef __alpha__
 	unsigned char tmp;
 	unsigned long flags;
 
@@ -510,11 +485,9 @@
 
 	rtc_irq_data = 0;
 	rtc_status &= ~RTC_IS_OPEN;
-#endif
 	return 0;
 }
 
-#ifndef __alpha__
 static unsigned int rtc_poll(struct file *file, poll_table *wait)
 {
 	poll_wait(file, &rtc_wait, wait);
@@ -522,7 +495,6 @@
 		return POLLIN | POLLRDNORM;
 	return 0;
 }
-#endif
 
 /*
  *	The various file operations we support.
@@ -533,11 +505,7 @@
 	rtc_read,
 	NULL,		/* No write */
 	NULL,		/* No readdir */
-#ifdef __alpha__
-	NULL,		/* No select on Alpha */
-#else
 	rtc_poll,
-#endif
 	rtc_ioctl,
 	NULL,		/* No mmap */
 	rtc_open,
@@ -560,14 +528,12 @@
 	char *guess = NULL;
 #endif
 	printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
-#ifndef __alpha__
 	if(request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, "rtc", NULL))
 	{
 		/* Yeah right, seeing as irq 8 doesn't even hit the bus. */
 		printk(KERN_ERR "rtc: IRQ %d is not free.\n", RTC_IRQ);
 		return -EIO;
 	}
-#endif
 	misc_register(&rtc_dev);
 	/* Check region? Naaah! Just snarf it up. */
 	request_region(RTC_PORT(0), RTC_IO_EXTENT, "rtc");
@@ -599,8 +565,8 @@
 		guess = "Digital UNIX";
 	}
 	if (guess)
-		printk("rtc: %s epoch (%ld) detected\n", guess, epoch);
-#else
+		printk("rtc: %s epoch (%lu) detected\n", guess, epoch);
+#endif
 	init_timer(&rtc_irq_timer);
 	rtc_irq_timer.function = rtc_dropped_irq;
 	rtc_wait = NULL;
@@ -610,7 +576,6 @@
 	CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
 	restore_flags(flags);
 	rtc_freq = 1024;
-#endif
 	return 0;
 }
 
@@ -626,7 +591,6 @@
  *	for something that requires a steady > 1KHz signal anyways.)
  */
 
-#ifndef __alpha__
 void rtc_dropped_irq(unsigned long data)
 {
 	unsigned long flags;
@@ -643,7 +607,6 @@
 	rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);	/* restart */
 	restore_flags(flags);
 }
-#endif
 
 /*
  *	Info exported via "/proc/rtc".
@@ -672,9 +635,10 @@
 	 */
 	p += sprintf(p,
 		     "rtc_time\t: %02d:%02d:%02d\n"
-		     "rtc_date\t: %04d-%02d-%02d\n",
+		     "rtc_date\t: %04d-%02d-%02d\n"
+	 	     "rtc_epoch\t: %04lu\n",
 		     tm.tm_hour, tm.tm_min, tm.tm_sec,
-		     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+		     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch);
 
 	get_rtc_alm_time(&tm);
 
@@ -788,12 +752,9 @@
 	 * Account for differences between how the RTC uses the values
 	 * and how they are defined in a struct rtc_time;
 	 */
-	if ((rtc_tm->tm_year += epoch - 1900) <= 69)
+	if ((rtc_tm->tm_year += (epoch - 1900)) <= 69)
 		rtc_tm->tm_year += 100;
 
-	/* if ARCFUDGE == 0, the optimizer should do away with this */
-	rtc_tm->tm_year -= ARCFUDGE;
-
 	rtc_tm->tm_mon--;
 }
 
@@ -832,7 +793,6 @@
  * meddles with the interrupt enable/disable bits.
  */
 
-#ifndef __alpha__
 void mask_rtc_irq_bit(unsigned char bit)
 {
 	unsigned char val;
@@ -862,4 +822,3 @@
 	rtc_irq_data = 0;
 	restore_flags(flags);
 }
-#endif
diff -u --recursive --new-file v2.1.89/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.89/linux/drivers/char/serial.c	Sun Dec  7 12:06:56 1997
+++ linux/drivers/char/serial.c	Wed Mar 11 17:37:13 1998
@@ -1652,7 +1652,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 	
-	printk("throttle %s: %d....\n", _tty_name(tty, buf),
+	printk("throttle %s: %d....\n", tty_name(tty, buf),
 	       tty->ldisc.chars_in_buffer(tty));
 #endif
 
@@ -1676,7 +1676,7 @@
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 	
-	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
+	printk("unthrottle %s: %d....\n", tty_name(tty, buf),
 	       tty->ldisc.chars_in_buffer(tty));
 #endif
 
diff -u --recursive --new-file v2.1.89/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.89/linux/drivers/char/tty_io.c	Tue Mar 10 10:03:31 1998
+++ linux/drivers/char/tty_io.c	Wed Mar 11 17:37:13 1998
@@ -87,8 +87,8 @@
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
 
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 #define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
@@ -127,11 +127,9 @@
 #endif
 
 /*
- * These two routines return the name of tty.  tty_name() should NOT
- * be used in interrupt drivers, since it's not re-entrant.  Use
- * _tty_name() instead.
+ * This routine returns the name of tty.
  */
-char *_tty_name(struct tty_struct *tty, char *buf)
+char *tty_name(struct tty_struct *tty, char *buf)
 {
 	if (tty)
 		sprintf(buf, "%s%d", tty->driver.name,
@@ -142,13 +140,6 @@
 	return buf;
 }
 
-char *tty_name(struct tty_struct *tty)
-{
-	static char buf[64];
-
-	return(_tty_name(tty, buf));
-}
-
 inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
 			      const char *routine)
 {
@@ -213,11 +204,13 @@
 {
 	int	retval = 0;
 	struct	tty_ldisc o_ldisc;
+	char buf[64];
 
 	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
 		return -EINVAL;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	/* Eduardo Blanco <ejbs@cs.cs.com.uy> */
+	/* Cyrus Durgin <cider@speakeasy.org> */
 	if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {
 		char modname [20];
 		sprintf(modname, "tty-ldisc-%d", ldisc);
@@ -254,7 +247,7 @@
 				if (r < 0)
 					panic("Couldn't open N_TTY ldisc for "
 					      "%s --- error %d.",
-					      tty_name(tty), r);
+					      tty_name(tty, buf), r);
 			}
 		}
 	}
@@ -455,7 +448,9 @@
 void tty_hangup(struct tty_struct * tty)
 {
 #ifdef TTY_DEBUG_HANGUP
-	printk("%s hangup...\n", tty_name(tty));
+	char	buf[64];
+	
+	printk("%s hangup...\n", tty_name(tty, buf));
 #endif
 	queue_task(&tty->tq_hangup, &tq_timer);
 }
@@ -463,7 +458,9 @@
 void tty_vhangup(struct tty_struct * tty)
 {
 #ifdef TTY_DEBUG_HANGUP
-	printk("%s vhangup...\n", tty_name(tty));
+	char	buf[64];
+
+	printk("%s vhangup...\n", tty_name(tty, buf));
 #endif
 	do_tty_hangup((void *) tty);
 }
@@ -950,6 +947,7 @@
 	struct tty_struct *tty, *o_tty;
 	int	pty_master, tty_closing, o_tty_closing, do_sleep;
 	int	idx;
+	char	buf[64];
 	
 	tty = (struct tty_struct *)filp->private_data;
 	if (tty_paranoia_check(tty, filp->f_dentry->d_inode->i_rdev, "release_dev"))
@@ -990,7 +988,7 @@
 #endif
 
 #ifdef TTY_DEBUG_HANGUP
-	printk("release_dev of %s (tty count=%d)...", tty_name(tty),
+	printk("release_dev of %s (tty count=%d)...", tty_name(tty, buf),
 	       tty->count);
 #endif
 
@@ -1068,7 +1066,7 @@
 			break;
 
 		printk("release_dev: %s: read/write wait queue active!\n",
-		       tty_name(tty));
+		       tty_name(tty, buf));
 		schedule();
 	}	
 
@@ -1084,13 +1082,13 @@
 	if (pty_master) {
 		if (--o_tty->count < 0) {
 			printk("release_dev: bad pty slave count (%d) for %s\n",
-			       o_tty->count, tty_name(o_tty));
+			       o_tty->count, tty_name(o_tty, buf));
 			o_tty->count = 0;
 		}
 	}
 	if (--tty->count < 0) {
 		printk("release_dev: bad tty->count (%d) for %s\n",
-		       tty->count, tty_name(tty));
+		       tty->count, tty_name(tty, buf));
 		tty->count = 0;
 	}
 
@@ -1178,6 +1176,7 @@
 	int noctty, retval;
 	kdev_t device;
 	unsigned short saved_flags;
+	char	buf[64];
 
 	saved_flags = filp->f_flags;
 retry_open:
@@ -1245,7 +1244,7 @@
 	    tty->driver.subtype == PTY_TYPE_MASTER)
 		noctty = 1;
 #ifdef TTY_DEBUG_HANGUP
-	printk("opening %s...", tty_name(tty));
+	printk("opening %s...", tty_name(tty, buf));
 #endif
 	if (tty->driver.open)
 		retval = tty->driver.open(tty, filp);
@@ -1258,7 +1257,8 @@
 
 	if (retval) {
 #ifdef TTY_DEBUG_HANGUP
-		printk("error %d in opening %s...", retval, tty_name(tty));
+		printk("error %d in opening %s...", retval,
+		       tty_name(tty, buf));
 #endif
 
 		release_dev(filp);
@@ -1281,6 +1281,11 @@
 		current->tty_old_pgrp = 0;
 		tty->session = current->session;
 		tty->pgrp = current->pgrp;
+	}
+	if ((tty->driver.type == TTY_DRIVER_TYPE_SERIAL) &&
+	    (tty->driver.subtype == SERIAL_TYPE_CALLOUT)) {
+		printk("Warning, %s opened, is a deprecated tty "
+		       "callout device\n", tty_name(tty, buf));
 	}
 	return 0;
 }
diff -u --recursive --new-file v2.1.89/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v2.1.89/linux/drivers/char/tty_ioctl.c	Mon Nov 24 08:45:44 1997
+++ linux/drivers/char/tty_ioctl.c	Wed Mar 11 17:37:13 1998
@@ -45,7 +45,9 @@
 	struct wait_queue wait = { current, NULL };
 
 #ifdef TTY_DEBUG_WAIT_UNTIL_SENT
-	printk("%s wait until sent...\n", tty_name(tty));
+	char buf[64];
+	
+	printk("%s wait until sent...\n", tty_name(tty, buf));
 #endif
 	if (!tty->driver.chars_in_buffer)
 		return;
@@ -57,7 +59,8 @@
 		current->timeout = (unsigned) -1;
 	do {
 #ifdef TTY_DEBUG_WAIT_UNTIL_SENT
-		printk("waiting %s...(%d)\n", tty_name(tty), tty->driver.chars_in_buffer(tty));
+		printk("waiting %s...(%d)\n", tty_name(tty, buf),
+		       tty->driver.chars_in_buffer(tty));
 #endif
 		current->state = TASK_INTERRUPTIBLE;
 		if (signal_pending(current))
diff -u --recursive --new-file v2.1.89/linux/drivers/misc/parport_init.c linux/drivers/misc/parport_init.c
--- v2.1.89/linux/drivers/misc/parport_init.c	Fri Jan 30 11:28:07 1998
+++ linux/drivers/misc/parport_init.c	Tue Mar 10 14:43:13 1998
@@ -17,7 +17,6 @@
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
-#include <linux/kerneld.h>
 
 #ifndef MODULE
 static int io[PARPORT_MAX+1] __initdata = { [0 ... PARPORT_MAX] = 0 };
diff -u --recursive --new-file v2.1.89/linux/drivers/misc/parport_share.c linux/drivers/misc/parport_share.c
--- v2.1.89/linux/drivers/misc/parport_share.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/misc/parport_share.c	Tue Mar 10 14:43:13 1998
@@ -28,8 +28,8 @@
 
 #include <asm/spinlock.h>
 
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 #undef PARPORT_PARANOID
@@ -44,14 +44,14 @@
 /* Return a list of all the ports we know about. */
 struct parport *parport_enumerate(void)
 {
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (portlist == NULL) {
 		request_module("parport_lowlevel");
 #ifdef CONFIG_PNP_PARPORT_MODULE
 		request_module("parport_probe");
 #endif /* CONFIG_PNP_PARPORT_MODULE */
 	}
-#endif /* CONFIG_KERNELD */
+#endif /* CONFIG_KMOD */
 	return portlist;
 }
 
diff -u --recursive --new-file v2.1.89/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v2.1.89/linux/drivers/net/Config.in	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/Config.in	Wed Mar 11 11:40:11 1998
@@ -49,10 +49,9 @@
   if [ "$CONFIG_NET_VENDOR_SMC" = "y" ]; then
     tristate 'WD80*3 support' CONFIG_WD80x3
     if [ "$CONFIG_MCA" = "y" ]; then
-		tristate 'SMC Ultra MCA support' CONFIG_ULTRA
-    else
-		tristate 'SMC Ultra support' CONFIG_ULTRA
+		tristate 'SMC Ultra MCA support' CONFIG_ULTRAMCA
     fi
+    tristate 'SMC Ultra support' CONFIG_ULTRA
     tristate 'SMC Ultra32 EISA support' CONFIG_ULTRA32
     tristate 'SMC 9194 support' CONFIG_SMC9194
   fi
diff -u --recursive --new-file v2.1.89/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v2.1.89/linux/drivers/net/Makefile	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/Makefile	Wed Mar 11 11:40:11 1998
@@ -158,13 +158,20 @@
 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
   M_OBJS += smc-ultra.o
+  endif
+endif
+
+ifeq ($(CONFIG_ULTRAMCA),y)
+L_OBJS += smc-mca.o
+CONFIG_8390_BUILTIN = y
+else
+  ifeq ($(CONFIG_ULTRAMCA),m)
+  CONFIG_8390_MODULE = y
+  M_OBJS += smc-mca.o
   endif
 endif
 
diff -u --recursive --new-file v2.1.89/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v2.1.89/linux/drivers/net/Space.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/Space.c	Wed Mar 11 11:40:11 1998
@@ -43,6 +43,7 @@
 extern int tulip_probe(struct device *dev);
 extern int hp100_probe(struct device *dev);
 extern int ultra_probe(struct device *dev);
+extern int ultra32_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);
@@ -135,12 +136,12 @@
 #endif	
 #if defined(CONFIG_ULTRA)
 	&& ultra_probe(dev)
-#if defined(CONFIG_MCA)
+#endif
+#if defined(CONFIG_ULTRAMCA)
         && ultramca_probe(dev)
 #endif
 #if defined(CONFIG_ULTRA32)
 	&& ultra32_probe(dev)
-#endif
 #endif
 #if defined(CONFIG_SMC9194)
 	&& smc_init(dev)
diff -u --recursive --new-file v2.1.89/linux/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- v2.1.89/linux/drivers/net/de4x5.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/de4x5.c	Tue Mar 17 21:15:40 1998
@@ -213,7 +213,7 @@
 
         insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
 
-    For a compiled in driver, somewhere in this file, place e.g.
+    For a compiled in driver, at or above line 526, place e.g.
 	#define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP"
 
     Yes,  I know full duplex  isn't permissible on BNC  or AUI; they're just
@@ -371,11 +371,14 @@
       0.533   9-Jan-98    Fix more 64 bit bugs reported by <jal@cs.brown.edu>.
       0.534  24-Jan-98    Fix last (?) endian bug from 
                            <Geert.Uytterhoeven@cs.kuleuven.ac.be>
+      0.535  21-Feb-98    Fix Ethernet Address PROM reset bug for DC21040.
+      0.536  21-Mar-98    Change pci_probe() to use the pci_dev structure.
+			  **Incompatible with 2.0.x from here.**
 
     =========================================================================
 */
 
-static const char *version = "de4x5.c:V0.534 1998/1/24 davies@maniac.ultranet.com\n";
+static const char *version = "de4x5.c:V0.536 1998/3/5 davies@maniac.ultranet.com\n";
 
 #include <linux/module.h>
 
@@ -390,11 +393,15 @@
 #include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/version.h>
+
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
+#include <asm/uaccess.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -408,31 +415,6 @@
 #include "de4x5.h"
 
 #define c_char const char
-
-#include <linux/version.h>
-#if	LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
-#  define __initfunc(__arginit) __arginit
-#  define test_and_set_bit      set_bit
-#  define net_device_stats      enet_statistics
-#  define copy_to_user(a,b,c)   memcpy_tofs(a,b,c)
-#  define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)
-#  define le16_to_cpu(a)        cpu_to_le16(a) 
-#  define le32_to_cpu(a)        cpu_to_le32(a) 
-#  ifdef __powerpc__
-#    define cpu_to_le16(a) ((((a) & 0x00ffU) << 8) | (((a) & 0xff00U) >> 8))
-#    define cpu_to_le32(a) ((((a) & 0x000000ffU) << 24) |\
-		            (((a) & 0x0000ff00U) <<  8) |\
-		            (((a) & 0x00ff0000U) >>  8) |\
-		            (((a) & 0xff000000U) >> 24))
-#  else
-#    define cpu_to_le16(a)      (a)
-#    define cpu_to_le32(a)      (a)
-#  endif  /* __powerpc__ */
-#  include <asm/segment.h>
-#else
-#  include <asm/uaccess.h>
-#  include <linux/init.h>
-#endif  /* LINUX_VERSION_CODE */
 #define TWIDDLE(a) (u_short)le16_to_cpu(get_unaligned((u_short *)(a)))
 
 /*
@@ -538,8 +520,8 @@
 ** insmod de4x5 args='eth1:fdx autosense=BNC eth0:autosense=100Mb'.
 **
 ** For a compiled in driver, place e.g.
-** #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP"
-** somewhere in this file above this point.
+**     #define DE4X5_PARM "eth0:fdx autosense=AUI eth2:autosense=TP"
+** here
 */
 #ifdef DE4X5_PARM
 static char *args = DE4X5_PARM;
@@ -592,6 +574,12 @@
 #define DE4X5_NAME_LENGTH 8
 
 /*
+** Ethernet PROM defines for DC21040
+*/
+#define PROBE_LENGTH    32
+#define ETH_PROM_SIG    0xAA5500FFUL
+
+/*
 ** PCI Bus defines
 */
 #define PCI_MAX_BUS_NUM      8
@@ -912,6 +900,7 @@
 static int     EISA_signature(char *name, s32 eisa_id);
 static int     PCI_signature(char *name, struct bus_type *lp);
 static void    DevicePresent(u_long iobase);
+static void    enet_addr_rst(u_long aprom_addr);
 static int     de4x5_bad_srom(struct bus_type *lp);
 static short   srom_rd(u_long address, u_char offset);
 static void    srom_latch(u_int command, u_long address);
@@ -981,11 +970,9 @@
 				     int (*init)(struct device *));
 static int count_adapters(void);
 static int loading_module = 1;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
 MODULE_PARM(de4x5_debug, "i");
 MODULE_PARM(dec_only, "i");
 MODULE_PARM(args, "s");
-#endif /* LINUX_VERSION_CODE */
 # else
 static int loading_module = 0;
 #endif /* MODULE */
@@ -1507,9 +1494,7 @@
 	    cli();
 	    test_and_set_bit(0, (void*)&dev->tbusy);
 	    load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
-#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
  	    lp->stats.tx_bytes += skb->len;
-#endif
 	    outl(POLL_DEMAND, DE4X5_TPD);/* Start the TX */
 		
 	    lp->tx_new = (++lp->tx_new) % lp->txRingSize;
@@ -1559,9 +1544,7 @@
       printk("%s: Re-entering the interrupt handler.\n", dev->name);
 	
     DISABLE_IRQs;                        /* Ensure non re-entrancy */
-#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
     synchronize_irq();
-#endif
     dev->interrupt = MASK_INTERRUPTS;
 	
     for (limit=0; limit<8; limit++) {
@@ -1659,9 +1642,7 @@
 		    
 		    /* Update stats */
 		    lp->stats.rx_packets++;
-#if	LINUX_VERSION_CODE >= ((2 << 16) | (1 << 8))
  		    lp->stats.rx_bytes += pkt_len;
-#endif
 		    de4x5_local_stats(dev, skb->data, pkt_len);
 		}
 	    }
@@ -2042,7 +2023,7 @@
 	    outl(0x00006000, PCI_CFLT);
 	    outl(iobase, PCI_CBIO);
 	    
-	    DevicePresent(DE4X5_APROM);
+	    DevicePresent(EISA_APROM);
 	    if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
 		dev->irq = irq;
 		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
@@ -2081,11 +2062,12 @@
 __initfunc(static void
 pci_probe(struct device *dev, u_long ioaddr))
 {
-    u_char pb, pbus, dev_num, dnum, dev_fn, timer, tirq;
-    u_short dev_id, vendor, index, status;
-    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE;
+    u_char pb, pbus, dev_num, dnum, dev_fn, timer;
+    u_short vendor, index, status;
+    u_int irq = 0, device, class = DE4X5_CLASS_CODE;
     u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     struct bus_type *lp = &bus;
+    struct pci_dev *pdev;
 
     if (lastPCI == NO_MORE_PCI) return;
 
@@ -2109,96 +2091,77 @@
 	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 index++) {
 	dev_num = PCI_SLOT(dev_fn);
-	if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
-#ifdef __sparc_v9__
-	    struct pci_dev *pdev;
-	    for (pdev = pci_devices; pdev; pdev = pdev->next) {
-		if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
-	    }
-#endif
-	    device = 0;
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
-	    device = dev_id;
-	    device <<= 8;
-	    if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
-		continue;
-	    }
-
-	    /* Search for an SROM on this bus */
-	    if (lp->bus_num != pb) {
-		lp->bus_num = pb;
-		srom_search(index);
-	    }
+	if ((pbus || dnum) && ((pbus != pb) || (dnum != dev_num))) continue;
+	for (pdev = pci_devices; pdev; pdev = pdev->next) {
+	    if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
+	}
 
-	    /* Get the chip configuration revision register */
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+	vendor = pdev->vendor;
+	device = pdev->device << 8;
+	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue;
 
-	    /* Set the device number information */
-	    lp->device = dev_num;
+	/* Search for an SROM on this bus */
+	if (lp->bus_num != pb) {
 	    lp->bus_num = pb;
+	    srom_search(index);
+	}
+
+	/* Get the chip configuration revision register */
+	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
+
+	/* Set the device number information */
+	lp->device = dev_num;
+	lp->bus_num = pb;
 	    
-	    /* Set the chipset information */
-	    if (is_DC2114x) device |= (cfrv & CFRV_RN);
-	    lp->chipset = device;
+	/* Set the chipset information */
+	if (is_DC2114x) device |= (cfrv & CFRV_RN);
+	lp->chipset = device;
 
-	    /* Get the board I/O address (64 bits on sparc64) */
-#ifndef __sparc_v9__
-	    pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
-	    iobase = tmp;
-#else
-	    iobase = pdev->base_address[0];
-#endif
-	    iobase &= CBIO_MASK;
+	/* Get the board I/O address (64 bits on sparc64) */
+	iobase = pdev->base_address[0] & CBIO_MASK;
 
-	    /* Fetch the IRQ to be used */
-#ifndef __sparc_v9__
-	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq);
-	    irq = tirq;
-#else
-	    irq = pdev->irq;
-#endif
-	    if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
+	/* Fetch the IRQ to be used */
+	irq = pdev->irq;
+	if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
 	    
-	    /* Check if I/O accesses and Bus Mastering are enabled */
-	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	/* Check if I/O accesses and Bus Mastering are enabled */
+	pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
 #ifdef __powerpc__
-	    if (!(status & PCI_COMMAND_IO)) {
-		status |= PCI_COMMAND_IO;
-		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
-		pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
-	    }
+	if (!(status & PCI_COMMAND_IO)) {
+	    status |= PCI_COMMAND_IO;
+	    pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	}
 #endif /* __powerpc__ */
-	    if (!(status & PCI_COMMAND_IO)) continue;
+	if (!(status & PCI_COMMAND_IO)) continue;
 
-	    if (!(status & PCI_COMMAND_MASTER)) {
-		status |= PCI_COMMAND_MASTER;
-		pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
-		pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
-	    }
-	    if (!(status & PCI_COMMAND_MASTER)) continue;
+	if (!(status & PCI_COMMAND_MASTER)) {
+	    status |= PCI_COMMAND_MASTER;
+	    pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+	    pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+	}
+	if (!(status & PCI_COMMAND_MASTER)) continue;
 
-	    /* Check the latency timer for values >= 0x60 */
-	    pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer);
-	    if (timer < 0x60) {
-		pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60);
-	    }
+	/* Check the latency timer for values >= 0x60 */
+	pcibios_read_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, &timer);
+	if (timer < 0x60) {
+	    pcibios_write_config_byte(pb, PCI_DEVICE, PCI_LATENCY_TIMER, 0x60);
+	}
 
-	    DevicePresent(DE4X5_APROM);
-	    if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
-		dev->irq = irq;
-		if ((status = de4x5_hw_init(dev, iobase)) == 0) {
-		    num_de4x5s++;
-		    if (loading_module) {
-			link_modules(lastModule, dev);
-			lastPCI = index;
-		    }
-		    return;
+	DevicePresent(DE4X5_APROM);
+	if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
+	    dev->irq = irq;
+	    if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+		num_de4x5s++;
+		if (loading_module) {
+		    link_modules(lastModule, dev);
+		    lastPCI = index;
 		}
-	    } else if (ioaddr != 0) {
-		printk("%s: region already allocated at 0x%04lx.\n", dev->name,
-		       iobase);
+		return;
 	    }
+	} else if (ioaddr != 0) {
+	    printk("%s: region already allocated at 0x%04lx.\n", dev->name,
+		   iobase);
 	}
     }
 
@@ -2216,12 +2179,13 @@
 __initfunc(static void
 srom_search(int index))
 {
-    u_char pb, dev_fn, tirq;
-    u_short dev_id, dev_num, vendor, status;
-    u_int tmp, irq = 0, device, class = DE4X5_CLASS_CODE;
+    u_char pb, dev_fn;
+    u_short dev_num, vendor, status;
+    u_int irq = 0, device, class = DE4X5_CLASS_CODE;
     u_long iobase = 0;                     /* Clear upper 32 bits in Alphas */
     int i, j;
     struct bus_type *lp = &bus;
+    struct pci_dev *pdev;
 
     for (; 
 	 (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
@@ -2229,20 +2193,13 @@
 
 	if (lp->bus_num != pb) return;
 	dev_num = PCI_SLOT(dev_fn);
-#ifdef __sparc_v9__
-	struct pci_dev *pdev;
 	for (pdev = pci_devices; pdev; pdev = pdev->next) {
 	    if ((pdev->bus->number == pb) && (pdev->devfn == dev_fn)) break;
 	}
-#endif
-	device = 0;
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
-	device = dev_id;
-	device <<= 8;
-	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) {
-	    continue;
-	}
+
+	vendor = pdev->vendor;
+	device = pdev->device << 8;
+	if (!(is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x)) continue;
 
 	/* Get the chip configuration revision register */
 	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_REVISION_ID, &cfrv);
@@ -2256,21 +2213,10 @@
 	lp->chipset = device;
 
 	/* Get the board I/O address (64 bits on sparc64) */
-#ifndef __sparc_v9__
-	pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &tmp);
-	iobase = tmp;
-#else
-	iobase = pdev->base_address[0];
-#endif
-	iobase &= CBIO_MASK;
+	iobase = pdev->base_address[0] & CBIO_MASK;
 
 	/* Fetch the IRQ to be used */
-#ifndef __sparc_v9__
-	pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &tirq);
-	irq = tirq;
-#else
 	irq = pdev->irq;
-#endif
 	if ((irq == 0) || (irq == 0xff) || ((int)irq == -1)) continue;
 	    
 	/* Check if I/O accesses are enabled */
@@ -3982,7 +3928,11 @@
     struct bus_type *lp = &bus;
     
     if (lp->chipset == DC21040) {
-	outl(0, aprom_addr);           /* Reset Ethernet Address ROM Pointer */
+	if (lp->bus == EISA) {
+	    enet_addr_rst(aprom_addr); /* Reset Ethernet Address ROM Pointer */
+	} else {
+	    outl(0, aprom_addr);       /* Reset Ethernet Address ROM Pointer */
+	}
     } else {                           /* Read new srom */
 	u_short tmp, *p = (short *)((char *)&lp->srom + SROM_HWADD);
 	for (i=0; i<(ETH_ALEN>>1); i++) {
@@ -4006,6 +3956,45 @@
 }
 
 /*
+** Since the write on the Enet PROM register doesn't seem to reset the PROM
+** pointer correctly (at least on my DE425 EISA card), this routine should do
+** it...from depca.c.
+*/
+static void
+enet_addr_rst(u_long aprom_addr)
+{
+    union {
+	struct {
+	    u32 a;
+	    u32 b;
+	} llsig;
+	char Sig[sizeof(u32) << 1];
+    } dev;
+    short sigLength=0;
+    s8 data;
+    int i, j;
+    
+    dev.llsig.a = ETH_PROM_SIG;
+    dev.llsig.b = ETH_PROM_SIG;
+    sigLength = sizeof(u32) << 1;
+    
+    for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+	data = inb(aprom_addr);
+	if (dev.Sig[j] == data) {    /* track signature */
+	    j++;
+	} else {                     /* lost signature; begin search again */
+	    if (data == dev.Sig[0]) {  /* rare case.... */
+		j=1;
+	    } else {
+	j=0;
+	    }
+	}
+    }
+    
+    return;
+}
+
+/*
 ** For the bad status case and no SROM, then add one to the previous
 ** address. However, need to add one backwards in case we have 0xff
 ** as one or more of the bytes. Only the last 3 bytes should be checked
@@ -5665,9 +5654,7 @@
 #define LP(a) ((struct de4x5_private *)(a))
 static struct device *mdev = NULL;
 static int io=0x0;/* EDIT THIS LINE FOR YOUR CONFIGURATION IF NEEDED        */
-#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,0)
 MODULE_PARM(io, "i");
-#endif /* LINUX_VERSION_CODE */
 
 int
 init_module(void)
@@ -5730,16 +5717,18 @@
 static int
 count_adapters(void)
 {
-    int i, j;
+    int i, j=0;
     char name[DE4X5_STRLEN];
-    u_char pb, dev_fn, dev_num;
-    u_short dev_id, vendor;
+    u_char pb, dev_fn;
+    u_short vendor;
     u_int class = DE4X5_CLASS_CODE;
     u_int device;
+    struct pci_dev *pdev;
+
 #ifndef __sparc_v9__
     u_long iobase = 0x1000;
 
-    for (j=0, i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
+    for (i=1; i<MAX_EISA_SLOTS; i++, iobase+=EISA_SLOT_INC) {
 	if (EISA_signature(name, EISA_ID)) j++;
     }
 #endif
@@ -5748,12 +5737,12 @@
     for (i=0; 
 	 (pcibios_find_class(class, i, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
 	 i++) {
-	dev_num = PCI_SLOT(dev_fn);
-	device = 0;
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
-	pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &dev_id);
-	device = dev_id;
-	device <<= 8;
+	for (pdev = pci_devices; pdev; pdev = pdev->next) {
+	    if ((pdev->bus->number==pb) && (pdev->devfn==dev_fn)) break;
+	}
+
+	vendor = pdev->vendor;
+	device = pdev->device << 8;
 	if (is_DC21040 || is_DC21041 || is_DC21140 || is_DC2114x) j++;
     }
 
diff -u --recursive --new-file v2.1.89/linux/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- v2.1.89/linux/drivers/net/eepro100.c	Mon Feb 23 18:12:05 1998
+++ linux/drivers/net/eepro100.c	Mon Mar 16 14:17:29 1998
@@ -1348,7 +1348,7 @@
 #if (LINUX_VERSION_CODE >= VERSION(1,3,44))
 			if (! rx_in_place) {
 				skb_reserve(skb, 2);	/* 16 byte align the data fields */
-#if defined(__i386)   &&  notyet
+#if defined(__i386__)   &&  notyet
 				/* Packet is in one chunk -- we can copy + cksum. */
 				eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
 									pkt_len, 0);
diff -u --recursive --new-file v2.1.89/linux/drivers/net/hp100.c linux/drivers/net/hp100.c
--- v2.1.89/linux/drivers/net/hp100.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/hp100.c	Tue Mar 17 21:21:43 1998
@@ -1,51 +1,77 @@
 /*
-   ** hp100.c 
-   ** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
-   **
-   ** $Id: hp100.c,v 1.14 1997/11/16 13:57:28 alan Exp $
-   **
-   ** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
-   ** Extended for new busmaster capable chipsets by 
-   ** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
-   **
-   ** Maintained by: Jaroslav Kysela <perex@jcu.cz>
-   ** 
-   ** This driver has only been tested with
-   ** -- HP J2585B 10/100 Mbit/s PCI Busmaster
-   ** -- HP J2585A 10/100 Mbit/s PCI 
-   ** -- HP J2970  10 Mbit/s PCI Combo 10base-T/BNC
-   ** -- HP J2973  10 Mbit/s PCI 10base-T
-   ** -- HP J2573  10/100 ISA
-   ** -- Compex ReadyLink ENET100-VG4  10/100 Mbit/s PCI / EISA
-   ** 
-   ** but it should also work with the other CASCADE based adapters.
-   **
-   ** TODO:
-   **       -  J2573 seems to hang sometimes when in shared memory mode.
-   **       -  Mode for Priority TX
-   **       -  Check PCI registers, performance might be improved?
-   **       -  To reduce interrupt load in busmaster, one could switch off
-   **          the interrupts that are used to refill the queues whenever the
-   **          queues are filled up to more than a certain threshold.
-   **
-   **
-   ** This source/code is public free; you can distribute it and/or modify 
-   ** it under terms of the GNU General Public License (published by the
-   ** Free Software Foundation) either version two of this License, or any 
-   ** later version.
-   **
- */
+** hp100.c 
+** HP CASCADE Architecture Driver for 100VG-AnyLan Network Adapters
+**
+** $Id: hp100.c,v 1.56 1998/03/04 15:23:59 perex Exp perex $
+**
+** Based on the HP100 driver written by Jaroslav Kysela <perex@jcu.cz>
+** Extended for new busmaster capable chipsets by 
+** Siegfried "Frieder" Loeffler (dg1sek) <floeff@mathematik.uni-stuttgart.de>
+**
+** Maintained by: Jaroslav Kysela <perex@jcu.cz>
+** 
+** This driver has only been tested with
+** -- HP J2585B 10/100 Mbit/s PCI Busmaster
+** -- HP J2585A 10/100 Mbit/s PCI 
+** -- HP J2970  10 Mbit/s PCI Combo 10base-T/BNC
+** -- HP J2973  10 Mbit/s PCI 10base-T
+** -- HP J2573  10/100 ISA
+** -- Compex ReadyLink ENET100-VG4  10/100 Mbit/s PCI / EISA
+** -- Compex FreedomLine 100/VG  10/100 Mbit/s ISA / EISA / PCI
+** 
+** but it should also work with the other CASCADE based adapters.
+**
+** TODO:
+**       -  J2573 seems to hang sometimes when in shared memory mode.
+**       -  Mode for Priority TX
+**       -  Check PCI registers, performance might be improved?
+**       -  To reduce interrupt load in busmaster, one could switch off
+**          the interrupts that are used to refill the queues whenever the
+**          queues are filled up to more than a certain threshold.
+**       -  some updates for EISA version of card
+**
+**
+** This source/code is public free; you can distribute it and/or modify 
+** it under terms of the GNU General Public License (published by the
+** Free Software Foundation) either version two of this License, or any 
+** later version.
+**
+** 1.55 -> 1.56
+**   - removed printk in misc. interrupt and update statistics to allow
+**     monitoring of card status
+**   - timing changes in xmit routines, relogin to 100VG hub added when
+**     driver does reset
+**   - included fix for Compex FreedomLine PCI adapter
+** 
+** 1.54 -> 1.55
+**   - fixed bad initialization in init_module
+**   - added Compex FreedomLine adapter
+**   - some fixes in card initialization
+**
+** 1.53 -> 1.54
+**   - added hardware multicast filter support (doesn't work)
+**   - little changes in hp100_sense_lan routine 
+**     - added support for Coax and AUI (J2970)
+**   - fix for multiple cards and hp100_mode parameter (insmod)
+**   - fix for shared IRQ 
+**
+** 1.52 -> 1.53
+**   - fixed bug in multicast support
+**
+*/
 
-#define HP100_DEFAULT_PRIORITY_TX 0
+#define HP100_DEFAULT_PRIORITY_TX 0 
 
 #undef HP100_DEBUG
-#undef HP100_DEBUG_B		/* Trace  */
-#undef HP100_DEBUG_BM		/* Debug busmaster code (PDL stuff) */
+#undef HP100_DEBUG_B           /* Trace  */
+#undef HP100_DEBUG_BM          /* Debug busmaster code (PDL stuff) */
+
+#undef HP100_DEBUG_TRAINING    /* Debug login-to-hub procedure */
+#undef HP100_DEBUG_TX   
+#undef HP100_DEBUG_IRQ 
+#undef HP100_DEBUG_RX 
 
-#undef HP100_DEBUG_TRAINING	/* Debug login-to-hub procedure */
-#undef HP100_DEBUG_TX
-#undef HP100_DEBUG_IRQ
-#undef HP100_DEBUG_RX
+#undef HP100_MULTICAST_FILTER  /* Need to be debugged... */
 
 #include <linux/version.h>
 #include <linux/module.h>
@@ -58,7 +84,6 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#include <linux/init.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 
@@ -67,7 +92,7 @@
 #include <linux/skbuff.h>
 
 #include <linux/types.h>
-#include <linux/config.h>	/* for CONFIG_PCI */
+#include <linux/config.h>  /* for CONFIG_PCI */
 #include <linux/delay.h>
 
 #if LINUX_VERSION_CODE < 0x020100
@@ -79,6 +104,12 @@
 typedef struct net_device_stats hp100_stats_t;
 #endif
 
+#ifndef __initfunc
+#define __initfunc(__initarg) __initarg
+#else
+#include <linux/init.h>
+#endif
+
 #include "hp100.h"
 
 /*
@@ -98,8 +129,14 @@
 #ifndef PCI_DEVICE_ID_COMPEX_ENET100VG4
 #define PCI_DEVICE_ID_COMPEX_ENET100VG4 0x0112
 #endif
+#ifndef PCI_VENDOR_ID_COMPEX2
+#define PCI_VENDOR_ID_COMPEX2 0x101a
+#endif
+#ifndef PCI_DEVICE_ID_COMPEX2_100VG
+#define PCI_DEVICE_ID_COMPEX2_100VG 0x0005
+#endif
 
-#define HP100_REGION_SIZE  0x20	/* for ioports */
+#define HP100_REGION_SIZE  0x20 /* for ioports */
 
 #define HP100_MAX_PACKET_SIZE  (1536+4)
 #define HP100_MIN_PACKET_SIZE  60
@@ -119,135 +156,167 @@
  */
 
 struct hp100_eisa_id {
-	u_int id;
-	const char *name;
-	u_char bus;
+  u_int id;
+  const char *name;
+  u_char bus;
+};
+
+struct hp100_pci_id {
+  u_short vendor;
+  u_short device;
 };
 
 struct hp100_private {
-	struct hp100_eisa_id *id;
-	u_short chip;
-	u_short soft_model;
-	u_int memory_size;
-	u_short rx_ratio;	/* 1 - 99 */
-	u_short priority_tx;	/* != 0 - priority tx */
-	u_short mode;		/* PIO, Shared Mem or Busmaster */
-	u_char bus;
-	u_char pci_bus;
-	u_char pci_device_fn;
-	short mem_mapped;	/* memory mapped access */
-	u_int *mem_ptr_virt;	/* virtual memory mapped area, maybe NULL */
-	u_int *mem_ptr_phys;	/* physical memory mapped area */
-	short lan_type;		/* 10Mb/s, 100Mb/s or -1 (error) */
-	int hub_status;		/* was login to hub successful? */
-	u_char mac1_mode;
-	u_char mac2_mode;
-	hp100_stats_t stats;
-
-	/* Rings for busmaster mode: */
-	hp100_ring_t *rxrhead;	/* Head (oldest) index into rxring */
-	hp100_ring_t *rxrtail;	/* Tail (newest) index into rxring */
-	hp100_ring_t *txrhead;	/* Head (oldest) index into txring */
-	hp100_ring_t *txrtail;	/* Tail (newest) index into txring */
-
-	hp100_ring_t rxring[MAX_RX_PDL];
-	hp100_ring_t txring[MAX_TX_PDL];
-
-	u_int *page_vaddr;	/* Virtual address of allocated page */
-	u_int *page_vaddr_algn;	/* Aligned virtual address of allocated page */
-	int rxrcommit;		/* # Rx PDLs commited to adapter */
-	int txrcommit;		/* # Tx PDLs commited to adapter */
+  struct hp100_eisa_id *id;
+  u_short chip;
+  u_short soft_model;
+  u_int memory_size; 
+  u_int virt_memory_size;
+  u_short rx_ratio;       /* 1 - 99 */
+  u_short priority_tx;    /* != 0 - priority tx */
+  u_short mode;           /* PIO, Shared Mem or Busmaster */
+  u_char bus;
+  u_char pci_bus;
+  u_char pci_device_fn;
+  short mem_mapped;	  /* memory mapped access */
+  u_int *mem_ptr_virt;    /* virtual memory mapped area, maybe NULL */
+  u_int *mem_ptr_phys;	  /* physical memory mapped area */
+  short lan_type;	  /* 10Mb/s, 100Mb/s or -1 (error) */
+  int hub_status;	  /* was login to hub successful? */
+  u_char mac1_mode;
+  u_char mac2_mode;
+  u_char hash_bytes[ 8 ];
+  hp100_stats_t stats;
+
+  /* Rings for busmaster mode: */
+  hp100_ring_t *rxrhead;  /* Head (oldest) index into rxring */
+  hp100_ring_t *rxrtail;  /* Tail (newest) index into rxring */
+  hp100_ring_t *txrhead;  /* Head (oldest) index into txring */
+  hp100_ring_t *txrtail;  /* Tail (newest) index into txring */
+
+  hp100_ring_t rxring[ MAX_RX_PDL ];
+  hp100_ring_t txring[ MAX_TX_PDL ];
+
+  u_int *page_vaddr;      /* Virtual address of allocated page */
+  u_int *page_vaddr_algn; /* Aligned virtual address of allocated page */
+  int rxrcommit;          /* # Rx PDLs commited to adapter */
+  int txrcommit;          /* # Tx PDLs commited to adapter */
 };
 
 /*
  *  variables
  */
 
-static struct hp100_eisa_id hp100_eisa_ids[] =
-{
+static struct hp100_eisa_id hp100_eisa_ids[] = {
 
   /* 10/100 EISA card with revision A Cascade chip */
-	{0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA},
+  { 0x80F1F022, "HP J2577 rev A", HP100_BUS_EISA },
 
   /* 10/100 ISA card with revision A Cascade chip */
-	{0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA},
+  { 0x50F1F022, "HP J2573 rev A", HP100_BUS_ISA },
 
   /* 10 only EISA card with Cascade chip */
-	{0x2019F022, "HP 27248B", HP100_BUS_EISA},
+  { 0x2019F022, "HP 27248B",      HP100_BUS_EISA },
 
   /* 10/100 EISA card with Cascade chip */
-	{0x4019F022, "HP J2577", HP100_BUS_EISA},
+  { 0x4019F022, "HP J2577",       HP100_BUS_EISA },
 
   /* 10/100 ISA card with Cascade chip */
-	{0x5019F022, "HP J2573", HP100_BUS_ISA},
+  { 0x5019F022, "HP J2573",       HP100_BUS_ISA },
 
   /* 10/100 PCI card - old J2585A */
-	{0x1030103c, "HP J2585A", HP100_BUS_PCI},
+  { 0x1030103c, "HP J2585A", 	    HP100_BUS_PCI },
 
   /* 10/100 PCI card - new J2585B - master capable */
-	{0x1041103c, "HP J2585B", HP100_BUS_PCI},
+  { 0x1041103c, "HP J2585B",      HP100_BUS_PCI },
 
   /* 10 Mbit Combo Adapter */
-	{0x1042103c, "HP J2970", HP100_BUS_PCI},
+  { 0x1042103c, "HP J2970",       HP100_BUS_PCI },
 
   /* 10 Mbit 10baseT Adapter */
-	{0x1040103c, "HP J2973", HP100_BUS_PCI},
+  { 0x1040103c, "HP J2973",       HP100_BUS_PCI },
 
   /* 10/100 EISA card from Compex */
-	{0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA},
+  { 0x0103180e, "ReadyLink ENET100-VG4", HP100_BUS_EISA },
 
+  /* 10/100 EISA card from Compex - FreedomLine (sq5bpf) */
+  /* Note: plhbrod@mbox.vol.cz reported that same ID have ISA */
+  /*       version of adapter, too... */
+  { 0x0104180e, "FreedomLine 100/VG", HP100_BUS_EISA },
+
+  /* 10/100 PCI card from Compex - FreedomLine
+   *
+   * I think this card doesn't like aic7178 scsi controller, but
+   * I haven't tested this much. It works fine on diskless machines.
+   *                            Jacek Lipkowski <sq5bpf@acid.ch.pw.edu.pl>
+   */
+  { 0x021211f6, "FreedomLine 100/VG", HP100_BUS_PCI },
+  
   /* 10/100 PCI card from Compex (J2585A compatible) */
-	{0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI}
+  { 0x011211f6, "ReadyLink ENET100-VG4", HP100_BUS_PCI }
+
+};
+
+#define HP100_EISA_IDS_SIZE	(sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id))
+
+static struct hp100_pci_id hp100_pci_ids[] = {
+  { PCI_VENDOR_ID_HP, 		PCI_DEVICE_ID_HP_J2585A },
+  { PCI_VENDOR_ID_HP,		PCI_DEVICE_ID_HP_J2585B },
+  { PCI_VENDOR_ID_COMPEX,	PCI_DEVICE_ID_COMPEX_ENET100VG4 },
+  { PCI_VENDOR_ID_COMPEX2,	PCI_DEVICE_ID_COMPEX2_100VG }
 };
 
+#define HP100_PCI_IDS_SIZE	(sizeof(hp100_pci_ids)/sizeof(struct hp100_pci_id))
+
 static int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
 static int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
 static int hp100_mode = 1;
 
 #ifdef LINUX_2_1
-MODULE_PARM(hp100_rx_ratio, "1i");
-MODULE_PARM(hp100_priority_tx, "1i");
-MODULE_PARM(hp100_mode, "1i");
+MODULE_PARM( hp100_rx_ratio, "1i" );
+MODULE_PARM( hp100_priority_tx, "1i" );
+MODULE_PARM( hp100_mode, "1i" );
 #endif
 
 /*
  *  prototypes
  */
 
-static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn);
-static int hp100_open(struct device *dev);
-static int hp100_close(struct device *dev);
-static int hp100_start_xmit(struct sk_buff *skb, struct device *dev);
-static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev);
-static void hp100_rx(struct device *dev);
-static hp100_stats_t *hp100_get_stats(struct device *dev);
-static void hp100_update_stats(struct device *dev);
-static void hp100_clear_stats(int ioaddr);
-static void hp100_set_multicast_list(struct device *dev);
-static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs);
-static void hp100_start_interface(struct device *dev);
-static void hp100_stop_interface(struct device *dev);
-static void hp100_load_eeprom(struct device *dev);
-static int hp100_sense_lan(struct device *dev);
-static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin);
-static int hp100_down_vg_link(struct device *dev);
-static void hp100_cascade_reset(struct device *dev, u_short enable);
-static void hp100_BM_shutdown(struct device *dev);
-static void hp100_mmuinit(struct device *dev);
-static void hp100_init_pdls(struct device *dev);
-static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u_int * pdlptr);
-static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u_int * pdlptr);
-static void hp100_rxfill(struct device *dev);
-static void hp100_hwinit(struct device *dev);
-static void hp100_clean_txring(struct device *dev);
+static int  hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn  );
+static int  hp100_open( struct device *dev );
+static int  hp100_close( struct device *dev );
+static int  hp100_start_xmit( struct sk_buff *skb, struct device *dev );
+static int  hp100_start_xmit_bm (struct sk_buff *skb, struct device *dev );
+static void hp100_rx( struct device *dev );
+static hp100_stats_t *hp100_get_stats( struct device *dev );
+static void hp100_misc_interrupt( struct device *dev );
+static void hp100_update_stats( struct device *dev );
+static void hp100_clear_stats( int ioaddr );
+static void hp100_set_multicast_list( struct device *dev);
+static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs );
+static void hp100_start_interface( struct device *dev );
+static void hp100_stop_interface( struct device *dev );
+static void hp100_load_eeprom( struct device *dev, u_short ioaddr );
+static int  hp100_sense_lan( struct device *dev );
+static int  hp100_login_to_vg_hub( struct device *dev, u_short force_relogin );
+static int  hp100_down_vg_link( struct device *dev );
+static void hp100_cascade_reset( struct device *dev, u_short enable );
+static void hp100_BM_shutdown( struct device *dev );
+static void hp100_mmuinit( struct device *dev );
+static void hp100_init_pdls( struct device *dev );
+static int  hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr);
+static int  hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u_int *pdlptr);
+static void hp100_rxfill( struct device *dev );
+static void hp100_hwinit( struct device *dev );
+static void hp100_clean_txring( struct device *dev );
 #ifdef HP100_DEBUG
-static void hp100_RegisterDump(struct device *dev);
+static void hp100_RegisterDump( struct device *dev );
 #endif
 
 /* TODO: This function should not really be needed in a good design... */
-static void wait(void)
+static void wait( void )
 {
-	udelay(1000);
+  udelay( 1000 );
 }
 
 /*
@@ -255,863 +324,984 @@
  *  These functions should - if possible - avoid doing write operations
  *  since this could cause problems when the card is not installed.
  */
-
-__initfunc(int hp100_probe(struct device *dev))
+ 
+__initfunc(int hp100_probe( struct device *dev ))
 {
-	int base_addr = dev ? dev->base_addr : 0;
-	int ioaddr = 0;
+  int base_addr = dev ? dev -> base_addr : 0;
+  int ioaddr = 0;
 #ifdef CONFIG_PCI
-	int pci_start_index = 0;
+  int pci_start_index = 0;
 #endif
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4200, TRACE);
-	printk("hp100: probe\n");
+  hp100_outw( 0x4200, TRACE );
+  printk( "hp100: %s: probe\n", dev->name );
 #endif
 
-	if (base_addr > 0xff) {	/* Check a single specified location. */
-		if (check_region(base_addr, HP100_REGION_SIZE))
-			return -EINVAL;
-		if (base_addr < 0x400)
-			return hp100_probe1(dev, base_addr, HP100_BUS_ISA, 0, 0);
-		else
-			return hp100_probe1(dev, base_addr, HP100_BUS_EISA, 0, 0);
-	} else
+  if ( base_addr > 0xff )	/* Check a single specified location. */
+    {
+      if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
+      if ( base_addr < 0x400 )
+        return hp100_probe1( dev, base_addr, HP100_BUS_ISA, 0, 0 );
+      if ( EISA_bus && base_addr >= 0x1c38 && ( (base_addr - 0x1c38) & 0x3ff ) == 0 )
+        return hp100_probe1( dev, base_addr, HP100_BUS_EISA, 0, 0 );
+#ifdef CONFIG_PCI
+      printk( "hp100: %s: You may specify card # in i/o address parameter for PCI bus...", dev->name );
+      return hp100_probe1( dev, base_addr, HP100_BUS_PCI, 0, 0 );
+#else
+      return -ENODEV;
+#endif
+    }
+  else
 #ifdef CONFIG_PCI
-	if (base_addr > 0 && base_addr < 8 + 1)
-		pci_start_index = 0x100 | (base_addr - 1);
-	else
+    if ( base_addr > 0 && base_addr < 8 + 1 )
+      pci_start_index = 0x100 | ( base_addr - 1 );
+    else
 #endif
-	if (base_addr != 0)
-		return -ENXIO;
+      if ( base_addr != 0 ) return -ENXIO;
 
-	/* at first - scan PCI bus(es) */
+  /* at first - scan PCI bus(es) */
 
 #ifdef CONFIG_PCI
-	if (pcibios_present()) {
-		int pci_index;
+  if ( pcibios_present() )
+    {
+      int pci_index;
 
 #ifdef HP100_DEBUG_PCI
-		printk("hp100: PCI BIOS is present, checking for devices..\n");
+      printk( "hp100: %s: PCI BIOS is present, checking for devices..\n", dev->name );
 #endif
-		for (pci_index = pci_start_index & 7; pci_index < 8; pci_index++) {
-			u_char pci_bus, pci_device_fn;
-			u_short pci_command;
-
-			if ((pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
-						 pci_index, &pci_bus,
-						 &pci_device_fn) != 0) &&
-			    (pcibios_find_device(PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585B,
-						 pci_index, &pci_bus,
-						 &pci_device_fn) != 0) &&
-			    (pcibios_find_device(PCI_VENDOR_ID_COMPEX, PCI_DEVICE_ID_COMPEX_ENET100VG4,
-						 pci_index, &pci_bus,
-						 &pci_device_fn) != 0))
-				break;
-
-			pcibios_read_config_dword(pci_bus, pci_device_fn,
-					    PCI_BASE_ADDRESS_0, &ioaddr);
-
-			ioaddr &= ~3;	/* remove I/O space marker in bit 0. */
-
-			if (check_region(ioaddr, HP100_REGION_SIZE))
-				continue;
-
-			pcibios_read_config_word(pci_bus, pci_device_fn,
-					      PCI_COMMAND, &pci_command);
-			if (!(pci_command & PCI_COMMAND_MASTER)) {
-#ifdef HP100_DEBUG
-				printk("hp100: PCI Master Bit has not been set. Setting...\n");
-#endif
-				pci_command |= PCI_COMMAND_MASTER;
-				pcibios_write_config_word(pci_bus, pci_device_fn,
-					       PCI_COMMAND, pci_command);
-			}
+      for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
+        {
+          u_char pci_bus, pci_device_fn;
+          u_short pci_command;
+          int pci_id_index;
+
+	  for ( pci_id_index = 0; pci_id_index < HP100_PCI_IDS_SIZE; pci_id_index++ )
+            if ( pcibios_find_device( hp100_pci_ids[ pci_id_index ].vendor,
+            			      hp100_pci_ids[ pci_id_index ].device,
+                                      pci_index, &pci_bus,
+                                      &pci_device_fn ) == 0 ) goto __pci_found;
+          break;
+
+	  __pci_found:
+          pcibios_read_config_dword( pci_bus, pci_device_fn,
+                                     PCI_BASE_ADDRESS_0, &ioaddr );
+
+          ioaddr &= ~3;    /* remove I/O space marker in bit 0. */
+
+          if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+
+          pcibios_read_config_word( pci_bus, pci_device_fn,
+                                    PCI_COMMAND, &pci_command );
+          if ( !( pci_command & PCI_COMMAND_IO ) )
+            {
+#ifdef HP100_DEBUG
+              printk( "hp100: %s: PCI I/O Bit has not been set. Setting...\n", dev->name );
+#endif
+              pci_command |= PCI_COMMAND_IO;
+              pcibios_write_config_word( pci_bus, pci_device_fn,
+                                         PCI_COMMAND, pci_command );
+            }
+          if ( !( pci_command & PCI_COMMAND_MASTER ) )
+            {
+#ifdef HP100_DEBUG
+              printk( "hp100: %s: PCI Master Bit has not been set. Setting...\n", dev->name );
+#endif
+              pci_command |= PCI_COMMAND_MASTER;
+              pcibios_write_config_word( pci_bus, pci_device_fn,
+                                         PCI_COMMAND, pci_command );
+            }
+#ifdef HP100_DEBUG
+          printk( "hp100: %s: PCI adapter found at 0x%x\n", dev->name, ioaddr );
+#endif
+	  if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn ) == 0 )
+	    return 0;
+        }
+    }
+  if ( pci_start_index > 0 ) return -ENODEV;
+#endif /* CONFIG_PCI */
+
+  /* Second: Probe all EISA possible port regions (if EISA bus present) */
+  for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
+    {
+      if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA, 0, 0 ) == 0 ) return 0;
+    }
+
+  /* Third Probe all ISA possible port regions */
+  for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
+    {
+      if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+      if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA, 0, 0 ) == 0 ) return 0;
+    }
+
+  return -ENODEV;
+}
+
+
+__initfunc(static int hp100_probe1( struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn ))
+{
+  int i;
+
+  u_char uc, uc_1;
+  u_int eisa_id;
+  u_int chip;
+  u_int memory_size = 0, virt_memory_size = 0;
+  u_short local_mode, lsw;
+  short mem_mapped;
+  u_int *mem_ptr_phys, *mem_ptr_virt;
+  struct hp100_private *lp;
+  struct hp100_eisa_id *eid;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4201, TRACE );
+  printk("hp100: %s: probe1\n",dev->name);
+#endif
+
+  if ( dev == NULL )
+    {
+#ifdef HP100_DEBUG
+      printk( "hp100_probe1: %s: dev == NULL ?\n", dev->name );
+#endif
+      return EIO;
+    }
+  
+  if ( hp100_inw( HW_ID ) != HP100_HW_ID_CASCADE ) 
+    {
+      return -ENODEV;
+    }
+  else
+    {
+      chip = hp100_inw( PAGING ) & HP100_CHIPID_MASK;
+#ifdef HP100_DEBUG
+      if ( chip == HP100_CHIPID_SHASTA )
+        printk("hp100: %s: Shasta Chip detected. (This is a pre 802.12 chip)\n", dev->name);
+      else if ( chip == HP100_CHIPID_RAINIER )
+        printk("hp100: %s: Rainier Chip detected. (This is a pre 802.12 chip)\n", dev->name);
+      else if ( chip == HP100_CHIPID_LASSEN )
+        printk("hp100: %s: Lassen Chip detected.\n", dev->name);
+      else
+        printk("hp100: %s: Warning: Unknown CASCADE chip (id=0x%.4x).\n",dev->name,chip);
+#endif 
+    }
+
+  dev->base_addr = ioaddr;
+
+  hp100_page( ID_MAC_ADDR );
+  for ( i = uc = eisa_id = 0; i < 4; i++ )
+    {
+      eisa_id >>= 8;
+      uc_1 = hp100_inb( BOARD_ID + i );
+      eisa_id |= uc_1 << 24;
+      uc += uc_1;
+    }
+  uc += hp100_inb( BOARD_ID + 4 );
+
+  if ( uc != 0xff )    /* bad checksum? */
+    {
+      printk("hp100_probe: %s: bad EISA ID checksum at base port 0x%x\n", dev->name, ioaddr );
+      return -ENODEV;
+    }
+
+  for ( i=0; i < HP100_EISA_IDS_SIZE; i++)
+    if ( hp100_eisa_ids[ i ].id == eisa_id )
+      break;
+  if ( i >= HP100_EISA_IDS_SIZE ) {
+    for ( i = 0; i < HP100_EISA_IDS_SIZE; i++)
+      if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) )
+        break;
+    if ( i >= HP100_EISA_IDS_SIZE ) {
+      printk( "hp100_probe: %s: card at port 0x%x isn't known (id = 0x%x)\n", dev -> name, ioaddr, eisa_id );
+        return -ENODEV;
+    }
+  }
+  eid = &hp100_eisa_ids[ i ];
+  if ( ( eid->id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
+    {
+      printk( "hp100_probe: %s: newer version of card %s at port 0x%x - unsupported\n",
+	      dev->name, eid->name, ioaddr );
+      return -ENODEV;
+    }
+
+  for ( i = uc = 0; i < 7; i++ )
+    uc += hp100_inb( LAN_ADDR + i );
+  if ( uc != 0xff )
+    {
+      printk("hp100_probe: %s: bad lan address checksum (card %s at port 0x%x)\n",
+	     dev->name, eid->name, ioaddr );
+      return -EIO;
+    }
+
+  /* Make sure, that all registers are correctly updated... */
+
+  hp100_load_eeprom( dev, ioaddr );
+  wait();
+
+  /*
+   * Determine driver operation mode
+   *
+   * Use the variable "hp100_mode" upon insmod or as kernel parameter to
+   * force driver modes:
+   * hp100_mode=1 -> default, use busmaster mode if configured.
+   * hp100_mode=2 -> enable shared memory mode 
+   * hp100_mode=3 -> force use of i/o mapped mode.
+   * hp100_mode=4 -> same as 1, but re-set the enable bit on the card.
+   */
+
+  /*
+   * LSW values:
+   *   0x2278 -> J2585B, PnP shared memory mode
+   *   0x2270 -> J2585B, shared memory mode, 0xdc000
+   *   0xa23c -> J2585B, I/O mapped mode
+   *   0x2240 -> EISA COMPEX, BusMaster (Shasta Chip)
+   *   0x2220 -> EISA HP, I/O (Shasta Chip)
+   *   0x2260 -> EISA HP, BusMaster (Shasta Chip)
+   */
+
+#if 0
+  local_mode = 0x2270;
+  hp100_outw(0xfefe,OPTION_LSW);
+  hp100_outw(local_mode|HP100_SET_LB|HP100_SET_HB,OPTION_LSW);
+#endif
+
+  /* hp100_mode value maybe used in future by another card */
+  local_mode=hp100_mode;
+  if ( local_mode < 1 || local_mode > 4 )
+    local_mode = 1;		/* default */
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: original LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
+
+  if(local_mode==3)
+    {
+      hp100_outw(HP100_MEM_EN|HP100_RESET_LB, OPTION_LSW);
+      hp100_outw(HP100_IO_EN|HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
+      printk("hp100: %s: IO mapped mode forced.\n", dev->name);
+    }
+  else if(local_mode==2)
+    {
+      hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_IO_EN |HP100_SET_LB, OPTION_LSW);
+      hp100_outw(HP100_BM_WRITE|HP100_BM_READ|HP100_RESET_HB, OPTION_LSW);
+      printk("hp100: %s: Shared memory mode requested.\n", dev->name);
+    } 
+  else if(local_mode==4)
+    {
+      if(chip==HP100_CHIPID_LASSEN)
+	{
+	  hp100_outw(HP100_BM_WRITE|
+		     HP100_BM_READ | HP100_SET_HB, OPTION_LSW);
+	  hp100_outw(HP100_IO_EN   | 
+		     HP100_MEM_EN  | HP100_RESET_LB, OPTION_LSW);
+	  printk("hp100: %s: Busmaster mode requested.\n",dev->name);
+	}
+      local_mode=1;
+    }
+		
+  if(local_mode==1) /* default behaviour */
+    {
+      lsw = hp100_inw(OPTION_LSW);
+    
+      if ( (lsw & HP100_IO_EN) &&
+	   (~lsw & HP100_MEM_EN) &&
+	   (~lsw & (HP100_BM_WRITE|HP100_BM_READ)) )
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: IO_EN bit is set on card.\n",dev->name);
+#endif
+	  local_mode=3;
+	}
+      else if ( chip == HP100_CHIPID_LASSEN &&
+	        ( lsw & (HP100_BM_WRITE|HP100_BM_READ) ) ==
+	                (HP100_BM_WRITE|HP100_BM_READ) )
+	{
+	  printk("hp100: %s: Busmaster mode enabled.\n",dev->name);
+	  hp100_outw(HP100_MEM_EN|HP100_IO_EN|HP100_RESET_LB, OPTION_LSW);
+	}
+      else
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: Card not configured for BM or BM not supported with this card.\n", dev->name );
+	  printk("hp100: %s: Trying shared memory mode.\n", dev->name);
+#endif
+	  /* In this case, try shared memory mode */
+	  local_mode=2;
+	  hp100_outw(HP100_MEM_EN|HP100_SET_LB, OPTION_LSW);
+	  /* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */
+	}
+    }
+
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: new LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
+
+  /* Check for shared memory on the card, eventually remap it */
+  hp100_page( HW_MAP );
+  mem_mapped = (( hp100_inw( OPTION_LSW ) & ( HP100_MEM_EN ) ) != 0);
+  mem_ptr_phys = mem_ptr_virt = NULL;
+  memory_size = (8192<<( (hp100_inb(SRAM)>>5)&0x07));
+  virt_memory_size = 0;
+
+  /* For memory mapped or busmaster mode, we want the memory address */
+  if ( mem_mapped || (local_mode==1))
+    {
+      mem_ptr_phys = (u_int *)( hp100_inw( MEM_MAP_LSW ) | 
+				( hp100_inw( MEM_MAP_MSW ) << 16 ) );
+      (u_int)mem_ptr_phys &= ~0x1fff;  /* 8k alignment */
+
+      if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 )
+        {
+	  printk("hp100: %s: Can only use programmed i/o mode.\n", dev->name);
+          mem_ptr_phys = NULL;
+          mem_mapped = 0;
+	  local_mode=3; /* Use programmed i/o */
+        }
+						
+      /* We do not need access to shared memory in busmaster mode */
+      /* However in slave mode we need to remap high (>1GB) card memory  */
+      if(local_mode!=1) /* = not busmaster */
+	{
+	  if ( bus == HP100_BUS_PCI && mem_ptr_phys >= (u_int *)0x100000 )
+	    {
+	      /* We try with smaller memory sizes, if ioremap fails */
+	      for(virt_memory_size = memory_size; virt_memory_size>16383; virt_memory_size>>=1)
+		{
+		  if((mem_ptr_virt=ioremap((u_long)mem_ptr_phys,virt_memory_size))==NULL)
+		    {
 #ifdef HP100_DEBUG
-			printk("hp100: PCI adapter found at 0x%x\n", ioaddr);
+		      printk( "hp100: %s: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys );
 #endif
-			if (hp100_probe1(dev, ioaddr, HP100_BUS_PCI, pci_bus, pci_device_fn) == 0)
-				return 0;
-		}
-	}
-	if (pci_start_index > 0)
-		return -ENODEV;
-#endif				/* CONFIG_PCI */
-
-	/* Second: Probe all EISA possible port regions (if EISA bus present) */
-	for (ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400) {
-		if (check_region(ioaddr, HP100_REGION_SIZE))
-			continue;
-		if (hp100_probe1(dev, ioaddr, HP100_BUS_EISA, 0, 0) == 0)
-			return 0;
-	}
-
-	/* Third Probe all ISA possible port regions */
-	for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
-		if (check_region(ioaddr, HP100_REGION_SIZE))
-			continue;
-		if (hp100_probe1(dev, ioaddr, HP100_BUS_ISA, 0, 0) == 0)
-			return 0;
-	}
-
-	return -ENODEV;
-}
-
-
-__initfunc(static int hp100_probe1(struct device *dev, int ioaddr, u_char bus, u_char pci_bus, u_char pci_device_fn))
-{
-	int i;
-
-	u_char uc, uc_1;
-	u_int eisa_id;
-	u_int chip;
-	u_int memory_size = 0;
-	short mem_mapped;
-	u_int *mem_ptr_phys, *mem_ptr_virt;
-	struct hp100_private *lp;
-	struct hp100_eisa_id *eid;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4201, TRACE);
-	printk("hp100: probe1\n");
-#endif
-
-	if (dev == NULL) {
-#ifdef HP100_DEBUG
-		printk("hp100_probe1: dev == NULL ?\n");
-#endif
-		return EIO;
-	}
-	if (hp100_inw(HW_ID) != HP100_HW_ID_CASCADE) {
-		return -ENODEV;
-	} else {
-		chip = hp100_inw(PAGING) & HP100_CHIPID_MASK;
-#ifdef HP100_DEBUG
-		if (chip == HP100_CHIPID_SHASTA)
-			printk("hp100: Shasta Chip detected. (This is a pre 802.12 chip)\n");
-		else if (chip == HP100_CHIPID_RAINIER)
-			printk("hp100: Rainier Chip detected. (This is a pre 802.12 chip)\n");
-		else if (chip == HP100_CHIPID_LASSEN)
-			printk("hp100: Lassen Chip detected.\n");
-		else
-			printk("hp100: Warning: Unknown CASCADE chip (id=0x%.4x).\n", chip);
-#endif
-	}
-
-	dev->base_addr = ioaddr;
-
-	hp100_page(ID_MAC_ADDR);
-	for (i = uc = eisa_id = 0; i < 4; i++) {
-		eisa_id >>= 8;
-		uc_1 = hp100_inb(BOARD_ID + i);
-		eisa_id |= uc_1 << 24;
-		uc += uc_1;
-	}
-	uc += hp100_inb(BOARD_ID + 4);
-
-	if (uc != 0xff) {	/* bad checksum? */
-		printk("hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr);
-		return -ENODEV;
-	}
-	for (i = 0; i < sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id); i++)
-		if ((hp100_eisa_ids[i].id & 0xf0ffffff) == (eisa_id & 0xf0ffffff))
-			break;
-	if (i >= sizeof(hp100_eisa_ids) / sizeof(struct hp100_eisa_id)) {
-		printk("hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id);
-		return -ENODEV;
-	}
-	eid = &hp100_eisa_ids[i];
-	if ((eid->id & 0x0f000000) < (eisa_id & 0x0f000000)) {
-		printk("hp100_probe1: newer version of card %s at port 0x%x - unsupported\n",
-		       eid->name, ioaddr);
-		return -ENODEV;
-	}
-	for (i = uc = 0; i < 7; i++)
-		uc += hp100_inb(LAN_ADDR + i);
-	if (uc != 0xff) {
-		printk("hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n",
-		       eid->name, ioaddr);
-		return -EIO;
-	}
-	/* Determine driver operation mode
-
-	 * Use the variable "hp100_mode" upon insmod or as kernel parameter to
-	 * force driver modes:
-	 * hp100_mode=1 -> default, use busmaster mode if configured.
-	 * hp100_mode=2 -> enable shared memory mode 
-	 * hp100_mode=3 -> force use of i/o mapped mode.
-	 * hp100_mode=4 -> same as 1, but re-set the enable bit on the card.
-	 */
-
-	if (hp100_mode == 3) {
-		hp100_outw(HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW);
-		hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
-		hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW);
-		printk("hp100: IO mapped mode forced.\n");
-	} else if (hp100_mode == 2) {
-		hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW);
-		hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
-		hp100_outw(HP100_BM_WRITE | HP100_BM_READ | HP100_RESET_HB, OPTION_LSW);
-		printk("hp100: Shared memory mode requested.\n");
-	} else if (hp100_mode == 4) {
-		if (chip == HP100_CHIPID_LASSEN) {
-			hp100_outw(HP100_BM_WRITE |
-			       HP100_BM_READ | HP100_SET_HB, OPTION_LSW);
-			hp100_outw(HP100_IO_EN |
-			      HP100_MEM_EN | HP100_RESET_LB, OPTION_LSW);
-			printk("hp100: Busmaster mode requested.\n");
-		}
-		hp100_mode = 1;
-	}
-	if (hp100_mode == 1) {	/* default behaviour */
-		if ((hp100_inw(OPTION_LSW) & HP100_IO_EN) &&
-		    (~hp100_inw(OPTION_LSW) & HP100_MEM_EN) &&
-		    (~hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ))
-		    ) {
-#ifdef HP100_DEBUG
-			printk("hp100: IO_EN bit is set on card.\n");
-#endif
-			hp100_mode = 3;
-		} else if ((chip == HP100_CHIPID_LASSEN) &&
-			   ((hp100_inw(OPTION_LSW) & (HP100_BM_WRITE | HP100_BM_READ)) ==
-			    (HP100_BM_WRITE | HP100_BM_READ))) {
-			printk("hp100: Busmaster mode enabled.\n");
-			hp100_outw(HP100_MEM_EN | HP100_IO_EN | HP100_RESET_LB, OPTION_LSW);
-		} else {
-#ifdef HP100_DEBUG
-			printk("hp100: Card not configured for BM or BM not supported with this card. Trying shared memory mode.\n");
-#endif
-			/* In this case, try shared memory mode */
-			hp100_mode = 2;
-			hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW);
-			/* hp100_outw(HP100_IO_EN|HP100_RESET_LB, OPTION_LSW); */
-		}
-	}
-	/* Check for shared memory on the card, eventually remap it */
-	hp100_page(HW_MAP);
-	mem_mapped = ((hp100_inw(OPTION_LSW) & (HP100_MEM_EN)) != 0);
-	mem_ptr_phys = mem_ptr_virt = NULL;
-	memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07));
-
-	/* For memory mapped or busmaster mode, we want the memory address */
-	if (mem_mapped || (hp100_mode == 1)) {
-		mem_ptr_phys = (u_int *) (hp100_inw(MEM_MAP_LSW) |
-					  (hp100_inw(MEM_MAP_MSW) << 16));
-		(u_int) mem_ptr_phys &= ~0x1fff;	/* 8k alignment */
-
-		if (bus == HP100_BUS_ISA && ((u_long) mem_ptr_phys & ~0xfffff) != 0) {
-			printk("hp100: Can only use programmed i/o mode.\n");
-			mem_ptr_phys = NULL;
-			mem_mapped = 0;
-			hp100_mode = 3;		/* Use programmed i/o */
-		}
-		/* We do not need access to shared memory in busmaster mode */
-		/* However in slave mode we need to remap high (>1GB) card memory  */
-		if (hp100_mode != 1) {	/* = not busmaster */
-			if (bus == HP100_BUS_PCI) {
-				/* We try with smaller memory sizes, if ioremap fails */
-				for (; memory_size > 16383; memory_size = memory_size / 2) {
-					if ((mem_ptr_virt = ioremap((u_long) mem_ptr_phys, memory_size)) == NULL) {
-#ifdef HP100_DEBUG
-						printk("hp100: ioremap for 0x%x bytes high PCI memory at 0x%lx failed\n", memory_size, (u_long) mem_ptr_phys);
-#endif
-					} else {
-#ifdef HP100_DEBUG
-						printk("hp100: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", memory_size, (u_long) mem_ptr_phys, (u_long) mem_ptr_virt);
-#endif
-						break;
-					}
-				}
-
-				if (mem_ptr_virt == NULL) {	/* all ioremap tries failed */
-					printk("hp100: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n");
-					hp100_mode = 3;
-					memory_size = (8192 << ((hp100_inb(SRAM) >> 5) & 0x07));
-				}
-			}
+		    }	
+		  else
+		    {
+#ifdef HP100_DEBUG
+		      printk( "hp100: %s: remapped 0x%x bytes high PCI memory at 0x%lx to 0x%lx.\n", dev->name, virt_memory_size, (u_long)mem_ptr_phys, (u_long)mem_ptr_virt);
+#endif
+		      break;
+		    }
 		}
-	}
-	if (hp100_mode == 3) {	/* io mapped forced */
-		mem_mapped = 0;
-		mem_ptr_phys = mem_ptr_virt = NULL;
-		printk("hp100: Using (slow) programmed i/o mode.\n");
-	}
-	/* Initialise the "private" data structure for this card. */
-	if ((dev->priv = kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL)
-		 return -ENOMEM;
-	memset(dev->priv, 0, sizeof(struct hp100_private));
-
-	lp = (struct hp100_private *) dev->priv;
-	lp->id = eid;
-	lp->chip = chip;
-	lp->mode = hp100_mode;
-	lp->pci_bus = pci_bus;
-	lp->bus = bus;
-	lp->pci_device_fn = pci_device_fn;
-	lp->priority_tx = hp100_priority_tx;
-	lp->rx_ratio = hp100_rx_ratio;
-	lp->mem_ptr_phys = mem_ptr_phys;
-	lp->mem_ptr_virt = mem_ptr_virt;
-	hp100_page(ID_MAC_ADDR);
-	lp->soft_model = hp100_inb(SOFT_MODEL);
-	lp->mac1_mode = HP100_MAC1MODE3;
-	lp->mac2_mode = HP100_MAC2MODE3;
-
-	dev->base_addr = ioaddr;
-
-	lp->memory_size = memory_size;
-	lp->rx_ratio = hp100_rx_ratio;	/* can be conf'd with insmod */
-
-	/* memory region for programmed i/o */
-	request_region(dev->base_addr, HP100_REGION_SIZE, eid->name);
-
-	dev->open = hp100_open;
-	dev->stop = hp100_close;
-
-	if (lp->mode == 1)	/* busmaster */
-		dev->hard_start_xmit = hp100_start_xmit_bm;
-	else
-		dev->hard_start_xmit = hp100_start_xmit;
-
-	dev->get_stats = hp100_get_stats;
-	dev->set_multicast_list = &hp100_set_multicast_list;
-
-	/* Ask the card for which IRQ line it is configured */
-	hp100_page(HW_MAP);
-	dev->irq = hp100_inb(IRQ_CHANNEL) & HP100_IRQMASK;
-	if (dev->irq == 2)
-		dev->irq = 9;
-
-	if (lp->mode == 1)	/* busmaster */
-		dev->dma = 4;
-
-	/* Ask the card for its MAC address and store it for later use. */
-	hp100_page(ID_MAC_ADDR);
-	for (i = uc = 0; i < 6; i++)
-		dev->dev_addr[i] = hp100_inb(LAN_ADDR + i);
-
-	/* Reset statistics (counters) */
-	hp100_clear_stats(ioaddr);
-
-	ether_setup(dev);
-
-	/* If busmaster mode is wanted, a dma-capable memory area is needed for
-	 * the rx and tx PDLs 
-	 * PCI cards can access the whole PC memory. Therefore GFP_DMA is not
-	 * needed for the allocation of the memory area. 
-	 */
-
-	/* TODO: We do not need this with old cards, where PDLs are stored
-	 * in the cards shared memory area. But currently, busmaster has been
-	 * implemented/tested only with the lassen chip anyway... */
-	if (lp->mode == 1) {	/* busmaster */
-		/* Get physically continous memory for TX & RX PDLs    */
-		if ((lp->page_vaddr = kmalloc(MAX_RINGSIZE + 0x0f, GFP_KERNEL)) == NULL)
-			return -ENOMEM;
-		lp->page_vaddr_algn = ((u_int *) (((u_int) (lp->page_vaddr) + 0x0f) & ~0x0f));
-		memset(lp->page_vaddr, 0, MAX_RINGSIZE + 0x0f);
+														
+	      if(mem_ptr_virt==NULL) /* all ioremap tries failed */
+		{
+		  printk("hp100: %s: Failed to ioremap the PCI card memory. Will have to use i/o mapped mode.\n", dev->name);
+		  local_mode=3;
+		  virt_memory_size = 0;
+		}
+	    }
+	}
+						
+    }
+
+  if(local_mode==3) /* io mapped forced */
+    {
+      mem_mapped = 0;
+      mem_ptr_phys = mem_ptr_virt = NULL;
+      printk("hp100: %s: Using (slow) programmed i/o mode.\n", dev->name);
+    }
+
+  /* Initialise the "private" data structure for this card. */
+  if ( (dev->priv=kmalloc(sizeof(struct hp100_private), GFP_KERNEL)) == NULL)
+    return -ENOMEM;
+  memset( dev->priv, 0, sizeof(struct hp100_private) );
+
+  lp = (struct hp100_private *)dev->priv;
+  lp->id = eid;
+  lp->chip = chip;
+  lp->mode = local_mode;
+  lp->pci_bus = pci_bus;
+  lp->bus = bus;
+  lp->pci_device_fn = pci_device_fn;
+  lp->priority_tx = hp100_priority_tx;
+  lp->rx_ratio = hp100_rx_ratio;
+  lp->mem_ptr_phys = mem_ptr_phys;
+  lp->mem_ptr_virt = mem_ptr_virt;
+  hp100_page( ID_MAC_ADDR );
+  lp->soft_model = hp100_inb( SOFT_MODEL );
+  lp->mac1_mode = HP100_MAC1MODE3;
+  lp->mac2_mode = HP100_MAC2MODE3;
+  memset( &lp->hash_bytes, 0x00, 8 );
+
+  dev->base_addr = ioaddr;
+
+  lp->memory_size = memory_size;
+  lp->virt_memory_size = virt_memory_size;
+  lp->rx_ratio = hp100_rx_ratio; /* can be conf'd with insmod */
+
+  /* memory region for programmed i/o */
+  request_region( dev->base_addr, HP100_REGION_SIZE, eid->name );
+
+  dev->open = hp100_open;
+  dev->stop = hp100_close;
+
+  if (lp->mode==1) /* busmaster */
+    dev->hard_start_xmit = hp100_start_xmit_bm;
+  else
+    dev->hard_start_xmit = hp100_start_xmit;
+
+  dev->get_stats = hp100_get_stats;
+  dev->set_multicast_list = &hp100_set_multicast_list;
+
+  /* Ask the card for which IRQ line it is configured */
+  hp100_page( HW_MAP );
+  dev->irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQMASK;
+  if ( dev->irq == 2 )
+    dev->irq = 9;
+
+  if(lp->mode==1) /* busmaster */
+    dev->dma=4; 
+
+  /* Ask the card for its MAC address and store it for later use. */
+  hp100_page( ID_MAC_ADDR );
+  for ( i = uc = 0; i < 6; i++ )
+    dev->dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
+
+  /* Reset statistics (counters) */
+  hp100_clear_stats( ioaddr );
+
+  ether_setup( dev );
+
+  /* If busmaster mode is wanted, a dma-capable memory area is needed for
+   * the rx and tx PDLs 
+   * PCI cards can access the whole PC memory. Therefore GFP_DMA is not
+   * needed for the allocation of the memory area. 
+   */
+
+  /* TODO: We do not need this with old cards, where PDLs are stored
+   * in the cards shared memory area. But currently, busmaster has been
+   * implemented/tested only with the lassen chip anyway... */
+  if(lp->mode==1) /* busmaster */
+    {
+      /* Get physically continous memory for TX & RX PDLs    */
+      if ( (lp->page_vaddr=kmalloc(MAX_RINGSIZE+0x0f,GFP_KERNEL) ) == NULL)
+        return -ENOMEM;
+      lp->page_vaddr_algn=((u_int *) ( ((u_int)(lp->page_vaddr)+0x0f) &~0x0f));
+      memset(lp->page_vaddr, 0, MAX_RINGSIZE+0x0f);
 
 #ifdef HP100_DEBUG_BM
-		printk("hp100: Reserved DMA memory from 0x%x to 0x%x\n",
-		       (u_int) lp->page_vaddr_algn,
-		       (u_int) lp->page_vaddr_algn + MAX_RINGSIZE);
-#endif
-		lp->rxrcommit = lp->txrcommit = 0;
-		lp->rxrhead = lp->rxrtail = &(lp->rxring[0]);
-		lp->txrhead = lp->txrtail = &(lp->txring[0]);
-	}
-	/* Initialise the card. */
-	/* (I'm not really sure if it's a good idea to do this during probing, but 
-	 * like this it's assured that the lan connection type can be sensed
-	 * correctly)
-	 */
-	hp100_hwinit(dev);
-
-	/* Try to find out which kind of LAN the card is connected to. */
-	lp->lan_type = hp100_sense_lan(dev);
-
-	/* Print out a message what about what we think we have probed. */
-	printk("hp100: %s: %s at 0x%x, IRQ %d, ",
-	       dev->name, lp->id->name, ioaddr, dev->irq);
-	switch (bus) {
-	case HP100_BUS_EISA:
-		printk("EISA");
-		break;
-	case HP100_BUS_PCI:
-		printk("PCI");
-		break;
-	default:
-		printk("ISA");
-		break;
-	}
-	printk(" bus, %dk SRAM (rx/tx %d%%).\n",
-	       lp->memory_size >> 10, lp->rx_ratio);
+      printk("hp100: %s: Reserved DMA memory from 0x%x to 0x%x\n",
+      	     dev->name,
+             (u_int)lp->page_vaddr_algn,
+             (u_int)lp->page_vaddr_algn+MAX_RINGSIZE);
+#endif
+      lp->rxrcommit  = lp->txrcommit = 0;
+      lp->rxrhead    = lp->rxrtail   = &(lp->rxring[0]);
+      lp->txrhead    = lp->txrtail   = &(lp->txring[0]); 
+    }
+
+  /* Initialise the card. */
+  /* (I'm not really sure if it's a good idea to do this during probing, but 
+   * like this it's assured that the lan connection type can be sensed
+   * correctly)
+   */
+  hp100_hwinit( dev );
+
+  /* Try to find out which kind of LAN the card is connected to. */
+  lp->lan_type = hp100_sense_lan( dev );
+     
+  /* Print out a message what about what we think we have probed. */
+  printk( "hp100: %s: %s at 0x%x, IRQ %d, ",
+          dev->name, lp->id->name, ioaddr, dev->irq );
+  switch ( bus ) {
+  case HP100_BUS_EISA: printk( "EISA" ); break;
+  case HP100_BUS_PCI:  printk( "PCI" );  break;
+  default:     printk( "ISA" );  break;
+  }
+  printk( " bus, %dk SRAM (rx/tx %d%%).\n",
+          lp->memory_size >> 10, lp->rx_ratio );
+
+  if ( lp->mode==2 ) /* memory mapped */ 
+    {
+      printk( "hp100: %s: Memory area at 0x%lx-0x%lx",
+              dev->name,(u_long)mem_ptr_phys,
+              ((u_long)mem_ptr_phys+(mem_ptr_phys>(u_int *)0x100000?(u_long)lp->memory_size:16*1024))-1 );
+      if ( mem_ptr_virt )
+	printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
+      printk( ".\n" );
+
+      /* Set for info when doing ifconfig */
+      dev->mem_start = (u_long)mem_ptr_phys;
+      dev->mem_end = (u_long)mem_ptr_phys+(u_long)lp->memory_size;
+    }
+  printk( "hp100: %s: ", dev->name );
+  if ( lp->lan_type != HP100_LAN_ERR )
+    printk( "Adapter is attached to " );
+  switch ( lp->lan_type ) {
+  case HP100_LAN_100:
+    printk( "100Mb/s Voice Grade AnyLAN network.\n" );
+    break;
+  case HP100_LAN_10:
+    printk( "10Mb/s network.\n" );
+    break;
+  default:
+    printk( "Warning! Link down.\n" );
+  }
 
-	if (lp->mode == 2) {	/* memory mapped */
-		printk("%s: Memory area at 0x%lx-0x%lx",
-		       dev->name, (u_long) mem_ptr_phys, (u_long) mem_ptr_phys + (u_long) lp->memory_size);
-		if (mem_ptr_virt)
-			printk(" (virtual base 0x%lx)", (u_long) mem_ptr_virt);
-		printk(".\n");
-
-		/* Set for info when doing ifconfig */
-		dev->mem_start = (u_long) mem_ptr_phys;
-		dev->mem_end = (u_long) mem_ptr_phys + (u_long) lp->memory_size;
-	}
-	printk("%s: ", dev->name);
-	if (lp->lan_type != HP100_LAN_ERR)
-		printk("Adapter is attached to ");
-	switch (lp->lan_type) {
-	case HP100_LAN_100:
-		printk("100Mb/s Voice Grade AnyLAN network.\n");
-		break;
-	case HP100_LAN_10:
-		printk("10Mb/s network.\n");
-		break;
-	default:
-		printk("Warning! Link down.\n");
-	}
-	return 0;
+  return 0;
 }
-
 
+
 /* This procedure puts the card into a stable init state */
-static void hp100_hwinit(struct device *dev)
+static void hp100_hwinit( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4202, TRACE);
-	printk("hp100: hwinit\n");
+  hp100_outw( 0x4202, TRACE );
+  printk("hp100: %s: hwinit\n", dev->name);
 #endif
 
-	/* Initialise the card. -------------------------------------------- */
-
-	/* Clear all pending Ints and disable Ints */
-	hp100_page(PERFORMANCE);
-	hp100_outw(0xfefe, IRQ_MASK);	/* mask off all ints */
-	hp100_outw(0xffff, IRQ_STATUS);		/* clear all pending ints */
-
-	hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
-	hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
-
-	if (lp->mode == 1) {
-		hp100_BM_shutdown(dev);		/* disables BM, puts cascade in reset */
-		wait();
-	} else {
-		hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
-		hp100_cascade_reset(dev, TRUE);
-		hp100_page(MAC_CTRL);
-		hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1);
-	}
-
-	/* Initiate EEPROM reload */
-	hp100_load_eeprom(dev);
-
-	wait();
-
-	/* Go into reset again. */
-	hp100_cascade_reset(dev, TRUE);
-
-	/* Set Option Registers to a safe state  */
-	hp100_outw(HP100_DEBUG_EN |
-		   HP100_RX_HDR |
-		   HP100_EE_EN |
-		   HP100_BM_WRITE |
-		   HP100_BM_READ | HP100_RESET_HB |
-		   HP100_FAKE_INT |
-		   HP100_INT_EN |
-		   HP100_MEM_EN |
-		   HP100_IO_EN | HP100_RESET_LB, OPTION_LSW);
-
-	hp100_outw(HP100_TRI_INT |
-		   HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW);
-
-	hp100_outb(HP100_PRIORITY_TX |
-		   HP100_ADV_NXT_PKT |
-		   HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW);
-
-	/* TODO: Configure MMU for Ram Test. */
-	/* TODO: Ram Test. */
-
-	/* Re-check if adapter is still at same i/o location      */
-	/* (If the base i/o in eeprom has been changed but the    */
-	/* registers had not been changed, a reload of the eeprom */
-	/* would move the adapter to the address stored in eeprom */
-
-	/* TODO: Code to implement. */
-
-	/* Until here it was code from HWdiscover procedure. */
-	/* Next comes code from mmuinit procedure of SCO BM driver which is
-	 * called from HWconfigure in the SCO driver.  */
-
-	/* Initialise MMU, eventually switch on Busmaster Mode, initialise 
-	 * multicast filter...
-	 */
-	hp100_mmuinit(dev);
-
-	/* We don't turn the interrupts on here - this is done by start_interface. */
-	wait();			/* TODO: Do we really need this? */
-
-	/* Enable Hardware (e.g. unreset) */
-	hp100_cascade_reset(dev, FALSE);
-
-	/* ------- initialisation complete ----------- */
-
-	/* Finally try to log in the Hub if there may be a VG connection. */
-	if (lp->lan_type != HP100_LAN_10)
-		hp100_login_to_vg_hub(dev, FALSE);	/* relogin */
+  /* Initialise the card. -------------------------------------------- */
+
+  /* Clear all pending Ints and disable Ints */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* clear all pending ints */
+
+  hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
+  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW );
+
+  if(lp->mode==1)
+    { 
+      hp100_BM_shutdown( dev ); /* disables BM, puts cascade in reset */
+      wait();
+    }
+  else
+    {
+      hp100_outw( HP100_INT_EN | HP100_RESET_LB, OPTION_LSW );
+      hp100_cascade_reset( dev, TRUE );
+      hp100_page( MAC_CTRL );
+      hp100_andb( ~(HP100_RX_EN|HP100_TX_EN), MAC_CFG_1); 
+    }
+		
+  /* Initiate EEPROM reload */
+  hp100_load_eeprom( dev, 0 );
+		
+  wait();
+		
+  /* Go into reset again. */
+  hp100_cascade_reset( dev, TRUE );
+		
+  /* Set Option Registers to a safe state  */
+  hp100_outw( HP100_DEBUG_EN |
+              HP100_RX_HDR   |
+              HP100_EE_EN    |
+              HP100_BM_WRITE |
+              HP100_BM_READ  | HP100_RESET_HB |
+              HP100_FAKE_INT |
+              HP100_INT_EN   |
+	      HP100_MEM_EN   |
+              HP100_IO_EN    | HP100_RESET_LB, OPTION_LSW);
+		
+  hp100_outw( HP100_TRI_INT  |
+              HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+
+  hp100_outb( HP100_PRIORITY_TX |
+              HP100_ADV_NXT_PKT |
+              HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW );
+
+  /* TODO: Configure MMU for Ram Test. */
+  /* TODO: Ram Test. */
+
+  /* Re-check if adapter is still at same i/o location      */
+  /* (If the base i/o in eeprom has been changed but the    */
+  /* registers had not been changed, a reload of the eeprom */
+  /* would move the adapter to the address stored in eeprom */
+  
+  /* TODO: Code to implement. */
+		
+  /* Until here it was code from HWdiscover procedure. */
+  /* Next comes code from mmuinit procedure of SCO BM driver which is
+   * called from HWconfigure in the SCO driver.  */
+
+  /* Initialise MMU, eventually switch on Busmaster Mode, initialise 
+   * multicast filter...
+   */
+  hp100_mmuinit( dev );
+
+  /* We don't turn the interrupts on here - this is done by start_interface. */
+  wait(); /* TODO: Do we really need this? */
+
+  /* Enable Hardware (e.g. unreset) */
+  hp100_cascade_reset( dev, FALSE );
+
+  /* ------- initialisation complete ----------- */
+
+  /* Finally try to log in the Hub if there may be a VG connection. */
+  if( lp->lan_type != HP100_LAN_10 )
+    hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
 }
-
 
+
 /* 
  * mmuinit - Reinitialise Cascade MMU and MAC settings.
  * Note: Must already be in reset and leaves card in reset. 
  */
-static void hp100_mmuinit(struct device *dev)
+static void hp100_mmuinit( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	int i;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4203, TRACE);
-	printk("hp100: mmuinit\n");
-#endif
-
-#ifdef HP100_DEBUG
-	if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) {
-		printk("hp100: Not in reset when entering mmuinit. Fix me.\n");
-		return;
-	}
-#endif
-
-	/* Make sure IRQs are masked off and ack'ed. */
-	hp100_page(PERFORMANCE);
-	hp100_outw(0xfefe, IRQ_MASK);	/* mask off all ints */
-	hp100_outw(0xffff, IRQ_STATUS);		/* ack IRQ */
-
-	/*
-	 * Enable Hardware 
-	 * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En
-	 * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable
-	 * - Clear Priority, Advance Pkt and Xmit Cmd
-	 */
-
-	hp100_outw(HP100_DEBUG_EN |
-		   HP100_RX_HDR |
-		   HP100_EE_EN | HP100_RESET_HB |
-		   HP100_IO_EN |
-		   HP100_FAKE_INT |
-		   HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
-
-	hp100_outw(HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
-
-	if (lp->mode == 1) {	/* busmaster */
-		hp100_outw(HP100_BM_WRITE |
-			   HP100_BM_READ |
-			   HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW);
-	} else if (lp->mode == 2) {	/* memory mapped */
-		hp100_outw(HP100_BM_WRITE |
-			   HP100_BM_READ | HP100_RESET_HB, OPTION_LSW);
-		hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW);
-		hp100_outw(HP100_MEM_EN | HP100_SET_LB, OPTION_LSW);
-		hp100_outw(HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
-	} else if (lp->mode == 3) {	/* i/o mapped mode */
-		hp100_outw(HP100_MMAP_DIS | HP100_SET_HB |
-			   HP100_IO_EN | HP100_SET_LB, OPTION_LSW);
-	}
-	hp100_page(HW_MAP);
-	hp100_outb(0, EARLYRXCFG);
-	hp100_outw(0, EARLYTXCFG);
-
-	/*
-	 * Enable Bus Master mode
-	 */
-	if (lp->mode == 1) {	/* busmaster */
-		/* Experimental: Set some PCI configuration bits */
-		hp100_page(HW_MAP);
-		hp100_andb(~HP100_PDL_USE3, MODECTRL1);		/* BM engine read maximum */
-		hp100_andb(~HP100_TX_DUALQ, MODECTRL1);		/* No Queue for Priority TX */
-
-		/* PCI Bus failures should result in a Misc. Interrupt */
-		hp100_orb(HP100_EN_BUS_FAIL, MODECTRL2);
-
-		hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW);
-		hp100_page(HW_MAP);
-		/* Use Burst Mode and switch on PAGE_CK */
-		hp100_orb(HP100_BM_BURST_RD |
-			  HP100_BM_BURST_WR, BM);
-		if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA))
-			hp100_orb(HP100_BM_PAGE_CK, BM);
-		hp100_orb(HP100_BM_MASTER, BM);
-	} else {		/* not busmaster */
-		hp100_page(HW_MAP);
-		hp100_andb(~HP100_BM_MASTER, BM);
-	}
-
-	/*
-	 * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs
-	 */
-	hp100_page(MMU_CFG);
-	if (lp->mode == 1) {	/* only needed for Busmaster */
-		int xmit_stop, recv_stop;
-
-		if ((lp->chip == HP100_CHIPID_RAINIER) || (lp->chip == HP100_CHIPID_SHASTA)) {
-			int pdl_stop;
-
-			/*
-			 * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and
-			 * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded
-			 * to the next higher 1k boundary) bytes for the rx-pdl's
-			 * Note: For non-etr chips the transmit stop register must be
-			 * programmed on a 1k boundary, i.e. bits 9:0 must be zero. 
-			 */
-			pdl_stop = lp->memory_size;
-			xmit_stop = (pdl_stop - 508 * (MAX_RX_PDL) - 16) & ~(0x03ff);
-			recv_stop = (xmit_stop * (lp->rx_ratio) / 100) & ~(0x03ff);
-			hp100_outw((pdl_stop >> 4) - 1, PDL_MEM_STOP);
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int i;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4203, TRACE );
+  printk("hp100: %s: mmuinit\n",dev->name);
+#endif
+
+#ifdef HP100_DEBUG
+  if( 0!=(hp100_inw(OPTION_LSW)&HP100_HW_RST) )
+    {
+      printk("hp100: %s: Not in reset when entering mmuinit. Fix me.\n",dev->name);
+      return;
+    }
+#endif
+
+  /* Make sure IRQs are masked off and ack'ed. */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+
+  /*
+   * Enable Hardware 
+   * - Clear Debug En, Rx Hdr Pipe, EE En, I/O En, Fake Int and Intr En
+   * - Set Tri-State Int, Bus Master Rd/Wr, and Mem Map Disable
+   * - Clear Priority, Advance Pkt and Xmit Cmd
+   */
+
+  hp100_outw( HP100_DEBUG_EN |
+              HP100_RX_HDR   |
+              HP100_EE_EN    | HP100_RESET_HB |
+              HP100_IO_EN    |
+              HP100_FAKE_INT |
+              HP100_INT_EN   | HP100_RESET_LB, OPTION_LSW );
+  	
+  hp100_outw( HP100_TRI_INT | HP100_SET_HB, OPTION_LSW);
+		
+  if(lp->mode==1) /* busmaster */
+    {
+      hp100_outw( HP100_BM_WRITE | 
+		  HP100_BM_READ  |
+		  HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+    }
+  else if(lp->mode==2) /* memory mapped */
+    {
+      hp100_outw( HP100_BM_WRITE |
+		  HP100_BM_READ  | HP100_RESET_HB, OPTION_LSW );
+      hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
+      hp100_outw( HP100_MEM_EN | HP100_SET_LB, OPTION_LSW );
+      hp100_outw( HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
+    }
+  else if( lp->mode==3 ) /* i/o mapped mode */
+    {
+      hp100_outw( HP100_MMAP_DIS | HP100_SET_HB | 
+                  HP100_IO_EN    | HP100_SET_LB, OPTION_LSW );
+    }
+
+  hp100_page( HW_MAP );
+  hp100_outb( 0, EARLYRXCFG );
+  hp100_outw( 0, EARLYTXCFG );
+  
+  /*
+   * Enable Bus Master mode
+   */
+  if(lp->mode==1) /* busmaster */
+    {
+      /* Experimental: Set some PCI configuration bits */
+      hp100_page( HW_MAP );
+      hp100_andb( ~HP100_PDL_USE3, MODECTRL1 ); /* BM engine read maximum */
+      hp100_andb( ~HP100_TX_DUALQ, MODECTRL1 ); /* No Queue for Priority TX */
+
+      /* PCI Bus failures should result in a Misc. Interrupt */
+      hp100_orb( HP100_EN_BUS_FAIL, MODECTRL2);
+					
+      hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_SET_HB, OPTION_LSW );
+      hp100_page( HW_MAP );					
+      /* Use Burst Mode and switch on PAGE_CK */
+      hp100_orb( HP100_BM_BURST_RD |
+                 HP100_BM_BURST_WR, BM);
+      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
+	hp100_orb( HP100_BM_PAGE_CK, BM );
+      hp100_orb( HP100_BM_MASTER, BM );		
+    }
+  else /* not busmaster */
+    {
+      hp100_page(HW_MAP);
+      hp100_andb(~HP100_BM_MASTER, BM );
+    }
+
+  /*
+   * Divide card memory into regions for Rx, Tx and, if non-ETR chip, PDLs
+   */  
+  hp100_page( MMU_CFG );
+  if(lp->mode==1) /* only needed for Busmaster */
+    {
+      int xmit_stop, recv_stop;
+
+      if((lp->chip==HP100_CHIPID_RAINIER)||(lp->chip==HP100_CHIPID_SHASTA))
+        {
+          int pdl_stop;
+          
+          /*
+           * Each pdl is 508 bytes long. (63 frags * 4 bytes for address and
+           * 4 bytes for for header). We will leave NUM_RXPDLS * 508 (rounded
+           * to the next higher 1k boundary) bytes for the rx-pdl's
+	   * Note: For non-etr chips the transmit stop register must be
+	   * programmed on a 1k boundary, i.e. bits 9:0 must be zero. 
+	   */
+          pdl_stop  = lp->memory_size;
+          xmit_stop = ( pdl_stop-508*(MAX_RX_PDL)-16 )& ~(0x03ff);
+          recv_stop = ( xmit_stop * (lp->rx_ratio)/100 ) &~(0x03ff);
+          hp100_outw( (pdl_stop>>4)-1, PDL_MEM_STOP );
 #ifdef HP100_DEBUG_BM
-			printk("hp100: PDL_STOP = 0x%x\n", pdl_stop);
+          printk("hp100: %s: PDL_STOP = 0x%x\n", dev->name, pdl_stop);
 #endif
-		} else {	/* ETR chip (Lassen) in busmaster mode */
-			xmit_stop = (lp->memory_size) - 1;
-			recv_stop = ((lp->memory_size * lp->rx_ratio) / 100) & ~(0x03ff);
-		}
+        }
+      else /* ETR chip (Lassen) in busmaster mode */
+        {
+          xmit_stop = ( lp->memory_size ) - 1;
+          recv_stop = ( ( lp->memory_size * lp->rx_ratio ) / 100 ) & ~(0x03ff);
+        }
 
-		hp100_outw(xmit_stop >> 4, TX_MEM_STOP);
-		hp100_outw(recv_stop >> 4, RX_MEM_STOP);
+      hp100_outw( xmit_stop>>4 , TX_MEM_STOP );
+      hp100_outw( recv_stop>>4 , RX_MEM_STOP );
 #ifdef HP100_DEBUG_BM
-		printk("hp100: TX_STOP  = 0x%x\n", xmit_stop >> 4);
-		printk("hp100: RX_STOP  = 0x%x\n", recv_stop >> 4);
+      printk("hp100: %s: TX_STOP  = 0x%x\n",dev->name,xmit_stop>>4);
+      printk("hp100: %s: RX_STOP  = 0x%x\n",dev->name,recv_stop>>4);
 #endif
-	} else {		/* Slave modes (memory mapped and programmed io)  */
-		hp100_outw((((lp->memory_size * lp->rx_ratio) / 100) >> 4), RX_MEM_STOP);
-		hp100_outw(((lp->memory_size - 1) >> 4), TX_MEM_STOP);
-#ifdef HP100_DEBUG
-		printk("hp100: TX_MEM_STOP: 0x%x\n", hp100_inw(TX_MEM_STOP));
-		printk("hp100: RX_MEM_STOP: 0x%x\n", hp100_inw(RX_MEM_STOP));
-#endif
-	}
-
-	/* Write MAC address into page 1 */
-	hp100_page(MAC_ADDRESS);
-	for (i = 0; i < 6; i++)
-		hp100_outb(dev->dev_addr[i], MAC_ADDR + i);
-
-	/* Zero the multicast hash registers */
-	for (i = 0; i < 8; i++)
-		hp100_outb(0x0, HASH_BYTE0 + i);
-
-	/* Set up MAC defaults */
-	hp100_page(MAC_CTRL);
-
-	/* Go to LAN Page and zero all filter bits */
-	/* Zero accept error, accept multicast, accept broadcast and accept */
-	/* all directed packet bits */
-	hp100_andb(~(HP100_RX_EN |
-		     HP100_TX_EN |
-		     HP100_ACC_ERRORED |
-		     HP100_ACC_MC |
-		     HP100_ACC_BC |
-		     HP100_ACC_PHY), MAC_CFG_1);
-
-	hp100_outb(0x00, MAC_CFG_2);
-
-	/* Zero the frame format bit. This works around a training bug in the */
-	/* new hubs. */
-	hp100_outb(0x00, VG_LAN_CFG_2);		/* (use 802.3) */
-
-	if (lp->priority_tx)
-		hp100_outb(HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW);
-	else
-		hp100_outb(HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW);
-
-	hp100_outb(HP100_ADV_NXT_PKT |
-		   HP100_TX_CMD | HP100_RESET_LB, OPTION_MSW);
-
-	/* If busmaster, initialize the PDLs */
-	if (lp->mode == 1)
-		hp100_init_pdls(dev);
-
-	/* Go to performance page and initalize isr and imr registers */
-	hp100_page(PERFORMANCE);
-	hp100_outw(0xfefe, IRQ_MASK);	/* mask off all ints */
-	hp100_outw(0xffff, IRQ_STATUS);		/* ack IRQ */
+    }  
+  else /* Slave modes (memory mapped and programmed io)  */
+    {
+      hp100_outw( (((lp->memory_size*lp->rx_ratio)/100)>>4), RX_MEM_STOP );
+      hp100_outw( ((lp->memory_size - 1 )>>4), TX_MEM_STOP );  
+#ifdef HP100_DEBUG
+      printk("hp100: %s: TX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(TX_MEM_STOP));
+      printk("hp100: %s: RX_MEM_STOP: 0x%x\n", dev->name,hp100_inw(RX_MEM_STOP));
+#endif
+    }
+
+  /* Write MAC address into page 1 */
+  hp100_page( MAC_ADDRESS );
+  for ( i = 0; i < 6; i++ )
+    hp100_outb( dev->dev_addr[ i ], MAC_ADDR + i );
+  
+  /* Zero the multicast hash registers */
+  for ( i = 0; i < 8; i++ )
+    hp100_outb( 0x0, HASH_BYTE0 + i );  
+  
+  /* Set up MAC defaults */
+  hp100_page( MAC_CTRL );
+ 
+  /* Go to LAN Page and zero all filter bits */
+  /* Zero accept error, accept multicast, accept broadcast and accept */
+  /* all directed packet bits */
+  hp100_andb( ~(HP100_RX_EN|
+		HP100_TX_EN|
+		HP100_ACC_ERRORED|
+		HP100_ACC_MC|
+		HP100_ACC_BC|
+		HP100_ACC_PHY),   MAC_CFG_1 );
+
+  hp100_outb( 0x00, MAC_CFG_2 );
+
+  /* Zero the frame format bit. This works around a training bug in the */
+  /* new hubs. */
+  hp100_outb( 0x00, VG_LAN_CFG_2); /* (use 802.3) */	
+
+  if(lp->priority_tx)
+    hp100_outb( HP100_PRIORITY_TX | HP100_SET_LB, OPTION_MSW );
+  else
+    hp100_outb( HP100_PRIORITY_TX | HP100_RESET_LB, OPTION_MSW );
+	
+  hp100_outb( HP100_ADV_NXT_PKT |
+	      HP100_TX_CMD      | HP100_RESET_LB, OPTION_MSW );
+	
+  /* If busmaster, initialize the PDLs */
+  if(lp->mode==1)
+    hp100_init_pdls( dev );
+
+  /* Go to performance page and initalize isr and imr registers */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
 }
-
 
+
 /*
  *  open/close functions
  */
 
-static int hp100_open(struct device *dev)
+static int hp100_open( struct device *dev )
 {
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 #ifdef HP100_DEBUG_B
-	int ioaddr = dev->base_addr;
+  int ioaddr=dev->base_addr;
 #endif
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4204, TRACE);
-	printk("hp100: open\n");
+  hp100_outw( 0x4204, TRACE );
+  printk("hp100: %s: open\n",dev->name);
 #endif
+		
+  /* New: if bus is PCI or EISA, interrupts might be shared interrupts */
+  if ( request_irq(dev->irq, hp100_interrupt,
+  		   lp->bus==HP100_BUS_PCI||lp->bus==HP100_BUS_EISA?SA_SHIRQ:SA_INTERRUPT,
+  		   lp->id->name, dev))
+    {
+      printk( "hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq );
+      return -EAGAIN;
+    }
 
-	/* New: if bus is PCI or EISA, interrupts might be shared interrupts */
-	if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA)) {
-		if (request_irq(dev->irq, hp100_interrupt, SA_SHIRQ, lp->id->name, dev)) {
-			printk("%s: unable to get IRQ %d\n", dev->name, dev->irq);
-			return -EAGAIN;
-		}
-	} else if (request_irq(dev->irq, hp100_interrupt, SA_INTERRUPT, lp->id->name, dev)) {
-		printk("%s: unable to get IRQ %d\n", dev->name, dev->irq);
-		return -EAGAIN;
-	}
-	MOD_INC_USE_COUNT;
-
-	dev->tbusy = 0;
-	dev->trans_start = jiffies;
-	dev->interrupt = 0;
-	dev->start = 1;
+  MOD_INC_USE_COUNT;
 
-	lp->lan_type = hp100_sense_lan(dev);
-	lp->mac1_mode = HP100_MAC1MODE3;
-	lp->mac2_mode = HP100_MAC2MODE3;
+  dev->tbusy = 0;
+  dev->trans_start = jiffies;
+  dev->interrupt = 0;
+  dev->start = 1;
 
-	hp100_stop_interface(dev);
+  lp->lan_type = hp100_sense_lan( dev );
+  lp->mac1_mode = HP100_MAC1MODE3;
+  lp->mac2_mode = HP100_MAC2MODE3;
+  memset( &lp->hash_bytes, 0x00, 8 );
 
-	hp100_hwinit(dev);
+  hp100_stop_interface( dev );
+ 
+  hp100_hwinit( dev );
 
-	hp100_start_interface(dev);	/* sets mac modes, enables interrupts */
+  hp100_start_interface( dev ); /* sets mac modes, enables interrupts */
 
-	return 0;
+  return 0;
 }
-
 
+
 /* The close function is called when the interface is to be brought down */
-static int hp100_close(struct device *dev)
+static int hp100_close( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4205, TRACE);
-	printk("hp100:close\n");
+  hp100_outw( 0x4205, TRACE );
+  printk("hp100: %s: close\n", dev->name);
 #endif
 
-	hp100_page(PERFORMANCE);
-	hp100_outw(0xfefe, IRQ_MASK);	/* mask off all IRQs */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );    /* mask off all IRQs */
 
-	hp100_stop_interface(dev);
+  hp100_stop_interface( dev );
 
-	if (lp->lan_type == HP100_LAN_100)
-		lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
+  if ( lp->lan_type == HP100_LAN_100 ) 
+    lp->hub_status=hp100_login_to_vg_hub( dev, FALSE );
 
-	dev->tbusy = 1;
-	dev->start = 0;
+  dev->tbusy = 1;
+  dev->start = 0;
+
+  free_irq( dev->irq, dev );
+
+#ifdef HP100_DEBUG
+  printk( "hp100: %s: close LSW = 0x%x\n", dev->name, hp100_inw(OPTION_LSW) );
+#endif
 
-	if ((lp->bus == HP100_BUS_PCI) || (lp->bus == HP100_BUS_EISA))
-		free_irq(dev->irq, dev);
-	else
-		free_irq(dev->irq, NULL);
-	MOD_DEC_USE_COUNT;
-	return 0;
+  MOD_DEC_USE_COUNT;
+  return 0;
 }
-
 
+
 /*
  * Configure the PDL Rx rings and LAN 
  */
-static void hp100_init_pdls(struct device *dev)
+static void hp100_init_pdls( struct device *dev )
 {
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	hp100_ring_t *ringptr;
-	u_int *pageptr;
-	int i;
-
-#ifdef HP100_DEBUG_B
-	int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t         *ringptr;
+  u_int                *pageptr;
+  int                  i;
+
+#ifdef HP100_DEBUG_B
+  int ioaddr = dev->base_addr;
 #endif
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4206, TRACE);
-	printk("hp100: init pdls\n");
-#endif
-
-	if (0 == lp->page_vaddr_algn)
-		printk("hp100: Warning: lp->page_vaddr_algn not initialised!\n");
-	else {
-		/* pageptr shall point into the DMA accessible memory region  */
-		/* we use this pointer to status the upper limit of allocated */
-		/* memory in the allocated page. */
-		/* note: align the pointers to the pci cache line size */
-		memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE);	/* Zero  Rx/Tx ring page */
-		pageptr = lp->page_vaddr_algn;
-
-		lp->rxrcommit = 0;
-		ringptr = lp->rxrhead = lp->rxrtail = &(lp->rxring[0]);
-
-		/* Initialise Rx Ring */
-		for (i = MAX_RX_PDL - 1; i >= 0; i--) {
-			lp->rxring[i].next = ringptr;
-			ringptr = &(lp->rxring[i]);
-			pageptr += hp100_init_rxpdl(ringptr, pageptr);
-		}
-
-		/* Initialise Tx Ring */
-		lp->txrcommit = 0;
-		ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]);
-		for (i = MAX_TX_PDL - 1; i >= 0; i--) {
-			lp->txring[i].next = ringptr;
-			ringptr = &(lp->txring[i]);
-			pageptr += hp100_init_txpdl(ringptr, pageptr);
-		}
-	}
+  hp100_outw( 0x4206, TRACE );
+  printk("hp100: %s: init pdls\n", dev->name);
+#endif
+
+  if(0==lp->page_vaddr_algn)
+    printk("hp100: %s: Warning: lp->page_vaddr_algn not initialised!\n",dev->name);
+  else
+    {
+      /* pageptr shall point into the DMA accessible memory region  */
+      /* we use this pointer to status the upper limit of allocated */
+      /* memory in the allocated page. */
+      /* note: align the pointers to the pci cache line size */
+      memset(lp->page_vaddr_algn, 0, MAX_RINGSIZE); /* Zero  Rx/Tx ring page */
+      pageptr=lp->page_vaddr_algn;
+						
+      lp->rxrcommit =0;
+      ringptr = lp->rxrhead = lp-> rxrtail = &(lp->rxring[0]);
+      
+      /* Initialise Rx Ring */
+      for (i=MAX_RX_PDL-1; i>=0; i--)
+        {
+          lp->rxring[i].next = ringptr;
+          ringptr=&(lp->rxring[i]);
+          pageptr+=hp100_init_rxpdl(dev, ringptr, pageptr);
+        }
+      
+      /* Initialise Tx Ring */
+      lp->txrcommit = 0;
+      ringptr = lp->txrhead = lp->txrtail = &(lp->txring[0]);
+      for (i=MAX_TX_PDL-1; i>=0; i--)
+        {
+          lp->txring[i].next = ringptr;
+          ringptr=&(lp->txring[i]);
+          pageptr+=hp100_init_txpdl(dev, ringptr, pageptr);
+        }
+    }
 }
-
 
+
 /* These functions "format" the entries in the pdl structure   */
 /* They return how much memory the fragments need.            */
-static int hp100_init_rxpdl(register hp100_ring_t * ringptr, register u32 * pdlptr)
+static int hp100_init_rxpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr )
 {
-	/* pdlptr is starting adress for this pdl */
+  /* pdlptr is starting adress for this pdl */
 
-	if (0 != (((unsigned) pdlptr) & 0xf))
-		printk("hp100: Init rxpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr);
+  if( 0!=( ((unsigned)pdlptr) & 0xf) )
+    printk("hp100: %s: Init rxpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned)pdlptr);
 
-	ringptr->pdl = pdlptr + 1;
-	ringptr->pdl_paddr = virt_to_bus(pdlptr + 1);
-	ringptr->skb = (void *) NULL;
+  ringptr->pdl       = pdlptr+1; 
+  ringptr->pdl_paddr = virt_to_bus(pdlptr+1);
+  ringptr->skb       = (void *) NULL; 
 
-	/* 
-	 * Write address and length of first PDL Fragment (which is used for
-	 * storing the RX-Header
-	 * We use the 4 bytes _before_ the PDH in the pdl memory area to 
-	 * store this information. (PDH is at offset 0x04)
-	 */
-	/* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */
+  /* 
+   * Write address and length of first PDL Fragment (which is used for
+   * storing the RX-Header
+   * We use the 4 bytes _before_ the PDH in the pdl memory area to 
+   * store this information. (PDH is at offset 0x04)
+   */
+  /* Note that pdlptr+1 and not pdlptr is the pointer to the PDH */
 
-	*(pdlptr + 2) = (u_int) virt_to_bus(pdlptr);	/* Address Frag 1 */
-	*(pdlptr + 3) = 4;	/* Length  Frag 1 */
+  *(pdlptr+2) =(u_int) virt_to_bus(pdlptr);  /* Address Frag 1 */ 
+  *(pdlptr+3) = 4;                           /* Length  Frag 1 */
 
-	return ((((MAX_RX_FRAG * 2 + 2) + 3) / 4) * 4);
+  return( ( ((MAX_RX_FRAG*2+2)+3) /4)*4 );
 }
 
 
-static int hp100_init_txpdl(register hp100_ring_t * ringptr, register u32 * pdlptr)
+static int hp100_init_txpdl( struct device *dev, register hp100_ring_t *ringptr, register u32 *pdlptr )
 {
-	if (0 != (((unsigned) pdlptr) & 0xf))
-		printk("hp100: Init txpdl: Unaligned pdlptr 0x%x.\n", (unsigned) pdlptr);
-
-	ringptr->pdl = pdlptr;	/* +1; */
-	ringptr->pdl_paddr = virt_to_bus(pdlptr);	/* +1 */
-	ringptr->skb = (void *) NULL;
+  if( 0!=( ((unsigned)pdlptr) & 0xf) )
+    printk("hp100: %s: Init txpdl: Unaligned pdlptr 0x%x.\n",dev->name,(unsigned) pdlptr);
 
-	return ((((MAX_TX_FRAG * 2 + 2) + 3) / 4) * 4);
+  ringptr->pdl       = pdlptr; /* +1; */  
+  ringptr->pdl_paddr = virt_to_bus(pdlptr); /* +1 */
+  ringptr->skb = (void *) NULL;
+  
+  return((((MAX_TX_FRAG*2+2)+3)/4)*4);
 }
-
 
+
 /*
  * hp100_build_rx_pdl allocates an skb_buff of maximum size plus two bytes 
  * for possible odd word alignment rounding up to next dword and set PDL
@@ -1119,77 +1309,80 @@
  * Returns: 0 if unable to allocate skb_buff
  *          1 if successful
  */
-int hp100_build_rx_pdl(hp100_ring_t * ringptr, struct device *dev)
+int hp100_build_rx_pdl( hp100_ring_t *ringptr, struct device *dev )
 {
 #ifdef HP100_DEBUG_B
-	int ioaddr = dev->base_addr;
+  int ioaddr = dev->base_addr;
 #endif
 #ifdef HP100_DEBUG_BM
-	u_int *p;
+  u_int *p;
 #endif
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4207, TRACE);
-	printk("hp100: build rx pdl\n");
+  hp100_outw( 0x4207, TRACE );
+  printk("hp100: %s: build rx pdl\n", dev->name);
 #endif
 
-	/* Allocate skb buffer of maximum size */
-	/* Note: This depends on the alloc_skb functions allocating more 
-	 * space than requested, i.e. aligning to 16bytes */
-
-	ringptr->skb = dev_alloc_skb(((MAX_ETHER_SIZE + 2 + 3) / 4) * 4);
-
-	if (NULL != ringptr->skb) {
-		/* 
-		 * Reserve 2 bytes at the head of the buffer to land the IP header
-		 * on a long word boundary (According to the Network Driver section
-		 * in the Linux KHG, this should help to increase performance.)
-		 */
-		skb_reserve(ringptr->skb, 2);
-
-		ringptr->skb->dev = dev;
-		ringptr->skb->data = (u_char *) skb_put(ringptr->skb, MAX_ETHER_SIZE);
-
-		/* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */
-		/* Note: 1st Fragment is used for the 4 byte packet status
-		 * (receive header). Its PDL entries are set up by init_rxpdl. So 
-		 * here we only have to set up the PDL fragment entries for the data
-		 * part. Those 4 bytes will be stored in the DMA memory region 
-		 * directly before the PDL. 
-		 */
+  /* Allocate skb buffer of maximum size */
+  /* Note: This depends on the alloc_skb functions allocating more 
+   * space than requested, i.e. aligning to 16bytes */
+
+  ringptr->skb = dev_alloc_skb( ((MAX_ETHER_SIZE+2+3)/4)*4 );
+		
+  if(NULL!=ringptr->skb)
+    {
+      /* 
+       * Reserve 2 bytes at the head of the buffer to land the IP header
+       * on a long word boundary (According to the Network Driver section
+       * in the Linux KHG, this should help to increase performance.)
+       */
+      skb_reserve(ringptr->skb, 2);
+
+      ringptr->skb->dev=dev; 
+      ringptr->skb->data=(u_char *)skb_put(ringptr->skb, MAX_ETHER_SIZE );
+						
+      /* ringptr->pdl points to the beginning of the PDL, i.e. the PDH */
+      /* Note: 1st Fragment is used for the 4 byte packet status
+       * (receive header). Its PDL entries are set up by init_rxpdl. So 
+       * here we only have to set up the PDL fragment entries for the data
+       * part. Those 4 bytes will be stored in the DMA memory region 
+       * directly before the PDL. 
+       */
 #ifdef HP100_DEBUG_BM
-		printk("hp100: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n",
-		       (u_int) ringptr->pdl,
-		       ((MAX_ETHER_SIZE + 2 + 3) / 4) * 4,
-		       (unsigned int) ringptr->skb->data);
+      printk("hp100: %s: build_rx_pdl: PDH@0x%x, skb->data (len %d) at 0x%x\n",
+      	     dev->name,
+	     (u_int) ringptr->pdl,
+	     ((MAX_ETHER_SIZE+2+3)/4)*4,
+	     (unsigned int) ringptr->skb->data);
 #endif
 
-		ringptr->pdl[0] = 0x00020000;	/* Write PDH */
-		ringptr->pdl[3] = ((u_int) virt_to_bus(ringptr->skb->data));
-		ringptr->pdl[4] = MAX_ETHER_SIZE;	/* Length of Data */
-
+      ringptr->pdl[0] = 0x00020000;                          /* Write PDH */
+      ringptr->pdl[3] = ((u_int)virt_to_bus(ringptr->skb->data)); 
+      ringptr->pdl[4] = MAX_ETHER_SIZE;                 /* Length of Data */
+						
 #ifdef HP100_DEBUG_BM
-		for (p = (ringptr->pdl); p < (ringptr->pdl + 5); p++)
-			printk("Adr 0x%.8x = 0x%.8x\n", (u_int) p, (u_int) * p);
+      for(p=(ringptr->pdl); p<(ringptr->pdl+5); p++)
+        printk("hp100: %s: Adr 0x%.8x = 0x%.8x\n",dev->name,(u_int) p,(u_int) *p );
 #endif
-		return (1);
-	}
-	/* else: */
-	/* alloc_skb failed (no memory) -> still can receive the header
-	 * fragment into PDL memory. make PDL safe by clearing msgptr and
-	 * making the PDL only 1 fragment (i.e. the 4 byte packet status)
-	 */
+      return(1);
+    }
+  /* else: */
+  /* alloc_skb failed (no memory) -> still can receive the header
+   * fragment into PDL memory. make PDL safe by clearing msgptr and
+   * making the PDL only 1 fragment (i.e. the 4 byte packet status)
+   */
 #ifdef HP100_DEBUG_BM
-	printk("hp100: build_rx_pdl: PDH@0x%x, No space for skb.\n",
-	       (u_int) ringptr->pdl);
+  printk("hp100: %s: build_rx_pdl: PDH@0x%x, No space for skb.\n",
+  	 dev->name,
+	 (u_int) ringptr->pdl);
 #endif
 
-	ringptr->pdl[0] = 0x00010000;	/* PDH: Count=1 Fragment */
+  ringptr->pdl[0]=0x00010000;   /* PDH: Count=1 Fragment */
 
-	return (0);
+  return(0);
 }
-
 
+
 /*
  *  hp100_rxfill - attempt to fill the Rx Ring will empty skb's
  *
@@ -1200,246 +1393,279 @@
  *      b.  Put the physical address of the buffer into the PDL.
  *      c.  Output physical address of PDL to adapter.
  */
-static void hp100_rxfill(struct device *dev)
+static void hp100_rxfill( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
+  int ioaddr=dev->base_addr; 
 
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	hp100_ring_t *ringptr;
+  struct hp100_private  *lp      = (struct hp100_private *)dev->priv;
+  hp100_ring_t    *ringptr;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4208, TRACE);
-	printk("hp100: rxfill\n");
-#endif
-
-	hp100_page(PERFORMANCE);
-
-	while (lp->rxrcommit < MAX_RX_PDL) {
-		/*
-		   ** Attempt to get a buffer and build a Rx PDL.
-		 */
-		ringptr = lp->rxrtail;
-		if (0 == hp100_build_rx_pdl(ringptr, dev)) {
-			return;	/* None available, return */
-		}
-		/* Hand this PDL over to the card */
-		/* Note: This needs performance page selected! */
+  hp100_outw( 0x4208, TRACE );
+  printk("hp100: %s: rxfill\n",dev->name);
+#endif
+		
+  hp100_page( PERFORMANCE );
+
+  while (lp->rxrcommit < MAX_RX_PDL)
+    {
+      /*
+      ** Attempt to get a buffer and build a Rx PDL.
+      */
+      ringptr = lp->rxrtail;
+      if (0 == hp100_build_rx_pdl( ringptr, dev ))
+        {
+          return;      /* None available, return */
+        }
+      
+      /* Hand this PDL over to the card */
+      /* Note: This needs performance page selected! */
 #ifdef HP100_DEBUG_BM
-		printk("hp100: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n",
-		       lp->rxrcommit,
-		       (u_int) ringptr->pdl,
-		       (u_int) ringptr->pdl_paddr,
-		       (u_int) ringptr->pdl[3]);
+      printk("hp100: %s: rxfill: Hand to card: pdl #%d @0x%x phys:0x%x, buffer: 0x%x\n",
+      	     dev->name,
+             lp->rxrcommit,
+             (u_int)ringptr->pdl,
+             (u_int)ringptr->pdl_paddr,
+             (u_int)ringptr->pdl[3]);
 #endif
 
-		hp100_outl((u32) ringptr->pdl_paddr, RX_PDA);
-
-		lp->rxrcommit += 1;
-		lp->rxrtail = ringptr->next;
-	}
+      hp100_outl( (u32)ringptr->pdl_paddr, RX_PDA); 
+      
+      lp->rxrcommit += 1;
+      lp->rxrtail = ringptr->next;
+    }
 }
-
 
+
 /*
  * BM_shutdown - shutdown bus mastering and leave chip in reset state
  */
 
-static void hp100_BM_shutdown(struct device *dev)
+static void hp100_BM_shutdown( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	unsigned long time;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4209, TRACE);
-	printk("hp100: bm shutdown\n");
-#endif
-
-	hp100_page(PERFORMANCE);
-	hp100_outw(0xfefe, IRQ_MASK);	/* mask off all ints */
-	hp100_outw(0xffff, IRQ_STATUS);		/* Ack all ints */
-
-	/* Ensure Interrupts are off */
-	hp100_outw(HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
-
-	/* Disable all MAC activity */
-	hp100_page(MAC_CTRL);
-	hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1);	/* stop rx/tx */
-
-	/* If cascade MMU is not already in reset */
-	if (0 != (hp100_inw(OPTION_LSW) & HP100_HW_RST)) {
-		/* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so
-		 * MMU pointers will not be reset out from underneath
-		 */
-		hp100_page(MAC_CTRL);
-		for (time = 0; time < 5000; time++) {
-			if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) ==
-			    (HP100_TX_IDLE | HP100_RX_IDLE))
-				break;
-		}
-
-		/* Shutdown algorithm depends on the generation of Cascade */
-		if (lp->chip == HP100_CHIPID_LASSEN) {	/* ETR shutdown/reset */
-			/* Disable Busmaster mode and wait for bit to go to zero. */
-			hp100_page(HW_MAP);
-			hp100_andb(~HP100_BM_MASTER, BM);
-			/* 100 ms timeout */
-			for (time = 0; time < 32000; time++) {
-				if (0 == (hp100_inb(BM) & HP100_BM_MASTER))
-					break;
-			}
-		} else {	/* Shasta or Rainier Shutdown/Reset */
-			/* To ensure all bus master inloading activity has ceased,
-			 * wait for no Rx PDAs or no Rx packets on card. 
-			 */
-			hp100_page(PERFORMANCE);
-			/* 100 ms timeout */
-			for (time = 0; time < 10000; time++) {
-				/* RX_PDL: PDLs not executed. */
-				/* RX_PKT_CNT: RX'd packets on card. */
-				if ((hp100_inb(RX_PDL) == 0) &&
-				    (hp100_inb(RX_PKT_CNT) == 0))
-					break;
-			}
-
-			if (time >= 10000)
-				printk("hp100: BM shutdown error.\n");
-
-			/* To ensure all bus master outloading activity has ceased,
-			 * wait until the Tx PDA count goes to zero or no more Tx space
-			 * available in the Tx region of the card. 
-			 */
-			/* 100 ms timeout */
-			for (time = 0; time < 10000; time++) {
-				if ((0 == hp100_inb(TX_PKT_CNT)) &&
-				    (0 != (hp100_inb(TX_MEM_FREE) & HP100_AUTO_COMPARE)))
-					break;
-			}
-
-			/* Disable Busmaster mode */
-			hp100_page(HW_MAP);
-			hp100_andb(~HP100_BM_MASTER, BM);
-		}		/* end of shutdown procedure for non-etr parts */
-
-		hp100_cascade_reset(dev, TRUE);
-	}
-	hp100_page(PERFORMANCE);
-	hp100_outw(HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW);
-	/* Busmaster mode should be shut down now. */
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  unsigned long time;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4209, TRACE );
+  printk("hp100: %s: bm shutdown\n",dev->name);
+#endif
+
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS ); /* Ack all ints */
+
+  /* Ensure Interrupts are off */
+  hp100_outw( HP100_INT_EN | HP100_RESET_LB , OPTION_LSW );
+
+  /* Disable all MAC activity */
+  hp100_page( MAC_CTRL );
+  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */
+
+  /* If cascade MMU is not already in reset */
+  if (0 != (hp100_inw(OPTION_LSW)&HP100_HW_RST) )
+    {
+      /* Wait 1.3ms (10Mb max packet time) to ensure MAC is idle so
+       * MMU pointers will not be reset out from underneath
+       */
+      hp100_page( MAC_CTRL );
+      for(time=0; time<5000; time++)
+        {
+          if( (hp100_inb(MAC_CFG_1)&(HP100_TX_IDLE|HP100_RX_IDLE))==
+              (HP100_TX_IDLE|HP100_RX_IDLE) ) break;
+        }
+      
+      /* Shutdown algorithm depends on the generation of Cascade */
+      if( lp->chip==HP100_CHIPID_LASSEN )
+        { /* ETR shutdown/reset */
+          /* Disable Busmaster mode and wait for bit to go to zero. */
+          hp100_page(HW_MAP);
+          hp100_andb( ~HP100_BM_MASTER, BM );
+          /* 100 ms timeout */
+          for(time=0; time<32000; time++)
+            {
+              if ( 0 == (hp100_inb( BM ) & HP100_BM_MASTER) ) break;
+            }
+        }
+      else
+        { /* Shasta or Rainier Shutdown/Reset */
+          /* To ensure all bus master inloading activity has ceased,
+           * wait for no Rx PDAs or no Rx packets on card. 
+           */
+          hp100_page( PERFORMANCE );
+          /* 100 ms timeout */
+          for(time=0; time<10000; time++)
+            {
+              /* RX_PDL: PDLs not executed. */
+              /* RX_PKT_CNT: RX'd packets on card. */
+              if ( (hp100_inb( RX_PDL ) == 0) &&
+                   (hp100_inb( RX_PKT_CNT ) == 0) ) break;
+            }
+          
+          if(time>=10000)
+            printk("hp100: %s: BM shutdown error.\n", dev->name);
+          
+          /* To ensure all bus master outloading activity has ceased,
+           * wait until the Tx PDA count goes to zero or no more Tx space
+           * available in the Tx region of the card. 
+           */
+          /* 100 ms timeout */
+          for(time=0; time<10000; time++) {
+            if ( (0 == hp100_inb( TX_PKT_CNT )) &&
+                 (0 != (hp100_inb( TX_MEM_FREE )&HP100_AUTO_COMPARE))) break;
+          } 
+          
+          /* Disable Busmaster mode */
+          hp100_page(HW_MAP);
+          hp100_andb( ~HP100_BM_MASTER, BM );
+        } /* end of shutdown procedure for non-etr parts */  
+						
+      hp100_cascade_reset( dev, TRUE );
+    }
+  hp100_page( PERFORMANCE );
+  /* hp100_outw( HP100_BM_READ | HP100_BM_WRITE | HP100_RESET_HB, OPTION_LSW ); */
+  /* Busmaster mode should be shut down now. */
 }
-
 
 
+
 /* 
  *  transmit functions
  */
 
 /* tx function for busmaster mode */
-static int hp100_start_xmit_bm(struct sk_buff *skb, struct device *dev)
+static int hp100_start_xmit_bm( struct sk_buff *skb, struct device *dev )
 {
-	int i, ok_flag;
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	hp100_ring_t *ringptr;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4210, TRACE);
-	printk("hp100: start_xmit_bm\n");
-#endif
-
-	/* Get Tx ring tail pointer */
-	if (lp->txrtail->next == lp->txrhead) {
-		/* No memory. */
-#ifdef HP100_DEBUG
-		printk("hp100: start_xmit_bm: No TX PDL available.\n");
-#endif
-		/* not waited long enough since last tx? */
-		if (jiffies - dev->trans_start < HZ / 10)
-			return -EAGAIN;
-
-		if (lp->lan_type < 0) {		/* no LAN type detected yet? */
-			hp100_stop_interface(dev);
-			if ((lp->lan_type = hp100_sense_lan(dev)) < 0) {
-				printk("%s: no connection found - check wire\n", dev->name);
-				hp100_start_interface(dev);	/* 10Mb/s RX pkts maybe handled */
-				return -EIO;
-			}
-			if (lp->lan_type == HP100_LAN_100)
-				lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);	/* relogin */
-			hp100_start_interface(dev);
-		}
-		if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0)
-			/* we have a 100Mb/s adapter but it isn't connected to hub */
-		{
-			printk("%s: login to 100Mb/s hub retry\n", dev->name);
-			hp100_stop_interface(dev);
-			lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
-			hp100_start_interface(dev);
-		} else {
-			hp100_ints_off();
-			i = hp100_sense_lan(dev);
-			hp100_page(PERFORMANCE);
-			hp100_ints_on();
-			if (i == HP100_LAN_ERR)
-				printk("%s: link down detected\n", dev->name);
-			else if (lp->lan_type != i) {	/* cable change! */
-				/* it's very hard - all network setting must be changed!!! */
-				printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name);
-				lp->lan_type = i;
-				hp100_stop_interface(dev);
-				if (lp->lan_type == HP100_LAN_100)
-					lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
-				hp100_start_interface(dev);
-			} else {
-				printk("%s: interface reset\n", dev->name);
-				hp100_stop_interface(dev);
-				hp100_start_interface(dev);
-			}
-		}
-
-		dev->trans_start = jiffies;
-		return -EAGAIN;
-	}
-	/*
-	 * we have to turn int's off before modifying this, otherwise
-	 * a tx_pdl_cleanup could occur at the same time
-	 */
-	cli();
-	ringptr = lp->txrtail;
-	lp->txrtail = ringptr->next;
-
-	/* Check whether packet has minimal packet size */
-	ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
-	i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
-
-	ringptr->skb = skb;
-	ringptr->pdl[0] = ((1 << 16) | i);	/* PDH: 1 Fragment & length */
-	ringptr->pdl[1] = (u32) virt_to_bus(skb->data);		/* 1st Frag: Adr. of data */
-	if (lp->chip == HP100_CHIPID_SHASTA) {
-		/* TODO:Could someone who has the EISA card please check if this works? */
-		ringptr->pdl[2] = i;
-	} else {		/* Lassen */
-		/* In the PDL, don't use the padded size but the real packet size: */
-		ringptr->pdl[2] = skb->len;	/* 1st Frag: Length of frag */
-	}
-
-	/* Hand this PDL to the card. */
-	hp100_outl(ringptr->pdl_paddr, TX_PDA_L);	/* Low Prio. Queue */
-
-	lp->txrcommit++;
-	sti();
-
-	/* Update statistics */
-	lp->stats.tx_packets++;
+  unsigned long flags;
+  int i, ok_flag;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t *ringptr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4210, TRACE );
+  printk("hp100: %s: start_xmit_bm\n",dev->name);
+#endif
+
+  if ( skb==NULL )
+    {
+#ifndef LINUX_2_1
+      dev_tint( dev );
+#endif
+      return 0;
+    }
+	
+  if ( skb->len <= 0 ) return 0;
+	
+  /* Get Tx ring tail pointer */
+  if( lp->txrtail->next==lp->txrhead )
+    {
+      /* No memory. */
+#ifdef HP100_DEBUG
+      printk("hp100: %s: start_xmit_bm: No TX PDL available.\n", dev->name);
+#endif
+      /* not waited long enough since last tx? */
+      if ( jiffies - dev->trans_start < HZ ) return -EAGAIN;
+
+      if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
+	{
+	  hp100_stop_interface( dev );
+	  if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
+	    {
+	      printk( "hp100: %s: no connection found - check wire\n", dev->name );
+	      hp100_start_interface( dev );  /* 10Mb/s RX pkts maybe handled */
+	      return -EIO;
+	    }
+	  if ( lp->lan_type == HP100_LAN_100 )
+	    lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
+	  hp100_start_interface( dev );
+	}
+				
+      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
+	/* we have a 100Mb/s adapter but it isn't connected to hub */
+	{
+	  printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name );
+	  hp100_stop_interface( dev );
+	  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+	  hp100_start_interface( dev );
+	}
+      else
+	{
+	  hp100_ints_off();
+	  i = hp100_sense_lan( dev );
+	  hp100_ints_on();
+	  if ( i == HP100_LAN_ERR )
+	    printk( "hp100: %s: link down detected\n", dev->name );
+	  else
+	    if ( lp->lan_type != i ) /* cable change! */
+	      {
+		/* it's very hard - all network setting must be changed!!! */
+		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
+		lp->lan_type = i;
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+	      }
+	    else
+	      {
+		printk( "hp100: %s: interface reset\n", dev->name );
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+	      }
+	}
+
+      dev->trans_start = jiffies;
+      return -EAGAIN;
+    }
+	
+  /*
+   * we have to turn int's off before modifying this, otherwise
+   * a tx_pdl_cleanup could occur at the same time
+   */
+  save_flags( flags );
+  cli();
+  ringptr=lp->txrtail;
+  lp->txrtail=ringptr->next;
+	
+  /* Check whether packet has minimal packet size */
+  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
+					
+  ringptr->skb=skb;
+  ringptr->pdl[0]=((1<<16) | i);                /* PDH: 1 Fragment & length */
+  ringptr->pdl[1]=(u32)virt_to_bus(skb->data);  /* 1st Frag: Adr. of data */
+  if(lp->chip==HP100_CHIPID_SHASTA)
+    {
+      /* TODO:Could someone who has the EISA card please check if this works? */
+      ringptr->pdl[2]=i;
+    }
+  else /* Lassen */
+    {
+      /* In the PDL, don't use the padded size but the real packet size: */
+      ringptr->pdl[2]=skb->len;              /* 1st Frag: Length of frag */
+    }
+
+  /* Hand this PDL to the card. */
+  hp100_outl( ringptr->pdl_paddr, TX_PDA_L ); /* Low Prio. Queue */
+	
+  lp->txrcommit++;
+  restore_flags( flags );
+	
+  /* Update statistics */	
+  lp->stats.tx_packets++;
 #ifdef LINUX_2_1
-	lp->stats.tx_bytes += skb->len;
+  lp->stats.tx_bytes += skb->len;
 #endif
-	dev->trans_start = jiffies;
-
-	return 0;
+  dev->trans_start = jiffies;
+	
+  return 0;
 }
-
 
+
 /* clean_txring checks if packets have been sent by the card by reading
  * the TX_PDL register from the performance page and comparing it to the
  * number of commited packets. It then frees the skb's of the packets that
@@ -1447,168 +1673,209 @@
  *
  * Needs the PERFORMANCE page selected. 
  */
-static void hp100_clean_txring(struct device *dev)
+static void hp100_clean_txring( struct device *dev )
 {
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	int ioaddr = dev->base_addr;
-	int donecount;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int    ioaddr = dev->base_addr;
+  int    donecount;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4211, TRACE);
-	printk("hp100: clean txring\n");
+  hp100_outw( 0x4211, TRACE );
+  printk("hp100: %s: clean txring\n", dev->name);
 #endif
 
-	/* How many PDLs have been transmitted? */
-	donecount = (lp->txrcommit) - hp100_inb(TX_PDL);
+  /* How many PDLs have been transmitted? */
+  donecount=(lp->txrcommit)-hp100_inb(TX_PDL);
 
 #ifdef HP100_DEBUG
-	if (donecount > MAX_TX_PDL)
-		printk("hp100: Warning: More PDLs transmitted than commited to card???\n");
+  if(donecount>MAX_TX_PDL)
+    printk("hp100: %s: Warning: More PDLs transmitted than commited to card???\n",dev->name);
 #endif
 
-	for (; 0 != donecount; donecount--) {
+  for( ; 0!=donecount; donecount-- )
+    {
 #ifdef HP100_DEBUG_BM
-		printk("hp100: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n",
-		       (u_int) lp->txrhead->skb->data,
-		       lp->txrcommit,
-		       hp100_inb(TX_PDL),
-		       donecount);
-#endif
-		dev_kfree_skb(lp->txrhead->skb);
-		lp->txrhead->skb = (void *) NULL;
-		lp->txrhead = lp->txrhead->next;
-		lp->txrcommit--;
-	}
+      printk("hp100: %s: Free skb: data @0x%.8x txrcommit=0x%x TXPDL=0x%x, done=0x%x\n",
+             dev->name,
+	     (u_int) lp->txrhead->skb->data,
+	     lp->txrcommit,
+	     hp100_inb(TX_PDL),
+	     donecount);
+#endif
+#ifdef LINUX_2_1
+      dev_kfree_skb( lp->txrhead->skb );
+#else
+      dev_kfree_skb( lp->txrhead->skb, FREE_WRITE );
+#endif
+      lp->txrhead->skb=(void *)NULL;
+      lp->txrhead=lp->txrhead->next;
+      lp->txrcommit--;
+    }
 }
-
 
+
 /* tx function for slave modes */
-static int hp100_start_xmit(struct sk_buff *skb, struct device *dev)
+static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
 {
-	int i, ok_flag;
-	int ioaddr = dev->base_addr;
-	u_short val;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4212, TRACE);
-	printk("hp100: start_xmit\n");
-#endif
+  int i, ok_flag;
+  int ioaddr = dev->base_addr;
+  u_short val;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4212, TRACE );
+  printk("hp100: %s: start_xmit\n", dev->name);
+#endif
+
+  if ( skb==NULL )
+    {
+#ifndef LINUX_2_1
+      dev_tint( dev );
+#endif
+      return 0;
+    }
+	
+  if ( skb->len <= 0 ) return 0;
+	
+  if ( lp->lan_type < 0 ) /* no LAN type detected yet? */
+    {
+      hp100_stop_interface( dev );
+      if ( ( lp->lan_type = hp100_sense_lan( dev ) ) < 0 )
+        {
+          printk( "hp100: %s: no connection found - check wire\n", dev->name );
+          hp100_start_interface( dev );  /* 10Mb/s RX packets maybe handled */
+          return -EIO;
+        }
+      if ( lp->lan_type == HP100_LAN_100 )
+        lp->hub_status = hp100_login_to_vg_hub( dev, FALSE ); /* relogin */
+      hp100_start_interface( dev );
+    }
+
+  /* If there is not enough free memory on the card... */
+  i=hp100_inl(TX_MEM_FREE)&0x7fffffff;
+  if ( !(((i/2)-539)>(skb->len+16) && (hp100_inb(TX_PKT_CNT)<255)) )
+    {
+#ifdef HP100_DEBUG
+      printk( "hp100: %s: start_xmit: tx free mem = 0x%x\n", dev->name, i );
+#endif
+      /* not waited long enough since last failed tx try? */
+      if ( jiffies - dev->trans_start < HZ ) 
+	{
+#ifdef HP100_DEBUG
+	  printk("hp100: %s: trans_start timing problem\n", dev->name);
+#endif
+	  return -EAGAIN;
+	}
+      if ( lp->lan_type == HP100_LAN_100 && lp->hub_status < 0 )
+	/* we have a 100Mb/s adapter but it isn't connected to hub */
+        {
+          printk( "hp100: %s: login to 100Mb/s hub retry\n", dev->name );
+          hp100_stop_interface( dev );
+          lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+          hp100_start_interface( dev );
+        }
+      else
+        {
+          hp100_ints_off();
+          i = hp100_sense_lan( dev );
+          hp100_ints_on();
+          if ( i == HP100_LAN_ERR )
+            printk( "hp100: %s: link down detected\n", dev->name );
+	  else
+	    if ( lp->lan_type != i ) /* cable change! */
+	      {
+		/* it's very hard - all network setting must be changed!!! */
+		printk( "hp100: %s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name );
+		lp->lan_type = i;
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+	      }
+	    else
+	      {
+		printk( "hp100: %s: interface reset\n", dev->name );
+		hp100_stop_interface( dev );
+		if ( lp->lan_type == HP100_LAN_100 )
+		  lp->hub_status = hp100_login_to_vg_hub( dev, FALSE );
+		hp100_start_interface( dev );
+		udelay(1000);
+	      }
+        }
+      dev->trans_start = jiffies;
+      return -EAGAIN;
+    }
 
-	if (lp->lan_type < 0) {	/* no LAN type detected yet? */
-		hp100_stop_interface(dev);
-		if ((lp->lan_type = hp100_sense_lan(dev)) < 0) {
-			printk("%s: no connection found - check wire\n", dev->name);
-			hp100_start_interface(dev);	/* 10Mb/s RX packets maybe handled */
-			return -EIO;
-		}
-		if (lp->lan_type == HP100_LAN_100)
-			lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);	/* relogin */
-		hp100_start_interface(dev);
-	}
-	/* If there is not enough free memory on the card... */
-	i = hp100_inl(TX_MEM_FREE) & 0x7fffffff;
-	if (!(((i / 2) - 539) > (skb->len + 16) && (hp100_inb(TX_PKT_CNT) < 255))) {
-#ifdef HP100_DEBUG
-		printk("hp100_start_xmit: tx free mem = 0x%x\n", i);
-#endif
-		/* not waited long enough since last failed tx try? */
-		if (jiffies - dev->trans_start < HZ / 2) {
-#ifdef HP100_DEBUG
-			printk("hp100: trans_start timing problem\n");
-#endif
-			return -EAGAIN;
-		}
-		if (lp->lan_type == HP100_LAN_100 && lp->hub_status < 0)
-			/* we have a 100Mb/s adapter but it isn't connected to hub */
-		{
-			printk("%s: login to 100Mb/s hub retry\n", dev->name);
-			hp100_stop_interface(dev);
-			lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
-			hp100_start_interface(dev);
-		} else {
-			hp100_ints_off();
-			i = hp100_sense_lan(dev);
-			hp100_page(PERFORMANCE);
-			hp100_ints_on();
-			if (i == HP100_LAN_ERR)
-				printk("%s: link down detected\n", dev->name);
-			else if (lp->lan_type != i) {	/* cable change! */
-				/* it's very hard - all network setting must be changed!!! */
-				printk("%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev->name);
-				lp->lan_type = i;
-				hp100_stop_interface(dev);
-				if (lp->lan_type == HP100_LAN_100)
-					lp->hub_status = hp100_login_to_vg_hub(dev, FALSE);
-				hp100_start_interface(dev);
-			} else {
-				printk("%s: interface reset\n", dev->name);
-				hp100_stop_interface(dev);
-				hp100_start_interface(dev);
-				udelay(1000);
-			}
-		}
-		dev->trans_start = jiffies;
-		return -EAGAIN;
-	}
-	for (i = 0; i < 6000 && (hp100_inb(OPTION_MSW) & HP100_TX_CMD); i++) {
+  for ( i=0; i<6000 && ( hp100_inb( OPTION_MSW ) & HP100_TX_CMD ); i++ )
+    {
 #ifdef HP100_DEBUG_TX
-		printk("hp100_start_xmit: busy\n");
+      printk( "hp100: %s: start_xmit: busy\n", dev->name );
 #endif
-	}
-
-	hp100_ints_off();
-	val = hp100_inw(IRQ_STATUS);
-	/* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set
-	 * when the current packet being transmitted on the wire is completed. */
-	hp100_outw(HP100_TX_COMPLETE, IRQ_STATUS);
+    }
+	
+  hp100_ints_off();
+  val = hp100_inw( IRQ_STATUS );
+  /* Ack / clear the interrupt TX_COMPLETE interrupt - this interrupt is set
+   * when the current packet being transmitted on the wire is completed. */
+  hp100_outw( HP100_TX_COMPLETE, IRQ_STATUS ); 
 #ifdef HP100_DEBUG_TX
-	printk("hp100_start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n", val, hp100_inw(IRQ_MASK), (int) skb->len);
+  printk("hp100: %s: start_xmit: irq_status=0x%.4x, irqmask=0x%.4x, len=%d\n",dev->name,val,hp100_inw(IRQ_MASK),(int)skb->len );
 #endif
 
-	ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
-	i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
-
-	hp100_outw(i, DATA32);	/* tell card the total packet length */
-	hp100_outw(i, FRAGMENT_LEN);	/* and first/only fragment length    */
+  ok_flag = skb->len >= HP100_MIN_PACKET_SIZE;
+  i = ok_flag ? skb->len : HP100_MIN_PACKET_SIZE;
 
-	if (lp->mode == 2) {	/* memory mapped */
-		if (lp->mem_ptr_virt) {		/* high pci memory was remapped */
-			/* Note: The J2585B needs alignment to 32bits here!  */
-			memcpy(lp->mem_ptr_virt, skb->data, (skb->len + 3) & ~3);
-			if (!ok_flag)
-				memset(lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len);
-		} else {
-			memcpy_toio(lp->mem_ptr_phys, skb->data, skb->len);
-			if (!ok_flag)
-				memset_io(lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len);
-		}
-	} else {		/* programmed i/o */
-		outsl(ioaddr + HP100_REG_DATA32, skb->data, (skb->len + 3) >> 2);
-		if (!ok_flag)
-			for (i = (skb->len + 3) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4)
-				hp100_outl(0, DATA32);
-	}
-
-	hp100_outb(HP100_TX_CMD | HP100_SET_LB, OPTION_MSW);	/* send packet */
-
-	lp->stats.tx_packets++;
+  hp100_outw( i, DATA32 );       /* tell card the total packet length */
+  hp100_outw( i, FRAGMENT_LEN ); /* and first/only fragment length    */
+	
+  if ( lp->mode==2 ) /* memory mapped */
+    {
+      if ( lp->mem_ptr_virt ) /* high pci memory was remapped */
+	{
+	  /* Note: The J2585B needs alignment to 32bits here!  */
+	  memcpy( lp->mem_ptr_virt, skb->data, ( skb->len + 3 ) & ~3 );
+	  if ( !ok_flag )
+	    memset( lp->mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb->len );
+	}
+      else
+	{
+	  /* Note: The J2585B needs alignment to 32bits here!  */
+	  memcpy_toio( lp->mem_ptr_phys, skb->data, (skb->len + 3) & ~3 );
+	  if ( !ok_flag )
+	    memset_io( lp->mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb->len );
+	}
+    }
+  else /* programmed i/o */
+    {
+      outsl( ioaddr + HP100_REG_DATA32, skb->data, ( skb->len + 3 ) >> 2 );
+      if ( !ok_flag )
+	for ( i = ( skb->len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
+	  hp100_outl( 0, DATA32 );
+    }
+	
+  hp100_outb( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
+	
+  lp->stats.tx_packets++;
 #ifdef LINUX_2_1
-	lp->stats.tx_bytes += skb->len;
+  lp->stats.tx_bytes += skb->len;
 #endif
-	dev->trans_start = jiffies;
-	hp100_ints_on();
-
-	dev_kfree_skb(skb);
-
+  dev->trans_start=jiffies;
+  hp100_ints_on();
+	
+#ifdef LINUX_2_1
+  dev_kfree_skb( skb );
+#else
+  dev_kfree_skb( skb, FREE_WRITE );
+#endif
+	
 #ifdef HP100_DEBUG_TX
-	printk("hp100_start_xmit: end\n");
+  printk( "hp100: %s: start_xmit: end\n", dev->name );
 #endif
-
-	return 0;
+	
+  return 0;
 }
-
 
+
 /*
  * Receive Function (Non-Busmaster mode)
  * Called when an "Receive Packet" interrupt occurs, i.e. the receive 
@@ -1618,981 +1885,1163 @@
  * and netif_rx. 
  */
 
-static void hp100_rx(struct device *dev)
+static void hp100_rx( struct device *dev )
 {
-	int packets, pkt_len;
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	u_int header;
-	struct sk_buff *skb;
+  int packets, pkt_len;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  u_int header;
+  struct sk_buff *skb;
 
 #ifdef DEBUG_B
-	hp100_outw(0x4213, TRACE);
-	printk("hp100: rx\n");
+  hp100_outw( 0x4213, TRACE );
+  printk("hp100: %s: rx\n", dev->name);
 #endif
 
-	/* First get indication of received lan packet */
-	/* RX_PKT_CND indicates the number of packets which have been fully */
-	/* received onto the card but have not been fully transfered of the card */
-	packets = hp100_inb(RX_PKT_CNT);
+  /* First get indication of received lan packet */
+  /* RX_PKT_CND indicates the number of packets which have been fully */
+  /* received onto the card but have not been fully transfered of the card */
+  packets = hp100_inb( RX_PKT_CNT );
 #ifdef HP100_DEBUG_RX
-	if (packets > 1)
-		printk("hp100_rx: waiting packets = %d\n", packets);
+  if ( packets > 1 )
+    printk( "hp100: %s: rx: waiting packets = %d\n", dev->name,packets );
 #endif
 
-	while (packets-- > 0) {
-		/* If ADV_NXT_PKT is still set, we have to wait until the card has */
-		/* really advanced to the next packet. */
-		for (pkt_len = 0; pkt_len < 6000 && (hp100_inb(OPTION_MSW) & HP100_ADV_NXT_PKT);
-		     pkt_len++) {
+  while ( packets-- > 0 )
+    {
+      /* If ADV_NXT_PKT is still set, we have to wait until the card has */
+      /* really advanced to the next packet. */
+      for (pkt_len=0; pkt_len<6000 &&(hp100_inb(OPTION_MSW)&HP100_ADV_NXT_PKT);
+	   pkt_len++ )
+        {
 #ifdef HP100_DEBUG_RX
-			printk("hp100_rx: busy, remaining packets = %d\n", packets);
-#endif
-		}
-
-		/* First we get the header, which contains information about the */
-		/* actual length of the received packet. */
-		if (lp->mode == 2) {	/* memory mapped mode */
-			if (lp->mem_ptr_virt)	/* if memory was remapped */
-				header = *(__u32 *) lp->mem_ptr_virt;
-			else
-				header = readl(lp->mem_ptr_phys);
-		} else		/* programmed i/o */
-			header = hp100_inl(DATA32);
-
-		pkt_len = header & HP100_PKT_LEN_MASK;
+          printk( "hp100: %s: rx: busy, remaining packets = %d\n", dev->name, packets );
+#endif    
+        }
+
+      /* First we get the header, which contains information about the */
+      /* actual length of the received packet. */
+      if( lp->mode==2 ) /* memory mapped mode */
+        {
+          if ( lp->mem_ptr_virt )    /* if memory was remapped */
+            header = *(__u32 *)lp->mem_ptr_virt;
+          else
+            header = readl( lp->mem_ptr_phys );
+        }
+      else /* programmed i/o */
+        header = hp100_inl( DATA32 );
+      
+      pkt_len = ((header & HP100_PKT_LEN_MASK) + 3) & ~3;
 
 #ifdef HP100_DEBUG_RX
-		printk("hp100_rx: new packet - length=%d, errors=0x%x, dest=0x%x\n",
-		    header & HP100_PKT_LEN_MASK, (header >> 16) & 0xfff8,
-		       (header >> 16) & 7);
-#endif
-
-		/* Now we allocate the skb and transfer the data into it. */
-		/* NOTE! This (and the skb_put() below) depends on the skb-functions
-		 * allocating more than asked (notably, aligning the request up to
-		 * the next 16-byte length).
-		 */
-		skb = dev_alloc_skb(pkt_len);
-		if (skb == NULL) {	/* Not enough memory->drop packet */
-#ifdef HP100_DEBUG
-			printk("hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len);
-#endif
-			lp->stats.rx_dropped++;
-		} else {	/* skb successfully allocated */
-			u_char *ptr;
-
-			skb->dev = dev;
-
-			/* ptr to start of the sk_buff data area */
-			ptr = (u_char *) skb_put(skb, pkt_len);
-
-			/* Now transfer the data from the card into that area */
-			if (lp->mode == 2) {
-				if (lp->mem_ptr_virt)
-					memcpy(ptr, lp->mem_ptr_virt, (pkt_len + 3) & ~3);
-				/* Note alignment to 32bit transfers */
-				else
-					memcpy_fromio(ptr, lp->mem_ptr_phys, (pkt_len + 3) & ~3);
-			} else	/* io mapped */
-				insl(ioaddr + HP100_REG_DATA32, ptr, (pkt_len + 3) >> 2);
+      printk( "hp100: %s: rx: new packet - length=%d, errors=0x%x, dest=0x%x\n",
+      	      dev->name,
+              header & HP100_PKT_LEN_MASK, (header>>16)&0xfff8,
+              (header>>16)&7);
+#endif
+    
+      /* Now we allocate the skb and transfer the data into it. */  
+      skb = dev_alloc_skb( pkt_len );
+      if ( skb == NULL ) /* Not enough memory->drop packet */
+	{
+#ifdef HP100_DEBUG
+	  printk( "hp100: %s: rx: couldn't allocate a sk_buff of size %d\n", dev->name, pkt_len );
+#endif
+	  lp->stats.rx_dropped++;
+	}
+      else /* skb successfully allocated */
+	{
+	  u_char *ptr;
+      
+	  skb->dev = dev;
+      
+	  /* ptr to start of the sk_buff data area */
+	  ptr = (u_char *)skb_put( skb, pkt_len );
+      
+	  /* Now transfer the data from the card into that area */
+	  if ( lp->mode==2 )
+            {
+              if ( lp->mem_ptr_virt )
+                memcpy( ptr, lp->mem_ptr_virt, pkt_len );
+              /* Note alignment to 32bit transfers */
+              else
+                memcpy_fromio( ptr, lp->mem_ptr_phys, pkt_len );
+            }
+	  else /* io mapped */
+	    insl( ioaddr + HP100_REG_DATA32, ptr, pkt_len >> 2 );
+      
+	  skb->protocol = eth_type_trans( skb, dev );
 
-			skb->protocol = eth_type_trans(skb, dev);
-
-			netif_rx(skb);
-			lp->stats.rx_packets++;
+	  netif_rx( skb );
+	  lp->stats.rx_packets++;
 #ifdef LINUX_2_1
-			lp->stats.rx_bytes += skb->len;
+	  lp->stats.rx_bytes += skb->len;
 #endif
-
+      
 #ifdef HP100_DEBUG_RX
-			printk("rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
-			  ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5],
-			ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11]);
-#endif
-		}
-
-		/* Indicate the card that we have got the packet */
-		hp100_outb(HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW);
-
-		switch (header & 0x00070000) {
-		case (HP100_MULTI_ADDR_HASH << 16):
-		case (HP100_MULTI_ADDR_NO_HASH << 16):
-			lp->stats.multicast++;
-			break;
-		}
-	}			/* end of while(there are packets) loop */
+	  printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+	  	  dev->name,
+		  ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
+		  ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
+#endif
+	}
+  
+      /* Indicate the card that we have got the packet */
+      hp100_outb( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
+
+      switch ( header & 0x00070000 ) {
+      case (HP100_MULTI_ADDR_HASH<<16):
+      case (HP100_MULTI_ADDR_NO_HASH<<16):
+	lp->stats.multicast++; break;
+      }
+    } /* end of while(there are packets) loop */
 #ifdef HP100_DEBUG_RX
-	printk("hp100_rx: end\n");
+  printk( "hp100_rx: %s: end\n", dev->name );
 #endif
 }
-
 
+
 /* 
  * Receive Function for Busmaster Mode
  */
-static void hp100_rx_bm(struct device *dev)
+static void hp100_rx_bm( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	hp100_ring_t *ptr;
-	u_int header;
-	int pkt_len;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4214, TRACE);
-	printk("hp100: rx_bm\n");
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  hp100_ring_t *ptr;
+  u_int header;
+  int pkt_len;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4214, TRACE );
+  printk("hp100: %s: rx_bm\n", dev->name);
 #endif
 
 #ifdef HP100_DEBUG
-	if (0 == lp->rxrcommit) {
-		printk("hp100: rx_bm called although no PDLs were committed to adapter?\n");
-		return;
-	} else
-		/* RX_PKT_CNT states how many PDLs are currently formatted and available to 
-		 * the cards BM engine */
-	if ((hp100_inw(RX_PKT_CNT) & 0x00ff) >= lp->rxrcommit) {
-		printk("hp100: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", hp100_inw(RX_PKT_CNT) & 0x00ff, lp->rxrcommit);
-		return;
-	}
-#endif
-
-	while ((lp->rxrcommit > hp100_inb(RX_PDL))) {
-		/*
-		 * The packet was received into the pdl pointed to by lp->rxrhead (
-		 * the oldest pdl in the ring 
-		 */
-
-		/* First we get the header, which contains information about the */
-		/* actual length of the received packet. */
-
-		ptr = lp->rxrhead;
-
-		header = *(ptr->pdl - 1);
-		pkt_len = (header & HP100_PKT_LEN_MASK);
+  if(0==lp->rxrcommit)
+    {
+      printk("hp100: %s: rx_bm called although no PDLs were committed to adapter?\n", dev->name); 
+      return;
+    }
+  else
+
+    /* RX_PKT_CNT states how many PDLs are currently formatted and available to 
+     * the cards BM engine */
+    if( (hp100_inw(RX_PKT_CNT)&0x00ff) >= lp->rxrcommit)
+      {
+	printk("hp100: %s: More packets received than commited? RX_PKT_CNT=0x%x, commit=0x%x\n", dev->name, hp100_inw(RX_PKT_CNT)&0x00ff, lp->rxrcommit);
+	return;
+      }
+#endif
+
+  while( (lp->rxrcommit > hp100_inb(RX_PDL)) )
+    {
+      /*
+       * The packet was received into the pdl pointed to by lp->rxrhead (
+       * the oldest pdl in the ring 
+       */
+						
+      /* First we get the header, which contains information about the */
+      /* actual length of the received packet. */
+      
+      ptr=lp->rxrhead;
+      
+      header = *(ptr->pdl-1);
+      pkt_len = (header & HP100_PKT_LEN_MASK);
 
 #ifdef HP100_DEBUG_BM
-		printk("hp100: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n",
-		       (u_int) (ptr->pdl - 1), (u_int) header,
-		       pkt_len,
-		       (header >> 16) & 0xfff8,
-		       (header >> 16) & 7);
-		printk("hp100: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n",
-		       hp100_inb(RX_PDL),
-		       hp100_inb(TX_PDL),
-		       hp100_inb(RX_PKT_CNT),
-		       (u_int) * (ptr->pdl),
-		       (u_int) * (ptr->pdl + 3),
-		       (u_int) * (ptr->pdl + 4));
-#endif
-
-		if ((pkt_len >= MIN_ETHER_SIZE) &&
-		    (pkt_len <= MAX_ETHER_SIZE)) {
-			if (ptr->skb == NULL) {
-				printk("hp100: rx_bm: skb null\n");
-				/* can happen if we only allocated room for the pdh due to memory shortage. */
-				lp->stats.rx_dropped++;
-			} else {
-				skb_trim(ptr->skb, pkt_len);	/* Shorten it */
-				ptr->skb->protocol = eth_type_trans(ptr->skb, dev);
-
-				netif_rx(ptr->skb);	/* Up and away... */
+      printk( "hp100: %s: rx_bm: header@0x%x=0x%x length=%d, errors=0x%x, dest=0x%x\n",
+      	      dev->name,
+              (u_int) (ptr->pdl-1),(u_int) header,
+              pkt_len, 
+              (header>>16)&0xfff8,
+              (header>>16)&7);
+      printk( "hp100: %s: RX_PDL_COUNT:0x%x TX_PDL_COUNT:0x%x, RX_PKT_CNT=0x%x PDH=0x%x, Data@0x%x len=0x%x\n",
+      	      dev->name,
+	      hp100_inb( RX_PDL ),
+	      hp100_inb( TX_PDL ),
+	      hp100_inb( RX_PKT_CNT ),
+	      (u_int) *(ptr->pdl),
+	      (u_int) *(ptr->pdl+3),
+	      (u_int) *(ptr->pdl+4));
+#endif
+      
+      if( (pkt_len>=MIN_ETHER_SIZE) &&
+          (pkt_len<=MAX_ETHER_SIZE) )  
+        {
+	  if(ptr->skb==NULL)
+	    {
+	      printk("hp100: %s: rx_bm: skb null\n", dev->name);
+	      /* can happen if we only allocated room for the pdh due to memory shortage. */
+	      lp->stats.rx_dropped++;
+	    }
+	  else
+	    {
+	      skb_trim( ptr->skb, pkt_len );     /* Shorten it */
+	      ptr->skb->protocol = eth_type_trans( ptr->skb, dev );
+														
+	      netif_rx( ptr->skb );              /* Up and away... */
 
-				lp->stats.rx_packets++;
+	      lp->stats.rx_packets++;
 #ifdef LINUX_2_1
-				lp->stats.rx_bytes += ptr->skb->len;
+	      lp->stats.rx_bytes += ptr->skb->len;
 #endif
-			}
-
-			switch (header & 0x00070000) {
-			case (HP100_MULTI_ADDR_HASH << 16):
-			case (HP100_MULTI_ADDR_NO_HASH << 16):
-				lp->stats.multicast++;
-				break;
-			}
-		} else {
-#ifdef HP100_DEBUG
-			printk("hp100: rx_bm: Received bad packet (length=%d)\n", pkt_len);
-#endif
-			if (ptr->skb != NULL)
-				dev_kfree_skb(ptr->skb);
-			lp->stats.rx_errors++;
-		}
-
-		lp->rxrhead = lp->rxrhead->next;
-
-		/* Allocate a new rx PDL (so lp->rxrcommit stays the same) */
-		if (0 == hp100_build_rx_pdl(lp->rxrtail, dev)) {
-			/* No space for skb, header can still be received. */
-#ifdef HP100_DEBUG
-			printk("hp100: rx_bm: No space for new PDL.\n");
-#endif
-			return;
-		} else {	/* successfully allocated new PDL - put it in ringlist at tail. */
-			hp100_outl((u32) lp->rxrtail->pdl_paddr, RX_PDA);
-			lp->rxrtail = lp->rxrtail->next;
-		}
+	    }
 
+          switch ( header & 0x00070000 ) {
+          case (HP100_MULTI_ADDR_HASH<<16):
+          case (HP100_MULTI_ADDR_NO_HASH<<16):
+            lp->stats.multicast++; break;
+          }
+        }
+      else
+        {
+#ifdef HP100_DEBUG
+          printk("hp100: %s: rx_bm: Received bad packet (length=%d)\n",dev->name,pkt_len);
+#endif
+	  if(ptr->skb!=NULL)
+#ifdef LINUX_2_1
+	    dev_kfree_skb( ptr->skb );
+#else
+	    dev_kfree_skb( ptr->skb, FREE_READ );					
+#endif
+          lp->stats.rx_errors++;
+        }
+						
+      lp->rxrhead=lp->rxrhead->next;
+
+      /* Allocate a new rx PDL (so lp->rxrcommit stays the same) */
+      if (0 == hp100_build_rx_pdl( lp->rxrtail, dev ))
+        {
+	  /* No space for skb, header can still be received. */
+#ifdef HP100_DEBUG
+          printk("hp100: %s: rx_bm: No space for new PDL.\n", dev->name);
+#endif
+	  return;
+        } 
+      else
+        { /* successfully allocated new PDL - put it in ringlist at tail. */
+	  hp100_outl((u32)lp->rxrtail->pdl_paddr, RX_PDA);
+          lp->rxrtail=lp->rxrtail->next;
 	}
+						
+    }
 }
-
 
 
+
 /*
  *  statistics
  */
-static hp100_stats_t *hp100_get_stats(struct device *dev)
+static hp100_stats_t *hp100_get_stats( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
+  int ioaddr = dev->base_addr;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4215, TRACE);
+  hp100_outw( 0x4215, TRACE );
 #endif
 
-	hp100_ints_off();
-	hp100_update_stats(dev);
-	hp100_ints_on();
-	return &((struct hp100_private *) dev->priv)->stats;
+  hp100_ints_off();
+  hp100_update_stats( dev );
+  hp100_ints_on();
+  return &((struct hp100_private *)dev->priv)->stats;
 }
 
-static void hp100_update_stats(struct device *dev)
+static void hp100_update_stats( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	u_short val;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
+  int ioaddr = dev->base_addr;
+  u_short val;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4216, TRACE);
-	printk("hp100: update-stats\n");
+  hp100_outw( 0x4216, TRACE );
+  printk("hp100: %s: update-stats\n", dev->name);
 #endif
 
-	/* Note: Statistics counters clear when read. */
-	hp100_page(MAC_CTRL);
-	val = hp100_inw(DROPPED) & 0x0fff;
-	lp->stats.rx_errors += val;
-	lp->stats.rx_over_errors += val;
-	val = hp100_inb(CRC);
-	lp->stats.rx_errors += val;
-	lp->stats.rx_crc_errors += val;
-	val = hp100_inb(ABORT);
-	lp->stats.tx_errors += val;
-	lp->stats.tx_aborted_errors += val;
-	hp100_page(PERFORMANCE);
+  /* Note: Statistics counters clear when read. */
+  hp100_page( MAC_CTRL ); 
+  val = hp100_inw( DROPPED ) & 0x0fff;
+  lp->stats.rx_errors += val;
+  lp->stats.rx_over_errors += val;
+  val = hp100_inb( CRC );
+  lp->stats.rx_errors += val;
+  lp->stats.rx_crc_errors += val;
+  val = hp100_inb( ABORT );
+  lp->stats.tx_errors += val;
+  lp->stats.tx_aborted_errors += val;
+  hp100_page( PERFORMANCE );
 }
 
-static void hp100_clear_stats(int ioaddr)
+static void hp100_misc_interrupt( struct device *dev )
 {
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4217, TRACE);
-	printk("hp100: clear_stats\n");
+  hp100_outw( 0x4216, TRACE );
+  printk("hp100: %s: misc_interrupt\n", dev->name);
 #endif
 
-	cli();
-	hp100_page(MAC_CTRL);	/* get all statistics bytes */
-	hp100_inw(DROPPED);
-	hp100_inb(CRC);
-	hp100_inb(ABORT);
-	hp100_page(PERFORMANCE);
-	sti();
+  /* Note: Statistics counters clear when read. */
+  lp->stats.rx_errors++;
+  lp->stats.tx_errors++;
+}
+
+static void hp100_clear_stats( int ioaddr )
+{
+  unsigned long flags;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4217, TRACE );
+  printk("hp100: %s: clear_stats\n", dev->name);
+#endif
+
+  save_flags( flags );
+  cli();
+  hp100_page( MAC_CTRL );    /* get all statistics bytes */
+  hp100_inw( DROPPED );
+  hp100_inb( CRC );
+  hp100_inb( ABORT );
+  hp100_page( PERFORMANCE );
+  restore_flags( flags );
 }
-
 
+
 /*
  *  multicast setup
  */
 
 /*
  *  Set or clear the multicast filter for this adapter.
- *  TODO: Currently when in multicast mode, card accepts all multicast packets
- *        for all MC addresses. Should better use the list on the card.
  */
-
-static void hp100_set_multicast_list(struct device *dev)
+                                                          
+static void hp100_set_multicast_list( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4218, TRACE);
-	printk("hp100: set_mc_list\n");
-#endif
-
-	cli();
-	hp100_ints_off();
-	hp100_page(MAC_CTRL);
-	hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1);	/* stop rx/tx */
-
-	if (dev->flags & IFF_PROMISC) {
-		lp->mac2_mode = HP100_MAC2MODE6;	/* promiscuous mode = get all good */
-		lp->mac1_mode = HP100_MAC1MODE6;	/* packets on the net */
-	} else if (dev->mc_count || (dev->flags & IFF_ALLMULTI)) {
-		lp->mac2_mode = HP100_MAC2MODE5;	/* multicast mode = get packets for */
-		lp->mac1_mode = HP100_MAC1MODE5;	/* me, broadcasts and all multicasts */
-	} else {
-		lp->mac2_mode = HP100_MAC2MODE3;	/* normal mode = get packets for me */
-		lp->mac1_mode = HP100_MAC1MODE3;	/* and broadcasts */
-	}
-
-	if (((hp100_inb(MAC_CFG_1) & 0x0f) != lp->mac1_mode) ||
-	    (hp100_inb(MAC_CFG_2) != lp->mac2_mode)) {
-		hp100_outb(lp->mac2_mode, MAC_CFG_2);
-		hp100_andb(HP100_MAC1MODEMASK, MAC_CFG_1);	/* clear mac1 mode bits */
-		hp100_orb(lp->mac1_mode, MAC_CFG_1);	/* and set the new mode */
-
-		if (lp->lan_type == HP100_LAN_100) {
-#ifdef HP100_DEBUG
-			printk("hp100: 100VG MAC settings have changed - relogin.\n");
+  unsigned long flags;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4218, TRACE );
+  printk("hp100: %s: set_mc_list\n", dev->name);
+#endif
+
+  save_flags( flags );
+  cli();
+  hp100_ints_off();
+  hp100_page( MAC_CTRL );
+  hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );  /* stop rx/tx */
+
+  if ( dev->flags & IFF_PROMISC )
+    {
+      lp->mac2_mode = HP100_MAC2MODE6;  /* promiscuous mode = get all good */
+      lp->mac1_mode = HP100_MAC1MODE6;  /* packets on the net */
+      memset( &lp->hash_bytes, 0xff, 8 );
+    }
+  else if ( dev->mc_count || (dev->flags&IFF_ALLMULTI) )
+    {
+      lp->mac2_mode = HP100_MAC2MODE5;  /* multicast mode = get packets for */
+      lp->mac1_mode = HP100_MAC1MODE5;  /* me, broadcasts and all multicasts */
+#ifdef HP100_MULTICAST_FILTER		/* doesn't work!!! */
+      if ( dev -> flags & IFF_ALLMULTI )
+        {
+          /* set hash filter to receive all multicast packets */
+          memset( &lp->hash_bytes, 0xff, 8 );
+        }
+       else
+        {
+          int i, j, idx;
+          u_char *addrs;
+          struct dev_mc_list *dmi;
+
+          memset( &lp->hash_bytes, 0x00, 8 );
+#ifdef HP100_DEBUG
+          printk("hp100: %s: computing hash filter - mc_count = %i\n", dev -> name, dev -> mc_count );
+#endif 
+          for ( i = 0, dmi = dev -> mc_list; i < dev -> mc_count; i++, dmi = dmi -> next )
+            {
+              addrs = dmi -> dmi_addr;
+              if ( ( *addrs & 0x01 ) == 0x01 )	/* multicast address? */
+                {
+#ifdef HP100_DEBUG
+                  printk("hp100: %s: multicast = %02x:%02x:%02x:%02x:%02x:%02x, ",
+                  	dev -> name,
+        		addrs[ 0 ], addrs[ 1 ], addrs[ 2 ],
+        		addrs[ 3 ], addrs[ 4 ], addrs[ 5 ] );
+#endif 
+                  for ( j = idx = 0; j < 6; j++ )
+                    {
+                      idx ^= *addrs++ & 0x3f;
+                      printk( ":%02x:", idx );
+                    }
+#ifdef HP100_DEBUG
+                  printk("idx = %i\n", idx );
+#endif
+		  lp->hash_bytes[ idx >> 3 ] |= ( 1 << ( idx & 7 ) );
+                }
+            }
+        }
+#else
+      memset( &lp->hash_bytes, 0xff, 8 );
 #endif
-			lp->hub_status = hp100_login_to_vg_hub(dev, TRUE);	/* force a relogin to the hub */
-		}
-	}
-	hp100_page(MAC_CTRL);
-	hp100_orb(HP100_RX_EN | HP100_RX_IDLE |		/* enable rx */
-		  HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1);	/* enable tx */
-
-	hp100_page(PERFORMANCE);
-	hp100_ints_on();
-	sti();
+    }
+  else
+    {
+      lp->mac2_mode = HP100_MAC2MODE3;  /* normal mode = get packets for me */
+      lp->mac1_mode = HP100_MAC1MODE3;  /* and broadcasts */
+      memset( &lp->hash_bytes, 0x00, 8 );
+    }
+
+  if ( ( (hp100_inb(MAC_CFG_1) & 0x0f)!=lp->mac1_mode ) ||
+        ( hp100_inb(MAC_CFG_2)!=lp->mac2_mode ) ) 
+    {
+      int i;
+    
+      hp100_outb( lp->mac2_mode, MAC_CFG_2 );
+      hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 ); /* clear mac1 mode bits */
+      hp100_orb( lp->mac1_mode, MAC_CFG_1 );       /* and set the new mode */
+
+      hp100_page( MAC_ADDRESS );
+      for ( i = 0; i < 8; i++ )
+        hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i );
+#ifdef HP100_DEBUG
+      printk("hp100: %s: mac1 = 0x%x, mac2 = 0x%x, multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
+      		dev->name, lp->mac1_mode, lp->mac2_mode,
+      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ],
+      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ],
+      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ],
+      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ]
+      		);
+#endif 
+
+      if(lp->lan_type==HP100_LAN_100)
+        {
+#ifdef HP100_DEBUG
+  	  printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name);
+#endif 
+	  lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */
+        }
+    }
+   else
+    {
+      int i;
+      u_char old_hash_bytes[ 8 ];
+
+      hp100_page( MAC_ADDRESS );
+      for ( i = 0; i < 8; i++ )
+        old_hash_bytes[ i ] = hp100_inb( HASH_BYTE0 + i );
+      if ( memcmp( old_hash_bytes, &lp->hash_bytes, 8 ) )
+        {
+          for ( i = 0; i < 8; i++ )
+            hp100_outb( lp->hash_bytes[ i ], HASH_BYTE0 + i );
+#ifdef HP100_DEBUG
+          printk("hp100: %s: multicast hash = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", 
+          	dev->name,
+      		lp->hash_bytes[ 0 ], lp->hash_bytes[ 1 ],
+      		lp->hash_bytes[ 2 ], lp->hash_bytes[ 3 ],
+      		lp->hash_bytes[ 4 ], lp->hash_bytes[ 5 ],
+      		lp->hash_bytes[ 6 ], lp->hash_bytes[ 7 ]
+      		);
+#endif 
+
+          if(lp->lan_type==HP100_LAN_100)
+            {
+#ifdef HP100_DEBUG
+  	      printk("hp100: %s: 100VG MAC settings have changed - relogin.\n", dev->name);
+#endif 
+   	      lp->hub_status=hp100_login_to_vg_hub( dev, TRUE );  /* force a relogin to the hub */
+            }
+        }
+    }
+
+  hp100_page( MAC_CTRL );
+  hp100_orb( HP100_RX_EN | HP100_RX_IDLE |              /* enable rx */
+	     HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );  /* enable tx */
+
+  hp100_page( PERFORMANCE );
+  hp100_ints_on();
+  restore_flags( flags );
 }
-
 
+
 /*
  *  hardware interrupt handling
  */
 
-static void hp100_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void hp100_interrupt( int irq, void *dev_id, struct pt_regs *regs )
 {
-	struct device *dev = dev_id;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
+  struct device *dev = (struct device *)dev_id;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 
-	int ioaddr;
-	u_int val;
+  int ioaddr;
+  u_int val;
 
-	if (dev == NULL)
-		return;
-	ioaddr = dev->base_addr;
+  if ( dev == NULL ) return;
+  ioaddr = dev->base_addr;
 
-	if (dev->interrupt)
-		printk("%s: re-entering the interrupt handler\n", dev->name);
-	hp100_ints_off();
-	dev->interrupt = 1;	/* mark that we are inside the handler */
+  if ( dev->interrupt )
+    printk( "hp100: %s: re-entering the interrupt handler\n", dev->name );
+  hp100_ints_off();
+  dev->interrupt = 1;          /* mark that we are inside the handler */
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4219, TRACE);
+  hp100_outw( 0x4219, TRACE );
 #endif
 
-	/*  hp100_page( PERFORMANCE ); */
-	val = hp100_inw(IRQ_STATUS);
+  /*  hp100_page( PERFORMANCE ); */
+  val = hp100_inw( IRQ_STATUS );
 #ifdef HP100_DEBUG_IRQ
-	printk("hp100: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n",
-	       lp->mode,
-	       (u_int) val,
-	       hp100_inb(RX_PKT_CNT),
-	       hp100_inb(RX_PDL),
-	       hp100_inb(TX_PKT_CNT),
-	       hp100_inb(TX_PDL)
-	    );
-#endif
-
-	if (val == 0) {		/* might be a shared interrupt */
-		dev->interrupt = 0;
-		hp100_ints_on();
-		return;
-	}
-	/* We're only interested in those interrupts we really enabled. */
-	/* val &= hp100_inw( IRQ_MASK ); */
-
-	/* 
-	 * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL 
-	 * is considered executed whenever the RX_PDL data structure is no longer 
-	 * needed.
-	 */
-	if (val & HP100_RX_PDL_FILL_COMPL) {
-		if (lp->mode == 1)
-			hp100_rx_bm(dev);
-		else
-			printk("hp100: rx_pdl_fill_compl interrupt although not busmaster?\n");
-	}
-	/* 
-	 * The RX_PACKET interrupt is set, when the receive packet counter is
-	 * non zero. We use this interrupt for receiving in slave mode. In
-	 * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill
-	 * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then
-	 * we somehow have missed a rx_pdl_fill_compl interrupt.
-	 */
-
-	if (val & HP100_RX_PACKET) {	/* Receive Packet Counter is non zero */
-		if (lp->mode != 1)	/* non busmaster */
-			hp100_rx(dev);
-		else if (!(val & HP100_RX_PDL_FILL_COMPL)) {
-			/* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt?  */
-			hp100_rx_bm(dev);
-		}
-	}
-	/*
-	 * Ack. that we have noticed the interrupt and thereby allow next one.
-	 * Note that this is now done after the slave rx function, since first
-	 * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt
-	 * on the J2573.
-	 */
-	hp100_outw(val, IRQ_STATUS);
-
-	/*
-	 * RX_ERROR is set when a packet is dropped due to no memory resources on 
-	 * the card or when a RCV_ERR occurs. 
-	 * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists  
-	 * only in the 802.3 MAC and happens when 16 collisions occur during a TX 
-	 */
-	if (val & (HP100_TX_ERROR | HP100_RX_ERROR)) {
+  printk( "hp100: %s: mode=%x,IRQ_STAT=0x%.4x,RXPKTCNT=0x%.2x RXPDL=0x%.2x TXPKTCNT=0x%.2x TXPDL=0x%.2x\n",
+  	  dev->name,
+          lp->mode,
+	  (u_int)val,
+          hp100_inb( RX_PKT_CNT ),
+          hp100_inb( RX_PDL ),
+	  hp100_inb( TX_PKT_CNT ),
+	  hp100_inb( TX_PDL )
+	  );
+#endif
+
+  if(val==0) /* might be a shared interrupt */ 
+    {
+      dev->interrupt=0;
+      hp100_ints_on();
+      return;
+    }
+  /* We're only interested in those interrupts we really enabled. */
+  /* val &= hp100_inw( IRQ_MASK ); */
+
+  /* 
+   * RX_PDL_FILL_COMPL is set whenever a RX_PDL has been executed. A RX_PDL 
+   * is considered executed whenever the RX_PDL data structure is no longer 
+   * needed.
+   */
+  if ( val & HP100_RX_PDL_FILL_COMPL )
+    {
+      if(lp->mode==1)
+	hp100_rx_bm( dev );
+      else
+	{
+	  printk("hp100: %s: rx_pdl_fill_compl interrupt although not busmaster?\n", dev->name);
+	}
+    }
+		
+  /* 
+   * The RX_PACKET interrupt is set, when the receive packet counter is
+   * non zero. We use this interrupt for receiving in slave mode. In
+   * busmaster mode, we use it to make sure we did not miss any rx_pdl_fill
+   * interrupts. If rx_pdl_fill_compl is not set and rx_packet is set, then
+   * we somehow have missed a rx_pdl_fill_compl interrupt.
+   */
+
+  if ( val & HP100_RX_PACKET  ) /* Receive Packet Counter is non zero */
+    {
+      if(lp->mode!=1) /* non busmaster */
+        hp100_rx( dev );
+      else if ( !(val & HP100_RX_PDL_FILL_COMPL ))
+	{
+	  /* Shouldnt happen - maybe we missed a RX_PDL_FILL Interrupt?  */
+	  hp100_rx_bm( dev );
+	}
+    }
+
+  /*
+   * Ack. that we have noticed the interrupt and thereby allow next one.
+   * Note that this is now done after the slave rx function, since first
+   * acknowledging and then setting ADV_NXT_PKT caused an extra interrupt
+   * on the J2573.
+   */
+  hp100_outw( val, IRQ_STATUS );
+
+  /*
+   * RX_ERROR is set when a packet is dropped due to no memory resources on 
+   * the card or when a RCV_ERR occurs. 
+   * TX_ERROR is set when a TX_ABORT condition occurs in the MAC->exists  
+   * only in the 802.3 MAC and happens when 16 collisions occur during a TX 
+   */
+  if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) )
+    {
 #ifdef HP100_DEBUG_IRQ
-		printk("hp100: TX/RX Error IRQ\n");
+      printk("hp100: %s: TX/RX Error IRQ\n", dev->name);
 #endif
-		hp100_update_stats(dev);
-		if (lp->mode == 1) {
-			hp100_rxfill(dev);
-			hp100_clean_txring(dev);
-		}
-	}
-	/* 
-	 * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. 
-	 */
-	if ((lp->mode == 1) && (val & (HP100_RX_PDA_ZERO)))
-		hp100_rxfill(dev);
-
-	/* 
-	 * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire 
-	 * is completed 
-	 */
-	if ((lp->mode == 1) && (val & (HP100_TX_COMPLETE)))
-		hp100_clean_txring(dev);
-
-	/* 
-	 * MISC_ERROR is set when either the LAN link goes down or a detected
-	 * bus error occurs.
-	 */
-	if (val & HP100_MISC_ERROR) {	/* New for J2585B */
-		printk("hp100: Misc. Error Interrupt - Check cabling.\n");
-		if (lp->mode == 1) {
-			hp100_clean_txring(dev);
-			hp100_rxfill(dev);
-		}
-	}
-	dev->interrupt = 0;
-	hp100_ints_on();
+      hp100_update_stats( dev );
+      if(lp->mode==1)
+	{
+	  hp100_rxfill( dev );
+	  hp100_clean_txring( dev );
+	}
+    }
+				
+  /* 
+   * RX_PDA_ZERO is set when the PDA count goes from non-zero to zero. 
+   */
+  if ( (lp->mode==1)&&(val &(HP100_RX_PDA_ZERO)) )
+    hp100_rxfill( dev );
+		
+  /* 
+   * HP100_TX_COMPLETE interrupt occurs when packet transmitted on wire 
+   * is completed 
+   */
+  if ( (lp->mode==1) && ( val & ( HP100_TX_COMPLETE )) )
+    hp100_clean_txring( dev );
+
+  /* 
+   * MISC_ERROR is set when either the LAN link goes down or a detected
+   * bus error occurs.
+   */
+  if ( val & HP100_MISC_ERROR ) /* New for J2585B */
+    {
+#ifdef HP100_DEBUG_IRQ
+      printk("hp100: %s: Misc. Error Interrupt - Check cabling.\n", dev->name);
+#endif
+      if(lp->mode==1)
+	{
+	  hp100_clean_txring( dev );
+	  hp100_rxfill( dev );
+	}
+      hp100_misc_interrupt( dev );
+    }
+	
+  dev->interrupt = 0;
+  hp100_ints_on();
 }
-
 
+
 /*
  *  some misc functions
  */
 
-static void hp100_start_interface(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4220, TRACE);
-	printk("hp100: hp100_start_interface %s\n", dev->name);
-#endif
-
-	cli();
-
-	/* Ensure the adapter does not want to request an interrupt when */
-	/* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */
-	hp100_page(PERFORMANCE);
-	hp100_outw(0xfefe, IRQ_MASK);	/* mask off all ints */
-	hp100_outw(0xffff, IRQ_STATUS);		/* ack all IRQs */
-	hp100_outw(HP100_FAKE_INT | HP100_INT_EN | HP100_RESET_LB, OPTION_LSW);
-	/* Un Tri-state int. TODO: Check if shared interrupts can be realised? */
-	hp100_outw(HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW);
-
-	if (lp->mode == 1) {
-		/* Make sure BM bit is set... */
-		hp100_page(HW_MAP);
-		hp100_orb(HP100_BM_MASTER, BM);
-		hp100_rxfill(dev);
-	} else if (lp->mode == 2) {
-		/* Enable memory mapping. Note: Don't do this when busmaster. */
-		hp100_outw(HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW);
-	}
-	hp100_page(PERFORMANCE);
-	hp100_outw(0xfefe, IRQ_MASK);	/* mask off all ints */
-	hp100_outw(0xffff, IRQ_STATUS);		/* ack IRQ */
-
-	/* enable a few interrupts: */
-	if (lp->mode == 1) {	/* busmaster mode */
-		hp100_outw(HP100_RX_PDL_FILL_COMPL |
-			   HP100_RX_PDA_ZERO |
-			   HP100_RX_ERROR |
-		/* HP100_RX_PACKET    | */
-		/* HP100_RX_EARLY_INT |  */ HP100_SET_HB |
-		/* HP100_TX_PDA_ZERO  |  */
-			   HP100_TX_COMPLETE |
-		/* HP100_MISC_ERROR   |  */
-			   HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK);
-	} else {
-		hp100_outw(HP100_RX_PACKET |
-			   HP100_RX_ERROR | HP100_SET_HB |
-			   HP100_TX_ERROR | HP100_SET_LB, IRQ_MASK);
-	}
-
-	/* Enable MAC Tx and RX, set MAC modes, ... */
-	/* Note: This function also turns on the interrupts. */
-	hp100_set_multicast_list(dev);
-}
-
-
-static void hp100_stop_interface(struct device *dev)
+static void hp100_start_interface( struct device *dev )
 {
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	int ioaddr = dev->base_addr;
-	u_int val;
-
-#ifdef HP100_DEBUG_B
-	printk("hp100: hp100_stop_interface %s\n", dev->name);
-	hp100_outw(0x4221, TRACE);
-#endif
-
-	if (lp->mode == 1)
-		hp100_BM_shutdown(dev);
-	else {
-		/* Note: MMAP_DIS will be reenabled by start_interface */
-		hp100_outw(HP100_INT_EN | HP100_RESET_LB |
-			   HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW);
-		val = hp100_inw(OPTION_LSW);
-
-		hp100_page(MAC_CTRL);
-		hp100_andb(~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1);
-
-		if (!(val & HP100_HW_RST))
-			return;	/* If reset, imm. return ... */
-		/* ... else: busy wait until idle */
-		for (val = 0; val < 6000; val++)
-			if ((hp100_inb(MAC_CFG_1) & (HP100_TX_IDLE | HP100_RX_IDLE)) ==
-			    (HP100_TX_IDLE | HP100_RX_IDLE)) {
-				hp100_page(PERFORMANCE);
-				return;
-			}
-		printk("%s: hp100_stop_interface - timeout\n", dev->name);
-		hp100_page(PERFORMANCE);
-	}
+  unsigned long flags;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4220, TRACE );
+  printk("hp100: %s: hp100_start_interface\n",dev->name);
+#endif
+
+  save_flags( flags );
+  cli();
+
+  /* Ensure the adapter does not want to request an interrupt when */
+  /* enabling the IRQ line to be active on the bus (i.e. not tri-stated) */
+  hp100_page( PERFORMANCE );
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack all IRQs */
+  hp100_outw( HP100_FAKE_INT|HP100_INT_EN|HP100_RESET_LB, OPTION_LSW);
+  /* Un Tri-state int. TODO: Check if shared interrupts can be realised? */
+  hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW ); 
+
+  if(lp->mode==1)
+    {
+      /* Make sure BM bit is set... */
+      hp100_page(HW_MAP);
+      hp100_orb( HP100_BM_MASTER, BM );
+      hp100_rxfill( dev );
+    }
+  else if(lp->mode==2)
+    {
+      /* Enable memory mapping. Note: Don't do this when busmaster. */
+      hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
+    }
+
+  hp100_page(PERFORMANCE);
+  hp100_outw( 0xfefe, IRQ_MASK );  /* mask off all ints */
+  hp100_outw( 0xffff, IRQ_STATUS );  /* ack IRQ */
+
+  /* enable a few interrupts: */
+  if(lp->mode==1) /* busmaster mode */
+    {
+      hp100_outw( HP100_RX_PDL_FILL_COMPL |
+		  HP100_RX_PDA_ZERO  |  
+		  HP100_RX_ERROR     |  
+		  /* HP100_RX_PACKET    | */    
+		  /* HP100_RX_EARLY_INT |  */     HP100_SET_HB  |  
+		  /* HP100_TX_PDA_ZERO  |  */
+		  HP100_TX_COMPLETE  |  
+		  /* HP100_MISC_ERROR   |  */
+		  HP100_TX_ERROR     | HP100_SET_LB, IRQ_MASK );
+    } 
+  else
+    {  
+      hp100_outw( HP100_RX_PACKET  |
+		  HP100_RX_ERROR   | HP100_SET_HB |
+                  HP100_TX_ERROR   | HP100_SET_LB , IRQ_MASK );
+    }
+	
+  /* Enable MAC Tx and RX, set MAC modes, ... */
+  hp100_set_multicast_list( dev );
+
+  restore_flags( flags );
+}
+
+
+static void hp100_stop_interface( struct device *dev )
+{
+  struct hp100_private *lp = (struct hp100_private *)dev->priv; 
+  int ioaddr = dev->base_addr;
+  u_int val;
+
+#ifdef HP100_DEBUG_B
+  printk("hp100: %s: hp100_stop_interface\n",dev->name);
+  hp100_outw( 0x4221, TRACE );
+#endif
+
+  if (lp->mode==1) 
+    hp100_BM_shutdown( dev );
+  else
+    {
+      /* Note: MMAP_DIS will be reenabled by start_interface */
+      hp100_outw( HP100_INT_EN | HP100_RESET_LB | 
+                  HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+      val = hp100_inw( OPTION_LSW );
+   
+      hp100_page( MAC_CTRL );
+      hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
+
+      if ( !(val & HP100_HW_RST) ) return; /* If reset, imm. return ... */
+      /* ... else: busy wait until idle */
+      for ( val = 0; val < 6000; val++ )
+	if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
+	     (HP100_TX_IDLE | HP100_RX_IDLE) )
+	  {
+	    hp100_page(PERFORMANCE);
+	    return;
+	  }
+      printk( "hp100: %s: hp100_stop_interface - timeout\n", dev->name );
+      hp100_page(PERFORMANCE);
+    }
+}
+
+
+static void hp100_load_eeprom( struct device *dev, u_short probe_ioaddr )
+{
+  int i;
+  int ioaddr = probe_ioaddr > 0 ? probe_ioaddr : dev->base_addr;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4222, TRACE );
+#endif
+
+  hp100_page( EEPROM_CTRL );
+  hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL );
+  hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL );
+  for ( i = 0; i < 10000; i++ )
+    if ( !( hp100_inb( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
+  printk( "hp100: %s: hp100_load_eeprom - timeout\n", dev->name );
 }
-
 
-static void hp100_load_eeprom(struct device *dev)
-{
-	int i;
-	int ioaddr = dev->base_addr;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4222, TRACE);
-#endif
-
-	hp100_page(EEPROM_CTRL);
-	hp100_andw(~HP100_EEPROM_LOAD, EEPROM_CTRL);
-	hp100_orw(HP100_EEPROM_LOAD, EEPROM_CTRL);
-	for (i = 0; i < 10000; i++)
-		if (!(hp100_inb(OPTION_MSW) & HP100_EE_LOAD))
-			return;
-	printk("%s: hp100_load_eeprom - timeout\n", dev->name);
-}
 
-
 /*  Sense connection status.
  *  return values: LAN_10  - Connected to 10Mbit/s network
  *                 LAN_100 - Connected to 100Mbit/s network
  *                 LAN_ERR - not connected or 100Mbit/s Hub down
  */
-static int hp100_sense_lan(struct device *dev)
+static int hp100_sense_lan( struct device *dev )
 {
-	int ioaddr = dev->base_addr;
-	u_short val_VG, val_10;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
+  int ioaddr = dev->base_addr;
+  u_short val_VG, val_10;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4223, TRACE);
+  hp100_outw( 0x4223, TRACE );
 #endif
 
-	hp100_page(MAC_CTRL);
-	/* Enable Auto Selection */
-	/* hp100_orb( HP100_VG_RESET|HP100_LINK_CMD|HP100_VG_SEL, VG_LAN_CFG_1 ); */
-	/* hp100_orb( HP100_DOT3_MAC,10_LAN_CFG_2); */
-	/* hp100_orb( HP100_AUTO_MODE,MAC_CFG_3); */
-	/* Now we have to wait a while... */
-	/* for(i=0; i<5000; i++) */
-	/* { */
-	val_10 = hp100_inb(10_LAN_CFG_1);
-	val_VG = hp100_inb(VG_LAN_CFG_1);
-	/* } */
+  hp100_page( MAC_CTRL );
+  val_10 = hp100_inb( 10_LAN_CFG_1 );
+  val_VG = hp100_inb( VG_LAN_CFG_1 );
+  hp100_page( PERFORMANCE );
 #ifdef HP100_DEBUG
-	printk("hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10);
+  printk( "hp100: %s: sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", dev->name, val_VG, val_10 );
 #endif
-	if (val_10 & HP100_LINK_BEAT_ST)
-		return HP100_LAN_10;
-	if ((lp->id->id == 0x02019F022) ||
-	    (lp->id->id == 0x01042103c) ||
-	    (lp->id->id == 0x01040103c)) {
-		hp100_page(PERFORMANCE);
-		return HP100_LAN_ERR;	/* Those cards don't have a 100 Mbit connector */
-	}
-	/* for ( i = 0; i < 2500; i++ ) */
-	/*     { */
-	val_VG = hp100_inb(VG_LAN_CFG_1);
-	hp100_page(PERFORMANCE);
 
-	if (val_VG & HP100_LINK_CABLE_ST)	/* Can hear the HUBs tone. */
-		return HP100_LAN_100;
-	/*   } */
-	return HP100_LAN_ERR;
+  if ( val_10 & HP100_LINK_BEAT_ST )	/* 10Mb connection is active */
+    return HP100_LAN_10;
+
+  if ( val_10 & HP100_AUI_ST )		/* have we BNC or AUI onboard? */
+    {
+      val_10 |= HP100_AUI_SEL | HP100_LOW_TH;
+      hp100_page( MAC_CTRL );
+      hp100_outb( val_10, 10_LAN_CFG_1 );
+      hp100_page( PERFORMANCE );
+      return HP100_LAN_10;
+    }
+
+  if ( (lp->id->id == 0x02019F022) || 
+       (lp->id->id == 0x01042103c) ||
+       (lp->id->id == 0x01040103c) )
+    return HP100_LAN_ERR; /* Those cards don't have a 100 Mbit connector */
+
+  if ( val_VG & HP100_LINK_CABLE_ST ) /* Can hear the HUBs tone. */ 
+    return HP100_LAN_100;
+  return HP100_LAN_ERR;
 }
-
 
 
-static int hp100_down_vg_link(struct device *dev)
+
+static int hp100_down_vg_link( struct device *dev )
 {
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	int ioaddr = dev->base_addr;
-	unsigned long time;
-	long savelan, newlan;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int ioaddr = dev->base_addr;
+  unsigned long time;
+  long savelan, newlan;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4224, TRACE);
-	printk("hp100: down_vg_link\n");
+  hp100_outw( 0x4224, TRACE );
+  printk("hp100: %s: down_vg_link\n", dev->name);
 #endif
 
-	hp100_page(MAC_CTRL);
-	time = jiffies + (HZ / 4);
-	do {
-		if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST)
-			break;
-	} while (time > jiffies);
+  hp100_page( MAC_CTRL );
+  time=jiffies+(HZ/4);
+  do{
+    if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
+  } while (time>jiffies);
 
-	if (jiffies >= time)	/* no signal->no logout */
-		return 0;
+  if ( jiffies >= time )       /* no signal->no logout */
+    return 0;
 
-	/* Drop the VG Link by clearing the link up cmd and load addr. */
+  /* Drop the VG Link by clearing the link up cmd and load addr.*/
 
-	hp100_andb(~(HP100_LOAD_ADDR | HP100_LINK_CMD), VG_LAN_CFG_1);
-	hp100_orb(HP100_VG_SEL, VG_LAN_CFG_1);
+  hp100_andb( ~( HP100_LOAD_ADDR| HP100_LINK_CMD), VG_LAN_CFG_1); 
+  hp100_orb( HP100_VG_SEL, VG_LAN_CFG_1); 
 
-	/* Conditionally stall for >250ms on Link-Up Status (to go down) */
-	time = jiffies + (HZ / 2);
-	do {
-		if (!(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST))
-			break;
-	} while (time > jiffies);
+  /* Conditionally stall for >250ms on Link-Up Status (to go down) */
+  time=jiffies+(HZ/2);
+  do{
+    if ( !(hp100_inb( VG_LAN_CFG_1) & HP100_LINK_UP_ST) ) break;
+  } while(time>jiffies);
 
 #ifdef HP100_DEBUG
-	if (jiffies >= time)
-		printk("hp100_down_vg_link: Link does not go down?\n");
+  if (jiffies>=time)
+    printk("hp100: %s: down_vg_link: Link does not go down?\n", dev->name);
 #endif
 
-	/* To prevent condition where Rev 1 VG MAC and old hubs do not complete */
-	/* logout under traffic (even though all the status bits are cleared),  */
-	/* do this workaround to get the Rev 1 MAC in its idle state */
-	if (lp->chip == HP100_CHIPID_LASSEN) {
-		/* Reset VG MAC to insure it leaves the logoff state even if */
-		/* the Hub is still emitting tones */
-		hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1);
-		udelay(1500);	/* wait for >1ms */
-		hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1);	/* Release Reset */
-		udelay(1500);
-	}
-	/* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */
-	/* to get the VG mac to full reset. This is not req.d with later chips */
-	/* Note: It will take the between 1 and 2 seconds for the VG mac to be */
-	/* selected again! This will be left to the connect hub function to */
-	/* perform if desired.  */
-	if (lp->chip == HP100_CHIPID_LASSEN) {
-		/* Have to write to 10 and 100VG control registers simultaneously */
-		savelan = newlan = hp100_inl(10_LAN_CFG_1);	/* read 10+100 LAN_CFG regs */
-		newlan &= ~(HP100_VG_SEL << 16);
-		newlan |= (HP100_DOT3_MAC) << 8;
-		hp100_andb(~HP100_AUTO_MODE, MAC_CFG_3);	/* Autosel off */
-		hp100_outl(newlan, 10_LAN_CFG_1);
+  /* To prevent condition where Rev 1 VG MAC and old hubs do not complete */
+  /* logout under traffic (even though all the status bits are cleared),  */
+  /* do this workaround to get the Rev 1 MAC in its idle state */
+  if ( lp->chip==HP100_CHIPID_LASSEN )
+    {
+      /* Reset VG MAC to insure it leaves the logoff state even if */
+      /* the Hub is still emitting tones */
+      hp100_andb(~HP100_VG_RESET, VG_LAN_CFG_1);
+      udelay(1500); /* wait for >1ms */
+      hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1); /* Release Reset */
+      udelay(1500);
+    }
 
-		/* Conditionally stall for 5sec on VG selected. */
-		time = jiffies + (HZ * 5);
-		do {
-			if (!(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST))
-				break;
-		} while (time > jiffies);
+  /* New: For lassen, switch to 10 Mbps mac briefly to clear training ACK */
+  /* to get the VG mac to full reset. This is not req.d with later chips */
+  /* Note: It will take the between 1 and 2 seconds for the VG mac to be */
+  /* selected again! This will be left to the connect hub function to */
+  /* perform if desired.  */
+  if (lp->chip==HP100_CHIPID_LASSEN)
+    {
+      /* Have to write to 10 and 100VG control registers simultaneously */
+      savelan=newlan=hp100_inl(10_LAN_CFG_1); /* read 10+100 LAN_CFG regs */
+      newlan &= ~(HP100_VG_SEL<<16);
+      newlan |= (HP100_DOT3_MAC)<<8;
+      hp100_andb( ~HP100_AUTO_MODE, MAC_CFG_3); /* Autosel off */
+      hp100_outl(newlan, 10_LAN_CFG_1);
 
-		hp100_orb(HP100_AUTO_MODE, MAC_CFG_3);	/* Autosel back on */
-		hp100_outl(savelan, 10_LAN_CFG_1);
-	}
-	time = jiffies + (3 * HZ);	/* Timeout 3s */
-	do {
-		if ((hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST) == 0)
-			break;
-	} while (time > jiffies);
+      /* Conditionally stall for 5sec on VG selected. */
+      time=jiffies+(HZ*5);
+      do{
+        if( !(hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST) ) break;
+      } while(time>jiffies);
+
+      hp100_orb( HP100_AUTO_MODE, MAC_CFG_3); /* Autosel back on */
+      hp100_outl(savelan, 10_LAN_CFG_1);
+    }
 
-	if (time <= jiffies) {
+  time=jiffies+(3*HZ); /* Timeout 3s */
+  do {
+    if ( (hp100_inb( VG_LAN_CFG_1 )&HP100_LINK_CABLE_ST) == 0) break;
+  } while (time>jiffies);
+  
+  if(time<=jiffies)
+    {
 #ifdef HP100_DEBUG
-		printk("hp100_down_vg_link: timeout\n");
+      printk( "hp100: %s: down_vg_link: timeout\n", dev->name );
 #endif
-		return -EIO;
-	}
-	time = jiffies + (2 * HZ);	/* This seems to take a while.... */
-	do {
-	} while (time > jiffies);
-
-	return 0;
+      return -EIO;
+    }
+  
+  time=jiffies+(2*HZ); /* This seems to take a while.... */
+  do {} while (time>jiffies);
+  
+  return 0;
 }
-
 
-static int hp100_login_to_vg_hub(struct device *dev, u_short force_relogin)
+
+static int hp100_login_to_vg_hub( struct device *dev, u_short force_relogin )
 {
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	u_short val = 0;
-	unsigned long time;
-	int startst;
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  u_short val=0;
+  unsigned long time;
+  int startst;
 
 #ifdef HP100_DEBUG_B
-	hp100_outw(0x4225, TRACE);
-	printk("hp100: login_to_vg_hub\n");
+  hp100_outw( 0x4225, TRACE );
+  printk("hp100: %s: login_to_vg_hub\n", dev->name);
 #endif
 
-	/* Initiate a login sequence iff VG MAC is enabled and either Load Address
-	 * bit is zero or the force relogin flag is set (e.g. due to MAC address or
-	 * promiscuous mode change)
-	 */
-	hp100_page(MAC_CTRL);
-	startst = hp100_inb(VG_LAN_CFG_1);
-	if ((force_relogin == TRUE) || (hp100_inb(MAC_CFG_4) & HP100_MAC_SEL_ST)) {
+  /* Initiate a login sequence iff VG MAC is enabled and either Load Address
+   * bit is zero or the force relogin flag is set (e.g. due to MAC address or
+   * promiscuous mode change)
+   */
+  hp100_page( MAC_CTRL );
+  startst=hp100_inb( VG_LAN_CFG_1 );
+  if((force_relogin==TRUE)||(hp100_inb( MAC_CFG_4 )&HP100_MAC_SEL_ST))
+    {
 #ifdef HP100_DEBUG_TRAINING
-		printk("hp100: Start training\n");
+      printk("hp100: %s: Start training\n", dev->name);
 #endif
 
-		/* Ensure VG Reset bit is 1 (i.e., do not reset) */
-		hp100_orb(HP100_VG_RESET, VG_LAN_CFG_1);
+      /* Ensure VG Reset bit is 1 (i.e., do not reset)*/
+      hp100_orb( HP100_VG_RESET , VG_LAN_CFG_1 );
 
-		/* If Lassen AND auto-select-mode AND VG tones were sensed on */
-		/* entry then temporarily put them into force 100Mbit mode */
-		if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST))
-			hp100_andb(~HP100_DOT3_MAC, 10_LAN_CFG_2);
-
-		/* Drop the VG link by zeroing Link Up Command and Load Address  */
-		hp100_andb(~(HP100_LINK_CMD /* |HP100_LOAD_ADDR */ ), VG_LAN_CFG_1);
+      /* If Lassen AND auto-select-mode AND VG tones were sensed on */
+      /* entry then temporarily put them into force 100Mbit mode */
+      if((lp->chip==HP100_CHIPID_LASSEN)&&( startst & HP100_LINK_CABLE_ST ) )
+        hp100_andb( ~HP100_DOT3_MAC, 10_LAN_CFG_2 );
+						
+      /* Drop the VG link by zeroing Link Up Command and Load Address  */
+      hp100_andb( ~(HP100_LINK_CMD/* |HP100_LOAD_ADDR */), VG_LAN_CFG_1);
 
 #ifdef HP100_DEBUG_TRAINING
-		printk("hp100: Bring down the link\n");
+      printk("hp100: %s: Bring down the link\n", dev->name);
 #endif
 
-		/* Wait for link to drop */
-		time = jiffies + (HZ / 10);
-		do {
-			if (~(hp100_inb(VG_LAN_CFG_1) & HP100_LINK_UP_ST))
-				break;
-		} while (time > jiffies);
-
-		/* Start an addressed training and optionally request promiscuous port */
-		if ((dev->flags) & IFF_PROMISC) {
-			hp100_orb(HP100_PROM_MODE, VG_LAN_CFG_2);
-			if (lp->chip == HP100_CHIPID_LASSEN)
-				hp100_orw(HP100_MACRQ_PROMSC, TRAIN_REQUEST);
-		} else {
-			hp100_andb(~HP100_PROM_MODE, VG_LAN_CFG_2);
-			/* For ETR parts we need to reset the prom. bit in the training
-			 * register, otherwise promiscious mode won't be disabled.
-			 */
-			if (lp->chip == HP100_CHIPID_LASSEN) {
-				hp100_andw(~HP100_MACRQ_PROMSC, TRAIN_REQUEST);
-			}
-		}
-
-		/* With ETR parts, frame format request bits can be set. */
-		if (lp->chip == HP100_CHIPID_LASSEN)
-			hp100_orb(HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST);
-
-		hp100_orb(HP100_LINK_CMD | HP100_LOAD_ADDR | HP100_VG_RESET, VG_LAN_CFG_1);
-
-		/* Note: Next wait could be omitted for Hood and earlier chips under */
-		/* certain circumstances */
-		/* TODO: check if hood/earlier and skip wait. */
-
-		/* Wait for either short timeout for VG tones or long for login    */
-		/* Wait for the card hardware to signalise link cable status ok... */
-		hp100_page(MAC_CTRL);
-		time = jiffies + (1 * HZ);	/* 1 sec timeout for cable st */
-		do {
-			if (hp100_inb(VG_LAN_CFG_1) & HP100_LINK_CABLE_ST)
-				break;
-		} while (jiffies < time);
-
-		if (jiffies >= time) {
+      /* Wait for link to drop */
+      time = jiffies + (HZ/10); 
+      do {
+        if (~(hp100_inb( VG_LAN_CFG_1 )& HP100_LINK_UP_ST) ) break;
+      } while (time>jiffies);
+
+      /* Start an addressed training and optionally request promiscuous port */
+      if ( (dev->flags) & IFF_PROMISC )
+	{
+	  hp100_orb( HP100_PROM_MODE, VG_LAN_CFG_2);
+	  if(lp->chip==HP100_CHIPID_LASSEN)
+	    hp100_orw( HP100_MACRQ_PROMSC, TRAIN_REQUEST );
+	}
+      else
+	{
+	  hp100_andb( ~HP100_PROM_MODE, VG_LAN_CFG_2);
+	  /* For ETR parts we need to reset the prom. bit in the training
+	   * register, otherwise promiscious mode won't be disabled.
+	   */
+	  if(lp->chip==HP100_CHIPID_LASSEN)
+	    {
+	      hp100_andw( ~HP100_MACRQ_PROMSC, TRAIN_REQUEST );
+	    }
+	}
+
+      /* With ETR parts, frame format request bits can be set. */
+      if(lp->chip==HP100_CHIPID_LASSEN)
+        hp100_orb( HP100_MACRQ_FRAMEFMT_EITHER, TRAIN_REQUEST);
+
+      hp100_orb( HP100_LINK_CMD|HP100_LOAD_ADDR|HP100_VG_RESET, VG_LAN_CFG_1);
+
+      /* Note: Next wait could be omitted for Hood and earlier chips under */
+      /* certain circumstances */
+      /* TODO: check if hood/earlier and skip wait. */
+
+      /* Wait for either short timeout for VG tones or long for login    */
+      /* Wait for the card hardware to signalise link cable status ok... */
+      hp100_page( MAC_CTRL );
+      time = jiffies + ( 1*HZ ); /* 1 sec timeout for cable st */
+      do {
+        if ( hp100_inb( VG_LAN_CFG_1 ) & HP100_LINK_CABLE_ST ) break;
+      } while ( jiffies < time );
+      
+      if ( jiffies >= time )
+	{
 #ifdef HP100_DEBUG_TRAINING
-			printk("hp100: Link cable status not ok? Training aborted.\n");
-#endif
-		} else {
+	  printk( "hp100: %s: Link cable status not ok? Training aborted.\n", dev->name );
+#endif  
+	}
+      else
+	{
 #ifdef HP100_DEBUG_TRAINING
-			printk("hp100: HUB tones detected. Trying to train.\n");
+	  printk( "hp100: %s: HUB tones detected. Trying to train.\n", dev->name);
 #endif
 
-			time = jiffies + (2 * HZ);	/* again a timeout */
-			do {
-				val = hp100_inb(VG_LAN_CFG_1);
-				if ((val & (HP100_LINK_UP_ST))) {
+	  time = jiffies + ( 2*HZ ); /* again a timeout */
+	  do {
+	    val = hp100_inb( VG_LAN_CFG_1 );
+	    if ( (val & ( HP100_LINK_UP_ST )) )
+	      {
 #ifdef HP100_DEBUG_TRAINING
-					printk("hp100: Passed training.\n");
+		printk( "hp100: %s: Passed training.\n", dev->name);
 #endif
-					break;
-				}
-			} while (time > jiffies);
-		}
-
-		/* If LINK_UP_ST is set, then we are logged into the hub. */
-		if ((jiffies <= time) && (val & HP100_LINK_UP_ST)) {
+		break;
+	      }
+	  } while ( time > jiffies );
+	}
+      
+      /* If LINK_UP_ST is set, then we are logged into the hub. */
+      if ( (jiffies<=time) && (val & HP100_LINK_UP_ST) )
+        {
 #ifdef HP100_DEBUG_TRAINING
-			printk("hp100: Successfully logged into the HUB.\n");
-			if (lp->chip == HP100_CHIPID_LASSEN) {
-				val = hp100_inw(TRAIN_ALLOW);
-				printk("hp100: Card supports 100VG MAC Version \"%s\" ",
-				       (hp100_inw(TRAIN_REQUEST) & HP100_CARD_MACVER) ? "802.12" : "Pre");
-				printk("Driver will use MAC Version \"%s\"\n",
-				       (val & HP100_HUB_MACVER) ? "802.12" : "Pre");
-				printk("hp100: Frame format is %s.\n", (val & HP100_MALLOW_FRAMEFMT) ? "802.5" : "802.3");
-			}
-#endif
-		} else {
-			/* If LINK_UP_ST is not set, login was not successful */
-			printk("hp100/%s: Problem logging into the HUB.\n", dev->name);
-			if (lp->chip == HP100_CHIPID_LASSEN) {
-				/* Check allowed Register to find out why there is a problem. */
-				val = hp100_inw(TRAIN_ALLOW);	/* wont work on non-ETR card */
+          printk( "hp100: %s: Successfully logged into the HUB.\n", dev->name);
+          if(lp->chip==HP100_CHIPID_LASSEN)
+            {
+	      val = hp100_inw(TRAIN_ALLOW);
+              printk( "hp100: %s: Card supports 100VG MAC Version \"%s\" ",
+              	      dev->name,(hp100_inw(TRAIN_REQUEST)&HP100_CARD_MACVER) ? "802.12" : "Pre");
+	      printk( "Driver will use MAC Version \"%s\"\n",
+                      ( val & HP100_HUB_MACVER) ? "802.12" : "Pre" ); 
+              printk( "hp100: %s: Frame format is %s.\n",dev->name,(val&HP100_MALLOW_FRAMEFMT)?"802.5":"802.3");
+            }
+#endif
+        }
+      else
+        {
+          /* If LINK_UP_ST is not set, login was not successful */
+          printk("hp100: %s: Problem logging into the HUB.\n",dev->name);
+          if(lp->chip==HP100_CHIPID_LASSEN)
+            {
+              /* Check allowed Register to find out why there is a problem. */
+              val = hp100_inw( TRAIN_ALLOW ); /* wont work on non-ETR card */
 #ifdef HP100_DEBUG_TRAINING
-				printk("hp100: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", hp100_inw(TRAIN_REQUEST), val);
+              printk("hp100: %s: MAC Configuration requested: 0x%04x, HUB allowed: 0x%04x\n", dev->name, hp100_inw(TRAIN_REQUEST), val);
 #endif
-				if (val & HP100_MALLOW_ACCDENIED)
-					printk("hp100: HUB access denied.\n");
-				if (val & HP100_MALLOW_CONFIGURE)
-					printk("hp100: MAC Configuration is incompatible with the Network.\n");
-				if (val & HP100_MALLOW_DUPADDR)
-					printk("hp100: Duplicate MAC Address on the Network.\n");
-			}
-		}
-
-		/* If we have put the chip into forced 100 Mbit mode earlier, go back */
-		/* to auto-select mode */
-
-		if ((lp->chip == HP100_CHIPID_LASSEN) && (startst & HP100_LINK_CABLE_ST)) {
-			hp100_page(MAC_CTRL);
-			hp100_orb(HP100_DOT3_MAC, 10_LAN_CFG_2);
-		}
-		val = hp100_inb(VG_LAN_CFG_1);
-
-		/* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */
-		hp100_page(PERFORMANCE);
-		hp100_outw(HP100_MISC_ERROR, IRQ_STATUS);
-
-		if (val & HP100_LINK_UP_ST)
-			return (0);	/* login was ok */
-		else {
-			printk("hp100: Training failed.\n");
-			hp100_down_vg_link(dev);
-			return -EIO;
-		}
+              if ( val & HP100_MALLOW_ACCDENIED )
+                printk("hp100: %s: HUB access denied.\n", dev->name);
+              if ( val & HP100_MALLOW_CONFIGURE )
+                printk("hp100: %s: MAC Configuration is incompatible with the Network.\n", dev->name);
+              if ( val & HP100_MALLOW_DUPADDR )
+                printk("hp100: %s: Duplicate MAC Address on the Network.\n", dev->name);
+            }
+        }
+      
+      /* If we have put the chip into forced 100 Mbit mode earlier, go back */
+      /* to auto-select mode */
+      
+      if( (lp->chip==HP100_CHIPID_LASSEN)&&(startst & HP100_LINK_CABLE_ST) )
+        {
+          hp100_page( MAC_CTRL );
+          hp100_orb( HP100_DOT3_MAC, 10_LAN_CFG_2 );
+        }
+     
+      val=hp100_inb(VG_LAN_CFG_1);
+
+      /* Clear the MISC_ERROR Interrupt, which might be generated when doing the relogin */
+      hp100_page(PERFORMANCE);
+      hp100_outw( HP100_MISC_ERROR, IRQ_STATUS);
+					
+      if (val&HP100_LINK_UP_ST)
+        return(0); /* login was ok */
+      else
+        {
+          printk("hp100: %s: Training failed.\n", dev->name);
+          hp100_down_vg_link( dev );
+          return -EIO;
+        }
+    }
+  /* no forced relogin & already link there->no training. */
+  return -EIO;
+}
+
+
+static void hp100_cascade_reset( struct device *dev, u_short enable )
+{
+  int ioaddr = dev->base_addr;
+  struct hp100_private *lp = (struct hp100_private *)dev->priv;
+  int i;
+
+#ifdef HP100_DEBUG_B
+  hp100_outw( 0x4226, TRACE );
+  printk("hp100: %s: cascade_reset\n", dev->name);
+#endif
+
+  if (enable==TRUE) 
+    {
+      hp100_outw( HP100_HW_RST | HP100_RESET_LB, OPTION_LSW );
+      if(lp->chip==HP100_CHIPID_LASSEN)
+	{
+	  /* Lassen requires a PCI transmit fifo reset */
+	  hp100_page( HW_MAP );
+	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
+	  hp100_orb( HP100_PCI_RESET, PCICTRL2 );
+	  /* Wait for min. 300 ns */
+	  /* we cant use jiffies here, because it may be */
+	  /* that we have disabled the timer... */
+	  for (i=0; i<0xffff; i++);
+	  hp100_andb( ~HP100_PCI_RESET, PCICTRL2 );
+	  hp100_page( PERFORMANCE );
+	}	
+    }
+  else
+    { /* bring out of reset */
+      hp100_outw(HP100_HW_RST|HP100_SET_LB, OPTION_LSW);
+      for (i=0; i<0xffff; i++ );
+      hp100_page(PERFORMANCE);
+    }
+}
+
+#ifdef HP100_DEBUG		
+void hp100_RegisterDump( struct device *dev )
+{
+  int ioaddr=dev->base_addr;
+  int Page;
+  int Register;
+
+  /* Dump common registers */
+  printk("hp100: %s: Cascade Register Dump\n", dev->name);
+  printk("hardware id #1: 0x%.2x\n",hp100_inb(HW_ID));
+  printk("hardware id #2/paging: 0x%.2x\n",hp100_inb(PAGING));
+  printk("option #1: 0x%.4x\n",hp100_inw(OPTION_LSW));
+  printk("option #2: 0x%.4x\n",hp100_inw(OPTION_MSW));
+
+  /* Dump paged registers */
+  for (Page = 0; Page < 8; Page++) 
+    {
+      /* Dump registers */
+      printk("page: 0x%.2x\n",Page);
+      outw( Page, ioaddr+0x02);
+      for (Register = 0x8; Register < 0x22; Register += 2)
+	{
+	  /* Display Register contents except data port */
+	  if (((Register != 0x10) && (Register != 0x12)) || (Page > 0))
+	    {
+	      printk("0x%.2x = 0x%.4x\n",Register,inw(ioaddr+Register));
+	    }
 	}
-	/* no forced relogin & already link there->no training. */
-	return -EIO;
+    }
+  hp100_page(PERFORMANCE);
 }
-
-
-static void hp100_cascade_reset(struct device *dev, u_short enable)
-{
-	int ioaddr = dev->base_addr;
-	struct hp100_private *lp = (struct hp100_private *) dev->priv;
-	int i;
-
-#ifdef HP100_DEBUG_B
-	hp100_outw(0x4226, TRACE);
-	printk("hp100: cascade_reset\n");
 #endif
 
-	if (enable == TRUE) {
-		hp100_outw(HP100_HW_RST | HP100_RESET_LB, OPTION_LSW);
-		if (lp->chip == HP100_CHIPID_LASSEN) {
-			/* Lassen requires a PCI transmit fifo reset */
-			hp100_page(HW_MAP);
-			hp100_andb(~HP100_PCI_RESET, PCICTRL2);
-			hp100_orb(HP100_PCI_RESET, PCICTRL2);
-			/* Wait for min. 300 ns */
-			/* we cant use jiffies here, because it may be */
-			/* that we have disabled the timer... */
-			for (i = 0; i < 0xffff; i++);
-			hp100_andb(~HP100_PCI_RESET, PCICTRL2);
-			hp100_page(PERFORMANCE);
-		}
-	} else {		/* bring out of reset */
-		hp100_outw(HP100_HW_RST | HP100_SET_LB, OPTION_LSW);
-		for (i = 0; i < 0xffff; i++);
-		hp100_page(PERFORMANCE);
-	}
-}
 
-#ifdef HP100_DEBUG
-void hp100_RegisterDump(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	int Page;
-	int Register;
-
-	/* Dump common registers */
-	printk("hp100: Cascade Register Dump\n");
-	printk("hardware id #1: 0x%.2x\n", hp100_inb(HW_ID));
-	printk("hardware id #2/paging: 0x%.2x\n", hp100_inb(PAGING));
-	printk("option #1: 0x%.4x\n", hp100_inw(OPTION_LSW));
-	printk("option #2: 0x%.4x\n", hp100_inw(OPTION_MSW));
-
-	/* Dump paged registers */
-	for (Page = 0; Page < 8; Page++) {
-		/* Dump registers */
-		printk("page: 0x%.2x\n", Page);
-		outw(Page, ioaddr + 0x02);
-		for (Register = 0x8; Register < 0x22; Register += 2) {
-			/* Display Register contents except data port */
-			if (((Register != 0x10) && (Register != 0x12)) || (Page > 0)) {
-				printk("0x%.2x = 0x%.4x\n", Register, inw(ioaddr + Register));
-			}
-		}
-	}
-	hp100_page(PERFORMANCE);
-}
-#endif
 
-
-
 /*
  *  module section
  */
-
+ 
 #ifdef MODULE
 
 /* Parameters set by insmod */
-int hp100_port[5] =
-{0, -1, -1, -1, -1};
+int hp100_port[5] = { 0, -1, -1, -1, -1 };
 #ifdef LINUX_2_1
 MODULE_PARM(hp100_port, "1-5i");
 #endif
 
 #ifdef LINUX_2_1
-char hp100_name[5][IFNAMSIZ] =
-{"", "", "", "", ""};
+char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" };
 MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ));
 #else
-static char devname[5][IFNAMSIZ] =
-{"", "", "", "", ""};
-static char *hp100_name[5] =
-{devname[0], devname[1],
- devname[2], devname[3],
- devname[4]};
+static char devname[5][IFNAMSIZ] = { "", "", "", "", "" };
+static char *hp100_name[5] = { devname[0], devname[1],
+                               devname[2], devname[3],
+                               devname[4] };
 #endif
 
 /* List of devices */
-static struct device *hp100_devlist[5] =
-{NULL, NULL, NULL, NULL, NULL};
+static struct device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL };
 
 /*
  * Note: if you have more than five 100vg cards in your pc, feel free to
@@ -2606,61 +3055,64 @@
  * option hp100 hp100_port=0x280 hp100_name=eth239
  */
 
-int init_module(void)
+int init_module( void )
 {
-	int i;
-	int ret = 0;
+  int	i, cards;
 
-	if (hp100_port == 0 && !EISA_bus && !pcibios_present())
-		printk("HP100: You should not use auto-probing with insmod!\n");
+  if (hp100_port == 0 && !EISA_bus && !pcibios_present())
+    printk("hp100: You should not use auto-probing with insmod!\n");
 
-	/* Loop on all possible base addresses */
-	i = -1;
-	while ((hp100_port[++i] != -1) && (i < 5)) {
-		/* Create device and set basics args */
-		hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL);
-		memset(hp100_devlist[i], 0x00, sizeof(struct device));
-		hp100_devlist[i]->name = hp100_name[i];
-		hp100_devlist[i]->base_addr = hp100_port[i];
-		hp100_devlist[i]->init = &hp100_probe;
-
-		/* Try to create the device */
-		if (register_netdev(hp100_devlist[i]) != 0) {
-			/* DeAllocate everything */
-			/* Note: if dev->priv is mallocated, there is no way to fail */
-			kfree_s(hp100_devlist[i], sizeof(struct device));
-			hp100_devlist[i] = (struct device *) NULL;
-			ret = -EIO;
-		}
-	}			/* Loop over all devices */
-
-	return ret;
+  /* Loop on all possible base addresses */
+  i = -1; cards = 0;
+  while((hp100_port[++i] != -1) && (i < 5))
+    {
+      /* Create device and set basics args */
+      hp100_devlist[i] = kmalloc(sizeof(struct device), GFP_KERNEL);
+      memset(hp100_devlist[i], 0x00, sizeof(struct device));
+      hp100_devlist[i]->name = hp100_name[i];
+      hp100_devlist[i]->base_addr = hp100_port[i];
+      hp100_devlist[i]->init = &hp100_probe;
+
+      /* Try to create the device */
+      if(register_netdev(hp100_devlist[i]) != 0)
+	{
+	  /* DeAllocate everything */
+	  /* Note: if dev->priv is mallocated, there is no way to fail */
+	  kfree_s(hp100_devlist[i], sizeof(struct device));
+	  hp100_devlist[i] = (struct device *) NULL;
+	}
+       else
+        cards++;
+    }			/* Loop over all devices */
+
+  return cards > 0 ? 0 : -ENODEV;
+}
+
+void cleanup_module( void )
+{
+  int		i;
+
+  /* TODO: Check if all skb's are released/freed. */
+  for(i = 0; i < 5; i++)
+    if(hp100_devlist[i] != (struct device *) NULL)
+      {
+	unregister_netdev( hp100_devlist[i] );
+	release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE );
+	if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */
+	  kfree_s( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE+0x0f); 
+	if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt )
+	  iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt );
+	kfree_s( hp100_devlist[i]->priv, sizeof( struct hp100_private ) );
+	hp100_devlist[i]->priv = NULL;
+	kfree_s(hp100_devlist[i], sizeof(struct device));
+	hp100_devlist[i] = (struct device *) NULL;
+      }
 }
 
-void cleanup_module(void)
-{
-	int i;
+#endif		/* MODULE */
 
-	/* TODO: Check if all skb's are released/freed. */
-	for (i = 0; i < 5; i++)
-		if (hp100_devlist[i] != (struct device *) NULL) {
-			unregister_netdev(hp100_devlist[i]);
-			release_region(hp100_devlist[i]->base_addr, HP100_REGION_SIZE);
-			if (((struct hp100_private *) hp100_devlist[i]->priv)->mode == 1)	/* busmaster */
-				kfree_s(((struct hp100_private *) hp100_devlist[i]->priv)->page_vaddr, MAX_RINGSIZE + 0x0f);
-			if (((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt)
-				iounmap(((struct hp100_private *) hp100_devlist[i]->priv)->mem_ptr_virt);
-			kfree_s(hp100_devlist[i]->priv, sizeof(struct hp100_private));
-			hp100_devlist[i]->priv = NULL;
-			kfree_s(hp100_devlist[i], sizeof(struct device));
-			hp100_devlist[i] = (struct device *) NULL;
-		}
-}
 
-#endif				/* MODULE */
 
-
-
 /*
  * Local variables:
  *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp100.c"
diff -u --recursive --new-file v2.1.89/linux/drivers/net/hp100.h linux/drivers/net/hp100.h
--- v2.1.89/linux/drivers/net/hp100.h	Sat Nov 29 10:33:19 1997
+++ linux/drivers/net/hp100.h	Tue Mar 17 21:21:43 1998
@@ -1,7 +1,7 @@
 /*
  * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
  *
- * $Id: hp100.h,v 1.4 1997/05/26 21:09:19 davem Exp $
+ * $Id: hp100.h,v 1.51 1997/04/08 14:26:42 floeff Exp floeff $
  *
  * Authors:  Jaroslav Kysela, <perex@pf.jcu.cz>
  *           Siegfried Loeffler <floeff@tunix.mathematik.uni-stuttgart.de>
diff -u --recursive --new-file v2.1.89/linux/drivers/net/ipddp.c linux/drivers/net/ipddp.c
--- v2.1.89/linux/drivers/net/ipddp.c	Thu Feb 12 20:56:08 1998
+++ linux/drivers/net/ipddp.c	Thu Mar 12 10:54:12 1998
@@ -83,9 +83,6 @@
 /* Index to functions, as function prototypes. */
 static int ipddp_xmit(struct sk_buff *skb, struct device *dev);
 static struct net_device_stats *ipddp_get_stats(struct device *dev);
-static int ipddp_rebuild_header(struct sk_buff *skb);
-static int ipddp_hard_header(struct sk_buff *skb, struct device *dev,
-                unsigned short type, void *daddr, void *saddr, unsigned len);
 static int ipddp_create(struct ipddp_route *new_rt);
 static int ipddp_delete(struct ipddp_route *rt);
 static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt);
@@ -119,10 +116,10 @@
 
 	/* Let the user now what mode we are in */
 	if(ipddp_mode == IPDDP_ENCAP)
-		printk("%s: Appletalk-IP Encapsulation mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 
+		printk("%s: Appletalk-IP Encap. mode by Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n", 
 			dev->name);
 	if(ipddp_mode == IPDDP_DECAP)
-		printk("%s: Appletalk-IP Decapsulation mode by Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n", 
+		printk("%s: Appletalk-IP Decap. mode by Jay Schulist <Jay.Schulist@spacs.k12.wi.us>\n", 
 			dev->name);
 
 	/* Fill in the device structure with ethernet-generic values. */
@@ -140,8 +137,6 @@
         dev->stop 	    = ipddp_close;
         dev->get_stats      = ipddp_get_stats;
         dev->do_ioctl       = ipddp_ioctl;
-	dev->hard_header    = ipddp_hard_header;        /* see ip_output.c */
-	dev->rebuild_header = ipddp_rebuild_header;
 
         dev->type = ARPHRD_IPDDP;       	/* IP over DDP tunnel */
         dev->mtu = 585;
@@ -158,24 +153,6 @@
 }
 
 /*
- * Transmit LLAP/ELAP frame using aarp_send_ddp.
- */
-static int ipddp_xmit(struct sk_buff *skb, struct device *dev)
-{
-        /* Retrieve the saved address hint */
-        struct at_addr *at = (struct at_addr *)skb->data;
-        skb_pull(skb,4);
-
-        ((struct net_device_stats *) dev->priv)->tx_packets++;
-        ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
-
-        if(aarp_send_ddp(skb->dev, skb, at, NULL) < 0)
-                dev_kfree_skb(skb);
-
-        return 0;
-}
-
-/*
  * Get the current statistics. This may be called with the card open or closed.
  */
 static struct net_device_stats *ipddp_get_stats(struct device *dev)
@@ -184,19 +161,15 @@
 }
 
 /*
- * Now the packet really wants to go out. On entry skb->data points to the
- * ddpehdr we reserved earlier. skb->h.raw will be the higher level header.
+ * Transmit LLAP/ELAP frame using aarp_send_ddp.
  */
-static int ipddp_rebuild_header(struct sk_buff *skb)
+static int ipddp_xmit(struct sk_buff *skb, struct device *dev)
 {
 	u32 paddr = ((struct rtable*)skb->dst)->rt_gateway;
         struct ddpehdr *ddp;
-        struct at_addr at;
         struct ipddp_route *rt;
         struct at_addr *our_addr;
 
-	/* Wow! I'll eat my hat if this routine is really called. --ANK */
-
 	/*
          * Find appropriate route to use, based only on IP number.
          */
@@ -205,25 +178,21 @@
                 if(rt->ip == paddr)
                         break;
         }
-
         if(rt == NULL)
-        {
-                printk("%s: unreachable dst %s\n", cardname, in_ntoa(paddr));
-                return -ENETUNREACH;
-        }
+                return 0;
 
         our_addr = atalk_find_dev_addr(rt->dev);
 
 	if(ipddp_mode == IPDDP_DECAP)
 		/* 
 		 * Pull off the excess room that should not be there.
-        	 * This is the case for Localtalk, this may not hold
-        	 * true for Ethertalk, etc. 
+		 * This is due to a hard-header problem. This is the
+		 * quick fix for now though, till it breaks.
 		 */
-		skb_pull(skb, 31-(sizeof(struct ddpehdr)+1));
+		skb_pull(skb, 35-(sizeof(struct ddpehdr)+1));
 
 	/* Create the Extended DDP header */
-	ddp = (struct ddpehdr *) (skb->data+4);
+	ddp = (struct ddpehdr *)skb->data;
         ddp->deh_len = skb->len;
         ddp->deh_hops = 1;
         ddp->deh_pad = 0;
@@ -231,7 +200,7 @@
 
 	/*
          * For Localtalk we need aarp_send_ddp to strip the
-         * Ext DDP header and place a Shrt DDP header on it.
+         * long DDP header and place a shot DDP header on it.
          */
         if(rt->dev->type == ARPHRD_LOCALTLK)
         {
@@ -248,24 +217,16 @@
         ddp->deh_dport = 72;
         ddp->deh_sport = 72;
 
-        *((__u8 *)(ddp+1)) = 22;        		/* ddp type = IP */
-        *((__u16 *)ddp)=ntohs(*((__u16 *)ddp));		/* fix up length field */
+        *((__u8 *)(ddp+1)) = 22;        	/* ddp type = IP */
+        *((__u16 *)ddp)=ntohs(*((__u16 *)ddp));	/* fix up length field */
 
-	/* Hide it at the start of the buffer, we pull it out in ipddp_xmit */
-        at = rt->at;
-        memcpy(skb->data,(void *)&at,sizeof(at));
-
-	skb->dev = rt->dev;	/* set skb->dev to appropriate device */
         skb->protocol = htons(ETH_P_ATALK);     /* Protocol has changed */
 
-        return 0;
-}
+	((struct net_device_stats *) dev->priv)->tx_packets++;
+        ((struct net_device_stats *) dev->priv)->tx_bytes+=skb->len;
 
-static int ipddp_hard_header(struct sk_buff *skb, struct device *dev, 
-		unsigned short type, void *daddr, void *saddr, unsigned len)
-{
-        /* Push down the header space and the type byte */
-        skb_push(skb, sizeof(struct ddpehdr)+1+4);
+        if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0)
+                dev_kfree_skb(skb);
 
         return 0;
 }
diff -u --recursive --new-file v2.1.89/linux/drivers/net/ipddp.h linux/drivers/net/ipddp.h
--- v2.1.89/linux/drivers/net/ipddp.h	Sun Dec 21 17:41:24 1997
+++ linux/drivers/net/ipddp.h	Thu Mar 12 10:54:12 1998
@@ -10,7 +10,6 @@
 #define SIOCADDIPDDPRT   (SIOCDEVPRIVATE)
 #define SIOCDELIPDDPRT   (SIOCDEVPRIVATE+1)
 #define SIOCFINDIPDDPRT  (SIOCDEVPRIVATE+2)
-#define SIOCPRINTIPDDPRT (SIOCDEVPRIVATE+3)
 
 struct ipddp_route
 {
diff -u --recursive --new-file v2.1.89/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.89/linux/drivers/net/ppp.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/ppp.c	Thu Mar 12 10:33:21 1998
@@ -3,6 +3,7 @@
  *  Michael Callahan <callahan@maths.ox.ac.uk>
  *  Al Longyear <longyear@netcom.com>
  *  Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
+ *  Cyrus Durgin <cider@speakeasy.org> (changes for kmod)
  *
  *  Dynamic PPP devices by Jim Freeman <jfree@caldera.com>.
  *  ppp_tty_receive ``noisy-raise-bug'' fixed by Ove Ewerlid <ewerlid@syscon.uu.se>
@@ -50,7 +51,7 @@
 
 /* $Id: ppp.c,v 1.14 1997/11/27 06:04:45 paulus Exp $ */
 
-#include <linux/config.h> /* for CONFIG_KERNELD */
+#include <linux/config.h> /* for CONFIG_KMOD */
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -94,8 +95,8 @@
 #include <linux/if_pppvar.h>
 #include <linux/ppp-comp.h>
 
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 #ifndef PPP_IPX
@@ -2190,14 +2191,14 @@
 	restore_flags(flags);
 
 	cp = find_compressor (ccp_option[0]);
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (cp == NULL) {
 		char modname[32];
 		sprintf(modname, "ppp-compress-%d", ccp_option[0]);
 		request_module(modname);
 		cp = find_compressor(ccp_option[0]);
 	}
-#endif /* CONFIG_KERNELD */
+#endif /* CONFIG_KMOD */
 
 	if (cp == NULL)
 		goto out_no_comp;
diff -u --recursive --new-file v2.1.89/linux/drivers/net/seeq8005.c linux/drivers/net/seeq8005.c
--- v2.1.89/linux/drivers/net/seeq8005.c	Mon Feb 23 18:12:06 1998
+++ linux/drivers/net/seeq8005.c	Mon Mar 16 14:17:29 1998
@@ -43,6 +43,7 @@
 #include <linux/init.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
+#include <asm/delay.h>
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <linux/errno.h>
diff -u --recursive --new-file v2.1.89/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.89/linux/drivers/net/slip.c	Tue Mar 10 10:03:32 1998
+++ linux/drivers/net/slip.c	Tue Mar 10 09:47:26 1998
@@ -181,12 +181,14 @@
 	sl->xleft    = 0;
 	rbuff = xchg(&sl->rbuff, rbuff);
 	xbuff = xchg(&sl->xbuff, xbuff);
-#ifdef CONFIG_SLIP_MODE_SLIP6
+#ifdef SL_INCLUDE_CSLIP
 	cbuff = xchg(&sl->cbuff, cbuff);
 	slcomp = xchg(&sl->slcomp, slcomp);
+#ifdef CONFIG_SLIP_MODE_SLIP6
 	sl->xdata    = 0;
 	sl->xbits    = 0;
 #endif
+#endif
 	end_bh_atomic();
 	err = 0;
 
@@ -1134,7 +1136,7 @@
 		   it breaks my old poor gcc on alpha --ANK
 		 */
 		tmp = strlen(sl->dev->name) + 1;
-		if (copy_to_user(arg, sl->dev->name, tmp) < 0)
+		if (copy_to_user(arg, sl->dev->name, tmp))
 			return -EFAULT;
 		return 0;
 
diff -u --recursive --new-file v2.1.89/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.1.89/linux/drivers/net/tulip.c	Thu Feb 12 20:56:09 1998
+++ linux/drivers/net/tulip.c	Mon Mar 16 14:17:29 1998
@@ -636,7 +636,7 @@
 		for (i = 0; i < 5; i++)
 			dev->dev_addr[i] = last_phys_addr[i];
 		dev->dev_addr[i] = last_phys_addr[i] + 1;
-#if defined(__i386)		/* This BIOS bug doesn't exist on Alphas. */
+#if defined(__i386__)		/* This BIOS bug doesn't exist on Alphas. */
 		irq = last_irq;
 #endif
 	}
@@ -1094,7 +1094,7 @@
 	outl(0x00200000 | 0xE000, ioaddr + CSR0);
 #elif defined(__powerpc__)
 	outl(0x00200080 | 0x8000, ioaddr + CSR0);
-#elif defined(__i386)
+#elif defined(__i386__)
 #if defined(MODULE)
 	/* When a module we don't have 'x86' to check. */
 	outl(0x00200000 | 0x4800, ioaddr + CSR0);
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/ncr53c8xx.c linux/drivers/scsi/ncr53c8xx.c
--- v2.1.89/linux/drivers/scsi/ncr53c8xx.c	Mon Jan 12 15:05:27 1998
+++ linux/drivers/scsi/ncr53c8xx.c	Mon Mar 16 14:17:29 1998
@@ -9448,7 +9448,7 @@
 	/*
 	 * Try to fix up PCI config according to wished features.
 	 */
-#if defined(__i386) && !defined(MODULE)
+#if defined(__i386__) && !defined(MODULE)
 	if ((driver_setup.pci_fix_up & 1) &&
 	    (chip->features & FE_CLSE) && cache_line_size == 0) {
 #if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- v2.1.89/linux/drivers/scsi/ppa.c	Sat Nov 22 10:28:31 1997
+++ linux/drivers/scsi/ppa.c	Tue Mar 10 14:43:13 1998
@@ -50,8 +50,8 @@
 #include  "ppa.h"
 #include <linux/parport.h>
 
-#ifdef CONFIG_KERNELD
-#include  <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include  <linux/kmod.h>
 #ifndef PARPORT_MODULES
 #define PARPORT_MODULES "parport_pc"
 #endif
@@ -130,7 +130,7 @@
     nhosts = 0;
     try_again = 0;
 
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
     if (!pb) {
 	request_module(PARPORT_MODULES);
 	pb = parport_enumerate();
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v2.1.89/linux/drivers/scsi/scsi.c	Tue Mar 10 10:03:33 1998
+++ linux/drivers/scsi/scsi.c	Wed Mar 11 15:07:43 1998
@@ -23,10 +23,13 @@
  *  Added request_module("scsi_hostadapter") for kerneld:
  *  (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules)
  *  Bjorn Ekwall  <bj0rn@blox.se>
+ *  (changed to kmod)
  *
  *  Major improvements to the timeout, abort, and reset processing,
  *  as well as performance modifications for large queue depths by
  *  Leonard N. Zubkoff <lnz@dandelion.com>
+ *
+ *  Converted cli() code to spinlocks, Ingo Molnar
  */
 
 #include <linux/config.h>
@@ -57,8 +60,8 @@
 #include "hosts.h"
 #include "constants.h"
 
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 #undef USE_STATIC_SCSI_MEMORY
@@ -350,8 +353,8 @@
      * (DB, 4 Feb 1995)
      */
 
-    save_flags(flags);
-    cli();
+   
+    spin_lock_irqsave(&io_request_lock, flags);
     host_active = NULL;
 
     for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {
@@ -383,7 +386,7 @@
 	       sh[index]->host_no);
     }
 
-    restore_flags(flags);
+    spin_unlock_irqrestore(&io_request_lock, flags);
 }
 
 static void scan_scsis_done (Scsi_Cmnd * SCpnt)
@@ -1130,21 +1133,21 @@
 	    SCpnt = found;
 	}
 
-	save_flags(flags);
-	cli();
+        __save_flags(flags);
+	__cli();
 	/* See if this request has already been queued by an interrupt routine
 	 */
 	if (req && (req->rq_status == RQ_INACTIVE || req->rq_dev != dev)) {
-	    restore_flags(flags);
+	   __restore_flags(flags);
 	    return NULL;
 	}
 	if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE)	/* Might have changed */
 	{
 		if (wait && SCwait && SCwait->request.rq_status != RQ_INACTIVE){
  			sleep_on(&device->device_wait);
- 			restore_flags(flags);
+			__restore_flags(flags);
 	 	} else {
- 			restore_flags(flags);
+			__restore_flags(flags);
 	 		if (!wait) return NULL;
  			if (!SCwait) {
 	 			printk("Attempt to allocate device channel %d,"
@@ -1194,7 +1197,7 @@
 					      * to complete */
 	    }
             atomic_inc(&SCpnt->host->host_active); 
-	    restore_flags(flags);
+	    __restore_flags(flags);
             SCSI_LOG_MLQUEUE(5, printk("Activating command for device %d (%d)\n", 
                                        SCpnt->target,
                                        atomic_read(&SCpnt->host->host_active)));
@@ -1291,8 +1294,7 @@
 
     host = SCpnt->host;
 
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&io_request_lock, flags);
     /* Assign a unique nonzero serial_number. */
     if (++serial_number == 0) serial_number = 1;
     SCpnt->serial_number = serial_number;
@@ -1302,6 +1304,8 @@
      * we can avoid the drive not being ready.
      */
     timeout = host->last_reset + MIN_RESET_DELAY;
+    spin_unlock(&io_request_lock);
+
     if (jiffies < timeout) {
 	int ticks_remaining = timeout - jiffies;
 	/*
@@ -1313,11 +1317,11 @@
 	 * interrupt handler (assuming there is one irq-level per
 	 * host).
 	 */
-	sti();
+	__sti();
 	while (--ticks_remaining >= 0) udelay(1000000/HZ);
 	host->last_reset = jiffies - MIN_RESET_DELAY;
     }
-    restore_flags(flags);
+    __restore_flags(flags); /* this possibly puts us back into __cli() */
 
     if( host->hostt->use_new_eh_code )
       {
@@ -1448,21 +1452,20 @@
      * ourselves.
      */
 
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&io_request_lock, flags);
     SCpnt->pid = scsi_pid++;
 
     while (SCSI_BLOCK((Scsi_Device *) NULL, host)) {
-	restore_flags(flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 	SCSI_SLEEP(&host->host_wait, SCSI_BLOCK((Scsi_Device *) NULL, host));
-	cli();
+	spin_lock_irqsave(&io_request_lock, flags);
     }
 
     if (host->block) host_active = host;
 
     host->host_busy++;
     device->device_busy++;
-    restore_flags(flags);
+    spin_unlock_irqrestore(&io_request_lock, flags);
 
     /*
      * Our own function scsi_done (which marks the host as not busy, disables
@@ -1816,8 +1819,7 @@
     if(len % SECTOR_SIZE != 0 || len > PAGE_SIZE)
 	return NULL;
 
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&io_request_lock, flags);
     nbits = len >> 9;
     mask = (1 << nbits) - 1;
 
@@ -1825,7 +1827,7 @@
 	for(j=0; j<=SECTORS_PER_PAGE - nbits; j++){
 	    if ((dma_malloc_freelist[i] & (mask << j)) == 0){
 		dma_malloc_freelist[i] |= (mask << j);
-		restore_flags(flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 		scsi_dma_free_sectors -= nbits;
 #ifdef DEBUG
                 SCSI_LOG_MLQUEUE(3,printk("SMalloc: %d %p [From:%p]\n",len, dma_malloc_pages[i] + (j << 9)));
@@ -1834,7 +1836,7 @@
 		return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
 	    }
 	}
-    restore_flags(flags);
+    spin_unlock_irqrestore(&io_request_lock, flags);
     return NULL;  /* Nope.  No more */
 }
 
@@ -1868,19 +1870,20 @@
             if ((mask << sector) >= (1 << SECTORS_PER_PAGE))
                 panic ("scsi_free:Bad memory alignment");
 
-            save_flags(flags);
-            cli();
+	    spin_lock_irqsave(&io_request_lock, flags);
             if((dma_malloc_freelist[page] &
                 (mask << sector)) != (mask<<sector)){
+		spin_unlock_irqrestore(&io_request_lock, flags);
 #ifdef DEBUG
 		printk("scsi_free(obj=%p, len=%d) called from %08lx\n",
                        obj, len, ret);
 #endif
                 panic("scsi_free:Trying to free unused memory");
+		spin_lock_irqsave(&io_request_lock, flags);
             }
             scsi_dma_free_sectors += nbits;
             dma_malloc_freelist[page] &= ~(mask << sector);
-            restore_flags(flags);
+	    spin_unlock_irqrestore(&io_request_lock, flags);
             return 0;
 	}
     }
@@ -2544,8 +2547,7 @@
     /* When we dick with the actual DMA list, we need to
      * protect things
      */
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&io_request_lock, flags);
     if (dma_malloc_freelist)
     {
         size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
@@ -2565,7 +2567,7 @@
     dma_malloc_pages = new_dma_malloc_pages;
     dma_sectors = new_dma_sectors;
     scsi_need_isa_buffer = new_need_isa_buffer;
-    restore_flags(flags);
+    spin_unlock_irqrestore(&io_request_lock, flags);
 
 #ifdef DEBUG_INIT
     printk("resize_dma_pool: dma free sectors   = %d\n", scsi_dma_free_sectors);
@@ -2804,11 +2806,10 @@
 	    {
                 online_status = SDpnt->online;
                 SDpnt->online = FALSE;
-	        save_flags(flags);
-	        cli();
+		spin_lock_irqsave(&io_request_lock, flags);
 	        if(SCpnt->request.rq_status != RQ_INACTIVE) 
                 {
-		    restore_flags(flags);
+		    spin_unlock_irqrestore(&io_request_lock, flags);
                     printk("SCSI device not inactive - state=%d, id=%d\n",
                            SCpnt->request.rq_status, SCpnt->target);
                     for(SDpnt1 = shpnt->host_queue; SDpnt1; 
@@ -2829,7 +2830,7 @@
                  */
                 SCpnt->state = SCSI_STATE_DISCONNECTING;
 	        SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING;  /* Mark as busy */
-	        restore_flags(flags);
+		spin_unlock_irqrestore(&io_request_lock, flags);
 	    }
         }
     }
@@ -3114,7 +3115,7 @@
         
 	/* Load upper level device handler of some kind */
     case MODULE_SCSI_DEV:
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (scsi_hosts == NULL)
             request_module("scsi_hostadapter");
 #endif
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/scsi_error.c linux/drivers/scsi/scsi_error.c
--- v2.1.89/linux/drivers/scsi/scsi_error.c	Tue Feb 17 13:12:47 1998
+++ linux/drivers/scsi/scsi_error.c	Thu Mar 12 10:33:21 1998
@@ -8,7 +8,6 @@
  *
  */
 
-#include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 
@@ -37,10 +36,6 @@
 #include "constants.h"
 
 #define SHUTDOWN_SIGS	(sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
-
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 
 #ifdef DEBUG
     #define SENSE_TIMEOUT SCSI_TIMEOUT
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/scsi_obsolete.c linux/drivers/scsi/scsi_obsolete.c
--- v2.1.89/linux/drivers/scsi/scsi_obsolete.c	Mon Feb 23 18:12:07 1998
+++ linux/drivers/scsi/scsi_obsolete.c	Thu Mar 12 10:33:21 1998
@@ -20,10 +20,6 @@
  *  Native multichannel, wide scsi, /proc/scsi and hot plugging
  *  support added by Michael Neuffer <mike@i-connect.net>
  *
- *  Added request_module("scsi_hostadapter") for kerneld:
- *  (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules)
- *  Bjorn Ekwall  <bj0rn@blox.se>
- *
  *  Major improvements to the timeout, abort, and reset processing,
  *  as well as performance modifications for large queue depths by
  *  Leonard N. Zubkoff <lnz@dandelion.com>
@@ -47,7 +43,6 @@
  * driver uses the new code this *ENTIRE* file will be nuked.
  */
 
-#include <linux/config.h> /* for CONFIG_KERNELD */
 #define __NO_VERSION__
 #include <linux/module.h>
 
@@ -69,10 +64,6 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "constants.h"
-
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 
 #undef USE_STATIC_SCSI_MEMORY
 
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/scsi_queue.c linux/drivers/scsi/scsi_queue.c
--- v2.1.89/linux/drivers/scsi/scsi_queue.c	Sun Dec 21 17:04:49 1997
+++ linux/drivers/scsi/scsi_queue.c	Thu Mar 12 10:33:21 1998
@@ -10,7 +10,6 @@
  *  we attempt to remove commands from the queue and retry them.
  */
 
-#include <linux/config.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 
@@ -37,10 +36,6 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "constants.h"
-
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 
 /*
  * TODO:
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.1.89/linux/drivers/scsi/sd.c	Mon Feb 23 18:12:08 1998
+++ linux/drivers/scsi/sd.c	Wed Mar 11 15:07:43 1998
@@ -1598,15 +1598,14 @@
     target =  DEVICE_NR(dev);
     gdev = &GENDISK_STRUCT;
 
-    save_flags(flags);
-    cli();
+    spin_lock_irqsave(&io_request_lock, flags);
     if (DEVICE_BUSY || USAGE > maxusage) {
-	restore_flags(flags);
+	spin_unlock_irqrestore(&io_request_lock, flags);
 	printk("Device busy for revalidation (usage=%d)\n", USAGE);
 	return -EBUSY;
     }
     DEVICE_BUSY = 1;
-    restore_flags(flags);
+    spin_unlock_irqrestore(&io_request_lock, flags);
 
     max_p = gdev->max_p;
     start = target << gdev->minor_shift;
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v2.1.89/linux/drivers/scsi/wd7000.c	Sun Jan  4 10:55:09 1998
+++ linux/drivers/scsi/wd7000.c	Tue Mar 10 10:20:58 1998
@@ -127,6 +127,19 @@
  *
  *  Thanks to Roger Scott for driver debugging.
  *
+ *  06/07/1997
+ *
+ *  Added support for /proc file system (/proc/scsi/wd7000/[0...] files).
+ *  Now, driver can handle hard disks with capacity >1GB.
+ *
+ *  01/15/1998
+ *
+ *  Added support for BUS_ON and BUS_OFF parameters in config line.
+ *  Miscellaneous cleanup.
+ *
+ *  03/01/1998
+ *
+ *  WD7000 driver now work on kernels >= 2.1.x
  */
 
 #ifdef MODULE
@@ -143,24 +156,29 @@
 #include <asm/system.h>
 #include <asm/dma.h>
 #include <asm/io.h>
-#include <asm/irq.h>
 #include <linux/ioport.h>
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
+#include <linux/version.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
 #define ANY2SCSI_INLINE		/* undef this to use old macros */
-#undef DEBUG
+#undef  WD7000_DEBUG		/* general debug                */
 
 #include "wd7000.h"
+#include <linux/stat.h>
 
-#include<linux/stat.h>
 
-struct proc_dir_entry proc_scsi_wd7000 = {
-    PROC_SCSI_7000FASST, 6, "wd7000",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+struct proc_dir_entry proc_scsi_wd7000 =
+{
+    PROC_SCSI_7000FASST,
+    6,
+    "wd7000",
+    S_IFDIR | S_IRUGO | S_IXUGO,
+    2
 };
 
 
@@ -186,52 +204,48 @@
  *  WD7000-specific mailbox structure
  *
  */
-typedef volatile struct mailbox{
-  unchar status;
-  unchar scbptr[3];		/* SCSI-style - MSB first (big endian) */
+typedef volatile struct mailbox {
+    unchar status;
+    unchar scbptr[3];		/* SCSI-style - MSB first (big endian) */
 } Mailbox;
 
 /*
  *  This structure should contain all per-adapter global data.  I.e., any
  *  new global per-adapter data should put in here.
- *
  */
 typedef struct adapter {
-  struct Scsi_Host *sh;		/* Pointer to Scsi_Host structure */
-  int iobase;			/* This adapter's I/O base address */
-  int irq;			/* This adapter's IRQ level */
-  int dma;			/* This adapter's DMA channel */
-  struct {			/* This adapter's mailboxes */
-    Mailbox ogmb[OGMB_CNT];	/* Outgoing mailboxes */
-    Mailbox icmb[ICMB_CNT];	/* Incoming mailboxes */
-  } mb;
-  int next_ogmb;		/* to reduce contention at mailboxes */
-  unchar control;		/* shadows CONTROL port value */
-  unchar rev1, rev2;		/* filled in by wd7000_revision */
+    struct Scsi_Host *sh;	/* Pointer to Scsi_Host structure    */
+    int iobase;			/* This adapter's I/O base address   */
+    int irq;			/* This adapter's IRQ level          */
+    int dma;			/* This adapter's DMA channel        */
+    int int_counter;		/* This adapter's interrupt counter  */
+    int bus_on;			/* This adapter's BUS_ON time        */
+    int bus_off;		/* This adapter's BUS_OFF time       */
+    struct {			/* This adapter's mailboxes          */
+	Mailbox ogmb[OGMB_CNT];	/* Outgoing mailboxes                */
+	Mailbox icmb[ICMB_CNT];	/* Incoming mailboxes                */
+    } mb;
+    int next_ogmb;		/* to reduce contention at mailboxes */
+    unchar control;		/* shadows CONTROL port value        */
+    unchar rev1, rev2;		/* filled in by wd7000_revision      */
 } Adapter;
 
 /*
- * The following is set up by wd7000_detect, and used thereafter by
- * wd7000_intr_handle to map the irq level to the corresponding Adapter.
- * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
- * changed to pick up the IRQ level correctly.
- */
-static Adapter *irq2host[NR_IRQS] = {NULL};
-
-/*
  * (linear) base address for ROM BIOS
  */
-static const long wd7000_biosaddr[] = {
-  0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000,
-  0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000
+static const long wd7000_biosaddr[] =
+{
+    0xc0000, 0xc2000, 0xc4000, 0xc6000, 0xc8000, 0xca000, 0xcc000, 0xce000,
+    0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000
 };
 #define NUM_ADDRS (sizeof(wd7000_biosaddr)/sizeof(long))
 
-static const unsigned short wd7000_iobase[] = {
-  0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,
-  0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,
-  0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,
-  0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8
+static const unsigned short wd7000_iobase[] =
+{
+    0x0300, 0x0308, 0x0310, 0x0318, 0x0320, 0x0328, 0x0330, 0x0338,
+    0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, 0x0378,
+    0x0380, 0x0388, 0x0390, 0x0398, 0x03a0, 0x03a8, 0x03b0, 0x03b8,
+    0x03c0, 0x03c8, 0x03d0, 0x03d8, 0x03e0, 0x03e8, 0x03f0, 0x03f8
 };
 #define NUM_IOPORTS (sizeof(wd7000_iobase)/sizeof(unsigned short))
 
@@ -240,24 +254,48 @@
 
 static const short wd7000_dma[] = { 5, 6, 7 };
 #define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short))
-  
+
+/*
+ * possible irq range
+ */
+#define IRQ_MIN   3
+#define IRQ_MAX   15
+#define IRQS      (IRQ_MAX - IRQ_MIN + 1)
+
+/*
+ * The following is set up by wd7000_detect, and used thereafter by
+ * wd7000_intr_handle to map the irq level to the corresponding Adapter.
+ * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
+ * changed to pick up the IRQ level correctly.
+ */
+static struct Scsi_Host *wd7000_host[IRQS];
+
+#define BUS_ON    64	/* x 125ns = 8000ns (BIOS default) */
+#define BUS_OFF   15	/* x 125ns = 1875ns (BIOS default) */
+
 /*
  *  Standard Adapter Configurations - used by wd7000_detect
  */
 typedef struct {
-  int      irq;			/* IRQ level        */
-  int      dma;			/* DMA channel      */
-  unsigned iobase;		/* I/O base address */
+    short irq;		/* IRQ level                                  */
+    short dma;		/* DMA channel                                */
+    unsigned iobase;	/* I/O base address                           */
+    short bus_on;	/* Time that WD7000 spends on the AT-bus when */
+			/* transferring data. BIOS default is 8000ns. */
+    short bus_off;	/* Time that WD7000 spends OFF THE BUS after  */
+			/* while it is transferring data.             */
+			/* BIOS default is 1875ns                     */
 } Config;
 
 /*
  * Add here your configuration...
  */
-static const Config configs[] = {
-  { 15,  6, 0x350 },		/* defaults for single adapter */
-  { 11,  5, 0x320 },		/* defaults for second adapter */
-  {  7,  6, 0x350 },		/* My configuration (Zaga)     */
-  { -1, -1, 0x0   }		/* Empty slot                  */
+static Config configs[] =
+{
+    { 15,  6, 0x350, BUS_ON, BUS_OFF },	/* defaults for single adapter */
+    { 11,  5, 0x320, BUS_ON, BUS_OFF },	/* defaults for second adapter */
+    {  7,  6, 0x350, BUS_ON, BUS_OFF },	/* My configuration (Zaga)     */
+    { -1, -1, 0x0,   BUS_ON, BUS_OFF }	/* Empty slot                  */
 };
 #define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
 
@@ -267,13 +305,14 @@
  *  added for the Future Domain version.
  */
 typedef struct signature {
-    const void *sig;		/* String to look for */
-    unsigned    ofs;		/* offset from BIOS base address */
-    unsigned    len;		/* length of string */
+    const char *sig;		/* String to look for            */
+    unsigned long ofs;		/* offset from BIOS base address */
+    unsigned len;		/* length of string              */
 } Signature;
 
-static const Signature signatures[] = {
-  { "SSTBIOS", 0x0000d, 7 }	/* "SSTBIOS" @ offset 0x0000d */
+static const Signature signatures[] =
+{
+    {"SSTBIOS", 0x0000d, 7}	/* "SSTBIOS" @ offset 0x0000d */
 };
 #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
 
@@ -282,22 +321,23 @@
  *  I/O Port Offsets and Bit Definitions
  *  4 addresses are used.  Those not defined here are reserved.
  */
-#define ASC_STAT        0	/* Status,  Read */
-#define ASC_COMMAND     0	/* Command, Write */
+#define ASC_STAT        0	/* Status,  Read          */
+#define ASC_COMMAND     0	/* Command, Write         */
 #define ASC_INTR_STAT   1	/* Interrupt Status, Read */
-#define ASC_INTR_ACK    1	/* Acknowledge, Write */
-#define ASC_CONTROL     2	/* Control, Write */
+#define ASC_INTR_ACK    1	/* Acknowledge, Write     */
+#define ASC_CONTROL     2	/* Control, Write         */
 
 /*
  * ASC Status Port
  */
-#define INT_IM		0x80	/* Interrupt Image Flag */
-#define CMD_RDY		0x40	/* Command Port Ready */
-#define CMD_REJ		0x20	/* Command Port Byte Rejected */
-#define ASC_INIT        0x10	/* ASC Initialized Flag */
+#define INT_IM		0x80	/* Interrupt Image Flag           */
+#define CMD_RDY		0x40	/* Command Port Ready             */
+#define CMD_REJ		0x20	/* Command Port Byte Rejected     */
+#define ASC_INIT        0x10	/* ASC Initialized Flag           */
 #define ASC_STATMASK    0xf0	/* The lower 4 Bytes are reserved */
 
-/* COMMAND opcodes
+/*
+ * COMMAND opcodes
  *
  *  Unfortunately, I have no idea how to properly use some of these commands,
  *  as the OEM manual does not make it clear.  I have not been able to use
@@ -305,39 +345,38 @@
  *  discernible effect whatsoever.  I think they may be related to certain
  *  ICB commands, but again, the OEM manual doesn't make that clear.
  */
-#define NO_OP             0	/* NO-OP toggles CMD_RDY bit in ASC_STAT */
-#define INITIALIZATION    1	/* initialization (10 bytes) */
-#define DISABLE_UNS_INTR  2	/* disable unsolicited interrupts */
-#define ENABLE_UNS_INTR   3	/* enable unsolicited interrupts */
-#define INTR_ON_FREE_OGMB 4	/* interrupt on free OGMB */
-#define SOFT_RESET        5	/* SCSI bus soft reset */
-#define HARD_RESET_ACK    6	/* SCSI bus hard reset acknowledge */
-#define START_OGMB        0x80	/* start command in OGMB (n) */
+#define NO_OP             0	/* NO-OP toggles CMD_RDY bit in ASC_STAT  */
+#define INITIALIZATION    1	/* initialization (10 bytes)              */
+#define DISABLE_UNS_INTR  2	/* disable unsolicited interrupts         */
+#define ENABLE_UNS_INTR   3	/* enable unsolicited interrupts          */
+#define INTR_ON_FREE_OGMB 4	/* interrupt on free OGMB                 */
+#define SOFT_RESET        5	/* SCSI bus soft reset                    */
+#define HARD_RESET_ACK    6	/* SCSI bus hard reset acknowledge        */
+#define START_OGMB        0x80	/* start command in OGMB (n)              */
 #define SCAN_OGMBS        0xc0	/* start multiple commands, signature (n) */
-				/*    where (n) = lower 6 bits */
-/* For INITIALIZATION:
+				/*    where (n) = lower 6 bits            */
+/*
+ * For INITIALIZATION:
  */
 typedef struct initCmd {
-  unchar op;			/* command opcode (= 1) */
-  unchar ID;			/* Adapter's SCSI ID */
-  unchar bus_on;		/* Bus on time, x 125ns (see below) */
-  unchar bus_off;		/* Bus off time, ""         ""      */
-  unchar rsvd;			/* Reserved */
-  unchar mailboxes[3];		/* Address of Mailboxes, MSB first  */
-  unchar ogmbs;			/* Number of outgoing MBs, max 64, 0,1 = 1 */
-  unchar icmbs;			/* Number of incoming MBs,   ""       ""   */
+    unchar op;			/* command opcode (= 1)                    */
+    unchar ID;			/* Adapter's SCSI ID                       */
+    unchar bus_on;		/* Bus on time, x 125ns (see below)        */
+    unchar bus_off;		/* Bus off time, ""         ""             */
+    unchar rsvd;		/* Reserved                                */
+    unchar mailboxes[3];	/* Address of Mailboxes, MSB first         */
+    unchar ogmbs;		/* Number of outgoing MBs, max 64, 0,1 = 1 */
+    unchar icmbs;		/* Number of incoming MBs,   ""       ""   */
 } InitCmd;
 
-#define BUS_ON            64	/* x 125ns = 8000ns (BIOS default) */
-#define BUS_OFF           15	/* x 125ns = 1875ns (BIOS default) */
- 
-/* Interrupt Status Port - also returns diagnostic codes at ASC reset
+/*
+ * Interrupt Status Port - also returns diagnostic codes at ASC reset
  *
  * if msb is zero, the lower bits are diagnostic status
  * Diagnostics:
- * 01	No diagnostic error occurred
- * 02	RAM failure
- * 03	FIFO R/W failed
+ * 01   No diagnostic error occurred
+ * 02   RAM failure
+ * 03   FIFO R/W failed
  * 04   SBIC register read/write failed
  * 05   Initialization D-FF failed
  * 06   Host IRQ D-FF failed
@@ -346,19 +385,20 @@
  * 10NNNNNN   outgoing mailbox NNNNNN is free
  * 11NNNNNN   incoming mailbox NNNNNN needs service
  */
-#define MB_INTR	 0xC0		/* Mailbox Service possible/required */
-#define IMB_INTR 0x40		/* 1 Incoming / 0 Outgoing */
-#define MB_MASK  0x3f           /* mask for mailbox number */
+#define MB_INTR    0xC0		/* Mailbox Service possible/required */
+#define IMB_INTR   0x40		/* 1 Incoming / 0 Outgoing           */
+#define MB_MASK    0x3f		/* mask for mailbox number           */
 
-/* CONTROL port bits
+/*
+ * CONTROL port bits
  */
-#define INT_EN		0x08	/* Interrupt Enable	*/
-#define DMA_EN		0x04	/* DMA Enable		*/
-#define SCSI_RES	0x02	/* SCSI Reset		*/
-#define ASC_RES		0x01	/* ASC Reset		*/
+#define INT_EN     0x08		/* Interrupt Enable */
+#define DMA_EN     0x04		/* DMA Enable       */
+#define SCSI_RES   0x02		/* SCSI Reset       */
+#define ASC_RES    0x01		/* ASC Reset        */
 
 /*
- *   Driver data structures:
+ * Driver data structures:
  *   - mb and scbs are required for interfacing with the host adapter.
  *     An SCB has extra fields not visible to the adapter; mb's
  *     _cannot_ do this, since the adapter assumes they are contiguous in
@@ -387,28 +427,28 @@
  */
 typedef struct sgb {
     unchar len[3];
-    unchar ptr[3];              /* Also SCSI-style - MSB first */
+    unchar ptr[3];		/* Also SCSI-style - MSB first */
 } Sgb;
 
-typedef struct scb {		/* Command Control Block 5.4.1 */
-  unchar op;			/* Command Control Block Operation Code */
-  unchar idlun;			/* op=0,2:Target Id, op=1:Initiator Id */
-				/* Outbound data transfer, length is checked*/
-				/* Inbound data transfer, length is checked */
-				/* Logical Unit Number */
-  unchar cdb[12];		/* SCSI Command Block */
-  volatile unchar status;       /* SCSI Return Status */
-  volatile unchar vue;		/* Vendor Unique Error Code */
-  unchar maxlen[3];		/* Maximum Data Transfer Length */
-  unchar dataptr[3];		/* SCSI Data Block Pointer */
-  unchar linkptr[3];		/* Next Command Link Pointer */
-  unchar direc;			/* Transfer Direction */
-  unchar reserved2[6];		/* SCSI Command Descriptor Block */
-				/* end of hardware SCB */
-  Scsi_Cmnd *SCpnt;             /* Scsi_Cmnd using this SCB */
-  Sgb sgb[WD7000_SG];           /* Scatter/gather list for this SCB */
-  Adapter *host;                /* host adapter */
-  struct scb *next;             /* for lists of scbs */
+typedef struct scb {		/* Command Control Block 5.4.1               */
+    unchar op;			/* Command Control Block Operation Code      */
+    unchar idlun;		/* op=0,2:Target Id, op=1:Initiator Id       */
+				/* Outbound data transfer, length is checked */
+				/* Inbound data transfer, length is checked  */
+				/* Logical Unit Number                       */
+    unchar cdb[12];		/* SCSI Command Block                        */
+    volatile unchar status;	/* SCSI Return Status                        */
+    volatile unchar vue;	/* Vendor Unique Error Code                  */
+    unchar maxlen[3];		/* Maximum Data Transfer Length              */
+    unchar dataptr[3];		/* SCSI Data Block Pointer                   */
+    unchar linkptr[3];		/* Next Command Link Pointer                 */
+    unchar direc;		/* Transfer Direction                        */
+    unchar reserved2[6];	/* SCSI Command Descriptor Block             */
+				/* end of hardware SCB                       */
+    Scsi_Cmnd *SCpnt;		/* Scsi_Cmnd using this SCB                  */
+    Sgb sgb[WD7000_SG];		/* Scatter/gather list for this SCB          */
+    Adapter *host;		/* host adapter                              */
+    struct scb *next;		/* for lists of scbs                         */
 } Scb;
 
 /*
@@ -422,110 +462,110 @@
  *  (notably, get/set unsolicited interrupt status) in my copy of the OEM
  *  manual, and others are ambiguous/hard to follow.
  */
-#define ICB_OP_MASK             0x80  /* distinguishes scbs from icbs */
-#define ICB_OP_OPEN_RBUF        0x80  /* open receive buffer */
-#define ICB_OP_RECV_CMD         0x81  /* receive command from initiator */
-#define ICB_OP_RECV_DATA        0x82  /* receive data from initiator */
-#define ICB_OP_RECV_SDATA       0x83  /* receive data with status from init. */
-#define ICB_OP_SEND_DATA        0x84  /* send data with status to initiator */
-#define ICB_OP_SEND_STAT        0x86  /* send command status to initiator */
-			     /* 0x87 is reserved */
-#define ICB_OP_READ_INIT        0x88  /* read initialization bytes */
-#define ICB_OP_READ_ID          0x89  /* read adapter's SCSI ID */
-#define ICB_OP_SET_UMASK        0x8A  /* set unsolicited interrupt mask */
-#define ICB_OP_GET_UMASK        0x8B  /* read unsolicited interrupt mask */
-#define ICB_OP_GET_REVISION     0x8C  /* read firmware revision level */
-#define ICB_OP_DIAGNOSTICS      0x8D  /* execute diagnostics */
-#define ICB_OP_SET_EPARMS       0x8E  /* set execution parameters */
-#define ICB_OP_GET_EPARMS       0x8F  /* read execution parameters */
+#define ICB_OP_MASK           0x80	/* distinguishes scbs from icbs        */
+#define ICB_OP_OPEN_RBUF      0x80	/* open receive buffer                 */
+#define ICB_OP_RECV_CMD       0x81	/* receive command from initiator      */
+#define ICB_OP_RECV_DATA      0x82	/* receive data from initiator         */
+#define ICB_OP_RECV_SDATA     0x83	/* receive data with status from init. */
+#define ICB_OP_SEND_DATA      0x84	/* send data with status to initiator  */
+#define ICB_OP_SEND_STAT      0x86	/* send command status to initiator    */
+					/* 0x87 is reserved                    */
+#define ICB_OP_READ_INIT      0x88	/* read initialization bytes           */
+#define ICB_OP_READ_ID        0x89	/* read adapter's SCSI ID              */
+#define ICB_OP_SET_UMASK      0x8A	/* set unsolicited interrupt mask      */
+#define ICB_OP_GET_UMASK      0x8B	/* read unsolicited interrupt mask     */
+#define ICB_OP_GET_REVISION   0x8C	/* read firmware revision level        */
+#define ICB_OP_DIAGNOSTICS    0x8D	/* execute diagnostics                 */
+#define ICB_OP_SET_EPARMS     0x8E	/* set execution parameters            */
+#define ICB_OP_GET_EPARMS     0x8F	/* read execution parameters           */
 
 typedef struct icbRecvCmd {
-  unchar op;
-  unchar IDlun;                 /* Initiator SCSI ID/lun */
-  unchar len[3];                /* command buffer length */
-  unchar ptr[3];                /* command buffer address */
-  unchar rsvd[7];               /* reserved */
-  volatile unchar vue;          /* vendor-unique error code */
-  volatile unchar status;       /* returned (icmb) status */
-  volatile unchar phase;        /* used by interrupt handler */
+    unchar op;
+    unchar IDlun;		/* Initiator SCSI ID/lun     */
+    unchar len[3];		/* command buffer length     */
+    unchar ptr[3];		/* command buffer address    */
+    unchar rsvd[7];		/* reserved                  */
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
 } IcbRecvCmd;
 
 typedef struct icbSendStat {
-  unchar op;
-  unchar IDlun;                 /* Target SCSI ID/lun */
-  unchar stat;                  /* (outgoing) completion status byte 1 */
-  unchar rsvd[12];              /* reserved */
-  volatile unchar vue;          /* vendor-unique error code */
-  volatile unchar status;       /* returned (icmb) status */
-  volatile unchar phase;        /* used by interrupt handler */
+    unchar op;
+    unchar IDlun;		/* Target SCSI ID/lun                  */
+    unchar stat;		/* (outgoing) completion status byte 1 */
+    unchar rsvd[12];		/* reserved                            */
+    volatile unchar vue;	/* vendor-unique error code            */
+    volatile unchar status;	/* returned (icmb) status              */
+    volatile unchar phase;	/* used by interrupt handler           */
 } IcbSendStat;
 
 typedef struct icbRevLvl {
-  unchar op;
-  volatile unchar primary;      /* primary revision level (returned) */
-  volatile unchar secondary;    /* secondary revision level (returned) */
-  unchar rsvd[12];              /* reserved */
-  volatile unchar vue;          /* vendor-unique error code */
-  volatile unchar status;       /* returned (icmb) status */
-  volatile unchar phase;        /* used by interrupt handler */
+    unchar op;
+    volatile unchar primary;	/* primary revision level (returned)   */
+    volatile unchar secondary;	/* secondary revision level (returned) */
+    unchar rsvd[12];		/* reserved                            */
+    volatile unchar vue;	/* vendor-unique error code            */
+    volatile unchar status;	/* returned (icmb) status              */
+    volatile unchar phase;	/* used by interrupt handler           */
 } IcbRevLvl;
 
-typedef struct icbUnsMask {     /* I'm totally guessing here */
-  unchar op;
-  volatile unchar mask[14];     /* mask bits */
-#if 0
-  unchar rsvd[12];              /* reserved */
-#endif
-  volatile unchar vue;          /* vendor-unique error code */
-  volatile unchar status;       /* returned (icmb) status */
-  volatile unchar phase;        /* used by interrupt handler */
+typedef struct icbUnsMask {	/* I'm totally guessing here */
+    unchar op;
+    volatile unchar mask[14];	/* mask bits                 */
+#ifdef 0
+    unchar rsvd[12];		/* reserved                  */
+#endif
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
 } IcbUnsMask;
 
 typedef struct icbDiag {
-  unchar op;
-  unchar type;                  /* diagnostics type code (0-3) */
-  unchar len[3];                /* buffer length */
-  unchar ptr[3];                /* buffer address */
-  unchar rsvd[7];               /* reserved */
-  volatile unchar vue;          /* vendor-unique error code */
-  volatile unchar status;       /* returned (icmb) status */
-  volatile unchar phase;        /* used by interrupt handler */
+    unchar op;
+    unchar type;		/* diagnostics type code (0-3) */
+    unchar len[3];		/* buffer length               */
+    unchar ptr[3];		/* buffer address              */
+    unchar rsvd[7];		/* reserved                    */
+    volatile unchar vue;	/* vendor-unique error code    */
+    volatile unchar status;	/* returned (icmb) status      */
+    volatile unchar phase;	/* used by interrupt handler   */
 } IcbDiag;
 
-#define ICB_DIAG_POWERUP        0     /* Power-up diags only */
-#define ICB_DIAG_WALKING        1     /* walking 1's pattern */
-#define ICB_DIAG_DMA            2     /* DMA - system memory diags */
-#define ICB_DIAG_FULL           3     /* do both 1 & 2 */
+#define ICB_DIAG_POWERUP   0	/* Power-up diags only       */
+#define ICB_DIAG_WALKING   1	/* walking 1's pattern       */
+#define ICB_DIAG_DMA       2	/* DMA - system memory diags */
+#define ICB_DIAG_FULL      3	/* do both 1 & 2             */
 
 typedef struct icbParms {
-  unchar op;
-  unchar rsvd1;                 /* reserved */
-  unchar len[3];                /* parms buffer length */
-  unchar ptr[3];                /* parms buffer address */
-  unchar idx[2];                /* index (MSB-LSB) */
-  unchar rsvd2[5];              /* reserved */
-  volatile unchar vue;          /* vendor-unique error code */
-  volatile unchar status;       /* returned (icmb) status */
-  volatile unchar phase;        /* used by interrupt handler */
+    unchar op;
+    unchar rsvd1;		/* reserved                  */
+    unchar len[3];		/* parms buffer length       */
+    unchar ptr[3];		/* parms buffer address      */
+    unchar idx[2];		/* index (MSB-LSB)           */
+    unchar rsvd2[5];		/* reserved                  */
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
 } IcbParms;
 
 typedef struct icbAny {
-  unchar op;
-  unchar data[14];              /* format-specific data */
-  volatile unchar vue;          /* vendor-unique error code */
-  volatile unchar status;       /* returned (icmb) status */
-  volatile unchar phase;        /* used by interrupt handler */
+    unchar op;
+    unchar data[14];		/* format-specific data      */
+    volatile unchar vue;	/* vendor-unique error code  */
+    volatile unchar status;	/* returned (icmb) status    */
+    volatile unchar phase;	/* used by interrupt handler */
 } IcbAny;
 
 typedef union icb {
-  unchar op;                    /* ICB opcode */
-  IcbRecvCmd recv_cmd;          /* format for receive command */
-  IcbSendStat send_stat;        /* format for send status */
-  IcbRevLvl rev_lvl;            /* format for get revision level */
-  IcbDiag diag;                 /* format for execute diagnostics */
-  IcbParms eparms;              /* format for get/set exec parms */
-  IcbAny icb;                   /* generic format */
-  unchar data[18];
+    unchar op;			/* ICB opcode                     */
+    IcbRecvCmd recv_cmd;	/* format for receive command     */
+    IcbSendStat send_stat;	/* format for send status         */
+    IcbRevLvl rev_lvl;		/* format for get revision level  */
+    IcbDiag diag;		/* format for execute diagnostics */
+    IcbParms eparms;		/* format for get/set exec parms  */
+    IcbAny icb;			/* generic format                 */
+    unchar data[18];
 } Icb;
 
 
@@ -536,27 +576,34 @@
  *  structure is not part of the Adapter structure.
  */
 static Scb scbs[MAX_SCBS];
-static Scb *scbfree = NULL;      /* free list */
-static int freescbs = MAX_SCBS;  /* free list counter */
-
-/*
- *
- */
-static short wd7000_setupIRQ[NUM_CONFIGS];
-static short wd7000_setupDMA[NUM_CONFIGS];
-static short wd7000_setupIO[NUM_CONFIGS];
-static short wd7000_card_num = 0;
+static Scb *scbfree = NULL;	/* free list         */
+static int freescbs = MAX_SCBS;	/* free list counter */
 
 /*
  *  END of data/declarations - code follows.
  */
+static void setup_error (char *mesg, int *ints)
+{
+    if (ints[0] == 3)
+        printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> %s\n",
+                ints[1], ints[2], ints[3], mesg);
+    else if (ints[0] == 4)
+        printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d\" -> %s\n",
+                ints[1], ints[2], ints[3], ints[4], mesg);
+    else
+        printk ("wd7000_setup: \"wd7000=%d,%d,0x%x,%d,%d\" -> %s\n",
+                ints[1], ints[2], ints[3], ints[4], ints[5], mesg);
+}
 
 
 /*
  * Note: You can now set these options from the kernel's "command line".
  * The syntax is:
  *
- *     wd7000=IRQ,DMA,IO
+ *     wd7000=<IRQ>,<DMA>,<IO>[,<BUS_ON>[,<BUS_OFF>]]
+ *
+ * , where BUS_ON and BUS_OFF are in nanoseconds. BIOS default values
+ * are 8000ns for BUS_ON and 1875ns for BUS_OFF.
  * eg:
  *     wd7000=7,6,0x350
  *
@@ -565,82 +612,99 @@
  */
 void wd7000_setup (char *str, int *ints)
 {
+    static short wd7000_card_num = 0;
     short i, j;
 
     if (wd7000_card_num >= NUM_CONFIGS) {
-        printk ("wd7000_setup: Too many \"wd7000=\" configurations in "
-	        "command line!\n");
-
-        return;
+	printk ("wd7000_setup: Too many \"wd7000=\" configurations in "
+		"command line!\n");
+	return;
     }
 
-    if (ints[0] != 3)
+    if ((ints[0] < 3) || (ints[0] > 5))
 	printk ("wd7000_setup: Error in command line!  "
-	        "Usage: wd7000=IRQ,DMA,IO\n");
+		"Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>[,<BUS_OFF>]]\n");
     else {
 	for (i = 0; i < NUM_IRQS; i++)
 	    if (ints[1] == wd7000_irq[i])
 		break;
 
 	if (i == NUM_IRQS) {
-	    printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> "
-		    "invalid IRQ.\n", ints[1], ints[2], ints[3]);
+	    setup_error ("invalid IRQ.", ints);
 	    return;
 	}
 	else
- 	    wd7000_setupIRQ[wd7000_card_num] = ints[1];
+	    configs[wd7000_card_num].irq = ints[1];
 
 	for (i = 0; i < NUM_DMAS; i++)
 	    if (ints[2] == wd7000_dma[i])
 		break;
 
 	if (i == NUM_DMAS) {
-	    printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> "
-		    "invalid DMA channel.\n", ints[1], ints[2], ints[3]);
+	    setup_error ("invalid DMA channel.", ints);
 	    return;
 	}
 	else
-	    wd7000_setupDMA[wd7000_card_num] = ints[2];
+	    configs[wd7000_card_num].dma = ints[2];
 
 	for (i = 0; i < NUM_IOPORTS; i++)
 	    if (ints[3] == wd7000_iobase[i])
 		break;
 
 	if (i == NUM_IOPORTS) {
-	    printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> "
-		    "invalid I/O base address.\n", ints[1], ints[2], ints[3]);
+	    setup_error ("invalid I/O base address.", ints);
 	    return;
 	}
 	else
-	    wd7000_setupIO[wd7000_card_num] = ints[3];
+	    configs[wd7000_card_num].iobase = ints[3];
+
+	if (ints[0] > 3) {
+	    if ((ints[4] < 500) || (ints[4] > 31875)) {
+	        setup_error ("BUS_ON value is out of range (500 to 31875 nanoseconds)!",
+		             ints);
+	        configs[wd7000_card_num].bus_on = BUS_ON;
+	    }
+	    else
+	        configs[wd7000_card_num].bus_on = ints[4] / 125.0;
+	}
+	else
+	    configs[wd7000_card_num].bus_on = BUS_ON;
+
+	if (ints[0] > 4) {
+	    if ((ints[5] < 500) || (ints[5] > 31875)) {
+	        setup_error ("BUS_OFF value is out of range (500 to 31875 nanoseconds)!",
+		             ints);
+	        configs[wd7000_card_num].bus_off = BUS_OFF;
+	    }
+	    else
+	        configs[wd7000_card_num].bus_off = ints[5] / 125.0;
+	}
+	else
+	    configs[wd7000_card_num].bus_off = BUS_OFF;
 
 	if (wd7000_card_num)
 	    for (i = 0; i < (wd7000_card_num - 1); i++)
-	        for (j = i + 1; j < wd7000_card_num; j++)
-	            if (wd7000_setupIRQ[i] == wd7000_setupIRQ[j]) {
-	                printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> "
-			        "duplicated IRQ!\n",
-				ints[1], ints[2], ints[3]);
-		        return;
-	            }
-	            else if (wd7000_setupDMA[i] == wd7000_setupDMA[j]) {
-	                printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> "
-			        "duplicated DMA channel!\n",
-				ints[1], ints[2], ints[3]);
-		        return;
-	            }
-	            else if (wd7000_setupIO[i] == wd7000_setupIO[j]) {
-	                printk ("wd7000_setup: \"wd7000=%d,%d,0x%x\" -> "
-			        "duplicated I/O base address!\n",
-				ints[1], ints[2], ints[3]);
-		        return;
-	            }
-
-#ifdef DEBUG
-	printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x\n",
-	        wd7000_setupIRQ[wd7000_card_num],
-		wd7000_setupDMA[wd7000_card_num],
-		wd7000_setupIO[wd7000_card_num]);
+		for (j = i + 1; j < wd7000_card_num; j++)
+		    if (configs[i].irq == configs[j].irq) {
+	                setup_error ("duplicated IRQ!", ints);
+			return;
+		    }
+		    else if (configs[i].dma == configs[j].dma) {
+	                setup_error ("duplicated DMA channel!", ints);
+			return;
+		    }
+		    else if (configs[i].iobase == configs[j].iobase) {
+	                setup_error ("duplicated I/O base address!", ints);
+			return;
+		    }
+
+#ifdef WD7000_DEBUG
+	printk ("wd7000_setup: IRQ=%d, DMA=%d, I/O=0x%x, BUS_ON=%dns, BUS_OFF=%dns\n",
+		configs[wd7000_card_num].irq,
+		configs[wd7000_card_num].dma,
+		configs[wd7000_card_num].iobase,
+		configs[wd7000_card_num].bus_on * 125,
+		configs[wd7000_card_num].bus_off * 125);
 #endif
 
 	wd7000_card_num++;
@@ -650,21 +714,20 @@
 
 #ifdef ANY2SCSI_INLINE
 /*
-   Since they're used a lot, I've redone the following from the macros
-   formerly in wd7000.h, hopefully to speed them up by getting rid of
-   all the shifting (it may not matter; GCC might have done as well anyway).
-
-   xany2scsi and xscsi2int were not being used, and are no longer defined.
-   (They were simply 4-byte versions of these routines).
-*/
-
-typedef union {  /* let's cheat... */
-  int i;
-  unchar u[sizeof(int)];  /* the sizeof(int) makes it more portable */
+ * Since they're used a lot, I've redone the following from the macros
+ * formerly in wd7000.h, hopefully to speed them up by getting rid of
+ * all the shifting (it may not matter; GCC might have done as well anyway).
+ *
+ * xany2scsi and xscsi2int were not being used, and are no longer defined.
+ * (They were simply 4-byte versions of these routines).
+ */
+typedef union {			/* let's cheat... */
+    int i;
+    unchar u[sizeof (int)];	/* the sizeof(int) makes it more portable */
 } i_u;
 
 
-static inline void any2scsi( unchar *scsi, int any )
+static inline void any2scsi (unchar * scsi, int any)
 {
     *scsi++ = ((i_u) any).u[2];
     *scsi++ = ((i_u) any).u[1];
@@ -672,49 +735,50 @@
 }
 
 
-static inline int scsi2int( unchar *scsi )
+static inline int scsi2int (unchar * scsi)
 {
     i_u result;
 
-    result.i = 0;  /* clears unused bytes */
-    *(result.u+2) = *scsi++;
-    *(result.u+1) = *scsi++;
-      *(result.u) = *scsi++;
-    return result.i;
+    result.i = 0;		/* clears unused bytes */
+    result.u[2] = *scsi++;
+    result.u[1] = *scsi++;
+    result.u[0] = *scsi++;
+
+    return (result.i);
 }
 #else
 /*
-   These are the old ones - I've just moved them here...
-*/
+ * These are the old ones - I've just moved them here...
+ */
 #undef any2scsi
-#define any2scsi(up, p)			\
-(up)[0] = (((unsigned long)(p)) >> 16);		\
-(up)[1] = ((unsigned long)(p)) >> 8;		\
-(up)[2] = ((unsigned long)(p));
+#define any2scsi(up, p)   (up)[0] = (((unsigned long) (p)) >> 16);	\
+			  (up)[1] = ((unsigned long) (p)) >> 8;		\
+			  (up)[2] = ((unsigned long) (p));
 
 #undef scsi2int
-#define scsi2int(up) ( (((unsigned long)*(up)) << 16) + \
- (((unsigned long)(up)[1]) << 8) + ((unsigned long)(up)[2]) )
+#define scsi2int(up)   ( (((unsigned long) *(up)) << 16) +	\
+			 (((unsigned long) (up)[1]) << 8) +	\
+			 ((unsigned long) (up)[2]) )
 #endif
 
-    
-static inline void wd7000_enable_intr(Adapter *host)
+
+static inline void wd7000_enable_intr (Adapter *host)
 {
     host->control |= INT_EN;
-    outb(host->control, host->iobase+ASC_CONTROL);
+    outb (host->control, host->iobase + ASC_CONTROL);
 }
 
 
-static inline void wd7000_enable_dma(Adapter *host)
+static inline void wd7000_enable_dma (Adapter *host)
 {
     host->control |= DMA_EN;
-    outb(host->control,host->iobase+ASC_CONTROL);
-    set_dma_mode(host->dma, DMA_MODE_CASCADE);
-    enable_dma(host->dma);
+    outb (host->control, host->iobase + ASC_CONTROL);
+    set_dma_mode (host->dma, DMA_MODE_CASCADE);
+    enable_dma (host->dma);
 }
 
 
-#define WAITnexttimeout 200  /* 2 seconds */
+#define WAITnexttimeout 200	/* 2 seconds */
 
 static inline short WAIT (unsigned port, unsigned mask, unsigned allof, unsigned noneof)
 {
@@ -722,7 +786,7 @@
     register unsigned long WAITtimeout = jiffies + WAITnexttimeout;
 
     while (jiffies <= WAITtimeout) {
-     	WAITbits = inb (port) & mask;
+	WAITbits = inb (port) & mask;
 
 	if (((WAITbits & allof) == allof) && ((WAITbits & noneof) == 0))
 	    return (0);
@@ -732,31 +796,32 @@
 }
 
 
-static inline void delay( unsigned how_long )
+static inline void delay (unsigned how_long)
 {
-     register unsigned long time = jiffies + how_long;
+    register unsigned long time = jiffies + how_long;
 
-     while (jiffies < time);
+    while (jiffies < time);
 }
 
 
-static inline int command_out(Adapter *host, unchar *cmd, int len)
+static inline int command_out (Adapter * host, unchar * cmd, int len)
 {
-    if (! WAIT (host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0)) {
-	while (len--)  {
-	    do  {
-		outb(*cmd, host->iobase+ASC_COMMAND);
-		WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
-	    }  while (inb(host->iobase+ASC_STAT) & CMD_REJ);
+    if (!WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+	while (len--) {
+	    do {
+		outb (*cmd, host->iobase + ASC_COMMAND);
+		WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
+	    } while (inb (host->iobase + ASC_STAT) & CMD_REJ);
 
 	    cmd++;
 	}
 
-	return 1;
+	return (1);
     }
 
-    printk("wd7000 command_out: WAIT failed(%d)\n", len+1);
-    return 0;
+    printk ("wd7000 command_out: WAIT failed(%d)\n", len + 1);
+
+    return (0);
 }
 
 
@@ -770,7 +835,7 @@
  *  the satisfiability of a request is not dependent on the size of the
  *  request.
  */
-static inline Scb *alloc_scbs(int needed)
+static inline Scb *alloc_scbs (int needed)
 {
     register Scb *scb, *p;
     register unsigned long flags;
@@ -779,84 +844,89 @@
     static int busy = 0;
     int i;
 
-    if (needed <= 0)  return NULL;  /* sanity check */
+    if (needed <= 0)
+	return (NULL);		/* sanity check */
 
-    save_flags(flags);
-    cli();
-    while (busy)  { /* someone else is allocating */
-	sti();	/* Yes this is really needed here */
-	now = jiffies;  while (jiffies == now)  /* wait a jiffy */;
-	cli();
+    save_flags (flags);
+    cli ();
+    while (busy) {		/* someone else is allocating */
+	sti ();			/* Yes this is really needed here */
+	for (now = jiffies; now == jiffies; );	/* wait a jiffy */
+	cli ();
     }
-    busy = 1;          /* not busy now; it's our turn */
+    busy = 1;			/* not busy now; it's our turn */
 
-    while (freescbs < needed)  {
+    while (freescbs < needed) {
 	timeout = jiffies + WAITnexttimeout;
 	do {
-	    sti();	/* Yes this is really needed here */
-	    now = jiffies;
-	    while (jiffies == now); /* wait a jiffy */
-	    cli();
-	}  while (freescbs < needed && jiffies <= timeout);
+	    sti ();		/* Yes this is really needed here */
+	    for (now = jiffies; now == jiffies; );	/* wait a jiffy */
+	    cli ();
+	} while (freescbs < needed && jiffies <= timeout);
 	/*
 	 *  If we get here with enough free Scbs, we can take them.
 	 *  Otherwise, we timed out and didn't get enough.
 	 */
-	if (freescbs < needed)  {
+	if (freescbs < needed) {
 	    busy = 0;
-	    panic("wd7000: can't get enough free SCBs.\n");
-	    restore_flags(flags);
-	    return NULL;
+	    panic ("wd7000: can't get enough free SCBs.\n");
+	    restore_flags (flags);
+	    return (NULL);
 	}
     }
-    scb = scbfree;  freescbs -= needed;
-    for (i = 0; i < needed; i++)  { p = scbfree;  scbfree = p->next; }
+    scb = scbfree;
+    freescbs -= needed;
+    for (i = 0; i < needed; i++) {
+	p = scbfree;
+	scbfree = p->next;
+    }
     p->next = NULL;
-    
-    busy = 0;   /* we're done */
+    busy = 0;			/* we're done */
 
-    restore_flags(flags);
+    restore_flags (flags);
 
-    return scb;
+    return (scb);
 }
 
 
-static inline void free_scb( Scb *scb )
+static inline void free_scb (Scb *scb)
 {
     register unsigned long flags;
 
-    save_flags(flags);
-    cli();
+    save_flags (flags);
+    cli ();
 
-    memset(scb, 0, sizeof(Scb));
-    scb->next = scbfree;  scbfree = scb;
+    memset (scb, 0, sizeof (Scb));
+    scb->next = scbfree;
+    scbfree = scb;
     freescbs++;
 
-    restore_flags(flags);
+    restore_flags (flags);
 }
 
 
-static inline void init_scbs(void)
+static inline void init_scbs (void)
 {
     int i;
     unsigned long flags;
 
-    save_flags(flags);
-    cli();
+    save_flags (flags);
+    cli ();
 
     scbfree = &(scbs[0]);
-    memset(scbs, 0, sizeof(scbs));
-    for (i = 0;  i < MAX_SCBS-1;  i++)  {
-      scbs[i].next = &(scbs[i+1]);  scbs[i].SCpnt = NULL;
-    }
-    scbs[MAX_SCBS-1].next = NULL;
-    scbs[MAX_SCBS-1].SCpnt = NULL;
-
-    restore_flags(flags);
-}    
-    
+    memset (scbs, 0, sizeof (scbs));
+    for (i = 0; i < MAX_SCBS - 1; i++) {
+	scbs[i].next = &(scbs[i + 1]);
+	scbs[i].SCpnt = NULL;
+    }
+    scbs[MAX_SCBS - 1].next = NULL;
+    scbs[MAX_SCBS - 1].SCpnt = NULL;
+
+    restore_flags (flags);
+}
 
-static int mail_out( Adapter *host, Scb *scbptr )
+
+static int mail_out (Adapter *host, Scb *scbptr)
 /*
  *  Note: this can also be used for ICBs; just cast to the parm type.
  */
@@ -866,30 +936,35 @@
     unchar start_ogmb;
     Mailbox *ogmbs = host->mb.ogmb;
     int *next_ogmb = &(host->next_ogmb);
-#ifdef DEBUG
-    printk("wd7000 mail_out: 0x%06lx",(long) scbptr);
+
+#ifdef WD7000_DEBUG
+    printk ("wd7000_mail_out: 0x%06lx", (long) scbptr);
 #endif
+
     /* We first look for a free outgoing mailbox */
-    save_flags(flags);
-    cli();
+    save_flags (flags);
+    cli ();
     ogmb = *next_ogmb;
     for (i = 0; i < OGMB_CNT; i++) {
-	if (ogmbs[ogmb].status == 0)  {
-#ifdef DEBUG
-	    printk(" using OGMB 0x%x",ogmb);
+	if (ogmbs[ogmb].status == 0) {
+#ifdef WD7000_DEBUG
+	    printk (" using OGMB 0x%x", ogmb);
 #endif
 	    ogmbs[ogmb].status = 1;
-	    any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
+	    any2scsi ((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
 
-	    *next_ogmb = (ogmb+1) % OGMB_CNT;
+	    *next_ogmb = (ogmb + 1) % OGMB_CNT;
 	    break;
-	}  else
+	}
+	else
 	    ogmb = (++ogmb) % OGMB_CNT;
     }
-    restore_flags(flags);
-#ifdef DEBUG
-    printk(", scb is 0x%06lx",(long) scbptr);
+    restore_flags (flags);
+
+#ifdef WD7000_DEBUG
+    printk (", scb is 0x%06lx", (long) scbptr);
 #endif
+
     if (i >= OGMB_CNT) {
 	/*
 	 *  Alternatively, we might issue the "interrupt on free OGMB",
@@ -899,171 +974,182 @@
 	 *  that marks OGMB's free, waiting even with interrupts off
 	 *  should work, since they are freed very quickly in most cases.
 	 */
-	#ifdef DEBUG
-	printk(", no free OGMBs.\n");
+#ifdef WD7000_DEBUG
+	printk (", no free OGMBs.\n");
 #endif
-	return 0;
+	return (0);
     }
 
-    wd7000_enable_intr(host); 
+    wd7000_enable_intr (host);
 
     start_ogmb = START_OGMB | ogmb;
-    command_out( host, &start_ogmb, 1 );
-#ifdef DEBUG
-    printk(", awaiting interrupt.\n");
+    command_out (host, &start_ogmb, 1);
+
+#ifdef WD7000_DEBUG
+    printk (", awaiting interrupt.\n");
 #endif
-    return 1;
+
+    return (1);
 }
 
 
-int make_code(unsigned hosterr, unsigned scsierr)
-{   
-#ifdef DEBUG
+int make_code (unsigned hosterr, unsigned scsierr)
+{
+#ifdef WD7000_DEBUG
     int in_error = hosterr;
 #endif
 
-    switch ((hosterr>>8)&0xff){
-	case 0:	/* Reserved */
-		hosterr = DID_ERROR;
-		break;
-	case 1:	/* Command Complete, no errors */
-		hosterr = DID_OK;
-		break;
-	case 2: /* Command complete, error logged in scb status (scsierr) */ 
-		hosterr = DID_OK;
-		break;
-	case 4:	/* Command failed to complete - timeout */
-		hosterr = DID_TIME_OUT;
-		break;
-	case 5:	/* Command terminated; Bus reset by external device */
-		hosterr = DID_RESET;
-		break;
-	case 6:	/* Unexpected Command Received w/ host as target */
-		hosterr = DID_BAD_TARGET;
-		break;
+    switch ((hosterr >> 8) & 0xff) {
+	case 0:  /* Reserved */
+                 hosterr = DID_ERROR;
+                 break;
+	case 1:  /* Command Complete, no errors */
+                 hosterr = DID_OK;
+                 break;
+	case 2:  /* Command complete, error logged in scb status (scsierr) */
+                 hosterr = DID_OK;
+                 break;
+	case 4:  /* Command failed to complete - timeout */
+                 hosterr = DID_TIME_OUT;
+                 break;
+	case 5:  /* Command terminated; Bus reset by external device */
+                 hosterr = DID_RESET;
+                 break;
+	case 6:  /* Unexpected Command Received w/ host as target */
+                 hosterr = DID_BAD_TARGET;
+                 break;
 	case 80: /* Unexpected Reselection */
 	case 81: /* Unexpected Selection */
-		hosterr = DID_BAD_INTR;
-		break;
+                 hosterr = DID_BAD_INTR;
+                 break;
 	case 82: /* Abort Command Message  */
-		hosterr = DID_ABORT;
-		break;
+                 hosterr = DID_ABORT;
+                 break;
 	case 83: /* SCSI Bus Software Reset */
 	case 84: /* SCSI Bus Hardware Reset */
-		hosterr = DID_RESET;
-		break;
+                 hosterr = DID_RESET;
+                 break;
 	default: /* Reserved */
-		hosterr = DID_ERROR;
-		break;
-	}
-#ifdef DEBUG
-    if (scsierr||hosterr)
-	printk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n",
-	       scsierr,in_error,hosterr);
+                 hosterr = DID_ERROR;
+    }
+#ifdef WD7000_DEBUG
+    if (scsierr || hosterr)
+	printk ("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n",
+		scsierr, in_error, hosterr);
 #endif
-    return scsierr | (hosterr << 16);
+    return (scsierr | (hosterr << 16));
 }
 
 
-static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
+static void wd7000_scsi_done (Scsi_Cmnd *SCpnt)
 {
-#ifdef DEBUG
+#ifdef WD7000_DEBUG
     printk ("wd7000_scsi_done: 0x%06lx\n", (long) SCpnt);
 #endif
+
     SCpnt->SCp.phase = 0;
 }
 
 
-#define wd7000_intr_ack(host)  outb(0,host->iobase+ASC_INTR_ACK)
+#define wd7000_intr_ack(host)   outb (0, host->iobase + ASC_INTR_ACK)
 
-void wd7000_intr_handle(int irq, void *dev_id, struct pt_regs * regs)
+void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
 {
     register int flag, icmb, errstatus, icmb_status;
     register int host_error, scsi_error;
-    register Scb *scb;             /* for SCSI commands */
-    register IcbAny *icb;          /* for host commands */
+    register Scb *scb;		/* for SCSI commands */
+    register IcbAny *icb;	/* for host commands */
     register Scsi_Cmnd *SCpnt;
-    Adapter *host = irq2host[irq];  /* This MUST be set!!! */
+    Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata;	/* This MUST be set!!! */
     Mailbox *icmbs = host->mb.icmb;
 
-#ifdef DEBUG
-    printk("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host);
+    host->int_counter++;
+
+#ifdef WD7000_DEBUG
+    printk ("wd7000_intr_handle: irq = %d, host = 0x%06lx\n", irq, (long) host);
 #endif
 
-    flag = inb(host->iobase+ASC_INTR_STAT);
-#ifdef DEBUG
-    printk("wd7000_intr_handle: intr stat = 0x%02x\n",flag);
+    flag = inb (host->iobase + ASC_INTR_STAT);
+
+#ifdef WD7000_DEBUG
+    printk ("wd7000_intr_handle: intr stat = 0x%02x\n", flag);
 #endif
 
-    if (!(inb(host->iobase+ASC_STAT) & INT_IM))  {
+    if (!(inb (host->iobase + ASC_STAT) & INT_IM)) {
 	/* NB: these are _very_ possible if IRQ 15 is being used, since
-	   it's the "garbage collector" on the 2nd 8259 PIC.  Specifically,
-	   any interrupt signal into the 8259 which can't be identified
-	   comes out as 7 from the 8259, which is 15 to the host.  Thus, it
-	   is a good thing the WD7000 has an interrupt status port, so we
-	   can sort these out.  Otherwise, electrical noise and other such
-	   problems would be indistinguishable from valid interrupts...
-	*/
-#ifdef DEBUG 
-	printk("wd7000_intr_handle: phantom interrupt...\n");
+	 * it's the "garbage collector" on the 2nd 8259 PIC.  Specifically,
+	 * any interrupt signal into the 8259 which can't be identified
+	 * comes out as 7 from the 8259, which is 15 to the host.  Thus, it
+	 * is a good thing the WD7000 has an interrupt status port, so we
+	 * can sort these out.  Otherwise, electrical noise and other such
+	 * problems would be indistinguishable from valid interrupts...
+	 */
+#ifdef WD7000_DEBUG
+	printk ("wd7000_intr_handle: phantom interrupt...\n");
 #endif
-	wd7000_intr_ack(host);
-	return; 
+	wd7000_intr_ack (host);
+	return;
     }
 
-    if (flag & MB_INTR)  {
+    if (flag & MB_INTR) {
 	/* The interrupt is for a mailbox */
 	if (!(flag & IMB_INTR)) {
-#ifdef DEBUG
-	    printk("wd7000_intr_handle: free outgoing mailbox\n");
+#ifdef WD7000_DEBUG
+	    printk ("wd7000_intr_handle: free outgoing mailbox\n");
 #endif
 	    /*
 	     * If sleep_on() and the "interrupt on free OGMB" command are
 	     * used in mail_out(), wake_up() should correspondingly be called
 	     * here.  For now, we don't need to do anything special.
 	     */
-	    wd7000_intr_ack(host);
+	    wd7000_intr_ack (host);
 	    return;
-	}  else  {
+	}
+	else {
 	    /* The interrupt is for an incoming mailbox */
 	    icmb = flag & MB_MASK;
 	    icmb_status = icmbs[icmb].status;
-	    if (icmb_status & 0x80)  {  /* unsolicited - result in ICMB */
-#ifdef DEBUG
- 		printk("wd7000_intr_handle: unsolicited interrupt 0x%02xh\n",
-		       icmb_status);
+	    if (icmb_status & 0x80) {	/* unsolicited - result in ICMB */
+#ifdef WD7000_DEBUG
+		printk ("wd7000_intr_handle: unsolicited interrupt 0x%02x\n",
+			icmb_status);
 #endif
-		wd7000_intr_ack(host);
+		wd7000_intr_ack (host);
 		return;
 	    }
-	    scb = (struct scb *) scsi2int((unchar *)icmbs[icmb].scbptr);
+	    /* Aaaargh! (Zaga) */
+	    scb = (struct scb *) (scsi2int ((unchar *) icmbs[icmb].scbptr) | PAGE_OFFSET);
 	    icmbs[icmb].status = 0;
-	    if (!(scb->op & ICB_OP_MASK))  {   /* an SCB is done */
+	    if (!(scb->op & ICB_OP_MASK)) {	/* an SCB is done */
 		SCpnt = scb->SCpnt;
-		if (--(SCpnt->SCp.phase) <= 0)  {  /* all scbs are done */
+		if (--(SCpnt->SCp.phase) <= 0) {	/* all scbs are done */
 		    host_error = scb->vue | (icmb_status << 8);
 		    scsi_error = scb->status;
-		    errstatus = make_code(host_error,scsi_error);    
+		    errstatus = make_code (host_error, scsi_error);
 		    SCpnt->result = errstatus;
 
-		    free_scb(scb);
+		    free_scb (scb);
 
-		    SCpnt->scsi_done(SCpnt);
+		    SCpnt->scsi_done (SCpnt);
 		}
-	    }  else  {    /* an ICB is done */
+	    }
+	    else {		/* an ICB is done */
 		icb = (IcbAny *) scb;
 		icb->status = icmb_status;
-		icb->phase  = 0;
+		icb->phase = 0;
 	    }
-	}  /* incoming mailbox */
+	}			/* incoming mailbox */
     }
 
-    wd7000_intr_ack(host);
-    return;
+    wd7000_intr_ack (host);
+
+#ifdef WD7000_DEBUG
+    printk ("wd7000_intr_handle: return from interrupt handler\n");
+#endif
 }
 
 
-int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+int wd7000_queuecommand (Scsi_Cmnd *SCpnt, void (*done) (Scsi_Cmnd *))
 {
     register Scb *scb;
     register Sgb *sgb;
@@ -1076,94 +1162,105 @@
     idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
     SCpnt->scsi_done = done;
     SCpnt->SCp.phase = 1;
-    scb = alloc_scbs(1);
+    scb = alloc_scbs (1);
     scb->idlun = idlun;
-    memcpy(scb->cdb, cdb, cdblen);
+    memcpy (scb->cdb, cdb, cdblen);
     scb->direc = 0x40;		/* Disable direction check */
 
-    scb->SCpnt = SCpnt;         /* so we can find stuff later */
+    scb->SCpnt = SCpnt;		/* so we can find stuff later */
     SCpnt->host_scribble = (unchar *) scb;
     scb->host = host;
 
-    if (SCpnt->use_sg)  {
+    if (SCpnt->use_sg) {
 	struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
 	unsigned i;
 
-	if (SCpnt->host->sg_tablesize == SG_NONE)  {
-	    panic("wd7000_queuecommand: scatter/gather not supported.\n");
+	if (SCpnt->host->sg_tablesize == SG_NONE) {
+	    panic ("wd7000_queuecommand: scatter/gather not supported.\n");
 	}
-#ifdef DEBUG
- 	printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg);
+#ifdef WD7000_DEBUG
+	printk ("Using scatter/gather with %d elements.\n", SCpnt->use_sg);
 #endif
 
 	sgb = scb->sgb;
- 	scb->op = 1;
- 	any2scsi(scb->dataptr, (int) sgb);
- 	any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) );
-
-	for (i = 0;  i < SCpnt->use_sg;  i++)  {
- 	    any2scsi(sgb[i].ptr, (int) sg[i].address);
- 	    any2scsi(sgb[i].len, sg[i].length);
+	scb->op = 1;
+	any2scsi (scb->dataptr, (int) sgb);
+	any2scsi (scb->maxlen, SCpnt->use_sg * sizeof (Sgb));
+
+	for (i = 0; i < SCpnt->use_sg; i++) {
+	    any2scsi (sgb[i].ptr, (int) sg[i].address);
+	    any2scsi (sgb[i].len, sg[i].length);
 	}
-    }  else  {
+    }
+    else {
 	scb->op = 0;
-	any2scsi(scb->dataptr, (int) SCpnt->request_buffer);
-	any2scsi(scb->maxlen, SCpnt->request_bufflen);
+	any2scsi (scb->dataptr, (int) SCpnt->request_buffer);
+	any2scsi (scb->maxlen, SCpnt->request_bufflen);
     }
-    while (!mail_out(host, scb)) /* keep trying */;
 
-    return 1;
+    while (!mail_out (host, scb));	/* keep trying */
+
+    return (1);
 }
 
 
-int wd7000_command(Scsi_Cmnd *SCpnt)
+int wd7000_command (Scsi_Cmnd *SCpnt)
 {
-    wd7000_queuecommand(SCpnt, wd7000_scsi_done);
+    wd7000_queuecommand (SCpnt, wd7000_scsi_done);
 
-    while (SCpnt->SCp.phase > 0) barrier();  /* phase counts scbs down to 0 */
+    while (SCpnt->SCp.phase > 0)
+	barrier ();		/* phase counts scbs down to 0 */
 
-    return SCpnt->result;
+    return (SCpnt->result);
 }
 
 
-int wd7000_diagnostics( Adapter *host, int code )
+int wd7000_diagnostics (Adapter *host, int code)
 {
     static IcbDiag icb = {ICB_OP_DIAGNOSTICS};
     static unchar buf[256];
     unsigned long timeout;
 
     icb.type = code;
-    any2scsi(icb.len, sizeof(buf));
-    any2scsi(icb.ptr, (int) &buf);
+    any2scsi (icb.len, sizeof (buf));
+    any2scsi (icb.ptr, (int) &buf);
     icb.phase = 1;
     /*
      * This routine is only called at init, so there should be OGMBs
      * available.  I'm assuming so here.  If this is going to
      * fail, I can just let the timeout catch the failure.
      */
-    mail_out(host, (struct scb *) &icb);
-    timeout = jiffies + WAITnexttimeout;  /* wait up to 2 seconds */
+    mail_out (host, (struct scb *) &icb);
+    timeout = jiffies + WAITnexttimeout;	/* wait up to 2 seconds */
     while (icb.phase && jiffies < timeout)
-    	barrier(); /* wait for completion */
+	barrier ();		/* wait for completion */
 
-    if (icb.phase)  {
-	printk("wd7000_diagnostics: timed out.\n");
-	return 0;
-    }
-    if (make_code(icb.vue|(icb.status << 8),0))  {
-	printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n",
-	       icb.vue, icb.status);
-	return 0;
+    if (icb.phase) {
+	printk ("wd7000_diagnostics: timed out.\n");
+	return (0);
+    }
+    if (make_code (icb.vue | (icb.status << 8), 0)) {
+	printk ("wd7000_diagnostics: failed (0x%02x,0x%02x)\n",
+		icb.vue, icb.status);
+	return (0);
     }
 
-    return 1;
+    return (1);
 }
 
 
-int wd7000_init( Adapter *host )
+int wd7000_init (Adapter *host)
 {
-    InitCmd init_cmd = {
-	INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, {0,0,0}, OGMB_CNT, ICMB_CNT
+    InitCmd init_cmd =
+    {
+	INITIALIZATION,
+ 	7,
+	host->bus_on,
+	host->bus_off,
+	0,
+	{ 0, 0, 0 },
+	OGMB_CNT,
+	ICMB_CNT
     };
     int diag;
 
@@ -1171,84 +1268,78 @@
      *  Reset the adapter - only.  The SCSI bus was initialized at power-up,
      *  and we need to do this just so we control the mailboxes, etc.
      */
-    outb(ASC_RES, host->iobase+ASC_CONTROL);
-    delay(1);  /* reset pulse: this is 10ms, only need 25us */
-    outb(0,host->iobase+ASC_CONTROL);
-    host->control = 0;   /* this must always shadow ASC_CONTROL */
-
-    if (WAIT (host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
-        printk ("wd7000_init: WAIT timed out.\n"); 
-        return 0;					/* 0 = not ok */
-    }
-
-    if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1) {
-	printk("wd7000_init: ");
-
-	switch (diag)  {
-	case 2:
-	  printk("RAM failure.\n");
-	  break;
-	case 3:
-	  printk("FIFO R/W failed\n");
-	  break;
-	case 4:
-	  printk("SBIC register R/W failed\n");
-	  break;
-	case 5:
-	  printk("Initialization D-FF failed.\n");
-	  break;
-	case 6:
-	  printk("Host IRQ D-FF failed.\n");
-	  break;
-	case 7:
-	  printk("ROM checksum error.\n");
-	  break;
-	default:
-	  printk("diagnostic code 0x%02Xh received.\n", diag);
+    outb (ASC_RES, host->iobase + ASC_CONTROL);
+    delay (1);			/* reset pulse: this is 10ms, only need 25us */
+    outb (0, host->iobase + ASC_CONTROL);
+    host->control = 0;		/* this must always shadow ASC_CONTROL */
+
+    if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
+	printk ("wd7000_init: WAIT timed out.\n");
+	return (0);		/* 0 = not ok */
+    }
+
+    if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) {
+	printk ("wd7000_init: ");
+
+	switch (diag) {
+	    case 2:  printk ("RAM failure.\n");
+		     break;
+	    case 3:  printk ("FIFO R/W failed\n");
+		     break;
+	    case 4:  printk ("SBIC register R/W failed\n");
+		     break;
+	    case 5:  printk ("Initialization D-FF failed.\n");
+		     break;
+	    case 6:  printk ("Host IRQ D-FF failed.\n");
+		     break;
+	    case 7:  printk ("ROM checksum error.\n");
+		     break;
+	    default: printk ("diagnostic code 0x%02Xh received.\n", diag);
 	}
-	return 0;
+	return (0);
     }
-    
+
     /* Clear mailboxes */
-    memset(&(host->mb), 0, sizeof(host->mb));
+    memset (&(host->mb), 0, sizeof (host->mb));
 
     /* Execute init command */
-    any2scsi((unchar *) &(init_cmd.mailboxes), (int) &(host->mb));
-    if (!command_out(host, (unchar *) &init_cmd, sizeof(init_cmd)))  {
-	printk("wd7000_init: adapter initialization failed.\n"); 
-	return 0;
+    any2scsi ((unchar *) & (init_cmd.mailboxes), (int) &(host->mb));
+    if (!command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) {
+	printk ("wd7000_init: adapter initialization failed.\n");
+	return (0);
     }
 
-    if (WAIT (host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) {
-        printk ("wd7000_init: WAIT timed out.\n"); 
-        return 0;
+    if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) {
+	printk ("wd7000_init: WAIT timed out.\n");
+	return (0);
     }
 
-    if (request_irq(host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) {
-	printk("wd7000_init: can't get IRQ %d.\n", host->irq);
-	return 0;
+    if (request_irq (host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) {
+	printk ("wd7000_init: can't get IRQ %d.\n", host->irq);
+	return (0);
     }
-    if (request_dma(host->dma,"wd7000"))  {
-	printk("wd7000_init: can't get DMA channel %d.\n", host->dma);
-	free_irq(host->irq, NULL);
-	return 0;
+    if (request_dma (host->dma, "wd7000")) {
+	printk ("wd7000_init: can't get DMA channel %d.\n", host->dma);
+	free_irq (host->irq, NULL);
+	return (0);
     }
-    wd7000_enable_dma(host);
-    wd7000_enable_intr(host);
+    wd7000_enable_dma (host);
+    wd7000_enable_intr (host);
 
-    if (!wd7000_diagnostics(host,ICB_DIAG_FULL))  {
-	free_dma(host->dma);
-	free_irq(host->irq, NULL);
-	return 0;
+    if (!wd7000_diagnostics (host, ICB_DIAG_FULL)) {
+	free_dma (host->dma);
+	free_irq (host->irq, NULL);
+	return (0);
     }
 
-    return 1;
+    return (1);
 }
 
 
-void wd7000_revision(Adapter *host)
+void wd7000_revision (Adapter *host)
 {
-    static IcbRevLvl icb = {ICB_OP_GET_REVISION};
+    static IcbRevLvl icb =
+    {ICB_OP_GET_REVISION};
 
     icb.phase = 1;
     /*
@@ -1257,14 +1348,160 @@
      * the only damage will be that the revision will show up as 0.0,
      * which in turn means that scatter/gather will be disabled.
      */
-    mail_out(host, (struct scb *) &icb);
+    mail_out (host, (struct scb *) &icb);
     while (icb.phase)
-    	barrier(); /* wait for completion */
+	barrier ();		/* wait for completion */
     host->rev1 = icb.primary;
     host->rev2 = icb.secondary;
 }
 
 
+#undef SPRINTF
+#define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
+
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host)
+{
+    unsigned long flags;
+
+    save_flags (flags);
+    cli ();
+
+#ifdef WD7000_DEBUG
+    printk ("Buffer = <%.*s>, length = %d\n", length, buffer, length);
+#endif
+
+    /*
+     * Currently this is a no-op
+     */
+    printk ("Sorry, this function is currently out of order...\n");
+
+    restore_flags (flags);
+
+    return (length);
+}
+
+
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+{
+    struct Scsi_Host *host = NULL;
+    Scsi_Device *scd;
+    Adapter *adapter;
+    unsigned long flags;
+    char *pos = buffer;
+    short i;
+
+#ifdef WD7000_DEBUG
+    Mailbox *ogmbs, *icmbs;
+    short count;
+#endif
+
+    /*
+     * Find the specified host board.
+     */
+    for (i = 0; i < IRQS; i++)
+	if (wd7000_host[i] && (wd7000_host[i]->host_no == hostno)) {
+	    host = wd7000_host[i];
+
+	    break;
+	}
+
+    /*
+     * Host not found!
+     */
+    if (! host)
+	return (-ESRCH);
+
+    /*
+     * Has data been written to the file ?
+     */
+    if (inout)
+	return (wd7000_set_info (buffer, length, host));
+
+    adapter = (Adapter *) host->hostdata;
+
+    save_flags (flags);
+    cli ();
+
+    SPRINTF ("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", hostno, adapter->rev1, adapter->rev2);
+    SPRINTF ("  IO base:      0x%x\n", adapter->iobase);
+    SPRINTF ("  IRQ:          %d\n", adapter->irq);
+    SPRINTF ("  DMA channel:  %d\n", adapter->dma);
+    SPRINTF ("  Interrupts:   %d\n", adapter->int_counter);
+    SPRINTF ("  BUS_ON time:  %d nanoseconds\n", adapter->bus_on * 125);
+    SPRINTF ("  BUS_OFF time: %d nanoseconds\n", adapter->bus_off * 125);
+
+#ifdef WD7000_DEBUG
+    ogmbs = adapter->mb.ogmb;
+    icmbs = adapter->mb.icmb;
+
+    SPRINTF ("\nControl port value: 0x%x\n", adapter->control);
+    SPRINTF ("Incoming mailbox:\n");
+    SPRINTF ("  size: %d\n", ICMB_CNT);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < ICMB_CNT; i++)
+	if (icmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+
+    SPRINTF ("Outgoing mailbox:\n");
+    SPRINTF ("  size: %d\n", OGMB_CNT);
+    SPRINTF ("  next message: 0x%x\n", adapter->next_ogmb);
+    SPRINTF ("  queued messages: ");
+
+    for (i = count = 0; i < OGMB_CNT; i++)
+	if (ogmbs[i].status) {
+	    count++;
+	    SPRINTF ("0x%x ", i);
+	}
+
+    SPRINTF (count ? "\n" : "none\n");
+#endif
+
+    /*
+     * Display driver information for each device attached to the board.
+     */
+    scd = host->host_queue;
+   
+    SPRINTF ("\nAttached devices: %s\n", scd ? "" : "none");
+
+    for ( ; scd; scd = scd->next)
+	if (scd->host->host_no == hostno) {
+	    SPRINTF ("  [Channel: %02d, Id: %02d, Lun: %02d]  ",
+		     scd->channel, scd->id, scd->lun);
+	    SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
+		     scsi_device_types[(short) scd->type] : "Unknown device");
+
+	    for (i = 0; (i < 8) && (scd->vendor[i] >= 0x20); i++)
+		SPRINTF ("%c", scd->vendor[i]);
+	    SPRINTF (" ");
+
+	    for (i = 0; (i < 16) && (scd->model[i] >= 0x20); i++)
+		SPRINTF ("%c", scd->model[i]);
+	    SPRINTF ("\n");
+	}
+
+    SPRINTF ("\n");
+
+    restore_flags (flags);
+
+    /*
+     * Calculate start of next buffer, and return value.
+     */
+    *start = buffer + offset;
+
+    if ((pos - buffer) < offset)
+	return (0);
+    else if ((pos - buffer - offset) < length)
+	return (pos - buffer - offset);
+    else
+	return (length);
+}
+
+
 /*
  *  Returns the number of adapters this driver is supporting.
  *
@@ -1277,84 +1514,102 @@
  */
 int wd7000_detect (Scsi_Host_Template *tpnt)
 {
-    short present = 0, biosaddr_ptr, cfg_ptr, sig_ptr, i, pass;
+    short present = 0, biosaddr_ptr, sig_ptr, i, pass;
     short biosptr[NUM_CONFIGS];
     unsigned iobase;
     Adapter *host = NULL;
     struct Scsi_Host *sh;
 
-    for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1);
+#ifdef WD7000_DEBUG
+    printk ("wd7000_detect: started\n");
+#endif
+
+    for (i = 0; i < IRQS; wd7000_host[i++] = NULL) ;
+    for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1) ;
 
     tpnt->proc_dir = &proc_scsi_wd7000;
+    tpnt->proc_info = &wd7000_proc_info;
 
     /*
      * Set up SCB free list, which is shared by all adapters
      */
     init_scbs ();
 
-    for (pass = 0, cfg_ptr = 0; pass < NUM_CONFIGS; pass++) {
-        for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++)
+    for (pass = 0; pass < NUM_CONFIGS; pass++) {
+	/*
+	 * First, search for BIOS SIGNATURE...
+	 */
+	for (biosaddr_ptr = 0; biosaddr_ptr < NUM_ADDRS; biosaddr_ptr++)
 	    for (sig_ptr = 0; sig_ptr < NUM_SIGNATURES; sig_ptr++) {
-	        for (i = 0; i < pass; i++)
-	            if (biosptr[i] == biosaddr_ptr)
-		        break;
-
-		if ((i == pass) &&
-		    check_signature(wd7000_biosaddr[biosaddr_ptr] +
-		             signatures[sig_ptr].ofs,
-			     signatures[sig_ptr].sig,
-		             signatures[sig_ptr].len))
-		    goto bios_matched;
-	    }
+		for (i = 0; i < pass; i++)
+		    if (biosptr[i] == biosaddr_ptr)
+			break;
 
-bios_matched:
+		if (i == pass) {
+#if (LINUX_VERSION_CODE < 0x020100)
+#else
+		    void *biosaddr = ioremap (wd7000_biosaddr[biosaddr_ptr] +
+			                      signatures[sig_ptr].ofs,
+					      signatures[sig_ptr].len);
+#endif
+		    short bios_match = memcmp ((char *) biosaddr, signatures[sig_ptr].sig,
+			                       signatures[sig_ptr].len);
+
+#if (LINUX_VERSION_CODE < 0x020100)
+#else
+		    iounmap (biosaddr);
+#endif
 
-#ifdef DEBUG
+		    if (! bios_match)
+		        goto bios_matched;
+		}
+	    }
+
+      bios_matched:
+	/*
+	 * BIOS SIGNATURE has been found.
+	 */
+#ifdef WD7000_DEBUG
 	printk ("wd7000_detect: pass %d\n", pass + 1);
 
-        if (biosaddr_ptr == NUM_ADDRS)
+	if (biosaddr_ptr == NUM_ADDRS)
 	    printk ("WD-7000 SST BIOS not detected...\n");
 	else
 	    printk ("WD-7000 SST BIOS detected at 0x%lx: checking...\n",
-	            wd7000_biosaddr[biosaddr_ptr]);
+		    wd7000_biosaddr[biosaddr_ptr]);
 #endif
 
-	if (wd7000_card_num) 
-	    iobase = wd7000_setupIO[wd7000_card_num - 1];
-	else {
-	    if (configs[cfg_ptr++].irq < 0)
-		continue;
+	if (configs[pass].irq < 0)
+	    continue;
 
-	    iobase = configs[cfg_ptr - 1].iobase;
-	}
+	iobase = configs[pass].iobase;
 
-#ifdef DEBUG
- 	printk ("wd7000_detect: check IO 0x%x region...\n", iobase);
+#ifdef WD7000_DEBUG
+	printk ("wd7000_detect: check IO 0x%x region...\n", iobase);
 #endif
 
-	if (! check_region (iobase, 4)) {
+	if (!check_region (iobase, 4)) {
 
-#ifdef DEBUG
- 	    printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
+#ifdef WD7000_DEBUG
+	    printk ("wd7000_detect: ASC reset (IO 0x%x) ...", iobase);
 #endif
-
- 	    /*
-  	     * ASC reset...
- 	     */
- 	    outb (ASC_RES, iobase + ASC_CONTROL);
- 	    delay (1);
- 	    outb (0, iobase + ASC_CONTROL);
-
- 	    if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0))
-#ifdef DEBUG
- 	    {
-  		printk ("failed!\n");
-  		continue;
- 	    }
- 	    else
-  		printk ("ok!\n");
+	    /*
+	     * ASC reset...
+	     */
+	    outb (ASC_RES, iobase + ASC_CONTROL);
+	    delay (1);
+	    outb (0, iobase + ASC_CONTROL);
+
+	    if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0))
+#ifdef WD7000_DEBUG
+	    {
+		printk ("failed!\n");
+		continue;
+	    }
+	    else
+		printk ("ok!\n");
 #else
-	    continue;
+		continue;
 #endif
 
 	    if (inb (iobase + ASC_INTR_STAT) == 1) {
@@ -1368,33 +1623,27 @@
 		sh = scsi_register (tpnt, sizeof (Adapter));
 		host = (Adapter *) sh->hostdata;
 
-#ifdef DEBUG
-		printk ("wd7000_detect: adapter allocated at 0x%x\n",
-			(int) host);
+#ifdef WD7000_DEBUG
+		printk ("wd7000_detect: adapter allocated at 0x%x\n", (int) host);
 #endif
 
 		memset (host, 0, sizeof (Adapter));
 
-		if (wd7000_card_num) {
-		    host->irq = wd7000_setupIRQ[--wd7000_card_num];
-		    host->dma = wd7000_setupDMA[wd7000_card_num];
-		}
-		else {
-		    host->irq = configs[cfg_ptr - 1].irq;
-		    host->dma = configs[cfg_ptr - 1].dma;
-		}
-
-	    	host->sh = sh;
-    		host->iobase = iobase;
-		irq2host[host->irq] = host;
+		host->irq = configs[pass].irq;
+		host->dma = configs[pass].dma;
+		host->iobase = iobase;
+		host->int_counter = 0;
+		host->bus_on = configs[pass].bus_on;
+		host->bus_off = configs[pass].bus_off;
+		host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
 
-#ifdef DEBUG
+#ifdef WD7000_DEBUG
 		printk ("wd7000_detect: Trying init WD-7000 card at IO "
 			"0x%x, IRQ %d, DMA %d...\n",
 			host->iobase, host->irq, host->dma);
 #endif
 
-		if (! wd7000_init (host)) {  /* Initialization failed */
+		if (!wd7000_init (host)) {	/* Initialization failed */
 		    scsi_unregister (sh);
 
 		    continue;
@@ -1403,7 +1652,7 @@
 		/*
 		 *  OK from here - we'll use this adapter/configuration.
 		 */
-		wd7000_revision (host);   /* important for scatter/gather */
+		wd7000_revision (host);		/* important for scatter/gather */
 
 		/*
 		 * Register our ports.
@@ -1411,34 +1660,34 @@
 		request_region (host->iobase, 4, "wd7000");
 
 		/*
-		 *  For boards before rev 6.0, scatter/gather
-		 *  isn't supported.
+		 *  For boards before rev 6.0, scatter/gather isn't supported.
 		 */
 		if (host->rev1 < 6)
 		    sh->sg_tablesize = SG_NONE;
 
-		present++;		/* count it */
+		present++;	/* count it */
 
 		if (biosaddr_ptr != NUM_ADDRS)
 		    biosptr[pass] = biosaddr_ptr;
 
 		printk ("Western Digital WD-7000 (rev %d.%d) ",
 			host->rev1, host->rev2);
-		printk ("using IO 0x%x, IRQ %d, DMA %d.\n", 
+		printk ("using IO 0x%x, IRQ %d, DMA %d.\n",
 			host->iobase, host->irq, host->dma);
+                printk ("  BUS_ON time: %dns, BUS_OFF time: %dns\n",
+                        host->bus_on * 125, host->bus_off * 125);
 	    }
 	}
 
-#ifdef DEBUG
+#ifdef WD7000_DEBUG
 	else
- 	    printk ("wd7000_detect: IO 0x%x region already allocated!\n",
-		    iobase);
+	    printk ("wd7000_detect: IO 0x%x region already allocated!\n", iobase);
 #endif
 
     }
 
-    if (! present)
-       printk ("Failed initialization of WD-7000 SCSI card!\n");
+    if (!present)
+	printk ("Failed initialization of WD-7000 SCSI card!\n");
 
     return (present);
 }
@@ -1447,42 +1696,81 @@
 /*
  *  I have absolutely NO idea how to do an abort with the WD7000...
  */
-int wd7000_abort(Scsi_Cmnd * SCpnt)
+int wd7000_abort (Scsi_Cmnd *SCpnt)
 {
     Adapter *host = (Adapter *) SCpnt->host->hostdata;
 
-    if (inb(host->iobase+ASC_STAT) & INT_IM)  {
-	printk("wd7000_abort: lost interrupt\n");
-	wd7000_intr_handle(host->irq, NULL, NULL);
-	return SCSI_ABORT_SUCCESS;
+    if (inb (host->iobase + ASC_STAT) & INT_IM) {
+	printk ("wd7000_abort: lost interrupt\n");
+	wd7000_intr_handle (host->irq, NULL, NULL);
+
+	return (SCSI_ABORT_SUCCESS);
     }
 
-    return SCSI_ABORT_SNOOZE;
+    return (SCSI_ABORT_SNOOZE);
 }
 
 
 /*
  *  I also have no idea how to do a reset...
  */
-int wd7000_reset(Scsi_Cmnd * SCpnt, unsigned int ignored)
+int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int unused)
 {
-    return SCSI_RESET_PUNT;
+    return (SCSI_RESET_PUNT);
 }
 
 
 /*
- *  This was borrowed directly from aha1542.c, but my disks are organized
- *  this way, so I think it will work OK.  Someone who is ambitious can
- *  borrow a newer or more complete version from another driver.
+ *  This was borrowed directly from aha1542.c. (Zaga)
  */
-int wd7000_biosparam(Disk * disk, kdev_t dev, int* ip)
+int wd7000_biosparam (Disk *disk, kdev_t dev, int *ip)
 {
-  int size = disk->capacity;
-  ip[0] = 64;
-  ip[1] = 32;
-  ip[2] = size >> 11;
-/*  if (ip[2] >= 1024) ip[2] = 1024; */
-  return 0;
+#ifdef WD7000_DEBUG
+    printk ("wd7000_biosparam: dev=%s, size=%d, ", kdevname (dev), disk->capacity);
+#endif
+
+    /*
+     * try default translation
+     */
+    ip[0] = 64;
+    ip[1] = 32;
+    ip[2] = disk->capacity / (64 * 32);
+
+    /*
+     * for disks >1GB do some guessing
+     */
+    if (ip[2] >= 1024) {
+	int info[3];
+
+	/*
+	 * try to figure out the geometry from the partition table
+	 */
+	if ((scsicam_bios_param (disk, dev, info) < 0) ||
+	    !(((info[0] == 64) && (info[1] == 32)) ||
+	      ((info[0] == 255) && (info[1] == 63)))) {
+	    printk ("wd7000_biosparam: unable to verify geometry for disk with >1GB.\n"
+		    "                  using extended translation.\n");
+
+	    ip[0] = 255;
+	    ip[1] = 63;
+	    ip[2] = disk->capacity / (255 * 63);
+	}
+	else {
+	    ip[0] = info[0];
+	    ip[1] = info[1];
+	    ip[2] = info[2];
+
+	    if (info[0] == 255)
+		printk ("wd7000_biosparam: current partition table is using extended translation.\n");
+	}
+    }
+
+#ifdef WD7000_DEBUG
+    printk ("bios geometry: head=%d, sec=%d, cyl=%d\n", ip[0], ip[1], ip[2]);
+    printk ("WARNING: check, if the bios geometry is correct.\n");
+#endif
+
+    return (0);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.89/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h
--- v2.1.89/linux/drivers/scsi/wd7000.h	Fri Feb  6 15:33:40 1998
+++ linux/drivers/scsi/wd7000.h	Tue Mar 10 10:20:58 1998
@@ -8,17 +8,23 @@
  * This file has been reduced to only the definitions needed for the
  * WD7000 host structure.
  *
+ * Revision by Miroslav Zagorac <zaga@fly.cc.fer.hr>  Jun 1997.
  */
 
 #include <linux/types.h>
 #include <linux/kdev_t.h>
 
-int wd7000_detect(Scsi_Host_Template *);
-int wd7000_command(Scsi_Cmnd *);
-int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int wd7000_abort(Scsi_Cmnd *);
-int wd7000_reset(Scsi_Cmnd *, unsigned int);
-int wd7000_biosparam(Disk *, kdev_t, int *);
+extern struct proc_dir_entry proc_scsi_wd7000;
+
+
+int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host);
+int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+int wd7000_detect (Scsi_Host_Template *);
+int wd7000_command (Scsi_Cmnd *);
+int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd7000_abort (Scsi_Cmnd *);
+int wd7000_reset (Scsi_Cmnd *, unsigned int);
+int wd7000_biosparam (Disk *, kdev_t, int *);
 
 #ifndef NULL
 #define NULL 0L
@@ -38,18 +44,22 @@
 #define WD7000_Q    16
 #define WD7000_SG   16
 
-#define WD7000 {					   \
-	name:              "Western Digital WD-7000",      \
-	detect:            wd7000_detect,                  \
-	command:           wd7000_command,		   \
-	queuecommand:      wd7000_queuecommand,		   \
-	abort:             wd7000_abort,		   \
-	reset:             wd7000_reset,		   \
-	bios_param:        wd7000_biosparam,               \
-	can_queue:         WD7000_Q,			   \
-	this_id:           7,				   \
-	sg_tablesize:      WD7000_SG,			   \
-	cmd_per_lun:       1,				   \
-	unchecked_isa_dma: 1,				   \
-	use_clustering:    ENABLE_CLUSTERING}
+#define WD7000 {						\
+	proc_dir:		&proc_scsi_wd7000,		\
+	proc_info:		wd7000_proc_info,		\
+	name:			"Western Digital WD-7000",	\
+	detect:			wd7000_detect,			\
+	command:		wd7000_command,			\
+	queuecommand:		wd7000_queuecommand,		\
+	abort:			wd7000_abort,			\
+	reset:			wd7000_reset,			\
+	bios_param:		wd7000_biosparam,		\
+	can_queue:		WD7000_Q,			\
+	this_id:		7,				\
+	sg_tablesize:		WD7000_SG,			\
+	cmd_per_lun:		1,				\
+	unchecked_isa_dma:	1,				\
+	use_clustering:		ENABLE_CLUSTERING,		\
+	use_new_eh_code:	0				\
+}
 #endif
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.89/linux/drivers/sound/Config.in	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/Config.in	Fri Mar 13 15:36:03 1998
@@ -6,6 +6,8 @@
 
 dep_tristate '100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support' CONFIG_SB $CONFIG_SOUND
 if [ "$CONFIG_SB" = "y" ]; then
+    bool 'Is the card a Soundman Games ?' CONFIG_SM_GAMES
+    bool 'Are you using the IBM Mwave "emulation" of SB ?' CONFIG_SB_MWAVE
     hex 'I/O base for SB Check from manual of the card' CONFIG_SB_BASE 220
     int 'Sound Blaster IRQ Check from manual of the card' CONFIG_SB_IRQ 7
     int 'Sound Blaster DMA 0, 1 or 3' CONFIG_SB_DMA 1
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/Makefile linux/drivers/sound/Makefile
--- v2.1.89/linux/drivers/sound/Makefile	Tue Feb 17 13:12:47 1998
+++ linux/drivers/sound/Makefile	Sat Mar 14 12:07:42 1998
@@ -117,22 +117,6 @@
   endif
 endif
 
-ifeq ($(CONFIG_MPU401),y)
-LX_OBJS += mpu401.o
-else
-  ifeq ($(CONFIG_MPU401),m)
-  MX_OBJS += mpu401.o
-  else
-    ifeq ($(CONFIG_MPU_EMU),y)
-    LX_OBJS += mpu401.o
-    else
-      ifeq ($(CONFIG_MPU_EMU),m)
-      MX_OBJS += mpu401.o
-      endif
-    endif
-  endif
-endif
-
 ifeq ($(CONFIG_UART401),y)
 LX_OBJS += uart401.o
 else
@@ -159,9 +143,31 @@
 
 ifeq ($(CONFIG_SSCAPE),y)
 L_OBJS += sscape.o
+LX_OBJS += ad1848.o
+CONFIG_MPU401 = y
 else
   ifeq ($(CONFIG_SSCAPE),m)
   M_OBJS += sscape.o
+  MX_OBJS += ad1848.o
+  ifneq ($(CONFIG_MPU401),y)
+    CONFIG_MPU401 = m
+  endif
+  endif
+endif
+
+ifeq ($(CONFIG_MPU401),y)
+LX_OBJS += mpu401.o
+else
+  ifeq ($(CONFIG_MPU401),m)
+  MX_OBJS += mpu401.o
+  else
+    ifeq ($(CONFIG_MPU_EMU),y)
+    LX_OBJS += mpu401.o
+    else
+      ifeq ($(CONFIG_MPU_EMU),m)
+      MX_OBJS += mpu401.o
+      endif
+    endif
   endif
 endif
 
@@ -265,7 +271,7 @@
 maui.o: maui_boot.h
 
 maui_boot.h: $(CONFIG_MAUI_BOOT_FILE) bin2hex
-	bin2hex maui_os < "$(CONFIG_MAUI_BOOT_FILE)" > $@
+	bin2hex -i maui_os < "$(CONFIG_MAUI_BOOT_FILE)" > $@
 	@ ( \
 	    echo 'ifeq ($(strip $(CONFIG_MAUI_BOOT_FILE)),$$(strip $$(CONFIG_MAUI_BOOT_FILE)))'; \
 	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
@@ -297,7 +303,7 @@
 trix.o: trix_boot.h
 
 trix_boot.h: $(CONFIG_TRIX_BOOT_FILE) hex2hex
-	hex2hex trix_boot < "$(CONFIG_TRIX_BOOT_FILE)" > $@
+	hex2hex -i trix_boot < "$(CONFIG_TRIX_BOOT_FILE)" > $@
 	@ ( \
 	    echo 'ifeq ($(strip $(CONFIG_TRIX_BOOT_FILE)),$$(strip $$(CONFIG_TRIX_BOOT_FILE)))'; \
 	    echo 'FILES_BOOT_UP_TO_DATE += $@'; \
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.89/linux/drivers/sound/ad1848.c	Mon Feb 23 18:12:08 1998
+++ linux/drivers/sound/ad1848.c	Thu Mar 12 10:52:09 1998
@@ -23,6 +23,12 @@
  */
 /*
  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ *		     general sleep/wakeup clean up.
+ * Alan Cox	   : reformatted. Fixed SMP bugs. Moved to kernel alloc/free
+ *		     of irqs. Use dev_id.
+ *
+ * Status:
+ *		Tested. Believed fully functional.
  */
 
 #include <linux/config.h>
@@ -144,9 +150,9 @@
 
 static int ad_read(ad1848_info * devc, int reg)
 {
-	unsigned long   flags;
-	int             x;
-	int             timeout = 900000;
+	unsigned long flags;
+	int x;
+	int timeout = 900000;
 
 	while (timeout > 0 && inb(devc->base) == 0x80)	/*Are we initializing */
 		timeout--;
@@ -163,11 +169,10 @@
 
 static void ad_write(ad1848_info * devc, int reg, int data)
 {
-	unsigned long   flags;
-	int             timeout = 900000;
+	unsigned long flags;
+	int timeout = 900000;
 
-	while (timeout > 0 &&
-	       inb(devc->base) == 0x80)		/*Are we initializing */
+	while (timeout > 0 && inb(devc->base) == 0x80)	/* Are we initializing */
 		timeout--;
 
 	save_flags(flags);
@@ -180,7 +185,7 @@
 
 static void wait_for_calibration(ad1848_info * devc)
 {
-	int             timeout = 0;
+	int timeout = 0;
 
 	/*
 	 * Wait until the auto calibration process has finished.
@@ -1751,7 +1756,11 @@
 		else
 			devc->audio_flags |= DMA_DUPLEX;
 	}
-	
+
+	portc = (ad1848_port_info *) kmalloc(sizeof(ad1848_port_info), GFP_KERNEL);
+	if(portc==NULL)
+		return -1;
+		
 	if ((my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
 					     dev_name,
 					     &ad1848_audio_driver,
@@ -1762,12 +1771,11 @@
 					     dma_playback,
 					     dma_capture)) < 0)
 	{
+		kfree(portc);
+		portc=NULL;
 		return -1;
 	}
-	portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(ad1848_port_info)));
-	sound_mem_sizes[sound_nblocks] = sizeof(ad1848_port_info);
-	if (sound_nblocks < 1024)
-		sound_nblocks++;;
+	
 	audio_devs[my_dev]->portc = portc;
 	memset((char *) portc, 0, sizeof(*portc));
 
@@ -1777,23 +1785,21 @@
 
 	if (irq > 0)
 	{
-		irq2dev[irq] = devc->dev_no = my_dev;
-		if (snd_set_irq_handler(devc->irq, adintr,
-					  devc->name,
-					  NULL) < 0)
+		devc->dev_no = my_dev;
+		if (request_irq(devc->irq, adintr, 0, devc->name, (void *)my_dev) < 0)
 		{
-			printk(KERN_WARNING "ad1848: IRQ in use\n");
+			printk(KERN_WARNING "ad1848: Unable to allocate IRQ\n");
 		}
 		if (devc->model != MD_1848 && devc->model != MD_C930)
 		{
-			int             x;
-			unsigned char   tmp = ad_read(devc, 16);
+			int x;
+			unsigned char tmp = ad_read(devc, 16);
 
 			devc->timer_ticks = 0;
 
 			ad_write(devc, 21, 0x00);	/* Timer MSB */
 			ad_write(devc, 20, 0x10);	/* Timer LSB */
-
+#ifndef __SMP__
 			ad_write(devc, 16, tmp | 0x40);	/* Enable timer */
 			for (x = 0; x < 100000 && devc->timer_ticks == 0; x++);
 			ad_write(devc, 16, tmp & ~0x40);	/* Disable timer */
@@ -1805,6 +1811,9 @@
 				DDB(printk("Interrupt test OK\n"));
 				devc->irq_ok = 1;
 			}
+#else
+			devc->irq_ok=1;
+#endif			
 		}
 		else
 			devc->irq_ok = 1;	/* Couldn't test. assume it's OK */
@@ -1840,7 +1849,7 @@
 
 void ad1848_control(int cmd, int arg)
 {
-	ad1848_info    *devc;
+	ad1848_info *devc;
 
 	if (nr_ad1848_devs < 1)
 		return;
@@ -1850,7 +1859,7 @@
 	switch (cmd)
 	{
 		case AD1848_SET_XTAL:	/* Change clock frequency of AD1845 (only ) */
-		if (devc->model != MD_1845)
+			if (devc->model != MD_1845)
 				return;
 			ad_enter_MCE(devc);
 			ad_write(devc, 29, (ad_read(devc, 29) & 0x1f) | (arg << 5));
@@ -1859,8 +1868,8 @@
 
 		case AD1848_MIXER_REROUTE:
 		{
-			int             o = (arg >> 8) & 0xff;
-			int             n = arg & 0xff;
+			int o = (arg >> 8) & 0xff;
+			int n = arg & 0xff;
 
 			if (n == SOUND_MIXER_NONE)
 			{	/* Just hide this control */
@@ -1906,12 +1915,14 @@
 		
 	if (devc != NULL)
 	{
+		if(audio_devs[dev]->portc!=NULL)
+			kfree(audio_devs[dev]->portc);
 		release_region(devc->base, 4);
 
 		if (!share_dma)
 		{
 			if (irq > 0)
-				snd_release_irq(devc->irq);
+				free_irq(devc->irq, NULL);
 
 			sound_free_dma(audio_devs[dev]->dmap_out->dma);
 
@@ -1926,35 +1937,15 @@
 
 void adintr(int irq, void *dev_id, struct pt_regs *dummy)
 {
-	unsigned char   status;
-	ad1848_info    *devc;
-	int             dev;
-	int             alt_stat = 0xff;
-	unsigned char   c930_stat = 0;
-	int             cnt = 0;
+	unsigned char status;
+	ad1848_info *devc;
+	int dev;
+	int alt_stat = 0xff;
+	unsigned char c930_stat = 0;
+	int cnt = 0;
 
-	if (irq < 0 || irq > 15)
-	{
-		dev = -1;
-	}
-	else
-		dev = irq2dev[irq];
-
-	if (dev < 0 || dev >= num_audiodevs)
-	{
-		for (irq = 0; irq < 17; irq++)
-			if (irq2dev[irq] != -1)
-				break;
-
-		if (irq > 15)
-		{
-			/* printk("ad1848.c: Bogus interrupt %d\n", irq); */
-			return;
-		}
-		dev = irq2dev[irq];
-		devc = (ad1848_info *) audio_devs[dev]->devc;
-	} else
-		devc = (ad1848_info *) audio_devs[dev]->devc;
+	dev = (int)dev_id;
+	devc = (ad1848_info *) audio_devs[dev]->devc;
 
 interrupt_again:		/* Jump back here if int status doesn't reset */
 
@@ -2542,12 +2533,12 @@
 
 #ifdef MODULE
 
-MODULE_PARM(io, "i");
-MODULE_PARM(irq, "i");
-MODULE_PARM(dma, "i");
-MODULE_PARM(dma2, "i");
-MODULE_PARM(type, "i");
-MODULE_PARM(deskpro_xl, "i");
+MODULE_PARM(io, "i");			/* I/O for a raw AD1848 card */
+MODULE_PARM(irq, "i");			/* IRQ to use */
+MODULE_PARM(dma, "i");			/* First DMA channel */
+MODULE_PARM(dma2, "i");			/* Second DMA channel */
+MODULE_PARM(type, "i");			/* Card type */
+MODULE_PARM(deskpro_xl, "i");		/* Special magic for Deskpro XL boxen */
 
 int io = -1;
 int irq = -1;
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v2.1.89/linux/drivers/sound/audio.c	Mon Feb 23 18:12:08 1998
+++ linux/drivers/sound/audio.c	Wed Mar 11 11:39:27 1998
@@ -21,7 +21,6 @@
 
 #include <linux/config.h>
 #include <linux/stddef.h>
-#include <linux/kerneld.h>
 
 #include "sound_config.h"
 
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/bin2hex.c linux/drivers/sound/bin2hex.c
--- v2.1.89/linux/drivers/sound/bin2hex.c	Tue Dec 23 13:52:02 1997
+++ linux/drivers/sound/bin2hex.c	Thu Mar 12 10:52:09 1998
@@ -2,12 +2,27 @@
 
 int main( int argc, const char * argv [] )
 {
-    const char * varname = argv[1];
+    const char * varname;
     int i = 0;
     int c;
+    int id = 0;
 
+    if(argv[1] && strcmp(argv[1],"-i")==0)
+    {
+    	argv++;
+    	argc--;
+    	id=1;
+    }
+    	
+    if(argc==1)
+    {
+    	fprintf(stderr, "bin2hex: [-i] firmware\n");
+    	exit(1);
+    }
+    
+    varname = argv[1];
     printf( "/* automatically generated by bin2hex */\n" );
-    printf( "static unsigned char %s [] =\n{\n", varname );
+    printf( "static unsigned char %s [] %s =\n{\n", varname , id?"__initdata":"");
 
     while ( ( c = getchar( ) ) != EOF )
     {
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c
--- v2.1.89/linux/drivers/sound/cs4232.c	Mon Feb 23 18:12:08 1998
+++ linux/drivers/sound/cs4232.c	Thu Mar 12 10:52:09 1998
@@ -12,7 +12,14 @@
  *      CS4232
  *      CS4236
  *      CS4236B
+ *
+ * Note: You will need a PnP config setup to initialise some CS4232 boards
+ * anyway.
+ *
+ * Changes
+ *	Alan Cox	Modularisation, Basic cleanups.
  */
+
 /*
  * Copyright (C) by Hannu Savolainen 1993-1997
  *
@@ -20,6 +27,7 @@
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
+
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -99,6 +107,8 @@
 	 * just one CS4232 compatible device can exist on the system. Also this
 	 * method conflicts with possible PnP support in the OS. For this reason 
 	 * driver is just a temporary kludge.
+	 *
+	 * Also the Cirrus/Crystal method doesnt always work. Try ISAPnP first ;)
 	 */
 
 	/*
@@ -106,8 +116,8 @@
 	 * first time.
 	 */
 
-	for (n = 0; n < 4; n++) {
-		
+	for (n = 0; n < 4; n++)
+	{	
 		/*
 		 *	Wake up the card by sending a 32 byte Crystal key to the key port.
 		 */
@@ -298,8 +308,7 @@
  *	loaded ready.
  */
 
-int
-init_module(void)
+int init_module(void)
 {
 	if (io == -1 || irq == -1 || dma == -1 || dma2 == -1)
 	{
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.89/linux/drivers/sound/dev_table.c	Mon Feb 23 18:12:08 1998
+++ linux/drivers/sound/dev_table.c	Tue Mar 10 11:01:47 1998
@@ -148,9 +148,6 @@
 		}
 	}
 
-        for (i=0;i<num_audiodevs;i++)
-		DMAbuf_deinit(i);
-
 	if (trace_init)
 		printk(KERN_DEBUG "Sound unload complete\n");
 }
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.89/linux/drivers/sound/dmabuf.c	Tue Mar 10 10:03:33 1998
+++ linux/drivers/sound/dmabuf.c	Thu Mar 12 10:53:23 1998
@@ -104,7 +104,7 @@
 	dmap->raw_buf_phys = virt_to_bus(start_addr);
 
 	for (i = MAP_NR(start_addr); i <= MAP_NR(end_addr); i++)
-		set_bit(PG_reserved, &mem_map[i].flags);;
+		set_bit(PG_reserved, &mem_map[i].flags);
 	return 0;
 }
 
@@ -115,8 +115,6 @@
 
 	if (dmap->raw_buf == NULL)
 		return;
-	if (dmap->mapping_flags & DMA_MAP_MAPPED)
-		return;		/* Don't free mmapped buffer. Will use it next time */
 	for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1);
 
 	start_addr = (unsigned long) dmap->raw_buf;
@@ -127,6 +125,8 @@
 
 	free_pages((unsigned long) dmap->raw_buf, sz);
 	dmap->raw_buf = NULL;
+	/* Remember the buffer is deleted so we dont Oops later */
+	dmap->fragment_size = 0;
 }
 
 
@@ -206,6 +206,7 @@
 		dmap->dma_mode = DMODE_NONE;
 	dmap->flags &= ~DMA_BUSY;
 	disable_dma(dmap->dma);
+	sound_free_dmap(dmap);
 }
 
 
@@ -832,12 +833,12 @@
 	dmap->user_counter += l;
 	dmap->flags |= DMA_DIRTY;
 
-	if (dmap->user_counter >= dmap->max_byte_counter) {
+	if (dmap->byte_counter >= dmap->max_byte_counter) {
 		/* Wrap the byte counters */
-		long decr = dmap->user_counter;
-		dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
-		decr -= dmap->user_counter;
-		dmap->byte_counter -= decr;
+		long decr = dmap->byte_counter;
+		dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
+		decr -= dmap->byte_counter;
+		dmap->user_counter -= decr;
 	}
 	end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
 
@@ -928,7 +929,7 @@
 			dmap->byte_counter += dmap->bytes_in_use;
 			if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
 				long decr = dmap->byte_counter;
-				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+				dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
 				decr -= dmap->byte_counter;
 				dmap->user_counter -= decr;
 			}
@@ -952,7 +953,7 @@
 		dmap->byte_counter += dmap->bytes_in_use;
 		if (dmap->byte_counter >= dmap->max_byte_counter) {	/* Overflow */
 			long decr = dmap->byte_counter;
-			dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
+			dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use);
 			decr -= dmap->byte_counter;
 			dmap->user_counter -= decr;
 		}
@@ -1209,20 +1210,6 @@
 unsigned int DMAbuf_poll(struct file * file, int dev, poll_table *wait)
 {
 	return poll_input(file, dev, wait) | poll_output(file, dev, wait);
-}
-
-void DMAbuf_deinit(int dev)
-{
-	struct audio_operations *adev = audio_devs[dev];
-	/* This routine is called when driver is being unloaded */
-	if (!adev)
-		return;
-#ifdef RUNTIME_DMA_ALLOC
-	sound_free_dmap(adev->dmap_out);
-
-	if (adev->flags & DMA_DUPLEX)
-		sound_free_dmap(adev->dmap_in);
-#endif
 }
 
 #endif
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/dmabuf.c.old linux/drivers/sound/dmabuf.c.old
--- v2.1.89/linux/drivers/sound/dmabuf.c.old	Mon Dec  1 09:45:01 1997
+++ linux/drivers/sound/dmabuf.c.old	Wed Dec 31 16:00:00 1969
@@ -1,1599 +0,0 @@
-/*
- * sound/dmabuf.c
- *
- * The DMA buffer manager for digitized voice applications
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1997
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-#include <linux/config.h>
-
-#define BE_CONSERVATIVE
-
-#include "sound_config.h"
-
-#if defined(CONFIG_AUDIO) || defined(CONFIG_GUSHW)
-
-static struct wait_queue *in_sleeper[MAX_AUDIO_DEV] =
-{NULL};
-static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] =
-{
-  {0}};
-static struct wait_queue *out_sleeper[MAX_AUDIO_DEV] =
-{NULL};
-static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] =
-{
-  {0}};
-
-static int      ndmaps = 0;
-
-#define MAX_DMAP (MAX_AUDIO_DEV*2)
-
-static struct dma_buffparms dmaps[MAX_DMAP] =
-{
-  {0}};
-
-static void     dma_reset_output (int dev);
-static void     dma_reset_input (int dev);
-static int      local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
-
-static void
-dma_init_buffers (int dev, struct dma_buffparms *dmap)
-{
-
-  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
-  dmap->byte_counter = 0;
-  dmap->max_byte_counter = 8000 * 60 * 60;
-  dmap->bytes_in_use = dmap->buffsize;
-
-  dmap->dma_mode = DMODE_NONE;
-  dmap->mapping_flags = 0;
-  dmap->neutral_byte = 0x80;
-  dmap->data_rate = 8000;
-  dmap->cfrag = -1;
-  dmap->closing = 0;
-  dmap->nbufs = 1;
-  dmap->flags = DMA_BUSY;	/* Other flags off */
-}
-
-static int
-open_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan)
-{
-  if (dmap->flags & DMA_BUSY)
-    return -EBUSY;
-
-  {
-    int             err;
-
-    if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0)
-      return err;
-  }
-
-  if (dmap->raw_buf == NULL)
-    {
-      printk ("Sound: DMA buffers not available\n");
-      return -ENOSPC;		/* Memory allocation failed during boot */
-    }
-
-  if (sound_open_dma (chan, audio_devs[dev]->name))
-    {
-      printk ("Unable to grab(2) DMA%d for the audio driver\n", chan);
-      return -EBUSY;
-    }
-
-  dma_init_buffers (dev, dmap);
-  dmap->open_mode = mode;
-  dmap->subdivision = dmap->underrun_count = 0;
-  dmap->fragment_size = 0;
-  dmap->max_fragments = 65536;	/* Just a large value */
-  dmap->byte_counter = 0;
-  dmap->max_byte_counter = 8000 * 60 * 60;
-  dmap->applic_profile = APF_NORMAL;
-  dmap->needs_reorg = 1;
-  dmap->audio_callback = NULL;
-  dmap->callback_parm = 0;
-
-
-  if (dmap->dma_mode & DMODE_OUTPUT)
-    {
-      out_sleep_flag[dev].opts = WK_NONE;
-    }
-  else
-    {
-      in_sleep_flag[dev].opts = WK_NONE;
-    }
-
-  return 0;
-}
-
-static void
-close_dmap (int dev, struct dma_buffparms *dmap, int chan)
-{
-  sound_close_dma (chan);
-
-  if (dmap->flags & DMA_BUSY)
-    dmap->dma_mode = DMODE_NONE;
-  dmap->flags &= ~DMA_BUSY;
-
-  disable_dma (dmap->dma);
-}
-
-
-static unsigned int
-default_set_bits (int dev, unsigned int bits)
-{
-  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits);
-}
-
-static int
-default_set_speed (int dev, int speed)
-{
-  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
-}
-
-static short
-default_set_channels (int dev, short channels)
-{
-  int             c = channels;
-
-  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
-}
-
-static void
-check_driver (struct audio_driver *d)
-{
-  if (d->set_speed == NULL)
-    d->set_speed = default_set_speed;
-  if (d->set_bits == NULL)
-    d->set_bits = default_set_bits;
-  if (d->set_channels == NULL)
-    d->set_channels = default_set_channels;
-}
-
-int
-DMAbuf_open (int dev, int mode)
-{
-  int             retval;
-  struct dma_buffparms *dmap_in = NULL;
-  struct dma_buffparms *dmap_out = NULL;
-
-  if (dev >= num_audiodevs)
-    {
-      return -ENXIO;
-    }
-
-  if (!audio_devs[dev])
-    {
-      return -ENXIO;
-    }
-
-  if (!(audio_devs[dev]->flags & DMA_DUPLEX))
-    {
-      audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
-      audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma;
-    }
-
-  check_driver (audio_devs[dev]->d);
-
-  if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0)
-    return retval;
-
-  dmap_out = audio_devs[dev]->dmap_out;
-  dmap_in = audio_devs[dev]->dmap_in;
-
-  if (dmap_in == dmap_out)
-    audio_devs[dev]->flags &= ~DMA_DUPLEX;
-
-  if (mode & OPEN_WRITE)
-    {
-      if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0)
-	{
-	  audio_devs[dev]->d->close (dev);
-	  return retval;
-	}
-    }
-
-  audio_devs[dev]->enable_bits = mode;
-
-  if (mode == OPEN_READ || (mode != OPEN_WRITE &&
-			    audio_devs[dev]->flags & DMA_DUPLEX))
-    {
-      if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0)
-	{
-	  audio_devs[dev]->d->close (dev);
-
-	  if (mode & OPEN_WRITE)
-	    {
-	      close_dmap (dev, dmap_out, audio_devs[dev]->dmap_out->dma);
-	    }
-
-	  return retval;
-	}
-    }
-
-  audio_devs[dev]->open_mode = mode;
-  audio_devs[dev]->go = 1;
-
-  if (mode & OPEN_READ)
-    in_sleep_flag[dev].opts = WK_NONE;
-
-  if (mode & OPEN_WRITE)
-    out_sleep_flag[dev].opts = WK_NONE;
-
-  audio_devs[dev]->d->set_bits (dev, 8);
-  audio_devs[dev]->d->set_channels (dev, 1);
-  audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED);
-
-  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
-    {
-      memset (audio_devs[dev]->dmap_out->raw_buf,
-	      audio_devs[dev]->dmap_out->neutral_byte,
-	      audio_devs[dev]->dmap_out->bytes_in_use);
-    }
-
-  return 0;
-}
-
-void
-DMAbuf_reset (int dev)
-{
-  if (audio_devs[dev]->open_mode & OPEN_WRITE)
-    dma_reset_output (dev);
-
-  if (audio_devs[dev]->open_mode & OPEN_READ)
-    dma_reset_input (dev);
-}
-
-static void
-dma_reset_output (int dev)
-{
-  unsigned long   flags;
-  int             tmout;
-
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-
-  if (!(dmap->flags & DMA_STARTED))	/* DMA is not active */
-    return;
-
-/*
- * First wait until the current fragment has been played completely
- */
-  save_flags (flags);
-  cli ();
-
-  tmout =
-    (dmap->fragment_size * HZ) / dmap->data_rate;
-
-  tmout += HZ / 5;		/* Some safety distance */
-
-  if (tmout < (HZ / 2))
-    tmout = HZ / 2;
-  if (tmout > 20 * HZ)
-    tmout = 20 * HZ;
-
-  audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
-
-  audio_devs[dev]->dmap_out->underrun_count = 0;
-  if (!signal_pending(current)
-      && audio_devs[dev]->dmap_out->qlen
-      && audio_devs[dev]->dmap_out->underrun_count == 0)
-    {
-
-      {
-	unsigned long   tlimit;
-
-	if (tmout)
-	  current->timeout = tlimit = jiffies + (tmout);
-	else
-	  tlimit = (unsigned long) -1;
-	out_sleep_flag[dev].opts = WK_SLEEP;
-	interruptible_sleep_on (&out_sleeper[dev]);
-	if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
-	  {
-	    if (jiffies >= tlimit)
-	      out_sleep_flag[dev].opts |= WK_TIMEOUT;
-	  }
-	out_sleep_flag[dev].opts &= ~WK_SLEEP;
-      };
-    }
-  audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-
-/*
- * Finally shut the device off
- */
-
-  if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
-      !audio_devs[dev]->d->halt_output)
-    audio_devs[dev]->d->halt_io (dev);
-  else
-    audio_devs[dev]->d->halt_output (dev);
-  audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED;
-  restore_flags (flags);
-
-  clear_dma_ff (dmap->dma);
-  disable_dma (dmap->dma);
-  dmap->byte_counter = 0;
-  reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
-  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
-}
-
-static void
-dma_reset_input (int dev)
-{
-  unsigned long   flags;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-
-  save_flags (flags);
-  cli ();
-  if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
-      !audio_devs[dev]->d->halt_input)
-    audio_devs[dev]->d->halt_io (dev);
-  else
-    audio_devs[dev]->d->halt_input (dev);
-  audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED;
-  restore_flags (flags);
-
-  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
-  dmap->byte_counter = 0;
-  reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
-}
-
-void
-DMAbuf_launch_output (int dev, struct dma_buffparms *dmap)
-{
-  if (!((audio_devs[dev]->enable_bits * audio_devs[dev]->go) & PCM_ENABLE_OUTPUT))
-    return;			/* Don't start DMA yet */
-
-  dmap->dma_mode = DMODE_OUTPUT;
-
-  if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
-    {
-      if (!(dmap->flags & DMA_STARTED))
-	{
-	  reorganize_buffers (dev, dmap, 0);
-
-	  if (audio_devs[dev]->d->prepare_for_output (dev,
-					  dmap->fragment_size, dmap->nbufs))
-	    return;
-
-	  if (!(dmap->flags & DMA_NODMA))
-	    {
-	      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
-			       DMA_MODE_WRITE);
-	    }
-	  dmap->flags |= DMA_STARTED;
-	}
-      if (dmap->counts[dmap->qhead] == 0)
-	dmap->counts[dmap->qhead] = dmap->fragment_size;
-
-      dmap->dma_mode = DMODE_OUTPUT;
-      audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
-					dmap->qhead * dmap->fragment_size,
-					dmap->counts[dmap->qhead], 1);
-      if (audio_devs[dev]->d->trigger)
-	audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-    }
-  dmap->flags |= DMA_ACTIVE;
-}
-
-int
-DMAbuf_sync (int dev)
-{
-  unsigned long   flags;
-  int             tmout, n = 0;
-
-  if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
-    return 0;
-
-  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
-    {
-
-      struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-      save_flags (flags);
-      cli ();
-
-      tmout =
-	(dmap->fragment_size * HZ) / dmap->data_rate;
-
-      tmout += HZ / 5;		/* Some safety distance */
-
-      if (tmout < (HZ / 2))
-	tmout = HZ / 2;
-      if (tmout > 20 * HZ)
-	tmout = 20 * HZ;
-
-      ;
-      if (dmap->qlen > 0)
-	if (!(dmap->flags & DMA_ACTIVE))
-	  DMAbuf_launch_output (dev, dmap);
-      ;
-
-      audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
-
-      audio_devs[dev]->dmap_out->underrun_count = 0;
-      while (!signal_pending(current)
-	     && n++ <= audio_devs[dev]->dmap_out->nbufs
-	     && audio_devs[dev]->dmap_out->qlen
-	     && audio_devs[dev]->dmap_out->underrun_count == 0)
-	{
-
-	  {
-	    unsigned long   tlimit;
-
-	    if (tmout)
-	      current->timeout = tlimit = jiffies + (tmout);
-	    else
-	      tlimit = (unsigned long) -1;
-	    out_sleep_flag[dev].opts = WK_SLEEP;
-	    interruptible_sleep_on (&out_sleeper[dev]);
-	    if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
-	      {
-		if (jiffies >= tlimit)
-		  out_sleep_flag[dev].opts |= WK_TIMEOUT;
-	      }
-	    out_sleep_flag[dev].opts &= ~WK_SLEEP;
-	  };
-	  if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
-	    {
-	      audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING;
-	      restore_flags (flags);
-	      return audio_devs[dev]->dmap_out->qlen;
-	    }
-	}
-      audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-      restore_flags (flags);
-      /*
-       * Some devices such as GUS have huge amount of on board RAM for the
-       * audio data. We have to wait until the device has finished playing.
-       */
-
-      save_flags (flags);
-      cli ();
-      if (audio_devs[dev]->d->local_qlen)	/* Device has hidden buffers */
-	{
-	  while (!signal_pending(current)
-		 && audio_devs[dev]->d->local_qlen (dev))
-	    {
-
-	      {
-		unsigned long   tlimit;
-
-		if (tmout)
-		  current->timeout = tlimit = jiffies + (tmout);
-		else
-		  tlimit = (unsigned long) -1;
-		out_sleep_flag[dev].opts = WK_SLEEP;
-		interruptible_sleep_on (&out_sleeper[dev]);
-		if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
-		  {
-		    if (jiffies >= tlimit)
-		      out_sleep_flag[dev].opts |= WK_TIMEOUT;
-		  }
-		out_sleep_flag[dev].opts &= ~WK_SLEEP;
-	      };
-	    }
-	}
-      restore_flags (flags);
-    }
-  audio_devs[dev]->dmap_out->dma_mode = DMODE_NONE;
-  return audio_devs[dev]->dmap_out->qlen;
-}
-
-int
-DMAbuf_release (int dev, int mode)
-{
-  unsigned long   flags;
-
-  if (audio_devs[dev]->open_mode & OPEN_WRITE)
-    audio_devs[dev]->dmap_out->closing = 1;
-  if (audio_devs[dev]->open_mode & OPEN_READ)
-    audio_devs[dev]->dmap_in->closing = 1;
-
-  if (audio_devs[dev]->open_mode & OPEN_WRITE)
-    if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
-      if (!signal_pending(current)
-	  && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
-	{
-	  DMAbuf_sync (dev);
-	}
-
-  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
-    {
-      memset (audio_devs[dev]->dmap_out->raw_buf,
-	      audio_devs[dev]->dmap_out->neutral_byte,
-	      audio_devs[dev]->dmap_out->bytes_in_use);
-    }
-
-  save_flags (flags);
-  cli ();
-
-  DMAbuf_reset (dev);
-  audio_devs[dev]->d->close (dev);
-
-  if (audio_devs[dev]->open_mode & OPEN_WRITE)
-    close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
-
-  if (audio_devs[dev]->open_mode == OPEN_READ ||
-      (audio_devs[dev]->open_mode != OPEN_WRITE &&
-       audio_devs[dev]->flags & DMA_DUPLEX))
-    close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
-  audio_devs[dev]->open_mode = 0;
-
-  restore_flags (flags);
-
-  return 0;
-}
-
-int
-DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap)
-{
-  if (!(audio_devs[dev]->open_mode & OPEN_READ))
-    return 0;
-
-  if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
-    return 0;
-
-  if (dmap->dma_mode == DMODE_OUTPUT)	/* Direction change */
-    {
-      DMAbuf_sync (dev);
-      DMAbuf_reset (dev);
-      dmap->dma_mode = DMODE_NONE;
-    }
-
-  if (!dmap->dma_mode)
-    {
-      int             err;
-
-      reorganize_buffers (dev, dmap, 1);
-      if ((err = audio_devs[dev]->d->prepare_for_input (dev,
-				     dmap->fragment_size, dmap->nbufs)) < 0)
-	{
-	  return err;
-	}
-      dmap->dma_mode = DMODE_INPUT;
-    }
-
-  if (!(dmap->flags & DMA_ACTIVE))
-    {
-      if (dmap->needs_reorg)
-	reorganize_buffers (dev, dmap, 0);
-      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
-		       DMA_MODE_READ);
-      audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
-				       dmap->qtail * dmap->fragment_size,
-				       dmap->fragment_size, 0);
-      dmap->flags |= DMA_ACTIVE;
-      if (audio_devs[dev]->d->trigger)
-	audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-    }
-  return 0;
-}
-
-int
-DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
-{
-  unsigned long   flags;
-  int             err = 0, n = 0;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-
-  if (!(audio_devs[dev]->open_mode & OPEN_READ))
-    return -EIO;
-  if (dmap->needs_reorg)
-    reorganize_buffers (dev, dmap, 0);
-
-  save_flags (flags);
-  cli ();
-  if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
-    {
-      printk ("Sound: Can't read from mmapped device (1)\n");
-      restore_flags (flags);
-      return -EINVAL;
-    }
-  else
-    while (dmap->qlen <= 0 && n++ < 10)
-      {
-	int             tmout;
-
-	if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) ||
-	    !audio_devs[dev]->go)
-	  {
-	    restore_flags (flags);
-	    return -EAGAIN;
-	  }
-
-	if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
-	  {
-	    restore_flags (flags);
-	    return err;
-	  }
-
-	/* Wait for the next block */
-
-	if (dontblock)
-	  {
-	    restore_flags (flags);
-	    return -EAGAIN;
-	  }
-
-	if (!audio_devs[dev]->go)
-	  tmout = 0;
-	else
-	  {
-	    tmout =
-	      (dmap->fragment_size * HZ) / dmap->data_rate;
-
-	    tmout += HZ / 5;	/* Some safety distance */
-
-	    if (tmout < (HZ / 2))
-	      tmout = HZ / 2;
-	    if (tmout > 20 * HZ)
-	      tmout = 20 * HZ;
-	  }
-
-
-	{
-	  unsigned long   tlimit;
-
-	  if (tmout)
-	    current->timeout = tlimit = jiffies + (tmout);
-	  else
-	    tlimit = (unsigned long) -1;
-	  in_sleep_flag[dev].opts = WK_SLEEP;
-	  interruptible_sleep_on (&in_sleeper[dev]);
-	  if (!(in_sleep_flag[dev].opts & WK_WAKEUP))
-	    {
-	      if (jiffies >= tlimit)
-		in_sleep_flag[dev].opts |= WK_TIMEOUT;
-	    }
-	  in_sleep_flag[dev].opts &= ~WK_SLEEP;
-	};
-	if ((in_sleep_flag[dev].opts & WK_TIMEOUT))
-	  {
-	    err = -EIO;
-	    printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
-	    dma_reset_input (dev);
-	    ;
-	  }
-	else
-	  err = -EINTR;
-      }
-  restore_flags (flags);
-
-  if (dmap->qlen <= 0)
-    {
-      if (err == 0)
-	err = -EINTR;
-      return err;
-    }
-
-  *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]];
-  *len = dmap->fragment_size - dmap->counts[dmap->qhead];
-
-  return dmap->qhead;
-}
-
-int
-DMAbuf_rmchars (int dev, int buff_no, int c)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-
-  int             p = dmap->counts[dmap->qhead] + c;
-
-  if (dmap->mapping_flags & DMA_MAP_MAPPED)
-    {
-      printk ("Sound: Can't read from mmapped device (2)\n");
-      return -EINVAL;
-    }
-  else if (dmap->qlen <= 0)
-    return -EIO;
-  else if (p >= dmap->fragment_size)
-    {				/* This buffer is completely empty */
-      dmap->counts[dmap->qhead] = 0;
-      dmap->qlen--;
-      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
-    }
-  else
-    dmap->counts[dmap->qhead] = p;
-
-  return 0;
-}
-
-int
-DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap, int direction)
-{
-/*
- * Try to approximate the active byte position of the DMA pointer within the
- * buffer area as well as possible.
- */
-  int             pos;
-  unsigned long   flags;
-
-  save_flags (flags);
-  cli ();
-  if (!(dmap->flags & DMA_ACTIVE))
-    pos = 0;
-  else
-    {
-      int             chan = dmap->dma;
-
-      clear_dma_ff (chan);
-      disable_dma (dmap->dma);
-      pos = get_dma_residue (chan);
-      pos = dmap->bytes_in_use - pos;
-
-      if (!(dmap->mapping_flags & DMA_MAP_MAPPED))
-	if (direction == DMODE_OUTPUT)
-	  {
-	    if (dmap->qhead == 0)
-	      if (pos > dmap->fragment_size)
-		pos = 0;
-	  }
-	else
-	  {
-	    if (dmap->qtail == 0)
-	      if (pos > dmap->fragment_size)
-		pos = 0;
-	  }
-
-      if (pos < 0)
-	pos = 0;
-      if (pos >= dmap->bytes_in_use)
-	pos = 0;
-      enable_dma (dmap->dma);
-    }
-  restore_flags (flags);
-  /* printk( "%04x ",  pos); */
-
-  return pos;
-}
-
-/*
- * DMAbuf_start_devices() is called by the /dev/music driver to start
- * one or more audio devices at desired moment.
- */
-static void
-DMAbuf_start_device (int dev)
-{
-  if (audio_devs[dev]->open_mode != 0)
-    if (!audio_devs[dev]->go)
-      {
-	/* OK to start the device */
-	audio_devs[dev]->go = 1;
-
-	if (audio_devs[dev]->d->trigger)
-	  audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-      }
-}
-
-void
-DMAbuf_start_devices (unsigned int devmask)
-{
-  int             dev;
-
-  for (dev = 0; dev < num_audiodevs; dev++)
-    if (devmask & (1 << dev))
-      DMAbuf_start_device (dev);
-}
-
-int
-DMAbuf_space_in_queue (int dev)
-{
-  int             len, max, tmp;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  int             lim = dmap->nbufs;
-
-
-  if (lim < 2)
-    lim = 2;
-
-  if (dmap->qlen >= lim)	/* No space at all */
-    return 0;
-
-  /*
-   * Verify that there are no more pending buffers than the limit
-   * defined by the process.
-   */
-
-  max = dmap->max_fragments;
-  if (max > lim)
-    max = lim;
-  len = dmap->qlen;
-
-  if (audio_devs[dev]->d->local_qlen)
-    {
-      tmp = audio_devs[dev]->d->local_qlen (dev);
-      if (tmp && len)
-	tmp--;			/*
-				 * This buffer has been counted twice
-				 */
-      len += tmp;
-    }
-  if (dmap->byte_counter % dmap->fragment_size)		/* There is a partial fragment */
-    len = len + 1;
-
-  if (len >= max)
-    return 0;
-  return max - len;
-}
-
-static int
-output_sleep (int dev, int dontblock)
-{
-  int             tmout;
-  int             err = 0;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  if (dontblock)
-    {
-      return -EAGAIN;
-    }
-
-  if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
-    {
-      return -EAGAIN;
-    }
-
-  /*
-   * Wait for free space
-   */
-  if (!audio_devs[dev]->go || dmap->flags & DMA_NOTIMEOUT)
-    tmout = 0;
-  else
-    {
-      tmout =
-	(dmap->fragment_size * HZ) / dmap->data_rate;
-
-      tmout += HZ / 5;		/* Some safety distance */
-
-      if (tmout < (HZ / 2))
-	tmout = HZ / 2;
-      if (tmout > 20 * HZ)
-	tmout = 20 * HZ;
-    }
-
-  if (signal_pending(current))
-    return -EIO;
-
-
-  {
-    unsigned long   tlimit;
-
-    if (tmout)
-      current->timeout = tlimit = jiffies + (tmout);
-    else
-      tlimit = (unsigned long) -1;
-    out_sleep_flag[dev].opts = WK_SLEEP;
-    interruptible_sleep_on (&out_sleeper[dev]);
-    if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
-      {
-	if (jiffies >= tlimit)
-	  out_sleep_flag[dev].opts |= WK_TIMEOUT;
-      }
-    out_sleep_flag[dev].opts &= ~WK_SLEEP;
-  };
-  if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
-    {
-      printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
-      ;
-      dma_reset_output (dev);
-    }
-  else if (signal_pending(current))
-    {
-      err = -EINTR;
-    }
-
-  return err;
-}
-
-static int
-find_output_space (int dev, char **buf, int *size)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-  unsigned long   flags;
-  unsigned long   active_offs;
-  long            len, offs;
-  int             maxfrags;
-  int             occupied_bytes = (dmap->user_counter % dmap->fragment_size);
-
-  *buf = dmap->raw_buf;
-
-  if (!(maxfrags = DMAbuf_space_in_queue (dev)) && !occupied_bytes)
-    {
-      return 0;
-    }
-
-  save_flags (flags);
-  cli ();
-
-#ifdef BE_CONSERVATIVE
-  active_offs = dmap->byte_counter + dmap->qhead * dmap->fragment_size;
-#else
-  active_offs = DMAbuf_get_buffer_pointer (dev, dmap, DMODE_OUTPUT);
-  /* Check for pointer wrapping situation */
-  if (active_offs < 0 || active_offs >= dmap->bytes_in_use)
-    active_offs = 0;
-  active_offs += dmap->byte_counter;
-#endif
-
-  offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
-  if (offs < 0 || offs >= dmap->bytes_in_use)
-    {
-      printk ("OSS: Got unexpected offs %ld. Giving up.\n", offs);
-      printk ("Counter = %ld, bytes=%d\n", dmap->user_counter, dmap->bytes_in_use);
-      return 0;
-    }
-  *buf = dmap->raw_buf + offs;
-
-  len = active_offs + dmap->bytes_in_use - dmap->user_counter;	/* Number of unused bytes in buffer */
-
-  if ((offs + len) > dmap->bytes_in_use)
-    {
-      len = dmap->bytes_in_use - offs;
-    }
-
-  if (len < 0)
-    {
-      restore_flags (flags);
-      return 0;
-    }
-
-  if (len > ((maxfrags * dmap->fragment_size) - occupied_bytes))
-    {
-      len = (maxfrags * dmap->fragment_size) - occupied_bytes;
-    }
-
-  *size = len & ~3;
-
-  restore_flags (flags);
-  return (len > 0);
-}
-
-int
-DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
-{
-  unsigned long   flags;
-  int             err = -EIO;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  if (dmap->needs_reorg)
-    reorganize_buffers (dev, dmap, 0);
-
-  if (dmap->mapping_flags & DMA_MAP_MAPPED)
-    {
-      printk ("Sound: Can't write to mmapped device (3)\n");
-      return -EINVAL;
-    }
-
-  if (dmap->dma_mode == DMODE_INPUT)	/* Direction change */
-    {
-      DMAbuf_reset (dev);
-      dmap->dma_mode = DMODE_NONE;
-    }
-
-  dmap->dma_mode = DMODE_OUTPUT;
-
-  save_flags (flags);
-  cli ();
-
-  while (find_output_space (dev, buf, size) <= 0)
-    {
-      if ((err = output_sleep (dev, dontblock)) < 0)
-	{
-	  restore_flags (flags);
-	  return err;
-	}
-    }
-
-  restore_flags (flags);
-
-  return 0;
-}
-
-int
-DMAbuf_move_wrpointer (int dev, int l)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-  unsigned long   ptr = (dmap->user_counter / dmap->fragment_size)
-  * dmap->fragment_size;
-
-  unsigned long   end_ptr, p;
-  int             post = (dmap->flags & DMA_POST);
-
-  ;
-
-  dmap->flags &= ~DMA_POST;
-
-  dmap->cfrag = -1;
-
-  dmap->user_counter += l;
-  dmap->flags |= DMA_DIRTY;
-
-  if (dmap->user_counter >= dmap->max_byte_counter)
-    {				/* Wrap the byte counters */
-      long            decr = dmap->user_counter;
-
-      dmap->user_counter = (dmap->user_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
-      decr -= dmap->user_counter;
-      dmap->byte_counter -= decr;
-    }
-
-  end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
-
-  p = (dmap->user_counter - 1) % dmap->bytes_in_use;
-  dmap->neutral_byte = dmap->raw_buf[p];
-
-  /* Update the fragment based bookkeeping too */
-  while (ptr < end_ptr)
-    {
-      dmap->counts[dmap->qtail] = dmap->fragment_size;
-      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
-      dmap->qlen++;
-      ptr += dmap->fragment_size;
-    }
-
-  dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
-
-/*
- * Let the low level driver to perform some postprocessing to
- * the written data.
- */
-  if (audio_devs[dev]->d->postprocess_write)
-    audio_devs[dev]->d->postprocess_write (dev);
-
-  if (!(dmap->flags & DMA_ACTIVE))
-    if (dmap->qlen > 1 ||
-	(dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
-      {
-	DMAbuf_launch_output (dev, dmap);
-      }
-
-  ;
-  return 0;
-}
-
-int
-DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
-{
-  int             chan;
-  struct dma_buffparms *dmap;
-
-  if (dma_mode == DMA_MODE_WRITE)
-    {
-      chan = audio_devs[dev]->dmap_out->dma;
-      dmap = audio_devs[dev]->dmap_out;
-    }
-  else
-    {
-      chan = audio_devs[dev]->dmap_in->dma;
-      dmap = audio_devs[dev]->dmap_in;
-    }
-
-  if (dmap->raw_buf == NULL)
-    {
-      printk ("sound: DMA buffer(1) == NULL\n");
-      printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
-      return 0;
-    }
-
-  if (chan < 0)
-    return 0;
-
-  sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0);
-
-  return count;
-}
-
-static int
-local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
-{
-  int             chan;
-  struct dma_buffparms *dmap;
-
-  if (dma_mode == DMA_MODE_WRITE)
-    {
-      chan = audio_devs[dev]->dmap_out->dma;
-      dmap = audio_devs[dev]->dmap_out;
-    }
-  else
-    {
-      chan = audio_devs[dev]->dmap_in->dma;
-      dmap = audio_devs[dev]->dmap_in;
-    }
-
-  if (dmap->raw_buf == NULL)
-    {
-      printk ("sound: DMA buffer(2) == NULL\n");
-      printk ("Device %d, chn=%s\n", dev, (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
-      return 0;
-    }
-
-  if (dmap->flags & DMA_NODMA)
-    {
-      return 1;
-    }
-
-  if (chan < 0)
-    return 0;
-
-  sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
-  dmap->flags |= DMA_STARTED;
-
-  return count;
-}
-
-static void
-finish_output_interrupt (int dev, struct dma_buffparms *dmap)
-{
-  unsigned long   flags;
-
-  if (dmap->audio_callback != NULL)
-    dmap->audio_callback (dev, dmap->callback_parm);
-
-  save_flags (flags);
-  cli ();
-  if ((out_sleep_flag[dev].opts & WK_SLEEP))
-    {
-      {
-	out_sleep_flag[dev].opts = WK_WAKEUP;
-	wake_up (&out_sleeper[dev]);
-      };
-    }
-  restore_flags (flags);
-}
-
-static void
-do_outputintr (int dev, int dummy)
-{
-  unsigned long   flags;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-  int             this_fragment;
-
-#ifdef OS_DMA_INTR
-  if (audio_devs[dev]->dmap_out->dma >= 0)
-    sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
-#endif
-
-  if (dmap->raw_buf == NULL)
-    {
-      printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev);
-      return;
-    }
-
-  if (dmap->mapping_flags & DMA_MAP_MAPPED)	/* Virtual memory mapped access */
-    {
-      /* mmapped access */
-      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
-      if (dmap->qhead == 0)	/* Wrapped */
-	{
-	  dmap->byte_counter += dmap->bytes_in_use;
-	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
-	    {
-	      long            decr = dmap->byte_counter;
-
-	      dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
-	      decr -= dmap->byte_counter;
-	      dmap->user_counter -= decr;
-	    }
-	}
-
-      dmap->qlen++;		/* Yes increment it (don't decrement) */
-      if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-	dmap->flags &= ~DMA_ACTIVE;
-      dmap->counts[dmap->qhead] = dmap->fragment_size;
-
-      DMAbuf_launch_output (dev, dmap);
-      finish_output_interrupt (dev, dmap);
-      return;
-    }
-
-  save_flags (flags);
-  cli ();
-
-  dmap->qlen--;
-  this_fragment = dmap->qhead;
-  dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
-
-  if (dmap->qhead == 0)		/* Wrapped */
-    {
-      dmap->byte_counter += dmap->bytes_in_use;
-      if (dmap->byte_counter >= dmap->max_byte_counter)		/* Overflow */
-	{
-	  long            decr = dmap->byte_counter;
-
-	  dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
-	  decr -= dmap->byte_counter;
-	  dmap->user_counter -= decr;
-	}
-    }
-
-  if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-    dmap->flags &= ~DMA_ACTIVE;
-
-  while (dmap->qlen < 0)
-    {
-      dmap->underrun_count++;
-
-      dmap->qlen++;
-      if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
-	{
-	  dmap->flags &= ~DMA_DIRTY;
-	  memset (audio_devs[dev]->dmap_out->raw_buf,
-		  audio_devs[dev]->dmap_out->neutral_byte,
-		  audio_devs[dev]->dmap_out->buffsize);
-	}
-      dmap->user_counter += dmap->fragment_size;
-      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
-    }
-
-  if (dmap->qlen > 0)
-    DMAbuf_launch_output (dev, dmap);
-
-  restore_flags (flags);
-  finish_output_interrupt (dev, dmap);
-}
-
-void
-DMAbuf_outputintr (int dev, int notify_only)
-{
-  unsigned long   flags;
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  save_flags (flags);
-  cli ();
-
-  if (!(dmap->flags & DMA_NODMA))
-    {
-      int             chan = dmap->dma, pos, n;
-
-      clear_dma_ff (chan);
-      disable_dma (dmap->dma);
-      pos = dmap->bytes_in_use - get_dma_residue (chan);
-      enable_dma (dmap->dma);
-
-      pos = pos / dmap->fragment_size;	/* Actual qhead */
-      if (pos < 0 || pos >= dmap->nbufs)
-	pos = 0;
-
-      n = 0;
-      while (dmap->qhead != pos && n++ < dmap->nbufs)
-	{
-	  do_outputintr (dev, notify_only);
-	}
-    }
-  else
-    do_outputintr (dev, notify_only);
-  restore_flags (flags);
-}
-
-static void
-do_inputintr (int dev)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-  unsigned long   flags;
-
-#ifdef OS_DMA_INTR
-  if (audio_devs[dev]->dmap_in->dma >= 0)
-    sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
-#endif
-
-  if (dmap->raw_buf == NULL)
-    {
-      printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
-      return;
-    }
-
-  if (dmap->mapping_flags & DMA_MAP_MAPPED)
-    {
-      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
-      if (dmap->qtail == 0)	/* Wrapped */
-	{
-	  dmap->byte_counter += dmap->bytes_in_use;
-	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
-	    {
-	      long            decr = dmap->byte_counter;
-
-	      dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
-	      decr -= dmap->byte_counter;
-	      dmap->user_counter -= decr;
-	    }
-	}
-      dmap->qlen++;
-
-      if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-	{
-	  if (dmap->needs_reorg)
-	    reorganize_buffers (dev, dmap, 0);
-	  local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
-			   DMA_MODE_READ);
-	  audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
-					   dmap->qtail * dmap->fragment_size,
-					   dmap->fragment_size, 1);
-	  if (audio_devs[dev]->d->trigger)
-	    audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-	}
-
-      dmap->flags |= DMA_ACTIVE;
-    }
-  else if (dmap->qlen >= (dmap->nbufs - 1))
-    {
-      printk ("Sound: Recording overrun\n");
-      dmap->underrun_count++;
-
-      /* Just throw away the oldest fragment but keep the engine running */
-      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
-      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
-    }
-  else if (dmap->qlen >= 0 && dmap->qlen < dmap->nbufs)
-    {
-      dmap->qlen++;
-      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
-      if (dmap->qtail == 0)	/* Wrapped */
-	{
-	  dmap->byte_counter += dmap->bytes_in_use;
-	  if (dmap->byte_counter >= dmap->max_byte_counter)	/* Overflow */
-	    {
-	      long            decr = dmap->byte_counter;
-
-	      dmap->byte_counter = (dmap->byte_counter % dmap->bytes_in_use) + dmap->bytes_in_use;
-	      decr -= dmap->byte_counter;
-	      dmap->user_counter -= decr;
-	    }
-	}
-    }
-
-  if (!(audio_devs[dev]->flags & DMA_AUTOMODE) || dmap->flags & DMA_NODMA)
-    {
-      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
-		       DMA_MODE_READ);
-      audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
-				       dmap->qtail * dmap->fragment_size,
-				       dmap->fragment_size, 1);
-      if (audio_devs[dev]->d->trigger)
-	audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-    }
-
-  dmap->flags |= DMA_ACTIVE;
-
-  save_flags (flags);
-  cli ();
-  if (dmap->qlen > 0)
-    if ((in_sleep_flag[dev].opts & WK_SLEEP))
-      {
-	{
-	  in_sleep_flag[dev].opts = WK_WAKEUP;
-	  wake_up (&in_sleeper[dev]);
-	};
-      }
-  restore_flags (flags);
-}
-
-void
-DMAbuf_inputintr (int dev)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
-  unsigned long   flags;
-
-  save_flags (flags);
-  cli ();
-
-  if (!(dmap->flags & DMA_NODMA))
-    {
-      int             chan = dmap->dma, pos, n;
-
-      clear_dma_ff (chan);
-      disable_dma (dmap->dma);
-      pos = dmap->bytes_in_use - get_dma_residue (chan);
-      enable_dma (dmap->dma);
-
-      pos = pos / dmap->fragment_size;	/* Actual qhead */
-      if (pos < 0 || pos >= dmap->nbufs)
-	pos = 0;
-
-      n = 0;
-      while (dmap->qtail != pos && ++n < dmap->nbufs)
-	{
-	  do_inputintr (dev);
-	}
-    }
-  else
-    do_inputintr (dev);
-  restore_flags (flags);
-}
-
-int
-DMAbuf_open_dma (int dev)
-{
-/*
- *    NOTE!  This routine opens only the primary DMA channel (output).
- */
-
-  int             chan = audio_devs[dev]->dmap_out->dma;
-  int             err;
-
-  if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
-    {
-      return -EBUSY;
-    }
-  dma_init_buffers (dev, audio_devs[dev]->dmap_out);
-  out_sleep_flag[dev].opts = WK_NONE;
-  audio_devs[dev]->dmap_out->flags |= DMA_ALLOC_DONE;
-  audio_devs[dev]->dmap_out->fragment_size = audio_devs[dev]->dmap_out->buffsize;
-
-  if (chan >= 0)
-    {
-      unsigned long   flags;
-
-      save_flags (flags);
-      cli ();
-      disable_dma (audio_devs[dev]->dmap_out->dma);
-      clear_dma_ff (chan);
-      restore_flags (flags);
-    }
-
-  return 0;
-}
-
-void
-DMAbuf_close_dma (int dev)
-{
-  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
-}
-
-void
-DMAbuf_init (int dev, int dma1, int dma2)
-{
-  /*
-     * NOTE! This routine could be called several times.
-   */
-
-  if (audio_devs[dev]->dmap_out == NULL)
-    {
-      if (audio_devs[dev]->d == NULL)
-	panic ("OSS: audio_devs[%d]->d == NULL\n", dev);
-
-      if (audio_devs[dev]->parent_dev)
-	{			/* Use DMA map of the parent dev */
-	  int             parent = audio_devs[dev]->parent_dev - 1;
-
-	  audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
-	  audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
-	}
-      else
-	{
-	  audio_devs[dev]->dmap_out =
-	    audio_devs[dev]->dmap_in =
-	    &dmaps[ndmaps++];
-	  audio_devs[dev]->dmap_out->dma = dma1;
-
-	  if (audio_devs[dev]->flags & DMA_DUPLEX)
-	    {
-	      audio_devs[dev]->dmap_in =
-		&dmaps[ndmaps++];
-	      audio_devs[dev]->dmap_in->dma = dma2;
-	    }
-	}
-    }
-}
-
-int
-DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
-{
-  struct dma_buffparms *dmap;
-  unsigned long   flags;
-
-  switch (sel_type)
-    {
-    case SEL_IN:
-      if (!(audio_devs[dev]->open_mode & OPEN_READ))
-	return 0;
-
-      dmap = audio_devs[dev]->dmap_in;
-
-      if (dmap->mapping_flags & DMA_MAP_MAPPED)
-	{
-	  if (dmap->qlen)
-	    return 1;
-
-	  save_flags (flags);
-	  cli ();
-
-	  in_sleep_flag[dev].opts = WK_SLEEP;
-	  poll_wait (&in_sleeper[dev], wait);
-	  restore_flags (flags);
-	  return 0;
-	}
-
-      if (dmap->dma_mode != DMODE_INPUT)
-	{
-	  if (dmap->dma_mode == DMODE_NONE &&
-	      audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
-	      !dmap->qlen &&
-	      audio_devs[dev]->go)
-	    {
-	      unsigned long   flags;
-
-	      save_flags (flags);
-	      cli ();
-	      DMAbuf_activate_recording (dev, dmap);
-	      restore_flags (flags);
-	    }
-	  return 0;
-	}
-
-      if (!dmap->qlen)
-	{
-	  save_flags (flags);
-	  cli ();
-
-	  in_sleep_flag[dev].opts = WK_SLEEP;
-	  poll_wait (&in_sleeper[dev], wait);
-	  restore_flags (flags);
-	  return 0;
-	}
-      return 1;
-      break;
-
-    case SEL_OUT:
-      dmap = audio_devs[dev]->dmap_out;
-
-      if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-	return 0;
-
-      if (dmap->mapping_flags & DMA_MAP_MAPPED)
-	{
-	  if (dmap->qlen)
-	    return 1;
-
-	  save_flags (flags);
-	  cli ();
-
-	  out_sleep_flag[dev].opts = WK_SLEEP;
-	  poll_wait (&out_sleeper[dev], wait);
-	  restore_flags (flags);
-	  return 0;
-	}
-
-      if (dmap->dma_mode == DMODE_INPUT)
-	{
-	  return 0;
-	}
-
-      if (dmap->dma_mode == DMODE_NONE)
-	{
-	  return 1;
-	}
-
-      if (!DMAbuf_space_in_queue (dev))
-	{
-	  save_flags (flags);
-	  cli ();
-
-	  out_sleep_flag[dev].opts = WK_SLEEP;
-	  poll_wait (&out_sleeper[dev], wait);
-	  restore_flags (flags);
-	  return 0;
-	}
-      return 1;
-      break;
-
-    case SEL_EX:
-      return 0;
-    }
-
-  return 0;
-}
-
-
-#endif
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v2.1.89/linux/drivers/sound/gus_card.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/gus_card.c	Thu Mar 12 10:52:09 1998
@@ -30,7 +30,7 @@
 
 void attach_gus_card(struct address_info *hw_config)
 {
-	snd_set_irq_handler(hw_config->irq, gusintr, "Gravis Ultrasound", hw_config->osp);
+	snd_set_irq_handler(hw_config->irq, gusintr, "Gravis Ultrasound", hw_config->osp, hw_config);
 
 	gus_wave_init(hw_config);
 
@@ -101,7 +101,7 @@
 
 	release_region(hw_config->io_base, 16);
 	release_region(hw_config->io_base + 0x100, 12);		/* 0x10c-> is MAX */
-	snd_release_irq(hw_config->irq);
+	snd_release_irq(hw_config->irq, hw_config);
 
 	sound_free_dma(hw_config->dma);
 
@@ -111,14 +111,15 @@
 
 void gusintr(int irq, void *dev_id, struct pt_regs *dummy)
 {
-	unsigned char   src;
-	extern int      gus_timer_enabled;
+	unsigned char src;
+	extern int gus_timer_enabled;
+	struct address_info *hw_config=dev_id;
 
 	sti();
 
 #ifdef CONFIG_GUSMAX
 	if (have_gus_max)
-		adintr(irq, NULL, NULL);
+		adintr(irq, (void *)hw_config->slots[3], NULL);
 #endif
 
 	while (1)
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/hex2hex.c linux/drivers/sound/hex2hex.c
--- v2.1.89/linux/drivers/sound/hex2hex.c	Tue Dec 23 13:52:02 1997
+++ linux/drivers/sound/hex2hex.c	Thu Mar 12 10:52:09 1998
@@ -66,14 +66,27 @@
 
 int main( int argc, const char * argv [] )
 {
-	const char * varline = argv[1];
+	const char * varline;
 	int i,l;
+	int id=0;
 
+	if(argv[1] && strcmp(argv[1], "-i")==0)
+	{
+		argv++;
+		argc--;
+		id=1;
+	}
+	if(argv[1]==NULL)
+	{
+		fprintf(stderr,"hex2hex: [-i] filename\n");
+		exit(1);
+	}
+	varline = argv[1;
 	l = loadhex(stdin, buf);
 
 	printf("/*\n *\t Computer generated file. Do not edit.\n */\n");
         printf("static int %s_len = %d;\n", varline, l);
-	printf("static unsigned char %s[] = {\n", varline);
+	printf("static unsigned char %s[] %s = {\n", varline, id?"__initdata":"");
 
 	for (i=0;i<l;i++)
 	{
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v2.1.89/linux/drivers/sound/maui.c	Tue Mar 10 10:03:33 1998
+++ linux/drivers/sound/maui.c	Thu Mar 12 10:52:09 1998
@@ -9,7 +9,15 @@
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
+ *
+ *	Changes:
+ *		Alan Cox		General clean up, use kernel IRQ 
+ *					system
+ *
+ *	Status:
+ *		Untested
  */
+ 
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -327,7 +335,7 @@
 	maui_base = hw_config->io_base;
 	maui_osp = hw_config->osp;
 
-	if (snd_set_irq_handler(hw_config->irq, mauiintr, "Maui", maui_osp) < 0)
+	if (request_irq(hw_config->irq, mauiintr, 0, "Maui", NULL) < 0)
 		return 0;
 
 	/*
@@ -342,26 +350,26 @@
 			maui_read() == -1 || maui_read() == -1)
 			if (!maui_init(hw_config->irq))
 			{
-				snd_release_irq(hw_config->irq);
+				free_irq(hw_config->irq, NULL);
 				return 0;
 			}
 	}
 	if (!maui_write(0xCF))	/* Report hardware version */
 	{
 		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
-		snd_release_irq(hw_config->irq);
+		free_irq(hw_config->irq, NULL);
 		return 0;
 	}
 	if ((tmp1 = maui_read()) == -1 || (tmp2 = maui_read()) == -1)
 	{
 		printk(KERN_ERR "No WaveFront firmware detected (card uninitialized?)\n");
-		snd_release_irq(hw_config->irq);
+		free_irq(hw_config->irq, NULL);
 		return 0;
 	}
 	if (tmp1 == 0xff || tmp2 == 0xff)
 	{
-		  snd_release_irq(hw_config->irq);
-		  return 0;
+		free_irq(hw_config->irq, NULL);
+		return 0;
 	}
 	if (trace_init)
 		printk(KERN_DEBUG "WaveFront hardware version %d.%d\n", tmp1, tmp2);
@@ -439,7 +447,7 @@
 	if (irq < 0)
 		irq = -irq;
 	if (irq > 0)
-		snd_release_irq(irq);
+		free_irq(irq, NULL);
 }
 
 #ifdef MODULE
@@ -452,8 +460,7 @@
 struct address_info cfg;
 
 /*
- *	Install a CS4232 based card. Need to have ad1848 and mpu401
- *	loaded ready.
+ *	Install a Maui card. Needs mpu401 loaded already.
  */
 
 int init_module(void)
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v2.1.89/linux/drivers/sound/midibuf.c	Tue Mar 10 10:03:33 1998
+++ linux/drivers/sound/midibuf.c	Wed Mar 11 11:39:27 1998
@@ -15,7 +15,6 @@
  */
 #include <linux/config.h>
 #include <linux/stddef.h>
-#include <linux/kerneld.h>
 
 #define MIDIBUF_C
 
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v2.1.89/linux/drivers/sound/mpu401.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/mpu401.c	Thu Mar 12 10:52:09 1998
@@ -9,10 +9,12 @@
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
+ *
+ *
+ * Thomas Sailer	ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox		modularisation, use normal request_irq, use dev_id
  */
-/*
- * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
- */
+
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -32,30 +34,30 @@
 #endif
 
 struct mpu_config
-  {
-	  int             base;	/*
+{
+	int             base;	/*
 				 * I/O base
 				 */
-	  int             irq;
-	  int             opened;	/*
-					 * Open mode
-					 */
-	  int             devno;
-	  int             synthno;
-	  int             uart_mode;
-	  int             initialized;
-	  int             mode;
+	int             irq;
+	int             opened;	/*
+				 * Open mode
+				 */
+	int             devno;
+	int             synthno;
+	int             uart_mode;
+	int             initialized;
+	int             mode;
 #define MODE_MIDI	1
 #define MODE_SYNTH	2
-	  unsigned char   version, revision;
-	  unsigned int    capabilities;
+	unsigned char   version, revision;
+	unsigned int    capabilities;
 #define MPU_CAP_INTLG	0x10000000
 #define MPU_CAP_SYNC	0x00000010
 #define MPU_CAP_FSK	0x00000020
 #define MPU_CAP_CLS	0x00000040
 #define MPU_CAP_SMPTE 	0x00000080
 #define MPU_CAP_2PORT	0x00000001
-	  int             timer_flag;
+	int             timer_flag;
 
 #define MBUF_MAX	10
 #define BUFTEST(dc) if (dc->m_ptr >= MBUF_MAX || dc->m_ptr < 0) \
@@ -75,28 +77,27 @@
 #define	COMDPORT(base)   (base+1)
 #define	STATPORT(base)   (base+1)
 
-static int
-mpu401_status(struct mpu_config *devc)
+static int mpu401_status(struct mpu_config *devc)
 {
 	return inb(STATPORT(devc->base));
 }
+
 #define input_avail(devc)		(!(mpu401_status(devc)&INPUT_AVAIL))
 #define output_ready(devc)		(!(mpu401_status(devc)&OUTPUT_READY))
-static void
-write_command(struct mpu_config *devc, unsigned char cmd)
+
+static void write_command(struct mpu_config *devc, unsigned char cmd)
 {
-	outb((cmd), COMDPORT(devc->base));
+	outb(cmd, COMDPORT(devc->base));
 }
-static int
-read_data(struct mpu_config *devc)
+
+static int read_data(struct mpu_config *devc)
 {
 	return inb(DATAPORT(devc->base));
 }
 
-static void
-write_data(struct mpu_config *devc, unsigned char byte)
+static void write_data(struct mpu_config *devc, unsigned char byte)
 {
-	outb((byte), DATAPORT(devc->base));
+	outb(byte, DATAPORT(devc->base));
 }
 
 #define	OUTPUT_READY	0x40
@@ -107,22 +108,27 @@
 
 static struct mpu_config dev_conf[MAX_MIDI_DEV] =
 {
-	{0}};
+	{0}
+};
 
-static int      n_mpu_devs = 0;
-static volatile int irq2dev[17] =
-{-1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1};
-
-static int      reset_mpu401(struct mpu_config *devc);
-static void     set_uart_mode(int dev, struct mpu_config *devc, int arg);
-
-static int      mpu_timer_init(int midi_dev);
-static void     mpu_timer_interrupt(void);
-static void     timer_ext_event(struct mpu_config *devc, int event, int parm);
+static int n_mpu_devs = 0;
 
-static struct synth_info mpu_synth_info_proto =
-{"MPU-401 MIDI interface", 0, SYNTH_TYPE_MIDI, MIDI_TYPE_MPU401, 0, 128, 0, 128, SYNTH_CAP_INPUT};
+static int reset_mpu401(struct mpu_config *devc);
+static void set_uart_mode(int dev, struct mpu_config *devc, int arg);
+
+static int mpu_timer_init(int midi_dev);
+static void mpu_timer_interrupt(void);
+static void timer_ext_event(struct mpu_config *devc, int event, int parm);
+
+static struct synth_info mpu_synth_info_proto = {
+	"MPU-401 MIDI interface", 
+	0, 
+	SYNTH_TYPE_MIDI, 
+	MIDI_TYPE_MPU401, 
+	0, 128, 
+	0, 128, 
+	SYNTH_CAP_INPUT
+};
 
 static struct synth_info mpu_synth_info[MAX_MIDI_DEV];
 
@@ -158,10 +164,10 @@
 #else
 #define STORE(cmd) \
 { \
-  int len; \
-  unsigned char obuf[8]; \
-  cmd; \
-  seq_input_event(obuf, len); \
+	int len; \
+	unsigned char obuf[8]; \
+	cmd; \
+	seq_input_event(obuf, len); \
 }
 #endif
 
@@ -169,242 +175,242 @@
 #define _seqbufptr 0
 #define _SEQ_ADVBUF(x) len=x
 
-static int
-mpu_input_scanner(struct mpu_config *devc, unsigned char midic)
+static int mpu_input_scanner(struct mpu_config *devc, unsigned char midic)
 {
 
 	switch (devc->m_state)
-	  {
-	  case ST_INIT:
-		  switch (midic)
-		    {
-		    case 0xf8:
-			    /* Timer overflow */
-			    break;
-
-		    case 0xfc:
-			    printk("<all end>");
-			    break;
-
-		    case 0xfd:
-			    if (devc->timer_flag)
-				    mpu_timer_interrupt();
-			    break;
-
-		    case 0xfe:
-			    return MPU_ACK;
-			    break;
-
-		    case 0xf0:
-		    case 0xf1:
-		    case 0xf2:
-		    case 0xf3:
-		    case 0xf4:
-		    case 0xf5:
-		    case 0xf6:
-		    case 0xf7:
-			    printk("<Trk data rq #%d>", midic & 0x0f);
-			    break;
-
-		    case 0xf9:
-			    printk("<conductor rq>");
-			    break;
-
-		    case 0xff:
-			    devc->m_state = ST_SYSMSG;
-			    break;
-
-		    default:
-			    if (midic <= 0xef)
-			      {
-				      /* printk( "mpu time: %d ",  midic); */
-				      devc->m_state = ST_TIMED;
-			    } else
-				    printk("<MPU: Unknown event %02x> ", midic);
-		    }
-		  break;
-
-	  case ST_TIMED:
-		  {
-			  int             msg = ((int) (midic & 0xf0) >> 4);
-
-			  devc->m_state = ST_DATABYTE;
-
-			  if (msg < 8)	/* Data byte */
-			    {
-				    /* printk( "midi msg (running status) "); */
-				    msg = ((int) (devc->last_status & 0xf0) >> 4);
-				    msg -= 8;
-				    devc->m_left = len_tab[msg] - 1;
-
-				    devc->m_ptr = 2;
-				    devc->m_buf[0] = devc->last_status;
-				    devc->m_buf[1] = midic;
-
-				    if (devc->m_left <= 0)
-				      {
-					      devc->m_state = ST_INIT;
-					      do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
-					      devc->m_ptr = 0;
-				      }
-			  } else if (msg == 0xf)	/* MPU MARK */
-			    {
-				    devc->m_state = ST_INIT;
-
-				    switch (midic)
-				      {
-				      case 0xf8:
-					      /* printk( "NOP "); */
-					      break;
-
-				      case 0xf9:
-					      /* printk( "meas end "); */
-					      break;
-
-				      case 0xfc:
-					      /* printk( "data end "); */
-					      break;
-
-				      default:
-					      printk("Unknown MPU mark %02x\n", midic);
-				      }
-			  } else
-			    {
-				    devc->last_status = midic;
-				    /* printk( "midi msg "); */
-				    msg -= 8;
-				    devc->m_left = len_tab[msg];
-
-				    devc->m_ptr = 1;
-				    devc->m_buf[0] = midic;
-
-				    if (devc->m_left <= 0)
-				      {
-					      devc->m_state = ST_INIT;
-					      do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
-					      devc->m_ptr = 0;
-				      }
-			    }
-		  }
-		  break;
-
-	  case ST_SYSMSG:
-		  switch (midic)
-		    {
-		    case 0xf0:
-			    printk("<SYX>");
-			    devc->m_state = ST_SYSEX;
-			    break;
-
-		    case 0xf1:
-			    devc->m_state = ST_MTC;
-			    break;
-
-		    case 0xf2:
-			    devc->m_state = ST_SONGPOS;
-			    devc->m_ptr = 0;
-			    break;
-
-		    case 0xf3:
-			    devc->m_state = ST_SONGSEL;
-			    break;
-
-		    case 0xf6:
-			    /* printk( "tune_request\n"); */
-			    devc->m_state = ST_INIT;
-
-			    /*
-			       *    Real time messages
-			     */
-		    case 0xf8:
-			    /* midi clock */
-			    devc->m_state = ST_INIT;
-			    timer_ext_event(devc, TMR_CLOCK, 0);
-			    break;
-
-		    case 0xfA:
-			    devc->m_state = ST_INIT;
-			    timer_ext_event(devc, TMR_START, 0);
-			    break;
-
-		    case 0xFB:
-			    devc->m_state = ST_INIT;
-			    timer_ext_event(devc, TMR_CONTINUE, 0);
-			    break;
-
-		    case 0xFC:
-			    devc->m_state = ST_INIT;
-			    timer_ext_event(devc, TMR_STOP, 0);
-			    break;
-
-		    case 0xFE:
-			    /* active sensing */
-			    devc->m_state = ST_INIT;
-			    break;
-
-		    case 0xff:
-			    /* printk( "midi hard reset"); */
-			    devc->m_state = ST_INIT;
-			    break;
-
-		    default:
-			    printk("unknown MIDI sysmsg %0x\n", midic);
-			    devc->m_state = ST_INIT;
-		    }
-		  break;
-
-	  case ST_MTC:
-		  devc->m_state = ST_INIT;
-		  printk("MTC frame %x02\n", midic);
-		  break;
-
-	  case ST_SYSEX:
-		  if (midic == 0xf7)
-		    {
-			    printk("<EOX>");
-			    devc->m_state = ST_INIT;
-		  } else
-			  printk("%02x ", midic);
-		  break;
-
-	  case ST_SONGPOS:
-		  BUFTEST(devc);
-		  devc->m_buf[devc->m_ptr++] = midic;
-		  if (devc->m_ptr == 2)
-		    {
-			    devc->m_state = ST_INIT;
-			    devc->m_ptr = 0;
-			    timer_ext_event(devc, TMR_SPP,
-					 ((devc->m_buf[1] & 0x7f) << 7) |
-					    (devc->m_buf[0] & 0x7f));
-		    }
-		  break;
-
-	  case ST_DATABYTE:
-		  BUFTEST(devc);
-		  devc->m_buf[devc->m_ptr++] = midic;
-		  if ((--devc->m_left) <= 0)
-		    {
-			    devc->m_state = ST_INIT;
-			    do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
-			    devc->m_ptr = 0;
-		    }
-		  break;
-
-	  default:
-		  printk("Bad state %d ", devc->m_state);
-		  devc->m_state = ST_INIT;
-	  }
+	{
+		case ST_INIT:
+			switch (midic)
+			{
+				case 0xf8:
+				/* Timer overflow */
+					break;
+
+				case 0xfc:
+					printk("<all end>");
+			 		break;
+
+				case 0xfd:
+					if (devc->timer_flag)
+						mpu_timer_interrupt();
+					break;
+
+				case 0xfe:
+					return MPU_ACK;
+
+				case 0xf0:
+				case 0xf1:
+				case 0xf2:
+				case 0xf3:
+				case 0xf4:
+				case 0xf5:
+				case 0xf6:
+				case 0xf7:
+					printk("<Trk data rq #%d>", midic & 0x0f);
+					break;
+
+				case 0xf9:
+					printk("<conductor rq>");
+					break;
+
+				case 0xff:
+					devc->m_state = ST_SYSMSG;
+					break;
+
+				default:
+					if (midic <= 0xef)
+					{
+						/* printk( "mpu time: %d ",  midic); */
+						devc->m_state = ST_TIMED;
+					}
+					else
+						printk("<MPU: Unknown event %02x> ", midic);
+			}
+			break;
+
+		case ST_TIMED:
+			{
+				int msg = ((int) (midic & 0xf0) >> 4);
+
+				devc->m_state = ST_DATABYTE;
+
+				if (msg < 8)	/* Data byte */
+				{
+					/* printk( "midi msg (running status) "); */
+					msg = ((int) (devc->last_status & 0xf0) >> 4);
+					msg -= 8;
+					devc->m_left = len_tab[msg] - 1;
+
+					devc->m_ptr = 2;
+					devc->m_buf[0] = devc->last_status;
+					devc->m_buf[1] = midic;
+
+					if (devc->m_left <= 0)
+					{
+						devc->m_state = ST_INIT;
+						do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+						devc->m_ptr = 0;
+					}
+				}
+				else if (msg == 0xf)	/* MPU MARK */
+				{
+					devc->m_state = ST_INIT;
+
+					switch (midic)
+					{
+						case 0xf8:
+							/* printk( "NOP "); */
+							break;
+
+						case 0xf9:
+							/* printk( "meas end "); */
+							break;
+
+						case 0xfc:
+							/* printk( "data end "); */
+							break;
+
+						default:
+							printk("Unknown MPU mark %02x\n", midic);
+					}
+				}
+				else
+				{
+					devc->last_status = midic;
+					/* printk( "midi msg "); */
+					msg -= 8;
+					devc->m_left = len_tab[msg];
+
+					devc->m_ptr = 1;
+					devc->m_buf[0] = midic;
+
+					if (devc->m_left <= 0)
+					{
+						devc->m_state = ST_INIT;
+						do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+						devc->m_ptr = 0;
+					}
+				}
+			}
+			break;
+
+		case ST_SYSMSG:
+			switch (midic)
+			{
+				case 0xf0:
+					printk("<SYX>");
+					devc->m_state = ST_SYSEX;
+					break;
+
+				case 0xf1:
+					devc->m_state = ST_MTC;
+					break;
+
+				case 0xf2:
+					devc->m_state = ST_SONGPOS;
+					devc->m_ptr = 0;
+					break;
+
+				case 0xf3:
+					devc->m_state = ST_SONGSEL;
+					break;
+
+				case 0xf6:
+					/* printk( "tune_request\n"); */
+					devc->m_state = ST_INIT;
+
+					/*
+					 *    Real time messages
+					 */
+				case 0xf8:
+					/* midi clock */
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_CLOCK, 0);
+					break;
+
+				case 0xfA:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_START, 0);
+					break;
+
+				case 0xFB:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_CONTINUE, 0);
+					break;
+
+				case 0xFC:
+					devc->m_state = ST_INIT;
+					timer_ext_event(devc, TMR_STOP, 0);
+					break;
+
+				case 0xFE:
+					/* active sensing */
+					devc->m_state = ST_INIT;
+					break;
+
+				case 0xff:
+					/* printk( "midi hard reset"); */
+					devc->m_state = ST_INIT;
+					break;
+
+				default:
+					printk("unknown MIDI sysmsg %0x\n", midic);
+					devc->m_state = ST_INIT;
+			}
+			break;
 
+		case ST_MTC:
+			devc->m_state = ST_INIT;
+			printk("MTC frame %x02\n", midic);
+			break;
+
+		case ST_SYSEX:
+			if (midic == 0xf7)
+			{
+				printk("<EOX>");
+				devc->m_state = ST_INIT;
+			}
+			else
+				printk("%02x ", midic);
+			break;
+
+		case ST_SONGPOS:
+			BUFTEST(devc);
+			devc->m_buf[devc->m_ptr++] = midic;
+			if (devc->m_ptr == 2)
+			{
+				devc->m_state = ST_INIT;
+				devc->m_ptr = 0;
+				timer_ext_event(devc, TMR_SPP,
+					((devc->m_buf[1] & 0x7f) << 7) |
+					(devc->m_buf[0] & 0x7f));
+			}
+			break;
+
+		case ST_DATABYTE:
+			BUFTEST(devc);
+			devc->m_buf[devc->m_ptr++] = midic;
+			if ((--devc->m_left) <= 0)
+			{
+				devc->m_state = ST_INIT;
+				do_midi_msg(devc->synthno, devc->m_buf, devc->m_ptr);
+				devc->m_ptr = 0;
+			}
+			break;
+
+		default:
+			printk("Bad state %d ", devc->m_state);
+			devc->m_state = ST_INIT;
+	}
 	return 1;
 }
 
-static void
-mpu401_input_loop(struct mpu_config *devc)
+static void mpu401_input_loop(struct mpu_config *devc)
 {
-	unsigned long   flags;
-	int             busy;
-	int             n;
+	unsigned long flags;
+	int busy;
+	int n;
 
 	save_flags(flags);
 	cli();
@@ -418,72 +424,45 @@
 	n = 50;
 
 	while (input_avail(devc) && n-- > 0)
-	  {
-		  unsigned char   c = read_data(devc);
-
-		  if (devc->mode == MODE_SYNTH)
-		    {
-			    mpu_input_scanner(devc, c);
-		  } else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
-			  devc->inputintr(devc->devno, c);
-	  }
+	{
+		unsigned char c = read_data(devc);
 
+		if (devc->mode == MODE_SYNTH)
+		{
+			mpu_input_scanner(devc, c);
+		}
+		else if (devc->opened & OPEN_READ && devc->inputintr != NULL)
+			devc->inputintr(devc->devno, c);
+	}
 	devc->m_busy = 0;
 }
 
-void
-mpuintr(int irq, void *dev_id, struct pt_regs *dummy)
+void mpuintr(int irq, void *dev_id, struct pt_regs *dummy)
 {
 	struct mpu_config *devc;
-	int             dev;
+	int dev = (int) dev_id;
 
 	sti();
-
-/*
- * FreeBSD (and some others) pass unit number to the interrupt handler.
- * In this case we have to scan the table for first handler.
- */
-
-	if (irq < 1 || irq > 15)
-	  {
-		  dev = -1;
-	} else
-		dev = irq2dev[irq];
-
-	if (dev == -1)
-	  {
-		  int             origirq = irq;
-
-		  for (irq = 0; irq <= 16; irq++)
-			  if (irq2dev[irq] != -1)
-				  break;
-		  if (irq > 15)
-		    {
-			    printk("MPU-401: Bogus interrupt #%d?\n", origirq);
-			    return;
-		    }
-		  dev = irq2dev[irq];
-		  devc = &dev_conf[dev];
-	} else
-		devc = &dev_conf[dev];
+	devc = &dev_conf[dev];
 
 	if (input_avail(devc))
+	{
 		if (devc->base != 0 && (devc->opened & OPEN_READ || devc->mode == MODE_SYNTH))
 			mpu401_input_loop(devc);
 		else
-		  {
-			  /* Dummy read (just to acknowledge the interrupt) */
-			  read_data(devc);
-		  }
+		{
+			/* Dummy read (just to acknowledge the interrupt) */
+			read_data(devc);
+		}
+	}
 }
 
-static int
-mpu401_open(int dev, int mode,
+static int mpu401_open(int dev, int mode,
 	    void            (*input) (int dev, unsigned char data),
 	    void            (*output) (int dev)
 )
 {
-	int             err;
+	int err;
 	struct mpu_config *devc;
 
 	if (dev < 0 || dev >= num_midis || midi_devs[dev] == NULL)
@@ -492,36 +471,34 @@
 	devc = &dev_conf[dev];
 
 	if (devc->opened)
-	  {
-		  printk("MPU-401: Midi busy\n");
 		  return -EBUSY;
-	  }
 	/*
-	   *  Verify that the device is really running.
-	   *  Some devices (such as Ensoniq SoundScape don't
-	   *  work before the on board processor (OBP) is initialized
-	   *  by downloading its microcode.
+	 *  Verify that the device is really running.
+	 *  Some devices (such as Ensoniq SoundScape don't
+	 *  work before the on board processor (OBP) is initialized
+	 *  by downloading its microcode.
 	 */
 
 	if (!devc->initialized)
-	  {
-		  if (mpu401_status(devc) == 0xff)	/* Bus float */
-		    {
-			    printk("MPU-401: Device not initialized properly\n");
-			    return -EIO;
-		    }
-		  reset_mpu401(devc);
-	  }
-	irq2dev[devc->irq] = dev;
+	{
+		if (mpu401_status(devc) == 0xff)	/* Bus float */
+		{
+			printk(KERN_ERR "mpu401: Device not initialized properly\n");
+			return -EIO;
+		}
+		reset_mpu401(devc);
+	}
 
 	if (midi_devs[dev]->coproc)
+	{
 		if ((err = midi_devs[dev]->coproc->
 		     open(midi_devs[dev]->coproc->devc, COPR_MIDI)) < 0)
-		  {
-			  printk("MPU-401: Can't access coprocessor device\n");
-
-			  return err;
-		  }
+		{
+			printk("MPU-401: Can't access coprocessor device\n");
+			return err;
+		}
+	}
+	
 	set_uart_mode(dev, devc, 1);
 	devc->mode = MODE_MIDI;
 	devc->synthno = 0;
@@ -534,19 +511,16 @@
 	return 0;
 }
 
-static void
-mpu401_close(int dev)
+static void mpu401_close(int dev)
 {
 	struct mpu_config *devc;
 
 	devc = &dev_conf[dev];
-
 	if (devc->uart_mode)
 		reset_mpu401(devc);	/*
 					 * This disables the UART mode
 					 */
 	devc->mode = 0;
-
 	devc->inputintr = NULL;
 
 	if (midi_devs[dev]->coproc)
@@ -554,11 +528,10 @@
 	devc->opened = 0;
 }
 
-static int
-mpu401_out(int dev, unsigned char midi_byte)
+static int mpu401_out(int dev, unsigned char midi_byte)
 {
-	int             timeout;
-	unsigned long   flags;
+	int timeout;
+	unsigned long flags;
 
 	struct mpu_config *devc;
 
@@ -574,21 +547,20 @@
 	save_flags(flags);
 	cli();
 	if (!output_ready(devc))
-	  {
-		  printk("MPU-401: Send data timeout\n");
-		  restore_flags(flags);
-		  return 0;
-	  }
+	{
+		printk(KERN_WARNING "mpu401: Send data timeout\n");
+		restore_flags(flags);
+		return 0;
+	}
 	write_data(devc, midi_byte);
 	restore_flags(flags);
 	return 1;
 }
 
-static int
-mpu401_command(int dev, mpu_command_rec * cmd)
+static int mpu401_command(int dev, mpu_command_rec * cmd)
 {
-	int             i, timeout, ok;
-	int             ret = 0;
+	int i, timeout, ok;
+	int ret = 0;
 	unsigned long   flags;
 	struct mpu_config *devc;
 
@@ -597,10 +569,10 @@
 	if (devc->uart_mode)	/*
 				 * Not possible in UART mode
 				 */
-	  {
-		  printk("MPU-401 commands not possible in the UART mode\n");
-		  return -EINVAL;
-	  }
+	{
+		printk(KERN_WARNING "mpu401: commands not possible in the UART mode\n");
+		return -EINVAL;
+	}
 	/*
 	 * Test for input since pending input seems to block the output.
 	 */
@@ -613,83 +585,87 @@
 	 */
 
 	timeout = 50000;
-      retry:
+retry:
 	if (timeout-- <= 0)
-	  {
-		  printk("MPU-401: Command (0x%x) timeout\n", (int) cmd->cmd);
-		  return -EIO;
-	  }
+	{
+		printk(KERN_WARNING "mpu401: Command (0x%x) timeout\n", (int) cmd->cmd);
+		return -EIO;
+	}
 	save_flags(flags);
 	cli();
 
 	if (!output_ready(devc))
-	  {
+	{
 		  restore_flags(flags);
 		  goto retry;
-	  }
+	}
 	write_command(devc, cmd->cmd);
 
 	ok = 0;
 	for (timeout = 50000; timeout > 0 && !ok; timeout--)
+	{
 		if (input_avail(devc))
-		  {
-			  if (devc->opened && devc->mode == MODE_SYNTH)
-			    {
-				    if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK)
-					    ok = 1;
-			  } else
-			    {	/* Device is not currently open. Use simpler method */
-				    if (read_data(devc) == MPU_ACK)
-					    ok = 1;
-			    }
-		  }
+		{
+			if (devc->opened && devc->mode == MODE_SYNTH)
+			{
+				if (mpu_input_scanner(devc, read_data(devc)) == MPU_ACK)
+					ok = 1;
+			}
+			else
+			{
+				/* Device is not currently open. Use simpler method */
+				if (read_data(devc) == MPU_ACK)
+					ok = 1;
+			}
+		}
+	}
 	if (!ok)
-	  {
-		  restore_flags(flags);
-		  /*       printk( "MPU: No ACK to command (0x%x)\n",  (int) cmd->cmd); */
-		  return -EIO;
-	  }
+	{
+		restore_flags(flags);
+		return -EIO;
+	}
 	if (cmd->nr_args)
+	{
 		for (i = 0; i < cmd->nr_args; i++)
-		  {
-			  for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--);
+		{
+			for (timeout = 3000; timeout > 0 && !output_ready(devc); timeout--);
 
-			  if (!mpu401_out(dev, cmd->data[i]))
-			    {
-				    restore_flags(flags);
-				    printk("MPU: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
-				    return -EIO;
-			    }
-		  }
+			if (!mpu401_out(dev, cmd->data[i]))
+			{
+				restore_flags(flags);
+				printk(KERN_WARNING "mpu401: Command (0x%x), parm send failed.\n", (int) cmd->cmd);
+				return -EIO;
+			}
+		}
+	}
 	ret = 0;
 	cmd->data[0] = 0;
 
 	if (cmd->nr_returns)
+	{
 		for (i = 0; i < cmd->nr_returns; i++)
-		  {
-			  ok = 0;
-			  for (timeout = 5000; timeout > 0 && !ok; timeout--)
-				  if (input_avail(devc))
-				    {
-					    cmd->data[i] = read_data(devc);
-					    ok = 1;
-				    }
-			  if (!ok)
-			    {
-				    restore_flags(flags);
-				    /* printk( "MPU: No response(%d) to command (0x%x)\n",  i,  (int) cmd->cmd);  */
-				    return -EIO;
-			    }
-		  }
+		{
+			ok = 0;
+			for (timeout = 5000; timeout > 0 && !ok; timeout--)
+				if (input_avail(devc))
+				{
+					cmd->data[i] = read_data(devc);
+					ok = 1;
+				}
+			if (!ok)
+			{
+				restore_flags(flags);
+				return -EIO;
+			}
+		}
+	}
 	restore_flags(flags);
-
 	return ret;
 }
 
-static int
-mpu_cmd(int dev, int cmd, int data)
+static int mpu_cmd(int dev, int cmd, int data)
 {
-	int             ret;
+	int ret;
 
 	static mpu_command_rec rec;
 
@@ -699,14 +675,11 @@
 	rec.data[0] = data & 0xff;
 
 	if ((ret = mpu401_command(dev, &rec)) < 0)
-	  {
-		  return ret;
-	  }
+		return ret;
 	return (unsigned char) rec.data[0];
 }
 
-static int
-mpu401_prefix_cmd(int dev, unsigned char status)
+static int mpu401_prefix_cmd(int dev, unsigned char status)
 {
 	struct mpu_config *devc = &dev_conf[dev];
 
@@ -714,37 +687,29 @@
 		return 1;
 
 	if (status < 0xf0)
-	  {
-		  if (mpu_cmd(dev, 0xD0, 0) < 0)
-		    {
-			    return 0;
-		    }
-		  return 1;
-	  }
+	{
+		if (mpu_cmd(dev, 0xD0, 0) < 0)
+			return 0;
+		return 1;
+	}
 	switch (status)
-	  {
-	  case 0xF0:
-		  if (mpu_cmd(dev, 0xDF, 0) < 0)
-		    {
-			    return 0;
-		    }
-		  return 1;
-		  break;
-
-	  default:
-		  return 0;
-	  }
+	{
+		case 0xF0:
+			if (mpu_cmd(dev, 0xDF, 0) < 0)
+				return 0;
+			return 1;
 
+		default:
+			return 0;
+	}
 }
 
-static int
-mpu401_start_read(int dev)
+static int mpu401_start_read(int dev)
 {
 	return 0;
 }
 
-static int
-mpu401_end_read(int dev)
+static int mpu401_end_read(int dev)
 {
 	return 0;
 }
@@ -756,49 +721,47 @@
 	int val, ret;
 
 	devc = &dev_conf[dev];
-	switch (cmd) {
-	case SNDCTL_MIDI_MPUMODE:
-		if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */
-			printk("MPU-401: Intelligent mode not supported by the HW\n");
-			return -EINVAL;
-		}
-		if (__get_user(val, (int *)arg))
-			return -EFAULT;
-		set_uart_mode(dev, devc, !val);
-		return 0;
-
-	case SNDCTL_MIDI_MPUCMD:
-		if (__copy_from_user(&rec, arg, sizeof(rec)))
-			return -EFAULT;
-		if ((ret = mpu401_command(dev, &rec)) < 0)
-			return ret;
-		if (__copy_to_user(arg, &rec, sizeof(rec)))
-			return -EFAULT;
-		return 0;
+	switch (cmd) 
+	{
+		case SNDCTL_MIDI_MPUMODE:
+			if (!(devc->capabilities & MPU_CAP_INTLG)) { /* No intelligent mode */
+				printk(KERN_WARNING "mpu401: Intelligent mode not supported by the HW\n");
+				return -EINVAL;
+			}
+			if (get_user(val, (int *)arg))
+				return -EFAULT;
+			set_uart_mode(dev, devc, !val);
+			return 0;
+
+		case SNDCTL_MIDI_MPUCMD:
+			if (copy_from_user(&rec, arg, sizeof(rec)))
+				return -EFAULT;
+			if ((ret = mpu401_command(dev, &rec)) < 0)
+				return ret;
+			if (copy_to_user(arg, &rec, sizeof(rec)))
+				return -EFAULT;
+			return 0;
 
-	default:
-		return -EINVAL;
+		default:
+			return -EINVAL;
 	}
 }
 
-static void
-mpu401_kick(int dev)
+static void mpu401_kick(int dev)
 {
 }
 
-static int
-mpu401_buffer_status(int dev)
+static int mpu401_buffer_status(int dev)
 {
 	return 0;		/*
 				 * No data in buffers
 				 */
 }
 
-static int
-mpu_synth_ioctl(int dev,
+static int mpu_synth_ioctl(int dev,
 		unsigned int cmd, caddr_t arg)
 {
-	int             midi_dev;
+	int midi_dev;
 	struct mpu_config *devc;
 
 	midi_dev = synth_devs[dev]->midi_dev;
@@ -809,88 +772,77 @@
 	devc = &dev_conf[midi_dev];
 
 	switch (cmd)
-	  {
-
-	  case SNDCTL_SYNTH_INFO:
-		  memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info));
+	{
 
-		  return 0;
-		  break;
+		case SNDCTL_SYNTH_INFO:
+			memcpy((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof(struct synth_info));
+			return 0;
 
-	  case SNDCTL_SYNTH_MEMAVL:
-		  return 0x7fffffff;
-		  break;
+		case SNDCTL_SYNTH_MEMAVL:
+			return 0x7fffffff;
 
-	  default:
-		  return -EINVAL;
-	  }
+		default:
+			return -EINVAL;
+	}
 }
 
-static int
-mpu_synth_open(int dev, int mode)
+static int mpu_synth_open(int dev, int mode)
 {
-	int             midi_dev, err;
+	int midi_dev, err;
 	struct mpu_config *devc;
 
 	midi_dev = synth_devs[dev]->midi_dev;
 
 	if (midi_dev < 0 || midi_dev > num_midis || midi_devs[midi_dev] == NULL)
-	  {
-		  return -ENXIO;
-	  }
+		return -ENXIO;
+
 	devc = &dev_conf[midi_dev];
 
 	/*
-	   *  Verify that the device is really running.
-	   *  Some devices (such as Ensoniq SoundScape don't
-	   *  work before the on board processor (OBP) is initialized
-	   *  by downloading its microcode.
+	 *  Verify that the device is really running.
+	 *  Some devices (such as Ensoniq SoundScape don't
+	 *  work before the on board processor (OBP) is initialized
+	 *  by downloading its microcode.
 	 */
 
 	if (!devc->initialized)
-	  {
-		  if (mpu401_status(devc) == 0xff)	/* Bus float */
-		    {
-			    printk("MPU-401: Device not initialized properly\n");
-			    return -EIO;
-		    }
-		  reset_mpu401(devc);
-	  }
+	{
+		if (mpu401_status(devc) == 0xff)	/* Bus float */
+		{
+			printk(KERN_ERR "mpu401: Device not initialized properly\n");
+			return -EIO;
+		}
+		reset_mpu401(devc);
+	}
 	if (devc->opened)
-	  {
-		  printk("MPU-401: Midi busy\n");
-		  return -EBUSY;
-	  }
+		return -EBUSY;
 	devc->mode = MODE_SYNTH;
 	devc->synthno = dev;
 
 	devc->inputintr = NULL;
-	irq2dev[devc->irq] = midi_dev;
 
 	if (midi_devs[midi_dev]->coproc)
 		if ((err = midi_devs[midi_dev]->coproc->
 		 open(midi_devs[midi_dev]->coproc->devc, COPR_MIDI)) < 0)
-		  {
-			  printk("MPU-401: Can't access coprocessor device\n");
-
-			  return err;
-		  }
+		{
+			printk(KERN_WARNING "mpu401: Can't access coprocessor device\n");
+			return err;
+		}
 	devc->opened = mode;
 	reset_mpu401(devc);
 
 	if (mode & OPEN_READ)
-	  {
-		  mpu_cmd(midi_dev, 0x8B, 0);	/* Enable data in stop mode */
-		  mpu_cmd(midi_dev, 0x34, 0);	/* Return timing bytes in stop mode */
-		  mpu_cmd(midi_dev, 0x87, 0);	/* Enable pitch & controller */
-	  }
+	{
+		mpu_cmd(midi_dev, 0x8B, 0);	/* Enable data in stop mode */
+		mpu_cmd(midi_dev, 0x34, 0);	/* Return timing bytes in stop mode */
+		mpu_cmd(midi_dev, 0x87, 0);	/* Enable pitch & controller */
+	}
 	return 0;
 }
 
-static void
-mpu_synth_close(int dev)
-{
-	int             midi_dev;
+static void mpu_synth_close(int dev)
+{ 
+	int midi_dev;
 	struct mpu_config *devc;
 
 	midi_dev = synth_devs[dev]->midi_dev;
@@ -958,55 +910,52 @@
 
 static struct midi_operations mpu401_midi_operations[MAX_MIDI_DEV];
 
-static void
-mpu401_chk_version(int n, struct mpu_config *devc)
+static void mpu401_chk_version(int n, struct mpu_config *devc)
 {
-	int             tmp;
-	unsigned long   flags;
+	int tmp;
+	unsigned long flags;
 
 	devc->version = devc->revision = 0;
 
 	save_flags(flags);
 	cli();
 	if ((tmp = mpu_cmd(n, 0xAC, 0)) < 0)
-	  {
-		  restore_flags(flags);
-		  return;
-	  }
+	{
+		restore_flags(flags);
+		return;
+	}
 	if ((tmp & 0xf0) > 0x20)	/* Why it's larger than 2.x ??? */
-	  {
-		  restore_flags(flags);
-		  return;
-	  }
+	{
+		restore_flags(flags);
+		return;
+	}
 	devc->version = tmp;
 
 	if ((tmp = mpu_cmd(n, 0xAD, 0)) < 0)
-	  {
-		  devc->version = 0;
-		  restore_flags(flags);
-		  return;
-	  }
+	{
+		devc->version = 0;
+		restore_flags(flags);
+		return;
+	}
 	devc->revision = tmp;
-
 	restore_flags(flags);
 }
 
-void
-attach_mpu401(struct address_info *hw_config)
+void attach_mpu401(struct address_info *hw_config)
 {
-	unsigned long   flags;
-	char            revision_char;
+	unsigned long flags;
+	char revision_char;
 
-	int             m;
+	int m;
 	struct mpu_config *devc;
 
 	hw_config->slots[1] = -1;
 	m = sound_alloc_mididev();
 	if (m == -1)
-	  {
-		  printk(KERN_WARNING "MPU-401: Too many midi devices detected\n");
-		  return;
-	  }
+	{
+		printk(KERN_WARNING "MPU-401: Too many midi devices detected\n");
+		return;
+	}
 	devc = &dev_conf[m];
 	devc->base = hw_config->io_base;
 	devc->osp = hw_config->osp;
@@ -1024,35 +973,36 @@
 	devc->irq = hw_config->irq;
 
 	if (devc->irq < 0)
-	  {
-		  devc->irq *= -1;
-		  devc->shared_irq = 1;
-	  }
-	irq2dev[devc->irq] = m;
+	{
+		devc->irq *= -1;
+		devc->shared_irq = 1;
+	}
 
 	if (!hw_config->always_detect)
-	  {
-		  /* Verify the hardware again */
-		  if (!reset_mpu401(devc))
-		    {
-			    printk(KERN_WARNING "mpu401: Device didn't respond\n");
-			    sound_unload_mididev(m);
-			    return;
-		    }
-		  if (!devc->shared_irq)
-			  if (snd_set_irq_handler(devc->irq, mpuintr, "mpu401", devc->osp) < 0)
-			    {
-				    printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq);
-				    sound_unload_mididev(m);
-				    return;
-			    }
-		  save_flags(flags);
-		  cli();
-		  mpu401_chk_version(m, devc);
-		  if (devc->version == 0)
-			  mpu401_chk_version(m, devc);
-		  restore_flags(flags);
-	  }
+	{
+		/* Verify the hardware again */
+		if (!reset_mpu401(devc))
+		{
+			printk(KERN_WARNING "mpu401: Device didn't respond\n");
+			sound_unload_mididev(m);
+			return;
+		}
+		if (!devc->shared_irq)
+		{
+			if (request_irq(devc->irq, mpuintr, 0, "mpu401", (void *)m) < 0)
+			{
+				printk(KERN_WARNING "mpu401: Failed to allocate IRQ%d\n", devc->irq);
+				sound_unload_mididev(m);
+				return;
+			}
+		}
+		save_flags(flags);
+		cli();
+		mpu401_chk_version(m, devc);
+		if (devc->version == 0)
+			mpu401_chk_version(m, devc);
+		restore_flags(flags);
+	}
 	request_region(hw_config->io_base, 2, "mpu401");
 
 	if (devc->version != 0)
@@ -1061,36 +1011,32 @@
 				devc->capabilities |= MPU_CAP_INTLG;	/* Supports intelligent mode */
 
 
-	mpu401_synth_operations[m] = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct synth_operations)));
-	sound_mem_sizes[sound_nblocks] = sizeof(struct synth_operations);
-
-	if (sound_nblocks < 1024)
-		sound_nblocks++;;
+	mpu401_synth_operations[m] = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
 
 	if (mpu401_synth_operations[m] == NULL)
-	  {
-		  sound_unload_mididev(m);
-		  printk(KERN_ERR "mpu401: Can't allocate memory\n");
-		  return;
-	  }
+	{
+		sound_unload_mididev(m);
+		printk(KERN_ERR "mpu401: Can't allocate memory\n");
+		return;
+	}
 	if (!(devc->capabilities & MPU_CAP_INTLG))	/* No intelligent mode */
-	  {
-		  memcpy((char *) mpu401_synth_operations[m],
-			 (char *) &std_midi_synth,
+	{
+		memcpy((char *) mpu401_synth_operations[m],
+			(char *) &std_midi_synth,
 			 sizeof(struct synth_operations));
-	} else
-	  {
-		  memcpy((char *) mpu401_synth_operations[m],
-			 (char *) &mpu401_synth_proto,
+	}
+	else
+	{
+		memcpy((char *) mpu401_synth_operations[m],
+			(char *) &mpu401_synth_proto,
 			 sizeof(struct synth_operations));
-	  }
+	}
 
 	memcpy((char *) &mpu401_midi_operations[m],
 	       (char *) &mpu401_midi_proto,
 	       sizeof(struct midi_operations));
 
-	mpu401_midi_operations[m].converter =
-	    mpu401_synth_operations[m];
+	mpu401_midi_operations[m].converter = mpu401_synth_operations[m];
 
 	memcpy((char *) &mpu_synth_info[m],
 	       (char *) &mpu_synth_info_proto,
@@ -1099,37 +1045,36 @@
 	n_mpu_devs++;
 
 	if (devc->version == 0x20 && devc->revision >= 0x07)	/* MusicQuest interface */
-	  {
-		  int             ports = (devc->revision & 0x08) ? 32 : 16;
+	{
+		int ports = (devc->revision & 0x08) ? 32 : 16;
 
-		  devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
-		      MPU_CAP_CLS | MPU_CAP_2PORT;
+		devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_SMPTE |
+				MPU_CAP_CLS | MPU_CAP_2PORT;
 
-		  revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
-		  sprintf(mpu_synth_info[m].name,
-			  "MQX-%d%c MIDI Interface #%d",
-			  ports,
-			  revision_char,
-			  n_mpu_devs);
-	} else
-	  {
-
-		  revision_char = devc->revision ? devc->revision + '@' : ' ';
-		  if ((int) devc->revision > ('Z' - '@'))
-			  revision_char = '+';
-
-		  devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
-
-		  if (hw_config->name)
-			  sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name);
-		  else
-			  sprintf(mpu_synth_info[m].name,
-				  "MPU-401 %d.%d%c Midi interface #%d",
-				  (int) (devc->version & 0xf0) >> 4,
-				  devc->version & 0x0f,
-				  revision_char,
-				  n_mpu_devs);
-	  }
+		revision_char = (devc->revision == 0x7f) ? 'M' : ' ';
+		sprintf(mpu_synth_info[m].name, "MQX-%d%c MIDI Interface #%d",
+				ports,
+				revision_char,
+				n_mpu_devs);
+	}
+	else
+	{
+		revision_char = devc->revision ? devc->revision + '@' : ' ';
+		if ((int) devc->revision > ('Z' - '@'))
+			revision_char = '+';
+
+		devc->capabilities |= MPU_CAP_SYNC | MPU_CAP_FSK;
+
+		if (hw_config->name)
+			sprintf(mpu_synth_info[m].name, "%s (MPU401)", hw_config->name);
+		else
+			sprintf(mpu_synth_info[m].name,
+				"MPU-401 %d.%d%c Midi interface #%d",
+				(int) (devc->version & 0xf0) >> 4,
+				devc->version & 0x0f,
+				revision_char,
+				n_mpu_devs);
+	}
 
 	strcpy(mpu401_midi_operations[m].info.name,
 	       mpu_synth_info[m].name);
@@ -1137,24 +1082,21 @@
 	conf_printf(mpu_synth_info[m].name, hw_config);
 
 	mpu401_synth_operations[m]->midi_dev = devc->devno = m;
-	mpu401_synth_operations[devc->devno]->info =
-	    &mpu_synth_info[devc->devno];
+	mpu401_synth_operations[devc->devno]->info = &mpu_synth_info[devc->devno];
 
 	if (devc->capabilities & MPU_CAP_INTLG)		/* Intelligent mode */
 		hw_config->slots[2] = mpu_timer_init(m);
 
-	irq2dev[devc->irq] = m;
 	midi_devs[m] = &mpu401_midi_operations[devc->devno];
 	hw_config->slots[1] = m;
 	sequencer_init();
 }
 
-static int
-reset_mpu401(struct mpu_config *devc)
+static int reset_mpu401(struct mpu_config *devc)
 {
-	unsigned long   flags;
-	int             ok, timeout, n;
-	int             timeout_limit;
+	unsigned long flags;
+	int ok, timeout, n;
+	int timeout_limit;
 
 	/*
 	 * Send the RESET command. Try again if no success at the first time.
@@ -1167,30 +1109,30 @@
 	devc->initialized = 1;
 
 	for (n = 0; n < 2 && !ok; n++)
-	  {
-		  for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
+	{
+		for (timeout = timeout_limit; timeout > 0 && !ok; timeout--)
 			  ok = output_ready(devc);
 
-		  write_command(devc, MPU_RESET);	/*
+		write_command(devc, MPU_RESET);	/*
 							   * Send MPU-401 RESET Command
 							 */
 
-		  /*
-		   * Wait at least 25 msec. This method is not accurate so let's make the
-		   * loop bit longer. Cannot sleep since this is called during boot.
-		   */
-
-		  for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
-		    {
-			    save_flags(flags);
-			    cli();
-			    if (input_avail(devc))
-				    if (read_data(devc) == MPU_ACK)
-					    ok = 1;
-			    restore_flags(flags);
-		    }
+		/*
+		 * Wait at least 25 msec. This method is not accurate so let's make the
+		 * loop bit longer. Cannot sleep since this is called during boot.
+		 */
+
+		for (timeout = timeout_limit * 2; timeout > 0 && !ok; timeout--)
+		{
+			save_flags(flags);
+			cli();
+			if (input_avail(devc))
+				if (read_data(devc) == MPU_ACK)
+					ok = 1;
+			restore_flags(flags);
+		}
 
-	  }
+	}
 
 	devc->m_state = ST_INIT;
 	devc->m_ptr = 0;
@@ -1201,43 +1143,37 @@
 	return ok;
 }
 
-static void
-set_uart_mode(int dev, struct mpu_config *devc, int arg)
+static void set_uart_mode(int dev, struct mpu_config *devc, int arg)
 {
 	if (!arg && (devc->capabilities & MPU_CAP_INTLG))
-	  {
-		  return;
-	  }
+		return;
 	if ((devc->uart_mode == 0) == (arg == 0))
-	  {
-		  return;	/* Already set */
-	  }
+		return;		/* Already set */
 	reset_mpu401(devc);	/* This exits the uart mode */
 
 	if (arg)
-	  {
-		  if (mpu_cmd(dev, UART_MODE_ON, 0) < 0)
-		    {
-			    printk("MPU%d: Can't enter UART mode\n", devc->devno);
-			    devc->uart_mode = 0;
-			    return;
-		    }
-	  }
+	{
+		if (mpu_cmd(dev, UART_MODE_ON, 0) < 0)
+		{
+			printk(KERN_ERR "mpu401: Can't enter UART mode\n");
+			devc->uart_mode = 0;
+			return;
+		}
+	}
 	devc->uart_mode = arg;
 
 }
 
-int
-probe_mpu401(struct address_info *hw_config)
+int probe_mpu401(struct address_info *hw_config)
 {
-	int             ok = 0;
+	int ok = 0;
 	struct mpu_config tmp_devc;
 
 	if (check_region(hw_config->io_base, 2))
-	  {
-		  printk("\n\nmpu401.c: I/O port %x already in use\n\n", hw_config->io_base);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "mpu401: I/O port %x already in use\n\n", hw_config->io_base);
+		return 0;
+	}
 	tmp_devc.base = hw_config->io_base;
 	tmp_devc.irq = hw_config->irq;
 	tmp_devc.initialized = 0;
@@ -1248,27 +1184,32 @@
 		return 1;
 
 	if (inb(hw_config->io_base + 1) == 0xff)
-	  {
-		  DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));
-		  return 0;	/* Just bus float? */
-	  }
+	{
+		DDB(printk("MPU401: Port %x looks dead.\n", hw_config->io_base));
+		return 0;	/* Just bus float? */
+	}
 	ok = reset_mpu401(&tmp_devc);
 
 	if (!ok)
-	  {
-		  DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));
-	  }
+	{
+		DDB(printk("MPU401: Reset failed on port %x\n", hw_config->io_base));
+	}
 	return ok;
 }
 
-void
-unload_mpu401(struct address_info *hw_config)
+void unload_mpu401(struct address_info *hw_config)
 {
+	void *p;
+	int n=hw_config->slots[1];
+	
 	release_region(hw_config->io_base, 2);
 	if (hw_config->always_detect == 0 && hw_config->irq > 0)
-		snd_release_irq(hw_config->irq);
-	sound_unload_mididev(hw_config->slots[1]);
+		free_irq(hw_config->irq, (void *)n);
+	p=mpu401_synth_operations[n];
+	sound_unload_mididev(n);
 	sound_unload_timerdev(hw_config->slots[2]);
+	if(p)
+		kfree(p);
 }
 
 /*****************************************************
@@ -1285,23 +1226,20 @@
 static unsigned long prev_event_time;
 static int      metronome_mode;
 
-static unsigned long
-clocks2ticks(unsigned long clocks)
+static unsigned long clocks2ticks(unsigned long clocks)
 {
 	/*
-	   * The MPU-401 supports just a limited set of possible timebase values.
-	   * Since the applications require more choices, the driver has to
-	   * program the HW to do its best and to convert between the HW and
-	   * actual timebases.
+	 * The MPU-401 supports just a limited set of possible timebase values.
+	 * Since the applications require more choices, the driver has to
+	 * program the HW to do its best and to convert between the HW and
+	 * actual timebases.
 	 */
-
 	return ((clocks * curr_timebase) + (hw_timebase / 2)) / hw_timebase;
 }
 
-static void
-set_timebase(int midi_dev, int val)
+static void set_timebase(int midi_dev, int val)
 {
-	int             hw_val;
+	int hw_val;
 
 	if (val < 48)
 		val = 48;
@@ -1314,19 +1252,18 @@
 		hw_val = max_timebase;
 
 	if (mpu_cmd(midi_dev, 0xC0 | (hw_val & 0x0f), 0) < 0)
-	  {
-		  printk("MPU: Can't set HW timebase to %d\n", hw_val * 24);
-		  return;
-	  }
+	{
+		printk(KERN_WARNING "mpu401: Can't set HW timebase to %d\n", hw_val * 24);
+		return;
+	}
 	hw_timebase = hw_val * 24;
 	curr_timebase = val;
 
 }
 
-static void
-tmr_reset(void)
+static void tmr_reset(void)
 {
-	unsigned long   flags;
+	unsigned long flags;
 
 	save_flags(flags);
 	cli();
@@ -1336,8 +1273,7 @@
 	restore_flags(flags);
 }
 
-static void
-set_timer_mode(int midi_dev)
+static void set_timer_mode(int midi_dev)
 {
 	if (timer_mode & TMR_MODE_CLS)
 		mpu_cmd(midi_dev, 0x3c, 0);	/* Use CLS sync */
@@ -1345,31 +1281,31 @@
 		mpu_cmd(midi_dev, 0x3d, 0);	/* Use SMPTE sync */
 
 	if (timer_mode & TMR_INTERNAL)
-	  {
+	{
 		  mpu_cmd(midi_dev, 0x80, 0);	/* Use MIDI sync */
-	} else
-	  {
-		  if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
-		    {
-			    mpu_cmd(midi_dev, 0x82, 0);		/* Use MIDI sync */
-			    mpu_cmd(midi_dev, 0x91, 0);		/* Enable ext MIDI ctrl */
-		  } else if (timer_mode & TMR_MODE_FSK)
-			  mpu_cmd(midi_dev, 0x81, 0);	/* Use FSK sync */
-	  }
+	}
+	else
+	{
+		if (timer_mode & (TMR_MODE_MIDI | TMR_MODE_CLS))
+		{
+			mpu_cmd(midi_dev, 0x82, 0);		/* Use MIDI sync */
+			mpu_cmd(midi_dev, 0x91, 0);		/* Enable ext MIDI ctrl */
+		}
+		else if (timer_mode & TMR_MODE_FSK)
+			mpu_cmd(midi_dev, 0x81, 0);	/* Use FSK sync */
+	}
 }
 
-static void
-stop_metronome(int midi_dev)
+static void stop_metronome(int midi_dev)
 {
 	mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */
 }
 
-static void
-setup_metronome(int midi_dev)
+static void setup_metronome(int midi_dev)
 {
-	int             numerator, denominator;
-	int             clks_per_click, num_32nds_per_beat;
-	int             beats_per_measure;
+	int numerator, denominator;
+	int clks_per_click, num_32nds_per_beat;
+	int beats_per_measure;
 
 	numerator = ((unsigned) metronome_mode >> 24) & 0xff;
 	denominator = ((unsigned) metronome_mode >> 16) & 0xff;
@@ -1380,15 +1316,14 @@
 	if (!metronome_mode)
 		mpu_cmd(midi_dev, 0x84, 0);	/* Disable metronome */
 	else
-	  {
-		  mpu_cmd(midi_dev, 0xE4, clks_per_click);
-		  mpu_cmd(midi_dev, 0xE6, beats_per_measure);
-		  mpu_cmd(midi_dev, 0x83, 0);	/* Enable metronome without accents */
-	  }
+	{
+		mpu_cmd(midi_dev, 0xE4, clks_per_click);
+		mpu_cmd(midi_dev, 0xE6, beats_per_measure);
+		mpu_cmd(midi_dev, 0x83, 0);	/* Enable metronome without accents */
+	}
 }
 
-static int
-mpu_start_timer(int midi_dev)
+static int mpu_start_timer(int midi_dev)
 {
 	tmr_reset();
 	set_timer_mode(midi_dev);
@@ -1397,25 +1332,24 @@
 		return TIMER_NOT_ARMED;		/* Already running */
 
 	if (timer_mode & TMR_INTERNAL)
-	  {
-		  mpu_cmd(midi_dev, 0x02, 0);	/* Send MIDI start */
-		  tmr_running = 1;
-		  return TIMER_NOT_ARMED;
-	} else
-	  {
-		  mpu_cmd(midi_dev, 0x35, 0);	/* Enable mode messages to PC */
-		  mpu_cmd(midi_dev, 0x38, 0);	/* Enable sys common messages to PC */
-		  mpu_cmd(midi_dev, 0x39, 0);	/* Enable real time messages to PC */
-		  mpu_cmd(midi_dev, 0x97, 0);	/* Enable system exclusive messages to PC */
-	  }
-
+	{
+		mpu_cmd(midi_dev, 0x02, 0);	/* Send MIDI start */
+		tmr_running = 1;
+		return TIMER_NOT_ARMED;
+	}
+	else
+	{
+		mpu_cmd(midi_dev, 0x35, 0);	/* Enable mode messages to PC */
+		mpu_cmd(midi_dev, 0x38, 0);	/* Enable sys common messages to PC */
+		mpu_cmd(midi_dev, 0x39, 0);	/* Enable real time messages to PC */
+		mpu_cmd(midi_dev, 0x97, 0);	/* Enable system exclusive messages to PC */
+	}
 	return TIMER_ARMED;
 }
 
-static int
-mpu_timer_open(int dev, int mode)
+static int mpu_timer_open(int dev, int mode)
 {
-	int             midi_dev = sound_timer_devs[dev]->devlink;
+	int midi_dev = sound_timer_devs[dev]->devlink;
 
 	if (timer_open)
 		return -EBUSY;
@@ -1435,10 +1369,9 @@
 	return 0;
 }
 
-static void
-mpu_timer_close(int dev)
+static void mpu_timer_close(int dev)
 {
-	int             midi_dev = sound_timer_devs[dev]->devlink;
+	int midi_dev = sound_timer_devs[dev]->devlink;
 
 	timer_open = tmr_running = 0;
 	mpu_cmd(midi_dev, 0x15, 0);	/* Stop all */
@@ -1447,86 +1380,80 @@
 	stop_metronome(midi_dev);
 }
 
-static int
-mpu_timer_event(int dev, unsigned char *event)
+static int mpu_timer_event(int dev, unsigned char *event)
 {
-	unsigned char   command = event[1];
-	unsigned long   parm = *(unsigned int *) &event[4];
-	int             midi_dev = sound_timer_devs[dev]->devlink;
+	unsigned char command = event[1];
+	unsigned long parm = *(unsigned int *) &event[4];
+	int midi_dev = sound_timer_devs[dev]->devlink;
 
 	switch (command)
-	  {
-	  case TMR_WAIT_REL:
-		  parm += prev_event_time;
-	  case TMR_WAIT_ABS:
-		  if (parm > 0)
-		    {
-			    long            time;
-
-			    if (parm <= curr_ticks)	/* It's the time */
-				    return TIMER_NOT_ARMED;
-
-			    time = parm;
-			    next_event_time = prev_event_time = time;
-
-			    return TIMER_ARMED;
-		    }
-		  break;
-
-	  case TMR_START:
-		  if (tmr_running)
-			  break;
-		  return mpu_start_timer(midi_dev);
-		  break;
-
-	  case TMR_STOP:
-		  mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
-		  stop_metronome(midi_dev);
-		  tmr_running = 0;
-		  break;
-
-	  case TMR_CONTINUE:
-		  if (tmr_running)
-			  break;
-		  mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
-		  setup_metronome(midi_dev);
-		  tmr_running = 1;
-		  break;
-
-	  case TMR_TEMPO:
-		  if (parm)
-		    {
-			    if (parm < 8)
-				    parm = 8;
-			    if (parm > 250)
-				    parm = 250;
-
-			    if (mpu_cmd(midi_dev, 0xE0, parm) < 0)
-				    printk("MPU: Can't set tempo to %d\n", (int) parm);
-			    curr_tempo = parm;
-		    }
-		  break;
-
-	  case TMR_ECHO:
-		  seq_copy_to_input(event, 8);
-		  break;
-
-	  case TMR_TIMESIG:
-		  if (metronome_mode)	/* Metronome enabled */
-		    {
-			    metronome_mode = parm;
-			    setup_metronome(midi_dev);
-		    }
-		  break;
+	{
+		case TMR_WAIT_REL:
+			parm += prev_event_time;
+		case TMR_WAIT_ABS:
+			if (parm > 0)
+			{
+				long time;
+
+				if (parm <= curr_ticks)	/* It's the time */
+					return TIMER_NOT_ARMED;
+				time = parm;
+				next_event_time = prev_event_time = time;
 
-	  default:;
-	  }
+				return TIMER_ARMED;
+			}
+			break;
+
+		case TMR_START:
+			if (tmr_running)
+				break;
+			return mpu_start_timer(midi_dev);
+
+		case TMR_STOP:
+			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
+			stop_metronome(midi_dev);
+			tmr_running = 0;
+			break;
 
+		case TMR_CONTINUE:
+			if (tmr_running)
+				break;
+			mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
+			setup_metronome(midi_dev);
+			tmr_running = 1;
+			break;
+
+		case TMR_TEMPO:
+			if (parm)
+			{
+				if (parm < 8)
+					parm = 8;
+			 	if (parm > 250)
+					parm = 250;
+				if (mpu_cmd(midi_dev, 0xE0, parm) < 0)
+					printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) parm);
+				curr_tempo = parm;
+			}
+			break;
+
+		case TMR_ECHO:
+			seq_copy_to_input(event, 8);
+			break;
+
+		case TMR_TIMESIG:
+			if (metronome_mode)	/* Metronome enabled */
+			{
+				metronome_mode = parm;
+				setup_metronome(midi_dev);
+			}
+			break;
+
+		default:
+	}
 	return TIMER_NOT_ARMED;
 }
 
-static unsigned long
-mpu_timer_get_time(int dev)
+static unsigned long mpu_timer_get_time(int dev)
 {
 	if (!timer_open)
 		return 0;
@@ -1534,128 +1461,115 @@
 	return curr_ticks;
 }
 
-static int
-mpu_timer_ioctl(int dev,
-		unsigned int command, caddr_t arg)
+static int mpu_timer_ioctl(int dev, unsigned int command, caddr_t arg)
 {
-	int             midi_dev = sound_timer_devs[dev]->devlink;
+	int midi_dev = sound_timer_devs[dev]->devlink;
 
 	switch (command)
-	  {
-	  case SNDCTL_TMR_SOURCE:
-		  {
-			  int             parm;
-
-			  parm = *(int *) arg;
-			  parm &= timer_caps;
-
-			  if (parm != 0)
-			    {
-				    timer_mode = parm;
-
-				    if (timer_mode & TMR_MODE_CLS)
-					    mpu_cmd(midi_dev, 0x3c, 0);		/* Use CLS sync */
-				    else if (timer_mode & TMR_MODE_SMPTE)
-					    mpu_cmd(midi_dev, 0x3d, 0);		/* Use SMPTE sync */
-			    }
-			  return (*(int *) arg = timer_mode);
-		  }
-		  break;
-
-	  case SNDCTL_TMR_START:
-		  mpu_start_timer(midi_dev);
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_STOP:
-		  tmr_running = 0;
-		  mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
-		  stop_metronome(midi_dev);
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_CONTINUE:
-		  if (tmr_running)
-			  return 0;
-		  tmr_running = 1;
-		  mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
-		  return 0;
-		  break;
-
-	  case SNDCTL_TMR_TIMEBASE:
-		  {
-			  int             val;
-
-			  val = *(int *) arg;
-			  if (val)
-				  set_timebase(midi_dev, val);
-
-			  return (*(int *) arg = curr_timebase);
-		  }
-		  break;
-
-	  case SNDCTL_TMR_TEMPO:
-		  {
-			  int             val;
-			  int             ret;
-
-			  val = *(int *) arg;
-
-			  if (val)
-			    {
-				    if (val < 8)
-					    val = 8;
-				    if (val > 250)
-					    val = 250;
-				    if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)
-				      {
-					      printk("MPU: Can't set tempo to %d\n", (int) val);
-					      return ret;
-				      }
-				    curr_tempo = val;
-			    }
-			  return (*(int *) arg = curr_tempo);
-		  }
-		  break;
-
-	  case SNDCTL_SEQ_CTRLRATE:
-		  {
-			  int             val;
-
-			  val = *(int *) arg;
-			  if (val != 0)		/* Can't change */
-				  return -EINVAL;
-
-			  return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
-		  }
-		  break;
-
-	  case SNDCTL_SEQ_GETTIME:
-		  return (*(int *) arg = curr_ticks);
-		  break;
-
-	  case SNDCTL_TMR_METRONOME:
-		  metronome_mode = *(int *) arg;
-		  setup_metronome(midi_dev);
-		  return 0;
-		  break;
+	{
+		case SNDCTL_TMR_SOURCE:
+			{
+				int parm;
+	
+				parm = *(int *) arg;
+				parm &= timer_caps;
+
+				if (parm != 0)
+				{
+					timer_mode = parm;
+	
+					if (timer_mode & TMR_MODE_CLS)
+						mpu_cmd(midi_dev, 0x3c, 0);		/* Use CLS sync */
+					else if (timer_mode & TMR_MODE_SMPTE)
+						mpu_cmd(midi_dev, 0x3d, 0);		/* Use SMPTE sync */
+				}
+				return (*(int *) arg = timer_mode);
+			}
+			break;
+
+		case SNDCTL_TMR_START:
+			mpu_start_timer(midi_dev);
+			return 0;
+
+		case SNDCTL_TMR_STOP:
+			tmr_running = 0;
+			mpu_cmd(midi_dev, 0x01, 0);	/* Send MIDI stop */
+			stop_metronome(midi_dev);
+			return 0;
+
+		case SNDCTL_TMR_CONTINUE:
+			if (tmr_running)
+				return 0;
+			tmr_running = 1;
+			mpu_cmd(midi_dev, 0x03, 0);	/* Send MIDI continue */
+			return 0;
+
+		case SNDCTL_TMR_TIMEBASE:
+			{
+				int val;
+
+				val = *(int *) arg;
+				if (val)
+					set_timebase(midi_dev, val);
+				return (*(int *) arg = curr_timebase);
+			}
+			break;
+
+		case SNDCTL_TMR_TEMPO:
+			{
+				int val;
+				int ret;
 
-	  default:;
-	  }
+				val = *(int *) arg;
 
+				if (val)
+				{
+					if (val < 8)
+						val = 8;
+					if (val > 250)
+						val = 250;
+					if ((ret = mpu_cmd(midi_dev, 0xE0, val)) < 0)
+					{
+						printk(KERN_WARNING "mpu401: Can't set tempo to %d\n", (int) val);
+						return ret;
+					}
+					curr_tempo = val;
+				}
+				return (*(int *) arg = curr_tempo);
+			}
+			break;
+
+		case SNDCTL_SEQ_CTRLRATE:
+			{
+				int val;
+
+				val = *(int *) arg;
+				if (val != 0)		/* Can't change */
+					return -EINVAL;
+				return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
+			}
+			break;
+
+		case SNDCTL_SEQ_GETTIME:
+			return (*(int *) arg = curr_ticks);
+
+		case SNDCTL_TMR_METRONOME:
+			metronome_mode = *(int *) arg;
+			setup_metronome(midi_dev);
+			return 0;
+
+		default:
+	}
 	return -EINVAL;
 }
 
-static void
-mpu_timer_arm(int dev, long time)
+static void mpu_timer_arm(int dev, long time)
 {
 	if (time < 0)
 		time = curr_ticks + 1;
 	else if (time <= curr_ticks)	/* It's the time */
 		return;
-
 	next_event_time = prev_event_time = time;
-
 	return;
 }
 
@@ -1672,10 +1586,8 @@
 	mpu_timer_arm
 };
 
-static void
-mpu_timer_interrupt(void)
+static void mpu_timer_interrupt(void)
 {
-
 	if (!timer_open)
 		return;
 
@@ -1686,10 +1598,10 @@
 	curr_ticks = clocks2ticks(curr_clocks);
 
 	if (curr_ticks >= next_event_time)
-	  {
-		  next_event_time = (unsigned long) -1;
-		  sequencer_timer(0);
-	  }
+	{
+		next_event_time = (unsigned long) -1;
+		sequencer_timer(0);
+	}
 }
 
 static void timer_ext_event(struct mpu_config *devc, int event, int parm)
@@ -1706,8 +1618,9 @@
 			break;
 
 		case TMR_START:
-			 printk("Ext MIDI start\n");
+			printk("Ext MIDI start\n");
 			if (!tmr_running)
+			{
 				if (timer_mode & TMR_EXTERNAL)
 				{
 					tmr_running = 1;
@@ -1715,6 +1628,7 @@
 					next_event_time = 0;
 					STORE(SEQ_START_TIMER());
 				}
+			}
 			break;
 
 		case TMR_STOP:
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v2.1.89/linux/drivers/sound/opl3.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/opl3.c	Thu Mar 12 10:52:09 1998
@@ -2,25 +2,30 @@
  * sound/opl3.c
  *
  * A low level driver for Yamaha YM3812 and OPL-3 -chips
- */
-/*
+ *
+*
  * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
+ *
+ *
+ * Changes
+ *	Thomas Sailer   ioctl code reworked (vmalloc/vfree removed)
+ *	Alan Cox	modularisation, fixed sound_mem allocs.
+ *
+ * Status
+ *	Believed to work. Badly needs rewriting a bit to support multiple
+ *	OPL3 devices.
  */
-/*
- * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
- */
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 
 /*
  * Major improvements to the FM handling 30AUG92 by Rob Hooft,
- */
-/*
  * hooft@chem.ruu.nl
  */
 
@@ -109,32 +114,32 @@
 	struct sbi_instrument ins;
 	
 	switch (cmd) {
-	case SNDCTL_FM_LOAD_INSTR:
-		printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
-		if (__copy_from_user(&ins, arg, sizeof(ins)))
-			return -EFAULT;
-		if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
-			printk("FM Error: Invalid instrument number %d\n", ins.channel);
-			return -EINVAL;
-		}
-		return store_instr(ins.channel, &ins);
-
-	case SNDCTL_SYNTH_INFO:
-		devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
-		if (__copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info)))
-			return -EFAULT;
-		return 0;
-
-	case SNDCTL_SYNTH_MEMAVL:
-		return 0x7fffffff;
+		case SNDCTL_FM_LOAD_INSTR:
+			printk(KERN_WARNING "Warning: Obsolete ioctl(SNDCTL_FM_LOAD_INSTR) used. Fix the program.\n");
+			if (copy_from_user(&ins, arg, sizeof(ins)))
+				return -EFAULT;
+			if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR) {
+				printk(KERN_WARNING "FM Error: Invalid instrument number %d\n", ins.channel);
+				return -EINVAL;
+			}
+			return store_instr(ins.channel, &ins);
 
-	case SNDCTL_FM_4OP_ENABLE:
-		if (devc->model == 2)
-			enter_4op_mode();
-		return 0;
+		case SNDCTL_SYNTH_INFO:
+			devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
+			if (copy_to_user(arg, &devc->fm_info, sizeof(devc->fm_info)))
+				return -EFAULT;
+			return 0;
+
+		case SNDCTL_SYNTH_MEMAVL:
+			return 0x7fffffff;
+
+		case SNDCTL_FM_4OP_ENABLE:
+			if (devc->model == 2)
+				enter_4op_mode();
+			return 0;
 
-	default:
-		return -EINVAL;
+		default:
+			return -EINVAL;
 	}
 }
 
@@ -151,8 +156,8 @@
 	 * Note2! The chip is initialized if detected.
 	 */
 
-	unsigned char   stat1, signature;
-	int             i;
+	unsigned char stat1, signature;
+	int i;
 
 	if (devc != NULL)
 	{
@@ -160,10 +165,7 @@
 		return 0;
 	}
 
-	devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(*devc)));
-	sound_mem_sizes[sound_nblocks] = sizeof(*devc);
-	if (sound_nblocks < 1024)
-		sound_nblocks++;;
+	devc = (struct opl_devinfo *)kmalloc(sizeof(*devc), GFP_KERNEL);
 
 	if (devc == NULL)
 	{
@@ -333,7 +335,7 @@
 
 static void calc_vol(unsigned char *regbyte, int volume, int main_vol)
 {
-	int level =     (~*regbyte & 0x3f);
+	int level = (~*regbyte & 0x3f);
 
 	if (main_vol > 127)
 		main_vol = 127;
@@ -814,7 +816,8 @@
 		return -EINVAL;
 	}
 
-	copy_from_user(&((char *) &ins)[offs], &(addr)[offs], sizeof(ins) - offs);
+	if(copy_from_user(&((char *) &ins)[offs], &(addr)[offs], sizeof(ins) - offs))
+		return -EFAULT;
 
 	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
 	{
@@ -1199,6 +1202,7 @@
 {
 	if (devc)
 	{
+		kfree(devc);
 		devc = NULL;
 		sound_unload_synthdev(me);
 	}
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.1.89/linux/drivers/sound/pas2_card.c	Tue Jan 20 16:44:57 1998
+++ linux/drivers/sound/pas2_card.c	Thu Mar 12 10:52:09 1998
@@ -164,7 +164,7 @@
 		}
 		else
 		{
-			if (snd_set_irq_handler(pas_irq, pasintr, "PAS16", hw_config->osp) < 0)
+			if (request_irq(pas_irq, pasintr, "PAS16", 0, NULL) < 0)
 				ok = 0;
 		}
 	}
@@ -355,7 +355,7 @@
 unload_pas(struct address_info *hw_config)
 {
 	sound_free_dma(hw_config->dma);
-	snd_release_irq(hw_config->irq);
+	free_irq(hw_config->irq, NULL);
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v2.1.89/linux/drivers/sound/pss.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/pss.c	Thu Mar 12 10:52:09 1998
@@ -11,7 +11,8 @@
  * for more info.
  */
 /*
- * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
+ * Thomas Sailer	ioctl code reworked (vmalloc/vfree removed)
+ * Alan Cox		modularisation, clean up.
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -62,12 +63,12 @@
 #endif
 
 typedef struct pss_confdata
-  {
-	  int             base;
-	  int             irq;
-	  int             dma;
-	  int            *osp;
-  }
+{
+	int	base;
+	int	irq;
+	int	dma;
+	int	*osp;
+}
 
 pss_confdata;
 
@@ -77,12 +78,11 @@
 static int      pss_initialized = 0;
 static int      nonstandard_microcode = 0;
 
-static void
-pss_write(int data)
+static void pss_write(int data)
 {
-	int             i, limit;
+	int i, limit;
 
-	limit = jiffies + 10;	/* The timeout is 0.1 seconds */
+	limit = jiffies + HZ/10;	/* The timeout is 0.1 seconds */
 	/*
 	 * Note! the i<5000000 is an emergency exit. The dsp_command() is sometimes
 	 * called while interrupts are disabled. This means that the timer is
@@ -92,21 +92,20 @@
 	 */
 
 	for (i = 0; i < 5000000 && jiffies < limit; i++)
-	  {
-		  if (inw(devc->base + PSS_STATUS) & PSS_WRITE_EMPTY)
-		    {
-			    outw(devc->base + PSS_DATA, data);
-			    return;
-		    }
-	  }
-	printk("PSS: DSP Command (%04x) Timeout.\n", data);
+	{
+		if (inw(devc->base + PSS_STATUS) & PSS_WRITE_EMPTY)
+		{
+			outw(devc->base + PSS_DATA, data);
+			return;
+		}
+	}
+	printk(KERN_ERR "PSS: DSP Command (%04x) Timeout.\n", data);
 }
 
-int
-probe_pss(struct address_info *hw_config)
+int probe_pss(struct address_info *hw_config)
 {
-	unsigned short  id;
-	int             irq, dma;
+	unsigned short id;
+	int irq, dma;
 
 	devc->base = hw_config->io_base;
 	irq = devc->irq = hw_config->irq;
@@ -129,8 +128,7 @@
 	return 1;
 }
 
-static int
-set_irq(pss_confdata * devc, int dev, int irq)
+static int set_irq(pss_confdata * devc, int dev, int irq)
 {
 	static unsigned short irq_bits[16] =
 	{
@@ -148,16 +146,15 @@
 	tmp = inw(REG(dev)) & ~0x38;	/* Load confreg, mask IRQ bits out */
 
 	if ((bits = irq_bits[irq]) == 0 && irq != 0)
-	  {
-		  printk("PSS: Invalid IRQ %d\n", irq);
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "PSS: Invalid IRQ %d\n", irq);
+		return 0;
+	}
 	outw(tmp | bits, REG(dev));
 	return 1;
 }
 
-static int
-set_io_base(pss_confdata * devc, int dev, int base)
+static int set_io_base(pss_confdata * devc, int dev, int base)
 {
 	unsigned short  tmp = inw(REG(dev)) & 0x003f;
 	unsigned short  bits = (base & 0x0ffc) << 4;
@@ -167,8 +164,7 @@
 	return 1;
 }
 
-static int
-set_dma(pss_confdata * devc, int dev, int dma)
+static int set_dma(pss_confdata * devc, int dev, int dma)
 {
 	static unsigned short dma_bits[8] =
 	{
@@ -184,150 +180,139 @@
 	tmp = inw(REG(dev)) & ~0x07;	/* Load confreg, mask DMA bits out */
 
 	if ((bits = dma_bits[dma]) == 0 && dma != 4)
-	  {
-		  printk("PSS: Invalid DMA %d\n", dma);
+	{
+		  printk(KERN_ERR "PSS: Invalid DMA %d\n", dma);
 		  return 0;
-	  }
+	}
 	outw(tmp | bits, REG(dev));
 	return 1;
 }
 
-static int
-pss_reset_dsp(pss_confdata * devc)
+static int pss_reset_dsp(pss_confdata * devc)
 {
-	unsigned long   i, limit = jiffies + 10;
+	unsigned long   i, limit = jiffies + HZ/10;
 
 	outw(0x2000, REG(PSS_CONTROL));
-
-	for (i = 0; i < 32768 && jiffies < limit; i++)
+	for (i = 0; i < 32768 && (limit-jiffies >= 0); i++)
 		inw(REG(PSS_CONTROL));
-
 	outw(0x0000, REG(PSS_CONTROL));
-
 	return 1;
 }
 
-static int
-pss_put_dspword(pss_confdata * devc, unsigned short word)
+static int pss_put_dspword(pss_confdata * devc, unsigned short word)
 {
-	int             i, val;
+	int i, val;
 
 	for (i = 0; i < 327680; i++)
-	  {
-		  val = inw(REG(PSS_STATUS));
-		  if (val & PSS_WRITE_EMPTY)
-		    {
-			    outw(word, REG(PSS_DATA));
-			    return 1;
-		    }
-	  }
+	{
+		val = inw(REG(PSS_STATUS));
+		if (val & PSS_WRITE_EMPTY)
+		{
+			outw(word, REG(PSS_DATA));
+			return 1;
+		}
+	}
 	return 0;
 }
 
-static int
-pss_get_dspword(pss_confdata * devc, unsigned short *word)
+static int pss_get_dspword(pss_confdata * devc, unsigned short *word)
 {
-	int             i, val;
+	int i, val;
 
 	for (i = 0; i < 327680; i++)
-	  {
-		  val = inw(REG(PSS_STATUS));
-		  if (val & PSS_READ_FULL)
-		    {
-			    *word = inw(REG(PSS_DATA));
-			    return 1;
-		    }
-	  }
-
+	{
+		val = inw(REG(PSS_STATUS));
+		if (val & PSS_READ_FULL)
+		{
+			*word = inw(REG(PSS_DATA));
+			return 1;
+		}
+	}
 	return 0;
 }
 
-static int
-pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags)
+static int pss_download_boot(pss_confdata * devc, unsigned char *block, int size, int flags)
 {
-	int             i, limit, val, count;
+	int i, limit, val, count;
 
 	if (flags & CPF_FIRST)
-	  {
+	{
 /*_____ Warn DSP software that a boot is coming */
-		  outw(0x00fe, REG(PSS_DATA));
+		outw(0x00fe, REG(PSS_DATA));
 
-		  limit = jiffies + 10;
-
-		  for (i = 0; i < 32768 && jiffies < limit; i++)
-			  if (inw(REG(PSS_DATA)) == 0x5500)
-				  break;
-
-		  outw(*block++, REG(PSS_DATA));
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && jiffies < limit; i++)
+			if (inw(REG(PSS_DATA)) == 0x5500)
+				break;
 
-		  pss_reset_dsp(devc);
-	  }
+		outw(*block++, REG(PSS_DATA));
+		pss_reset_dsp(devc);
+	}
 	count = 1;
 	while (1)
-	  {
-		  int             j;
+	{
+		int j;
 
-		  for (j = 0; j < 327670; j++)
-		    {
+		for (j = 0; j < 327670; j++)
+		{
 /*_____ Wait for BG to appear */
-			    if (inw(REG(PSS_STATUS)) & PSS_FLAG3)
-				    break;
-		    }
-
-		  if (j == 327670)
-		    {
-			    /* It's ok we timed out when the file was empty */
-			    if (count >= size && flags & CPF_LAST)
-				    break;
-			    else
-			      {
-				      printk("\nPSS: Download timeout problems, byte %d=%d\n", count, size);
-				      return 0;
-			      }
-		    }
+			if (inw(REG(PSS_STATUS)) & PSS_FLAG3)
+				break;
+		}
+
+		if (j == 327670)
+		{
+			/* It's ok we timed out when the file was empty */
+			if (count >= size && flags & CPF_LAST)
+				break;
+			else
+			{
+				printk("\nPSS: Download timeout problems, byte %d=%d\n", count, size);
+				return 0;
+			}
+		}
 /*_____ Send the next byte */
-		  outw(*block++, REG(PSS_DATA));
-		  count++;
-	  }
+		outw(*block++, REG(PSS_DATA));
+		count++;
+	}
 
 	if (flags & CPF_LAST)
-	  {
+	{
 /*_____ Why */
-		  outw(0, REG(PSS_DATA));
+		outw(0, REG(PSS_DATA));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && (limit - jiffies >= 0); i++)
+			val = inw(REG(PSS_STATUS));
+
+		limit = jiffies + HZ/10;
+		for (i = 0; i < 32768 && (limit-jiffies >= 0); i++)
+		{
+			val = inw(REG(PSS_STATUS));
+			if (val & 0x4000)
+				break;
+		}
 
-		  limit = jiffies + 10;
-		  for (i = 0; i < 32768 && jiffies < limit; i++)
-			  val = inw(REG(PSS_STATUS));
-
-		  limit = jiffies + 10;
-		  for (i = 0; i < 32768 && jiffies < limit; i++)
-		    {
-			    val = inw(REG(PSS_STATUS));
-			    if (val & 0x4000)
-				    break;
-		    }
-
-		  /* now read the version */
-		  for (i = 0; i < 32000; i++)
-		    {
-			    val = inw(REG(PSS_STATUS));
-			    if (val & PSS_READ_FULL)
-				    break;
-		    }
-		  if (i == 32000)
-			  return 0;
-
-		  val = inw(REG(PSS_DATA));
-		  /* printk( "<PSS: microcode version %d.%d loaded>",  val/16,  val % 16); */
-	  }
+		/* now read the version */
+		for (i = 0; i < 32000; i++)
+		{
+			val = inw(REG(PSS_STATUS));
+			if (val & PSS_READ_FULL)
+				break;
+		}
+		if (i == 32000)
+			return 0;
+
+		val = inw(REG(PSS_DATA));
+		/* printk( "<PSS: microcode version %d.%d loaded>",  val/16,  val % 16); */
+	}
 	return 1;
 }
 
-void
-attach_pss(struct address_info *hw_config)
+void attach_pss(struct address_info *hw_config)
 {
 	unsigned short  id;
-	char            tmp[100];
+	char tmp[100];
 
 	devc->base = hw_config->io_base;
 	devc->irq = hw_config->irq;
@@ -350,20 +335,20 @@
 
 #if YOU_REALLY_WANT_TO_ALLOCATE_THESE_RESOURCES
 	if (sound_alloc_dma(hw_config->dma, "PSS"))
-	  {
-		  printk("pss.c: Can't allocate DMA channel\n");
-		  return;
-	  }
+	{
+		printk("pss.c: Can't allocate DMA channel.\n");
+		return;
+	}
 	if (!set_irq(devc, CONF_PSS, devc->irq))
-	  {
-		  printk("PSS: IRQ error\n");
-		  return;
-	  }
+	{
+		printk("PSS: IRQ allocation error.\n");
+		return;
+	}
 	if (!set_dma(devc, CONF_PSS, devc->dma))
-	  {
-		  printk("PSS: DRQ error\n");
-		  return;
-	  }
+	{
+		printk(KERN_ERR "PSS: DMA allocation error\n");
+		return;
+	}
 #endif
 
 	pss_initialized = 1;
@@ -371,8 +356,7 @@
 	conf_printf(tmp, hw_config);
 }
 
-static void
-pss_init_speaker(void)
+static void pss_init_speaker(void)
 {
 /* Don't ask what are these commands. I really don't know */
 	pss_write(0x0010);
@@ -387,53 +371,52 @@
 	pss_write(0x0800 | 0x00ce);	/* Stereo switch? */
 }
 
-int
-probe_pss_mpu(struct address_info *hw_config)
+int probe_pss_mpu(struct address_info *hw_config)
 {
-	int             timeout;
+	int timeout;
 
 	if (!pss_initialized)
 		return 0;
 
 	if (check_region(hw_config->io_base, 2))
-	  {
-		  printk("PSS: MPU I/O port conflict\n");
-		  return 0;
-	  }
+	{
+		printk("PSS: MPU I/O port conflict\n");
+		return 0;
+	}
 	if (!set_io_base(devc, CONF_MIDI, hw_config->io_base))
-	  {
-		  printk("PSS: MIDI base error.\n");
+	{
+		  printk("PSS: MIDI base could not be set.\n");
 		  return 0;
-	  }
+	}
 	if (!set_irq(devc, CONF_MIDI, hw_config->irq))
-	  {
-		  printk("PSS: MIDI IRQ error.\n");
+	{
+		  printk("PSS: MIDI IRQ allocation error.\n");
 		  return 0;
-	  }
+	}
 	if (!pss_synthLen)
-	  {
-		  printk("PSS: Can't enable MPU. MIDI synth microcode not available.\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "PSS: Can't enable MPU. MIDI synth microcode not available.\n");
+		return 0;
+	}
 	if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
-	  {
-		  printk("PSS: Unable to load MIDI synth microcode to DSP.\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+		return 0;
+	}
 	pss_init_speaker();
 
-/*
- * Finally wait until the DSP algorithm has initialized itself and
- * deactivates receive interrupt.
- */
+	/*
+	 * Finally wait until the DSP algorithm has initialized itself and
+	 * deactivates receive interrupt.
+	 */
 
 	for (timeout = 900000; timeout > 0; timeout--)
-	  {
-		  if ((inb(hw_config->io_base + 1) & 0x80) == 0)	/* Input data avail */
-			  inb(hw_config->io_base);	/* Discard it */
-		  else
-			  break;	/* No more input */
-	  }
+	{
+		if ((inb(hw_config->io_base + 1) & 0x80) == 0)	/* Input data avail */
+			inb(hw_config->io_base);	/* Discard it */
+		else
+			break;	/* No more input */
+	}
 
 #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
 	return probe_mpu401(hw_config);
@@ -442,62 +425,56 @@
 #endif
 }
 
-static int
-pss_coproc_open(void *dev_info, int sub_device)
+static int pss_coproc_open(void *dev_info, int sub_device)
 {
 	switch (sub_device)
-	  {
-	  case COPR_MIDI:
-
-		  if (pss_synthLen == 0)
-		    {
-			    printk("PSS: MIDI synth microcode not available.\n");
-			    return -EIO;
-		    }
-		  if (nonstandard_microcode)
-			  if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
-			    {
-				    printk("PSS: Unable to load MIDI synth microcode to DSP.\n");
-				    return -EIO;
-			    }
-		  nonstandard_microcode = 0;
-		  break;
+	{
+		case COPR_MIDI:
+			if (pss_synthLen == 0)
+			{
+				printk(KERN_ERR "PSS: MIDI synth microcode not available.\n");
+				return -EIO;
+			}
+			if (nonstandard_microcode)
+				if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
+			{
+				printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+				return -EIO;
+			}
+			nonstandard_microcode = 0;
+			break;
 
-	  default:;
-	  }
+		default:
+	}
 	return 0;
 }
 
-static void
-pss_coproc_close(void *dev_info, int sub_device)
+static void pss_coproc_close(void *dev_info, int sub_device)
 {
 	return;
 }
 
-static void
-pss_coproc_reset(void *dev_info)
+static void pss_coproc_reset(void *dev_info)
 {
 	if (pss_synthLen)
 		if (!pss_download_boot(devc, pss_synth, pss_synthLen, CPF_FIRST | CPF_LAST))
-		  {
-			  printk("PSS: Unable to load MIDI synth microcode to DSP.\n");
-		  }
+		{
+			printk(KERN_ERR "PSS: Unable to load MIDI synth microcode to DSP.\n");
+		}
 	nonstandard_microcode = 0;
 }
 
-static int
-download_boot_block(void *dev_info, copr_buffer * buf)
+static int download_boot_block(void *dev_info, copr_buffer * buf)
 {
 	if (buf->len <= 0 || buf->len > sizeof(buf->data))
 		return -EINVAL;
 
 	if (!pss_download_boot(devc, buf->data, buf->len, buf->flags))
-	  {
-		  printk("PSS: Unable to load microcode block to DSP.\n");
-		  return -EIO;
-	  }
+	{
+		printk(KERN_ERR "PSS: Unable to load microcode block to DSP.\n");
+		return -EIO;
+	}
 	nonstandard_microcode = 1;	/* The MIDI microcode has been overwritten */
-
 	return 0;
 }
 
@@ -512,169 +489,170 @@
 	int i, err;
 	/* printk( "PSS coproc ioctl %x %x %d\n",  cmd,  arg,  local); */
 	
-	switch (cmd) {
-	case SNDCTL_COPR_RESET:
-		pss_coproc_reset(dev_info);
-		return 0;
+	switch (cmd) 
+	{
+		case SNDCTL_COPR_RESET:
+			pss_coproc_reset(dev_info);
+			return 0;
 
-	case SNDCTL_COPR_LOAD:
-		buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
-		if (buf == NULL)
-			return -ENOSPC;
-		if (__copy_from_user(buf, arg, sizeof(copr_buffer))) {
+		case SNDCTL_COPR_LOAD:
+			buf = (copr_buffer *) vmalloc(sizeof(copr_buffer));
+			if (buf == NULL)
+				return -ENOSPC;
+			if (copy_from_user(buf, arg, sizeof(copr_buffer))) {
+				vfree(buf);
+				return -EFAULT;
+			}
+			err = download_boot_block(dev_info, buf);
 			vfree(buf);
-			return -EFAULT;
-		}
-		err = download_boot_block(dev_info, buf);
-		vfree(buf);
-		return err;
+			return err;
 		
-	case SNDCTL_COPR_SENDMSG:
-		mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
-		if (mbuf == NULL)
-			return -ENOSPC;
-		if (__copy_from_user(mbuf, arg, sizeof(copr_msg))) {
-			vfree(mbuf);
-			return -EFAULT;
-		}
-		data = (unsigned short *)(mbuf->data);
-		save_flags(flags);
-		cli();
-		for (i = 0; i < mbuf->len; i++) {
-			if (!pss_put_dspword(devc, *data++)) {
-				restore_flags(flags);
-				mbuf->len = i;	/* feed back number of WORDs sent */
-				err = __copy_to_user(arg, mbuf, sizeof(copr_msg));
+		case SNDCTL_COPR_SENDMSG:
+			mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
+			if (mbuf == NULL)
+				return -ENOSPC;
+			if (copy_from_user(mbuf, arg, sizeof(copr_msg))) {
 				vfree(mbuf);
-				return err ? -EFAULT : -EIO;
+				return -EFAULT;
 			}
-		}
-		restore_flags(flags);
-		vfree(mbuf);
-		return 0;
-
-	case SNDCTL_COPR_RCVMSG:
-		err = 0;
-		mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
-		if (mbuf == NULL)
-			return -ENOSPC;
-		data = (unsigned short *)mbuf->data;
-		save_flags(flags);
-		cli();
-		for (i = 0; i < mbuf->len; i++) {
-			mbuf->len = i;	/* feed back number of WORDs read */
-			if (!pss_get_dspword(devc, data++)) {
-				if (i == 0)
-					err = -EIO;
-				break;
+			data = (unsigned short *)(mbuf->data);
+			save_flags(flags);
+			cli();
+			for (i = 0; i < mbuf->len; i++) {
+				if (!pss_put_dspword(devc, *data++)) {
+					restore_flags(flags);
+					mbuf->len = i;	/* feed back number of WORDs sent */
+					err = copy_to_user(arg, mbuf, sizeof(copr_msg));
+					vfree(mbuf);
+					return err ? -EFAULT : -EIO;
+				}
 			}
-		}
-		restore_flags(flags);
-		if (__copy_to_user(arg, mbuf, sizeof(copr_msg)))
-			err = -EFAULT;
-		vfree(mbuf);
-		return err;
-		
-	case SNDCTL_COPR_RDATA:
-		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
-			return -EFAULT;
-		save_flags(flags);
-		cli();
-		if (!pss_put_dspword(devc, 0x00d0)) {
 			restore_flags(flags);
-			return -EIO;
-		}
-		if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if (!pss_get_dspword(devc, &tmp)) {
+			vfree(mbuf);
+			return 0;
+
+		case SNDCTL_COPR_RCVMSG:
+			err = 0;
+			mbuf = (copr_msg *)vmalloc(sizeof(copr_msg));
+			if (mbuf == NULL)
+				return -ENOSPC;
+			data = (unsigned short *)mbuf->data;
+			save_flags(flags);
+			cli();
+			for (i = 0; i < mbuf->len; i++) {
+				mbuf->len = i;	/* feed back number of WORDs read */
+				if (!pss_get_dspword(devc, data++)) {
+					if (i == 0)
+						err = -EIO;
+					break;
+				}
+			}
 			restore_flags(flags);
-			return -EIO;
-		}
-		dbuf.parm1 = tmp;
-		restore_flags(flags);
-		if (__copy_to_user(arg, &dbuf, sizeof(dbuf)))
-			return -EFAULT;
-		return 0;
+			if (copy_to_user(arg, mbuf, sizeof(copr_msg)))
+				err = -EFAULT;
+			vfree(mbuf);
+			return err;
 		
-	case SNDCTL_COPR_WDATA:
-		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
-			return -EFAULT;
-		save_flags(flags);
-		cli();
-		if (!pss_put_dspword(devc, 0x00d1)) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		tmp = (unsigned int)dbuf.parm2 & 0xffff;
-		if (!pss_put_dspword(devc, tmp)) {
+		case SNDCTL_COPR_RDATA:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d0)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_get_dspword(devc, &tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			dbuf.parm1 = tmp;
 			restore_flags(flags);
-			return -EIO;
-		}
-		restore_flags(flags);
-		return 0;
+			if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
+				return -EFAULT;
+			return 0;
 		
-	case SNDCTL_COPR_WCODE:
-		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
-			return -EFAULT;
-		save_flags(flags);
-		cli();
-		if (!pss_put_dspword(devc, 0x00d3)) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		tmp = (unsigned int)dbuf.parm2 & 0x00ff;
-		if (!pss_put_dspword(devc, tmp)) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff;
-		if (!pss_put_dspword(devc, tmp)) {
+		case SNDCTL_COPR_WDATA:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d1)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short) (dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			tmp = (unsigned int)dbuf.parm2 & 0xffff;
+			if (!pss_put_dspword(devc, tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
 			restore_flags(flags);
-			return -EIO;
-		}
-		restore_flags(flags);
-		return 0;
+			return 0;
 		
-	case SNDCTL_COPR_RCODE:
-		if (__copy_from_user(&dbuf, arg, sizeof(dbuf)))
-			return -EFAULT;
-		save_flags(flags);
-		cli();
-		if (!pss_put_dspword(devc, 0x00d2)) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
-			restore_flags(flags);
-			return -EIO;
-		}
-		if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */
+		case SNDCTL_COPR_WCODE:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d3)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			tmp = (unsigned int)dbuf.parm2 & 0x00ff;
+			if (!pss_put_dspword(devc, tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			tmp = ((unsigned int)dbuf.parm2 >> 8) & 0xffff;
+			if (!pss_put_dspword(devc, tmp)) {
+				restore_flags(flags);
+				return -EIO;
+			}
 			restore_flags(flags);
-			return -EIO;
-		}
-		dbuf.parm1 = tmp << 8;
-		if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */
+			return 0;
+		
+		case SNDCTL_COPR_RCODE:
+			if (copy_from_user(&dbuf, arg, sizeof(dbuf)))
+				return -EFAULT;
+			save_flags(flags);
+			cli();
+			if (!pss_put_dspword(devc, 0x00d2)) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_put_dspword(devc, (unsigned short)(dbuf.parm1 & 0xffff))) {
+				restore_flags(flags);
+				return -EIO;
+			}
+			if (!pss_get_dspword(devc, &tmp)) { /* Read MSB */
+				restore_flags(flags);
+				return -EIO;
+			}
+			dbuf.parm1 = tmp << 8;
+			if (!pss_get_dspword(devc, &tmp)) { /* Read LSB */
+				restore_flags(flags);
+				return -EIO;
+			}
+			dbuf.parm1 |= tmp & 0x00ff;
 			restore_flags(flags);
-			return -EIO;
-		}
-		dbuf.parm1 |= tmp & 0x00ff;
-		restore_flags(flags);
-		if (__copy_to_user(arg, &dbuf, sizeof(dbuf)))
-			return -EFAULT;
-		return 0;
+			if (copy_to_user(arg, &dbuf, sizeof(dbuf)))
+				return -EFAULT;
+			return 0;
 
-	default:
-		return -EINVAL;
+		default:
+			return -EINVAL;
 	}
 	return -EINVAL;
 }
@@ -689,52 +667,47 @@
 	&pss_data
 };
 
-void
-attach_pss_mpu(struct address_info *hw_config)
+void attach_pss_mpu(struct address_info *hw_config)
 {
 #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
-	{
-		attach_mpu401(hw_config);	/* Slot 1 */
-
-		if (hw_config->slots[1] != -1)	/* The MPU driver installed itself */
-			midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
-	}
+	attach_mpu401(hw_config);	/* Slot 1 */
+	if (hw_config->slots[1] != -1)	/* The MPU driver installed itself */
+		midi_devs[hw_config->slots[1]]->coproc = &pss_coproc_operations;
 #endif
 }
 
-int
-probe_pss_mss(struct address_info *hw_config)
+int probe_pss_mss(struct address_info *hw_config)
 {
-	volatile int    timeout;
+	volatile int timeout;
 
 	if (!pss_initialized)
 		return 0;
 
 	if (check_region(hw_config->io_base, 8))
-	  {
-		  printk("PSS: WSS I/O port conflict\n");
+	{
+		  printk(KERN_ERR "PSS: WSS I/O port conflicts.\n");
 		  return 0;
-	  }
+	}
 	if (!set_io_base(devc, CONF_WSS, hw_config->io_base))
-	  {
-		  printk("PSS: WSS base error.\n");
-		  return 0;
-	  }
+	{
+		printk("PSS: WSS base not settable.\n");
+		return 0;
+	}
 	if (!set_irq(devc, CONF_WSS, hw_config->irq))
-	  {
-		  printk("PSS: WSS IRQ error.\n");
-		  return 0;
-	  }
+	{
+		printk("PSS: WSS IRQ allocation error.\n");
+		return 0;
+	}
 	if (!set_dma(devc, CONF_WSS, hw_config->dma))
-	  {
-		  printk("PSS: WSS DRQ error\n");
-		  return 0;
-	  }
+	{
+		printk(KERN_ERR "PSS: WSS DMA allocation error\n");
+		return 0;
+	}
 	/*
-	   * For some reason the card returns 0xff in the WSS status register
-	   * immediately after boot. Probably MIDI+SB emulation algorithm
-	   * downloaded to the ADSP2115 spends some time initializing the card.
-	   * Let's try to wait until it finishes this task.
+	 * For some reason the card returns 0xff in the WSS status register
+	 * immediately after boot. Probably MIDI+SB emulation algorithm
+	 * downloaded to the ADSP2115 spends some time initializing the card.
+	 * Let's try to wait until it finishes this task.
 	 */
 	for (timeout = 0;
 	timeout < 100000 && (inb(hw_config->io_base + 3) & 0x3f) != 0x04;
@@ -748,8 +721,7 @@
 	return probe_ms_sound(hw_config);
 }
 
-void
-attach_pss_mss(struct address_info *hw_config)
+void attach_pss_mss(struct address_info *hw_config)
 {
 	attach_ms_sound(hw_config);	/* Slot 0 */
 
@@ -757,34 +729,31 @@
 		audio_devs[hw_config->slots[0]]->coproc = &pss_coproc_operations;
 }
 
-void
-unload_pss(struct address_info *hw_config)
+void unload_pss(struct address_info *hw_config)
 {
 }
 
-void
-unload_pss_mpu(struct address_info *hw_config)
+void unload_pss_mpu(struct address_info *hw_config)
 {
 #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
 	unload_mpu401(hw_config);
 #endif
 }
 
-void
-unload_pss_mss(struct address_info *hw_config)
+void unload_pss_mss(struct address_info *hw_config)
 {
 	unload_ms_sound(hw_config);
 }
 
 #ifdef MODULE
 
-int pss_io = 0x220;
+int pss_io = -1;
 
-int mss_io = 0x530;
-int mss_irq = 11;
-int mss_dma = 1;
+int mss_io = -1;
+int mss_irq = -1;
+int mss_dma = -1;
 
-int mpu_io = 0x330;
+int mpu_io = -1;
 int mpu_irq = -1;
 
 struct address_info cfgpss = { 0 /* pss_io */, 0, -1, -1 };
@@ -798,22 +767,20 @@
 MODULE_PARM(mpu_io, "i");
 MODULE_PARM(mpu_irq, "i");
 
-static int      fw_load = 0;
-static int      pssmpu = 0, pssmss = 0;
+static int fw_load = 0;
+static int pssmpu = 0, pssmss = 0;
 
 /*
  *    Load a PSS sound card module
  */
 
-int 
-init_module(void)
+int init_module(void)
 {
-#if 0
-	if (pss_io == -1 || irq == -1 || dma == -1) {
-		  printk("pss: dma, irq and io must be set.\n");
-		  return -EINVAL;
+	if (pss_io == -1 || mss_io == -1 || mss_irq == -1 || mss_dma == -1) {
+		printk(KERN_INFO "pss: mss_io, mss_dma, mss_irq and pss_io must be set.\n");
+		return -EINVAL;
 	}
-#endif
+
 	cfgpss.io_base = pss_io;
 
 	cfgmss.io_base = mss_io;
@@ -823,7 +790,8 @@
 	cfgmpu.io_base = mpu_io;
 	cfgmpu.irq = mpu_irq;
 
-	if (!pss_synth) {
+	if (!pss_synth) 
+	{
 		fw_load = 1;
 		pss_synthLen = mod_firmware_load("/etc/sound/pss_synth", (void *) &pss_synth);
 	}
@@ -845,8 +813,7 @@
 	return 0;
 }
 
-void 
-cleanup_module(void)
+void cleanup_module(void)
 {
 	if (fw_load && pss_synth)
 		kfree(pss_synth);
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.1.89/linux/drivers/sound/sb_audio.c	Sun Jan  4 10:40:16 1998
+++ linux/drivers/sound/sb_audio.c	Thu Mar 12 10:52:09 1998
@@ -1151,14 +1151,14 @@
 	}
 
 	if ((devc->my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
-						   name,
-						   driver,
-					     sizeof(struct audio_driver),
-						   audio_flags,
-						   format_mask,
-						   devc,
-						   devc->dma8,
-						   devc->dma8)) < 0)
+						name,
+						driver,
+						sizeof(struct audio_driver),
+						audio_flags,
+						format_mask,
+						devc,
+						devc->dma8,
+						devc->dma8)) < 0)
 	{
 		  printk(KERN_ERR "sb: unable to install audio.\n");
 		  return;
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- v2.1.89/linux/drivers/sound/sb_card.c	Mon Dec 29 10:22:46 1997
+++ linux/drivers/sound/sb_card.c	Fri Mar 13 15:36:03 1998
@@ -69,6 +69,7 @@
 int             pas2 = 0;	/* Set pas2=1 to load this as support for pas2 */
 int             sm_games = 0;	/* Mixer - see sb_mixer.c */
 int             acer = 0;	/* Do acer notebook init */
+int		mwave_bug = 0;	/* Using the dreadful mwave sb emulation */
 
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
@@ -80,10 +81,11 @@
 MODULE_PARM(trix, "i");
 MODULE_PARM(pas2, "i");
 MODULE_PARM(sm_games, "i");
+MODULE_PARM(mwave_bug, "i");
 
-static int      sbmpu = 0;
+static int sbmpu = 0;
 
-void           *smw_free = NULL;
+void *smw_free = NULL;
 
 int init_module(void)
 {
@@ -119,8 +121,7 @@
 	return 0;
 }
 
-void
-cleanup_module(void)
+void cleanup_module(void)
 {
 	if (smw_free)
 		kfree(smw_free);
@@ -133,19 +134,20 @@
 
 #else
 
-#ifdef SM_GAMES
+#ifdef CONFIG_SM_GAMES
 int             sm_games = 1;
-
 #else
 int             sm_games = 0;
-
 #endif
-#ifdef SB_ACER
+#ifdef CONFIG_SB_ACER
 int             acer = 1;
-
 #else
 int             acer = 0;
-
+#endif
+#ifdef CONFIG_SB_MWAVE
+int		mwave_bug = 1;
+#else
+int 		mwave_bug = 0;
 #endif
 #endif
 #endif
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.89/linux/drivers/sound/sb_common.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/sb_common.c	Fri Mar 13 15:36:03 1998
@@ -28,7 +28,6 @@
 
 static sb_devc *detected_devc = NULL;	/* For communication from probe to init */
 static sb_devc *last_devc = NULL;	/* For MPU401 initialization */
-static sb_devc *irq2devc[16] = {NULL};
 static unsigned char jazz_irq_bits[] = {
 	0, 0, 2, 3, 0, 1, 0, 4, 0, 2, 5, 0, 0, 0, 0, 6
 };
@@ -123,15 +122,9 @@
 	int status;
 	unsigned char   src = 0xff;
 
-	sb_devc *devc = irq2devc[irq];
+	sb_devc *devc = dev_id;
 
-	if (devc == NULL || devc->irq != irq)
-	{
-		DEB(printk("sbintr: Bogus interrupt IRQ%d\n", irq));
-		return;
-	}
 	devc->irq_ok = 1;
-
 	if (devc->model == MDL_SB16)
 	{
 		src = sb_getmixer(devc, IRQ_STAT);	/* Interrupt source register */
@@ -710,6 +703,7 @@
 	sb_devc *devc;
 	char name[100];
 	extern int sb_be_quiet;
+	extern int mwave_bug;
 
 /*
  * Check if we had detected a SB device earlier
@@ -743,21 +737,19 @@
 
 	if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && hw_config->irq > 0)
 	{			/* IRQ setup */
-		if (snd_set_irq_handler(hw_config->irq, sbintr, "soundblaster", devc->osp) < 0)
+		if (request_irq(hw_config->irq, sbintr, 0, "soundblaster", devc) < 0)
 		{
 			printk(KERN_ERR "SB: Can't allocate IRQ%d\n", hw_config->irq);
 			sound_unload_audiodev(devc->dev);
 			return;
 		}
-		irq2devc[hw_config->irq] = devc;
 		devc->irq_ok = 0;
 
 		if (devc->major == 4)
 			if (!sb16_set_irq_hw(devc, devc->irq))	/* Unsupported IRQ */
 			{
-				snd_release_irq(devc->irq);
+				free_irq(devc->irq, devc);
 				sound_unload_audiodev(devc->dev);
-				irq2devc[hw_config->irq] = NULL;
 				return;
 			}
 		if ((devc->type == 0 || devc->type == MDL_ESS) &&
@@ -777,7 +769,7 @@
 		/* Skip IRQ detection if SMP (doesn't work) */
 		devc->irq_ok = 1;
 #else
-		if (devc->major == 4 && devc->minor <= 11)	/* Won't work */
+		if ((devc->major == 4 && devc->minor <= 11 ) || mwave_bug )	/* Won't work */
 			devc->irq_ok = 1;
 		else
 		{
@@ -883,6 +875,7 @@
 		}
 	}
 	hw_config->card_subtype = devc->model;
+	hw_config->slots[0]=devc->dev;
 	last_devc = devc;	/* For SB MPU detection */
 
 	if (!(devc->caps & SB_NO_AUDIO) && devc->dma8 >= 0)
@@ -915,15 +908,8 @@
 void sb_dsp_unload(struct address_info *hw_config)
 {
 	sb_devc *devc;
-	int irq = hw_config->irq;
-
-	if (irq < 0)
-		irq *= -1;
 
-	if (irq > 2 && irq < 16)
-		devc = irq2devc[irq];
-	else
-		devc = NULL;
+	devc = audio_devs[hw_config->slots[0]]->devc;
 
 	if (devc && devc->base == hw_config->io_base)
 	{
@@ -937,12 +923,12 @@
 		}
 		if (!(devc->caps & SB_NO_AUDIO && devc->caps & SB_NO_MIDI) && devc->irq > 0)
 		{
-			snd_release_irq(devc->irq);
-			irq2devc[devc->irq] = NULL;
+			free_irq(devc->irq, devc);
 			sound_unload_mixerdev(devc->my_mixerdev);
 			sound_unload_mididev(devc->my_mididev);
 			sound_unload_audiodev(devc->my_dev);
 		}
+		kfree(devc);
 	}
 	else
 		release_region(hw_config->io_base, 16);
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.1.89/linux/drivers/sound/sound_calls.h	Tue Mar 10 10:03:33 1998
+++ linux/drivers/sound/sound_calls.h	Thu Mar 12 10:52:09 1998
@@ -11,7 +11,6 @@
 int DMAbuf_move_wrpointer(int dev, int l);
 /* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */
 void DMAbuf_init(int dev, int dma1, int dma2);
-void DMAbuf_deinit(int dev);
 int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
 int DMAbuf_open_dma (int dev);
 void DMAbuf_close_dma (int dev);
@@ -84,8 +83,9 @@
 /*	From soundcard.c	*/
 void request_sound_timer (int count);
 void sound_stop_timer(void);
-int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp);
-void snd_release_irq(int vect);
+/* These two are about to die.. */
+int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp, void *dev_id);
+void snd_release_irq(int vect, void *ptr);
 void sound_dma_malloc(int dev);
 void sound_dma_free(int dev);
 void conf_printf(char *name, struct address_info *hw_config);
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/sound_timer.c linux/drivers/sound/sound_timer.c
--- v2.1.89/linux/drivers/sound/sound_timer.c	Mon Feb 23 18:12:09 1998
+++ linux/drivers/sound/sound_timer.c	Thu Mar 12 10:52:09 1998
@@ -47,6 +47,14 @@
 {
 	unsigned long   usecs_per_tick;
 
+	/*
+	 *	The user is changing the timer rate before setting a timer
+	 *	slap, bad bad not allowed.
+	 */
+	 
+	if(!tmr)
+		return;
+		
 	usecs_per_tick = (60 * 1000000) / (curr_tempo * curr_timebase);
 
 	/*
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.89/linux/drivers/sound/soundcard.c	Tue Mar 10 10:03:33 1998
+++ linux/drivers/sound/soundcard.c	Thu Mar 12 10:52:09 1998
@@ -26,7 +26,7 @@
 #include <linux/fcntl.h>
 #include <linux/ctype.h>
 #include <linux/stddef.h>
-#include <linux/kerneld.h>
+#include <linux/kmod.h>
 #ifdef __KERNEL__
 #include <asm/io.h>
 #include <asm/segment.h>
@@ -439,7 +439,7 @@
 
 	case SND_DEV_CTL:
 		dev >>= 4;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (dev >= 0 && dev < MAX_MIXER_DEV && mixer_devs[dev] == NULL) {
 		char modname[20];
 		sprintf(modname, "mixer%d", dev);
@@ -555,14 +555,14 @@
 {
  	if (mixdev < 0 || mixdev >= MAX_MIXER_DEV)
  		return -ENXIO;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
  	/* Try to load the mixer... */
  	if (mixer_devs[mixdev] == NULL) {
  		char modname[20];
  		sprintf(modname, "mixer%d", mixdev);
  		request_module(modname);
  	}
-#endif	/* CONFIG_KERNELD */
+#endif	/* CONFIG_KMOD */
  	if (mixdev >= num_mixers || !mixer_devs[mixdev])
  		return -ENXIO;
 	if (cmd == SOUND_MIXER_INFO)
@@ -801,7 +801,7 @@
 		if (irqs & (1ul << i))
 		{
 			printk(KERN_WARNING "Sound warning: IRQ%d was left allocated - fixed.\n", i);
-			snd_release_irq(i);
+			snd_release_irq(i, NULL);
 		}
 	}
 	irqs = 0;
@@ -895,14 +895,14 @@
 }
 #endif
 
-int snd_set_irq_handler(int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp)
+int snd_set_irq_handler(int interrupt_level, void (*iproc) (int, void *, struct pt_regs *), char *name, int *osp, void *dev_id)
 {
-	int             retcode;
-	unsigned long   flags;
+	int retcode;
+	unsigned long flags;
 
 	save_flags(flags);
 	cli();
-	retcode = request_irq(interrupt_level, iproc, 0, name, NULL);
+	retcode = request_irq(interrupt_level, iproc, 0, name, dev_id);
 	
 	if (retcode < 0)
 	{
@@ -915,13 +915,13 @@
 	return retcode;
 }
 
-void snd_release_irq(int vect)
+void snd_release_irq(int vect, void *dev_id)
 {
 	if (!(irqs & (1ul << vect)))
 		return;
 
 	irqs &= ~(1ul << vect);
-	free_irq(vect, NULL);
+	free_irq(vect, dev_id);
 }
 
 int sound_alloc_dma(int chn, char *deviceID)
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c
--- v2.1.89/linux/drivers/sound/uart401.c	Tue Dec 30 10:59:17 1997
+++ linux/drivers/sound/uart401.c	Thu Mar 12 10:52:09 1998
@@ -9,7 +9,15 @@
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
+ *
+ * Changes:
+ *	Alan Cox	Reformatted, removed sound_mem usage, use normal Linux
+ *			interrupt allocation.
+ *
+ * Status:
+ *		Untested
  */
+ 
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -19,45 +27,43 @@
 #if (defined(CONFIG_UART401)||defined(CONFIG_MIDI)) || defined(MODULE)
 
 typedef struct uart401_devc
-  {
-	  int             base;
-	  int             irq;
-	  int            *osp;
-	  void            (*midi_input_intr) (int dev, unsigned char data);
-	  int             opened, disabled;
-	  volatile unsigned char input_byte;
-	  int             my_dev;
-	  int             share_irq;
-  }
+{
+	int             base;
+	int             irq;
+	int            *osp;
+	void            (*midi_input_intr) (int dev, unsigned char data);
+	int             opened, disabled;
+	volatile unsigned char input_byte;
+	int             my_dev;
+	int             share_irq;
+}
 uart401_devc;
 
 static uart401_devc *detected_devc = NULL;
-static uart401_devc *irq2devc[16] =
-{NULL};
 
 #define	DATAPORT   (devc->base)
 #define	COMDPORT   (devc->base+1)
 #define	STATPORT   (devc->base+1)
 
-static int
-uart401_status(uart401_devc * devc)
+static int uart401_status(uart401_devc * devc)
 {
 	return inb(STATPORT);
 }
+
 #define input_avail(devc) (!(uart401_status(devc)&INPUT_AVAIL))
 #define output_ready(devc)	(!(uart401_status(devc)&OUTPUT_READY))
-static void
-uart401_cmd(uart401_devc * devc, unsigned char cmd)
+
+static void uart401_cmd(uart401_devc * devc, unsigned char cmd)
 {
 	outb((cmd), COMDPORT);
 }
-static int
-uart401_read(uart401_devc * devc)
+
+static int uart401_read(uart401_devc * devc)
 {
 	return inb(DATAPORT);
 }
-static void
-uart401_write(uart401_devc * devc, unsigned char byte)
+
+static void uart401_write(uart401_devc * devc, unsigned char byte)
 {
 	outb((byte), DATAPORT);
 }
@@ -71,30 +77,26 @@
 static int      reset_uart401(uart401_devc * devc);
 static void     enter_uart_mode(uart401_devc * devc);
 
-static void
-uart401_input_loop(uart401_devc * devc)
+static void uart401_input_loop(uart401_devc * devc)
 {
 	while (input_avail(devc))
-	  {
-		  unsigned char   c = uart401_read(devc);
+	{
+		unsigned char   c = uart401_read(devc);
 
-		  if (c == MPU_ACK)
-			  devc->input_byte = c;
-		  else if (devc->opened & OPEN_READ && devc->midi_input_intr)
-			  devc->midi_input_intr(devc->my_dev, c);
-	  }
+		if (c == MPU_ACK)
+			devc->input_byte = c;
+		else if (devc->opened & OPEN_READ && devc->midi_input_intr)
+			devc->midi_input_intr(devc->my_dev, c);
+	}
 }
 
-void
-uart401intr(int irq, void *dev_id, struct pt_regs *dummy)
+void uart401intr(int irq, void *dev_id, struct pt_regs *dummy)
 {
-	uart401_devc   *devc;
+	uart401_devc *devc = dev_id;
 
 	if (irq < 1 || irq > 15)
 		return;
 
-	devc = irq2devc[irq];
-
 	if (devc == NULL)
 		return;
 
@@ -108,12 +110,12 @@
 	     void            (*output) (int dev)
 )
 {
-	uart401_devc   *devc = (uart401_devc *) midi_devs[dev]->devc;
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
 
 	if (devc->opened)
-	  {
-		  return -EBUSY;
-	  }
+	{
+		return -EBUSY;
+	}
 	while (input_avail(devc))
 		uart401_read(devc);
 
@@ -125,21 +127,19 @@
 	return 0;
 }
 
-static void
-uart401_close(int dev)
+static void uart401_close(int dev)
 {
-	uart401_devc   *devc = (uart401_devc *) midi_devs[dev]->devc;
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
 
 	reset_uart401(devc);
 	devc->opened = 0;
 }
 
-static int
-uart401_out(int dev, unsigned char midi_byte)
+static int uart401_out(int dev, unsigned char midi_byte)
 {
-	int             timeout;
-	unsigned long   flags;
-	uart401_devc   *devc = (uart401_devc *) midi_devs[dev]->devc;
+	int timeout;
+	unsigned long flags;
+	uart401_devc *devc = (uart401_devc *) midi_devs[dev]->devc;
 
 	if (devc->disabled)
 		return 1;
@@ -163,36 +163,32 @@
 	for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
 
 	if (!output_ready(devc))
-	  {
-		  printk("MPU-401: Timeout - Device not responding\n");
+	{
+		  printk(KERN_WARNING "uart401: Timeout - Device not responding\n");
 		  devc->disabled = 1;
 		  reset_uart401(devc);
 		  enter_uart_mode(devc);
 		  return 1;
-	  }
+	}
 	uart401_write(devc, midi_byte);
 	return 1;
 }
 
-static int
-uart401_start_read(int dev)
+static int uart401_start_read(int dev)
 {
 	return 0;
 }
 
-static int
-uart401_end_read(int dev)
+static int uart401_end_read(int dev)
 {
 	return 0;
 }
 
-static void
-uart401_kick(int dev)
+static void uart401_kick(int dev)
 {
 }
 
-static int
-uart401_buffer_status(int dev)
+static int uart401_buffer_status(int dev)
 {
 	return 0;
 }
@@ -203,7 +199,9 @@
 
 static struct midi_operations uart401_operations =
 {
-	{"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401},
+	{
+		"MPU-401 (UART) MIDI", 0, 0, SNDCARD_MPU401
+	},
 	&std_midi_synth,
 	{0},
 	uart401_open,
@@ -218,11 +216,10 @@
 	NULL
 };
 
-static void
-enter_uart_mode(uart401_devc * devc)
+static void enter_uart_mode(uart401_devc * devc)
 {
-	int             ok, timeout;
-	unsigned long   flags;
+	int ok, timeout;
+	unsigned long flags;
 
 	save_flags(flags);
 	cli();
@@ -242,11 +239,10 @@
 	restore_flags(flags);
 }
 
-void
-attach_uart401(struct address_info *hw_config)
+void attach_uart401(struct address_info *hw_config)
 {
-	uart401_devc   *devc;
-	char           *name = "MPU-401 (UART) MIDI";
+	uart401_devc *devc;
+	char *name = "MPU-401 (UART) MIDI";
 
 	if (hw_config->name)
 		name = hw_config->name;
@@ -255,80 +251,75 @@
 		return;
 
 
-	devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(uart401_devc)));
-	sound_mem_sizes[sound_nblocks] = sizeof(uart401_devc);
-	if (sound_nblocks < 1024)
-		sound_nblocks++;;
+	devc = (uart401_devc *) kmalloc(sizeof(uart401_devc), GFP_KERNEL);
 	if (devc == NULL)
-	  {
-		  printk(KERN_WARNING "uart401: Can't allocate memory\n");
-		  return;
-	  }
+	{
+		printk(KERN_WARNING "uart401: Can't allocate memory\n");
+		return;
+	}
 	memcpy((char *) devc, (char *) detected_devc, sizeof(uart401_devc));
 	detected_devc = NULL;
 
 	devc->irq = hw_config->irq;
 	if (devc->irq < 0)
-	  {
-		  devc->share_irq = 1;
-		  devc->irq *= -1;
-	} else
+	{
+		devc->share_irq = 1;
+		devc->irq *= -1;
+	}
+	else
 		devc->share_irq = 0;
 
 	if (devc->irq < 1 || devc->irq > 15)
+	{
+		kfree(devc);
 		return;
+	}
 
 	if (!devc->share_irq)
-		if (snd_set_irq_handler(devc->irq, uart401intr, "MPU-401 UART", devc->osp) < 0)
-		  {
-			  printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
-			  devc->share_irq = 1;
-		  }
-	irq2devc[devc->irq] = devc;
+	{
+		if (request_irq(devc->irq, uart401intr, 0, "MPU-401 UART", devc) < 0)
+		{
+			printk(KERN_WARNING "uart401: Failed to allocate IRQ%d\n", devc->irq);
+			devc->share_irq = 1;
+		}
+	}
 	devc->my_dev = sound_alloc_mididev();
 
 	request_region(hw_config->io_base, 4, "MPU-401 UART");
 	enter_uart_mode(devc);
 
 	if (devc->my_dev == -1)
-	  {
-		  printk(KERN_INFO "uart401: Too many midi devices detected\n");
-		  return;
-	  }
+	{
+		printk(KERN_INFO "uart401: Too many midi devices detected\n");
+		kfree(devc);
+		return;
+	}
 	conf_printf(name, hw_config);
 
 	std_midi_synth.midi_dev = devc->my_dev;
-
-
-	midi_devs[devc->my_dev] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct midi_operations)));
-	sound_mem_sizes[sound_nblocks] = sizeof(struct midi_operations);
-
-	if (sound_nblocks < 1024)
-		sound_nblocks++;;
+	midi_devs[devc->my_dev] = (struct midi_operations *)kmalloc(sizeof(struct midi_operations), GFP_KERNEL);
 	if (midi_devs[devc->my_dev] == NULL)
-	  {
-		  printk("uart401: Failed to allocate memory\n");
-		  sound_unload_mididev(devc->my_dev);
-		  return;
-	  }
+	{
+		printk(KERN_ERR "uart401: Failed to allocate memory\n");
+		sound_unload_mididev(devc->my_dev);
+		kfree(devc);
+		devc=NULL;
+		return;
+	}
 	memcpy((char *) midi_devs[devc->my_dev], (char *) &uart401_operations,
 	       sizeof(struct midi_operations));
 
 	midi_devs[devc->my_dev]->devc = devc;
-
-
-	midi_devs[devc->my_dev]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct synth_operations)));
-	sound_mem_sizes[sound_nblocks] = sizeof(struct synth_operations);
-
-	if (sound_nblocks < 1024)
-		sound_nblocks++;
-
+	midi_devs[devc->my_dev]->converter = (struct synth_operations *)kmalloc(sizeof(struct synth_operations), GFP_KERNEL);
 	if (midi_devs[devc->my_dev]->converter == NULL)
-	  {
-		  printk(KERN_WARNING "uart401: Failed to allocate memory\n");
-		  sound_unload_mididev(devc->my_dev);
-		  return;
-	  }
+	{
+		printk(KERN_WARNING "uart401: Failed to allocate memory\n");
+		sound_unload_mididev(devc->my_dev);
+		kfree(midi_devs[devc->my_dev]);
+		kfree(devc);
+		devc=NULL;
+		return;
+	}
 	memcpy((char *) midi_devs[devc->my_dev]->converter, (char *) &std_midi_synth,
 	       sizeof(struct synth_operations));
 
@@ -339,10 +330,9 @@
 	devc->opened = 0;
 }
 
-static int
-reset_uart401(uart401_devc * devc)
+static int reset_uart401(uart401_devc * devc)
 {
-	int             ok, timeout, n;
+	int ok, timeout, n;
 
 	/*
 	 * Send the RESET command. Try again if no success at the first time.
@@ -351,31 +341,33 @@
 	ok = 0;
 
 	for (n = 0; n < 2 && !ok; n++)
-	  {
-		  for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
-
-		  devc->input_byte = 0;
-		  uart401_cmd(devc, MPU_RESET);
-
-		  /*
-		   * Wait at least 25 msec. This method is not accurate so let's make the
-		   * loop bit longer. Cannot sleep since this is called during boot.
-		   */
-
-		  for (timeout = 50000; timeout > 0 && !ok; timeout--)
-			  if (devc->input_byte == MPU_ACK)	/* Interrupt */
-				  ok = 1;
-			  else if (input_avail(devc))
-				  if (uart401_read(devc) == MPU_ACK)
-					  ok = 1;
-
-	  }
+	{
+		for (timeout = 30000; timeout > 0 && !output_ready(devc); timeout--);
+		devc->input_byte = 0;
+		uart401_cmd(devc, MPU_RESET);
+		/*
+		 * Wait at least 25 msec. This method is not accurate so let's make the
+		 * loop bit longer. Cannot sleep since this is called during boot.
+		 */
+
+		for (timeout = 50000; timeout > 0 && !ok; timeout--)
+		{
+			if (devc->input_byte == MPU_ACK)	/* Interrupt */
+				ok = 1;
+			else if (input_avail(devc))
+			{
+				if (uart401_read(devc) == MPU_ACK)
+					ok = 1;
+			}
+		}
+	}
 
 
 	if (ok)
-	  {
-		  DEB(printk("Reset UART401 OK\n"));
-	} else
+	{
+		DEB(printk("Reset UART401 OK\n"));
+	}
+	else
 		DDB(printk("Reset UART401 failed - No hardware detected.\n"));
 
 	if (ok)
@@ -386,14 +378,12 @@
 	return ok;
 }
 
-int
-probe_uart401(struct address_info *hw_config)
+int probe_uart401(struct address_info *hw_config)
 {
-	int             ok = 0;
-	unsigned long   flags;
-
+	int ok = 0;
+	unsigned long flags;
 	static uart401_devc hw_info;
-	uart401_devc   *devc = &hw_info;
+	uart401_devc *devc = &hw_info;
 
 	DDB(printk("Entered probe_uart401()\n"));
 
@@ -422,21 +412,10 @@
 	return ok;
 }
 
-void
-unload_uart401(struct address_info *hw_config)
+void unload_uart401(struct address_info *hw_config)
 {
-	uart401_devc   *devc;
-
-	int             irq = hw_config->irq;
-
-	if (irq < 0)
-	  {
-		  irq *= -1;
-	  }
-	if (irq < 1 || irq > 15)
-		return;
-
-	devc = irq2devc[irq];
+	uart401_devc *devc;
+	devc = midi_devs[hw_config->slots[4]]->devc;
 	if (devc == NULL)
 		return;
 
@@ -444,47 +423,47 @@
 	release_region(hw_config->io_base, 4);
 
 	if (!devc->share_irq)
-		snd_release_irq(devc->irq);
-
+		free_irq(devc->irq, devc);
+	sound_unload_mididev(hw_config->slots[4]);
 	if (devc)
+	{
+		kfree(midi_devs[devc->my_dev]->converter);
+		kfree(midi_devs[devc->my_dev]);
+		kfree(devc);
 		devc = NULL;
-	sound_unload_mididev(hw_config->slots[4]);
+	}
 }
 
 #ifdef MODULE
 
-int             io = -1;
-int             irq = -1;
+int io = -1;
+int irq = -1;
 
 MODULE_PARM(io, "i");
 MODULE_PARM(irq, "i");
 struct address_info hw;
 
-int 
-init_module(void)
+int init_module(void)
 {
 	/* Can be loaded either for module use or to provide functions
 	   to others */
 	if (io != -1 && irq != -1)
-	  {
-		  printk("MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
-		  hw.irq = irq;
-		  hw.io_base = io;
-		  if (probe_uart401(&hw) == 0)
-			  return -ENODEV;
-		  attach_uart401(&hw);
-	  }
+	{
+		printk(KERN_INFO "MPU-401 UART driver Copyright (C) Hannu Savolainen 1993-1997");
+		hw.irq = irq;
+		hw.io_base = io;
+		if (probe_uart401(&hw) == 0)
+			return -ENODEV;
+		attach_uart401(&hw);
+	}
 	SOUND_LOCK;
 	return 0;
 }
 
-void 
-cleanup_module(void)
+void cleanup_module(void)
 {
 	if (io != -1 && irq != -1)
-	  {
-		  unload_uart401(&hw);
-	  }
+		unload_uart401(&hw);
 	/*  FREE SYMTAB */
 	SOUND_LOCK_END;
 }
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c
--- v2.1.89/linux/drivers/sound/uart6850.c	Thu Feb 12 20:56:10 1998
+++ linux/drivers/sound/uart6850.c	Thu Mar 12 10:52:09 1998
@@ -10,6 +10,11 @@
  * Extended by Alan Cox for Red Hat Software. Now a loadable MIDI driver.
  * 28/4/97 - (C) Copyright Alan Cox. Released under the GPL version 2.
  *
+ * Alan Cox:	Updated for new modular code. Removed snd_* irq handling. Now
+ *		uses native linux resources
+ *
+ *	Status: Testing required
+ *
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -297,7 +302,7 @@
 	uart6850_base = hw_config->io_base;
 	uart6850_irq = hw_config->irq;
 
-	if (snd_set_irq_handler(uart6850_irq, m6850intr, "MIDI6850", uart6850_osp) < 0)
+	if (request_irq(uart6850_irq, m6850intr, 0, "MIDI6850", NULL) < 0)
 		return 0;
 
 	ok = reset_uart6850();
@@ -307,7 +312,7 @@
 
 void unload_uart6850(struct address_info *hw_config)
 {
-	snd_release_irq(hw_config->irq);
+	free_irq(hw_config->irq, NULL);
 	sound_unload_mididev(hw_config->slots[4]);
 }
 
diff -u --recursive --new-file v2.1.89/linux/drivers/sound/v_midi.c linux/drivers/sound/v_midi.c
--- v2.1.89/linux/drivers/sound/v_midi.c	Thu Feb 12 20:56:10 1998
+++ linux/drivers/sound/v_midi.c	Thu Mar 12 10:52:09 1998
@@ -2,15 +2,22 @@
  * sound/v_midi.c
  *
  * The low level driver for the Sound Blaster DS chips.
- */
-/*
+ *
+ *
  * Copyright (C) by Hannu Savolainen 1993-1996
  *
  * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  * ??
+ *
+ * Changes
+ *	Alan Cox		Modularisation, changed memory allocations
+ *
+ * Status
+ *	Untested
  */
+
 #include <linux/config.h>
 #include <linux/module.h>
 
diff -u --recursive --new-file v2.1.89/linux/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- v2.1.89/linux/drivers/video/fbcon.c	Mon Nov 24 01:18:56 1997
+++ linux/drivers/video/fbcon.c	Tue Mar 10 14:43:13 1998
@@ -74,8 +74,8 @@
 #include <linux/vt_kern.h>
 #include <linux/selection.h>
 #include <linux/init.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 #include <asm/irq.h>
@@ -196,7 +196,7 @@
 static struct display_switch *probe_list(struct display_switch *dispsw,
 					 struct display *disp);
 
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 static void request_driver(struct display *disp, int is_accel);
 #endif
 static struct display_switch *fbcon_get_driver(struct display *disp);
@@ -1368,7 +1368,7 @@
 }
 
 
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 static void request_driver(struct display *disp, int is_accel)
 {
     char modname[30];
@@ -1400,7 +1400,7 @@
 	len += sprintf(modname+len, "-%d", disp->var.accel);
     request_module(modname);
 }
-#endif /* CONFIG_KERNELD */
+#endif /* CONFIG_KMOD */
 
 
 static struct display_switch *fbcon_get_driver(struct display *disp)
@@ -1410,7 +1410,7 @@
     if (disp->var.accel != FB_ACCEL_NONE) {
 	/* First try an accelerated driver */
 	dispsw = probe_list(accel_drivers, disp);
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (!dispsw) {
 	    request_driver(disp, 1);
 	    dispsw = probe_list(accel_drivers, disp);
@@ -1422,7 +1422,7 @@
 
     /* Then try an unaccelerated driver */
     dispsw = probe_list(drivers, disp);
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
     if (!dispsw) {
 	request_driver(disp, 0);
 	dispsw = probe_list(drivers, disp);
diff -u --recursive --new-file v2.1.89/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.89/linux/fs/buffer.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/buffer.c	Thu Mar 12 10:56:00 1998
@@ -731,7 +731,8 @@
 	/* We are going to try to locate this much memory. */
 	needed = bdf_prm.b_un.nrefill * size;  
 
-	while ((nr_free_pages > min_free_pages*2) && 
+	while ((nr_free_pages > freepages.min*2) &&
+	        BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) &&
 		grow_buffers(GFP_BUFFER, size)) {
 		obtained += PAGE_SIZE;
 		if (obtained >= needed)
@@ -815,7 +816,8 @@
 	 * are _any_ free buffers.
 	 */
 	while (obtained < (needed >> 1) &&
-	       nr_free_pages > min_free_pages + 5 &&
+	       nr_free_pages > freepages.min + 5 &&
+	       BUFFER_MEM < (buffer_mem.max_percent * num_physpages / 100) &&
 	       grow_buffers(GFP_BUFFER, size))
 		obtained += PAGE_SIZE;
 
diff -u --recursive --new-file v2.1.89/linux/fs/coda/Makefile linux/fs/coda/Makefile
--- v2.1.89/linux/fs/coda/Makefile	Sun Dec 21 14:45:13 1997
+++ linux/fs/coda/Makefile	Tue Mar 17 21:19:04 1998
@@ -3,7 +3,7 @@
 #
 
 O_TARGET := coda.o
-O_OBJS   := psdev.o cache.o cnode.o super.o dir.o file.o upcall.o coda_linux.o\
+O_OBJS   := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o coda_linux.o\
             symlink.o pioctl.o sysctl.o
 M_OBJS   := $(O_TARGET)
 
diff -u --recursive --new-file v2.1.89/linux/fs/coda/cache.c linux/fs/coda/cache.c
--- v2.1.89/linux/fs/coda/cache.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/cache.c	Tue Mar 17 21:19:05 1998
@@ -24,16 +24,22 @@
 #include <linux/coda_fs_i.h>
 #include <linux/coda_cache.h>
 
-/* Keep various stats */
-struct cfsnc_statistics cfsnc_stat;
+static void coda_ccinsert(struct coda_cache *el, struct super_block *sb);
+static void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cii);
+static void coda_ccremove(struct coda_cache *el);
+static void coda_cnremove(struct coda_cache *el);
+static void coda_cache_create(struct inode *inode, int mask);
+static struct coda_cache * coda_cache_find(struct inode *inode);
 
 
-/* we need to call INIT_LIST_HEAD on cnp->c_cnhead and sbi->sbi_cchead */
+/* Keep various stats */
+struct cfsnc_statistics cfsnc_stat;
 
-void coda_ccinsert(struct coda_cache *el, struct super_block *sb)
+/* insert a acl-cache entry in sb list */
+static void coda_ccinsert(struct coda_cache *el, struct super_block *sb)
 {
 	struct coda_sb_info *sbi = coda_sbp(sb);
-ENTRY;
+	ENTRY;
 	if ( !sbi ||  !el) {
 		printk("coda_ccinsert: NULL sbi or el!\n");
 		return ;
@@ -42,17 +48,19 @@
 	list_add(&el->cc_cclist, &sbi->sbi_cchead);
 }
 
-void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp)
+/* insert a acl-cache entry in the inode list */
+static void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cii)
 {
-ENTRY;
-	if ( !cnp ||  !el) {
-		printk("coda_cninsert: NULL cnp or el!\n");
+	ENTRY;
+	if ( !cii ||  !el) {
+		printk("coda_cninsert: NULL cii or el!\n");
 		return ;
 	}
-	list_add(&el->cc_cnlist, &cnp->c_cnhead);
+	list_add(&el->cc_cnlist, &cii->c_cnhead);
 }
 
-void coda_ccremove(struct coda_cache *el)
+/* remove a cache entry from the superblock list */
+static void coda_ccremove(struct coda_cache *el)
 {
 	ENTRY;
         if (el->cc_cclist.next && el->cc_cclist.prev)
@@ -61,7 +69,8 @@
 		printk("coda_cnremove: trying to remove 0 entry!");
 }
 
-void coda_cnremove(struct coda_cache *el)
+/* remove a cache entry from the inode's list */
+static void coda_cnremove(struct coda_cache *el)
 {
 	ENTRY;
 	if (el->cc_cnlist.next && el->cc_cnlist.prev)
@@ -70,10 +79,10 @@
 		printk("coda_cnremove: trying to remove 0 entry!");
 }
 
-
-void coda_cache_create(struct inode *inode, int mask)
+/* create a new cache entry and enlist it */
+static void coda_cache_create(struct inode *inode, int mask)
 {
-	struct coda_inode_info *cnp = ITOC(inode);
+	struct coda_inode_info *cii = ITOC(inode);
 	struct super_block *sb = inode->i_sb;
 	struct coda_cache *cc = NULL;
 	ENTRY;
@@ -85,17 +94,19 @@
 	}
 	coda_load_creds(&cc->cc_cred);
 	cc->cc_mask = mask;
-	coda_cninsert(cc, cnp);
+	coda_cninsert(cc, cii);
 	coda_ccinsert(cc, sb);
 }
 
-struct coda_cache * coda_cache_find(struct inode *inode)
+/* see if there is a match for the current 
+   credentials already */
+static struct coda_cache * coda_cache_find(struct inode *inode)
 {
-	struct coda_inode_info *cnp = ITOC(inode);
+	struct coda_inode_info *cii = ITOC(inode);
 	struct list_head *lh, *le;
 	struct coda_cache *cc = NULL;
 	
-	le = lh = &cnp->c_cnhead;
+	le = lh = &cii->c_cnhead;
 	while( (le = le->next ) != lh )  {
 		/* compare name and creds */
 		cc = list_entry(le, struct coda_cache, cc_cnlist);
@@ -107,6 +118,7 @@
 		return NULL;
 }
 
+/* create or extend an acl cache hit */
 void coda_cache_enter(struct inode *inode, int mask)
 {
 	struct coda_cache *cc;
@@ -120,17 +132,21 @@
 	}
 }
 
-void coda_cache_clear_cnp(struct coda_inode_info *cnp)
+/* remove all cached acl matches from an inode */
+void coda_cache_clear_inode(struct inode *inode)
 {
 	struct list_head *lh, *le;
+	struct coda_inode_info *cii;
 	struct coda_cache *cc;
+	ENTRY;
 
-	if ( !cnp ) {
-		printk("coda_cache_cnp_clear: NULL cnode\n");
+	if ( !inode ) {
+		CDEBUG(D_CACHE, "coda_cache_clear_inode: NULL inode\n");
 		return;
 	}
+	cii = ITOC(inode);
 	
-	lh = le = &cnp->c_cnhead;
+	lh = le = &cii->c_cnhead;
 	while ( (le = le->next ) != lh ) {
 		cc = list_entry(le, struct coda_cache, cc_cnlist);
 		coda_cnremove(cc);
@@ -139,6 +155,7 @@
 	}
 }
 
+/* remove all acl caches */
 void coda_cache_clear_all(struct super_block *sb)
 {
 	struct list_head *lh, *le;
@@ -150,6 +167,9 @@
 		return;
 	}
 	
+	if ( list_empty(&sbi->sbi_cchead) )
+		return;
+
 	lh = le = &sbi->sbi_cchead;
 	while ( (le = le->next ) != lh ) {
 		cc = list_entry(le, struct coda_cache, cc_cclist);
@@ -159,6 +179,7 @@
 	}
 }
 
+/* remove all acl caches for a principal */
 void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred)
 {
 	struct list_head *lh, *le;
@@ -170,6 +191,9 @@
 		return;
 	}
 	
+	if (list_empty(&sbi->sbi_cchead))
+		return;
+
 	lh = le = &sbi->sbi_cchead;
 	while ( (le = le->next ) != lh ) {
 		cc = list_entry(le, struct coda_cache, cc_cclist);
@@ -180,15 +204,17 @@
 		}
 	}
 }
-		
 
+
+/* check if the mask has been matched against the acl
+   already */
 int coda_cache_check(struct inode *inode, int mask)
 {
-	struct coda_inode_info *cnp = ITOC(inode);
+	struct coda_inode_info *cii = ITOC(inode);
 	struct list_head *lh, *le;
 	struct coda_cache *cc = NULL;
 	
-	le = lh = &cnp->c_cnhead;
+	le = lh = &cii->c_cnhead;
 	while( (le = le->next ) != lh )  {
 		/* compare name and creds */
 		cc = list_entry(le, struct coda_cache, cc_cnlist);
@@ -204,110 +230,70 @@
 }
 
 
-/*   DENTRY related stuff */
+/*   DCACHE & ZAPPING related stuff */
 
-/* when the dentry count falls to 0 this is called. If Venus has
-   asked for it to be flushed, we take it out of the dentry hash 
-   table with d_drop */
-
-static void coda_flag_children(struct dentry *parent)
+/* the following routines set flags in the inodes. They are 
+   detected by:
+   - a dentry method: coda_dentry_revalidate (for lookups)
+     if the flag is C_PURGE
+   - an inode method coda_revalidate (for attributes) if the 
+     flag is C_ATTR
+*/
+static void coda_flag_children(struct dentry *parent, int flag)
 {
 	struct list_head *child;
-	struct coda_inode_info *cnp;
 	struct dentry *de;
 
 	child = parent->d_subdirs.next;
 	while ( child != &parent->d_subdirs ) {
 		de = list_entry(child, struct dentry, d_child);
-		cnp = ITOC(de->d_inode);
-		if (cnp) 
-			cnp->c_flags |= C_ZAPFID;
-		CDEBUG(D_CACHE, "ZAPFID for %s\n", coda_f2s(&cnp->c_fid));
-		
+		coda_flag_inode(de->d_inode, flag);
+		CDEBUG(D_CACHE, "%d for %*s/%*s\n", flag, 
+		       de->d_name.len, de->d_name.name, 
+		       de->d_parent->d_name.len, de->d_parent->d_name.name);
 		child = child->next;
+		if ( !de->d_inode )
+			d_drop(de); 
 	}
 	return; 
 }
 
-/* flag dentry and possibly  children of a dentry with C_ZAPFID */
-void  coda_dentry_delete(struct dentry *dentry)
-{
-	struct inode *inode = dentry->d_inode;
-	struct coda_inode_info *cnp = NULL;
-	ENTRY;
-
-	if (inode) { 
-		cnp = ITOC(inode);
-		if ( cnp ) 
-			CHECK_CNODE(cnp);
-	} else {
-		CDEBUG(D_CACHE, "No inode for dentry_delete!\n");
-		return;
-	}
 
-	
-	if ( !cnp ) {
-		printk("No cnode for dentry_delete!\n");
-		return;
-	}
+void coda_flag_alias_children(struct inode *inode, int flag)
+{
+	struct list_head *alias;
+	struct dentry *alias_de;
 
-	if ( cnp->c_flags & (C_ZAPFID | C_ZAPDIR) )
-		d_drop(dentry);
-	if ( (cnp->c_flags & C_ZAPDIR) && S_ISDIR(inode->i_mode) ) {
-		coda_flag_children(dentry);
+	if ( !inode ) 
+		return; 
+	alias = inode->i_dentry.next; 
+	while ( alias != &inode->i_dentry ) {
+		alias_de = list_entry(alias, struct dentry, d_alias);
+		if ( !alias_de ) {
+			printk("Corrupt alias list for %*s\n",
+			       alias_de->d_name.len, alias_de->d_name.name);
+			return;
+		}
+		coda_flag_children(alias_de, flag);
+		alias= alias->next;
 	}
-	return;
 }
 
-static void coda_zap_cnode(struct coda_inode_info *cnp, int flags)
+void coda_flag_inode(struct inode *inode, int flag)
 {
-	cnp->c_flags |= flags;
-	coda_cache_clear_cnp(cnp);
-}
+	struct coda_inode_info *cii;
 
-
-
-/* the dache will notice the flags and drop entries (possibly with
-   children) the moment they are no longer in use  */
-void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag)
-{
-	struct inode *inode = NULL;
-	struct coda_inode_info *cnp;
-
-	ENTRY;
- 
-	if ( !sb ) {
-		printk("coda_zapfid: no sb!\n");
+	if ( !inode ) {
+		CDEBUG(D_CACHE, " no inode!\n");
 		return;
 	}
+	cii = ITOC(inode);
+	cii->c_flags |= flag;
+}		
 
-	if ( !fid ) {
-		printk("coda_zapfid: no fid!\n");
-		return;
-	}
 
-	if ( coda_fid_is_volroot(fid) ) {
-		struct list_head *lh, *le;
-		struct coda_sb_info *sbi = coda_sbp(sb);
-		le = lh = &sbi->sbi_volroothead;
-		while ( (le = le->next) != lh ) {
-			cnp = list_entry(le, struct coda_inode_info, c_volrootlist);
-			if ( cnp->c_fid.Volume == fid->Volume) 
-				coda_zap_cnode(cnp, flag);
-		}
-		return;
-	}
 
 
-	inode = coda_fid_to_inode(fid, sb);
-	if ( !inode ) {
-		CDEBUG(D_CACHE, "coda_zapfid: no inode!\n");
-		return;
-	}
-	cnp = ITOC(inode);
-	coda_zap_cnode(cnp, flag);
-}
-		
 
 int
 cfsnc_nc_info(char *buffer, char **start, off_t offset, int length, int dummy)
diff -u --recursive --new-file v2.1.89/linux/fs/coda/cnode.c linux/fs/coda/cnode.c
--- v2.1.89/linux/fs/coda/cnode.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/cnode.c	Tue Mar 17 21:19:05 1998
@@ -15,8 +15,6 @@
 
 /* cnode.c */
 
-
-              
 static void coda_fill_inode (struct inode *inode, struct coda_vattr *attr)
 {
         CDEBUG(D_SUPER, "ino: %ld\n", inode->i_ino);
@@ -56,8 +54,8 @@
         ENTRY;
 
         /* 
-         * We get inode numbers from Venus -- see venus source
-         */
+        * We get inode numbers from Venus -- see venus source
+	*/
 
 	error = venus_getattr(sb, fid, &attr);
 	if ( error ) {
@@ -79,7 +77,7 @@
 		memset(cnp, 0, (int) sizeof(struct coda_inode_info));
 		cnp->c_fid = *fid;
 		cnp->c_magic = CODA_CNODE_MAGIC;
-		cnp->c_flags = C_VATTR;
+		cnp->c_flags = 0;
 		cnp->c_vnode = *inode;
 		INIT_LIST_HEAD(&(cnp->c_cnhead));
 		INIT_LIST_HEAD(&(cnp->c_volrootlist));
@@ -111,20 +109,51 @@
 
  
 
-/* convert a fid to an inode. Avoids having a hash table
-   such as present in the Mach minicache */
+/* convert a fid to an inode. Mostly we can compute
+   the inode number from the FID, but not for volume
+   mount points: those are in a list */
 struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb) 
 {
 	ino_t nr;
 	struct inode *inode;
 	struct coda_inode_info *cnp;
-ENTRY;
+	ENTRY;
 
 	CDEBUG(D_INODE, "%s\n", coda_f2s(fid));
 
+	if ( !sb ) {
+		printk("coda_fid_to_inode: no sb!\n");
+		return NULL;
+	}
+
+	if ( !fid ) {
+		printk("coda_fid_to_inode: no fid!\n");
+		return NULL;
+	}
+
+
+	if ( coda_fid_is_volroot(fid) ) {
+		struct coda_inode_info *cii;
+		struct list_head *lh, *le;
+		struct coda_sb_info *sbi = coda_sbp(sb);
+		le = lh = &sbi->sbi_volroothead;
+
+		while ( (le = le->next) != lh ) {
+			cii = list_entry(le, struct coda_inode_info, 
+					 c_volrootlist);
+			if ( cii->c_fid.Volume == fid->Volume) {
+				inode = cii->c_vnode;
+				CDEBUG(D_INODE, "volume root, found %ld\n", cii->c_vnode->i_ino);
+				return cii->c_vnode;
+			}
+			
+		}
+		return NULL;
+	}
+
+	/* fid is not volume root, hence ino is computable */
 	nr = coda_f2i(fid);
 	inode = iget(sb, nr);
-
 	if ( !inode ) {
 		printk("coda_fid_to_inode: null from iget, sb %p, nr %ld.\n",
 		       sb, nr);
@@ -133,19 +162,25 @@
 
 	/* check if this inode is linked to a cnode */
 	cnp = ITOC(inode);
-
 	if ( cnp->c_magic != CODA_CNODE_MAGIC ) {
+		CDEBUG(D_INODE, "uninitialized inode. Return.\n");
 		iput(inode);
 		return NULL;
 	}
 
-	/* make sure fid is the one we want */
-	if ( !coda_fideq(fid, &(cnp->c_fid)) ) {
+	/* make sure fid is the one we want; 
+	   unfortunately Venus will shamelessly send us mount-symlinks.
+	   These have the same inode as the root of the volume they
+	   mount, but the fid will be wrong. 
+	*/
+	if ( !coda_fideq(fid, &(cnp->c_fid)) && 
+	     !coda_fid_is_volroot(&(cnp->c_fid))) {
 		printk("coda_fid2inode: bad cnode! Tell Peter.\n");
 		iput(inode);
 		return NULL;
 	}
 
+	CDEBUG(D_INODE, "found %ld\n", inode->i_ino);
 	iput(inode);	
 	return inode;
 }
diff -u --recursive --new-file v2.1.89/linux/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c
--- v2.1.89/linux/fs/coda/coda_linux.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/coda_linux.c	Tue Mar 17 21:19:05 1998
@@ -32,14 +32,15 @@
 /* caller must allocate 36 byte string ! */
 char * coda_f2s(ViceFid *f)
 {
-	static char s[50];
+	static char s[60];
 	if ( f ) {
-		sprintf(s, "(%10lx,%10lx,%10lx)", 
+		sprintf(s, "(%-#lx,%-#lx,%-#lx)", 
 			 f->Volume, f->Vnode, f->Unique);
 	}
 	return s;
 }
 
+/* recognize special .CONTROL name */
 int coda_iscontrol(const char *name, size_t length)
 {
 	if ((CFS_CONTROLLEN == length) && 
@@ -48,16 +49,23 @@
 	return 0;
 }
 
+/* recognize /coda inode */
 int coda_isroot(struct inode *i)
 {
     if ( i->i_sb->s_root->d_inode == i ) {
-	return 1;
+	    return 1;
     } else {
-	return 0;
+	    return 0;
     }
 }
 
-	
+/* is this a volume root FID */
+int coda_fid_is_volroot(struct ViceFid *fid)
+{
+	return ( (fid->Vnode == 1) && (fid->Unique == 1 ) );
+}
+
+/* put the current process credentials in the cred */
 void coda_load_creds(struct coda_cred *cred)
 {
         cred->cr_uid = (vuid_t) current->uid;
@@ -97,11 +105,6 @@
 	return coda_flags;
 }
 
-
-int coda_fid_is_volroot(struct ViceFid *fid)
-{
-	return ( (fid->Vnode == 1) && (fid->Unique == 1 ) );
-}
 
 /* utility functions below */
 void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
diff -u --recursive --new-file v2.1.89/linux/fs/coda/dir.c linux/fs/coda/dir.c
--- v2.1.89/linux/fs/coda/dir.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/dir.c	Tue Mar 17 21:19:05 1998
@@ -40,17 +40,21 @@
 /* dir file-ops */
 static int coda_readdir(struct file *file, void *dirent, filldir_t filldir);
 
+/* dentry ops */
+int coda_dentry_revalidate(struct dentry *de);
+
 /* support routines */
 static int coda_venus_readdir(struct file *filp, void *dirent, 
 			      filldir_t filldir);
 int coda_fsync(struct file *, struct dentry *dentry);
+static int coda_refresh_inode(struct dentry *dentry);
 
 struct dentry_operations coda_dentry_operations =
 {
-	NULL, /* revalidate */
+	coda_dentry_revalidate, /* revalidate */
 	NULL, /* hash */
 	NULL,
-	coda_dentry_delete
+	NULL,
 };
 
 struct inode_operations coda_dir_inode_operations =
@@ -74,7 +78,7 @@
 	coda_permission,        /* permission */
 	NULL,                   /* smap */
 	NULL,                   /* update page */
-        NULL                    /* revalidate */
+        coda_revalidate_inode   /* revalidate */
 };
 
 struct file_operations coda_dir_operations = {
@@ -117,7 +121,6 @@
 	}
 
 	dircnp = ITOC(dir);
-	CHECK_CNODE(dircnp);
 
 	if ( length > CFS_MAXNAMLEN ) {
 	        printk("name too long: lookup, %s (%*s)\n", 
@@ -141,9 +144,13 @@
 			     (const char *)name, length, &type, &resfid);
 
 	res_inode = NULL;
-	if (!error || (error == -CFS_NOCACHE) ) {
-		if (error == -CFS_NOCACHE) 
+	if (!error) {
+		if (type & CFS_NOCACHE) {
+			type &= (~CFS_NOCACHE);
+			CDEBUG(D_INODE, "dropme set for %s\n", 
+			       coda_f2s(&resfid));
 			dropme = 1;
+		}
 	    	error = coda_cnode_make(&res_inode, &resfid, dir->i_sb);
 		if (error)
 			return -error;
@@ -152,7 +159,7 @@
 		       coda_f2s(&dircnp->c_fid), length, name, error);
 		return error;
 	}
-	CDEBUG(D_INODE, "lookup: %s is (%s) type %d result %d, dropme %d\n",
+	CDEBUG(D_INODE, "lookup: %s is (%s), type %d result %d, dropme %d\n",
 	       name, coda_f2s(&resfid), type, error, dropme);
 
 exit:
@@ -228,7 +235,6 @@
         CHECK_CNODE(dircnp);
 
         if ( length > CFS_MAXNAMLEN ) {
-		char str[50];
 		printk("name too long: create, %s(%s)\n", 
 		       coda_f2s(&dircnp->c_fid), name);
 		return -ENAMETOOLONG;
@@ -238,7 +244,6 @@
 				0, mode, &newfid, &attrs);
 
         if ( error ) {
-		char str[50];
 		CDEBUG(D_INODE, "create: %s, result %d\n",
 		       coda_f2s(&newfid), error); 
 		d_drop(de);
@@ -321,7 +326,6 @@
         const char * name = de->d_name.name;
 	int len = de->d_name.len;
         struct coda_inode_info *dir_cnp, *cnp;
-	char str[50];
 	int error;
 
         ENTRY;
@@ -408,7 +412,6 @@
         int error;
 	const char *name = de->d_name.name;
 	int len = de->d_name.len;
-	char fidstr[50];
 
 	ENTRY;
 
@@ -781,3 +784,82 @@
         CODA_FREE(buff, size);
         return error;
 }
+
+int coda_dentry_revalidate(struct dentry *de)
+{
+	int valid = 1;
+	struct inode *inode = de->d_inode;
+	struct coda_inode_info *cii;
+	ENTRY;
+
+	if (inode) {
+		if (is_bad_inode(inode))
+			return 0;
+		cii = ITOC(de->d_inode);
+		if (cii->c_flags & C_PURGE) 
+			valid = 0;
+	}
+	return valid ||  coda_isroot(de->d_inode);
+}
+
+
+static int coda_refresh_inode(struct dentry *dentry)
+{
+	struct coda_vattr attr;
+	int error;
+	int old_mode;
+	ino_t old_ino;
+	struct inode *inode = dentry->d_inode;
+	struct coda_inode_info *cii = ITOC(inode);
+	
+	ENTRY;
+	error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
+	if ( error ) { 
+		make_bad_inode(inode);
+		return -EIO;
+	}
+
+	/* this baby may be lost if:
+	   - it's type changed
+            - it's ino changed 
+	*/
+	old_mode = inode->i_mode;
+	old_ino = inode->i_ino;
+	coda_vattr_to_iattr(inode, &attr);
+
+	if ((inode->i_ino != old_ino) ||
+	    ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT))) {
+		make_bad_inode(inode);
+		inode->i_mode = old_mode;
+		return -EIO;
+	}
+	
+	cii->c_flags &= ~C_VATTR;
+	return 0;
+}
+
+
+/*
+ * This is called when we want to check if the inode has
+ * changed on the server.  Coda makes this easy since the
+ * cache manager Venus issues a downcall to the kernel when this 
+ * happens 
+ */
+
+int coda_revalidate_inode(struct dentry *dentry)
+{
+	int error = 0;
+	struct coda_inode_info *cii = ITOC(dentry->d_inode);
+
+	ENTRY;
+	CDEBUG(D_INODE, "revalidating: %*s/%*s\n", 
+	       dentry->d_name.len, dentry->d_name.name,
+	       dentry->d_parent->d_name.len, dentry->d_parent->d_name.name);
+
+	if ( cii->c_flags & (C_VATTR | C_PURGE )) {
+		error = coda_refresh_inode(dentry);
+	}
+
+	return error;
+}
+
diff -u --recursive --new-file v2.1.89/linux/fs/coda/file.c linux/fs/coda/file.c
--- v2.1.89/linux/fs/coda/file.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/file.c	Tue Mar 17 21:19:05 1998
@@ -30,7 +30,7 @@
 static ssize_t coda_file_write(struct file *f, const char *buf, size_t count, loff_t *off);
 static int coda_file_mmap(struct file * file, struct vm_area_struct * vma);
 
-/* exported from this file */
+/* also exported from this file (used for dirs) */
 int coda_fsync(struct file *, struct dentry *dentry);
 
 struct inode_operations coda_file_inode_operations = {
@@ -43,7 +43,7 @@
 	NULL,			/* mkdir */
 	NULL,			/* rmdir */
 	NULL,			/* mknod */
-	NULL,		/* rename */
+	NULL,		        /* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
 	coda_readpage,    	/* readpage */
@@ -53,7 +53,7 @@
         coda_permission,        /* permission */
 	NULL,                   /* smap */
 	NULL,                   /* update page */
-        NULL                    /* revalidate */
+        coda_revalidate_inode   /* revalidate */
 };
 
 struct file_operations coda_file_operations = {
@@ -74,41 +74,47 @@
 };
 
 /*  File file operations */
-static int coda_readpage(struct file * file, struct page * page)
+static int coda_readpage(struct file * coda_file, struct page * page)
 {
-	struct dentry *de = file->f_dentry;
-	struct inode *inode = de->d_inode;
+	struct dentry *de = coda_file->f_dentry;
+	struct inode *coda_inode = de->d_inode;
 	struct dentry cont_dentry;
-        struct inode *cont_inode;
-        struct coda_inode_info *cnp;
+	struct file cont_file;
+        struct coda_inode_info *cii;
 
         ENTRY;
         
-        cnp = ITOC(inode);
-        CHECK_CNODE(cnp);
+        cii = ITOC(coda_inode);
 
-        if ( ! cnp->c_ovp ) {
-            printk("coda_readpage: no open inode for ino %ld\n", inode->i_ino);
+        if ( ! cii->c_ovp ) {
+		printk("coda_readpage: no open inode for ino %ld, %s\n", 
+		       coda_inode->i_ino, de->d_name.name);
                 return -ENXIO;
         }
+       
+        coda_prepare_openfile(coda_inode, coda_file, cii->c_ovp,
+			      &cont_file, &cont_dentry);
 
-        cont_inode = cnp->c_ovp;
-	cont_dentry.d_inode = cont_inode;
-
-        CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", inode->i_ino, cont_inode->i_ino, page->offset);
+        CDEBUG(D_INODE, "coda ino: %ld, cached ino %ld, page offset: %lx\n", 
+	       coda_inode->i_ino, cii->c_ovp->i_ino, page->offset);
 
-        generic_readpage(&cont_dentry, page);
+        generic_readpage(&cont_file, page);
         EXIT;
         return 0;
 }
 
 static int coda_file_mmap(struct file * file, struct vm_area_struct * vma)
 {
-        struct coda_inode_info *cnp;
-	cnp = ITOC(file->f_dentry->d_inode);
-	cnp->c_mmcount++;
+        struct coda_inode_info *cii;
+	int res;
+
+        ENTRY;
+	cii = ITOC(file->f_dentry->d_inode);
+	cii->c_mmcount++;
   
-	return generic_file_mmap(file, vma);
+	res =generic_file_mmap(file, vma);
+	EXIT;
+	return res;
 }
 
 static ssize_t coda_file_read(struct file *coda_file, char *buff, 
@@ -120,7 +126,6 @@
         struct file  cont_file;
 	struct dentry cont_dentry;
         int result = 0;
-
         ENTRY;
 
         cnp = ITOC(coda_inode);
diff -u --recursive --new-file v2.1.89/linux/fs/coda/inode.c linux/fs/coda/inode.c
--- v2.1.89/linux/fs/coda/inode.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/coda/inode.c	Tue Mar 17 21:19:05 1998
@@ -0,0 +1,369 @@
+/*
+ * Super block/filesystem wide operations
+ *
+ * Copryright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and 
+ * Michael Callahan <callahan@maths.ox.ac.uk> 
+ * 
+ * Rewritten for Linux 2.1.  Peter Braam <braam@cs.cmu.edu>
+ * Copyright (C) Carnegie Mellon University
+ */
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/unistd.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/locks.h>
+#include <linux/string.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+#include <asm/segment.h>
+
+#include <linux/coda.h>
+#include <linux/coda_linux.h>
+#include <linux/coda_psdev.h>
+#include <linux/coda_fs_i.h>
+#include <linux/coda_cache.h>
+
+
+/* VFS super_block ops */
+static struct super_block *coda_read_super(struct super_block *, void *, int);
+static void coda_read_inode(struct inode *);
+static int  coda_notify_change(struct dentry *dentry, struct iattr *attr);
+static void coda_put_inode(struct inode *);
+static void coda_delete_inode(struct inode *);
+static void coda_put_super(struct super_block *);
+static int coda_statfs(struct super_block *sb, struct statfs *buf, 
+		       int bufsiz);
+
+/* helper functions */
+static inline struct vcomm *coda_psinode2vcomm(struct inode *inode);
+static int coda_get_psdev(void *, struct inode **);
+static struct coda_sb_info *coda_psinode2sbi(struct inode *inode);
+
+/* exported operations */
+struct super_operations coda_super_operations =
+{
+	coda_read_inode,        /* read_inode */
+	NULL,                   /* write_inode */
+	coda_put_inode,	        /* put_inode */
+	coda_delete_inode,      /* delete_inode */
+	coda_notify_change,	/* notify_change */
+	coda_put_super,	        /* put_super */
+	NULL,			/* write_super */
+	coda_statfs,   		/* statfs */
+	NULL			/* remount_fs */
+};
+
+/*
+ * globals
+ */
+struct coda_sb_info coda_super_info[MAX_CODADEVS];
+
+
+static struct super_block * coda_read_super(struct super_block *sb, 
+					    void *data, int silent)
+{
+        struct inode *psdev = 0, *root = 0; 
+	struct coda_sb_info *sbi = NULL;
+	struct vcomm *vc = NULL;
+        ViceFid fid;
+	kdev_t dev = sb->s_dev;
+        int error;
+
+	ENTRY;
+        MOD_INC_USE_COUNT; 
+        if (coda_get_psdev(data, &psdev))
+                goto error;
+
+        vc = coda_psinode2vcomm(psdev);
+        if ( !vc )
+	        goto error;
+	vc->vc_sb = sb;
+	vc->vc_inuse = 1;
+
+	sbi = coda_psinode2sbi(psdev);
+	if ( !sbi )
+	        goto error;
+        sbi->sbi_psdev = psdev;
+	sbi->sbi_vcomm = vc;
+	INIT_LIST_HEAD(&(sbi->sbi_cchead));
+	INIT_LIST_HEAD(&(sbi->sbi_volroothead));
+
+        lock_super(sb);
+        sb->u.generic_sbp = sbi;
+        sb->s_blocksize = 1024;	/* XXXXX  what do we put here?? */
+        sb->s_blocksize_bits = 10;
+        sb->s_magic = CODA_SUPER_MAGIC;
+        sb->s_dev = dev;
+        sb->s_op = &coda_super_operations;
+
+	/* get root fid from Venus: this needs the root inode */
+	error = venus_rootfid(sb, &fid);
+	if ( error ) {
+	        printk("coda_read_super: coda_get_rootfid failed with %d\n",
+		       error);
+		sb->s_dev = 0;
+	        unlock_super(sb);
+		goto error;
+	}	  
+	printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
+	
+	/* make root inode */
+        error = coda_cnode_make(&root, &fid, sb);
+        if ( error || !root ) {
+	    printk("Failure of coda_cnode_make for root: error %d\n", error);
+	    sb->s_dev = 0;
+	    unlock_super(sb);
+	    goto error;
+	} 
+
+	printk("coda_read_super: rootinode is %ld dev %d\n", 
+	       root->i_ino, root->i_dev);
+	sbi->sbi_root = root;
+	sb->s_root = d_alloc_root(root, NULL);
+	unlock_super(sb);
+	EXIT;  
+        return sb;
+
+error:
+EXIT;  
+	MOD_DEC_USE_COUNT;
+	if (sbi) {
+		sbi->sbi_vcomm = NULL;
+		sbi->sbi_root = NULL;
+	}
+	if ( vc ) {
+		vc->vc_sb = NULL;
+		vc->vc_inuse = 0;
+	}
+        if (root) {
+                iput(root);
+        }
+        sb->s_dev = 0;
+        return NULL;
+}
+
+static void coda_put_super(struct super_block *sb)
+{
+        struct coda_sb_info *sb_info;
+
+        ENTRY;
+
+        lock_super(sb);
+
+        sb->s_dev = 0;
+	coda_cache_clear_all(sb);
+	sb_info = coda_sbp(sb);
+	sb_info->sbi_vcomm->vc_inuse = 0;
+	sb_info->sbi_vcomm->vc_sb = NULL;
+	printk("Coda: Bye bye.\n");
+	memset(sb_info, 0, sizeof(* sb_info));
+
+        unlock_super(sb);
+        MOD_DEC_USE_COUNT;
+	EXIT;
+}
+
+/* all filling in of inodes postponed until lookup */
+static void coda_read_inode(struct inode *inode)
+{
+	struct coda_inode_info *cii;
+	ENTRY;
+	cii = ITOC(inode);
+	cii->c_magic = 0;
+	return;
+}
+
+static void coda_put_inode(struct inode *in) 
+{
+	ENTRY;
+
+        CDEBUG(D_INODE,"ino: %ld, count %d\n", in->i_ino, in->i_count);
+
+	if ( in->i_count == 1 ) 
+		in->i_nlink = 0;
+		
+}
+
+static void coda_delete_inode(struct inode *inode)
+{
+        struct coda_inode_info *cii;
+        struct inode *open_inode;
+
+        ENTRY;
+        CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", 
+	       inode->i_ino, inode->i_count);        
+
+        cii = ITOC(inode);
+	if ( inode->i_ino == CTL_INO || cii->c_magic != CODA_CNODE_MAGIC ) {
+	        clear_inode(inode);
+		return;
+	}
+
+
+	if ( coda_fid_is_volroot(&cii->c_fid) )
+		list_del(&cii->c_volrootlist);
+
+        open_inode = cii->c_ovp;
+        if ( open_inode ) {
+                CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n",  
+		       open_inode->i_ino,  open_inode->i_count);
+                cii->c_ovp = NULL;
+                iput(open_inode);
+        }
+	
+	coda_cache_clear_inode(inode);
+
+	inode->u.generic_ip = NULL;
+        clear_inode(inode);
+	EXIT;
+}
+
+static int  coda_notify_change(struct dentry *de, struct iattr *iattr)
+{
+	struct inode *inode = de->d_inode;
+        struct coda_inode_info *cii;
+        struct coda_vattr vattr;
+        int error;
+	
+	ENTRY;
+        memset(&vattr, 0, sizeof(vattr)); 
+        cii = ITOC(inode);
+        CHECK_CNODE(cii);
+
+        coda_iattr_to_vattr(iattr, &vattr);
+        vattr.va_type = C_VNON; /* cannot set type */
+	CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode);
+
+        error = venus_setattr(inode->i_sb, &cii->c_fid, &vattr);
+
+        if ( !error ) {
+	        coda_vattr_to_iattr(inode, &vattr); 
+		coda_cache_clear_inode(inode);
+        }
+	CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", 
+	       inode->i_mode, error);
+
+	EXIT;
+        return error;
+}
+
+/*  we need _something_ for this routine. Let's mimic AFS */
+static int coda_statfs(struct super_block *sb, struct statfs *buf, 
+		       int bufsiz)
+{
+	struct statfs tmp;
+
+	tmp.f_type = CODA_SUPER_MAGIC;
+	tmp.f_bsize = 1024;
+	tmp.f_blocks = 9000000;
+	tmp.f_bfree = 9000000;
+	tmp.f_bavail = 9000000 ;
+	tmp.f_files = 9000000;
+	tmp.f_ffree = 9000000;
+	tmp.f_namelen = 0;
+	copy_to_user(buf, &tmp, bufsiz);
+	return 0; 
+}
+
+
+/* init_coda: used by filesystems.c to register coda */
+
+struct file_system_type coda_fs_type = {
+   "coda", 0, coda_read_super, NULL
+};
+
+int init_coda_fs(void)
+{
+	return register_filesystem(&coda_fs_type);
+}
+
+/* MODULE stuff is in psdev.c */
+
+/*  helpers */
+static inline struct vcomm *coda_psinode2vcomm(struct inode *inode) 
+{
+        
+	unsigned int minor = MINOR(inode->i_rdev);
+	CDEBUG(D_PSDEV,"minor %d\n", minor);
+	if ( minor < MAX_CODADEVS ) 
+	      return &(psdev_vcomm[minor]);
+	else
+	      return NULL;
+}
+
+static struct coda_sb_info *coda_psinode2sbi(struct inode *inode) 
+{
+	unsigned int minor = MINOR(inode->i_rdev);
+
+	CDEBUG(D_PSDEV,"minor %d\n", minor);
+	if ( (minor >= 0) && (minor < MAX_CODADEVS)) 
+	        return &(coda_super_info[minor]);
+	else
+	        return NULL;
+}
+
+/* name lookup for psdev passed in by mount */
+static int coda_get_psdev(void *data, struct inode **res_dev)
+{
+        char **psdev_path;
+        struct inode *psdev = 0;
+	struct dentry *ent=NULL;
+
+ 
+	if ( ! data ) { 
+		printk("coda_get_psdev: no data!\n");
+		return 1;
+	} 
+
+	psdev_path = data;
+        ent = namei((char *) *psdev_path);
+        if (IS_ERR(ent)) {
+		printk("namei error %ld for %d\n", PTR_ERR(ent), 
+		       (int) psdev_path);
+		return 1;
+        }
+	psdev = ent->d_inode;
+
+        if (!S_ISCHR(psdev->i_mode)) {
+		printk("not a character device\n");
+		return 1;
+        }
+	CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", 
+	       MAJOR(psdev->i_rdev), 
+	       MINOR(psdev->i_rdev), psdev->i_count);
+        
+        if (MAJOR(psdev->i_rdev) != CODA_PSDEV_MAJOR) {
+		printk("device %d not a Coda PSDEV device\n", 
+		       MAJOR(psdev->i_rdev));
+		return 1;
+        }
+
+        if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) { 
+		printk("minor %d not an allocated Coda PSDEV\n", 
+		       psdev->i_rdev);
+		return 1;
+        }
+
+        if (psdev->i_count < 1) {
+		printk("coda device minor %d not open (i_count = %d)\n", 
+		       MINOR(psdev->i_rdev), psdev->i_count);
+		return 1;
+        }
+        
+        *res_dev = psdev;
+	EXIT;  
+        return 0;
+}
diff -u --recursive --new-file v2.1.89/linux/fs/coda/psdev.c linux/fs/coda/psdev.c
--- v2.1.89/linux/fs/coda/psdev.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/psdev.c	Tue Mar 17 21:19:05 1998
@@ -65,7 +65,7 @@
 /* statistics */
 struct coda_upcallstats coda_callstats;
 int           coda_hard = 0;  /* introduces a timeout on upcalls */
-unsigned long coda_timeout = 10; /* .. secs, then signals will dequeue */
+unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
 extern struct coda_sb_info coda_super_info[MAX_CODADEVS];
 struct vcomm psdev_vcomm[MAX_CODADEVS];
 
@@ -447,7 +447,7 @@
 int init_module(void)
 {
   int status;
-  printk(KERN_INFO "Coda Kernel/User communications module 1.0\n");
+  printk(KERN_INFO "Coda Kernel/User communications module 2.0\n");
 
   status = init_coda_psdev();
   if ( status ) {
diff -u --recursive --new-file v2.1.89/linux/fs/coda/super.c linux/fs/coda/super.c
--- v2.1.89/linux/fs/coda/super.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/super.c	Wed Dec 31 16:00:00 1969
@@ -1,370 +0,0 @@
-/*
- * Super block/filesystem wide operations
- *
- * Copryright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and 
- * Michael Callahan <callahan@maths.ox.ac.uk> 
- * 
- * Rewritten for Linux 2.1.?? Peter Braam <braam@cs.cmu.edu>
- * Copyright (C) Carnegie Mellon University
- */
-
-#define __NO_VERSION__
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/locks.h>
-#include <linux/unistd.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-
-#include <linux/fs.h>
-#include <linux/stat.h>
-#include <linux/errno.h>
-#include <linux/locks.h>
-#include <linux/string.h>
-#include <asm/uaccess.h>
-#include <linux/vmalloc.h>
-#include <asm/segment.h>
-
-#include <linux/coda.h>
-#include <linux/coda_linux.h>
-#include <linux/coda_psdev.h>
-#include <linux/coda_fs_i.h>
-#include <linux/coda_cache.h>
-
-
-/* VFS super_block ops */
-static struct super_block *coda_read_super(struct super_block *, void *, int);
-static void coda_read_inode(struct inode *);
-static int  coda_notify_change(struct dentry *dentry, struct iattr *attr);
-static void coda_put_inode(struct inode *);
-static void coda_delete_inode(struct inode *);
-static void coda_put_super(struct super_block *);
-static int coda_statfs(struct super_block *sb, struct statfs *buf, 
-		       int bufsiz);
-
-/* helper functions */
-static inline struct vcomm *coda_psinode2vcomm(struct inode *inode);
-static int coda_get_psdev(void *, struct inode **);
-static struct coda_sb_info *coda_psinode2sbi(struct inode *inode);
-
-/* exported operations */
-struct super_operations coda_super_operations =
-{
-	coda_read_inode,        /* read_inode */
-	NULL,                   /* write_inode */
-	coda_put_inode,	        /* put_inode */
-	coda_delete_inode,      /* delete_inode */
-	coda_notify_change,	/* notify_change */
-	coda_put_super,	        /* put_super */
-	NULL,			/* write_super */
-	coda_statfs,   		/* statfs */
-	NULL			/* remount_fs */
-};
-
-/*
- * globals
- */
-struct coda_sb_info coda_super_info[MAX_CODADEVS];
-
-
-static struct super_block * coda_read_super(struct super_block *sb, 
-					    void *data, int silent)
-{
-        struct inode *psdev = 0, *root = 0; 
-	struct coda_sb_info *sbi = NULL;
-	struct vcomm *vc = NULL;
-        ViceFid fid;
-	kdev_t dev = sb->s_dev;
-        int error;
-	char str[50];
-
-	ENTRY;
-        MOD_INC_USE_COUNT; 
-        if (coda_get_psdev(data, &psdev))
-                goto error;
-
-        vc = coda_psinode2vcomm(psdev);
-        if ( !vc )
-	        goto error;
-	vc->vc_sb = sb;
-	vc->vc_inuse = 1;
-
-	sbi = coda_psinode2sbi(psdev);
-	if ( !sbi )
-	        goto error;
-        sbi->sbi_psdev = psdev;
-	sbi->sbi_vcomm = vc;
-	INIT_LIST_HEAD(&(sbi->sbi_cchead));
-	INIT_LIST_HEAD(&(sbi->sbi_volroothead));
-
-        lock_super(sb);
-        sb->u.generic_sbp = sbi;
-        sb->s_blocksize = 1024;	/* XXXXX  what do we put here?? */
-        sb->s_blocksize_bits = 10;
-        sb->s_magic = CODA_SUPER_MAGIC;
-        sb->s_dev = dev;
-        sb->s_op = &coda_super_operations;
-
-	/* get root fid from Venus: this needs the root inode */
-	error = venus_rootfid(sb, &fid);
-	if ( error ) {
-	        printk("coda_read_super: coda_get_rootfid failed with %d\n",
-		       error);
-		sb->s_dev = 0;
-	        unlock_super(sb);
-		goto error;
-	}	  
-	printk("coda_read_super: rootfid is %s\n", coda_f2s(&fid));
-	
-	/* make root inode */
-        error = coda_cnode_make(&root, &fid, sb);
-        if ( error || !root ) {
-	    printk("Failure of coda_cnode_make for root: error %d\n", error);
-	    sb->s_dev = 0;
-	    unlock_super(sb);
-	    goto error;
-	} 
-
-	printk("coda_read_super: rootinode is %ld dev %d\n", 
-	       root->i_ino, root->i_dev);
-	sbi->sbi_root = root;
-	sb->s_root = d_alloc_root(root, NULL);
-	unlock_super(sb);
-	EXIT;  
-        return sb;
-
-error:
-EXIT;  
-	MOD_DEC_USE_COUNT;
-	if (sbi) {
-		sbi->sbi_vcomm = NULL;
-		sbi->sbi_root = NULL;
-	}
-	if ( vc ) {
-		vc->vc_sb = NULL;
-		vc->vc_inuse = 0;
-	}
-        if (root) {
-                iput(root);
-        }
-        sb->s_dev = 0;
-        return NULL;
-}
-
-static void coda_put_super(struct super_block *sb)
-{
-        struct coda_sb_info *sb_info;
-
-        ENTRY;
-
-        lock_super(sb);
-
-        sb->s_dev = 0;
-	coda_cache_clear_all(sb);
-	sb_info = coda_sbp(sb);
-	sb_info->sbi_vcomm->vc_inuse = 0;
-	sb_info->sbi_vcomm->vc_sb = NULL;
-	printk("Coda: Bye bye.\n");
-	memset(sb_info, 0, sizeof(* sb_info));
-
-        unlock_super(sb);
-        MOD_DEC_USE_COUNT;
-	EXIT;
-}
-
-/* all filling in of inodes postponed until lookup */
-static void coda_read_inode(struct inode *inode)
-{
-	struct coda_inode_info *cnp;
-	ENTRY;
-	cnp = ITOC(inode);
-	cnp->c_magic = 0;
-	return;
-}
-
-static void coda_put_inode(struct inode *in) 
-{
-	ENTRY;
-
-        CDEBUG(D_INODE,"ino: %ld, count %d\n", in->i_ino, in->i_count);
-
-	if ( in->i_count == 1 ) 
-		in->i_nlink = 0;
-		
-}
-
-static void coda_delete_inode(struct inode *inode)
-{
-        struct coda_inode_info *cnp;
-        struct inode *open_inode;
-
-        ENTRY;
-        CDEBUG(D_SUPER, " inode->ino: %ld, count: %d\n", 
-	       inode->i_ino, inode->i_count);        
-
-        cnp = ITOC(inode);
-	if ( inode->i_ino == CTL_INO || cnp->c_magic != CODA_CNODE_MAGIC ) {
-	        clear_inode(inode);
-		return;
-	}
-
-
-	if ( coda_fid_is_volroot(&cnp->c_fid) )
-		list_del(&cnp->c_volrootlist);
-
-        open_inode = cnp->c_ovp;
-        if ( open_inode ) {
-                CDEBUG(D_SUPER, "DELINO cached file: ino %ld count %d.\n",  
-		       open_inode->i_ino,  open_inode->i_count);
-                cnp->c_ovp = NULL;
-                iput(open_inode);
-        }
-	
-	coda_cache_clear_cnp(cnp);
-
-	inode->u.generic_ip = NULL;
-        clear_inode(inode);
-	EXIT;
-}
-
-static int  coda_notify_change(struct dentry *de, struct iattr *iattr)
-{
-	struct inode *inode = de->d_inode;
-        struct coda_inode_info *cnp;
-        struct coda_vattr vattr;
-        int error;
-	
-	ENTRY;
-        memset(&vattr, 0, sizeof(vattr)); 
-        cnp = ITOC(inode);
-        CHECK_CNODE(cnp);
-
-        coda_iattr_to_vattr(iattr, &vattr);
-        vattr.va_type = C_VNON; /* cannot set type */
-	CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode);
-
-        error = venus_setattr(inode->i_sb, &cnp->c_fid, &vattr);
-
-        if ( !error ) {
-	        coda_vattr_to_iattr(inode, &vattr); 
-		coda_cache_clear_cnp(cnp);
-        }
-	CDEBUG(D_SUPER, "inode.i_mode %o, error %d\n", 
-	       inode->i_mode, error);
-
-	EXIT;
-        return error;
-}
-
-/*  we need _something_ for this routine. Let's mimic AFS */
-static int coda_statfs(struct super_block *sb, struct statfs *buf, 
-		       int bufsiz)
-{
-	struct statfs tmp;
-
-	tmp.f_type = CODA_SUPER_MAGIC;
-	tmp.f_bsize = 1024;
-	tmp.f_blocks = 9000000;
-	tmp.f_bfree = 9000000;
-	tmp.f_bavail = 9000000 ;
-	tmp.f_files = 9000000;
-	tmp.f_ffree = 9000000;
-	tmp.f_namelen = 0;
-	copy_to_user(buf, &tmp, bufsiz);
-	return 0; 
-}
-
-
-/* init_coda: used by filesystems.c to register coda */
-
-struct file_system_type coda_fs_type = {
-   "coda", 0, coda_read_super, NULL
-};
-
-int init_coda_fs(void)
-{
-	return register_filesystem(&coda_fs_type);
-}
-
-/* MODULE stuff is in psdev.c */
-
-/*  helpers */
-static inline struct vcomm *coda_psinode2vcomm(struct inode *inode) 
-{
-        
-	unsigned int minor = MINOR(inode->i_rdev);
-	CDEBUG(D_PSDEV,"minor %d\n", minor);
-	if ( minor < MAX_CODADEVS ) 
-	      return &(psdev_vcomm[minor]);
-	else
-	      return NULL;
-}
-
-static struct coda_sb_info *coda_psinode2sbi(struct inode *inode) 
-{
-	unsigned int minor = MINOR(inode->i_rdev);
-
-	CDEBUG(D_PSDEV,"minor %d\n", minor);
-	if ( (minor >= 0) && (minor < MAX_CODADEVS)) 
-	        return &(coda_super_info[minor]);
-	else
-	        return NULL;
-}
-
-/* name lookup for psdev passed in by mount */
-static int coda_get_psdev(void *data, struct inode **res_dev)
-{
-        char **psdev_path;
-        struct inode *psdev = 0;
-	struct dentry *ent=NULL;
-
- 
-	if ( ! data ) { 
-		printk("coda_get_psdev: no data!\n");
-		return 1;
-	} 
-
-	psdev_path = data;
-        ent = namei((char *) *psdev_path);
-        if (IS_ERR(ent)) {
-		printk("namei error %ld for %d\n", PTR_ERR(ent), 
-		       (int) psdev_path);
-		return 1;
-        }
-	psdev = ent->d_inode;
-
-        if (!S_ISCHR(psdev->i_mode)) {
-		printk("not a character device\n");
-		return 1;
-        }
-	CDEBUG(D_PSDEV,"major %d, minor %d, count %d\n", 
-	       MAJOR(psdev->i_rdev), 
-	       MINOR(psdev->i_rdev), psdev->i_count);
-        
-        if (MAJOR(psdev->i_rdev) != CODA_PSDEV_MAJOR) {
-		printk("device %d not a Coda PSDEV device\n", 
-		       MAJOR(psdev->i_rdev));
-		return 1;
-        }
-
-        if (MINOR(psdev->i_rdev) >= MAX_CODADEVS) { 
-		printk("minor %d not an allocated Coda PSDEV\n", 
-		       psdev->i_rdev);
-		return 1;
-        }
-
-        if (psdev->i_count < 1) {
-		printk("coda device minor %d not open (i_count = %d)\n", 
-		       MINOR(psdev->i_rdev), psdev->i_count);
-		return 1;
-        }
-        
-        *res_dev = psdev;
-	EXIT;  
-        return 0;
-}
diff -u --recursive --new-file v2.1.89/linux/fs/coda/upcall.c linux/fs/coda/upcall.c
--- v2.1.89/linux/fs/coda/upcall.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/coda/upcall.c	Tue Mar 17 21:19:05 1998
@@ -500,7 +500,6 @@
         union outputArgs *outp;
         int insize, outsize, error;
 	int iocsize;
-	char str[50];
 
 	insize = VC_MAXMSGSIZE;
 	UPARG(CFS_IOCTL);
@@ -587,7 +586,6 @@
 static inline void coda_waitfor_upcall(struct vmsg *vmp)
 {
 	struct wait_queue	wait = { current, NULL };
-	old_sigset_t pending;
 
 	vmp->vm_posttime = jiffies;
 
@@ -608,13 +606,9 @@
 		if ( jiffies > vmp->vm_posttime + coda_timeout * HZ )
 			break; 
 				
-		spin_lock_irq(&current->sigmask_lock);
-		pending = current->blocked.sig[0] & current->signal.sig[0];
-		spin_unlock_irq(&current->sigmask_lock);
-
 		/* if this process really wants to die, let it go */
-		if ( sigismember(&pending, SIGKILL) ||
-		     sigismember(&pending, SIGINT) )
+		if ( sigismember(&current->signal, SIGKILL) ||
+		     sigismember(&current->signal, SIGINT) )
 			break;
 		else 
 			schedule();
@@ -765,10 +759,14 @@
  *                  This call is a result of token expiration.
  *
  * The next arise as the result of callbacks on a file or directory.
- * CFS_ZAPDIR    -- flush the attributes for the dir from its cnode.
- *                  Zap all children of this directory from the namecache.
  * CFS_ZAPFILE   -- flush the cached attributes for a file.
- * CFS_ZAPVNODE  -- intended to be a zapfile for just one cred. Not used?
+
+ * CFS_ZAPDIR    -- flush the attributes for the dir and
+ *                  force a new lookup for all the children
+                    of this dir.
+
+ * CFS_ZAPVNODE  -- intended to be a zapfile for just one cred. 
+                    Not used?
  *
  * The next is a result of Venus detecting an inconsistent file.
  * CFS_PURGEFID  -- flush the attribute for the file
@@ -803,53 +801,48 @@
 	    return(0);
     }
     case CFS_ZAPDIR : {
+	    struct inode *inode;
 	    ViceFid *fid = &out->cfs_zapdir.CodaFid;
-	    char str[50];
 	    if ( !fid ) {
 		    printk("ZAPDIR: Null fid\n");
 		    return 0;
 	    }
 	    CDEBUG(D_DOWNCALL, "zapdir: fid = %s\n", coda_f2s(fid));
 	    clstats(CFS_ZAPDIR);
-	    coda_zapfid(fid, sb, C_ZAPDIR);
-	    return(0);
-    }
-    case CFS_ZAPVNODE : {
-	    ViceFid *fid = &out->cfs_zapvnode.VFid;
-	    char str[50];
-	    struct coda_cred *cred = &out->cfs_zapvnode.cred;
-	    if ( !fid || !cred ) {
-		    printk("ZAPVNODE: Null fid or cred\n");
-		    return 0;
-	    }
-	    CDEBUG(D_DOWNCALL, "zapvnode: fid = %s\n", coda_f2s(fid));
-	    coda_zapfid(fid, sb, C_ZAPFID);
-	    coda_cache_clear_cred(sb, cred);
-	    clstats(CFS_ZAPVNODE);
+	    inode = coda_fid_to_inode(fid, sb);
+	    coda_flag_inode(inode, C_VATTR);
+	    coda_cache_clear_inode(inode);
+	    coda_flag_alias_children(inode, C_PURGE);
 	    return(0);
     }
+
+    case CFS_ZAPVNODE : 
     case CFS_ZAPFILE : {
+	    struct inode *inode;
 	    struct ViceFid *fid = &out->cfs_zapfile.CodaFid;
-	    char str[50];
 	    clstats(CFS_ZAPFILE);
 	    if ( !fid ) {
 		    printk("ZAPFILE: Null fid\n");
 		    return 0;
 	    }
 	    CDEBUG(D_DOWNCALL, "zapfile: fid = %s\n", coda_f2s(fid));
-	    coda_zapfid(fid, sb, C_ZAPFID);
+	    inode = coda_fid_to_inode(fid, sb);
+	    coda_flag_inode(inode, C_VATTR);
+	    coda_cache_clear_inode(inode);
 	    return 0;
     }
     case CFS_PURGEFID : {
+	    struct inode *inode;
 	    ViceFid *fid = &out->cfs_purgefid.CodaFid;
-	    char str[50];
 	    if ( !fid ) {
 		    printk("PURGEFID: Null fid\n");
 		    return 0;
 	    }
 	    CDEBUG(D_DOWNCALL, "purgefid: fid = %s\n", coda_f2s(fid));
 	    clstats(CFS_PURGEFID);
-	    coda_zapfid(fid, sb, C_ZAPDIR);
+	    inode = coda_fid_to_inode(fid, sb);
+	    coda_flag_inode(inode, C_PURGE);
+	    coda_cache_clear_inode(inode);
 	    return 0;
     }
     case CFS_REPLACE : {
diff -u --recursive --new-file v2.1.89/linux/fs/devices.c linux/fs/devices.c
--- v2.1.89/linux/fs/devices.c	Tue Aug 12 17:15:15 1997
+++ linux/fs/devices.c	Tue Mar 10 14:43:13 1998
@@ -6,6 +6,7 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *
  *  Added kerneld support: Jacques Gelinas and Bjorn Ekwall
+ *  (changed to kmod)
  */
 
 #include <linux/config.h>
@@ -16,12 +17,12 @@
 #include <linux/stat.h>
 #include <linux/fcntl.h>
 #include <linux/errno.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 
 #include <linux/tty.h>
 
-/* serial module kerneld load support */
+/* serial module kmod load support */
 struct tty_driver *get_tty_driver(kdev_t device);
 #define isa_tty_dev(ma)	(ma == TTY_MAJOR || ma == TTYAUX_MAJOR)
 #define need_serial(ma,mi) (get_tty_driver(MKDEV(ma,mi)) == NULL)
@@ -74,12 +75,12 @@
 	struct file_operations *ret = NULL;
 
 	if (major < maxdev){
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 		/*
 		 * I do get request for device 0. I have no idea why. It happen
 		 * at shutdown time for one. Without the following test, the
 		 * kernel will happily trigger a request_module() which will
-		 * trigger kerneld and modprobe for nothing (since there
+		 * trigger kmod and modprobe for nothing (since there
 		 * is no device with major number == 0. And furthermore
 		 * it locks the reboot process :-(
 		 *
@@ -87,7 +88,7 @@
 		 *
 		 * A. Haritsis <ah@doc.ic.ac.uk>: fix for serial module
 		 *  though we need the minor here to check if serial dev,
-		 *  we pass only the normal major char dev to kerneld 
+		 *  we pass only the normal major char dev to kmod 
 		 *  as there is no other loadable dev on these majors
 		 */
 		if ((isa_tty_dev(major) && need_serial(major,minor)) ||
diff -u --recursive --new-file v2.1.89/linux/fs/exec.c linux/fs/exec.c
--- v2.1.89/linux/fs/exec.c	Tue Mar 10 10:03:33 1998
+++ linux/fs/exec.c	Thu Mar 12 22:01:53 1998
@@ -49,8 +49,9 @@
 #include <asm/mmu_context.h>
 
 #include <linux/config.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 asmlinkage int sys_exit(int exit_code);
@@ -417,6 +418,7 @@
 	retval = new_page_tables(current);
 	if (retval)
 		goto fail_restore;
+	up(&mm->mmap_sem);
 	mmput(old_mm);
 	return 0;
 
@@ -693,7 +695,7 @@
 		}
 		if (retval != -ENOEXEC) {
 			break;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 		}else{
 #define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
 			char modname[20];
diff -u --recursive --new-file v2.1.89/linux/fs/fcntl.c linux/fs/fcntl.c
--- v2.1.89/linux/fs/fcntl.c	Sun Nov 30 10:59:02 1997
+++ linux/fs/fcntl.c	Tue Mar 10 13:28:05 1998
@@ -9,6 +9,7 @@
 #include <linux/errno.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/file.h>
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/smp.h>
@@ -22,18 +23,32 @@
 static inline int dupfd(unsigned int fd, unsigned int arg)
 {
 	struct files_struct * files = current->files;
+	struct file * file;
+	int error;
 
-	if (fd >= NR_OPEN || !files->fd[fd])
-		return -EBADF;
+	error = -EINVAL;
 	if (arg >= NR_OPEN)
-		return -EINVAL;
+		goto out;
+
+	error = -EBADF;
+	file = fget(fd);
+	if (!file)
+		goto out;
+
+	error = -EMFILE;
 	arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg);
 	if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur)
-		return -EMFILE;
+		goto out_putf;
 	FD_SET(arg, &files->open_fds);
 	FD_CLR(arg, &files->close_on_exec);
-	(files->fd[arg] = files->fd[fd])->f_count++;
-	return arg;
+	fd_install(arg, file);
+	error = arg;
+out:
+	return error;
+
+out_putf:
+	fput(file);
+	goto out;
 }
 
 asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd)
@@ -41,7 +56,7 @@
 	int err = -EBADF;
 
 	lock_kernel();
-	if (oldfd >= NR_OPEN || !current->files->fd[oldfd])
+	if (!fcheck(oldfd))
 		goto out;
 	err = newfd;
 	if (newfd == oldfd)
@@ -51,7 +66,7 @@
 		goto out;	/* following POSIX.1 6.2.1 */
 
 	sys_close(newfd);
-	err = dupfd(oldfd,newfd);
+	err = dupfd(oldfd, newfd);
 out:
 	unlock_kernel();
 	return err;
@@ -62,7 +77,7 @@
 	int ret;
 
 	lock_kernel();
-	ret = dupfd(fildes,0);
+	ret = dupfd(fildes, 0);
 	unlock_kernel();
 	return ret;
 }
@@ -101,12 +116,13 @@
 	long err = -EBADF;
 
 	lock_kernel();
-	if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
+	filp = fget(fd);
+	if (!filp)
 		goto out;
 	err = 0;
 	switch (cmd) {
 		case F_DUPFD:
-			err = dupfd(fd,arg);
+			err = dupfd(fd, arg);
 			break;
 		case F_GETFD:
 			err = FD_ISSET(fd, &current->files->close_on_exec);
@@ -158,6 +174,7 @@
 				err = -EINVAL;
 			break;
 	}
+	fput(filp);
 out:
 	unlock_kernel();
 	return err;
diff -u --recursive --new-file v2.1.89/linux/fs/filesystems.c linux/fs/filesystems.c
--- v2.1.89/linux/fs/filesystems.c	Fri Jan 30 11:28:08 1998
+++ linux/fs/filesystems.c	Tue Mar 10 14:43:13 1998
@@ -29,8 +29,8 @@
 #include <linux/major.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
@@ -187,12 +187,12 @@
 		ret = do_nfsservctl(cmd, argp, resp);
 		goto out;
 	}
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (request_module ("nfsd") == 0) {
 		if (do_nfsservctl)
 			ret = do_nfsservctl(cmd, argp, resp);
 	}
-#endif /* CONFIG_KERNELD */
+#endif /* CONFIG_KMOD */
 out:
 	unlock_kernel();
 	return ret;
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/ChangeLog linux/fs/hfs/ChangeLog
--- v2.1.89/linux/fs/hfs/ChangeLog	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/ChangeLog	Wed Mar 11 20:22:04 1998
@@ -1,12 +1,55 @@
-Wed Jan  7 19:33:33 1998  a sun  <asun@zoology.washington.edu>
+Wed Jan 21 14:04:26 1998  a sun  <asun@zoology.washington.edu>
+
+	* inode.c, sysdep.c
+	use d_iput to uncache dentry from catalog entry instead of relying
+	on put_inode. no more NULL pointer dereferences!
+
+	* catalog.c
+	cleaned up hfs_cat_put a little.
+	
+	ISSUES (non-fatal): mv dir dir2 while creating files in dir screws
+	                    up directory counts.
+		
+			    deletion using netatalk screws up directory
+ 			    counts.
+
+Thu Jan 15 19:14:28 1998  a sun  <asun@zoology.washington.edu>
+
+	* catalog.c
+	make deletion happen when requested instead of waiting until
+	an hfs_cat_put as the dcache can hold onto entries for quite
+	some time.
+
+Wed Jan 14 14:43:16 1998  a sun  <asun@zoology.washington.edu>
+	
+	* catalog.c
+	the current catalog allocation scheme allocates
+	PAGE_SIZE/sizeof(struct hfs_cat_entry) entries at a time and keeps
+	a pool of free entries up to this allocation unit * 8.
 
 	* inode.c
-	don't hfs_cat_put in hfs_iget. that's a bad idea and results
-	in screwed up entry counts.
+	make sure to always hfs_cat_put if hfs_iget is going to return
+	NULL.
+
+	* string.c, catalog.c
+	use linux' hashing method to generate hashes. the old hashing was
+ 	getting collisions. catalog.c also has a larger hash table to
+ 	prevent collisions.
+	
+Tue Jan 13 13:06:01 1998  a sun  <asun@zoology.washington.edu>
+
+	* version.c
+	bumped to 0.95+asun3
 
 	* catalog.c
-	modified hfs_cat_put to undirty deleted entries without trying to 
-	write them out.
+	re-wrote to dynamically allocate/delete catalog entries. on a 486,
+	entries fit into the size-256 slab.
+
+Wed Jan  7 19:33:33 1998  a sun  <asun@zoology.washington.edu>
+
+	* inode.c 
+	don't hfs_cat_put gratuitously in hfs_iget. that's a bad
+ 	idea and results in screwed up entry counts.
 
 Tue Jan  6 14:38:24 1998  a sun  <asun@zoology.washington.edu>
 
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/Makefile linux/fs/hfs/Makefile
--- v2.1.89/linux/fs/hfs/Makefile	Sun Jan  4 10:40:17 1998
+++ linux/fs/hfs/Makefile	Wed Mar 11 20:22:04 1998
@@ -1,5 +1,5 @@
 #
-# Makefile for the linux nfs-filesystem routines.
+# Makefile for the linux hfs-filesystem routines.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/catalog.c linux/fs/hfs/catalog.c
--- v2.1.89/linux/fs/hfs/catalog.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/catalog.c	Wed Mar 11 20:22:04 1998
@@ -10,6 +10,7 @@
  *
  * Cache code shamelessly stolen from 
  *     linux/fs/inode.c Copyright (C) 1991, 1992  Linus Torvalds
+ *     re-shamelessly stolen Copyright (C) 1997 Linus Torvalds
  *
  * In function preconditions the term "valid" applied to a pointer to
  * a structure means that the pointer is non-NULL and the structure it
@@ -24,16 +25,15 @@
 
 /*================ Variable-like macros ================*/
 
-#define NUM_FREE_ENTRIES 8
-
 /* Number of hash table slots */
-#define CCACHE_NR 128
-
-/* Max number of entries in memory */
-#define CCACHE_MAX 1024
-
-/* Number of entries to fit in a single page on an i386 */
-#define CCACHE_INC ((PAGE_SIZE - sizeof(void *))/sizeof(struct hfs_cat_entry))
+#define C_HASHBITS  10
+#define C_HASHSIZE  (1UL << C_HASHBITS)
+#define C_HASHMASK  (C_HASHSIZE - 1)
+
+/* Number of entries to fit in a single page on an i386.
+ * Actually, now it's used to increment the free entry pool. */
+#define CCACHE_INC (PAGE_SIZE/sizeof(struct hfs_cat_entry))
+#define CCACHE_MAX (CCACHE_INC * 8)
 
 /*================ File-local data types ================*/
 
@@ -94,18 +94,11 @@
 	} u;
 };
 
-
-struct allocation_unit {
-	struct allocation_unit *next;
-	struct hfs_cat_entry entries[CCACHE_INC];
-};
-
 /*================ File-local variables ================*/
  
 static LIST_HEAD(entry_in_use);
-static LIST_HEAD(entry_dirty); /* all the dirty entries */
 static LIST_HEAD(entry_unused);
-static struct list_head hash_table[CCACHE_NR];
+static struct list_head hash_table[C_HASHSIZE];
 
 spinlock_t entry_lock = SPIN_LOCK_UNLOCKED;
 
@@ -114,8 +107,6 @@
         int nr_free_entries;
 } entries_stat;
 
-static struct allocation_unit *allocation = NULL;
-
 /*================ File-local functions ================*/
 
 /*
@@ -136,13 +127,16 @@
  *
  * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
  */
-static inline unsigned int hashfn(const struct hfs_mdb *mdb,
+static inline unsigned long hashfn(const struct hfs_mdb *mdb,
 				  const struct hfs_cat_key *key)
 {
 #define LSB(X) (((unsigned char *)(&X))[3])
-	return ((unsigned int)LSB(mdb->create_date) ^ 
-		(unsigned int)key->ParID[3] ^
-		hfs_strhash(&key->CName)) % CCACHE_NR;
+	unsigned long hash;
+	
+	hash = (unsigned long) mdb | (unsigned long) key->ParID[3] |
+	  hfs_strhash(&key->CName);
+	hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2);
+	return hash & C_HASHMASK;
 #undef LSB
 }
 
@@ -208,24 +202,7 @@
 	hfs_wake_up(&entry->wait);
 }
 
-/*
- * clear_entry()
- *
- * Zero all the fields of an entry and place it on the free list.
- */
-static void clear_entry(struct hfs_cat_entry * entry)
-{
-	wait_on_entry(entry);
-	/* zero all but the wait queue */
-	memset(&entry->wait, 0,
-	       sizeof(*entry) - offsetof(struct hfs_cat_entry, wait));
-	INIT_LIST_HEAD(&entry->hash);
-	INIT_LIST_HEAD(&entry->list);
-	INIT_LIST_HEAD(&entry->dirty);
-}
-
-/* put entry on mdb dirty list. this only does it if it's on the hash
- * list. we also add it to the global dirty list as well. */
+/* put entry on mdb dirty list. */
 void hfs_cat_mark_dirty(struct hfs_cat_entry *entry)
 {
         struct hfs_mdb *mdb = entry->mdb;
@@ -234,153 +211,74 @@
 	if (!(entry->state & HFS_DIRTY)) {
 	        entry->state |= HFS_DIRTY;
 
-		/* Only add valid (ie hashed) entries to the
-		 * dirty list */
+		/* Only add valid (ie hashed) entries to the dirty list. */
 		if (!list_empty(&entry->hash)) {
 		        list_del(&entry->list);
 			list_add(&entry->list, &mdb->entry_dirty);
-			INIT_LIST_HEAD(&entry->dirty);
-			list_add(&entry->dirty, &entry_dirty);
 		}
 	}
 	spin_unlock(&entry_lock);
 }
 
-/* prune all entries */
-static void dispose_list(struct list_head *head)
+/* delete an entry and remove it from the hash table. */
+static void delete_entry(struct hfs_cat_entry *entry)
 {
-        struct list_head *next;
-	int count = 0;
+        if (!(entry->state & HFS_DELETED)) {
+	        entry->state |= HFS_DELETED;
+		list_del(&entry->hash);
+		INIT_LIST_HEAD(&entry->hash);
 
-	next = head->next;
-	for (;;) {
-		struct list_head * tmp = next;
-
-		next = next->next;
-		if (tmp == head)
-			break;
-		hfs_cat_prune(list_entry(tmp, struct hfs_cat_entry, list));
-		count++;
-	}
-}
-
-/*
- * try_to_free_entries works by getting the underlying 
- * cache system to release entries. it gets called with the entry lock
- * held. 
- *
- * count can be up to 2 due to both a resource and data fork being
- * listed. we can unuse dirty entries as well.
- */
-#define CAN_UNUSE(tmp) (((tmp)->count < 3) && ((tmp)->state <= HFS_DIRTY))
-static int try_to_free_entries(const int goal) 
-{
-        struct list_head *tmp, *head = &entry_in_use;
-	LIST_HEAD(freeable);
-	int found = 0, depth = goal << 1;
-
-	/* try freeing from entry_in_use */
-	while ((tmp = head->prev) != head && depth--) {
-	        struct hfs_cat_entry *entry = 
-		  list_entry(tmp, struct hfs_cat_entry, list);
-		list_del(tmp);
-		if (CAN_UNUSE(entry)) {
-		        list_del(&entry->hash);
-			INIT_LIST_HEAD(&entry->hash);
-			list_add(tmp, &freeable);
-			if (++found < goal)
-			       continue;
-			break;
+	        if (entry->type == HFS_CDR_FIL) {
+		  /* free all extents */
+		  entry->u.file.data_fork.lsize = 0;
+		  hfs_extent_adj(&entry->u.file.data_fork);
+		  entry->u.file.rsrc_fork.lsize = 0;
+		  hfs_extent_adj(&entry->u.file.rsrc_fork);
 		}
-		list_add(tmp, head);
 	}
+}
 
-	if (found < goal) { /* try freeing from global dirty list */
-	        head = &entry_dirty;
-		depth = goal << 1;
-		while ((tmp = head->prev) != head && depth--) {
-		        struct hfs_cat_entry *entry = 
-			  list_entry(tmp, struct hfs_cat_entry, dirty);
-			list_del(tmp);
-			if (CAN_UNUSE(entry)) {
-			        list_del(&entry->hash);
-				INIT_LIST_HEAD(&entry->hash);
-				list_del(&entry->list);
-				INIT_LIST_HEAD(&entry->list);
-				list_add(&entry->list, &freeable);
-				if (++found < goal)
-				  continue;
-				break;
-			}
-			list_add(tmp, head);
-		}
-	}
-		
-	if (found) {
-	        spin_unlock(&entry_lock);
-		dispose_list(&freeable);
-		spin_lock(&entry_lock);
-	}
 
-	return found;
-}
-  
-/* init_once */
-static inline void init_once(struct hfs_cat_entry *entry)
+static inline void init_entry(struct hfs_cat_entry *entry)
 {
-	init_waitqueue(&entry->wait);
+	memset(entry, 0, sizeof(*entry));
+	hfs_init_waitqueue(&entry->wait);
 	INIT_LIST_HEAD(&entry->hash);
 	INIT_LIST_HEAD(&entry->list);
-	INIT_LIST_HEAD(&entry->dirty);
 }
 
 /*
- * grow_entries()
+ * hfs_cat_alloc()
  *
- * Try to allocate more entries, adding them to the free list. this returns
- * with the spinlock held if successful 
+ * Try to allocate another entry. 
  */
-static struct hfs_cat_entry *grow_entries(struct hfs_mdb *mdb)
+static inline struct hfs_cat_entry *hfs_cat_alloc(void)
 {
-	struct allocation_unit *tmp;
-	struct hfs_cat_entry * entry;
-	int i;
+        struct hfs_cat_entry *entry;
 
-	spin_unlock(&entry_lock);
-	if ((entries_stat.nr_entries < CCACHE_MAX) &&
-	    HFS_NEW(tmp)) {
-	        spin_lock(&entry_lock);
-		memset(tmp, 0, sizeof(*tmp));
-		tmp->next = allocation;
-		allocation = tmp;
-		entry = tmp->entries;
-		for (i = 1; i < CCACHE_INC; i++) {
-		        entry++;
-			init_once(entry);
-		        list_add(&entry->list, &entry_unused);
-		}
-		init_once(tmp->entries);
-
-		entries_stat.nr_entries += CCACHE_INC;
-		entries_stat.nr_free_entries += CCACHE_INC - 1;
-		return tmp->entries;
-	}
+	if (!HFS_NEW(entry))
+	        return NULL;
 
-	/* allocation failed. do some pruning and try again */
-	spin_lock(&entry_lock);
-	try_to_free_entries(entries_stat.nr_entries >> 2);
-	{
-		struct list_head *tmp = entry_unused.next;
-		if (tmp != &entry_unused) {
-			entries_stat.nr_free_entries--;
-			list_del(tmp);
-			entry = list_entry(tmp, struct hfs_cat_entry, list);
-			return entry;
-		}
+	init_entry(entry);
+	return entry;
+}
+
+/* this gets called with the spinlock held. */
+static int grow_entries(void)
+{
+        struct hfs_cat_entry *entry;
+	int i;
+	
+	for (i = 0; i < CCACHE_INC; i++) {
+	        if (!(entry = hfs_cat_alloc()))
+		        break;
+		list_add(&entry->list, &entry_unused);
 	}
-	spin_unlock(&entry_lock);
 
-	return NULL;
+	entries_stat.nr_entries += i;
+	entries_stat.nr_free_entries += i;
+	        
+	return i;
 }
 
 /*
@@ -537,7 +435,8 @@
 /*
  * write_entry()
  *
- * Write a modified entry back to the catalog B-tree.
+ * Write a modified entry back to the catalog B-tree. this gets called
+ * with the entry locked.
  */
 static void write_entry(struct hfs_cat_entry * entry)
 {
@@ -577,6 +476,7 @@
 }
 
 
+/* this gets called with the spinlock held. */
 static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb,
 					const struct hfs_cat_key *key)
 {
@@ -592,8 +492,9 @@
 		entry = list_entry(tmp, struct hfs_cat_entry, hash);
 		if (entry->mdb != mdb)
 			continue;
-		if (hfs_cat_compare(&entry->key, key))
+		if (hfs_cat_compare(&entry->key, key)) {
 			continue;
+		}
 		entry->count++;
 		break;
 	}
@@ -609,13 +510,14 @@
 {
 	struct hfs_cat_entry *entry;
 	struct list_head *head = hash(mdb, key);
-	struct list_head *tmp = entry_unused.next;
+	struct list_head *tmp;
 
-	if (tmp != &entry_unused) {
+add_new_entry:
+	tmp = entry_unused.next;
+	if ((tmp != &entry_unused) ) {
 		list_del(tmp);
 		entries_stat.nr_free_entries--;
 		entry = list_entry(tmp, struct hfs_cat_entry, list);
-add_new_entry:
 		list_add(&entry->list, &entry_in_use);
 		list_add(&entry->hash, head);
 		entry->mdb = mdb;
@@ -629,7 +531,8 @@
 
 		   if (hfs_bfind(&brec, mdb->cat_tree,
 				 HFS_BKEY(key), HFS_BFIND_READ_EQ)) {
-		        /* uh oh. we failed to read the record */
+		        /* uh oh. we failed to read the record.
+			 * the entry doesn't actually exist. */
 		        entry->state |= HFS_DELETED;
 		        goto read_fail;
 		   }
@@ -651,28 +554,18 @@
 		return entry;
 	}
 
-	/*
-	 * Uhhuh.. We need to expand. Note that "grow_entries()" will
-	 * release the spinlock, but will return with the lock held
-	 * again if the allocation succeeded.
-	 */
-	entry = grow_entries(mdb);
-	if (entry) {
-		/* We released the lock, so.. */
-		struct hfs_cat_entry * old = find_entry(mdb, key);
-		if (!old)
-			goto add_new_entry;
-		list_add(&entry->list, &entry_unused);
-		entries_stat.nr_free_entries++;
-		spin_unlock(&entry_lock);
-		wait_on_entry(old);
-		return old;
-	}
 
-	return entry;
+	/* try to allocate more entries. grow_entries() doesn't release
+	 * the spinlock. */
+	if (grow_entries())
+	        goto add_new_entry;
 
+	spin_unlock(&entry_lock);
+	return NULL;
 
-read_fail:
+read_fail: 
+	/* spinlock unlocked already. we don't need to mark the entry
+	 * dirty here because we know that it doesn't exist. */
 	remove_hash(entry);
 	entry->state &= ~HFS_LOCK;
 	hfs_wake_up(&entry->wait);
@@ -694,11 +587,6 @@
 	struct hfs_cat_entry * entry;
 
 	spin_lock(&entry_lock);
-	if (!entries_stat.nr_free_entries &&
-	    (entries_stat.nr_entries >= CCACHE_MAX))
-		goto restock;
-
-search:
 	entry = find_entry(mdb, key);
 	if (!entry) {
 	        return get_new_entry(mdb, key, read);
@@ -706,10 +594,6 @@
 	spin_unlock(&entry_lock);
 	wait_on_entry(entry);
 	return entry;
-
-restock:
-	try_to_free_entries(8);
-	goto search;
 }
 
 /* 
@@ -753,6 +637,9 @@
 
 /*
  * Add a writer to dir, excluding readers.
+ *
+ * XXX: this is wrong. it allows a move to occur when a directory
+ *      is being written to. 
  */
 static inline void start_write(struct hfs_cat_entry *dir)
 {
@@ -880,7 +767,10 @@
 	goto done;
 
 bail1:
+	/* entry really didn't exist, so we don't need to really delete it.
+	 * we do need to remove it from the hash, though. */
 	entry->state |= HFS_DELETED;
+	remove_hash(entry);
 	unlock_entry(entry);
 bail2:
 	hfs_cat_put(entry);
@@ -900,13 +790,21 @@
  * entry that the entry is in a consistent state, since another
  * process may get the entry while we sleep. That is why we
  * 'goto repeat' after each operation that might sleep.
+ *
+ * ADDITIONAL NOTE: the sys_entries will remove themselves from 
+ *                  the sys_entry list on the final iput, so we don't need 
+ *                  to worry about them here.
+ *
+ *                  nothing in hfs_cat_put goes to sleep now except 
+ *                  on the initial entry.
  */
 void hfs_cat_put(struct hfs_cat_entry * entry)
 {
 	if (entry) {
 	        wait_on_entry(entry);
 
-		if (!entry->count) {/* just in case */
+		/* just in case. this should never happen. */
+		if (!entry->count) { 
 		  hfs_warn("hfs_cat_put: trying to free free entry: %p\n",
 			   entry);
 		  return;
@@ -914,52 +812,41 @@
 
 		spin_lock(&entry_lock);
 		if (!--entry->count) {
-repeat:		  
-		        if ((entry->state & HFS_DELETED)) {
-				if (entry->type == HFS_CDR_FIL) {
-				  /* free all extents */
-				  entry->u.file.data_fork.lsize = 0;
-				  hfs_extent_adj(&entry->u.file.data_fork);
-				  entry->u.file.rsrc_fork.lsize = 0;
-				  hfs_extent_adj(&entry->u.file.rsrc_fork);
-				}
-				entry->state = 0;
-			} else if (entry->type == HFS_CDR_FIL) {
+		        if ((entry->state & HFS_DELETED))
+			        goto entry_deleted;
+
+			if ((entry->type == HFS_CDR_FIL)) {
 		                /* clear out any cached extents */
 			        if (entry->u.file.data_fork.first.next) {
 				  hfs_extent_free(&entry->u.file.data_fork);
-				  spin_unlock(&entry_lock);
-				  wait_on_entry(entry);
-				  spin_lock(&entry_lock);
-				  goto repeat;
 				}
 				if (entry->u.file.rsrc_fork.first.next) {
 				  hfs_extent_free(&entry->u.file.rsrc_fork);
-				  spin_unlock(&entry_lock);
-				  wait_on_entry(entry);
-				  spin_lock(&entry_lock);
-				  goto repeat;
 				}
 			}
 
 			/* if we put a dirty entry, write it out. */
 			if ((entry->state & HFS_DIRTY)) {
-				list_del(&entry->dirty);
-				INIT_LIST_HEAD(&entry->dirty);
-				spin_unlock(&entry_lock);
+			        entry->state ^= HFS_DIRTY | HFS_LOCK;
 				write_entry(entry);
-				spin_lock(&entry_lock);
-				entry->state &= ~HFS_DIRTY;
-				goto repeat;
+				entry->state &= ~HFS_LOCK;
 			}
 
 			list_del(&entry->hash);
+entry_deleted: 		/* deleted entries have already been removed
+			 * from the hash list. */
 			list_del(&entry->list);
-			spin_unlock(&entry_lock);
-			clear_entry(entry);
-			spin_lock(&entry_lock);
-			list_add(&entry->list, &entry_unused);
-			entries_stat.nr_free_entries++;
+			if (entries_stat.nr_free_entries > CCACHE_MAX) {
+			        HFS_DELETE(entry);
+				entries_stat.nr_entries--;
+			} else {
+			        spin_unlock(&entry_lock);
+				wait_on_entry(entry);
+				init_entry(entry);
+				spin_lock(&entry_lock);
+				list_add(&entry->list, &entry_unused);
+				entries_stat.nr_free_entries++;
+			}
 		}
 		spin_unlock(&entry_lock);
 	}
@@ -995,20 +882,37 @@
 		if (entry->mdb != mdb) {
 			continue;
 		}
+
 		if (!entry->count) {
 		        list_del(&entry->hash);
 			INIT_LIST_HEAD(&entry->hash);
-			list_del(&entry->dirty);
-			INIT_LIST_HEAD(&entry->dirty);
 			list_del(&entry->list);
 			list_add(&entry->list, dispose);
 			continue;
 		}
-		hfs_warn("hfs_fs: entry %p(%u:%lu) busy on removed device %s.\n",
-			 entry, entry->count, entry->state,
+		
+		hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n",
+			 entry, entry->count, 
 			 hfs_mdb_name(entry->mdb->sys_mdb));
 	}
+}
+
+/* delete entries from a list */
+static void delete_list(struct list_head *head) 
+{
+	struct list_head *next = head->next;
+	struct hfs_cat_entry *entry;
+	
+	for (;;) {
+		struct list_head * tmp = next;
 
+		next = next->next;
+		if (tmp == head) {
+			break;
+		}
+		entry = list_entry(tmp, struct hfs_cat_entry, list);
+		HFS_DELETE(entry);
+	}
 }
 
 /* 
@@ -1026,7 +930,7 @@
 	invalidate_list(&mdb->entry_dirty, mdb, &throw_away);
 	spin_unlock(&entry_lock);
 
-	dispose_list(&throw_away); 
+	delete_list(&throw_away);
 }
 
 /*
@@ -1052,9 +956,6 @@
 
 		       if (!entry->count)
 			        insert = entry_in_use.prev;
-		       /* remove from global dirty list */
-		       list_del(&entry->dirty); 
-		       INIT_LIST_HEAD(&entry->dirty);
 
 		       /* add to in_use list */
 		       list_del(&entry->list);
@@ -1077,16 +978,13 @@
  *
  * Releases all the memory allocated in grow_entries().
  * Must call hfs_cat_invalidate() on all MDBs before calling this.
+ * This only gets rid of the unused pool of entries. all the other
+ * entry references should have either been freed by cat_invalidate
+ * or moved onto the unused list.
  */
 void hfs_cat_free(void)
 {
-	struct allocation_unit *tmp;
-
-	while (allocation) {
-		tmp = allocation->next;
-		HFS_DELETE(allocation);
-		allocation = tmp;
-	}
+	delete_list(&entry_unused);
 }
 
 /*
@@ -1272,6 +1170,9 @@
  * Create a new file with the indicated name in the indicated directory.
  * The file will have the indicated flags, type and creator.
  * If successful an (struct hfs_cat_entry) is returned in '*result'.
+ *
+ * XXX: the presence of "record" probably means that the following two
+ *      aren't currently SMP safe and need spinlocks.
  */
 int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
 		   hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
@@ -1358,7 +1259,7 @@
 
 	/* try to delete the file or directory */
 	if (!error) {
-	        lock_entry(entry);
+		lock_entry(entry);
 		if ((entry->state & HFS_DELETED)) {
 			/* somebody beat us to it */
 			error = -ENOENT;
@@ -1371,8 +1272,8 @@
 
 	if (!error) {
 		/* Mark the entry deleted and remove it from the cache */
-		entry->state |= HFS_DELETED;
-		remove_hash(entry);
+		lock_entry(entry);
+		delete_entry(entry);
 
 		/* try to delete the thread entry if it exists */
 		if (with_thread) {
@@ -1380,6 +1281,7 @@
 			(void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
 		}
 
+		unlock_entry(entry);
 		update_dir(mdb, parent, is_dir, -1);
 	}
 
@@ -1430,10 +1332,12 @@
 		return -EINVAL;
 	}
 
+	spin_lock(&entry_lock);
 	while (mdb->rename_lock) {
 		hfs_sleep_on(&mdb->rename_wait);
 	}
 	mdb->rename_lock = 1;
+	spin_unlock(&entry_lock);
 
 	/* keep readers from getting confused by changing dir size */
 	start_write(new_dir);
@@ -1501,7 +1405,7 @@
 				    &new_record, is_dir ? 2 + sizeof(DIR_REC) :
 							  2 + sizeof(FIL_REC));
 		if (error == -EEXIST) {
-			dest->state |= HFS_DELETED;
+			delete_entry(dest);
 			unlock_entry(dest);
 			hfs_cat_put(dest);
 			goto restart;
@@ -1590,8 +1494,7 @@
 			/* Something went seriously wrong.
 			   The dir/file has been deleted. */
 			/* XXX try some recovery? */
-			entry->state |= HFS_DELETED;
-			remove_hash(entry);
+			delete_entry(entry);
 			goto bail1;
 		}
 	}
@@ -1620,7 +1523,7 @@
 
 	/* delete any pre-existing or place-holder entry */
 	if (dest) {
-		dest->state |= HFS_DELETED;
+		delete_entry(dest);
 		unlock_entry(dest);
 		if (removed && dest->cnid) {
 			*removed = dest;
@@ -1639,7 +1542,7 @@
 			(void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key));
 			update_dir(mdb, new_dir, is_dir, -1);
 bail3:
-			dest->state |= HFS_DELETED;
+			delete_entry(dest);
 		}
 		unlock_entry(dest);
 		hfs_cat_put(dest);
@@ -1649,8 +1552,10 @@
 		end_write(old_dir);
 	}
 	end_write(new_dir);
+	spin_lock(&entry_lock);
 	mdb->rename_lock = 0;
 	hfs_wake_up(&mdb->rename_wait);
+	spin_unlock(&entry_lock);
 
 	return error;
 }
@@ -1663,7 +1568,7 @@
 	int i;
 	struct list_head *head = hash_table;
 
-        i = CCACHE_NR;
+        i = C_HASHSIZE;
         do {
                 INIT_LIST_HEAD(head);
                 head++;
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/dir.c linux/fs/hfs/dir.c
--- v2.1.89/linux/fs/hfs/dir.c	Sun Jan  4 10:40:17 1998
+++ linux/fs/hfs/dir.c	Wed Mar 11 20:22:04 1998
@@ -95,7 +95,7 @@
 }
 
 /*
- * update_dirs_plus()
+ * update_dirs_minus()
  *
  * Update the fields 'i_size', 'i_nlink', 'i_ctime', 'i_mtime' and
  * 'i_version' of the inodes associated with a directory that has
@@ -138,10 +138,9 @@
 
 	for (i = 0; i < 4; ++i) {
 		if ((de = entry->sys_entry[i]) && (dentry != de)) {
-		    entry->sys_entry[i] = NULL;
-		    dget(de);
-		    d_delete(de);
-		    dput(de);
+		      dget(de);
+		      d_delete(de);
+		      dput(de);
 		}
 	}
 }
@@ -198,7 +197,7 @@
 		        error = -EIO;
    		} else {
 		  if (HFS_I(dir)->d_drop_op)
-		    HFS_I(dir)->d_drop_op(HFS_I(dir)->file_type, dentry);
+		    HFS_I(dir)->d_drop_op(dentry, HFS_I(dir)->file_type);
 		  d_instantiate(dentry, inode);
 		}
 	}
@@ -285,7 +284,7 @@
 	struct hfs_cat_key key;
 	int error;
 
-	if (build_key(&key, dir, dentry->d_name.name, 
+	if (build_key(&key, dir, dentry->d_name.name,
 		      dentry->d_name.len)) {
 		error = -EPERM;
 	} else if (!(victim = hfs_cat_get(entry->mdb, &key))) {
@@ -386,15 +385,15 @@
 		} else {
 		  /* no existing inodes. just drop negative dentries */
 		  if (HFS_I(new_dir)->d_drop_op) 
-		    HFS_I(new_dir)->d_drop_op(HFS_I(new_dir)->file_type,
-						   new_dentry);
+		    HFS_I(new_dir)->d_drop_op(new_dentry, 
+					      HFS_I(new_dir)->file_type);
 		  update_dirs_plus(new_parent, is_dir);
 		}
 
 		/* update dcache */
 		d_move(old_dentry, new_dentry);
 	}
-	 
+
 	hfs_cat_put(victim);	/* Note that hfs_cat_put(NULL) is safe. */
 	return error;
 }
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/dir_cap.c linux/fs/hfs/dir_cap.c
--- v2.1.89/linux/fs/hfs/dir_cap.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/dir_cap.c	Wed Mar 11 20:22:04 1998
@@ -1,5 +1,4 @@
-/* linux/fs/hfs/dir_cap.c
- *
+/*
  * Copyright (C) 1995-1997  Paul H. Hargrove
  * This file may be distributed under the terms of the GNU Public License.
  *
@@ -154,11 +153,12 @@
 	struct hfs_cat_entry *entry;
 	struct hfs_cat_key key;
 	struct inode *inode = NULL;
-	
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
-		goto done;
+		return -ENOENT;
 	}
 
+	dentry->d_op = &hfs_dentry_operations;
 	entry = HFS_I(dir)->entry;
 	dtype = HFS_ITYPE(dir->i_ino);
 
@@ -215,13 +215,13 @@
 			 HFS_I(dir)->file_type, dentry);
 
 	/* Don't return a resource fork for a directory */
-	if (inode && (dtype == HFS_CAP_RDIR) &&
+	if (inode && (dtype == HFS_CAP_RDIR) && 
 	    (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
+	        iput(inode); /* this does an hfs_cat_put */
 		inode = NULL;
 	}
 
 done:
-	dentry->d_op = &hfs_dentry_operations;
 	d_add(dentry, inode);
 	return 0;
 }
@@ -261,7 +261,7 @@
 		return -EBADF;
 	}
 
-        entry = HFS_I(dir)->entry;
+	entry = HFS_I(dir)->entry;
 	type = HFS_ITYPE(dir->i_ino);
 	skip_dirs = (type == HFS_CAP_RDIR);
 
@@ -368,7 +368,7 @@
  * related calls (create, rename, and mknod). the directory calls
  * should be immune. the relevant calls in dir.c call drop_dentry 
  * upon successful completion. */
-void hfs_cap_drop_dentry(const ino_t type, struct dentry *dentry)
+void hfs_cap_drop_dentry(struct dentry *dentry, const ino_t type)
 {
   if (type == HFS_CAP_DATA) { /* given name */
     hfs_drop_special(DOT_FINDERINFO, dentry->d_parent, dentry);
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/dir_dbl.c linux/fs/hfs/dir_dbl.c
--- v2.1.89/linux/fs/hfs/dir_dbl.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/dir_dbl.c	Wed Mar 11 20:22:04 1998
@@ -135,11 +135,12 @@
 	struct hfs_cat_entry *entry;
 	struct hfs_cat_key key;
 	struct inode *inode = NULL;
-	
+
 	if (!dir || !S_ISDIR(dir->i_mode)) {
-		goto done;
+		return -ENOENT;
 	}
 
+	dentry->d_op = &hfs_dentry_operations;
 	entry = HFS_I(dir)->entry;
 	
 	/* Perform name-mangling */
@@ -175,12 +176,11 @@
 		hfs_nameout(dir, &cname, dentry->d_name.name+1,
 			    dentry->d_name.len-1);
 		hfs_cat_build_key(entry->cnid, &cname, &key);
-		inode = hfs_iget(hfs_cat_get(entry->mdb, &key), 
+		inode = hfs_iget(hfs_cat_get(entry->mdb, &key),
 				 HFS_DBL_HDR, dentry);
 	}
 	
 done:
-	dentry->d_op = &hfs_dentry_operations;
 	d_add(dentry, inode);
 	return 0;
 }
@@ -219,7 +219,7 @@
 		return -EBADF;
 	}
 
-        entry = HFS_I(dir)->entry;
+	entry = HFS_I(dir)->entry;
 
 	if (filp->f_pos == 0) {
 		/* Entry 0 is for "." */
@@ -414,14 +414,14 @@
 {
 	int error;
 
-	if (is_hdr(new_dir, new_dentry->d_name.name, 
+	if (is_hdr(new_dir, new_dentry->d_name.name,
 		   new_dentry->d_name.len)) {
 		error = -EPERM;
 	} else {
 		error = hfs_rename(old_dir, old_dentry,
 				   new_dir, new_dentry);
 		if ((error == -ENOENT) /*&& !must_be_dir*/ &&
-		    is_hdr(old_dir, old_dentry->d_name.name, 
+		    is_hdr(old_dir, old_dentry->d_name.name,
 			   old_dentry->d_name.len)) {
 			error = -EPERM;
 		}
@@ -435,9 +435,8 @@
  * as far as i can tell, the calls that need to do this are the file
  * related calls (create, rename, and mknod). the directory calls
  * should be immune. the relevant calls in dir.c call drop_dentry 
- * upon successful completion. this allocates an array for %name
- * on the first attempt to access it. */
-void hfs_dbl_drop_dentry(const ino_t type, struct dentry *dentry)
+ * upon successful completion. */
+void hfs_dbl_drop_dentry(struct dentry *dentry, const ino_t type)
 {
   unsigned char tmp_name[HFS_NAMEMAX + 1];
   struct dentry *de = NULL;
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/dir_nat.c linux/fs/hfs/dir_nat.c
--- v2.1.89/linux/fs/hfs/dir_nat.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/dir_nat.c	Wed Mar 11 20:22:04 1998
@@ -142,9 +142,10 @@
 	struct inode *inode = NULL;
 
 	if (!dir || !S_ISDIR(dir->i_mode)) {
-		goto done;
+		return -ENOENT;
 	}
 
+	dentry->d_op = &hfs_dentry_operations;
 	entry = HFS_I(dir)->entry;
 	dtype = HFS_ITYPE(dir->i_ino);
 
@@ -200,12 +201,11 @@
 	if (inode && (dtype == HFS_NAT_HDIR) &&
 	    (HFS_I(inode)->entry != entry) &&
 	    (HFS_I(inode)->entry->type == HFS_CDR_DIR)) {
-	        iput(inode);
+	        iput(inode); /* this does an hfs_cat_put */
 		inode = NULL;
 	}
 
 done:
-	dentry->d_op = &hfs_dentry_operations;
 	d_add(dentry, inode);
 	return 0;
 }
@@ -241,7 +241,7 @@
 		return -EBADF;
 	}
 
-        entry = HFS_I(dir)->entry;
+	entry = HFS_I(dir)->entry;
 	type = HFS_ITYPE(dir->i_ino);
 	skip_dirs = (type == HFS_NAT_HDIR);
 
@@ -329,7 +329,7 @@
  * related calls (create, rename, and mknod). the directory calls
  * should be immune. the relevant calls in dir.c call drop_dentry 
  * upon successful completion. */
-void hfs_nat_drop_dentry(const ino_t type, struct dentry *dentry)
+void hfs_nat_drop_dentry(struct dentry *dentry, const ino_t type)
 {
   struct dentry *de;
   
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/file.c linux/fs/hfs/file.c
--- v2.1.89/linux/fs/hfs/file.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/file.c	Wed Mar 11 20:22:04 1998
@@ -98,11 +98,10 @@
 		   to the file until we return, so it can't have moved.
 		*/
 	       if (tmp) {
-	               hfs_cat_mark_dirty(fork->entry);
-		       return getblk(dev, tmp, HFS_SECTOR_SIZE);
+		 hfs_cat_mark_dirty(fork->entry);
+		 return getblk(dev, tmp, HFS_SECTOR_SIZE);
 	       }
 	       return NULL;
-
 	} else {
 		/* If reading the block, then retry since the
 		   location on disk could have changed while
@@ -236,6 +235,7 @@
  */
 static void hfs_file_truncate(struct inode * inode)
 {
+ 	/*struct inode *inode = dentry->d_inode;*/
 	struct hfs_fork *fork = HFS_I(inode)->fork;
 
 	fork->lsize = inode->i_size;
@@ -268,7 +268,7 @@
  */
 static inline void xlate_from_user(char *data, const char *buf, int count)
 {
-	copy_from_user(data, buf, count);
+	count -= copy_from_user(data, buf, count);
 	while (count--) {
 		if (*data == '\n') {
 			*data = '\r';
@@ -398,16 +398,16 @@
 			} else {
 				chars = HFS_SECTOR_SIZE - offset;
 			}
-			count -= chars;
-			read += chars;
 			p = (*bhe)->b_data + offset;
 			if (convert) {
 				xlate_to_user(buf, p, chars);
 			} else {
-				copy_to_user(buf, p, chars);
+				chars -= copy_to_user(buf, p, chars);
 			}
 			brelse(*bhe);
+			count -= chars;
 			buf += chars;
+			read += chars;
 			offset = 0;
 			if (++bhe == &buflist[NBUF]) {
 				bhe = buflist;
@@ -479,7 +479,7 @@
 		if (convert) {
 			xlate_from_user(p, buf, c);
 		} else {
-			copy_from_user(p, buf, c);
+			c -= copy_from_user(p, buf, c);
 		}
 		update_vm_cache(inode,pos,p,c);
 		pos += c;
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/file_cap.c linux/fs/hfs/file_cap.c
--- v2.1.89/linux/fs/hfs/file_cap.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/file_cap.c	Wed Mar 11 20:22:04 1998
@@ -164,8 +164,7 @@
 			memcount = left;
 		}
 		cap_build_meta(&meta, entry);
-		/* is copy_to_user guaranteed to write memcount? */
-		copy_to_user(buf, ((char *)&meta) + pos, memcount);
+		memcount -= copy_to_user(buf, ((char *)&meta) + pos, memcount);
 		left -= memcount;
 		read += memcount;
 		pos += memcount;
@@ -291,6 +290,8 @@
  */
 static void cap_info_truncate(struct inode *inode)
 {
+  	/*struct inode *inode = dentry->d_inode;*/
+
 	if (inode->i_size > HFS_FORK_MAX) {
 		inode->i_size = HFS_FORK_MAX;
 	}
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/file_hdr.c linux/fs/hfs/file_hdr.c
--- v2.1.89/linux/fs/hfs/file_hdr.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/file_hdr.c	Wed Mar 11 20:22:04 1998
@@ -288,12 +288,10 @@
 		    (descr->length != entry->u.file.data_fork.lsize)) {
 			entry->u.file.data_fork.lsize = descr->length;
 			hfs_extent_adj(&entry->u.file.data_fork);
-			hfs_cat_mark_dirty(entry);
 		} else if ((descr->id == HFS_HDR_RSRC) &&
 			   (descr->length != entry->u.file.rsrc_fork.lsize)) {
 			entry->u.file.rsrc_fork.lsize = descr->length;
 			hfs_extent_adj(&entry->u.file.rsrc_fork);
-			hfs_cat_mark_dirty(entry);
 		}
 	}
 }
@@ -414,7 +412,7 @@
 		}
 
 		hdr_build_meta(&meta, layout, entry);
-		copy_to_user(buf, ((char *)&meta) + pos, left);
+		left -= copy_to_user(buf, ((char *)&meta) + pos, left);
 		count -= left;
 		read += left;
 		pos += left;
@@ -531,7 +529,7 @@
 
 		/* transfer the data */
 		if (p) {
-			copy_to_user(buf, p + offset, left);
+			left -= copy_to_user(buf, p + offset, left);
 		} else if (fork) {
 			left = hfs_do_read(inode, fork, offset, buf, left,
 					   filp->f_reada != 0);
@@ -654,6 +652,7 @@
 		/* Handle possible size changes for the forks */
 		if (entry->type == HFS_CDR_FIL) {
 			adjust_forks(entry, layout);
+			hfs_cat_mark_dirty(entry);
 		}
 	}
 
@@ -887,6 +886,8 @@
  */
 static void hdr_truncate(struct inode *inode)
 {
+  	/*struct inode *inode = dentry->d_inode;*/
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
 	struct hfs_hdr_layout *layout;
 	size_t size = inode->i_size;
 	int lcv, last;
@@ -907,14 +908,14 @@
 		}
 
 		if (descr->id == HFS_HDR_RSRC) {
-			fork = &HFS_I(inode)->entry->u.file.rsrc_fork;
+			fork = &entry->u.file.rsrc_fork;
 #if 0
 /* Can't yet truncate the data fork via a header file, since there is the
  * possibility to truncate via the data file, and the only locking is at
  * the inode level.
  */
 		} else if (descr->id == HFS_HDR_DATA) {
-			fork = &HFS_I(inode)->entry->u.file.data_fork;
+			fork = &entry->u.file.data_fork;
 #endif
 		} else {
 			continue;
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/hfs.h linux/fs/hfs/hfs.h
--- v2.1.89/linux/fs/hfs/hfs.h	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/hfs.h	Wed Mar 11 20:22:04 1998
@@ -11,10 +11,10 @@
 #define _HFS_H
 
 #include <linux/hfs_sysdep.h>
-#include <linux/hfs_fs.h>
 
 #define HFS_NEW(X)	((X) = hfs_malloc(sizeof(*(X))))
-#define HFS_DELETE(X)	{ hfs_free((X), sizeof(*(X))); (X) = NULL; }
+#define HFS_DELETE(X)	do { hfs_free((X), sizeof(*(X))); (X) = NULL; } \
+                        while (0)
  
 /* offsets to various blocks */
 #define HFS_DD_BLK		0 /* Driver Descriptor block */
@@ -337,13 +337,12 @@
  * This structure holds information about a
  * file or directory in an HFS filesystem.
  *
- * 'wait' must remain 1st and 'next' 2nd since we do some pointer arithmetic.
+ * 'wait' must remain 1st and 'hash' 2nd since we do some pointer arithmetic.
  */
 struct hfs_cat_entry {
 	hfs_wait_queue		wait;
         struct list_head        hash;
         struct list_head        list;
-        struct list_head        dirty;
 	struct hfs_mdb		*mdb;
 	hfs_sysentry		sys_entry;
 	struct hfs_cat_key	key;
@@ -366,7 +365,6 @@
 #define HFS_KEYDIRTY     2
 #define HFS_LOCK         4
 #define HFS_DELETED      8
-#define HFS_SUPERBLK    16
 
 /* 
  * struct hfs_bnode_ref
@@ -486,13 +484,10 @@
 extern int hfs_part_find(hfs_sysmdb, int, int, hfs_s32 *, hfs_s32 *);
 
 /* string.c */
-extern unsigned int hfs_strhash(const struct hfs_name *);
+extern unsigned long hfs_strhash(const struct hfs_name *);
 extern int hfs_strcmp(const struct hfs_name *, const struct hfs_name *);
 extern int hfs_streq(const struct hfs_name *, const struct hfs_name *);
 extern void hfs_tolower(unsigned char *, int);
-
-/* sysdep.c */
-extern void hfs_cat_prune(struct hfs_cat_entry *);
 
 extern __inline__ struct dentry 
 *hfs_lookup_dentry(const char *name, const int len, 
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/inode.c linux/fs/hfs/inode.c
--- v2.1.89/linux/fs/hfs/inode.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/inode.c	Wed Mar 11 20:22:04 1998
@@ -73,17 +73,13 @@
  */
 void hfs_put_inode(struct inode * inode)
 {
-	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
-
-	entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL;
-	hfs_cat_put(entry);
-
 	if (inode->i_count == 1) {
-		struct hfs_hdr_layout *tmp = HFS_I(inode)->layout;
-		if (tmp) {
-			HFS_I(inode)->layout = NULL;
-			HFS_DELETE(tmp);
-		}
+	  struct hfs_hdr_layout *tmp = HFS_I(inode)->layout;
+
+	  if (tmp) {
+		HFS_I(inode)->layout = NULL;
+		HFS_DELETE(tmp);
+	  }
 	}
 }
 
@@ -153,7 +149,7 @@
 	/* We must change all in-core inodes corresponding to this file. */
 	for (i = 0; i < 4; ++i) {
 	  if (de[i] && (de[i] != dentry)) {
-			inode_setattr(de[i]->d_inode, attr);
+		inode_setattr(de[i]->d_inode, attr);
 	  }
 	}
 
@@ -213,7 +209,7 @@
  * benefit from a way to pass an additional (void *) through iget() to
  * the VFS read_inode() function.
  *
- * hfs_iget no longer touches hfs_cat_entries.
+ * this will hfs_cat_put() the entry if it fails.
  */
 struct inode *hfs_iget(struct hfs_cat_entry *entry, ino_t type,
 		       struct dentry *dentry)
@@ -239,25 +235,15 @@
 	sb = entry->mdb->sys_mdb;
 	sys_entry = &entry->sys_entry[HFS_ITYPE_TO_INT(type)];
 
-	if (*sys_entry && (inode = (*sys_entry)->d_inode)) {
-		/* There is an existing inode for this file/dir.  Use it. */
-		++inode->i_count;
-		return inode;
-	}
-
-	if (!(inode = iget(sb, ntohl(entry->cnid) | type)))
+	if (!(inode = iget(sb, ntohl(entry->cnid) | type))) {
+	        hfs_cat_put(entry);
 	        return NULL;
+	}
 
 	if (inode->i_dev != sb->s_dev) {
-	        iput(inode);
+	        iput(inode); /* automatically does an hfs_cat_put */
 		inode = NULL;
-	} else if (inode->i_mode) {
-		/* The inode has been initialized by another process.
-		   Note that if hfs_put_inode() is sleeping in hfs_cat_put()
-		   then we still need to attach it to the entry. */
-		if (!(*sys_entry))
-			*sys_entry = dentry; /* cache dentry */
-	} else {
+	} else if (!inode->i_mode) {
 		/* Initialize the inode */
 		struct hfs_sb_info *hsb = HFS_SB(sb);
 
@@ -281,10 +267,11 @@
 
 		if (!inode->i_mode) {
 			clear_inode(inode);
+			hfs_cat_put(entry);
 			inode = NULL;
-		} 
+		} else
+			*sys_entry = dentry; /* cache dentry */
 
-		*sys_entry = dentry; /* cache dentry */
 	}
 
 	return inode;
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/string.c linux/fs/hfs/string.c
--- v2.1.89/linux/fs/hfs/string.c	Sun Jan  4 10:40:17 1998
+++ linux/fs/hfs/string.c	Wed Mar 11 20:22:05 1998
@@ -81,11 +81,14 @@
 /*
  * Hash a string to an integer in a case-independent way
  */
-unsigned int hfs_strhash(const struct hfs_name *cname)
+unsigned long hfs_strhash(const struct hfs_name *cname)
 {
-	/* Currently just sum of the 'order' of first and last characters */
-	return ((unsigned int)caseorder[cname->Name[0]] +
-		(unsigned int)caseorder[cname->Name[cname->Len - 1]]);
+	unsigned long hash = init_name_hash();
+	unsigned int i;
+	for (i = 0; i < cname->Len; i++) {
+	        hash = partial_name_hash(caseorder[cname->Name[i]], hash);
+	}
+	return end_name_hash(hash);
 }
 
 /*
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/super.c linux/fs/hfs/super.c
--- v2.1.89/linux/fs/hfs/super.c	Mon Feb 23 18:12:10 1998
+++ linux/fs/hfs/super.c	Wed Mar 11 20:22:05 1998
@@ -163,8 +163,8 @@
 	tmp.f_blocks = mdb->alloc_blksz * mdb->fs_ablocks;
 	tmp.f_bfree = mdb->alloc_blksz * mdb->free_ablocks;
 	tmp.f_bavail = tmp.f_bfree;
-	tmp.f_files = mdb->fs_ablocks;   /* According to the statfs manual page, -1 is the  */
-	tmp.f_ffree = mdb->free_ablocks; /* correct value when the meaning is undefined. */ 
+	tmp.f_files = mdb->fs_ablocks;  
+	tmp.f_ffree = mdb->free_ablocks;
 	tmp.f_namelen = HFS_NAMELEN;
 
 	return copy_to_user(buf, &tmp, len) ? -EFAULT : 0;
@@ -459,16 +459,13 @@
 	if (!root_inode) 
 		goto bail_no_root;
 	  
-	/* cache the dentry in the inode */
-	s->s_root = 
-	  HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] =
-	  d_alloc_root(root_inode, NULL);
+	s->s_root = d_alloc_root(root_inode, NULL);
 	if (!s->s_root) 
 		goto bail_no_root;
 
-	/* HFS_SUPERBLK prevents the root inode from being flushed 
-	 * inadvertantly. */
-	HFS_I(root_inode)->entry->state = HFS_SUPERBLK;
+	/* fix up pointers. */
+	HFS_I(root_inode)->entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE_NORM)] =
+	  s->s_root;
 	s->s_root->d_op = &hfs_dentry_operations;
 
 	/* everything's okay */
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/sysdep.c linux/fs/hfs/sysdep.c
--- v2.1.89/linux/fs/hfs/sysdep.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/sysdep.c	Wed Mar 11 20:22:05 1998
@@ -19,13 +19,16 @@
 #include <linux/hfs_fs.h>
 
 static int hfs_hash_dentry(struct dentry *, struct qstr *);
-static int hfs_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
+static int hfs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
+static void hfs_dentry_iput(struct dentry *, struct inode *);
 struct dentry_operations hfs_dentry_operations =
 {
 	NULL,	                /* d_validate(struct dentry *) */
 	hfs_hash_dentry,	/* d_hash */
 	hfs_compare_dentry,    	/* d_compare */
-	NULL	                /* d_delete(struct dentry *) */
+	NULL,	                /* d_delete(struct dentry *) */
+	NULL,                   /* d_release(struct dentry *) */
+	hfs_dentry_iput         /* d_iput(struct dentry *, struct inode *) */
 };
 
 /*
@@ -55,19 +58,16 @@
 
 /* dentry case-handling: just lowercase everything */
 
-/* should we use hfs_strhash? if so, it probably needs to be beefed 
- * up a little. */
+/* hfs_strhash now uses the same hashing function as the dcache. */
 static int hfs_hash_dentry(struct dentry *dentry, struct qstr *this)
 {
-        unsigned char name[HFS_NAMELEN];
-	int len = this->len;
+        struct hfs_name cname;
 	
-	if (len > HFS_NAMELEN)
+	if ((cname.Len = this->len) > HFS_NAMELEN)
 	        return 0;
-  
-	strncpy(name, this->name, len);
-	hfs_tolower(name, len);
-	this->hash = full_name_hash(name, len);
+	
+	strncpy(cname.Name, this->name, this->len);
+	this->hash = hfs_strhash(&cname);
 	return 0;
 }
 
@@ -86,18 +86,11 @@
 	return hfs_streq(&s1, &s2);
 }
 
-
-/* toss a catalog entry. this does it by dropping the dentry. */
-void hfs_cat_prune(struct hfs_cat_entry *entry)
+static void hfs_dentry_iput(struct dentry *dentry, struct inode *inode)
 {
-        int i;
+	struct hfs_cat_entry *entry = HFS_I(inode)->entry;
 
-	for (i = 0; i < 4; i++) {
-	       struct dentry *de = entry->sys_entry[i];
-	       if (de) {
-			dget(de);
-		 	d_drop(de);
-		 	dput(de);
-	       }
-	}
+	entry->sys_entry[HFS_ITYPE_TO_INT(HFS_ITYPE(inode->i_ino))] = NULL;
+        hfs_cat_put(entry);
+	iput(inode);
 }
diff -u --recursive --new-file v2.1.89/linux/fs/hfs/version.c linux/fs/hfs/version.c
--- v2.1.89/linux/fs/hfs/version.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/hfs/version.c	Wed Mar 11 20:22:05 1998
@@ -7,4 +7,4 @@
  * This file contains the version string for this release.
  */
 
-const char hfs_version[]="0.95+asun2";
+const char hfs_version[]="0.95+asun3";
diff -u --recursive --new-file v2.1.89/linux/fs/ncpfs/Config.in linux/fs/ncpfs/Config.in
--- v2.1.89/linux/fs/ncpfs/Config.in	Tue Mar 10 10:03:34 1998
+++ linux/fs/ncpfs/Config.in	Tue Mar 17 11:53:16 1998
@@ -1,7 +1,7 @@
 #
 # NCP Filesystem configuration
 #
-# bool     '   Packet singatures' CONFIG_NCPFS_PACKET_SIGNING
+bool     '   Packet singatures' CONFIG_NCPFS_PACKET_SIGNING
 bool     '   Proprietary file locking' CONFIG_NCPFS_IOCTL_LOCKING
 bool     '   Clear remove/delete inhibit when needed' CONFIG_NCPFS_STRONG
 bool     '   Use NFS namespace if available' CONFIG_NCPFS_NFS_NS
diff -u --recursive --new-file v2.1.89/linux/fs/ncpfs/Makefile linux/fs/ncpfs/Makefile
--- v2.1.89/linux/fs/ncpfs/Makefile	Mon Jan  5 01:41:01 1998
+++ linux/fs/ncpfs/Makefile	Tue Mar 17 11:53:16 1998
@@ -8,7 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := ncpfs.o
-O_OBJS   := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o
+O_OBJS   := dir.o file.o inode.o ioctl.o mmap.o ncplib_kernel.o sock.o \
+		ncpsign_kernel.o
 M_OBJS   := $(O_TARGET)
 
 # If you want debugging output, please uncomment the following line
diff -u --recursive --new-file v2.1.89/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c
--- v2.1.89/linux/fs/ncpfs/dir.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/ncpfs/dir.c	Tue Mar 10 10:36:04 1998
@@ -112,7 +112,7 @@
 static int ncp_compare_dentry (struct dentry *, struct qstr *, struct qstr *);
 static void ncp_delete_dentry(struct dentry *);
 
-static struct dentry_operations ncp_dentry_operations =
+struct dentry_operations ncp_dentry_operations =
 {
 	ncp_lookup_validate,	/* d_validate(struct dentry *) */
 	ncp_hash_dentry,	/* d_hash */
diff -u --recursive --new-file v2.1.89/linux/fs/ncpfs/inode.c linux/fs/ncpfs/inode.c
--- v2.1.89/linux/fs/ncpfs/inode.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/ncpfs/inode.c	Tue Mar 17 11:53:16 1998
@@ -25,9 +25,6 @@
 #include <linux/fcntl.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
 
 #include <linux/ncp_fs.h>
 #include "ncplib_kernel.h"
@@ -287,6 +284,7 @@
 	server->buffer_size = 0;
 	server->conn_status = 0;
 	server->root_dentry = NULL;
+	server->root_setuped = 0;
 #ifdef CONFIG_NCPFS_PACKET_SIGNING
 	server->sign_wanted = 0;
 	server->sign_active = 0;
diff -u --recursive --new-file v2.1.89/linux/fs/ncpfs/ioctl.c linux/fs/ncpfs/ioctl.c
--- v2.1.89/linux/fs/ncpfs/ioctl.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/ncpfs/ioctl.c	Tue Mar 17 11:53:16 1998
@@ -32,6 +32,22 @@
 	struct ncp_ioctl_request request;
 	struct ncp_fs_info info;
 
+#ifdef NCP_IOC_GETMOUNTUID_INT
+	/* remove after ncpfs-2.0.13/2.2.0 gets released */
+	if ((NCP_IOC_GETMOUNTUID != NCP_IOC_GETMOUNTUID_INT) &&
+             (cmd == NCP_IOC_GETMOUNTUID_INT)) {
+		int tmp = server->m.mounted_uid;
+
+		if (   (permission(inode, MAY_READ) != 0)
+		    && (current->uid != server->m.mounted_uid))
+		{
+			return -EACCES;
+		}
+		if (put_user(tmp, (unsigned int*) arg)) return -EFAULT;
+		return 0;
+	}
+#endif	/* NCP_IOC_GETMOUNTUID_INT */
+
 	switch (cmd) {
 	case NCP_IOC_NCPREQUEST:
 
@@ -80,6 +96,8 @@
 		    && (current->uid != server->m.mounted_uid)) {
 			return -EACCES;
 		}
+		if (server->root_setuped) return -EBUSY;
+		server->root_setuped = 1;
 		return ncp_conn_logged_in(server);
 
 	case NCP_IOC_GET_FS_INFO:
@@ -121,19 +139,6 @@
 		put_user(server->m.mounted_uid, (uid_t *) arg);
 		return 0;
 
-	case NCP_IOC_GETMOUNTUID_INT:
-		if (   (permission(inode, MAY_READ) != 0)
-		    && (current->uid != server->m.mounted_uid))
-		{
-			return -EACCES;
-		}
-
-		{
-			unsigned int tmp=server->m.mounted_uid;
-			if (put_user(tmp, (unsigned long*) arg)) return -EFAULT;
-		}
-		return 0;
-
 #ifdef CONFIG_NCPFS_MOUNT_SUBDIR
 	case NCP_IOC_GETROOT:
 		{
@@ -168,6 +173,7 @@
 			{
 				return -EACCES;
 			}
+			if (server->root_setuped) return -EBUSY;
 			if (copy_from_user(&sr,
 					   (struct ncp_setroot_ioctl*)arg, 
 					   sizeof(sr))) return -EFAULT;
@@ -184,6 +190,7 @@
 				}
 			}
 			dentry = server->root_dentry;
+			server->root_setuped = 1;
 			if (dentry) {
 				struct inode* inode = dentry->d_inode;
 				
diff -u --recursive --new-file v2.1.89/linux/fs/ncpfs/ncpsign_kernel.c linux/fs/ncpfs/ncpsign_kernel.c
--- v2.1.89/linux/fs/ncpfs/ncpsign_kernel.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/ncpfs/ncpsign_kernel.c	Tue Mar 17 11:53:16 1998
@@ -0,0 +1,114 @@
+/*
+ *  ncpsign_kernel.c
+ *
+ *  Arne de Bruijn (arne@knoware.nl), 1997
+ *
+ */
+
+#include <linux/config.h>
+
+#ifdef CONFIG_NCPFS_PACKET_SIGNING
+
+#include <linux/string.h>
+#include <linux/ncp.h>
+#include "ncpsign_kernel.h"
+
+#define rol32(i,c) (((((i)&0xffffffff)<<c)&0xffffffff)| \
+                    (((i)&0xffffffff)>>(32-c)))
+/* i386: 32-bit, little endian, handles mis-alignment */
+#ifdef __i386__
+#define GET_LE32(p) (*(int *)(p))
+#define PUT_LE32(p,v) { *(int *)(p)=v; }
+#else
+/* from include/ncplib.h */
+#define BVAL(buf,pos) (((__u8 *)(buf))[pos])
+#define PVAL(buf,pos) ((unsigned)BVAL(buf,pos))
+#define BSET(buf,pos,val) (BVAL(buf,pos) = (val))
+
+static inline word
+WVAL_LH(__u8 * buf, int pos)
+{
+	return PVAL(buf, pos) | PVAL(buf, pos + 1) << 8;
+}
+static inline dword
+DVAL_LH(__u8 * buf, int pos)
+{
+	return WVAL_LH(buf, pos) | WVAL_LH(buf, pos + 2) << 16;
+}
+static inline void
+WSET_LH(__u8 * buf, int pos, word val)
+{
+	BSET(buf, pos, val & 0xff);
+	BSET(buf, pos + 1, val >> 8);
+}
+static inline void
+DSET_LH(__u8 * buf, int pos, dword val)
+{
+	WSET_LH(buf, pos, val & 0xffff);
+	WSET_LH(buf, pos + 2, val >> 16);
+}
+
+#define GET_LE32(p) DVAL_LH(p,0)
+#define PUT_LE32(p,v) DSET_LH(p,0,v)
+#endif
+
+#define min(a,b) ((a)<(b)?(a):(b))
+
+static void nwsign(char *r_data1, char *r_data2, char *outdata) {
+ int i;
+ unsigned int w0,w1,w2,w3;
+ static int rbit[4]={0, 2, 1, 3};
+#ifdef __i386__
+ unsigned int *data2=(int *)r_data2;
+#else
+ unsigned int data2[16];
+ for (i=0;i<16;i++)
+  data2[i]=GET_LE32(r_data2+(i<<2));
+#endif 
+ w0=GET_LE32(r_data1);
+ w1=GET_LE32(r_data1+4);
+ w2=GET_LE32(r_data1+8);
+ w3=GET_LE32(r_data1+12);
+ for (i=0;i<16;i+=4) {
+  w0=rol32(w0 + ((w1 & w2) | ((~w1) & w3)) + data2[i+0],3);
+  w3=rol32(w3 + ((w0 & w1) | ((~w0) & w2)) + data2[i+1],7);
+  w2=rol32(w2 + ((w3 & w0) | ((~w3) & w1)) + data2[i+2],11);
+  w1=rol32(w1 + ((w2 & w3) | ((~w2) & w0)) + data2[i+3],19);
+ }
+ for (i=0;i<4;i++) {
+  w0=rol32(w0 + (((w2 | w3) & w1) | (w2 & w3)) + 0x5a827999 + data2[i+0],3);
+  w3=rol32(w3 + (((w1 | w2) & w0) | (w1 & w2)) + 0x5a827999 + data2[i+4],5);
+  w2=rol32(w2 + (((w0 | w1) & w3) | (w0 & w1)) + 0x5a827999 + data2[i+8],9);
+  w1=rol32(w1 + (((w3 | w0) & w2) | (w3 & w0)) + 0x5a827999 + data2[i+12],13);
+ }
+ for (i=0;i<4;i++) {
+  w0=rol32(w0 + ((w1 ^ w2) ^ w3) + 0x6ed9eba1 + data2[rbit[i]+0],3);
+  w3=rol32(w3 + ((w0 ^ w1) ^ w2) + 0x6ed9eba1 + data2[rbit[i]+8],9);
+  w2=rol32(w2 + ((w3 ^ w0) ^ w1) + 0x6ed9eba1 + data2[rbit[i]+4],11);
+  w1=rol32(w1 + ((w2 ^ w3) ^ w0) + 0x6ed9eba1 + data2[rbit[i]+12],15);
+ }
+ PUT_LE32(outdata,(w0+GET_LE32(r_data1)) & 0xffffffff);
+ PUT_LE32(outdata+4,(w1+GET_LE32(r_data1+4)) & 0xffffffff);
+ PUT_LE32(outdata+8,(w2+GET_LE32(r_data1+8)) & 0xffffffff);
+ PUT_LE32(outdata+12,(w3+GET_LE32(r_data1+12)) & 0xffffffff);
+}
+
+/* Make a signature for the current packet and add it at the end of the */
+/* packet. */
+void sign_packet(struct ncp_server *server, int *size) {
+ char data[64];
+
+ memset(data,0,64);
+ memcpy(data,server->sign_root,8);
+ PUT_LE32(data+8,(*size));
+ memcpy(data+12,server->packet+sizeof(struct ncp_request_header)-1,
+  min((*size)-sizeof(struct ncp_request_header)+1,52));
+
+ nwsign(server->sign_last,data,server->sign_last);
+
+ memcpy(server->packet+(*size),server->sign_last,8);
+ (*size)+=8;
+}
+
+#endif	/* CONFIG_NCPFS_PACKET_SIGNING */
+
diff -u --recursive --new-file v2.1.89/linux/fs/ncpfs/ncpsign_kernel.h linux/fs/ncpfs/ncpsign_kernel.h
--- v2.1.89/linux/fs/ncpfs/ncpsign_kernel.h	Wed Dec 31 16:00:00 1969
+++ linux/fs/ncpfs/ncpsign_kernel.h	Tue Mar 17 11:53:16 1998
@@ -0,0 +1,16 @@
+/*
+ *  ncpsign_kernel.h
+ *
+ *  Arne de Bruijn (arne@knoware.nl), 1997
+ *
+ */
+ 
+#ifndef _NCPSIGN_KERNEL_H
+#define _NCPSIGN_KERNEL_H
+
+#include <linux/ncp_fs.h>
+#include <linux/ncp_fs_sb.h>
+
+void sign_packet(struct ncp_server *server, int *size);
+
+#endif
diff -u --recursive --new-file v2.1.89/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v2.1.89/linux/fs/nfs/inode.c	Fri Jan 30 11:28:08 1998
+++ linux/fs/nfs/inode.c	Tue Mar 10 13:31:22 1998
@@ -650,10 +650,31 @@
 		inode->i_ino);
 	status = nfs_proc_getattr(server, NFS_FH(dentry), &fattr);
 	if (status) {
+		int error;
+		u32 *fh;
+		struct nfs_fh fhandle;
 #ifdef NFS_PARANOIA
 printk("nfs_revalidate_inode: %s/%s getattr failed, ino=%ld, error=%d\n",
 dentry->d_parent->d_name.name, dentry->d_name.name, inode->i_ino, status);
 #endif
+		if (status != -ESTALE)
+			goto out;
+		/*
+		 * A "stale filehandle" error ... show the current fh
+		 * and find out what the filehandle should be.
+		 */
+		fh = (u32 *) NFS_FH(dentry);
+		printk("NFS: bad fh %08x%08x%08x%08x%08x%08x%08x%08x\n",
+			fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
+		error = nfs_proc_lookup(server, NFS_FH(dentry->d_parent), 
+					dentry->d_name.name, &fhandle, &fattr);
+		if (error) {
+			printk("NFS: lookup failed, error=%d\n", error);
+			goto out;
+		}
+		fh = (u32 *) &fhandle;
+		printk("            %08x%08x%08x%08x%08x%08x%08x%08x\n",
+			fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
 		goto out;
 	}
 
diff -u --recursive --new-file v2.1.89/linux/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- v2.1.89/linux/fs/nfs/nfs2xdr.c	Fri Jan 30 11:28:08 1998
+++ linux/fs/nfs/nfs2xdr.c	Tue Mar 10 13:26:56 1998
@@ -22,6 +22,9 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
 
+/* Uncomment this to support servers requiring longword lengths */
+#define NFS_PAD_WRITES 1
+
 #define NFSDBG_FACILITY		NFSDBG_XDR
 /* #define NFS_PARANOIA 1 */
 
@@ -181,7 +184,7 @@
 /*
  * Arguments to a READ call. Since we read data directly into the page
  * cache, we also set up the reply iovec here so that iov[1] points
- * exactly to the page wewant to fetch.
+ * exactly to the page we want to fetch.
  */
 static int
 nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
@@ -258,18 +261,38 @@
 static int
 nfs_xdr_writeargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args)
 {
+	u32 count = args->count;
+
 	p = xdr_encode_fhandle(p, args->fh);
 	*p++ = htonl(args->offset);
 	*p++ = htonl(args->offset);
-	*p++ = htonl(args->count);
-	*p++ = htonl(args->count);
+	*p++ = htonl(count);
+	*p++ = htonl(count);
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
 	req->rq_svec[1].iov_base = (void *) args->buffer;
-	req->rq_svec[1].iov_len = args->count;
-	req->rq_slen += args->count;
+	req->rq_svec[1].iov_len = count;
+	req->rq_slen += count;
 	req->rq_snr = 2;
 
+#ifdef NFS_PAD_WRITES
+	/*
+	 * Some old servers require that the message length
+	 * be a multiple of 4, so we pad it here if needed.
+	 */
+	count = ((count + 3) & ~3) - count;
+	if (count) {
+#if 0
+printk("nfs_writeargs: padding write, len=%d, slen=%d, pad=%d\n",
+req->rq_svec[1].iov_len, req->rq_slen, count);
+#endif
+		req->rq_svec[2].iov_base = (void *) "\0\0\0";
+		req->rq_svec[2].iov_len  = count;
+		req->rq_slen += count;
+		req->rq_snr = 3;
+	}
+#endif
+
 	return 0;
 }
 
@@ -334,12 +357,21 @@
 static int
 nfs_xdr_readdirargs(struct rpc_rqst *req, u32 *p, struct nfs_readdirargs *args)
 {
-	struct rpc_auth	*auth = req->rq_task->tk_auth;
+	struct rpc_task	*task = req->rq_task;
+	struct rpc_auth	*auth = task->tk_auth;
+	u32		bufsiz = args->bufsiz;
 	int		replen;
 
+	/*
+	 * Some servers (e.g. HP OS 9.5) seem to expect the buffer size
+	 * to be in longwords ... check whether to convert the size.
+	 */
+	if (task->tk_client->cl_flags & NFS_CLNTF_BUFSIZE)
+		bufsiz = bufsiz >> 2;
+
 	p = xdr_encode_fhandle(p, args->fh);
 	*p++ = htonl(args->cookie);
-	*p++ = htonl(args->bufsiz);
+	*p++ = htonl(bufsiz); /* see above */
 	req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
 
 	/* set up reply iovec */
@@ -380,10 +412,9 @@
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
 {
 	struct iovec		*iov = req->rq_rvec;
-	int			status, nr, len;
+	int			 status, nr;
 	char			*string, *start;
-	u32			*end;
-	__u32			fileid, cookie, *entry;
+	u32			*end, *entry, len, fileid, cookie;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
@@ -398,17 +429,25 @@
 	end = (u32 *) ((u8 *) p + iov[1].iov_len);
 
 	/* Get start and end of dirent buffer */
-	entry  = (__u32 *) res->buffer;
+	entry  = (u32 *) res->buffer;
 	start  = (char *) res->buffer;
 	string = (char *) res->buffer + res->bufsiz;
 	for (nr = 0; *p++; nr++) {
 		fileid = ntohl(*p++);
 
 		len = ntohl(*p++);
+		/*
+		 * Check whether the server has exceeded our reply buffer,
+		 * and set a flag to convert the size to longwords.
+		 */
 		if ((p + QUADLEN(len) + 3) > end) {
-			printk(KERN_WARNING "NFS: short readdir reply! "
-				"nr=%d, slots=%d, len=%d\n",
+			struct rpc_clnt *clnt = req->rq_task->tk_client;
+			printk(KERN_WARNING
+				"NFS: server %s, readdir reply truncated\n",
+				clnt->cl_server);
+			printk(KERN_WARNING "NFS: nr=%d, slots=%d, len=%d\n",
 				nr, (end - p), len);
+			clnt->cl_flags |= NFS_CLNTF_BUFSIZE;
 			break;
 		}
 		if (len > NFS_MAXNAMLEN) {
diff -u --recursive --new-file v2.1.89/linux/fs/nls/nls_base.c linux/fs/nls/nls_base.c
--- v2.1.89/linux/fs/nls/nls_base.c	Sun Dec 21 17:41:24 1997
+++ linux/fs/nls/nls_base.c	Tue Mar 10 14:43:13 1998
@@ -12,8 +12,8 @@
 #include <linux/config.h>
 #include <linux/nls.h>
 #include <linux/malloc.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 #include <asm/byteorder.h>
 
@@ -205,7 +205,7 @@
 struct nls_table *load_nls(char *charset)
 {
 	struct nls_table *nls;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	char buf[40];
 	int ret;
 #endif
@@ -216,7 +216,7 @@
 		return nls;
 	}
 
-#ifndef CONFIG_KERNELD
+#ifndef CONFIG_KMOD
 	return NULL;
 #else
 	if (strlen(charset) > sizeof(buf) - sizeof("nls_")) {
diff -u --recursive --new-file v2.1.89/linux/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- v2.1.89/linux/fs/ntfs/fs.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/ntfs/fs.c	Tue Mar 10 14:43:13 1998
@@ -776,13 +776,13 @@
 	struct buffer_head *bh;
 	int i;
 
-	/* When the driver is compiled as a module, kerneld must know when it
+	/* When the driver is compiled as a module, kmod must know when it
 	 * can safely remove it from memory. To do this, each module owns a
 	 * reference counter.
 	 */
 	MOD_INC_USE_COUNT;
 	/* Don't put ntfs_debug() before MOD_INC_USE_COUNT, printk() can block
-	 * so this could lead to a race condition with kerneld.
+	 * so this could lead to a race condition with kmod.
 	 */
 	ntfs_debug(DEBUG_OTHER, "ntfs_read_super\n");
 
@@ -939,7 +939,7 @@
 #ifdef MODULE
 /* A module is a piece of code which can be inserted in and removed
  * from the running kernel whenever you want using lsmod, or on demand using
- * kerneld
+ * kmod
  */
 
 /* No function of this module is needed by another module */
@@ -956,7 +956,7 @@
 
 /* When this code is compiled as a module, if you use mount -t ntfs when no
  * ntfs filesystem is registered (see /proc/filesystems), get_fs_type() in
- * fs/super.c asks kerneld to load the module named ntfs in memory.
+ * fs/super.c asks kmod to load the module named ntfs in memory.
  *
  * Therefore, this function is the main entry point in this case
  */
@@ -965,7 +965,7 @@
 	return init_ntfs_fs();
 }
 
-/* Called by kerneld just before the kernel removes the module from memory */
+/* Called by kmod just before the kernel removes the module from memory */
 void cleanup_module(void)
 {
 	SYSCTL(0);
diff -u --recursive --new-file v2.1.89/linux/fs/open.c linux/fs/open.c
--- v2.1.89/linux/fs/open.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/open.c	Tue Mar 10 13:28:05 1998
@@ -681,24 +681,37 @@
 }
 
 /*
- * Find an empty file descriptor entry, and mark it busy
+ * Find an empty file descriptor entry, and mark it busy.
  */
 int get_unused_fd(void)
 {
-	int fd;
 	struct files_struct * files = current->files;
+	int fd, error;
 
+	error = -EMFILE;
 	fd = find_first_zero_bit(&files->open_fds, NR_OPEN);
 	/*
 	 * N.B. For clone tasks sharing a files structure, this test
 	 * will limit the total number of files that can be opened.
 	 */
-	if (fd < current->rlim[RLIMIT_NOFILE].rlim_cur) {
-		FD_SET(fd, &files->open_fds);
-		FD_CLR(fd, &files->close_on_exec);
-		return fd;
+	if (fd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+		goto out;
+
+	/* Check here for fd > files->max_fds to do dynamic expansion */
+
+	FD_SET(fd, &files->open_fds);
+	FD_CLR(fd, &files->close_on_exec);
+#if 1
+	/* Sanity check */
+	if (files->fd[fd] != NULL) {
+		printk("get_unused_fd: slot %d not NULL!\n", fd);
+		files->fd[fd] = NULL;
 	}
-	return -EMFILE;
+#endif
+	error = fd;
+
+out:
+	return error;
 }
 
 inline void put_unused_fd(unsigned int fd)
@@ -796,15 +809,15 @@
 {
 	int error;
 	struct file * filp;
-	struct files_struct * files;
 
 	lock_kernel();
-	files = current->files;
 	error = -EBADF;
-	if (fd < NR_OPEN && (filp = files->fd[fd]) != NULL) {
+	filp = fcheck(fd);
+	if (filp) {
+		struct files_struct * files = current->files;
+		files->fd[fd] = NULL;
 		put_unused_fd(fd);
 		FD_CLR(fd, &files->close_on_exec);
-		files->fd[fd] = NULL;
 		error = close_fp(filp, files);
 	}
 	unlock_kernel();
diff -u --recursive --new-file v2.1.89/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.1.89/linux/fs/proc/array.c	Tue Mar 10 10:03:34 1998
+++ linux/fs/proc/array.c	Wed Mar 11 15:53:18 1998
@@ -446,7 +446,11 @@
 
 static int get_env(int pid, char * buffer)
 {
-	struct task_struct *p = find_task_by_pid(pid);
+	struct task_struct *p;
+	
+	read_lock(&tasklist_lock);
+	p = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
 
 	if (!p || !p->mm)
 		return 0;
@@ -455,8 +459,11 @@
 
 static int get_arg(int pid, char * buffer)
 {
-	struct task_struct *p = find_task_by_pid(pid);
+	struct task_struct *p;
 
+	read_lock(&tasklist_lock);
+	p = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
 	if (!p || !p->mm)
 		return 0;
 	return get_array(p, p->mm->arg_start, p->mm->arg_end, buffer);
@@ -760,8 +767,11 @@
 static int get_status(int pid, char * buffer)
 {
 	char * orig = buffer;
-	struct task_struct *tsk = find_task_by_pid(pid);
+	struct task_struct *tsk;
 
+	read_lock(&tasklist_lock);
+	tsk = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
 	if (!tsk)
 		return 0;
 	buffer = task_name(tsk, buffer);
@@ -773,7 +783,7 @@
 
 static int get_stat(int pid, char * buffer)
 {
-	struct task_struct *tsk = find_task_by_pid(pid);
+	struct task_struct *tsk;
 	unsigned long vsize, eip, esp, wchan;
 	long priority, nice;
 	int tty_pgrp;
@@ -784,6 +794,9 @@
 	char sigcatch_str[sizeof(sigset_t)*2+1];
 	char state;
 
+	read_lock(&tasklist_lock);
+	tsk = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
 	if (!tsk)
 		return 0;
 	state = *get_task_state(tsk);
@@ -938,6 +951,9 @@
 	struct task_struct *tsk = find_task_by_pid(pid);
 	int size=0, resident=0, share=0, trs=0, lrs=0, drs=0, dt=0;
 
+	read_lock(&tasklist_lock);
+	tsk = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
 	if (!tsk)
 		return 0;
 	if (tsk->mm && tsk->mm != &init_mm) {
@@ -1020,7 +1036,9 @@
 		goto out;
 
 	retval = -EINVAL;
+	read_lock(&tasklist_lock);
 	p = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
 	if (!p)
 		goto freepage_out;
 
@@ -1131,9 +1149,11 @@
 {
 	struct task_struct * tsk = current ;
 	int i, len;
-	
+
+	read_lock(&tasklist_lock);
 	if (pid != tsk->pid)
 		tsk = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done after the last use */
 
 	if (tsk == NULL)
 		return 0;
diff -u --recursive --new-file v2.1.89/linux/fs/proc/base.c linux/fs/proc/base.c
--- v2.1.89/linux/fs/proc/base.c	Mon Jan 12 14:46:24 1998
+++ linux/fs/proc/base.c	Wed Mar 11 15:45:53 1998
@@ -60,12 +60,14 @@
 	int pid = inode->i_ino >> 16;
 	int ino = inode->i_ino & 0xffff;
 
+	read_lock(&tasklist_lock);
 	if (fill && (p = find_task_by_pid(pid)) != NULL) {
 		if (p->dumpable || ino == PROC_PID_INO) {
 			inode->i_uid = p->euid;
 			inode->i_gid = p->gid;
 		}
 	}
+	read_unlock(&tasklist_lock);
 }
 
 /*
diff -u --recursive --new-file v2.1.89/linux/fs/proc/fd.c linux/fs/proc/fd.c
--- v2.1.89/linux/fs/proc/fd.c	Sun Sep  7 14:00:24 1997
+++ linux/fs/proc/fd.c	Wed Mar 11 15:48:37 1998
@@ -96,7 +96,9 @@
 			break;
 		}
 	}
+	read_lock(&tasklist_lock);
 	p = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done only after not using 'p' any more */
 	if (!pid || !p)
 		return -ENOENT;
 
@@ -149,7 +151,9 @@
 			return 0;
 	}
 
+	read_lock(&tasklist_lock);
 	p = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done only after not using 'p' any more */
 	if(!p)
 		return 0;
 	tarrayp = p->tarray_ptr;
diff -u --recursive --new-file v2.1.89/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v2.1.89/linux/fs/proc/inode.c	Sun Oct 12 10:16:38 1997
+++ linux/fs/proc/inode.c	Wed Mar 11 15:49:40 1998
@@ -260,7 +260,13 @@
 	inode->i_size = 0;
 	pid = ino >> 16;
 
-	if (!pid || ((p = find_task_by_pid(pid)) == NULL))
+	if (!pid)
+		return;
+	read_lock(&tasklist_lock);
+	p = find_task_by_pid(pid);
+	read_unlock(&tasklist_lock);	/* FIXME!! This should be done only after we have stopped using 'p' */
+
+	if (!p)
 		return;
 
 	ino &= 0x0000ffff;
diff -u --recursive --new-file v2.1.89/linux/fs/proc/mem.c linux/fs/proc/mem.c
--- v2.1.89/linux/fs/proc/mem.c	Tue Oct 21 08:57:29 1997
+++ linux/fs/proc/mem.c	Wed Mar 11 15:47:11 1998
@@ -83,7 +83,9 @@
 	char *tmp;
 	ssize_t scount, i;
 
+	read_lock(&tasklist_lock);
 	tsk = get_task(inode->i_ino >> 16);
+	read_unlock(&tasklist_lock);	/* FIXME: This should really be done only afetr not using tsk any more!!! */
 	if (!tsk)
 		return -ESRCH;
 	addr = *ppos;
diff -u --recursive --new-file v2.1.89/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.89/linux/fs/proc/root.c	Wed Feb  4 11:36:01 1998
+++ linux/fs/proc/root.c	Wed Mar 11 15:45:32 1998
@@ -14,8 +14,8 @@
 #include <linux/stat.h>
 #include <linux/config.h>
 #include <asm/bitops.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 /*
@@ -234,7 +234,7 @@
 }		      
 #endif
 
-#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KERNELD)
+#if defined(CONFIG_SUN_OPENPROMFS_MODULE) && defined(CONFIG_KMOD)
 static int 
 proc_openprom_defreaddir(struct inode * inode, struct file * filp,
 			 void * dirent, filldir_t filldir)
@@ -812,14 +812,18 @@
 			break;
 		}
 	}
+	read_lock(&tasklist_lock);
 	p = find_task_by_pid(pid);
 	inode = NULL;
 	if (pid && p) {
 		unsigned long ino = (pid << 16) + PROC_PID_INO;
 		inode = proc_get_inode(dir->i_sb, ino, &proc_pid);
-		if (!inode)
+		if (!inode) {
+			read_unlock(&tasklist_lock);
 			return -EINVAL;
+		}
 	}
+	read_unlock(&tasklist_lock);
 
 	dentry->d_op = &proc_dentry_operations;
 	d_add(dentry, inode);
diff -u --recursive --new-file v2.1.89/linux/fs/super.c linux/fs/super.c
--- v2.1.89/linux/fs/super.c	Tue Feb  3 18:26:29 1998
+++ linux/fs/super.c	Tue Mar 10 14:43:13 1998
@@ -37,14 +37,14 @@
 #include <asm/uaccess.h>
 #include <asm/bitops.h>
 
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
-#endif
- 
 #include <linux/nfs_fs.h>
 #include <linux/nfs_fs_sb.h>
 #include <linux/nfs_mount.h>
 
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
+
 /*
  * We use a semaphore to synchronize all mount/umount
  * activity - imagine the mess if we have a race between
@@ -405,7 +405,7 @@
 		return fs;
 	for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next)
 		;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	if (!fs && (request_module(name) == 0)) {
 		for (fs = file_systems; fs && strcmp(fs->name, name); fs = fs->next)
 			;
diff -u --recursive --new-file v2.1.89/linux/fs/umsdos/README-WIP.txt linux/fs/umsdos/README-WIP.txt
--- v2.1.89/linux/fs/umsdos/README-WIP.txt	Mon Feb 23 18:12:11 1998
+++ linux/fs/umsdos/README-WIP.txt	Tue Mar 17 21:32:17 1998
@@ -32,34 +32,42 @@
 - long file names - works
 - read file - works
 - switching MSDOS/UMSDOS - works?
-- switching UMSDOS/MSDOS - untested
-- pseudo root things - commented out mostly currently. To be fixed when
+- switching UMSDOS/MSDOS - UNTESTED
+- pseudo root things - COMMENTED OUT mostly currently. To be fixed when
   dentries stuff is straightened out.
 - resolve symlink - seems to work fully now!
 - dereference symlink - seems to work fully now!
 - hard links - seems to work now
 - special files (block/char device, fifos, sockets...) - seems to work ok.
-- other ioctls - mostly untested
+- other ioctls - MOSTLY UNTESTED
 - dangling symlink - UNTESTED !
 
-- create symlink		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- create hardlink		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- create file			- creates, but corrupts. after reboot seem ok ?
-- create special file		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- write to file			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- rename file (same dir)	- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- rename file (dif. dir)	- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- rename dir (same dir)		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- rename dir (dif. dir)		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- delete file			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- delete hardlink		- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- mkdir				- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- rmdir 			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
-- umssyncing			- WARNING: NONE OF WRITE OPERATIONS FIXED YET!
+- create symlink		- works on short names, but fails (gets
+				  truncated on long ones) (also
+				  due to some dentries problems, it may not
+				  be visible right away always - eg. before
+				  umount/mount)
+- create hardlink		- WARNING: NOT FIXED YET!
+- create file			- creates short names, but probs with long ones ?
+- create special file		- seems to work on short names.
+- write to file			- seems to work on short names.
+- rename file (same dir)	- WARNING: NOT FIXED YET!
+- rename file (dif. dir)	- WARNING: NOT FIXED YET!
+- rename dir (same dir)		- WARNING: NOT FIXED YET!
+- rename dir (dif. dir)		- WARNING: NOT FIXED YET!
+- delete file			- WARNING: NOT FIXED YET!
+- notify_change (chown,perms)	- seems to work!
+- delete hardlink		- WARNING: NOT FIXED YET!
+- mkdir				- seems to work, even with long names ! (but
+				  due to some dentries problems, it may not
+				  be visible right away always - eg. before
+				  umount/mount)
+- rmdir 			- WARNING: NOT FIXED YET!
+- umssyncing			- does something :-), but NEEDS EXTENSIVE TESTING
 
 Notes: moderate dentry/inode kernel structures trashing. Probably some other
 kernel structures compromised. Have SysRq support compiled in, and use
-Sync/Emergency-remount-RO. And don't try mounting read/write yet - and then
+Sync/Emergency-remount-RO. And if you don't try mounting read/write -
 you should have no big problems...
 
 Notes2: kernel structures trashing seems to be _MUCH_ lower if no
@@ -69,6 +77,17 @@
 is supposed to be fixed enough to work, but I haven't got the time to test
 it.
 
+Note4: on failure of creating of long filenames: MSDOS filename gets
+created, and EMD entry gets created. Check: either they mismatch, or EMD
+entry contains some wrong flags.
+
+Note5: rmdir(2) probably fails because do_rmdir calls lock_parent, which
+uses dentry->d_parent, which we neglect to set, so it returns -ENOENT.
+Probably same problem on unlink(2) ? What to do ? How to set
+dentry->d_parent to something useful ?? Must I recurse down whole pathname
+and set one by one all directory components ?! or only last one is really
+needed ? help !
+
 ------------------------------------------------------------------------------
 
 Some general notes:
@@ -85,7 +104,7 @@
 'reboot/set_environment' component that now occures every few cycles.
 
 But I need some help from someone knowing about dentries/inodes use more
-than I. If you can help, please contact me... I'm mostly worries about
+than I. If you can help, please contact me... I'm mostly worried about
 iget/iput and dget/dput, and deallocating temporary dentries we create.
 should we destroy temp dentries ? using d_invalidate ? using d_drop ? just
 dput them ?
@@ -96,9 +115,9 @@
 message. You can try mnalis@open.hr or mnalis@voyager.hr; however
 mnalis@jagor.srce.hr is preferable one.
 
-------------------------------------------------------------------------------
 
-some of my notes for myself:
+------------------------------------------------------------------------------
+some of my notes for myself /mn/:
 
 + hardlinks/symlinks. test with files in not_the_same_dir
 - also test not_the_same_dir for other file operations like rename etc.
@@ -108,3 +127,17 @@
 - what about .dotfiles ? working ? multiple dots ? etc....
 - fix stuff like dir->i_count++ to atomic_inc(&dir->i_count) and simular?
 
+
+- umsdos_create_any - calling msdos_create will create dentry for shor name. Hmmmm..?
+- kill_dentry - put it where is needed. Also dput() at needed places.
+
+- when should dput()/iput() be used ?!!
+
+- probably problem with filename mangling somewhere, since both create and
+  write to file work on short filenames, but fail on long ones. Path
+  components may be of any size (eg. mkfifo /mnt/Very_long_dir2/blah1 will
+  succeed, but mkfifo /mnt/very_long_filename.txt won't)
+
+
+- what is dir->i_count++ ? locking directory ? should this be lock_parent or
+something ?
diff -u --recursive --new-file v2.1.89/linux/fs/umsdos/dir.c linux/fs/umsdos/dir.c
--- v2.1.89/linux/fs/umsdos/dir.c	Mon Feb 23 18:12:11 1998
+++ linux/fs/umsdos/dir.c	Tue Mar 17 21:32:17 1998
@@ -31,7 +31,7 @@
    uses. It's easier to do once than hack all the other instances. Probably safer as well
 */   
 
-int compat_umsdos_real_lookup(struct inode *dir,const char *name,int len, struct inode **inode)
+int compat_umsdos_real_lookup (struct inode *dir,const char *name,int len, struct inode **inode)
 {
   int rv;
   struct dentry *dentry;
@@ -39,6 +39,7 @@
   dentry = creat_dentry (name, len, NULL);
   rv = umsdos_real_lookup(dir,dentry);
   if (inode) *inode = dentry->d_inode;
+  kill_dentry (dentry);
   
   return rv;
 }    
@@ -458,6 +459,7 @@
     if (inode->u.umsdos_i.i_emd_owner==0) printk (KERN_WARNING "emd_owner still 0 ???\n");
   }
 }
+
 struct UMSDOS_DIRENT_K{
   off_t f_pos; /* will hold the offset of the entry in EMD */
   ino_t ino;
@@ -533,6 +535,9 @@
       /* This is a DOS directory */
       struct UMSDOS_DIR_SEARCH bufk;
       struct file filp;
+      
+      fill_new_filp (&filp, NULL);
+            
       Printk ((KERN_ERR "umsdos_inode2entry emddir==NULL: WARNING: Known filp problem. segfaulting :) /mn/\n"));
       filp.f_reada = 1;
       filp.f_pos = 0;
@@ -548,6 +553,8 @@
     }else{
       /* skip . and .. see umsdos_readdir_x() */
       struct file filp;
+      fill_new_filp (&filp, NULL);
+      
       filp.f_reada = 1;
       filp.f_pos = UMSDOS_SPECIAL_DIRFPOS;
       Printk ((KERN_ERR "umsdos_inode2entry skip./..: WARNING: Known filp problem. segfaulting :) /mn/\n"));
@@ -855,6 +862,9 @@
   }else{
     struct file filp;
     loff_t offs = 0;
+
+    fill_new_filp (&filp, NULL);
+      
 
     dentry_src = creat_dentry ("hlink-mn", 8, hlink);
 
diff -u --recursive --new-file v2.1.89/linux/fs/umsdos/emd.c linux/fs/umsdos/emd.c
--- v2.1.89/linux/fs/umsdos/emd.c	Mon Feb 23 18:12:11 1998
+++ linux/fs/umsdos/emd.c	Tue Mar 17 21:32:17 1998
@@ -14,6 +14,7 @@
 #include <linux/string.h>
 #include <linux/msdos_fs.h>
 #include <linux/umsdos_fs.h>
+#include <linux/dcache.h>
 
 #include <asm/uaccess.h>
 
@@ -23,6 +24,29 @@
 #define Printk(x) printk x
 
 /*
+ * makes empty filp
+ *
+ */
+ 
+void fill_new_filp (struct file *filp, struct dentry *dentry)
+{
+    Printk (("/mn/ fill_new_filp: filling empty filp at %p\n", filp));
+    if (dentry) 
+      Printk (("     dentry=%.*s\n", (int) dentry->d_name.len, dentry->d_name.name));
+    else
+      Printk (("     dentry is NULL ! you must fill it later...\n"));
+
+    memset (filp, 0, sizeof (struct file));
+
+    filp->f_pos = 0;
+    filp->f_reada = 1;
+    filp->f_flags = O_RDWR;
+    filp->f_dentry = dentry;
+    filp->f_op = &umsdos_file_operations;        /* /mn/ - we have to fill it with SOMETHING */
+} 
+
+
+/*
  * makes dentry. for name name with length len. /mn/
  * if inode is not NULL, puts it also.
  *
@@ -34,19 +58,49 @@
     struct qstr qname;
     
     if (inode)
-      Printk (("/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
+      Printk ((KERN_DEBUG "/mn/ creat_dentry: creating dentry with inode=%lu for %.*s\n", inode->i_ino, len, name));
     else
-      Printk (("/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name));
+      Printk ((KERN_DEBUG "/mn/ creat_dentry: creating empty dentry for %.*s\n", len, name));
 
     qname.name = name;
     qname.len = len;
     qname.hash = 0;
 
     ret = d_alloc (parent,&qname);	/* create new dentry */
-    ret->d_inode = inode;
+    ret->d_inode = NULL;
+
+    if (inode) d_add (ret, inode);
+      
+/*    ret->d_inode = inode; /mn/ FIXME this was old, replaced by d_add, delete this ! */
     return ret;
 }
 
+/*
+ * removes temporary dentry created by creat_dentry
+ *
+ */
+ 
+void kill_dentry (struct dentry *dentry)
+{
+    if (dentry) {
+      Printk (("/mn/ kill_dentry: kill_dentry %.*s :", (int) dentry->d_name.len, dentry->d_name.name));
+      if (dentry->d_inode) 
+        Printk (("inode=%lu\n", dentry->d_inode->i_ino));
+      else
+        Printk (("inode is NULL\n"));
+
+      /* FIXME: is this ok ?! /mn/ */
+      /* d_invalidate (dentry); */
+      /*dput (dentry);*/
+    } else {
+      Printk (("/mn/ kill_dentry: dentry is NULL ?!\n"));
+    }
+
+    
+    Printk ((KERN_DEBUG "/mn/ kill_dentry: exiting...\n"));
+    return;
+}
+
 
 
 /*
@@ -126,23 +180,24 @@
 
 
 /*
-	Write to a file from kernel space
-*/
-ssize_t umsdos_file_write_kmem (struct inode *emd_dir,
-				struct file *filp,
+ *	Write to file from kernel space. 
+ *	Does the real job, assumes all structures are initialized !
+ */
+	
+
+ssize_t umsdos_file_write_kmem_real (struct file *filp,
 				const char *buf,
 				size_t  count,
-				loff_t *offs
-				)
+				loff_t *offs)
 {
-	int ret;
+	ssize_t ret;
 	mm_segment_t old_fs = get_fs();
-	struct dentry *old_dentry;
 	
-	Printk ((KERN_ERR " STARTED WRITE_KMEM /mn/\n"));
+	set_fs (KERNEL_DS);
 
         Printk ((KERN_ERR "umsdos_file_write_kmem /mn/: Checkin: filp=%p, buf=%p, size=%d, offs=%p\n", filp, buf, count, offs));
-        Printk ((KERN_ERR "  using emd=%ld\n", emd_dir->i_ino));
+        Printk ((KERN_ERR "  struct dentry=%p\n", filp->f_dentry));
+        Printk ((KERN_ERR "  struct inode=%p\n", filp->f_dentry->d_inode));
         Printk ((KERN_ERR "  inode=%lu, i_size=%lu\n", filp->f_dentry->d_inode->i_ino, filp->f_dentry->d_inode->i_size));
         Printk ((KERN_ERR "  ofs=%ld\n",(unsigned long) *offs));
         Printk ((KERN_ERR "  f_pos=%Lu\n", filp->f_pos));
@@ -152,23 +207,49 @@
         Printk ((KERN_ERR "  f_owner=%d\n", filp->f_owner.uid));
         Printk ((KERN_ERR "  f_version=%ld\n", filp->f_version));
         Printk ((KERN_ERR "  f_reada=%ld, f_ramax=%ld, f_raend=%ld, f_ralen=%ld, f_rawin=%ld\n", filp->f_reada, filp->f_ramax, filp->f_raend, filp->f_ralen, filp->f_rawin));
+
+	ret = fat_file_write (filp, buf, count, offs);
+	PRINTK ((KERN_ERR "fat_file_write returned with %ld!\n", ret));
+
+	set_fs (old_fs);
+	return ret;
+}
+
+
+/*
+ *	Write to a file from kernel space
+ */
+ 
+ssize_t umsdos_file_write_kmem (struct inode *emd_dir,
+				struct file *filp,
+				const char *buf,
+				size_t  count,
+				loff_t *offs
+				)
+{
+	int ret;
+	struct dentry *old_dentry;
+
 	
-	set_fs (KERNEL_DS);
+	Printk ((KERN_ERR " STARTED WRITE_KMEM /mn/\n"));
+        Printk ((KERN_ERR "  using emd=%ld\n", emd_dir->i_ino));
+
 	old_dentry=filp->f_dentry;	/* save it */
 	filp->f_dentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, emd_dir);
-	*offs = filp->f_pos;
 
-	ret = fat_file_write (filp, buf, count, offs);
-	PRINTK ((KERN_ERR "fat_file_write returned with %ld!\n", ret));
+	*offs = filp->f_pos;	/* FIXME, in read_kmem also: offs is not used so why pass it ?!!! /mn/ */
+
+        ret=umsdos_file_write_kmem_real (filp, buf, count, offs);
 
 	filp->f_pos = *offs;
 	filp->f_dentry=old_dentry;
 
-	set_fs (old_fs);
 	return ret;
 }
 
 
+
+
 /*
 	Write a block of bytes into one EMD file.
 	The block of data is NOT in user space.
@@ -201,7 +282,7 @@
 #endif	
 	
 	if (offs) myofs=*offs;	/* if offs is not NULL, read it */
-	Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %ld, %Ld\n", emd_dir, filp, buf, count, myofs));
+	Printk (("umsdos_emd_dir_write /mn/: calling write_kmem with %p, %p, %p, %d, %Ld\n", emd_dir, filp, buf, count, myofs));
 	written = umsdos_file_write_kmem (emd_dir, filp, buf, count, &myofs);
 	Printk (("umsdos_emd_dir_write /mn/: write_kmem returned\n"));
 	if (offs) *offs=myofs;	/* if offs is not NULL, store myofs there */
@@ -216,6 +297,12 @@
 	d->rdev = le16_to_cpu (d->rdev);
 	d->mode = le16_to_cpu (d->mode);
 #endif	
+
+#ifdef 1
+	if (written != count) Printk ((KERN_ERR "umsdos_emd_dir_write: ERROR: written (%d) != count (%d)\n", written, count));
+#endif
+
+
 	return written != count ? -EIO : 0;
 }
 
@@ -409,7 +496,9 @@
 	struct file filp;
 	struct umsdos_dirent *entry = &info->entry;
 	struct umsdos_dirent entry0;
-	
+
+        fill_new_filp (&filp, NULL);
+
 	Printk (("umsdos_writeentry /mn/: entering...\n"));
 	emd_dentry=creat_dentry ("wremd_mn", 8, emd_dir);
 	
@@ -444,7 +533,7 @@
        	filp.f_op = &umsdos_file_operations;	/* /mn/ - we have to fill it with dummy values so we won't segfault */
        	
 	ret = umsdos_emd_dir_write (emd_dir, &filp, (char*)entry, info->recsize, NULL);
-	Printk (("emd_dir_write returned !\n"));
+	Printk (("emd_dir_write returned with %d!\n", ret));
 	if (ret != 0){
 		printk ("UMSDOS: problem with EMD file. Can't write\n");
 	}else{
@@ -452,7 +541,7 @@
 		/* dir->i_dirt = 1; FIXME iput/dput ??? */
 	}
 
-	Printk (("umsdos_writeentry /mn/: returning...\n"));
+	Printk (("umsdos_writeentry /mn/: returning %d...\n", ret));
 	return ret;
 }
 
@@ -541,10 +630,13 @@
 		record, multiple contiguous record are allocated.
 	*/
 	int ret = -ENOENT;
-	/* FIXME  -- /mn/ fixed ? */
-	struct inode *emd_dir = umsdos_emd_dir_lookup (dir, 1);
+	struct inode *emd_dir;
+	struct umsdos_dirent *entry = &info->entry;
+	
+	Printk (("umsdos_find: locating %.*s in dir %lu\n", entry->name_len, entry->name, dir->i_ino));
+	
+	emd_dir = umsdos_emd_dir_lookup (dir, 1);
 	if (emd_dir != NULL){
-		struct umsdos_dirent *entry = &info->entry;
 		int recsize = info->recsize;
 		struct {
 			off_t posok;	/* Position available to store the entry */
@@ -560,11 +652,7 @@
 
 		dentry = creat_dentry ("umsfind-mn", 10, emd_dir);
 	
-		buf.filp.f_pos = 0;
-		buf.filp.f_reada = 1;
-		buf.filp.f_flags = O_RDONLY;
-		buf.filp.f_dentry = dentry;
-        	buf.filp.f_op = &umsdos_file_operations;	/* /mn/ - we have to fill it with dummy values so we won't segfault */
+		fill_new_filp (&buf.filp, dentry);
 
 		buf.pos = 0;
 		buf.size = 0;
@@ -631,6 +719,8 @@
 		umsdos_manglename(info);
 	}
 	*pt_emd_dir = emd_dir;
+	
+	Printk (("umsdos_find: returning %d\n", ret));
 	return ret;
 }
 
@@ -651,7 +741,7 @@
 		ret = -EEXIST;
 	}else if (ret == -ENOENT){
 		ret = umsdos_writeentry(dir,emd_dir,info,0);
-		Printk (("umsdos_newentry EDM ret = %d\n",ret));
+		Printk (("umsdos_newentry EMD ret = %d\n",ret));
 	}
 	iput (emd_dir);
 	return ret;
@@ -729,6 +819,8 @@
 	/* If the EMD file does not exist, it is certainly empty :-) */
 	if (emd_dir != NULL){
 		struct file filp;
+                fill_new_filp (&filp, NULL);
+
 		/* Find an empty slot */
 		memset (&filp, 0, sizeof (filp));
 
@@ -779,6 +871,7 @@
 		}
 	}
 	iput (emd_dir);
+	Printk (("umsdos_findentry: returning %d\n", ret));
 	return ret;
 }
 
diff -u --recursive --new-file v2.1.89/linux/fs/umsdos/inode.c linux/fs/umsdos/inode.c
--- v2.1.89/linux/fs/umsdos/inode.c	Mon Feb 23 18:12:11 1998
+++ linux/fs/umsdos/inode.c	Tue Mar 17 21:32:17 1998
@@ -105,7 +105,7 @@
       inode->i_op = &umsdos_dir_inode_operations;
     }
     
-    iput (emd_dir);
+/*    iput (emd_dir); FIXME /mn/ ! */
   }
 }
 
@@ -119,9 +119,10 @@
 			off_t f_pos)
 {
     struct inode *emd_owner;
-    /* FIXME, I don't have a clue on this one */
-    Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue\n"));
+    /* FIXME, I don't have a clue on this one - /mn/ hmmm ? ok ? */
+/*    Printk ((KERN_WARNING "umsdos_set_dirinfo: /mn/ FIXME: no clue. inode=%lu dir=%lu\n", inode->i_ino, dir->i_ino));*/
     emd_owner = umsdos_emd_dir_lookup(dir,1);
+    Printk (("umsdos_set_dirinfo: emd_owner is %lu for dir %lu\n", emd_owner->i_ino, dir->i_ino));
     inode->u.umsdos_i.i_dir_owner = dir->i_ino;
     inode->u.umsdos_i.i_emd_owner = emd_owner->i_ino;
     iput (emd_owner);
@@ -317,10 +318,10 @@
 	/* FIXME inode->i_dirt = 0; */
 }
 
-int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr)
+
+int internal_notify_change(struct inode *inode, struct iattr *attr)
 {
   int ret = 0;
-  struct inode *inode = dentry->d_inode;
   
   Printk ((KERN_ERR "UMSDOS_notify_change: /mn/ completly untested\n"));
   
@@ -362,10 +363,15 @@
       }else{
 	struct file filp;
 	struct umsdos_dirent entry;
+	struct dentry *emd_dentry;
 	loff_t offs;
-	offs = 0;
+	
+	emd_dentry = creat_dentry ("notify_emd", 10, emd_owner);
+	fill_new_filp (&filp, emd_dentry);
+	
 	filp.f_pos = inode->u.umsdos_i.pos;
 	filp.f_reada = 0;
+	offs = filp.f_pos;	/* FIXME: /mn/ is this ok ? */
 	Printk (("pos = %Lu ", filp.f_pos));
 	/* Read only the start of the entry since we don't touch */
 	/* the name */
@@ -386,7 +392,7 @@
 	  
 	  entry.nlink = inode->i_nlink;
 	  filp.f_pos = inode->u.umsdos_i.pos;
-	  offs = 0; /* FIXME */
+	  offs = filp.f_pos;	/* FIXME: /mn/ is this ok ? */
 	  ret = umsdos_emd_dir_write (emd_owner, &filp, (char*)&entry, UMSDOS_REC_SIZE, &offs);
 	  
 	  Printk (("notify pos %lu ret %d nlink %d "
@@ -407,6 +413,15 @@
   return ret;
 }
 
+
+int UMSDOS_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+  return internal_notify_change (dentry->d_inode, attr);
+}
+
+
+
+
 /* #Specification: function name / convention
    A simple convention for function name has been used in
    the UMSDOS file system. First all function use the prefix
@@ -457,7 +472,7 @@
     PRINTK ((KERN_DEBUG "UMSDOS /mn/: sb = %p\n",sb));
     res = msdos_read_super(sb,data,silent);
     PRINTK ((KERN_DEBUG "UMSDOS /mn/: res = %p\n",res));
-    printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-1 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
+    printk (KERN_INFO "UMSDOS dentry-WIP-Beta 0.82-2 (compatibility level %d.%d, fast msdos)\n", UMSDOS_VERSION, UMSDOS_RELEASE);
 	  
     if (res == NULL) { MOD_DEC_USE_COUNT; return NULL; }
 
@@ -504,7 +519,7 @@
 	 The word "linux" is hardcoded in /usr/include/linux/umsdos_fs.h
 	 in the macro UMSDOS_PSDROOT_NAME.
       */
-      struct dentry *root, *etc, *etc_rc, *init, *sbin; /* FIXME */
+      struct dentry *root, *etc, *etc_rc, *init, *sbin;
 
       root = creat_dentry (UMSDOS_PSDROOT_NAME, strlen(UMSDOS_PSDROOT_NAME), NULL);
       sbin = creat_dentry ("sbin", 4, NULL);
diff -u --recursive --new-file v2.1.89/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v2.1.89/linux/fs/umsdos/namei.c	Mon Feb 23 18:12:11 1998
+++ linux/fs/umsdos/namei.c	Tue Mar 17 21:32:17 1998
@@ -39,6 +39,7 @@
   }
   return ret;
 }
+
 /*
 	Wait for any lookup process to finish
 */
@@ -48,6 +49,7 @@
     sleep_on(&dir->u.umsdos_i.u.dir_info.p);
   }
 }
+
 /*
 	Lock all other process out of this directory.
 */
@@ -90,6 +92,7 @@
   dir->u.umsdos_i.u.dir_info.pid = current->pid;
   umsdos_waitlookup (dir);
 }
+
 /*
 	Lock all other process out of those two directories.
 */
@@ -115,6 +118,7 @@
   umsdos_waitlookup(dir1);
   umsdos_waitlookup(dir2);
 }
+
 /*
 	Wait until creation is finish in this directory.
 */
@@ -157,6 +161,7 @@
 static void umsdos_unlockcreate (struct inode *dir){}
 void umsdos_endlookup (struct inode *dir){}
 #endif
+
 static int umsdos_nevercreat(
 			     struct inode *dir,
 			     struct dentry *dentry,
@@ -210,7 +215,11 @@
      /* file */
 {
     
-  int ret = umsdos_nevercreat(dir,dentry,-EEXIST);
+  int ret;
+  
+  Printk (("umsdos_create_any /mn/: create %.*s in dir=%lu - nevercreat=/", (int) dentry->d_name.len, dentry->d_name.name, dir->i_ino));
+  ret = umsdos_nevercreat(dir,dentry,-EEXIST);
+  Printk (("%d/\n", ret));
   if (ret == 0){
     struct umsdos_info info;
     ret = umsdos_parse(dentry->d_name.name,dentry->d_name.len,&info);
@@ -237,8 +246,8 @@
 	if (ret == 0){
 	  struct inode *inode = dentry->d_inode;
 	  umsdos_lookup_patch (dir,inode,&info.entry,info.f_pos);
-	  Printk (("inode %p[%d] ",inode,inode->i_count));
-	  Printk (("Creation OK: [%lu] %.*s %d pos %ld\n", dir->i_ino,
+	  Printk (("inode %p[%lu], count=%d ",inode, inode->i_ino, inode->i_count));
+	  Printk (("Creation OK: [dir %lu] %.*s pid=%d pos %ld\n", dir->i_ino,
 		   info.fake.len, info.fake.fname, current->pid, info.f_pos));
 	}else{
 	  /* #Specification: create / file exist in DOS
@@ -276,9 +285,10 @@
       umsdos_unlockcreate(dir);
     }
   }
-  d_add(dentry,dir);
+  /* d_add(dentry,dir); /mn/ FIXME: msdos_create already did this for short name ! */
   return ret;
 }
+
 /*
 	Initialise the new_entry from the old for a rename operation.
 	(Only useful for umsdos_rename_f() below).
@@ -425,6 +435,7 @@
   Printk (("\n"));
   return ret;
 }
+
 /*
 	Setup un Symbolic link or a (pseudo) hard link
 	Return a negative error code or 0 if ok.
@@ -456,11 +467,13 @@
   if (ret == 0){
     int len = strlen(symname);
     struct file filp;
-    filp.f_pos = 0;
+    loff_t myofs=0;
+    fill_new_filp (&filp, dentry);
+
     /* Make the inode acceptable to MSDOS FIXME */
     Printk ((KERN_ERR "umsdos_symlink_x: FIXME /mn/ Here goes the crash.. known wrong code...\n"));
-    ret = umsdos_file_write_kmem (dentry->d_inode, &filp,symname,ret,NULL);	/* FIXME /mn/: dentry->d_inode->i_ino is totaly wrong, just put in to compile the beast...
- PTW dentry->d_inode is "less incorrect" 										 */
+    Printk ((KERN_WARNING "   symname=%s ; dentry name=%.*s (ino=%lu)\n", symname, (int) dentry->d_name.len, dentry->d_name.name, dentry->d_inode->i_ino));
+    ret = umsdos_file_write_kmem_real (&filp, symname, len, &myofs);
     /* dput(dentry); ?? where did this come from FIXME */
     if (ret >= 0){
       if (ret != len){
@@ -480,6 +493,7 @@
   Printk (("\n"));
   return ret;
 }
+
 /*
   Setup un Symbolic link.
   Return a negative error code or 0 if ok.
@@ -492,6 +506,7 @@
 {
   return umsdos_symlink_x (dir,dentry,symname,S_IFLNK|0777,0);
 }
+
 /*
   Add a link to an inode in a directory
 */
@@ -671,6 +686,9 @@
   Printk (("umsdos_link %d\n",ret));
   return ret;
 }
+
+
+
 /*
   Add a new file into the alternate directory.
   The file is added to the real MSDOS directory. If successful, it
@@ -680,13 +698,16 @@
 */
 int UMSDOS_create (
 		   struct inode *dir,
-		   struct dentry *dentry, /* Length of the name */
+		   struct dentry *dentry,
 		   int mode		  /* Permission bit + file type ??? */
 		   )	/* Will hold the inode of the newly created */
 							/* file */
 {
   return umsdos_create_any (dir,dentry,mode,0,0);
 }
+
+
+
 /*
 	Add a sub-directory in a directory
 */
@@ -735,7 +756,7 @@
 	  ret = compat_umsdos_real_lookup (dir,info.fake.fname,
 					   info.fake.len,&subdir);
 	  if (ret == 0){
-	    struct inode *result;
+/*	    struct inode *result;	FIXME /mn/ hmmm what is this supposed to be ? */
 	    struct dentry *tdentry;
 	    tdentry = creat_dentry (UMSDOS_EMD_FILE, UMSDOS_EMD_NAMELEN, NULL);
 
@@ -753,9 +774,10 @@
     }
   }
   Printk (("umsdos_mkdir %d\n",ret));
-  dput (dentry);
+/*  dput (dentry); FIXME /mn/ */
   return ret;
 }
+
 /*
 	Add a new device special file into a directory.
 */
@@ -779,8 +801,9 @@
      for ordinary files was causing major trouble with hard link
      in particular and other parts of the kernel I guess.
   */
+
   int ret = umsdos_create_any (dir,dentry,mode,rdev,0);
-  dput(dentry);
+/*  dput(dentry); /mn/ FIXME! */
   return ret;
 }
 
@@ -952,13 +975,20 @@
 	struct inode * dir,
 	struct dentry *dentry)
 {
-  int ret = umsdos_nevercreat(dir,dentry,-EPERM);
+  int ret;
+  Printk ((" *** UMSDOS_unlink entering /mn/ *** \n"));
+
+  ret = umsdos_nevercreat(dir,dentry,-EPERM);
+  
+  Printk (("UMSDOS_unlink /mn/: nevercreat=%d\n", ret));
+  
   if (ret == 0){
     struct umsdos_info info;
     ret = umsdos_parse (dentry->d_name.name,dentry->d_name.len,&info);
     if (ret == 0){
       umsdos_lockcreate(dir);
       ret = umsdos_findentry(dir,&info,1);
+      Printk (("UMSDOS_unlink: findentry returned %d\n", ret));
       if (ret == 0){
 	Printk (("UMSDOS_unlink %.*s ",info.fake.len,info.fake.fname));
 				/* check sticky bit */
diff -u --recursive --new-file v2.1.89/linux/fs/umsdos/symlink.c linux/fs/umsdos/symlink.c
--- v2.1.89/linux/fs/umsdos/symlink.c	Mon Feb 23 18:12:11 1998
+++ linux/fs/umsdos/symlink.c	Tue Mar 17 21:32:17 1998
@@ -39,7 +39,8 @@
 	int ret;
 	loff_t loffs = 0;
 	struct file filp;
-	
+
+        fill_new_filp (&filp, NULL);
 	
 	ret = dentry->d_inode->i_size;
 	
diff -u --recursive --new-file v2.1.89/linux/include/asm-alpha/fpu.h linux/include/asm-alpha/fpu.h
--- v2.1.89/linux/include/asm-alpha/fpu.h	Tue Feb 17 13:12:48 1998
+++ linux/include/asm-alpha/fpu.h	Sun Mar 15 09:57:33 1998
@@ -37,21 +37,21 @@
  * compatibly.  The corresponding definitions are in
  * /usr/include/machine/fpu.h under OSF/1.
  */
-#define IEEE_TRAP_ENABLE_INV	(1<<1)	/* invalid op */
-#define IEEE_TRAP_ENABLE_DZE	(1<<2)	/* division by zero */
-#define IEEE_TRAP_ENABLE_OVF	(1<<3)	/* overflow */
-#define IEEE_TRAP_ENABLE_UNF	(1<<4)	/* underflow */
-#define IEEE_TRAP_ENABLE_INE	(1<<5)	/* inexact */
+#define IEEE_TRAP_ENABLE_INV	(1UL<<1)	/* invalid op */
+#define IEEE_TRAP_ENABLE_DZE	(1UL<<2)	/* division by zero */
+#define IEEE_TRAP_ENABLE_OVF	(1UL<<3)	/* overflow */
+#define IEEE_TRAP_ENABLE_UNF	(1UL<<4)	/* underflow */
+#define IEEE_TRAP_ENABLE_INE	(1UL<<5)	/* inexact */
 #define IEEE_TRAP_ENABLE_MASK	(IEEE_TRAP_ENABLE_INV | IEEE_TRAP_ENABLE_DZE |\
 				 IEEE_TRAP_ENABLE_OVF | IEEE_TRAP_ENABLE_UNF |\
 				 IEEE_TRAP_ENABLE_INE)
 
 /* status bits coming from fpcr: */
-#define IEEE_STATUS_INV		(1<<17)
-#define IEEE_STATUS_DZE		(1<<18)
-#define IEEE_STATUS_OVF		(1<<19)
-#define IEEE_STATUS_UNF		(1<<20)
-#define IEEE_STATUS_INE		(1<<21)
+#define IEEE_STATUS_INV		(1UL<<17)
+#define IEEE_STATUS_DZE		(1UL<<18)
+#define IEEE_STATUS_OVF		(1UL<<19)
+#define IEEE_STATUS_UNF		(1UL<<20)
+#define IEEE_STATUS_INE		(1UL<<21)
 
 #define IEEE_STATUS_MASK	(IEEE_STATUS_INV | IEEE_STATUS_DZE |	\
 				 IEEE_STATUS_OVF | IEEE_STATUS_UNF |	\
@@ -64,7 +64,7 @@
 #define IEEE_INHERIT    (1UL<<63)	/* inherit on thread create? */
 
 /*
- * Convert the spftware IEEE trap enable and status bits into the
+ * Convert the software IEEE trap enable and status bits into the
  * hardware fpcr format.
  */
 
diff -u --recursive --new-file v2.1.89/linux/include/asm-i386/page.h linux/include/asm-i386/page.h
--- v2.1.89/linux/include/asm-i386/page.h	Mon Oct 14 23:14:53 1996
+++ linux/include/asm-i386/page.h	Tue Mar 17 21:42:20 1998
@@ -1,12 +1,15 @@
 #ifndef _I386_PAGE_H
 #define _I386_PAGE_H
 
+#include <linux/config.h>
+
 /* PAGE_SHIFT determines the page size */
 #define PAGE_SHIFT	12
 #define PAGE_SIZE	(1UL << PAGE_SHIFT)
 #define PAGE_MASK	(~(PAGE_SIZE-1))
 
 #ifdef __KERNEL__
+#ifndef __ASSEMBLY__
 
 #define STRICT_MM_TYPECHECKS
 
@@ -52,12 +55,14 @@
 #define __pgprot(x)	(x)
 
 #endif
+#endif /* !__ASSEMBLY__ */
 
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
 
 /* This handles the memory map.. */
-#define PAGE_OFFSET		0xC0000000
+#define __PAGE_OFFSET		((0x1000-CONFIG_MAX_MEMSIZE)<<20)
+#define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
 #define MAP_NR(addr)		(__pa(addr) >> PAGE_SHIFT)
diff -u --recursive --new-file v2.1.89/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h
--- v2.1.89/linux/include/asm-i386/pgtable.h	Tue Feb 17 13:12:48 1998
+++ linux/include/asm-i386/pgtable.h	Tue Mar 17 21:42:22 1998
@@ -13,6 +13,7 @@
  * the i386 page table tree.
  */
 
+#ifndef __ASSEMBLY__
 /* Caches aren't brain-dead on the intel. */
 #define flush_cache_all()			do { } while (0)
 #define flush_cache_mm(mm)			do { } while (0)
@@ -155,6 +156,7 @@
 }
 #endif
 #endif
+#endif /* !__ASSEMBLY__ */
 
 
 /* Certain architectures need to do special things when pte's
@@ -181,6 +183,16 @@
 #define PTRS_PER_PMD	1
 #define PTRS_PER_PGD	1024
 
+/*
+ * pgd entries used up by user/kernel:
+ */
+
+#define USER_PGD_PTRS (PAGE_OFFSET >> PGDIR_SHIFT)
+#define KERNEL_PGD_PTRS (PTRS_PER_PGD-USER_PGD_PTRS)
+#define __USER_PGD_PTRS ((__PAGE_OFFSET >> PGDIR_SHIFT) & 0x3ff)
+#define __KERNEL_PGD_PTRS (PTRS_PER_PGD-__USER_PGD_PTRS)
+
+#ifndef __ASSEMBLY__
 /* Just any arbitrary offset to the start of the vmalloc VM area: the
  * current 8MB value just means that there will be a 8MB "hole" after the
  * physical memory until the kernel virtual memory starts.  That means that
@@ -496,5 +508,7 @@
 
 #define module_map      vmalloc
 #define module_unmap    vfree
+
+#endif /* !__ASSEMBLY__ */
 
 #endif /* _I386_PAGE_H */
diff -u --recursive --new-file v2.1.89/linux/include/asm-i386/processor.h linux/include/asm-i386/processor.h
--- v2.1.89/linux/include/asm-i386/processor.h	Thu Jan 15 20:47:55 1998
+++ linux/include/asm-i386/processor.h	Tue Mar 17 21:42:21 1998
@@ -10,6 +10,7 @@
 #include <asm/vm86.h>
 #include <asm/math_emu.h>
 #include <asm/segment.h>
+#include <asm/page.h>
 
 /*
  *  CPU type and hardware bug flags. Kept separately for each CPU.
@@ -71,10 +72,9 @@
 extern unsigned int BIOS_revision;
 
 /*
- * User space process size: 3GB. This is hardcoded into a few places,
- * so don't change it unless you know what you are doing.
+ * User space process size: 3GB (default).
  */
-#define TASK_SIZE	(0xC0000000UL)
+#define TASK_SIZE	(PAGE_OFFSET)
 
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
diff -u --recursive --new-file v2.1.89/linux/include/asm-i386/uaccess.h linux/include/asm-i386/uaccess.h
--- v2.1.89/linux/include/asm-i386/uaccess.h	Fri Feb  6 15:32:54 1998
+++ linux/include/asm-i386/uaccess.h	Tue Mar 17 21:42:24 1998
@@ -5,6 +5,7 @@
  * User space memory access functions
  */
 #include <linux/sched.h>
+#include <asm/page.h>
 
 #define VERIFY_READ 0
 #define VERIFY_WRITE 1
@@ -21,7 +22,7 @@
 
 
 #define KERNEL_DS	MAKE_MM_SEG(0xFFFFFFFF)
-#define USER_DS		MAKE_MM_SEG(0xC0000000)
+#define USER_DS		MAKE_MM_SEG(PAGE_OFFSET)
 
 #define get_ds()	(KERNEL_DS)
 #define get_fs()	(current->addr_limit)
diff -u --recursive --new-file v2.1.89/linux/include/linux/coda.h linux/include/linux/coda.h
--- v2.1.89/linux/include/linux/coda.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/coda.h	Tue Mar 17 21:19:05 1998
@@ -587,9 +587,9 @@
 };
 
 /* 
- * Occasionally, don't cache the fid returned by CFS_LOOKUP. For instance, if
- * the fid is inconsistent. This case is handled by setting the top bit of the
- * return result parameter.
+ * Occasionally, we don't cache the fid returned by CFS_LOOKUP. 
+ * For instance, if the fid is inconsistent. 
+ * This case is handled by setting the top bit of the type result parameter.
  */
 #define CFS_NOCACHE          0x80000000
 
diff -u --recursive --new-file v2.1.89/linux/include/linux/coda_cache.h linux/include/linux/coda_cache.h
--- v2.1.89/linux/include/linux/coda_cache.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/coda_cache.h	Tue Mar 17 21:19:05 1998
@@ -21,19 +21,16 @@
 	struct coda_cred   cc_cred;
 };
 
-void coda_ccinsert(struct coda_cache *el, struct super_block *sb);
-void coda_cninsert(struct coda_cache *el, struct coda_inode_info *cnp);
-void coda_ccremove(struct coda_cache *el);
-void coda_cnremove(struct coda_cache *el);
-void coda_cache_create(struct inode *inode, int mask);
-struct coda_cache *coda_cache_find(struct inode *inode);
+/* credential cache */
 void coda_cache_enter(struct inode *inode, int mask);
-void coda_cache_clear_cnp(struct coda_inode_info *cnp);
+void coda_cache_clear_inode(struct inode *);
 void coda_cache_clear_all(struct super_block *sb);
 void coda_cache_clear_cred(struct super_block *sb, struct coda_cred *cred);
 int coda_cache_check(struct inode *inode, int mask);
-void coda_dentry_delete(struct dentry *dentry);
-void coda_zapfid(struct ViceFid *fid, struct super_block *sb, int flag);
+
+/* for downcalls and attributes and lookups */
+void coda_flag_inode(struct inode *inode, int flag);
+void coda_flag_alias_children(struct inode *inode, int flag);
 
 
 /*
diff -u --recursive --new-file v2.1.89/linux/include/linux/coda_fs_i.h linux/include/linux/coda_fs_i.h
--- v2.1.89/linux/include/linux/coda_fs_i.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/coda_fs_i.h	Tue Mar 17 21:19:05 1998
@@ -17,7 +17,7 @@
 
 #define CODA_CNODE_MAGIC        0x47114711
 /*
- * smb fs inode data (in memory only)
+ * coda fs inode data
  */
 struct coda_inode_info {
         struct ViceFid     c_fid;	/* Coda identifier */
@@ -36,16 +36,13 @@
 #define C_VATTR       0x1         /* Validity of vattr in the cnode */
 #define C_SYMLINK     0x2         /* Validity of symlink pointer in the cnode */
 #define C_DYING       0x4	  /* Set for outstanding cnodes from venus (which died) */
-#define C_ZAPFID      0x8
+#define C_PURGE      0x8
 #define C_ZAPDIR      0x10
 #define C_INITED      0x20
 
 int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
 int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
 struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb);
-
-/* inode to cnode */
-#define ITOC(inode) ((struct coda_inode_info *)&((inode)->u.coda_i))
 
 
 #endif
diff -u --recursive --new-file v2.1.89/linux/include/linux/coda_linux.h linux/include/linux/coda_linux.h
--- v2.1.89/linux/include/linux/coda_linux.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/coda_linux.h	Tue Mar 17 21:19:05 1998
@@ -36,6 +36,7 @@
 int coda_open(struct inode *i, struct file *f);
 int coda_release(struct inode *i, struct file *f);
 int coda_permission(struct inode *inode, int mask);
+int coda_revalidate_inode(struct dentry *);
 
 /* global variables */
 extern int coda_debug;
@@ -43,10 +44,13 @@
 extern int coda_access_cache;
 
 /* this file:  heloers */
+static __inline__ struct ViceFid *coda_i2f(struct inode *);
 char *coda_f2s(ViceFid *f);
 int coda_isroot(struct inode *i);
 int coda_fid_is_volroot(struct ViceFid *);
 int coda_iscontrol(const char *name, size_t length);
+
+
 void coda_load_creds(struct coda_cred *cred);
 int coda_mycred(struct coda_cred *);
 void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
@@ -111,5 +115,19 @@
 
 
 #define CODA_FREE(ptr,size) do {if (size < 3000) { kfree_s((ptr), (size)); CDEBUG(D_MALLOC, "kfreed: %x at %x.\n", (int) size, (int) ptr); } else { vfree((ptr)); CDEBUG(D_MALLOC, "vfreed: %x at %x.\n", (int) size, (int) ptr);} } while (0)
+
+/* inode to cnode */
+
+static __inline__ struct ViceFid *coda_i2f(struct inode *inode)
+{
+	return &(inode->u.coda_i.c_fid);
+}
+
+#define ITOC(inode) (&((inode)->u.coda_i))
+
+
+
+
+
 
 #endif
diff -u --recursive --new-file v2.1.89/linux/include/linux/file.h linux/include/linux/file.h
--- v2.1.89/linux/include/linux/file.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/file.h	Tue Mar 10 13:28:04 1998
@@ -1,19 +1,41 @@
+/*
+ * Wrapper functions for accessing the file_struct fd array.
+ */
+
 #ifndef __LINUX_FILE_H
 #define __LINUX_FILE_H
 
-extern inline struct file * fget(unsigned long fd)
+extern int __fput(struct file *);
+extern void insert_file_free(struct file *file);
+
+/*
+ * Check whether the specified fd has an open file.
+ */
+extern inline struct file * fcheck(unsigned int fd)
 {
 	struct file * file = NULL;
-	if (fd < NR_OPEN) {
+
+	if (fd < NR_OPEN)
 		file = current->files->fd[fd];
-		if (file)
-			file->f_count++;
-	}
 	return file;
 }
 
-extern int __fput(struct file *);
-extern void insert_file_free(struct file *file);
+extern inline struct file * fget(unsigned int fd)
+{
+	struct file * file = fcheck(fd);
+
+	if (file)
+		file->f_count++;
+	return file;
+}
+
+/*
+ * Install a file pointer in the fd array.
+ */
+extern inline void fd_install(unsigned int fd, struct file *file)
+{
+	current->files->fd[fd] = file;
+}
 
 /* It does not matter which list it is on. */
 extern inline void remove_filp(struct file *file)
@@ -45,14 +67,6 @@
 		remove_filp(file);
 		insert_file_free(file);
 	}
-}
-
-/*
- * Install a file pointer in the files structure.
- */
-extern inline void fd_install(unsigned long fd, struct file *file)
-{
-	current->files->fd[fd] = file;
 }
 
 #endif
diff -u --recursive --new-file v2.1.89/linux/include/linux/hfs_fs.h linux/include/linux/hfs_fs.h
--- v2.1.89/linux/include/linux/hfs_fs.h	Fri Feb  6 15:34:54 1998
+++ linux/include/linux/hfs_fs.h	Tue Mar 17 21:45:50 1998
@@ -237,20 +237,20 @@
 extern struct inode_operations hfs_cap_ndir_inode_operations;
 extern struct inode_operations hfs_cap_fdir_inode_operations;
 extern struct inode_operations hfs_cap_rdir_inode_operations;
-extern void hfs_cap_drop_dentry(const ino_t, struct dentry *);
+extern void hfs_cap_drop_dentry(struct dentry *, const ino_t);
 
 /* dir_dbl.c */
 extern const struct hfs_name hfs_dbl_reserved1[];
 extern const struct hfs_name hfs_dbl_reserved2[];
 extern struct inode_operations hfs_dbl_dir_inode_operations;
-extern void hfs_dbl_drop_dentry(const ino_t, struct dentry *);
+extern void hfs_dbl_drop_dentry(struct dentry *, const ino_t);
 
 /* dir_nat.c */
 extern const struct hfs_name hfs_nat_reserved1[];
 extern const struct hfs_name hfs_nat_reserved2[];
 extern struct inode_operations hfs_nat_ndir_inode_operations;
 extern struct inode_operations hfs_nat_hdir_inode_operations;
-extern void hfs_nat_drop_dentry(const ino_t, struct dentry *);
+extern void hfs_nat_drop_dentry(struct dentry *, const ino_t);
 
 /* dir_sngl.c */
 extern const struct hfs_name hfs_sngl_reserved1[];
diff -u --recursive --new-file v2.1.89/linux/include/linux/hfs_fs_i.h linux/include/linux/hfs_fs_i.h
--- v2.1.89/linux/include/linux/hfs_fs_i.h	Sun Jan  4 10:40:17 1998
+++ linux/include/linux/hfs_fs_i.h	Wed Mar 11 20:22:05 1998
@@ -34,7 +34,7 @@
 	struct hfs_hdr_layout		*layout;
 
         /* for dentry cleanup */
-        void (*d_drop_op)(const ino_t, struct dentry *);
+        void (*d_drop_op)(struct dentry *, const ino_t);
 };
 
 #endif
diff -u --recursive --new-file v2.1.89/linux/include/linux/hfs_sysdep.h linux/include/linux/hfs_sysdep.h
--- v2.1.89/linux/include/linux/hfs_sysdep.h	Fri Feb  6 15:34:49 1998
+++ linux/include/linux/hfs_sysdep.h	Tue Mar 17 21:45:50 1998
@@ -78,6 +78,10 @@
  */
 typedef struct wait_queue *hfs_wait_queue;
 
+extern inline void hfs_init_waitqueue(hfs_wait_queue *queue) {
+        init_waitqueue(queue);
+}
+
 extern inline void hfs_sleep_on(hfs_wait_queue *queue) {
 	sleep_on(queue);
 }
diff -u --recursive --new-file v2.1.89/linux/include/linux/kerneld.h linux/include/linux/kerneld.h
--- v2.1.89/linux/include/linux/kerneld.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/kerneld.h	Wed Dec 31 16:00:00 1969
@@ -1,135 +0,0 @@
-#ifndef _LINUX_KERNELD_H
-#define _LINUX_KERNELD_H
-
-#define KERNELD_SYSTEM 1
-#define KERNELD_REQUEST_MODULE 2 /* "insmod" */
-#define KERNELD_RELEASE_MODULE 3 /* "rmmod" */
-#define KERNELD_DELAYED_RELEASE_MODULE 4 /* "rmmod" */
-#define KERNELD_CANCEL_RELEASE_MODULE 5 /* "rmmod" */
-#define KERNELD_REQUEST_ROUTE 6 /* from net/ipv4/route.c */
-#define KERNELD_BLANKER 7 /* from drivers/char/console.c */
-#define KERNELD_PNP 8 /* from drivers/pnp/kerneld.c */
-#define KERNELD_ARP 256 /* from net/ipv4/arp.c */
-
-/*
- * Uncomment the following line for the new kerneld protocol
- * This includes the pid of the kernel level requester into the kerneld header
- */
-/*
-#define NEW_KERNELD_PROTOCOL
- */
-#ifdef NEW_KERNELD_PROTOCOL
-#define OLDIPC_KERNELD 00040000   /* use the kerneld message channel */
-#define IPC_KERNELD 00140000   /* use the kerneld message channel, new protocol */
-#define KDHDR (sizeof(long) + sizeof(short) + sizeof(short))
-#define NULL_KDHDR 0, 2, 0
-#else
-#define IPC_KERNELD 00040000   /* use the kerneld message channel */
-#define KDHDR (sizeof(long))
-#define NULL_KDHDR 0
-#endif
-#define KERNELD_MAXCMD 0x7ffeffff
-#define KERNELD_MINSEQ 0x7fff0000 /* "commands" legal up to 0x7ffeffff */
-#define KERNELD_WAIT 0x80000000
-#define KERNELD_NOWAIT 0
-
-struct kerneld_msg {
-	long mtype;
-	long id;
-#ifdef NEW_KERNELD_PROTOCOL
-	short version;
-	short pid;
-#endif
-#ifdef __KERNEL__
-	char *text;
-#else
-	char text[1];
-#endif /* __KERNEL__ */
-};
-
-#ifdef __KERNEL__
-#include <linux/string.h>
-
-extern int kerneld_send(int msgtype, int ret_size, int msgsz,
-			const char *text, const char *ret_val);
-
-/*
- * Request that a module should be loaded.
- * Wait for the exit status from insmod/modprobe.
- * If it fails, it fails... at least we tried...
- */
-static inline int request_module(const char *name)
-{
-	return kerneld_send(KERNELD_REQUEST_MODULE,
-			    0 | KERNELD_WAIT,
-			    strlen(name), name, NULL);
-}
-
-/*
- * Request the removal of a module, maybe don't wait for it.
- * It doesn't matter if the removal fails, now does it?
- */
-static inline int release_module(const char *name, int waitflag)
-{
-	return kerneld_send(KERNELD_RELEASE_MODULE,
-			    0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT),
-			    strlen(name), name, NULL);
-}
-
-/*
- * Request a delayed removal of a module, but don't wait for it.
- * The delay is done by kerneld (default: 60 seconds)
- */
-static inline int delayed_release_module(const char *name)
-{
-	return kerneld_send(KERNELD_DELAYED_RELEASE_MODULE,
-			    0 | KERNELD_NOWAIT,
-			    strlen(name), name, NULL);
-}
-
-/*
- * Attempt to cancel a previous request for removal of a module,
- * but don't wait for it.
- * This call can be made if the kernel wants to prevent a delayed
- * unloading of a module.
- */
-static inline int cancel_release_module(const char *name)
-{
-	return kerneld_send(KERNELD_CANCEL_RELEASE_MODULE,
-			    0 | KERNELD_NOWAIT,
-			    strlen(name), name, NULL);
-}
-
-/*
- * Perform an "inverted" system call, maybe return the exit status
- */
-static inline int ksystem(const char *cmd, int waitflag)
-{
-	return kerneld_send(KERNELD_SYSTEM,
-			    0 | (waitflag?KERNELD_WAIT:KERNELD_NOWAIT),
-			    strlen(cmd), cmd, NULL);
-}
-
-/*
- * Try to create a route, possibly by opening a ppp-connection
- */
-static inline int kerneld_route(const char *ip_route)
-{
-	return kerneld_send(KERNELD_REQUEST_ROUTE,
-			    0 | KERNELD_WAIT,
-			    strlen(ip_route), ip_route, NULL);
-}
-
-/*
- * Handle an external screen blanker
- */
-static inline int kerneld_blanker(int on_off)
-{
-	char *s = on_off ? "on" : "off";
-	return kerneld_send(KERNELD_BLANKER,
-			    0 | (on_off ? KERNELD_NOWAIT : KERNELD_WAIT),
-			    strlen(s), s, NULL);
-}
-
-#endif /* __KERNEL__ */
-#endif
diff -u --recursive --new-file v2.1.89/linux/include/linux/kmod.h linux/include/linux/kmod.h
--- v2.1.89/linux/include/linux/kmod.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/kmod.h	Tue Mar 10 14:43:13 1998
@@ -0,0 +1,4 @@
+/*
+	kmod header
+*/
+extern int request_module(const char * name);
diff -u --recursive --new-file v2.1.89/linux/include/linux/module.h linux/include/linux/module.h
--- v2.1.89/linux/include/linux/module.h	Thu Feb 12 20:56:13 1998
+++ linux/include/linux/module.h	Tue Mar 17 21:42:24 1998
@@ -148,7 +148,7 @@
 const char __module_description[] __attribute__((section(".modinfo"))) =   \
 "description=" desc
 
-/* Could potentially be used by kerneld...  */
+/* Could potentially be used by kmod...  */
 
 #define MODULE_SUPPORTED_DEVICE(dev)					   \
 const char __module_device[] __attribute__((section(".modinfo"))) = 	   \
diff -u --recursive --new-file v2.1.89/linux/include/linux/mroute.h linux/include/linux/mroute.h
--- v2.1.89/linux/include/linux/mroute.h	Fri Feb  6 15:35:08 1998
+++ linux/include/linux/mroute.h	Tue Mar 17 21:46:22 1998
@@ -217,7 +217,7 @@
 extern int pim_rcv_v1(struct sk_buff * , unsigned short len);
 
 struct rtmsg;
-extern int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm);
+extern int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait);
 #endif
 
 #endif
diff -u --recursive --new-file v2.1.89/linux/include/linux/ncp_fs.h linux/include/linux/ncp_fs.h
--- v2.1.89/linux/include/linux/ncp_fs.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/ncp_fs.h	Tue Mar 17 21:45:50 1998
@@ -83,8 +83,15 @@
 };
 
 #define	NCP_IOC_NCPREQUEST		_IOR('n', 1, struct ncp_ioctl_request)
-#define	NCP_IOC_GETMOUNTUID		_IOW('n', 2, uid_t)
-#define NCP_IOC_GETMOUNTUID_INT		_IOW('n', 2, unsigned int)
+#define	NCP_IOC_GETMOUNTUID		_IOW('n', 2, __kernel_uid_t)
+
+#if 1
+#ifdef __KERNEL__
+/* remove after ncpfs-2.0.13 gets released or at the beginning of kernel-2.1. codefreeze */
+#define	NCP_IOC_GETMOUNTUID_INT		_IOW('n', 2, unsigned int)
+#endif
+#endif
+
 #define NCP_IOC_CONN_LOGGED_IN          _IO('n', 3)
 
 #define NCP_GET_FS_INFO_VERSION (1)
diff -u --recursive --new-file v2.1.89/linux/include/linux/ncp_fs_sb.h linux/include/linux/ncp_fs_sb.h
--- v2.1.89/linux/include/linux/ncp_fs_sb.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/ncp_fs_sb.h	Tue Mar 17 21:45:50 1998
@@ -51,11 +51,9 @@
 	int ncp_reply_size;
 
 	struct ncp_inode_info root;
-#if 0
-	char root_path;		/* '\0' */
-#else
 	struct dentry* root_dentry;
-#endif
+
+	int root_setuped;
 
 /* info for packet signing */
 	int sign_wanted;        /* 1=Server needs signed packets */
diff -u --recursive --new-file v2.1.89/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v2.1.89/linux/include/linux/netdevice.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/netdevice.h	Tue Mar 17 21:43:34 1998
@@ -125,6 +125,9 @@
 	unsigned long	tx_heartbeat_errors;
 	unsigned long	tx_window_errors;
 	
+	/* for cslip etc */
+	unsigned long	rx_compressed;
+	unsigned long	tx_compressed;
 };
 
 #ifdef CONFIG_NET_FASTROUTE
@@ -352,6 +355,7 @@
 #define HAVE_NETIF_RX 1
 extern void		netif_rx(struct sk_buff *skb);
 extern void		net_bh(void);
+extern void		dev_tint(struct device *dev);
 extern int		dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
 extern int		dev_ioctl(unsigned int cmd, void *);
 extern int		dev_change_flags(struct device *, unsigned);
@@ -423,7 +427,7 @@
 extern void		dev_mc_discard(struct device *dev);
 extern void		dev_set_promiscuity(struct device *dev, int inc);
 extern void		dev_set_allmulti(struct device *dev, int inc);
-/* Load a device via the kerneld */
+/* Load a device via the kmod */
 extern void		dev_load(const char *name);
 extern void		dev_mcast_init(void);
 extern int		netdev_register_fc(struct device *dev, void (*stimul)(struct device *dev));
diff -u --recursive --new-file v2.1.89/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v2.1.89/linux/include/linux/nfs_fs.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/nfs_fs.h	Tue Mar 17 21:42:40 1998
@@ -85,6 +85,9 @@
  */
 #define NFS_RPC_SWAPFLAGS		(RPC_TASK_SWAPPER|RPC_TASK_ROOTCREDS)
 
+/* Flags in the RPC client structure */
+#define NFS_CLNTF_BUFSIZE	0x0001	/* readdir buffer in longwords */
+
 #ifdef __KERNEL__
 
 /*
diff -u --recursive --new-file v2.1.89/linux/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- v2.1.89/linux/include/linux/rtnetlink.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/rtnetlink.h	Tue Mar 10 09:47:26 1998
@@ -66,7 +66,7 @@
 
 #define RTA_ALIGNTO	4
 #define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) )
-#define RTA_OK(rta,len) ((rta)->rta_len > sizeof(struct rtattr) && \
+#define RTA_OK(rta,len) ((rta)->rta_len >= sizeof(struct rtattr) && \
 			 (rta)->rta_len <= (len))
 #define RTA_NEXT(rta,attrlen)	((attrlen) -= RTA_ALIGN((rta)->rta_len), \
 				 (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len)))
diff -u --recursive --new-file v2.1.89/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.1.89/linux/include/linux/sched.h	Thu Feb 12 20:56:13 1998
+++ linux/include/linux/sched.h	Tue Mar 17 21:42:21 1998
@@ -86,6 +86,12 @@
 #define SCHED_FIFO		1
 #define SCHED_RR		2
 
+/*
+ * This is an additional bit set when we want to
+ * yield the CPU for one re-schedule..
+ */
+#define SCHED_YIELD		0x10
+
 struct sched_param {
 	int sched_priority;
 };
@@ -113,19 +119,24 @@
 
 asmlinkage void schedule(void);
 
-/* Open file table structure */
+
+/*
+ * Open file table structure
+ */
 struct files_struct {
 	int count;
+	int max_fds;
+	struct file ** fd;	/* current fd array */
 	fd_set close_on_exec;
 	fd_set open_fds;
-	struct file * fd[NR_OPEN];
 };
 
 #define INIT_FILES { \
 	1, \
+	NR_OPEN, \
+	&init_fd_array[0], \
 	{ { 0, } }, \
-	{ { 0, } }, \
-	{ NULL, } \
+	{ { 0, } } \
 }
 
 struct fs_struct {
@@ -387,43 +398,32 @@
 /* PID hashing. */
 #define PIDHASH_SZ (NR_TASKS >> 2)
 extern struct task_struct *pidhash[PIDHASH_SZ];
-extern spinlock_t pidhash_lock;
 
 #define pid_hashfn(x)	((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
 
 extern __inline__ void hash_pid(struct task_struct *p)
 {
 	struct task_struct **htable = &pidhash[pid_hashfn(p->pid)];
-	unsigned long flags;
 
-	spin_lock_irqsave(&pidhash_lock, flags);
 	if((p->pidhash_next = *htable) != NULL)
 		(*htable)->pidhash_pprev = &p->pidhash_next;
 	*htable = p;
 	p->pidhash_pprev = htable;
-	spin_unlock_irqrestore(&pidhash_lock, flags);
 }
 
 extern __inline__ void unhash_pid(struct task_struct *p)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&pidhash_lock, flags);
 	if(p->pidhash_next)
 		p->pidhash_next->pidhash_pprev = p->pidhash_pprev;
 	*p->pidhash_pprev = p->pidhash_next;
-	spin_unlock_irqrestore(&pidhash_lock, flags);
 }
 
 extern __inline__ struct task_struct *find_task_by_pid(int pid)
 {
 	struct task_struct *p, **htable = &pidhash[pid_hashfn(pid)];
-	unsigned long flags;
 
-	spin_lock_irqsave(&pidhash_lock, flags);
 	for(p = *htable; p && p->pid != pid; p = p->pidhash_next)
 		;
-	spin_unlock_irqrestore(&pidhash_lock, flags);
 
 	return p;
 }
@@ -571,19 +571,6 @@
 extern int do_execve(char *, char **, char **, struct pt_regs *);
 extern int do_fork(unsigned long, unsigned long, struct pt_regs *);
 
-/* See if we have a valid user level fd.
- * If it makes sense, return the file structure it references.
- * Otherwise return NULL.
- */
-extern inline struct file *file_from_fd(const unsigned int fd)
-{
-
-	if (fd >= NR_OPEN)
-		return NULL;
-	/* either valid or null */
-	return current->files->fd[fd];
-}
-	
 /*
  * The wait-queues are circular lists, and you have to be *very* sure
  * to keep them correct. Use only these two functions to add/remove
@@ -627,11 +614,9 @@
 	write_unlock_irqrestore(&waitqueue_lock, flags); 
 }
 
-#define REMOVE_LINKS(p) do { unsigned long flags; \
-	write_lock_irqsave(&tasklist_lock, flags); \
+#define REMOVE_LINKS(p) do { \
 	(p)->next_task->prev_task = (p)->prev_task; \
 	(p)->prev_task->next_task = (p)->next_task; \
-	write_unlock_irqrestore(&tasklist_lock, flags); \
 	if ((p)->p_osptr) \
 		(p)->p_osptr->p_ysptr = (p)->p_ysptr; \
 	if ((p)->p_ysptr) \
@@ -640,13 +625,11 @@
 		(p)->p_pptr->p_cptr = (p)->p_osptr; \
 	} while (0)
 
-#define SET_LINKS(p) do { unsigned long flags; \
-	write_lock_irqsave(&tasklist_lock, flags); \
+#define SET_LINKS(p) do { \
 	(p)->next_task = &init_task; \
 	(p)->prev_task = init_task.prev_task; \
 	init_task.prev_task->next_task = (p); \
 	init_task.prev_task = (p); \
-	write_unlock_irqrestore(&tasklist_lock, flags); \
 	(p)->p_ysptr = NULL; \
 	if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
 		(p)->p_osptr->p_ysptr = p; \
diff -u --recursive --new-file v2.1.89/linux/include/linux/socket.h linux/include/linux/socket.h
--- v2.1.89/linux/include/linux/socket.h	Fri Feb  6 15:32:54 1998
+++ linux/include/linux/socket.h	Tue Mar 10 09:47:26 1998
@@ -149,6 +149,7 @@
 #define AF_NETLINK	16
 #define AF_ROUTE	AF_NETLINK /* Alias to emulate 4.4BSD */
 #define AF_PACKET	17	/* Packet family		*/
+#define AF_ASH		18	/* Ash				*/
 #define AF_MAX		32	/* For now.. */
 
 /* Protocol families, same as address families. */
@@ -172,6 +173,7 @@
 #define PF_NETLINK	AF_NETLINK
 #define PF_ROUTE	AF_ROUTE
 #define PF_PACKET	AF_PACKET
+#define PF_ASH		AF_ASH
 
 #define PF_MAX		AF_MAX
 
diff -u --recursive --new-file v2.1.89/linux/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h
--- v2.1.89/linux/include/linux/sunrpc/clnt.h	Fri Feb  6 15:34:19 1998
+++ linux/include/linux/sunrpc/clnt.h	Tue Mar 17 21:44:53 1998
@@ -30,6 +30,7 @@
  * The high-level client handle
  */
 struct rpc_clnt {
+	unsigned int		cl_users;	/* number of references */
 	struct rpc_xprt *	cl_xprt;	/* transport */
 	struct rpc_procinfo *	cl_procinfo;	/* procedure info */
 	u32			cl_maxproc;	/* max procedure number */
@@ -37,7 +38,6 @@
 	char *			cl_server;	/* server machine name */
 	char *			cl_protname;	/* protocol name */
 	struct rpc_auth *	cl_auth;	/* authenticator */
-	struct rpc_portmap	cl_pmap;	/* port mapping */
 	struct rpc_stat *	cl_stats;	/* statistics */
 
 	unsigned int		cl_softrtry : 1,/* soft timeouts */
@@ -47,10 +47,11 @@
 				cl_binding  : 1,/* doing a getport() */
 				cl_oneshot  : 1,/* dispose after use */
 				cl_dead     : 1;/* abandoned */
+	unsigned int		cl_flags;	/* misc client flags */
 	unsigned long		cl_hardmax;	/* max hard timeout */
 
+	struct rpc_portmap	cl_pmap;	/* port mapping */
 	struct rpc_wait_queue	cl_bindwait;	/* waiting on getport() */
-	unsigned int		cl_users;	/* number of references */
 };
 #define cl_timeout		cl_xprt->timeout
 #define cl_prog			cl_pmap.pm_prog
diff -u --recursive --new-file v2.1.89/linux/include/linux/swap.h linux/include/linux/swap.h
--- v2.1.89/linux/include/linux/swap.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/swap.h	Thu Mar 12 10:56:00 1998
@@ -36,10 +36,10 @@
 extern int nr_swap_pages;
 extern int nr_free_pages;
 extern atomic_t nr_async_pages;
-extern int min_free_pages;
-extern int free_pages_low;
-extern int free_pages_high;
 extern struct inode swapper_inode;
+extern unsigned long page_cache_size;
+extern int buffermem;
+#define BUFFER_MEM ((buffermem >> PAGE_SHIFT) + page_cache_size)
 
 /* Incomplete types for prototype declarations: */
 struct task_struct;
diff -u --recursive --new-file v2.1.89/linux/include/linux/swapctl.h linux/include/linux/swapctl.h
--- v2.1.89/linux/include/linux/swapctl.h	Fri Feb  6 15:32:55 1998
+++ linux/include/linux/swapctl.h	Tue Mar 17 21:42:40 1998
@@ -6,29 +6,18 @@
 
 /* Swap tuning control */
 
-/* First, enumerate the different reclaim policies */
-enum RCL_POLICY {RCL_ROUND_ROBIN, RCL_BUFF_FIRST, RCL_PERSIST};
-
-typedef struct swap_control_v5
+typedef struct swap_control_v6
 {
 	unsigned int	sc_max_page_age;
 	unsigned int	sc_page_advance;
 	unsigned int	sc_page_decline;
 	unsigned int	sc_page_initial_age;
-	unsigned int	sc_max_buff_age;
-	unsigned int	sc_buff_advance;
-	unsigned int	sc_buff_decline;
-	unsigned int	sc_buff_initial_age;
 	unsigned int	sc_age_cluster_fract;
 	unsigned int	sc_age_cluster_min;
 	unsigned int	sc_pageout_weight;
 	unsigned int	sc_bufferout_weight;
-	unsigned int 	sc_buffer_grace;
-	unsigned int 	sc_nr_buffs_to_free;
-	unsigned int 	sc_nr_pages_to_free;
-	enum RCL_POLICY	sc_policy;
-} swap_control_v5;
-typedef struct swap_control_v5 swap_control_t;
+} swap_control_v6;
+typedef struct swap_control_v6 swap_control_t;
 extern swap_control_t swap_control;
 
 typedef struct swapstat_v1
@@ -42,7 +31,23 @@
 typedef swapstat_v1 swapstat_t;
 extern swapstat_t swapstats;
 
-extern int min_free_pages, free_pages_low, free_pages_high;
+typedef struct buffer_mem_v1
+{
+	unsigned int	min_percent;
+	unsigned int	borrow_percent;
+	unsigned int	max_percent;
+} buffer_mem_v1;
+typedef buffer_mem_v1 buffer_mem_t;
+extern buffer_mem_t buffer_mem;
+
+typedef struct freepages_v1
+{
+	unsigned int	min;
+	unsigned int	low;
+	unsigned int	high;
+} freepages_v1;
+typedef freepages_v1 freepages_t;
+extern freepages_t freepages;
 
 #define SC_VERSION	1
 #define SC_MAX_VERSION	1
@@ -55,28 +60,17 @@
    failure to free a resource at any priority */
 #define RCL_FAILURE (RCL_MAXPRI + 1)
 
-#define RCL_POLICY		(swap_control.sc_policy)
 #define AGE_CLUSTER_FRACT	(swap_control.sc_age_cluster_fract)
 #define AGE_CLUSTER_MIN		(swap_control.sc_age_cluster_min)
 #define PAGEOUT_WEIGHT		(swap_control.sc_pageout_weight)
 #define BUFFEROUT_WEIGHT	(swap_control.sc_bufferout_weight)
 
-#define NR_BUFFS_TO_FREE	(swap_control.sc_nr_buffs_to_free)
-#define NR_PAGES_TO_FREE	(swap_control.sc_nr_pages_to_free)
-
-#define BUFFERMEM_GRACE		(swap_control.sc_buffer_grace)
-
 /* Page aging (see mm/swap.c) */
 
 #define MAX_PAGE_AGE		(swap_control.sc_max_page_age)
 #define PAGE_ADVANCE		(swap_control.sc_page_advance)
 #define PAGE_DECLINE		(swap_control.sc_page_decline)
 #define PAGE_INITIAL_AGE	(swap_control.sc_page_initial_age)
-
-#define MAX_BUFF_AGE		(swap_control.sc_max_buff_age)
-#define BUFF_ADVANCE		(swap_control.sc_buff_advance)
-#define BUFF_DECLINE		(swap_control.sc_buff_decline)
-#define BUFF_INITIAL_AGE	(swap_control.sc_buff_initial_age)
 
 /* Given a resource of N units (pages or buffers etc), we only try to
  * age and reclaim AGE_CLUSTER_FRACT per 1024 resources each time we
diff -u --recursive --new-file v2.1.89/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v2.1.89/linux/include/linux/sysctl.h	Tue Mar 10 10:03:35 1998
+++ linux/include/linux/sysctl.h	Tue Mar 17 21:06:51 1998
@@ -70,7 +70,9 @@
 	KERN_PRINTK,            /* sturct: control printk logging parameters */
 	KERN_NAMETRANS,		/* Name translation */
 	KERN_STATINODE,
-	KERN_DENTRY		/* dentry statistics */
+	KERN_DENTRY,		/* dentry statistics */
+	KERN_MODPROBE,
+	KERN_KMOD_UNLOAD_DELAY
 };
 
 
@@ -82,6 +84,7 @@
 	VM_FREEPG,		/* struct: Set free page thresholds */
 	VM_BDFLUSH,		/* struct: Control buffer cache flushing */
 	VM_OVERCOMMIT_MEMORY,	/* Turn off the virtual memory safety limit */
+	VM_BUFFERMEM		/* struct: Set cache memory thresholds */
 };
 
 
@@ -118,6 +121,7 @@
 	NET_CORE_FASTROUTE,
 	NET_CORE_MSG_COST,
 	NET_CORE_MSG_BURST,
+	NET_CORE_OPTMEM_MAX,
 };
 
 /* /proc/sys/net/ethernet */
@@ -145,8 +149,6 @@
 	NET_IPV4_FIB_HASH = 19,
 
 	NET_IPV4_TCP_HOE_RETRANSMITS=32,
-	NET_IPV4_TCP_SACK,
-	NET_IPV4_TCP_TSACK,
 	NET_IPV4_TCP_TIMESTAMPS,
 	NET_IPV4_TCP_WINDOW_SCALING,
 	NET_IPV4_TCP_VEGAS_CONG_AVOID,
@@ -167,6 +169,7 @@
 	NET_IPV4_IP_MASQ_DEBUG,
 	NET_TCP_SYNCOOKIES,
 	NET_TCP_STDURG,
+	NET_TCP_RFC1337,
 	NET_TCP_SYN_TAILDROP,
 	NET_TCP_MAX_SYN_BACKLOG,
 	NET_IPV4_LOCAL_PORT_RANGE,
diff -u --recursive --new-file v2.1.89/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.1.89/linux/include/linux/tty.h	Tue Feb 17 13:12:49 1998
+++ linux/include/linux/tty.h	Tue Mar 17 21:42:20 1998
@@ -314,8 +314,7 @@
 
 extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
 			      const char *routine);
-extern char *_tty_name(struct tty_struct *tty, char *buf);
-extern char *tty_name(struct tty_struct *tty);
+extern char *tty_name(struct tty_struct *tty, char *buf);
 extern void tty_wait_until_sent(struct tty_struct * tty, int timeout);
 extern int tty_check_change(struct tty_struct * tty);
 extern void stop_tty(struct tty_struct * tty);
diff -u --recursive --new-file v2.1.89/linux/include/linux/umsdos_fs.p linux/include/linux/umsdos_fs.p
--- v2.1.89/linux/include/linux/umsdos_fs.p	Mon Feb 23 18:12:12 1998
+++ linux/include/linux/umsdos_fs.p	Tue Mar 17 21:32:17 1998
@@ -1,9 +1,6 @@
 /* check.c 23/01/95 03.38.30 */
 void check_page_tables (void);
 /* dir.c 22/06/95 00.22.12 */
-struct dentry *creat_dentry (const char *name,
-			     const int len,
-			     struct inode *inode);
 int compat_msdos_create(struct inode *dir,
 			const char *name,
 			int len,
@@ -30,6 +27,16 @@
 	 
 int umsdos_hlink2inode (struct inode *hlink, struct inode **result);
 /* emd.c 22/06/95 00.22.04 */
+void fill_new_filp (struct file *filp, struct dentry *dentry);
+void kill_dentry (struct dentry *dentry);
+struct dentry *creat_dentry (const char *name,
+			     const int len,
+			     struct inode *inode);
+ssize_t umsdos_file_write_kmem_real (struct file *filp,
+				const char *buf,
+				size_t  count,
+				loff_t *offs);
+
 ssize_t umsdos_file_read_kmem (struct inode *emd_dir,
 	 struct file *filp,
 	 char *buf,
diff -u --recursive --new-file v2.1.89/linux/include/net/dst.h linux/include/net/dst.h
--- v2.1.89/linux/include/net/dst.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/dst.h	Tue Mar 17 21:46:01 1998
@@ -120,6 +120,8 @@
 extern __inline__
 void dst_free(struct dst_entry * dst)
 {
+	if (dst->obsolete > 1)
+		return;
 	if (!atomic_read(&dst->use)) {
 		dst_destroy(dst);
 		return;
diff -u --recursive --new-file v2.1.89/linux/include/net/ip6_route.h linux/include/net/ip6_route.h
--- v2.1.89/linux/include/net/ip6_route.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/ip6_route.h	Tue Mar 10 09:47:26 1998
@@ -114,7 +114,7 @@
 	struct rt6_info *rt;
 		
 	np = &sk->net_pinfo.af_inet6;
-	sk->dst_cache = dst;
+	dst_release(xchg(&sk->dst_cache,dst));
 	
 	rt = (struct rt6_info *) dst;
 	
diff -u --recursive --new-file v2.1.89/linux/include/net/ipv6.h linux/include/net/ipv6.h
--- v2.1.89/linux/include/net/ipv6.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/ipv6.h	Tue Mar 17 21:46:15 1998
@@ -4,7 +4,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: ipv6.h,v 1.8 1997/12/29 19:52:09 kuznet Exp $
+ *	$Id: ipv6.h,v 1.9 1998/03/08 05:55:20 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.89/linux/include/net/route.h linux/include/net/route.h
--- v2.1.89/linux/include/net/route.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/route.h	Tue Mar 17 21:46:01 1998
@@ -102,6 +102,7 @@
 extern void		ip_rt_multicast_event(struct in_device *);
 extern int		ip_rt_ioctl(unsigned int cmd, void *arg);
 extern void		ip_rt_get_source(u8 *src, struct rtable *rt);
+extern int		ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb);
 
 
 extern __inline__ void ip_rt_put(struct rtable * rt)
diff -u --recursive --new-file v2.1.89/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.89/linux/include/net/sock.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/sock.h	Tue Mar 17 21:43:36 1998
@@ -191,42 +191,75 @@
 
 struct tcp_opt
 {
+	int	tcp_header_len;	/* Bytes of tcp header to send		*/
+
+/*
+ *	Header prediction flags
+ *	0x5?10 << 16 + snd_wnd in net byte order
+ */
+	__u32	pred_flags;
+
 /*
  *	RFC793 variables by their proper names. This means you can
  *	read the code and the spec side by side (and laugh ...)
  *	See RFC793 and RFC1122. The RFC writes these in capitals.
  */
  	__u32	rcv_nxt;	/* What we want to receive next 	*/
- 	__u32	rcv_up;		/* The urgent point (may not be valid) 	*/
- 	__u32	rcv_wnd;	/* Current receiver window		*/
  	__u32	snd_nxt;	/* Next sequence we send		*/
+
  	__u32	snd_una;	/* First byte we want an ack for	*/
-	__u32	snd_up;		/* Outgoing urgent pointer		*/
-	__u32	snd_wl1;	/* Sequence for window update		*/
-	__u32	snd_wl2;	/* Ack sequence for update		*/
+	__u32	rcv_tstamp;	/* timestamp of last received packet	*/
+	__u32	lrcvtime;	/* timestamp of last received data packet*/
+	__u32	srtt;		/* smothed round trip time << 3		*/
 
-	__u32	rcv_wup;	/* rcv_nxt on last window update sent	*/
+	__u32	ato;		/* delayed ack timeout */
+	__u32	snd_wl1;	/* Sequence for window update		*/
 
-	__u32	fin_seq;	/* XXX This one should go, we don't need it. -DaveM */
+	__u32	snd_wl2;	/* Ack sequence for update		*/
+	__u32	snd_wnd;	/* The window we expect to receive	*/
+	__u32	max_window;
+	__u8	pending;	/* pending events			*/
+	__u8	retransmits;
+	__u32	last_ack_sent;	/* last ack we sent			*/
 
-	__u32	srtt;		/* smothed round trip time << 3		*/
+	__u32	backoff;	/* backoff				*/
 	__u32	mdev;		/* medium deviation			*/
+ 	__u32	snd_cwnd;	/* Sending congestion window		*/
 	__u32	rto;		/* retransmit timeout			*/
-	__u32	backoff;	/* backoff				*/
+
+	__u32	packets_out;	/* Packets which are "in flight" */
+	__u32	high_seq;	/* highest sequence number sent by onset of congestion */
 /*
  *	Slow start and congestion control (see also Nagle, and Karn & Partridge)
  */
- 	__u32	snd_cwnd;	/* Sending congestion window		*/
  	__u32	snd_ssthresh;	/* Slow start size threshold		*/
 	__u16	snd_cwnd_cnt;
-	__u16	max_window;
+	__u8	dup_acks;	/* Consequetive duplicate acks seen from other end */
+	__u8	delayed_acks;
+
+	/* Two commonly used timers in both sender and receiver paths. */
+ 	struct timer_list	retransmit_timer;	/* Resend (no ack)	*/
+ 	struct timer_list	delack_timer;		/* Ack delay 		*/
+
+	struct sk_buff_head	out_of_order_queue; /* Out of order segments go here */
+
+	struct tcp_func		*af_specific;	/* Operations which are AF_INET{4,6} specific	*/
+	struct sk_buff		*send_head;	/* Front of stuff to transmit			*/
+	struct sk_buff		*retrans_head;	/* retrans head can be 
+						 * different to the head of
+						 * write queue if we are doing
+						 * fast retransmit
+						 */
 
+ 	__u32	rcv_wnd;	/* Current receiver window		*/
+	__u32	rcv_wup;	/* rcv_nxt on last window update sent	*/
+	__u32	write_seq;
+	__u32	copied_seq;
 /*
  *      Options received (usually on last packet, some only on SYN packets).
  */
 	char	tstamp_ok,	/* TIMESTAMP seen on SYN packet		*/
-		wscale_ok,	/* Wscale seen on SYN packet		*/
-		sack_ok;	/* SACK_PERM seen on SYN packet		*/
+		wscale_ok;	/* Wscale seen on SYN packet		*/
 	char	saw_tstamp;	/* Saw TIMESTAMP on last packet		*/
         __u16	in_mss;		/* MSS option received from sender	*/
         __u8	snd_wscale;	/* Window scaling received from sender	*/
@@ -235,60 +268,20 @@
         __u32	rcv_tsecr;	/* Time stamp echo reply        	*/
         __u32	ts_recent;	/* Time stamp to echo next		*/
         __u32	ts_recent_stamp;/* Time we stored ts_recent (for aging) */
-	__u32	last_ack_sent;	/* last ack we sent			*/
-        int	sacks;		/* Number of SACK blocks if any		*/
-        __u32	left_sack[4];	/* Left edges of blocks         	*/
-        __u32	right_sack[4];	/* Right edges of blocks        	*/
-	int	tcp_header_len;      /* Bytes of tcp header to send 	*/
 
-/*
- *	Timers used by the TCP protocol layer
- */
- 	struct timer_list	delack_timer;		/* Ack delay 	*/
- 	struct timer_list	idle_timer;		/* Idle watch 	*/
- 	struct timer_list	completion_timer;	/* Up/Down timer */
  	struct timer_list	probe_timer;		/* Probes	*/
- 	struct timer_list	retransmit_timer;	/* Resend (no ack) */
-
-	__u32	basertt;	/* Vegas baseRTT */
-	__u32	packets_out;	/* Packets which are "in flight" */
-	__u32	window_clamp;	/* XXX Document this... -DaveM */
-
-	__u8	pending;	/* pending events */
-	__u8	delayed_acks;
-	__u8	dup_acks;	/* Consequetive duplicate acks seen from other end */
-	__u8	retransmits;
-
-	__u32	lrcvtime;	/* timestamp of last received data packet  */
-	__u32	rcv_tstamp;	/* timestamp of last received packet  */
-	__u32	iat_mdev;	/* interarrival time medium deviation */
-	__u32	iat;		/* interarrival time */
-	__u32	ato;		/* delayed ack timeout */
-	__u32	high_seq;	/* highest sequence number sent by onset of congestion */
-
-/*
- *	new send pointers
- */
-	struct sk_buff *	send_head;
-	struct sk_buff *	retrans_head;	/* retrans head can be 
-						 * different to the head of
-						 * write queue if we are doing
-						 * fast retransmit
-						 */
-/*
- *	Header prediction flags
- *	0x5?10 << 16 + snd_wnd in net byte order
- */
-	__u32	pred_flags;
-	__u32	snd_wnd;		/* The window we expect to receive */
-
-	__u32	probes_out;		/* unanswered 0 window probes	   */
+	__u32	basertt;	/* Vegas baseRTT			*/
+	__u32	window_clamp;	/* XXX Document this... -DaveM		*/
+	__u32	probes_out;	/* unanswered 0 window probes		*/
+	__u32	syn_seq;
+	__u32	fin_seq;
+	__u32	urg_seq;
+	__u32	urg_data;
 
 	struct open_request	*syn_wait_queue;
 	struct open_request	**syn_wait_last;
 	int syn_backlog;
 
-	struct tcp_func		*af_specific;
 };
 
  	
@@ -347,73 +340,73 @@
 	struct sock		*sklist_next;
 	struct sock		*sklist_prev;
 
-	atomic_t		wmem_alloc;
-	atomic_t		rmem_alloc;
-	unsigned long		allocation;		/* Allocation mode */
-
-	/* The following stuff should probably move to the tcp private area */
-	__u32			write_seq;
-	__u32			copied_seq;
-	__u32			syn_seq;
-	__u32			urg_seq;
-	__u32			urg_data;
-	unsigned char		delayed_acks;
-	/* End of block to move */
+	/* Local port binding hash linkage. */
+	struct sock		*bind_next;
+	struct sock		**bind_pprev;
 
-	int			sock_readers;		/* user count */
+	/* Main hash linkage for various protocol lookup tables. */
+	struct sock		*next;
+	struct sock		**pprev;
+
+	/* Socket demultiplex comparisons on incoming packets. */
+	__u32			daddr;		/* Foreign IPv4 addr			*/
+	__u32			rcv_saddr;	/* Bound local IPv4 addr		*/
+	int			bound_dev_if;	/* Bound device index if != 0		*/
+	unsigned short		num;		/* Local port				*/
+	volatile unsigned char	state,		/* Connection state			*/
+				zapped;		/* In ax25 & ipx means not linked	*/
+	struct tcphdr		dummy_th;	/* TCP header template			*/
+
+	int			sock_readers;	/* user count				*/
+	int			rcvbuf;
+
+	struct wait_queue	**sleep;
+	struct dst_entry	*dst_cache;	/* Destination cache			*/
+	atomic_t		rmem_alloc;	/* Receive queue bytes committed	*/
+	struct sk_buff_head	receive_queue;	/* Incoming packets			*/
+	atomic_t		wmem_alloc;	/* Transmit queue bytes committed	*/
+	struct sk_buff_head	write_queue;	/* Packet sending queue			*/
+	atomic_t		omem_alloc;	/* "o" is "option" or "other" */
+	__u32			saddr;		/* Sending source			*/
+	unsigned int		allocation;	/* Allocation mode			*/
+	int			sndbuf;
+	struct sock		*prev;
 
   /*
    *	Not all are volatile, but some are, so we
    * 	might as well say they all are.
    */
 	volatile char		dead,
-				urginline,
 				done,
+				urginline,
 				reuse,
 				keepopen,
 				linger,
 				destroy,
 				no_check,
-				zapped,	/* In ax25 & ipx means not linked */
 				broadcast,
 				nonagle,
 				bsdism;
-	int			bound_dev_if;
-	unsigned long	        lingertime;
+	unsigned char		debug;
 	int			proc;
+	unsigned long	        lingertime;
 
-	struct sock		*next;
-	struct sock		**pprev;
-	struct sock		*bind_next;
-	struct sock		**bind_pprev;
-	struct sock		*prev;
 	int			hashent;
 	struct sock		*pair;
 
-	struct sk_buff_head	back_log;
-
-	struct sk_buff_head	write_queue,
-				receive_queue,
-				out_of_order_queue,
+	/* Error and backlog packet queues, rarely used. */
+	struct sk_buff_head	back_log,
 	                        error_queue;
 
 	unsigned short		family;
 	struct proto		*prot;
-	struct wait_queue	**sleep;
-
-	__u32			daddr;
-	__u32			saddr;		/* Sending source */
-	__u32			rcv_saddr;	/* Bound address */
 
-	struct dst_entry	*dst_cache;
 /*
  *	mss is min(mtu, max_window) 
  */
 	unsigned short		mtu;       /* mss negotiated in the syn's */
 	unsigned short		mss;       /* current eff. mss - can change */
 	unsigned short		user_mss;  /* mss requested by user in ioctl */
-	unsigned short		num;
-
 	unsigned short		shutdown;
 
 #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
@@ -436,16 +429,12 @@
 						   cause failure but are the cause
 						   of a persistent failure not just
 						   'timed out' */
-	unsigned char		protocol;
-	volatile unsigned char	state;
 	unsigned short		ack_backlog;
 	unsigned short		max_ack_backlog;
-	unsigned char		debug;
 	__u32			priority;
-	int			rcvbuf;
-	int			sndbuf;
 	unsigned short		type;
 	unsigned char		localroute;	/* Route locally only */
+	unsigned char		protocol;
 	struct ucred		peercred;
 
 #ifdef CONFIG_FILTER
@@ -472,11 +461,6 @@
 #if defined (CONFIG_PACKET) || defined(CONFIG_PACKET_MODULE)
 		struct packet_opt	*af_packet;
 #endif
-#ifdef CONFIG_INET
-#ifdef CONFIG_NUTCP		
-		struct tcp_opt		af_tcp;
-#endif		
-#endif
 #if defined(CONFIG_X25) || defined(CONFIG_X25_MODULE)
 		x25_cb			*x25;
 #endif
@@ -503,7 +487,6 @@
 	int			ip_ttl;			/* TTL setting */
 	int			ip_tos;			/* TOS */
 	unsigned	   	ip_cmsg_flags;
-	struct tcphdr		dummy_th;
 	struct ip_options	*opt;
 	unsigned char		ip_hdrincl;		/* Include headers ? */
 	__u8			ip_mc_ttl;		/* Multicasting TTL */
@@ -731,7 +714,7 @@
 }
 
 /*
- *	This might not be the most apropriate place for this two	 
+ *	This might not be the most appropriate place for this two	 
  *	but since they are used by a lot of the net related code
  *	at least they get declared on a include that is common to all
  */
@@ -750,7 +733,7 @@
 	return a;
 }
 
-extern struct sock *		sk_alloc(int family, int priority);
+extern struct sock *		sk_alloc(int family, int priority, int zero_it);
 extern void			sk_free(struct sock *sk);
 extern void			destroy_sock(struct sock *sk);
 
@@ -884,7 +867,6 @@
 	 */
 	if (atomic_read(&sk->rmem_alloc) + skb->truesize >= (unsigned)sk->rcvbuf)
                 return -ENOMEM;
-        skb_set_owner_r(skb, sk);
 
 #ifdef CONFIG_FILTER
 	if (sk->filter)
@@ -894,7 +876,8 @@
 	}
 #endif /* CONFIG_FILTER */
 
-	skb_queue_tail(&sk->receive_queue,skb);
+	skb_set_owner_r(skb, sk);
+	skb_queue_tail(&sk->receive_queue, skb);
 	if (!sk->dead)
 		sk->data_ready(sk,skb->len);
 	return 0;
diff -u --recursive --new-file v2.1.89/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.89/linux/include/net/tcp.h	Tue Mar 10 10:03:35 1998
+++ linux/include/net/tcp.h	Tue Mar 17 21:46:01 1998
@@ -27,13 +27,13 @@
  * New scheme, half the table is for TIME_WAIT, the other half is
  * for the rest.  I'll experiment with dynamic table growth later.
  */
-#define TCP_HTABLE_SIZE		1024
+#define TCP_HTABLE_SIZE		512
 
 /* This is for listening sockets, thus all sockets which possess wildcards. */
 #define TCP_LHTABLE_SIZE	32	/* Yes, really, this is all you need. */
 
 /* This is for all sockets, to keep track of the local port allocations. */
-#define TCP_BHTABLE_SIZE	64
+#define TCP_BHTABLE_SIZE	512
 
 /* tcp_ipv4.c: These need to be shared by v4 and v6 because the lookup
  *             and hashing code needs to work with different AF's yet
@@ -41,46 +41,152 @@
  */
 extern struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
 extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
-extern struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE];
 
-/* tcp_ipv4.c: These sysctl variables need to be shared between v4 and v6
- * because the v6 tcp code to intialize a connection needs to interoperate
- * with the v4 code using the same variables.
- * FIXME: It would be better to rewrite the connection code to be
- * address family independent and just leave one copy in the ipv4 section.
- * This would also clean up some code duplication. -- erics
- */
-extern int sysctl_tcp_sack;
-extern int sysctl_tcp_timestamps;
-extern int sysctl_tcp_window_scaling;
+/* There are a few simple rules, which allow for local port reuse by
+ * an application.  In essence:
+ *
+ *	1) Sockets bound to different interfaces may share a local port.
+ *	   Failing that, goto test 2.
+ *	2) If all sockets have sk->reuse set, and none of them are in
+ *	   TCP_LISTEN state, the port may be shared.
+ *	   Failing that, goto test 3.
+ *	3) If all sockets are bound to a specific sk->rcv_saddr local
+ *	   address, and none of them are the same, the port may be
+ *	   shared.
+ *	   Failing this, the port cannot be shared.
+ *
+ * The interesting point, is test #2.  This is what an FTP server does
+ * all day.  To optimize this case we use a specific flag bit defined
+ * below.  As we add sockets to a bind bucket list, we perform a
+ * check of: (newsk->reuse && (newsk->state != TCP_LISTEN))
+ * As long as all sockets added to a bind bucket pass this test,
+ * the flag bit will be set.
+ * The resulting situation is that tcp_v[46]_verify_bind() can just check
+ * for this flag bit, if it is set and the socket trying to bind has
+ * sk->reuse set, we don't even have to walk the owners list at all,
+ * we return that it is ok to bind this socket to the requested local port.
+ *
+ * Sounds like a lot of work, but it is worth it.  In a more naive
+ * implementation (ie. current FreeBSD etc.) the entire list of ports
+ * must be walked for each data port opened by an ftp server.  Needless
+ * to say, this does not scale at all.  With a couple thousand FTP
+ * users logged onto your box, isn't it nice to know that new data
+ * ports are created in O(1) time?  I thought so. ;-)	-DaveM
+ */
+struct tcp_bind_bucket {
+	unsigned short		port;
+	unsigned short		flags;
+#define TCPB_FLAG_LOCKED	0x0001
+#define TCPB_FLAG_FASTREUSE	0x0002
+
+	struct tcp_bind_bucket	*next;
+	struct sock		*owners;
+	struct tcp_bind_bucket	**pprev;
+};
+
+extern struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE];
+extern kmem_cache_t *tcp_bucket_cachep;
+extern struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum);
+extern void tcp_bucket_unlock(struct sock *sk);
+extern int tcp_port_rover;
+
+/* Level-1 socket-demux cache. */
+#define TCP_NUM_REGS		32
+extern struct sock *tcp_regs[TCP_NUM_REGS];
+
+#define TCP_RHASH_FN(__fport) \
+	((((__fport) >> 7) ^ (__fport)) & (TCP_NUM_REGS - 1))
+#define TCP_RHASH(__fport)	tcp_regs[TCP_RHASH_FN((__fport))]
+#define TCP_SK_RHASH_FN(__sock)	TCP_RHASH_FN((__sock)->dummy_th.dest)
+#define TCP_SK_RHASH(__sock)	tcp_regs[TCP_SK_RHASH_FN((__sock))]
+
+static __inline__ void tcp_reg_zap(struct sock *sk)
+{
+	struct sock **rpp;
+
+	rpp = &(TCP_SK_RHASH(sk));
+	if(*rpp == sk)
+		*rpp = NULL;
+}
 
 /* These are AF independent. */
 static __inline__ int tcp_bhashfn(__u16 lport)
 {
-	return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1);
+	return (lport & (TCP_BHTABLE_SIZE - 1));
 }
 
-/* Find the next port that hashes h that is larger than lport.
- * If you change the hash, change this function to match, or you will
- * break TCP port selection. This function must also NOT wrap around
- * when the next number exceeds the largest possible port (2^16-1).
- */
-static __inline__ int tcp_bhashnext(__u16 lport, __u16 h)
+static __inline__ void tcp_sk_bindify(struct sock *sk)
 {
-        __u32 s;	/* don't change this to a smaller type! */
+	struct tcp_bind_bucket *tb;
+	unsigned short snum = sk->num;
 
-        s = (lport ^ (h ^ tcp_bhashfn(lport)));
-        if (s > lport)
-                return s;
-        s = lport + TCP_BHTABLE_SIZE;
-        return (s ^ (h ^ tcp_bhashfn(s)));
+	for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb->port != snum; tb = tb->next)
+		;
+	/* Update bucket flags. */
+	if(tb->owners == NULL) {
+		/* We're the first. */
+		if(sk->reuse && sk->state != TCP_LISTEN)
+			tb->flags = TCPB_FLAG_FASTREUSE;
+		else
+			tb->flags = 0;
+	} else {
+		if((tb->flags & TCPB_FLAG_FASTREUSE) &&
+		   ((sk->reuse == 0) || (sk->state == TCP_LISTEN)))
+			tb->flags &= ~TCPB_FLAG_FASTREUSE;
+	}
+	if((sk->bind_next = tb->owners) != NULL)
+		tb->owners->bind_pprev = &sk->bind_next;
+	tb->owners = sk;
+	sk->bind_pprev = &tb->owners;
+	sk->prev = (struct sock *) tb;
 }
 
-static __inline__ int tcp_sk_bhashfn(struct sock *sk)
-{
-	__u16 lport = sk->num;
-	return tcp_bhashfn(lport);
-}
+/* This is a TIME_WAIT bucket.  It works around the memory consumption
+ * problems of sockets in such a state on heavily loaded servers, but
+ * without violating the protocol specification.
+ */
+struct tcp_tw_bucket {
+	/* These _must_ match the beginning of struct sock precisely.
+	 * XXX Yes I know this is gross, but I'd have to edit every single
+	 * XXX networking file if I created a "struct sock_header". -DaveM
+	 */
+	struct sock		*sklist_next;
+	struct sock		*sklist_prev;
+	struct sock		*bind_next;
+	struct sock		**bind_pprev;
+	struct sock		*next;
+	struct sock		**pprev;
+	__u32			daddr;
+	__u32			rcv_saddr;
+	int			bound_dev_if;
+	unsigned short		num;
+	unsigned char		state,
+				family;		/* sk->zapped */
+	__u16			source;		/* sk->dummy_th.source */
+	__u16			dest;		/* sk->dummy_th.dest */
+
+	/* And these are ours. */
+	__u32			rcv_nxt;
+	struct tcp_func		*af_specific;
+	struct tcp_bind_bucket	*tb;
+	struct timer_list	timer;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	struct in6_addr		v6_daddr;
+	struct in6_addr		v6_rcv_saddr;
+#endif
+};
+
+extern kmem_cache_t *tcp_timewait_cachep;
+
+/* tcp_ipv4.c: These sysctl variables need to be shared between v4 and v6
+ * because the v6 tcp code to intialize a connection needs to interoperate
+ * with the v4 code using the same variables.
+ * FIXME: It would be better to rewrite the connection code to be
+ * address family independent and just leave one copy in the ipv4 section.
+ * This would also clean up some code duplication. -- erics
+ */
+extern int sysctl_tcp_timestamps;
+extern int sysctl_tcp_window_scaling;
 
 /* These can have wildcards, don't try too hard. */
 static __inline__ int tcp_lhashfn(unsigned short num)
@@ -93,28 +199,6 @@
 	return tcp_lhashfn(sk->num);
 }
 
-/* Only those holding the sockhash lock call these two things here.
- * Note the slightly gross overloading of sk->prev, AF_UNIX is the
- * only other main benefactor of that member of SK, so who cares.
- */
-static __inline__ void tcp_sk_bindify(struct sock *sk)
-{
-	int hashent = tcp_sk_bhashfn(sk);
-	struct sock **htable = &tcp_bound_hash[hashent];
-
-	if((sk->bind_next = *htable) != NULL)
-		(*htable)->bind_pprev = &sk->bind_next;
-	*htable = sk;
-	sk->bind_pprev = htable;
-}
-
-static __inline__ void tcp_sk_unbindify(struct sock *sk)
-{
-	if(sk->bind_next)
-		sk->bind_next->bind_pprev = sk->bind_pprev;
-	*(sk->bind_pprev) = sk->bind_next;
-}
-
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #define NETHDR_SIZE	sizeof(struct ipv6hdr)
 #else
@@ -186,6 +270,8 @@
 				    * we tell the LL layer that it is something
 				    * wrong (e.g. that it can expire redirects) */
 
+#define TCP_BUCKETGC_PERIOD	(HZ)
+
 /*
  *	TCP option
  */
@@ -193,9 +279,6 @@
 #define TCPOPT_NOP		1	/* Padding */
 #define TCPOPT_EOL		0	/* End of options */
 #define TCPOPT_MSS		2	/* Segment size negotiating */
-/*
- *	We don't use these yet, but they are for PAWS and big windows
- */
 #define TCPOPT_WINDOW		3	/* Window scaling */
 #define TCPOPT_SACK_PERM        4       /* SACK Permitted */
 #define TCPOPT_SACK             5       /* SACK Block */
@@ -210,6 +293,10 @@
 #define TCPOLEN_SACK_PERM      2
 #define TCPOLEN_TIMESTAMP      10
 
+/* But this is what stacks really send out. */
+#define TCPOLEN_TSTAMP_ALIGNED	12
+#define TCPOLEN_WSCALE_ALIGNED	4
+
 /*
  *      TCP option flags for parsed options.
  */
@@ -259,7 +346,6 @@
 	__u8			__pad;
 	unsigned snd_wscale : 4, 
 		rcv_wscale : 4, 
-		sack_ok : 1,
 		tstamp_ok : 1,
 		wscale_ok : 1;
 	/* The following two fields can be easily recomputed I think -AK */
@@ -355,7 +441,7 @@
 /* is s2<=s1<=s3 ? */
 extern __inline int between(__u32 seq1, __u32 seq2, __u32 seq3)
 {
-	return (after(seq1+1, seq2) && before(seq1, seq3+1));
+	return seq3 - seq2 >= seq1 - seq2;
 }
 
 
@@ -390,6 +476,11 @@
 						    struct tcphdr *th, 
 						    __u16 len);
 
+extern int			tcp_timewait_state_process(struct tcp_tw_bucket *tw,
+							   struct sk_buff *skb,
+							   struct tcphdr *th,
+							   void *opt, __u16 len);
+
 extern void			tcp_close(struct sock *sk, 
 					  unsigned long timeout);
 extern struct sock *		tcp_accept(struct sock *sk, int flags);
@@ -427,6 +518,10 @@
 						    struct sk_buff *skb,
 						    void *ptr, __u32 isn);
 
+extern struct sock *		tcp_create_openreq_child(struct sock *sk,
+							 struct open_request *req,
+							 struct sk_buff *skb);
+
 extern struct sock *		tcp_v4_syn_recv_sock(struct sock *sk,
 						     struct sk_buff *skb,
 						     struct open_request *req,
@@ -457,10 +552,11 @@
 extern void tcp_send_partial(struct sock *);
 extern void tcp_write_wakeup(struct sock *);
 extern void tcp_send_fin(struct sock *sk);
+extern void tcp_send_active_reset(struct sock *sk);
 extern int  tcp_send_synack(struct sock *);
-extern void tcp_send_skb(struct sock *, struct sk_buff *);
+extern void tcp_send_skb(struct sock *, struct sk_buff *, int force_queue);
 extern void tcp_send_ack(struct sock *sk);
-extern void tcp_send_delayed_ack(struct sock *sk, int max_timeout);
+extern void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout);
 
 /* CONFIG_IP_TRANSPARENT_PROXY */
 extern int tcp_chkaddr(struct sk_buff *);
@@ -492,40 +588,94 @@
 
 #define TCP_SLT_SYNACK		0
 #define TCP_SLT_KEEPALIVE	1
-#define TCP_SLT_MAX		2
+#define TCP_SLT_BUCKETGC	2
+#define TCP_SLT_MAX		3
 
 extern struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX];
  
-/*
- * FIXME: this method of choosing when to send a window update
- * does not seem correct to me. -- erics
+/* Compute the actual receive window we are currently advertising. */
+static __inline__ u32 tcp_receive_window(struct tcp_opt *tp)
+{
+	return tp->rcv_wup - (tp->rcv_nxt - tp->rcv_wnd);
+}
+
+/* Choose a new window, without checks for shrinking, and without
+ * scaling applied to the result.  The caller does these things
+ * if necessary.  This is a "raw" window selection.
+ */
+extern u32	__tcp_select_window(struct sock *sk);
+
+/* Chose a new window to advertise, update state in tcp_opt for the
+ * socket, and return result with RFC1323 scaling applied.  The return
+ * value can be stuffed directly into th->window for an outgoing
+ * frame.
  */
-static __inline__ unsigned short tcp_raise_window(struct sock *sk)
+extern __inline__ u16 tcp_select_window(struct sock *sk)
 {
-	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	long cur_win;
-	int res = 0;
-	
-	/* 
-         * compute the actual window i.e. 
-         * old_window - received_bytes_on_that_win
-	 */
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	u32 new_win = __tcp_select_window(sk);
+	u32 cur_win = tcp_receive_window(tp);
 
-	cur_win = tp->rcv_wup - (tp->rcv_nxt - tp->rcv_wnd);
+	/* Never shrink the offered window */
+	if(new_win < cur_win)
+		new_win = cur_win;
+	tp->rcv_wnd = new_win;
+	tp->rcv_wup = tp->rcv_nxt;
 
+	/* RFC1323 scaling applied */
+	return new_win >> tp->rcv_wscale;
+}
 
-	/*
-	 *	We need to send an ack right away if
-	 *	our rcv window is blocking the sender and 
-	 *	we have more free space to offer.
+/* See if we can advertise non-zero, and if so how much we
+ * can increase our advertisement.  If it becomes more than
+ * twice what we are talking about right now, return true.
+ */
+extern __inline__ int tcp_raise_window(struct sock *sk)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	u32 new_win = __tcp_select_window(sk);
+	u32 cur_win = tcp_receive_window(tp);
+
+	return (new_win && (new_win > (cur_win << 1)));
+}
+
+/* This checks if the data bearing packet SKB (usually tp->send_head)
+ * should be put on the wire right now.
+ */
+static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	int nagle_check = 1;
+	int len;
+
+	/*	RFC 1122 - section 4.2.3.4
+	 *
+	 *	We must queue if
+	 *
+	 *	a) The right edge of this frame exceeds the window
+	 *	b) There are packets in flight and we have a small segment
+	 *	   [SWS avoidance and Nagle algorithm]
+	 *	   (part of SWS is done on packetization)
+	 *	c) We are retransmiting [Nagle]
+	 *	d) We have too many packets 'in flight'
+	 *
+	 * 	Don't use the nagle rule for urgent data.
 	 */
+	len = skb->end_seq - skb->seq;
+	if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out && 
+	    !skb->h.th->urg)
+		nagle_check = 0;
 
-	if (cur_win < (sk->mss << 1))
-		res = 1;
-	return res;
+	return (nagle_check && tp->packets_out < tp->snd_cwnd &&
+		!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
+		tp->retransmits == 0);
 }
 
-extern unsigned short	tcp_select_window(struct sock *sk);
+/* This tells the input processing path that an ACK should go out
+ * right now.
+ */
+#define tcp_enter_quickack_mode(__tp)	((__tp)->ato = (HZ/100))
+#define tcp_in_quickack_mode(__tp)	((__tp)->ato == (HZ/100))
 
 /*
  * List all states of a TCP socket that can be viewed as a "connected"
@@ -581,41 +731,49 @@
 	case TCP_CLOSE:
 		/* Should be about 2 rtt's */
 		net_reset_timer(sk, TIME_DONE, min(tp->srtt * 2, TCP_DONE_TIME));
+		sk->prot->unhash(sk);
 		/* fall through */
 	default:
 		if (oldstate==TCP_ESTABLISHED)
 			tcp_statistics.TcpCurrEstab--;
-		if (state == TCP_TIME_WAIT || state == TCP_CLOSE)
-			sk->prot->rehash(sk);
 	}
 }
 
 static __inline__ void tcp_build_options(__u32 *ptr, struct tcp_opt *tp)
 {
-	/* FIXME: We will still need to do SACK here. */
 	if (tp->tstamp_ok) {
-		*ptr = ntohl((TCPOPT_NOP << 24)
-			| (TCPOPT_NOP << 16)
-                        | (TCPOPT_TIMESTAMP << 8)
-			| TCPOLEN_TIMESTAMP);
+		*ptr = __constant_htonl((TCPOPT_NOP << 24) |
+					(TCPOPT_NOP << 16) |
+					(TCPOPT_TIMESTAMP << 8) |
+					TCPOLEN_TIMESTAMP);
 		/* rest filled in by tcp_update_options */
 	}
 }
 
 static __inline__ void tcp_update_options(__u32 *ptr, struct tcp_opt *tp)
 {
-	/* FIXME: We will still need to do SACK here. */
 	if (tp->tstamp_ok) {
 		*++ptr = htonl(jiffies);
 		*++ptr = htonl(tp->ts_recent);
 	}
 }
 
+static __inline__ void tcp_build_and_update_options(__u32 *ptr, struct tcp_opt *tp)
+{
+	if (tp->tstamp_ok) {
+		*ptr++ = __constant_htonl((TCPOPT_NOP << 24) |
+					  (TCPOPT_NOP << 16) |
+					  (TCPOPT_TIMESTAMP << 8) |
+					  TCPOLEN_TIMESTAMP);
+		*ptr++ = htonl(jiffies);
+		*ptr   = htonl(tp->ts_recent);
+	}
+}
+
 /* 
  *	This routines builds a generic TCP header. 
  *	They also build the RFC1323 Timestamp, but don't fill the
  *	actual timestamp in (you need to call tcp_update_options for this).
- *	It can't (unfortunately) do SACK as well.
  *	XXX: pass tp instead of sk here.
  */
  
@@ -624,23 +782,12 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 	memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
-	th->seq = htonl(sk->write_seq);
+	th->seq = htonl(tp->write_seq);
 	if (!push)
 		th->psh = 1;
 	tcp_build_options((__u32*)(th+1), tp);
 }
 
-static inline void tcp_build_header(struct tcphdr *th, struct sock *sk)
-{
-     	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-
-	memcpy(th,(void *) &(sk->dummy_th), sizeof(*th));
-	th->seq = htonl(sk->write_seq);
-	th->ack_seq = htonl(tp->last_ack_sent = tp->rcv_nxt);
-	th->window = htons(tcp_select_window(sk));
-	tcp_build_options((__u32 *)(th+1), tp);
-}
-
 /*
  * Construct a tcp options header for a SYN or SYN_ACK packet.
  * If this is every changed make sure to change the definition of
@@ -651,31 +798,32 @@
  * It would be especially magical to compute the checksum for this
  * stuff on the fly here.
  */
-extern __inline__ int tcp_syn_build_options(struct sk_buff *skb, int mss, int sack, int ts, int offer_wscale, int wscale)
+extern __inline__ int tcp_syn_build_options(struct sk_buff *skb, int mss, int ts, int offer_wscale, int wscale)
 {
-	int count = 4 + (offer_wscale ? 4 : 0) +  ((ts || sack) ? 4 : 0) +  (ts ? 8 : 0);
+	int count = 4 + (offer_wscale ? TCPOLEN_WSCALE_ALIGNED : 0) +
+		((ts) ? TCPOLEN_TSTAMP_ALIGNED : 0);
 	unsigned char *optr = skb_put(skb,count);
 	__u32 *ptr = (__u32 *)optr;
 
-	/*
-	 * We always get an MSS option.
+	/* We always get an MSS option.
+	 * The option bytes which will be seen in normal data
+	 * packets should timestamps be used, must be in the MSS
+	 * advertised.  But we subtract them from sk->mss so
+	 * that calculations in tcp_sendmsg are simpler etc.
+	 * So account for this fact here if necessary.  If we
+	 * don't do this correctly, as a receiver we won't
+	 * recognize data packets as being full sized when we
+	 * should, and thus we won't abide by the delayed ACK
+	 * rules correctly.
 	 */
+	if(ts)
+		mss += TCPOLEN_TSTAMP_ALIGNED;
 	*ptr++ = htonl((TCPOPT_MSS << 24) | (TCPOLEN_MSS << 16) | mss);
 	if (ts) {
-		if (sack) {
-			*ptr++ = htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16)
-					| (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
-			*ptr++ = htonl(jiffies);	/* TSVAL */
-			*ptr++ = htonl(0);		/* TSECR */
-		} else {
-			*ptr++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
-					| (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
-			*ptr++ = htonl(jiffies);	/* TSVAL */
-			*ptr++ = htonl(0);		/* TSECR */
-		}
-	} else if (sack) {
-		*ptr++ = htonl((TCPOPT_SACK_PERM << 24) | (TCPOLEN_SACK_PERM << 16)
-				| (TCPOPT_NOP << 8) | TCPOPT_NOP);
+		*ptr++ = __constant_htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
+					  (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
+		*ptr++ = htonl(jiffies);	/* TSVAL */
+		*ptr++ = __constant_htonl(0);	/* TSECR */
 	}
 	if (offer_wscale)
 		*ptr++ = htonl((TCPOPT_WINDOW << 24) | (TCPOLEN_WINDOW << 16) | (wscale << 8));
@@ -724,33 +872,15 @@
 	(*window_clamp) = min(65535<<(*rcv_wscale),*window_clamp);
 }
 
-#define SYNQ_DEBUG 1
-
 extern __inline__ void tcp_synq_unlink(struct tcp_opt *tp, struct open_request *req, struct open_request *prev)
 {
-#ifdef SYNQ_DEBUG
-	if (prev->dl_next != req) {
-		printk(KERN_DEBUG "synq_unlink: bad prev ptr: %p\n",prev);
-		return;
-	}
-#endif
-	if(!req->dl_next) {
-#ifdef SYNQ_DEBUG
-		if (tp->syn_wait_last != (void*) req)
-			printk(KERN_DEBUG "synq_unlink: bad last ptr %p,%p\n",
-			       req,tp->syn_wait_last);
-#endif
+	if(!req->dl_next)
 		tp->syn_wait_last = (struct open_request **)prev;
-	}
 	prev->dl_next = req->dl_next;
 }
 
 extern __inline__ void tcp_synq_queue(struct tcp_opt *tp, struct open_request *req)
 { 
-#ifdef SYNQ_DEBUG
-	if (*tp->syn_wait_last != NULL)
-	    printk("synq_queue: last ptr doesn't point to last req.\n"); 
-#endif
 	req->dl_next = NULL;
 	*tp->syn_wait_last = req; 
 	tp->syn_wait_last = &req->dl_next;
@@ -765,14 +895,11 @@
 extern __inline__ struct open_request *tcp_synq_unlink_tail(struct tcp_opt *tp)
 {
 	struct open_request *head = tp->syn_wait_queue;
-#ifdef SYNQ_DEBUG
-	if (!head) {
-		printk(KERN_DEBUG "tail drop on empty queue? - bug\n"); 
-		return NULL;
-	}
-#endif
+#if 0
+	/* Should be a net-ratelimit'd thing, not all the time. */
 	printk(KERN_DEBUG "synq tail drop with expire=%ld\n", 
 	       head->expires-jiffies);
+#endif
 	if (head->dl_next == NULL)
 		tp->syn_wait_last = &tp->syn_wait_queue;
 	tp->syn_wait_queue = head->dl_next;
@@ -799,6 +926,17 @@
 	atomic_dec(&slt->count);
 }
 
+/* This needs to use a slow timer, so it is here. */
+static __inline__ void tcp_sk_unbindify(struct sock *sk)
+{
+	struct tcp_bind_bucket *tb = (struct tcp_bind_bucket *) sk->prev;
+	if(sk->bind_next)
+		sk->bind_next->bind_pprev = sk->bind_pprev;
+	*sk->bind_pprev = sk->bind_next;
+	if(tb->owners == NULL)
+		tcp_inc_slow_timer(TCP_SLT_BUCKETGC);
+}
+
 extern const char timer_bug_msg[];
 
 static inline void tcp_clear_xmit_timer(struct sock *sk, int what)
@@ -820,7 +958,8 @@
 		printk(timer_bug_msg);
 		return;
 	};
-	del_timer(timer);
+	if(timer->prev != NULL)
+		del_timer(timer);
 }
 
 static inline int tcp_timer_is_set(struct sock *sk, int what)
@@ -829,13 +968,13 @@
 
 	switch (what) {
 	case TIME_RETRANS:
-		return tp->retransmit_timer.next != NULL;
+		return tp->retransmit_timer.prev != NULL;
 		break;
 	case TIME_DACK:
-		return tp->delack_timer.next != NULL;
+		return tp->delack_timer.prev != NULL;
 		break;
 	case TIME_PROBE0:
-		return tp->probe_timer.next != NULL;
+		return tp->probe_timer.prev != NULL;
 		break;	
 	default:
 		printk(timer_bug_msg);
diff -u --recursive --new-file v2.1.89/linux/init/main.c linux/init/main.c
--- v2.1.89/linux/init/main.c	Thu Feb 12 20:56:13 1998
+++ linux/init/main.c	Thu Mar 12 10:56:01 1998
@@ -78,8 +78,6 @@
 extern void smp_setup(char *str, int *ints);
 extern void ioapic_pirq_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);
 extern void panic_setup(char *str, int *ints);
 extern void bmouse_setup(char *str, int *ints);
 extern void msmouse_setup(char *str, int *ints);
@@ -278,7 +276,7 @@
 extern void ftape_setup(char *str, int *ints);
 #endif
 
-#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
+#if defined(CONFIG_SYSVIPC)
 extern void ipc_init(void);
 #endif
 
@@ -490,8 +488,6 @@
 #if defined (CONFIG_AMIGA) || defined (CONFIG_ATARI)
 	{ "video=", video_setup },
 #endif
-	{ "swap=", swap_setup },
-	{ "buff=", buff_setup },
 	{ "panic=", panic_setup },
 	{ "console=", console_setup },
 #ifdef CONFIG_VT
@@ -1061,7 +1057,7 @@
 	inode_init();
 	file_table_init();
 	sock_init();
-#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
+#if defined(CONFIG_SYSVIPC)
 	ipc_init();
 #endif
 	dquot_init();
@@ -1180,6 +1176,13 @@
 				printk(KERN_ERR "Change root to /initrd: "
 				    "error %d\n",error);
 		}
+	}
+#endif
+
+#ifdef CONFIG_KMOD
+	{
+		extern int kmod_init(void);
+		kmod_init();
 	}
 #endif
 
diff -u --recursive --new-file v2.1.89/linux/ipc/Makefile linux/ipc/Makefile
--- v2.1.89/linux/ipc/Makefile	Tue Jan  9 02:26:52 1996
+++ linux/ipc/Makefile	Tue Mar 10 14:43:13 1998
@@ -10,10 +10,6 @@
 O_TARGET := ipc.o
 O_OBJS   := util.o
 
-ifdef CONFIG_KERNELD
-CONFIG_SYSVIPC=1
-endif
-
 ifdef CONFIG_SYSVIPC
 O_OBJS += msg.o sem.o shm.o
 endif
diff -u --recursive --new-file v2.1.89/linux/ipc/msg.c linux/ipc/msg.c
--- v2.1.89/linux/ipc/msg.c	Tue Dec  9 09:49:59 1997
+++ linux/ipc/msg.c	Sun Mar 15 10:25:43 1998
@@ -1,19 +1,13 @@
 /*
  * linux/ipc/msg.c
  * Copyright (C) 1992 Krishna Balasubramanian 
- *
- * Kerneld extensions by Bjorn Ekwall <bj0rn@blox.se> in May 1995, and May 1996
- *
- * See <linux/kerneld.h> for the (optional) new kerneld protocol
  */
 
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/msg.h>
 #include <linux/stat.h>
 #include <linux/malloc.h>
-#include <linux/kerneld.h>
 #include <linux/interrupt.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
@@ -34,11 +28,6 @@
 static int used_queues = 0;
 static int max_msqid = 0;
 static struct wait_queue *msg_lock = NULL;
-static int kerneld_msqid = -1;
-
-#define MAX_KERNELDS 20
-static int kerneld_arr[MAX_KERNELDS];
-static int n_kernelds = 0;
 
 __initfunc(void msg_init (void))
 {
@@ -51,39 +40,6 @@
 	return;
 }
 
-/*
- * If the send queue is full, try to free any old messages.
- * These are most probably unwanted, since no one has picked them up...
- */
-#define MSG_FLUSH_TIME 10 /* seconds */
-static void flush_msg(struct msqid_ds *msq)
-{
-	struct msg *nmsg;
-	unsigned long flags;
-	int flushed = 0;
-
-	save_flags(flags);
-	cli();
-
-	/* messages were put on the queue in time order */
-	while ( (nmsg = msq->msg_first) &&
-		((CURRENT_TIME - nmsg->msg_stime) > MSG_FLUSH_TIME)) {
-		msgbytes -= nmsg->msg_ts; 
-		msghdrs--; 
-		msq->msg_cbytes -= nmsg->msg_ts;
-		msq->msg_qnum--;
-		msq->msg_first = nmsg->msg_next;
-		++flushed;
-		kfree(nmsg);
-	}
-
-	if (msq->msg_qnum == 0)
-		msq->msg_first = msq->msg_last = NULL;
-	restore_flags(flags);
-	if (flushed)
-		printk(KERN_WARNING "flushed %d old SYSVIPC messages", flushed);
-}
-
 static int real_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
 {
 	int id, err;
@@ -97,20 +53,12 @@
 		return -EINVAL;
 	if (!msgp) 
 		return -EFAULT;
-	/*
-	 * Calls from kernel level (IPC_KERNELD set)
-	 * have the message somewhere in kernel space already!
-	 */
-	if ((msgflg & IPC_KERNELD))
-		mtype = msgp->mtype;
-	else {
-		err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
-		if (err) 
-			return err;
-		get_user(mtype, &msgp->mtype);
-		if (mtype < 1)
-			return -EINVAL;
-	}
+	err = verify_area (VERIFY_READ, msgp->mtext, msgsz);
+	if (err) 
+		return err;
+	get_user(mtype, &msgp->mtype);
+	if (mtype < 1)
+		return -EINVAL;
 	id = (unsigned int) msqid % MSGMNI;
 	msq = msgque [id];
 	if (msq == IPC_UNUSED || msq == IPC_NOID)
@@ -120,29 +68,17 @@
  slept:
 	if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) 
 		return -EIDRM;
-	/*
-	 * Non-root kernel level processes may send to kerneld! 
-	 * i.e. no permission check if called from the kernel
-	 * otoh we don't want user level non-root snoopers...
-	 */
-	if ((msgflg & IPC_KERNELD) == 0)
-		if (ipcperms(ipcp, S_IWUGO)) 
-			return -EACCES;
+
+	if (ipcperms(ipcp, S_IWUGO)) 
+		return -EACCES;
 	
 	if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
-		if ((kerneld_msqid != -1) && (kerneld_msqid == msqid))
-			flush_msg(msq); /* flush the kerneld channel only */
 		if (msgsz + msq->msg_cbytes > msq->msg_qbytes) { 
 			/* still no space in queue */
 			if (msgflg & IPC_NOWAIT)
 				return -EAGAIN;
 			if (signal_pending(current))
 				return -EINTR;
-			if (in_interrupt()) {
-				/* Very unlikely, but better safe than sorry */
-				printk(KERN_WARNING "Ouch, kerneld:msgsnd buffers full!\n");
-				return -EINTR;
-			}
 			interruptible_sleep_on (&msq->wwait);
 			goto slept;
 		}
@@ -154,22 +90,7 @@
 		return -ENOMEM;
 	msgh->msg_spot = (char *) (msgh + 1);
 
-	/*
-	 * Calls from kernel level (IPC_KERNELD set)
-	 * have the message somewhere in kernel space already!
-	 */
-	if (msgflg & IPC_KERNELD) {
-		struct kerneld_msg *kdmp = (struct kerneld_msg *)msgp;
-
-		/*
-		 * Note that the kernel supplies a pointer
-		 * but the user-level kerneld uses a char array...
-		 */
-		memcpy(msgh->msg_spot, (char *)(&(kdmp->id)), KDHDR); 
-		memcpy(msgh->msg_spot + KDHDR, kdmp->text, msgsz - KDHDR); 
-	}
-	else
-		copy_from_user (msgh->msg_spot, msgp->mtext, msgsz); 
+	copy_from_user (msgh->msg_spot, msgp->mtext, msgsz); 
 	
 	if (msgque[id] == IPC_UNUSED || msgque[id] == IPC_NOID
 		|| msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
@@ -201,42 +122,8 @@
 	return 0;
 }
 
-/*
- * Take care of missing kerneld, especially in case of multiple daemons
- */
-#define KERNELD_TIMEOUT 1 * (HZ)
-#define DROP_TIMER del_timer(&kd_timer)
-/*#define DROP_TIMER if ((msgflg & IPC_KERNELD) && kd_timer.next && kd_timer.prev) del_timer(&kd_timer)*/
-
-static void kd_timeout(unsigned long msgid)
-{
-	struct msqid_ds *msq;
-	struct msg *tmsg;
-	unsigned long flags;
-
-	msq = msgque [ (unsigned int) kerneld_msqid % MSGMNI ];
-	if (msq == IPC_NOID || msq == IPC_UNUSED)
-		return;
-
-	save_flags(flags);
-	cli();
-	for (tmsg = msq->msg_first; tmsg; tmsg = tmsg->msg_next)
-		if (*(long *)(tmsg->msg_spot) == msgid)
-			break;
-	restore_flags(flags);
-	if (tmsg) { /* still there! */
-		struct kerneld_msg kmsp = { msgid, NULL_KDHDR, "" };
-
-		printk(KERN_ALERT "Ouch, no kerneld for message %ld\n", msgid);
-		kmsp.id = -ENODEV;
-		real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, KDHDR,
-			S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR);
-	}
-}
-
 static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
 {
-	struct timer_list kd_timer = { NULL, NULL, 0, 0, 0};
 	struct msqid_ds *msq;
 	struct ipc_perm *ipcp;
 	struct msg *tmsg, *leastp = NULL;
@@ -248,15 +135,10 @@
 		return -EINVAL;
 	if (!msgp || !msgp->mtext)
 	    return -EFAULT;
-	/*
-	 * Calls from kernel level (IPC_KERNELD set)
-	 * wants the message put in kernel space!
-	 */
-	if ((msgflg & IPC_KERNELD) == 0) {
-		err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
-		if (err)
-			return err;
-	}
+
+	err = verify_area (VERIFY_WRITE, msgp->mtext, msgsz);
+	if (err)
+		return err;
 
 	id = (unsigned int) msqid % MSGMNI;
 	msq = msgque [id];
@@ -264,16 +146,6 @@
 		return -EINVAL;
 	ipcp = &msq->msg_perm; 
 
-	/*
-	 * Start timer for missing kerneld
-	 */
-	if (msgflg & IPC_KERNELD) {
-		kd_timer.data = (unsigned long)msgtyp;
-		kd_timer.expires = jiffies + KERNELD_TIMEOUT;
-		kd_timer.function = kd_timeout;
-		add_timer(&kd_timer);
-	}
-
 	/* 
 	 *  find message of correct type.
 	 *  msgtyp = 0 => get first.
@@ -282,19 +154,10 @@
 	 */
 	while (!nmsg) {
 		if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
-			DROP_TIMER;
 			return -EIDRM;
 		}
-		if ((msgflg & IPC_KERNELD) == 0) {
-			/*
-			 * All kernel level processes may receive from kerneld! 
-			 * i.e. no permission check if called from the kernel
-			 * otoh we don't want user level non-root snoopers...
-			 */
-			if (ipcperms (ipcp, S_IRUGO)) {
-				DROP_TIMER; /* Not needed, but doesn't hurt */
-				return -EACCES;
-			}
+		if (ipcperms (ipcp, S_IRUGO)) {
+			return -EACCES;
 		}
 
 		save_flags(flags);
@@ -326,7 +189,6 @@
 		restore_flags(flags);
 		
 		if (nmsg) { /* done finding a message */
-			DROP_TIMER;
 			if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
 				return -E2BIG;
 			}
@@ -354,43 +216,20 @@
 			msq->msg_cbytes -= nmsg->msg_ts;
 			restore_flags(flags);
 			wake_up (&msq->wwait);
-			/*
-			 * Calls from kernel level (IPC_KERNELD set)
-			 * wants the message copied to kernel space!
-			 */
-			if (msgflg & IPC_KERNELD) {
-				struct kerneld_msg *kdmp = (struct kerneld_msg *) msgp;
-
-				memcpy((char *)(&(kdmp->id)),
-					nmsg->msg_spot, KDHDR); 
-				/*
-				 * Note that kdmp->text is a pointer
-				 * when called from kernel space!
-				 */
-				if ((msgsz > KDHDR) && kdmp->text)
-					memcpy(kdmp->text,
-						nmsg->msg_spot + KDHDR,
-						msgsz - KDHDR); 
-			}
-			else {
-				put_user (nmsg->msg_type, &msgp->mtype);
-				copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz);
-			}
+			put_user (nmsg->msg_type, &msgp->mtype);
+			copy_to_user (msgp->mtext, nmsg->msg_spot, msgsz);
 			kfree(nmsg);
 			return msgsz;
 		} else {  /* did not find a message */
 			if (msgflg & IPC_NOWAIT) {
-				DROP_TIMER;
 				return -ENOMSG;
 			}
 			if (signal_pending(current)) {
-				DROP_TIMER;
 				return -EINTR; 
 			}
 			interruptible_sleep_on (&msq->rwait);
 		}
 	} /* end while */
-	DROP_TIMER;
 	return -1;
 }
 
@@ -398,9 +237,8 @@
 {
 	int ret;
 
-	/* IPC_KERNELD is used as a marker for kernel level calls */
 	lock_kernel();
-	ret = real_msgsnd(msqid, msgp, msgsz, msgflg & ~IPC_KERNELD);
+	ret = real_msgsnd(msqid, msgp, msgsz, msgflg);
 	unlock_kernel();
 	return ret;
 }
@@ -410,9 +248,8 @@
 {
 	int ret;
 
-	/* IPC_KERNELD is used as a marker for kernel level calls */
 	lock_kernel();
-	ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg & ~IPC_KERNELD);
+	ret = real_msgrcv (msqid, msgp, msgsz, msgtyp, msgflg);
 	unlock_kernel();
 	return ret;
 }
@@ -479,36 +316,7 @@
 	int id, ret = -EPERM;
 	struct msqid_ds *msq;
 	
-	/*
-	 * If the IPC_KERNELD flag is set, the key is forced to IPC_PRIVATE,
-	 * and a designated kerneld message queue is created/referred to
-	 */
 	lock_kernel();
-	if ((msgflg & IPC_KERNELD)) {
-		int i;
-		if (!suser())
-			goto out;
-#ifdef NEW_KERNELD_PROTOCOL
-		if ((msgflg & IPC_KERNELD) == OLDIPC_KERNELD) {
-			printk(KERN_ALERT "Please recompile your kerneld daemons!\n");
-			goto out;
-		}
-#endif
-		ret = -ENOSPC;
-		if ((kerneld_msqid == -1) && (kerneld_msqid =
-				newque(IPC_PRIVATE, msgflg & S_IRWXU)) < 0)
-			goto out;
-		for (i = 0; i < MAX_KERNELDS; ++i) {
-			if (kerneld_arr[i] == 0) {
-				kerneld_arr[i] = current->pid;
-				++n_kernelds;
-				ret = kerneld_msqid;
-				goto out;
-			}
-		}
-		goto out;
-	}
-	/* else it is a "normal" request */
 	if (key == IPC_PRIVATE) 
 		ret = newque(key, msgflg);
 	else if ((id = findkey (key)) == -1) { /* key not used */
@@ -527,7 +335,6 @@
 		else
 			ret = (unsigned int) msq->msg_perm.seq * MSGMNI + id;
 	}
-out:
 	unlock_kernel();
 	return ret;
 } 
@@ -687,12 +494,7 @@
 		if (current->euid != ipcp->cuid && 
 		    current->euid != ipcp->uid && !suser())
 			goto out;
-		/*
-		 * There is only one kerneld message queue,
-		 * mark it as non-existent
-		 */
-		if ((kerneld_msqid >= 0) && (msqid == kerneld_msqid))
-			kerneld_msqid = -1;
+
 		freeque (id); 
 		err = 0;
 		goto out;
@@ -705,104 +507,3 @@
 	return err;
 }
 
-/*
- * We do perhaps need a "flush" for waiting processes,
- * so that if they are terminated, a call from do_exit
- * will minimize the possibility of orphaned received
- * messages in the queue.  For now we just make sure
- * that the queue is shut down whenever all kernelds have died.
- */
-void kerneld_exit(void)
-{
-	int i;
-
-        if (kerneld_msqid == -1)
-		return;
-	for (i = 0; i < MAX_KERNELDS; ++i) {
-		if (kerneld_arr[i] == current->pid) {
-			kerneld_arr[i] = 0;
-			--n_kernelds;
-			if (n_kernelds == 0)
-				sys_msgctl(kerneld_msqid, IPC_RMID, NULL);
-			break;
-		}
-	}
-}
-
-/*
- * Kerneld internal message format/syntax:
- *
- * The message type from the kernel to kerneld is used to specify _what_
- * function we want kerneld to perform. 
- *
- * The "normal" message area is divided into a header, followed by a char array.
- * The header is used to hold the sequence number of the request, which will
- * be used as the return message type from kerneld back to the kernel.
- * In the return message, the header will be used to store the exit status
- * of the kerneld "job", or task.
- * The character array is used to pass parameters to kerneld and (optional)
- * return information from kerneld back to the kernel.
- * It is the responsibility of kerneld and the kernel level caller
- * to set usable sizes on the parameter/return value array, since
- * that information is _not_ included in the message format
- */
-
-/*
- * The basic kernel level entry point to kerneld.
- *	msgtype should correspond to a task type for (a) kerneld
- *	ret_size is the size of the (optional) return _value,
- *		OR-ed with KERNELD_WAIT if we want an answer
- *	msgsize is the size (in bytes) of the message, not including
- *		the header that is always sent first in a kerneld message
- *	text is the parameter for the kerneld specific task
- *	ret_val is NULL or the kernel address where an expected answer
- *		from kerneld should be placed.
- *
- * See <linux/kerneld.h> for usage (inline convenience functions)
- *
- */
-int kerneld_send(int msgtype, int ret_size, int msgsz,
-		const char *text, const char *ret_val)
-{
-	int status = -ENOSYS;
-#ifdef CONFIG_KERNELD
-	static int id = KERNELD_MINSEQ;
-	struct kerneld_msg kmsp = { msgtype, NULL_KDHDR, (char *)text };
-	int msgflg = S_IRUSR | S_IWUSR | IPC_KERNELD | MSG_NOERROR;
-	unsigned long flags;
-
-	if (kerneld_msqid == -1)
-		return -ENODEV;
-
-	/* Do not wait for an answer at interrupt-time! */
-	if (in_interrupt())
-		ret_size &= ~KERNELD_WAIT;
-#ifdef NEW_KERNELD_PROTOCOL
-	else
-		kmsp.pid = current->pid;
-#endif
-
-	msgsz += KDHDR;
-	if (ret_size & KERNELD_WAIT) {
-		save_flags(flags);
-		cli();
-		if (++id <= 0) /* overflow */
-			id = KERNELD_MINSEQ;
-		kmsp.id = id;
-		restore_flags(flags);
-	}
-
-	status = real_msgsnd(kerneld_msqid, (struct msgbuf *)&kmsp, msgsz, msgflg);
-	if ((status >= 0) && (ret_size & KERNELD_WAIT)) {
-		ret_size &= ~KERNELD_WAIT;
-		kmsp.text = (char *)ret_val;
-		status = real_msgrcv(kerneld_msqid, (struct msgbuf *)&kmsp,
-				KDHDR + ((ret_val)?ret_size:0),
-				kmsp.id, msgflg);
-		if (status > 0) /* a valid answer contains at least a long */
-			status = kmsp.id;
-	}
-
-#endif /* CONFIG_KERNELD */
-	return status;
-}
diff -u --recursive --new-file v2.1.89/linux/ipc/util.c linux/ipc/util.c
--- v2.1.89/linux/ipc/util.c	Tue Dec  9 09:49:59 1997
+++ linux/ipc/util.c	Tue Mar 10 14:43:13 1998
@@ -20,7 +20,7 @@
 
 #include <asm/uaccess.h>
 
-#if defined(CONFIG_SYSVIPC) || defined(CONFIG_KERNELD)
+#if defined(CONFIG_SYSVIPC)
 
 extern void sem_init (void), msg_init (void), shm_init (void);
 
@@ -123,7 +123,4 @@
 	return -ENOSYS;
 }
 
-void kerneld_exit(void)
-{
-}
 #endif /* CONFIG_SYSVIPC */
diff -u --recursive --new-file v2.1.89/linux/kernel/Makefile linux/kernel/Makefile
--- v2.1.89/linux/kernel/Makefile	Mon Jan  5 01:41:01 1998
+++ linux/kernel/Makefile	Tue Mar 10 14:43:13 1998
@@ -17,6 +17,10 @@
 
 OX_OBJS  += signal.o
 
+ifeq ($(CONFIG_KMOD),y)
+O_OBJS += kmod.o
+endif
+
 ifeq ($(CONFIG_MODULES),y)
 OX_OBJS  += ksyms.o
 endif
diff -u --recursive --new-file v2.1.89/linux/kernel/exit.c linux/kernel/exit.c
--- v2.1.89/linux/kernel/exit.c	Tue Mar 10 10:03:35 1998
+++ linux/kernel/exit.c	Wed Mar 11 15:37:02 1998
@@ -27,7 +27,6 @@
 #include <asm/mmu_context.h>
 
 extern void sem_exit (void);
-extern void kerneld_exit(void);
 
 int getrusage(struct task_struct *, int, struct rusage *);
 
@@ -44,8 +43,14 @@
 		charge_uid(p, -1);
 		nr_tasks--;
 		add_free_taskslot(p->tarray_ptr);
-		unhash_pid(p);
-		REMOVE_LINKS(p);
+		{
+			unsigned long flags;
+
+			write_lock_irqsave(&tasklist_lock, flags);
+			unhash_pid(p);
+			REMOVE_LINKS(p);
+			write_unlock_irqrestore(&tasklist_lock, flags);
+		}
 		release_thread(p);
 		current->cmin_flt += p->min_flt + p->cmin_flt;
 		current->cmaj_flt += p->maj_flt + p->cmaj_flt;
@@ -157,7 +162,7 @@
 		unsigned long set = files->open_fds.fds_bits[j];
 		i = j * __NFDBITS;
 		j++;
-		if (i >= NR_OPEN)
+		if (i >= files->max_fds)
 			break;
 		while (set) {
 			if (set & 1) {
@@ -183,6 +188,13 @@
 		tsk->files = NULL;
 		if (!--files->count) {
 			close_files(files);
+			/*
+			 * Free the fd array as appropriate ...
+			 */
+			if (NR_OPEN * sizeof(struct file *) == PAGE_SIZE)
+				free_page((unsigned long) files->fd);
+			else
+				kfree(files->fd);
 			kmem_cache_free(files_cachep, files);
 		}
 	}
@@ -328,7 +340,6 @@
 	acct_process(code);
 	del_timer(&current->real_timer);
 	sem_exit();
-	kerneld_exit();
 	__exit_mm(current);
 #if CONFIG_AP1000
 	exit_msc(current);
diff -u --recursive --new-file v2.1.89/linux/kernel/fork.c linux/kernel/fork.c
--- v2.1.89/linux/kernel/fork.c	Tue Mar 10 10:03:36 1998
+++ linux/kernel/fork.c	Thu Mar 12 22:00:07 1998
@@ -41,7 +41,6 @@
 kmem_cache_t *files_cachep; 
 
 struct task_struct *pidhash[PIDHASH_SZ];
-spinlock_t pidhash_lock = SPIN_LOCK_UNLOCKED;
 
 struct task_struct **tarray_freelist = NULL;
 spinlock_t taskslot_lock = SPIN_LOCK_UNLOCKED;
@@ -263,6 +262,9 @@
 
 /*
  * Allocate and initialize an mm_struct.
+ *
+ * NOTE! The mm mutex will be locked until the
+ * caller decides that all systems are go..
  */
 struct mm_struct * mm_alloc(void)
 {
@@ -275,7 +277,7 @@
 		mm->count = 1;
 		mm->map_count = 0;
 		mm->def_flags = 0;
-		mm->mmap_sem = MUTEX;
+		mm->mmap_sem = MUTEX_LOCKED;
 		/*
 		 * Leave mm->pgd set to the parent's pgd
 		 * so that pgd_offset() is always valid.
@@ -328,6 +330,7 @@
 	retval = dup_mmap(mm);
 	if (retval)
 		goto free_pt;
+	up(&mm->mmap_sem);
 	return 0;
 
 free_mm:
@@ -375,44 +378,66 @@
 	return __copy_fdset(dst->fds_bits, src->fds_bits);  
 }
 
-static inline int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
 {
-	int i;  
 	struct files_struct *oldf, *newf;
 	struct file **old_fds, **new_fds;
+	int size, i, error = 0;
 
 	/*
 	 * A background process may not have any files ...
 	 */
 	oldf = current->files;
 	if (!oldf)
-		return 0;
+		goto out;
 
 	if (clone_flags & CLONE_FILES) {
 		oldf->count++;
-		return 0;
+		goto out;
 	}
 
+	tsk->files = NULL;
+	error = -ENOMEM;
 	newf = kmem_cache_alloc(files_cachep, SLAB_KERNEL);
-	tsk->files = newf;
 	if (!newf) 
-		return -1;
+		goto out;
+
+	/*
+	 * Allocate the fd array, using get_free_page() if possible.
+	 * Eventually we want to make the array size variable ...
+	 */
+	size = NR_OPEN * sizeof(struct file *);
+	if (size == PAGE_SIZE)
+		new_fds = (struct file **) __get_free_page(GFP_KERNEL);
+	else
+		new_fds = (struct file **) kmalloc(size, GFP_KERNEL);
+	if (!new_fds)
+		goto out_release;
+	memset((void *) new_fds, 0, size);
 
 	newf->count = 1;
+	newf->max_fds = NR_OPEN;
+	newf->fd = new_fds;
 	newf->close_on_exec = oldf->close_on_exec;
-	i = copy_fdset(&newf->open_fds,&oldf->open_fds);
+	i = copy_fdset(&newf->open_fds, &oldf->open_fds);
 
 	old_fds = oldf->fd;
-	new_fds = newf->fd;
 	for (; i != 0; i--) {
 		struct file * f = *old_fds;
 		old_fds++;
 		*new_fds = f;
-		new_fds++;
 		if (f)
 			f->f_count++;
+		new_fds++;
 	}
-	return 0;
+	tsk->files = newf;
+	error = 0;
+out:
+	return error;
+
+out_release:
+	kmem_cache_free(files_cachep, newf);
+	goto out;
 }
 
 static inline int copy_sighand(unsigned long clone_flags, struct task_struct * tsk)
@@ -495,8 +520,15 @@
 	p->start_time = jiffies;
 	p->tarray_ptr = &task[nr];
 	*p->tarray_ptr = p;
-	SET_LINKS(p);
-	hash_pid(p);
+
+	{
+		unsigned long flags;
+		write_lock_irqsave(&tasklist_lock, flags);
+		SET_LINKS(p);
+		hash_pid(p);
+		write_unlock_irqrestore(&tasklist_lock, flags);
+	}
+
 	nr_tasks++;
 
 	error = -ENOMEM;
@@ -553,8 +585,15 @@
 	if (p->binfmt && p->binfmt->module)
 		__MOD_DEC_USE_COUNT(p->binfmt->module);
 	add_free_taskslot(p->tarray_ptr);
-	unhash_pid(p);
-	REMOVE_LINKS(p);
+
+	{
+		unsigned long flags;
+		write_lock_irqsave(&tasklist_lock, flags); 
+		unhash_pid(p);
+		REMOVE_LINKS(p);
+		write_unlock_irqrestore(&tasklist_lock, flags);
+	}
+
 	nr_tasks--;
 bad_fork_free:
 	free_task_struct(p);
diff -u --recursive --new-file v2.1.89/linux/kernel/kmod.c linux/kernel/kmod.c
--- v2.1.89/linux/kernel/kmod.c	Wed Dec 31 16:00:00 1969
+++ linux/kernel/kmod.c	Wed Mar 11 10:51:10 1998
@@ -0,0 +1,149 @@
+/*
+	kmod, the new module loader (replaces kerneld)
+	Kirk Petersen
+*/
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+static inline _syscall1(int,delete_module,const char *,name_user)
+
+/*
+	kmod_unload_delay and modprobe_path are set via /proc/sys.
+*/
+int kmod_unload_delay = 60;
+char modprobe_path[256] = "/sbin/modprobe";
+char module_name[64] = "";
+char * argv[] = { "modprobe", "-k", NULL, NULL, };
+char * envp[] = { "HOME=/", "TERM=linux", NULL, };
+
+/*
+	kmod_queue synchronizes the kmod thread and the rest of the system
+	kmod_unload_timer is what we use to unload modules
+	after kmod_unload_delay seconds
+*/
+struct wait_queue * kmod_queue = NULL;
+struct timer_list kmod_unload_timer;
+
+/*
+	kmod_thread is the thread that does most of the work.  kmod_unload and
+	request_module tell it to wake up and do work.
+*/
+int kmod_thread(void * data)
+{
+	int pid;
+
+	/*
+		Initialize basic thread information
+	*/
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "kmod");
+	sigfillset(&current->blocked);
+
+	/*
+		This is the main kmod_thread loop.  It first sleeps, then
+		handles requests from request_module or kmod_unload.
+	*/
+
+	while (1) {
+		interruptible_sleep_on(&kmod_queue);
+
+		/*
+			If request_module woke us up, we should try to
+			load module_name.  If not, kmod_unload woke us up,
+			do call delete_module.
+			(if somehow both want us to do something, ignore the
+			 delete_module request)
+		*/
+		if (module_name[0] == '\0') {
+			delete_module(NULL);
+		} else {
+			pid = fork();
+			if (pid > 0) {
+				waitpid(pid, NULL, 0);
+				module_name[0] = '\0';
+				wake_up(&kmod_queue);
+			} else
+			if (pid == 0) {
+
+				/*
+					Call modprobe with module_name.  If execve returns,
+					print out an error.
+				*/
+				argv[2] = module_name;
+				execve(modprobe_path, argv, envp);
+
+				printk("kmod: failed to load module %s\n", module_name);
+				_exit(0);
+			} else {
+				printk("error, fork failed in kmod\n");
+			}
+		}
+	}
+
+	return 0;	/* Never reached. */
+}
+
+/*
+	kmod_unload is the function that the kernel calls when
+	the kmod_unload_timer expires
+*/
+void kmod_unload(unsigned long x)
+{
+	/*
+		wake up the kmod thread, which does the work
+		(we can't call delete_module, as it locks the kernel and
+		 we are in the bottom half of the kernel (right?))
+		once it is awake, reset the timer
+	*/
+	wake_up(&kmod_queue);
+	kmod_unload_timer.expires = jiffies + (kmod_unload_delay * HZ);
+	add_timer(&kmod_unload_timer);
+}
+
+int kmod_init(void)
+{
+	printk ("Starting kmod\n");
+
+	kernel_thread(kmod_thread, NULL, 0);
+
+	kmod_unload_timer.next = NULL;
+	kmod_unload_timer.prev = NULL;
+	kmod_unload_timer.expires = jiffies + (5 * 60 * HZ);
+	kmod_unload_timer.data = 0L;
+	kmod_unload_timer.function = kmod_unload;
+	add_timer(&kmod_unload_timer);
+
+	return 0;
+}
+
+/*
+	request_module, the function that everyone calls when they need a
+	module to be loaded
+*/
+int request_module(const char * name)
+{
+	/* first, copy the name of the module into module_name */
+	/* then wake_up() the kmod daemon */
+	/* wait for the kmod daemon to finish (it will wake us up) */
+
+	/*
+		kmod_thread is sleeping, so start by copying the name of
+		the module into module_name.  Once that is done, wake up
+		kmod_thread.
+	*/
+	strcpy(module_name, name);
+	wake_up(&kmod_queue);
+
+	/*
+		Now that we have told kmod_thread what to do, we want to
+		go to sleep and let it do its work.  It will wake us up,
+		at which point we will be done (the module will be loaded).
+	*/
+	interruptible_sleep_on(&kmod_queue);
+	return 0;
+}
diff -u --recursive --new-file v2.1.89/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v2.1.89/linux/kernel/ksyms.c	Tue Mar 10 10:03:36 1998
+++ linux/kernel/ksyms.c	Tue Mar 10 14:43:13 1998
@@ -61,8 +61,8 @@
 #if defined(CONFIG_PROC_FS)
 #include <linux/proc_fs.h>
 #endif
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 #include <asm/irq.h>
 #ifdef __SMP__
@@ -91,11 +91,12 @@
 #endif
 
 
+#ifdef CONFIG_KMOD
+EXPORT_SYMBOL(request_module);
+#endif
+
 #ifdef CONFIG_MODULES
 EXPORT_SYMBOL(get_module_symbol);
-#endif
-#ifdef CONFIG_KERNELD
-EXPORT_SYMBOL(kerneld_send);
 #endif
 EXPORT_SYMBOL(get_options);
 
diff -u --recursive --new-file v2.1.89/linux/kernel/module.c linux/kernel/module.c
--- v2.1.89/linux/kernel/module.c	Tue Mar 10 10:03:36 1998
+++ linux/kernel/module.c	Tue Mar 10 14:43:13 1998
@@ -324,7 +324,7 @@
 		dep->next_ref = d->refs;
 		d->refs = dep;
 		/* Being referenced by a dependant module counts as a 
-		   use as far as kerneld is concerned.  */
+		   use as far as kmod is concerned.  */
 		d->flags |= MOD_USED_ONCE;
 	}
 
diff -u --recursive --new-file v2.1.89/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.89/linux/kernel/sched.c	Thu Feb 12 20:56:13 1998
+++ linux/kernel/sched.c	Sun Mar 15 10:13:07 1998
@@ -155,9 +155,9 @@
  * The run-queue lock locks the parts that actually access
  * and change the run-queues, and have to be interrupt-safe.
  */
-rwlock_t tasklist_lock = RW_LOCK_UNLOCKED;
-spinlock_t scheduler_lock = SPIN_LOCK_UNLOCKED;
-spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t scheduler_lock = SPIN_LOCK_UNLOCKED;	/* should be aquired first */
+spinlock_t runqueue_lock = SPIN_LOCK_UNLOCKED;  /* second */
+rwlock_t tasklist_lock = RW_LOCK_UNLOCKED;	/* third */
 
 /*
  * Wake up a process. Put it on the run-queue if it's not
@@ -201,14 +201,20 @@
  */
 static inline int goodness(struct task_struct * p, struct task_struct * prev, int this_cpu)
 {
+	int policy = p->policy;
 	int weight;
 
+	if (policy & SCHED_YIELD) {
+		p->policy = policy & ~SCHED_YIELD;
+		return 0;
+	}
+
 	/*
 	 * Realtime process, select the first one on the
 	 * runqueue (taking priorities within processes
 	 * into account).
 	 */
-	if (p->policy != SCHED_OTHER)
+	if (policy != SCHED_OTHER)
 		return 1000 + p->rt_priority;
 
 	/*
@@ -228,9 +234,10 @@
 			weight += PROC_CHANGE_PENALTY;
 #endif
 
-		/* .. and a slight advantage to the current process */
-		if (p == prev)
+		/* .. and a slight advantage to the current thread */
+		if (p->mm == prev->mm)
 			weight += 1;
+		weight += p->priority;
 	}
 
 	return weight;
@@ -1253,10 +1260,11 @@
 
 static inline struct task_struct *find_process_by_pid(pid_t pid)
 {
+	struct task_struct *tsk = current;
+
 	if (pid)
-		return find_task_by_pid(pid);
-	else
-		return current;
+		tsk = find_task_by_pid(pid);
+	return tsk;
 }
 
 static int setscheduler(pid_t pid, int policy, 
@@ -1264,48 +1272,70 @@
 {
 	struct sched_param lp;
 	struct task_struct *p;
+	int retval;
 
+	retval = -EINVAL;
 	if (!param || pid < 0)
-		return -EINVAL;
+		goto out_nounlock;
 
+	retval = -EFAULT;
 	if (copy_from_user(&lp, param, sizeof(struct sched_param)))
-		return -EFAULT;
+		goto out_nounlock;
+
+	/*
+	 * We play safe to avoid deadlocks.
+	 */
+	spin_lock_irq(&scheduler_lock);
+	spin_lock(&runqueue_lock);
+	read_lock(&tasklist_lock);
 
 	p = find_process_by_pid(pid);
+
+	retval = -ESRCH;
 	if (!p)
-		return -ESRCH;
+		goto out_unlock;
 			
 	if (policy < 0)
 		policy = p->policy;
-	else if (policy != SCHED_FIFO && policy != SCHED_RR &&
-		 policy != SCHED_OTHER)
-		return -EINVAL;
+	else {
+		retval = -EINVAL;
+		if (policy != SCHED_FIFO && policy != SCHED_RR &&
+				policy != SCHED_OTHER)
+			goto out_unlock;
+	}
 	
 	/*
 	 * Valid priorities for SCHED_FIFO and SCHED_RR are 1..99, valid
 	 * priority for SCHED_OTHER is 0.
 	 */
+	retval = -EINVAL;
 	if (lp.sched_priority < 0 || lp.sched_priority > 99)
-		return -EINVAL;
+		goto out_unlock;
 	if ((policy == SCHED_OTHER) != (lp.sched_priority == 0))
-		return -EINVAL;
+		goto out_unlock;
 
+	retval = -EPERM;
 	if ((policy == SCHED_FIFO || policy == SCHED_RR) && !suser())
-		return -EPERM;
+		goto out_unlock;
 	if ((current->euid != p->euid) && (current->euid != p->uid) &&
 	    !suser())
-		return -EPERM;
+		goto out_unlock;
 
+	retval = 0;
 	p->policy = policy;
 	p->rt_priority = lp.sched_priority;
-	spin_lock(&scheduler_lock);
-	spin_lock_irq(&runqueue_lock);
 	if (p->next_run)
 		move_last_runqueue(p);
-	spin_unlock_irq(&runqueue_lock);
-	spin_unlock(&scheduler_lock);
+
 	need_resched = 1;
-	return 0;
+
+out_unlock:
+	read_unlock(&tasklist_lock);
+	spin_unlock(&runqueue_lock);
+	spin_unlock_irq(&scheduler_lock);
+
+out_nounlock:
+	return retval;
 }
 
 asmlinkage int sys_sched_setscheduler(pid_t pid, int policy, 
@@ -1322,42 +1352,64 @@
 asmlinkage int sys_sched_getscheduler(pid_t pid)
 {
 	struct task_struct *p;
+	int retval;
 
+	retval = -EINVAL;
 	if (pid < 0)
-		return -EINVAL;
+		goto out_nounlock;
+
+	read_lock(&tasklist_lock);
 
+	retval = -ESRCH;
 	p = find_process_by_pid(pid);
 	if (!p)
-		return -ESRCH;
+		goto out_unlock;
 			
-	return p->policy;
+	retval = p->policy;
+
+out_unlock:
+	read_unlock(&tasklist_lock);
+
+out_nounlock:
+	return retval;
 }
 
 asmlinkage int sys_sched_getparam(pid_t pid, struct sched_param *param)
 {
 	struct task_struct *p;
 	struct sched_param lp;
+	int retval;
 
+	retval = -EINVAL;
 	if (!param || pid < 0)
-		return -EINVAL;
+		goto out_nounlock;
 
+	read_lock(&tasklist_lock);
 	p = find_process_by_pid(pid);
+	retval = -ESRCH;
 	if (!p)
-		return -ESRCH;
-
+		goto out_unlock;
 	lp.sched_priority = p->rt_priority;
-	return copy_to_user(param, &lp, sizeof(struct sched_param)) ? -EFAULT : 0;
+	read_unlock(&tasklist_lock);
+
+	/*
+	 * This one might sleep, we cannot do it with a spinlock held ...
+	 */
+	retval = copy_to_user(param, &lp, sizeof(*param)) ? -EFAULT : 0;
+
+out_nounlock:
+	return retval;
+
+out_unlock:
+	read_unlock(&tasklist_lock);
+	return retval;
 }
 
 asmlinkage int sys_sched_yield(void)
 {
-	/*
-	 * This is not really right. We'd like to reschedule
-	 * just _once_ with this process having a zero count.
-	 */
-	current->counter = 0;
 	spin_lock(&scheduler_lock);
 	spin_lock_irq(&runqueue_lock);
+	current->policy |= SCHED_YIELD;
 	move_last_runqueue(current);
 	spin_unlock_irq(&runqueue_lock);
 	spin_unlock(&scheduler_lock);
diff -u --recursive --new-file v2.1.89/linux/kernel/signal.c linux/kernel/signal.c
--- v2.1.89/linux/kernel/signal.c	Tue Mar 10 10:03:36 1998
+++ linux/kernel/signal.c	Wed Mar 11 15:57:39 1998
@@ -438,8 +438,16 @@
 inline int
 kill_proc_info(int sig, struct siginfo *info, pid_t pid)
 {
-	struct task_struct *p = find_task_by_pid(pid);
-	return p ? send_sig_info(sig, info, p) : -ESRCH;
+	int error;
+	struct task_struct *p;
+
+	read_lock(&tasklist_lock);
+	p = find_task_by_pid(pid);
+	error = -ESRCH;
+	if (p)
+		error = send_sig_info(sig, info, p);
+	read_unlock(&tasklist_lock);
+	return error;
 }
 
 /*
diff -u --recursive --new-file v2.1.89/linux/kernel/sys.c linux/kernel/sys.c
--- v2.1.89/linux/kernel/sys.c	Sun Nov 30 12:34:44 1997
+++ linux/kernel/sys.c	Wed Mar 11 16:02:09 1998
@@ -578,14 +578,16 @@
 	if (pgid < 0)
 		return -EINVAL;
 
-	if((p = find_task_by_pid(pid)) == NULL)
-		return -ESRCH;
-
 	/* From this point forward we keep holding onto the tasklist lock
 	 * so that our parent does not change from under us. -DaveM
 	 */
 	read_lock(&tasklist_lock);
+
 	err = -ESRCH;
+	p = find_task_by_pid(pid);
+	if (!p)
+		goto out;
+
 	if (p->p_pptr == current || p->p_opptr == current) {
 		err = -EPERM;
 		if (p->session != current->session)
@@ -622,12 +624,17 @@
 	if (!pid) {
 		return current->pgrp;
 	} else {
-		struct task_struct *p = find_task_by_pid(pid);
+		int retval;
+		struct task_struct *p;
 
-		if(p)
-			return p->pgrp;
-		else
-			return -ESRCH;
+		read_lock(&tasklist_lock);
+		p = find_task_by_pid(pid);
+
+		retval = -ESRCH;
+		if (p)
+			retval = p->pgrp;
+		read_unlock(&tasklist_lock);
+		return retval;
 	}
 }
 
@@ -642,12 +649,17 @@
 	if (!pid) {
 		return current->session;
 	} else {
-		struct task_struct *p = find_task_by_pid(pid);
+		int retval;
+		struct task_struct *p;
+
+		read_lock(&tasklist_lock);
+		p = find_task_by_pid(pid);
 
+		retval = -ESRCH;
 		if(p)
-			return p->session;
-		else
-			return -ESRCH;
+			retval = p->session;
+		read_unlock(&tasklist_lock);
+		return retval;
 	}
 }
 
diff -u --recursive --new-file v2.1.89/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.1.89/linux/kernel/sysctl.c	Tue Mar 10 10:03:36 1998
+++ linux/kernel/sysctl.c	Thu Mar 12 10:56:00 1998
@@ -41,6 +41,10 @@
 extern int bdf_prm[], bdflush_min[], bdflush_max[];
 extern char binfmt_java_interpreter[], binfmt_java_appletviewer[];
 extern int sysctl_overcommit_memory;
+#ifdef CONFIG_KMOD
+extern char modprobe_path[];
+extern int kmod_unload_delay;
+#endif
 
 #ifdef __sparc__
 extern char reboot_command [];
@@ -174,6 +178,12 @@
 	 0644, NULL, &proc_dointvec},
 	{KERN_PRINTK, "printk", &console_loglevel, 4*sizeof(int),
 	 0644, NULL, &proc_dointvec},
+#ifdef CONFIG_KMOD
+	{KERN_MODPROBE, "modprobe", &modprobe_path, 256,
+	 0644, NULL, &proc_dostring, &sysctl_string },
+	{KERN_KMOD_UNLOAD_DELAY, "kmod_unload_delay", &kmod_unload_delay,
+	sizeof(int), 0644, NULL, &proc_dointvec},
+#endif
 	{0}
 };
 
@@ -183,12 +193,14 @@
 	{VM_SWAPOUT, "swapout_interval",
 	 &swapout_interval, sizeof(int), 0600, NULL, &proc_dointvec_jiffies},
 	{VM_FREEPG, "freepages", 
-	 &min_free_pages, 3*sizeof(int), 0600, NULL, &proc_dointvec},
+	 &freepages, sizeof(freepages_t), 0600, NULL, &proc_dointvec},
 	{VM_BDFLUSH, "bdflush", &bdf_prm, 9*sizeof(int), 0600, NULL,
 	 &proc_dointvec_minmax, &sysctl_intvec, NULL,
 	 &bdflush_min, &bdflush_max},
 	{VM_OVERCOMMIT_MEMORY, "overcommit_memory", &sysctl_overcommit_memory,
 	 sizeof(sysctl_overcommit_memory), 0644, NULL, &proc_dointvec},
+	{VM_BUFFERMEM, "buffermem",
+	 &buffer_mem, sizeof(buffer_mem_t), 0600, NULL, &proc_dointvec},
 	{0}
 };
 
diff -u --recursive --new-file v2.1.89/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.1.89/linux/mm/page_alloc.c	Tue Mar 10 10:03:36 1998
+++ linux/mm/page_alloc.c	Sat Mar 14 21:07:19 1998
@@ -330,9 +330,9 @@
 	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7);
 	if (i < 48)
 		i = 48;
-	min_free_pages = i;
-	free_pages_low = i + (i>>1);
-	free_pages_high = i + i;
+	freepages.min = i;
+	freepages.low = i + (i>>1);
+	freepages.high = i + i;
 	mem_map = (mem_map_t *) LONG_ALIGN(start_mem);
 	p = mem_map + MAP_NR(end_mem);
 	start_mem = LONG_ALIGN((unsigned long) p);
diff -u --recursive --new-file v2.1.89/linux/mm/slab.c linux/mm/slab.c
--- v2.1.89/linux/mm/slab.c	Thu Jan 15 21:08:36 1998
+++ linux/mm/slab.c	Tue Mar 10 09:48:36 1998
@@ -1808,7 +1808,9 @@
 	}
 
 	spin_lock_irq(&best_cachep->c_spinlock);
-	if (!best_cachep->c_growing && !(slabp = best_cachep->c_lastp)->s_inuse && slabp != kmem_slab_end(best_cachep)) {
+	while (!best_cachep->c_growing &&
+	       !(slabp = best_cachep->c_lastp)->s_inuse &&
+	       slabp != kmem_slab_end(best_cachep)) {
 		if (gfp_mask & GFP_DMA) {
 			do {
 				if (slabp->s_dma)
@@ -1832,7 +1834,7 @@
 		 */
 		spin_unlock_irq(&best_cachep->c_spinlock);
 		kmem_slab_destroy(best_cachep, slabp);
-		return;
+		spin_lock_irq(&best_cachep->c_spinlock);
 	}
 dma_fail:
 	spin_unlock_irq(&best_cachep->c_spinlock);
diff -u --recursive --new-file v2.1.89/linux/mm/swap.c linux/mm/swap.c
--- v2.1.89/linux/mm/swap.c	Tue May 13 22:41:20 1997
+++ linux/mm/swap.c	Thu Mar 12 10:56:00 1998
@@ -5,10 +5,12 @@
  */
 
 /*
- * This file should contain most things doing the swapping from/to disk.
+ * This file contains the default values for the opereation of the
+ * Linux VM subsystem. Finetuning documentation can be found in
+ * linux/Documentation/sysctl/vm.txt.
  * Started 18.12.91
- *
  * Swap aging added 23.2.95, Stephen Tweedie.
+ * Buffermem limits added 12.3.98, Rik van Riel.
  */
 
 #include <linux/mm.h>
@@ -33,15 +35,18 @@
 
 /*
  * We identify three levels of free memory.  We never let free mem
- * fall below the min_free_pages except for atomic allocations.  We
- * start background swapping if we fall below free_pages_high free
- * pages, and we begin intensive swapping below free_pages_low.
+ * fall below the freepages.min except for atomic allocations.  We
+ * start background swapping if we fall below freepages.high free
+ * pages, and we begin intensive swapping below freepages.low.
  *
- * Keep these three variables contiguous for sysctl(2).  
+ * These values are there to keep GCC from complaining. Actual
+ * initialization is done in mm/page_alloc.c or arch/sparc(64)/mm/init.c.
  */
-int min_free_pages = 48;
-int free_pages_low = 72;
-int free_pages_high = 96;
+freepages_t freepages = {
+	48,	/* freepages.min */
+	72,	/* freepages.low */
+	96	/* freepages.high */
+};
 
 /* We track the number of pages currently being asynchronously swapped
    out, so that we don't try to swap TOO many pages out at once */
@@ -55,53 +60,15 @@
 
 swap_control_t swap_control = {
 	20, 3, 1, 3,		/* Page aging */
-	10, 2, 2, 4,		/* Buffer aging */
 	32, 4,			/* Aging cluster */
 	8192, 8192,		/* Pageout and bufferout weights */
-	-200,			/* Buffer grace */
-	1, 1,			/* Buffs/pages to free */
-	RCL_ROUND_ROBIN		/* Balancing policy */
 };
 
 swapstat_t swapstats = {0};
 
-/* General swap control */
-
-/* Parse the kernel command line "swap=" option at load time: */
-__initfunc(void swap_setup(char *str, int *ints))
-{
-	int * swap_vars[8] = {
-		&MAX_PAGE_AGE,
-		&PAGE_ADVANCE,
-		&PAGE_DECLINE,
-		&PAGE_INITIAL_AGE,
-		&AGE_CLUSTER_FRACT,
-		&AGE_CLUSTER_MIN,
-		&PAGEOUT_WEIGHT,
-		&BUFFEROUT_WEIGHT
-	};
-	int i;
-	for (i=0; i < ints[0] && i < 8; i++) {
-		if (ints[i+1])
-			*(swap_vars[i]) = ints[i+1];
-	}
-}
-
-/* Parse the kernel command line "buff=" option at load time: */
-__initfunc(void buff_setup(char *str, int *ints))
-{
-	int * buff_vars[6] = {
-		&MAX_BUFF_AGE,
-		&BUFF_ADVANCE,
-		&BUFF_DECLINE,
-		&BUFF_INITIAL_AGE,
-		&BUFFEROUT_WEIGHT,
-		&BUFFERMEM_GRACE
-	};
-	int i;
-	for (i=0; i < ints[0] && i < 6; i++) {
-		if (ints[i+1])
-			*(buff_vars[i]) = ints[i+1];
-	}
-}
+buffer_mem_t buffer_mem = {
+	6,	/* minimum percent buffer + cache memory */
+	20,	/* borrow percent buffer + cache memory */
+	90	/* maximum percent buffer + cache memory */
+};
 
diff -u --recursive --new-file v2.1.89/linux/mm/swap_state.c linux/mm/swap_state.c
--- v2.1.89/linux/mm/swap_state.c	Tue Mar 10 10:03:36 1998
+++ linux/mm/swap_state.c	Tue Mar 10 10:51:02 1998
@@ -64,13 +64,13 @@
 #endif
 	if (PageTestandSetSwapCache(page)) {
 		printk("swap_cache: replacing non-empty entry %08lx "
-		       "on page %08lx",
+		       "on page %08lx\n",
 		       page->offset, page_address(page));
 		return 0;
 	}
 	if (page->inode) {
 		printk("swap_cache: replacing page-cached entry "
-		       "on page %08lx", page_address(page));
+		       "on page %08lx\n", page_address(page));
 		return 0;
 	}
 	atomic_inc(&page->count);
@@ -138,18 +138,18 @@
 {
 	if (!page->inode) {
 		printk ("VM: Removing swap cache page with zero inode hash "
-			"on page %08lx", page_address(page));
+			"on page %08lx\n", page_address(page));
 		return;
 	}
 	if (page->inode != &swapper_inode) {
 		printk ("VM: Removing swap cache page with wrong inode hash "
-			"on page %08lx", page_address(page));
+			"on page %08lx\n", page_address(page));
 	}
 	/*
 	 * This will be a legal case once we have a more mature swap cache.
 	 */
 	if (atomic_read(&page->count) == 1) {
-		printk ("VM: Removing page cache on unshared page %08lx", 
+		printk ("VM: Removing page cache on unshared page %08lx\n", 
 			page_address(page));
 		return;
 	}
diff -u --recursive --new-file v2.1.89/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.1.89/linux/mm/vmscan.c	Tue Mar 10 10:03:36 1998
+++ linux/mm/vmscan.c	Thu Mar 12 10:58:47 1998
@@ -6,7 +6,7 @@
  *  Swap reorganised 29.12.95, Stephen Tweedie.
  *  kswapd added: 7.1.96  sct
  *  Removed kswapd_ctl limits, and swap out as many pages as needed
- *  to bring the system back to free_pages_high: 2.4.97, Rik van Riel.
+ *  to bring the system back to freepages.high: 2.4.97, Rik van Riel.
  *  Version: $Id: vmscan.c,v 1.5 1998/02/23 22:14:28 sct Exp $
  */
 
@@ -22,6 +22,8 @@
 #include <linux/smp_lock.h>
 #include <linux/slab.h>
 #include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
 
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
@@ -454,11 +456,14 @@
 	stop = 3;
 	if (gfp_mask & __GFP_WAIT)
 		stop = 0;
+	if (BUFFER_MEM > buffer_mem.borrow_percent * num_physpages / 100)
+		state = 0;
 
 	switch (state) {
 		do {
 		case 0:
-			if (shrink_mmap(i, gfp_mask))
+			if (BUFFER_MEM > (buffer_mem.min_percent * num_physpages /100) &&
+					shrink_mmap(i, gfp_mask))
 				return 1;
 			state = 1;
 		case 1:
@@ -511,7 +516,6 @@
        printk ("Starting kswapd v%.*s\n", i, s);
 }
 
-#define MAX_SWAP_FAIL 3
 /*
  * The background pageout daemon.
  * Started as a kernel thread from the init process.
@@ -542,32 +546,25 @@
 	while (1) {
 		int tries;
 
+		current->state = TASK_INTERRUPTIBLE;
 		kswapd_awake = 0;
 		flush_signals(current);
 		run_task_queue(&tq_disk);
 		schedule();
-		current->state = TASK_INTERRUPTIBLE;
 		kswapd_awake = 1;
 		swapstats.wakeups++;
 		/* Do the background pageout: 
 		 * When we've got loads of memory, we try
-		 * (free_pages_high - nr_free_pages) times to
+		 * (freepages.high - nr_free_pages) times to
 		 * free memory. As memory gets tighter, kswapd
 		 * gets more and more agressive. -- Rik.
 		 */
-		tries = free_pages_high - nr_free_pages;
-		if (tries < min_free_pages) {
-			tries = min_free_pages;
+		tries = freepages.high - nr_free_pages;
+		if (tries < freepages.min) {
+			tries = freepages.min;
 		}
-		else if (nr_free_pages < (free_pages_high + free_pages_low) / 2) {
+		if (nr_free_pages < freepages.high + freepages.low)
 			tries <<= 1;
-			if (nr_free_pages < free_pages_low) {
-				tries <<= 1;
-				if (nr_free_pages <= min_free_pages) {
-					tries <<= 1;
-				}
-			}
-		}
 		while (tries--) {
 			int gfp_mask;
 
@@ -583,14 +580,6 @@
 				run_task_queue(&tq_disk);
 
 		}
-#if 0
-	/*
-	 * Report failure if we couldn't even reach min_free_pages.
-	 */
-	if (nr_free_pages < min_free_pages)
-		printk("kswapd: failed, got %d of %d\n",
-			nr_free_pages, min_free_pages);
-#endif
 	}
 	/* As if we could ever get here - maybe we want to make this killable */
 	remove_wait_queue(&kswapd_wait, &wait);
@@ -606,9 +595,10 @@
 	int want_wakeup = 0, memory_low = 0;
 	int pages = nr_free_pages + atomic_read(&nr_async_pages);
 
-	if (pages < free_pages_low)
+	if (pages < freepages.low)
 		memory_low = want_wakeup = 1;
-	else if (pages < free_pages_high && jiffies >= next_swap_jiffies)
+	else if ((pages < freepages.high || BUFFER_MEM > (num_physpages * buffer_mem.max_percent / 100))
+			&& jiffies >= next_swap_jiffies)
 		want_wakeup = 1;
 
 	if (want_wakeup) { 
diff -u --recursive --new-file v2.1.89/linux/net/802/sysctl_net_802.c linux/net/802/sysctl_net_802.c
--- v2.1.89/linux/net/802/sysctl_net_802.c	Mon Apr 14 16:28:27 1997
+++ linux/net/802/sysctl_net_802.c	Thu Mar 12 10:32:03 1998
@@ -23,5 +23,6 @@
 ctl_table tr_table[] = {
 	{NET_TR_RIF_TIMEOUT, "rif_timeout", &sysctl_tr_rif_timeout, sizeof(int),
 	 0644, NULL, &proc_dointvec},
+	{0}
 };
 #endif
diff -u --recursive --new-file v2.1.89/linux/net/802/tr.c linux/net/802/tr.c
--- v2.1.89/linux/net/802/tr.c	Mon Feb 23 18:12:12 1998
+++ linux/net/802/tr.c	Mon Mar 16 10:36:01 1998
@@ -510,10 +510,18 @@
  *	Called during bootup.  We don't actually have to initialise
  *	too much for this.
  */
- 
+
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry tr_rif_proc = {
+	PROC_NET_TR_RIF, 6, "tr_rif",
+	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_net_inode_operations,
+	rif_get_info
+};
+#endif
+
 __initfunc(void rif_init(struct net_proto *unused))
 {
-
 	rif_timer.expires  = RIF_TIMEOUT;
 	rif_timer.data     = 0L;
 	rif_timer.function = rif_check_expire;
@@ -521,11 +529,6 @@
 	add_timer(&rif_timer);
 
 #ifdef CONFIG_PROC_FS
-	proc_net_register(&(struct proc_dir_entry) {
-	  PROC_NET_TR_RIF, 6, "tr_rif",
-	    S_IFREG | S_IRUGO, 1, 0, 0,
-	    0, &proc_net_inode_operations,
-	    rif_get_info
-        });   
+	proc_net_register(&tr_rif_proc);
 #endif
 }
diff -u --recursive --new-file v2.1.89/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.1.89/linux/net/appletalk/ddp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/appletalk/ddp.c	Thu Mar 12 10:54:12 1998
@@ -990,7 +990,7 @@
 {
 	struct sock *sk;
 
-	sk = sk_alloc(AF_APPLETALK, GFP_KERNEL);
+	sk = sk_alloc(AF_APPLETALK, GFP_KERNEL, 1);
 	if(sk == NULL)
 		return (-ENOMEM);
 
@@ -1404,6 +1404,31 @@
 		return (0);
 	}
 
+#if defined(CONFIG_IPDDP) || defined(CONFIG_IPDDP_MODULE)
+        /*
+         *      Check if IP-over-DDP
+         */
+        if(skb->data[12] == 22)
+        {
+                struct device *dev;
+
+		/* This needs to be able to handle ipddp"N" devices */
+                if((dev = dev_get("ipddp0")) == NULL)
+                        return (-ENODEV);
+
+                skb->protocol = htons(ETH_P_IP);
+                skb_pull(skb, 13);
+                skb->dev = dev;
+                skb->h.raw = skb->data;
+
+                ((struct net_device_stats *)dev->priv)->rx_packets++;
+                ((struct net_device_stats *)dev->priv)->rx_bytes += skb->len+13;
+                netif_rx(skb);  /* Send the SKB up to a higher place. */
+
+                return (0);
+        }
+#endif
+
 	/*
 	 * Which socket - atalk_search_socket() looks for a *full match*
 	 * of the <net,node,port> tuple.
@@ -1420,38 +1445,6 @@
 		return (0);
 	}
 
-#ifdef CONFIG_IPDDP
-	/*
-	 *	Check if IP-over-DDP
-	 */
-	if(skb->data[12] == 22) 
-	{
-		struct device *dev;
-                struct net_device_stats *estats;
-
-		if((dev = dev_get("ipddp0")) == NULL)
-                	return (-ENODEV);
-
-		estats = (struct net_device_stats *) dev->priv;
-		skb->protocol = htons(ETH_P_IP);
-		skb_pull(skb, 13);
-		skb->dev = dev;
-		skb->h.raw = skb->data;
-		skb->nh.raw = skb->data;
-
-	/*	printk("passing up ipddp, 0x%02x better be 45\n",skb->data[0]);
-	 *	printk("tot_len %d, skb->len %d\n",
-	 *		ntohs(skb->h.iph->tot_len),skb->len);
-	 */
-
-		estats->rx_packets++;
-		estats->rx_bytes += skb->len + 13;
-		netif_rx(skb);	/* Send the SKB up to a higher place. */
-
-		return (0);
-	}
-#endif /* CONFIG_IPDDP */
-	
 	/*
 	 *	Queue packet (standard)
 	 */
diff -u --recursive --new-file v2.1.89/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v2.1.89/linux/net/ax25/af_ax25.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ax25/af_ax25.c	Thu Mar 12 10:54:12 1998
@@ -828,7 +828,7 @@
 			return -ESOCKTNOSUPPORT;
 	}
 
-	if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC, 1)) == NULL)
 		return -ENOMEM;
 
 	if ((ax25 = ax25_create_cb()) == NULL) {
@@ -854,7 +854,7 @@
 	struct sock *sk;
 	ax25_cb *ax25;
 
-	if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_AX25, GFP_ATOMIC, 1)) == NULL)
 		return NULL;
 
 	if ((ax25 = ax25_create_cb()) == NULL) {
@@ -1237,6 +1237,8 @@
 
 	newsk = skb->sk;
 	newsk->pair = NULL;
+	newsk->socket = newsock;
+	newsk->sleep = &newsock->wait;
 	sti();
 
 	/* Now attach up the new socket */
diff -u --recursive --new-file v2.1.89/linux/net/core/dev.c linux/net/core/dev.c
--- v2.1.89/linux/net/core/dev.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/dev.c	Tue Mar 10 14:47:28 1998
@@ -50,6 +50,7 @@
  *					is no device open function.
  *		Andi Kleen	:	Fix error reporting for SIOCGIFCONF
  *	    Michael Chastain	:	Fix signed/unsigned for SIOCGIFCONF
+ *		Cyrus Durgin	:	Cleaned for KMOD
  *
  */
 
@@ -81,7 +82,7 @@
 #include <net/pkt_sched.h>
 #include <net/profile.h>
 #include <linux/init.h>
-#include <linux/kerneld.h>
+#include <linux/kmod.h>
 #ifdef CONFIG_NET_RADIO
 #include <linux/wireless.h>
 #endif	/* CONFIG_NET_RADIO */
@@ -316,7 +317,7 @@
  *	Find and possibly load an interface.
  */
  
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 
 void dev_load(const char *name)
 {
@@ -398,20 +399,24 @@
 }
 
 #ifdef CONFIG_NET_FASTROUTE
-void dev_clear_fastroute(struct device *dev)
+
+static __inline__ void dev_do_clear_fastroute(struct device *dev)
 {
-	int i;
+	if (dev->accept_fastpath) {
+		int i;
 
-	if (dev) {
 		for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++)
 			dst_release(xchg(dev->fastpath+i, NULL));
+	}
+}
+
+void dev_clear_fastroute(struct device *dev)
+{
+	if (dev) {
+		dev_do_clear_fastroute(dev);
 	} else {
-		for (dev = dev_base; dev; dev = dev->next) {
-			if (dev->accept_fastpath) {
-				for (i=0; i<=NETDEV_FASTROUTE_HMASK; i++)
-					dst_release(xchg(dev->fastpath+i, NULL));
-			}
-		}
+		for (dev = dev_base; dev; dev = dev->next)
+			dev_do_clear_fastroute(dev);
 	}
 }
 #endif
@@ -643,7 +648,7 @@
 		set_bit(bit, &netdev_fc_mask);
 		clear_bit(bit, &netdev_fc_xoff);
 	}
-	sti();
+	restore_flags(flags);
 	return bit;
 }
 
@@ -659,7 +664,7 @@
 		clear_bit(bit, &netdev_fc_mask);
 		clear_bit(bit, &netdev_fc_xoff);
 	}
-	sti();
+	restore_flags(flags);
 }
 
 static void netdev_wakeup(void)
@@ -978,39 +983,6 @@
 
 
 /*
-   This ioctl is wrong by design. It really existed in some
-   old SYSV systems, only was named SIOCGIFNUM.
-   In multiprotocol environment it is just useless.
-   Well, SIOCGIFCONF is wrong too, but we have to preserve
-   it by compatibility reasons.
-
-   If someone wants to achieve the same effect, please, use undocumented
-   feature of SIOCGIFCONF: it returns buffer length, if buffer
-   is not supplied.
-
-   Let's remove it, until someone started to use it. --ANK
-
-   In any case, if someone cannot live without it, it should
-   be renamed to SIOCGIFNUM.
- */
-
-
-/*
- *	Count the installed interfaces (SIOCGIFCOUNT)
- */
-
-static int dev_ifcount(unsigned int *arg)
-{
-	struct device *dev;
-	unsigned int count = 0;
-
-	for (dev = dev_base; dev != NULL; dev = dev->next) 
-		count++;
-
-	return put_user(count, arg); 
-}
-
-/*
  *	Map an interface index to its name (SIOCGIFNAME)
  */
 
@@ -1022,6 +994,11 @@
  *	Besides that, it is pretty silly to put "drawing" facility
  *	to kernel, it is useful only to print ifindices
  *	in readable form, is not it? --ANK
+ *
+ *	We need this ioctl for efficient implementation of the
+ *	if_indextoname() function required by the IPv6 API.  Without
+ *	it, we would have to search all the interfaces to find a
+ *	match.  --pb
  */
 
 static int dev_ifname(struct ifreq *arg)
@@ -1120,20 +1097,21 @@
 	int size;
 	
 	if (stats)
-		size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %4lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu %4lu\n",
-		   dev->name,
+		size = sprintf(buffer, "%6s:%8lu %7lu %4lu %4lu %4lu %5lu %10lu %9lu %8lu %8lu %4lu %4lu %4lu %5lu %4lu %4lu\n",
+ 		   dev->name,
 		   stats->rx_bytes,
 		   stats->rx_packets, stats->rx_errors,
 		   stats->rx_dropped + stats->rx_missed_errors,
 		   stats->rx_fifo_errors,
 		   stats->rx_length_errors + stats->rx_over_errors
 		   + stats->rx_crc_errors + stats->rx_frame_errors,
+		   stats->rx_compressed, stats->multicast,
 		   stats->tx_bytes,
 		   stats->tx_packets, stats->tx_errors, stats->tx_dropped,
 		   stats->tx_fifo_errors, stats->collisions,
 		   stats->tx_carrier_errors + stats->tx_aborted_errors
 		   + stats->tx_window_errors + stats->tx_heartbeat_errors,
-		   stats->multicast);
+		   stats->tx_compressed);
 	else
 		size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
 
@@ -1156,8 +1134,8 @@
 
 
 	size = sprintf(buffer, 
-		"Inter-|   Receive                           |  Transmit\n"
-		" face |bytes    packets errs drop fifo frame|bytes    packets errs drop fifo colls carrier multicast\n");
+		"Inter-|   Receive                                                |  Transmit\n"
+		" face |bytes    packets errs drop fifo frame compressed multicast|bytes    packets errs drop fifo colls carrier compressed\n");
 	
 	pos+=size;
 	len+=size;
@@ -1554,9 +1532,6 @@
 		ret = dev_ifconf((char *) arg);
 		rtnl_shunlock();
 		return ret;
-	}
-	if (cmd == SIOCGIFCOUNT) {
-		return dev_ifcount((unsigned int*)arg);
 	}
 	if (cmd == SIOCGIFNAME) {
 		return dev_ifname((struct ifreq *)arg);
diff -u --recursive --new-file v2.1.89/linux/net/core/dst.c linux/net/core/dst.c
--- v2.1.89/linux/net/core/dst.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/dst.c	Mon Mar 16 10:36:01 1998
@@ -101,7 +101,7 @@
 void __dst_free(struct dst_entry * dst)
 {
 	start_bh_atomic();
-	dst->obsolete = 1;
+	dst->obsolete = 2;
 	dst->next = dst_garbage_list;
 	dst_garbage_list = dst;
 	if (dst_gc_timer_inc > DST_GC_INC) {
diff -u --recursive --new-file v2.1.89/linux/net/core/iovec.c linux/net/core/iovec.c
--- v2.1.89/linux/net/core/iovec.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/iovec.c	Thu Mar 12 10:32:03 1998
@@ -80,18 +80,21 @@
 
 /*
  *	Copy kernel to iovec.
+ *
+ *	Note: this modifies the original iovec.
  */
  
 int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
 {
-	int err = -EFAULT; 
+	int err;
 
 	while(len>0)
 	{
 		if(iov->iov_len)
 		{
 			int copy = min(iov->iov_len, len);
-			if (copy_to_user(iov->iov_base, kdata, copy))
+			err = copy_to_user(iov->iov_base, kdata, copy);
+			if (err)
 				goto out;
 			kdata+=copy;
 			len-=copy;
@@ -107,6 +110,8 @@
 
 /*
  *	Copy iovec to kernel.
+ *
+ *	Note: this modifies the original iovec.
  */
  
 int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len)
@@ -187,9 +192,8 @@
  *	call to this function will be unaligned also.
  */
 
-int csum_partial_copy_fromiovecend(unsigned char *kdata, 
-				   struct iovec *iov, int offset, 
-				   unsigned int len, int *csump)
+int csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov,
+				 int offset, unsigned int len, int *csump)
 {
 	int partial_cnt = 0;
 	int err = 0;
@@ -246,9 +250,9 @@
 				if (copy_from_user(kdata, base, copy))
 					goto out_fault;
 				kdata += copy;
-				base += copy;
+				base  += copy;
 				partial_cnt += copy;
-				len -= copy;
+				len   -= copy;
 				iov++;
 				if (len)
 					continue;
@@ -260,9 +264,9 @@
 				goto out_fault;
 			csum = csum_partial(kdata - partial_cnt, 4, csum);
 			kdata += par_len;
-			base += par_len;
-			copy -= par_len;
-			len -= par_len;
+			base  += par_len;
+			copy  -= par_len;
+			len   -= par_len;
 			partial_cnt = 0;
 		}
 
@@ -278,16 +282,12 @@
 			}
 		}
 
-		/* Why do we want to break?? There may be more to copy ... */
-		if (copy == 0) {
-if (len > partial_cnt)
-printk("csum_iovec: early break? len=%d, partial=%d\n", len, partial_cnt);
-			break;
+		if (copy) {
+			csum = csum_and_copy_from_user(base, kdata, copy,
+							csum, &err);
+			if (err)
+				goto out;
 		}
-
-		csum = csum_and_copy_from_user(base, kdata, copy, csum, &err);
-		if (err)
-			goto out;
 		len   -= copy + partial_cnt;
 		kdata += copy + partial_cnt;
 		iov++;
diff -u --recursive --new-file v2.1.89/linux/net/core/neighbour.c linux/net/core/neighbour.c
--- v2.1.89/linux/net/core/neighbour.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/neighbour.c	Mon Mar 16 10:36:01 1998
@@ -153,12 +153,14 @@
 static struct neighbour *neigh_alloc(struct neigh_table *tbl, int creat)
 {
 	struct neighbour *n;
+	unsigned long now = jiffies;
 
 	if (tbl->entries > tbl->gc_thresh1) {
 		if (creat < 0)
 			return NULL;
-		if (tbl->entries > tbl->gc_thresh2 ||
-		    jiffies - tbl->last_flush > 5*HZ) {
+		if (tbl->entries > tbl->gc_thresh3 ||
+		    (tbl->entries > tbl->gc_thresh2 &&
+		     now - tbl->last_flush > 5*HZ)) {
 			if (neigh_forced_gc(tbl) == 0 &&
 			    tbl->entries > tbl->gc_thresh3)
 				return NULL;
@@ -172,7 +174,7 @@
 	memset(n, 0, tbl->entry_size);
 
 	skb_queue_head_init(&n->arp_queue);
-	n->updated = n->used = jiffies;
+	n->updated = n->used = now;
 	n->nud_state = NUD_NONE;
 	n->output = neigh_blackhole;
 	n->parms = &tbl->parms;
@@ -666,8 +668,18 @@
 		neigh_suspect(neigh);
 	if (!(old&NUD_VALID)) {
 		struct sk_buff *skb;
-		while ((skb=__skb_dequeue(&neigh->arp_queue)) != NULL)
-			neigh->output(skb);
+
+		/* Again: avoid dead loop if something went wrong */
+
+		while (neigh->nud_state&NUD_VALID &&
+		       (skb=__skb_dequeue(&neigh->arp_queue)) != NULL) {
+			struct neighbour *n1 = neigh;
+			/* On shaper/eql skb->dst->neighbour != neigh :( */
+			if (skb->dst && skb->dst->neighbour)
+				n1 = skb->dst->neighbour;
+			n1->output(skb);
+		}
+		skb_queue_purge(&neigh->arp_queue);
 	}
 	return 0;
 }
@@ -1228,7 +1240,7 @@
          &proc_dointvec},
 	 {0}},
 
-	{{1, "default", NULL, 0, 0555, NULL},{0}},
+	{{NET_PROTO_CONF_DEFAULT, "default", NULL, 0, 0555, NULL},{0}},
 	{{0, "neigh", NULL, 0, 0555, NULL},{0}},
 	{{0, NULL, NULL, 0, 0555, NULL},{0}},
 	{{CTL_NET, "net", NULL, 0, 0555, NULL},{0}}
@@ -1243,10 +1255,11 @@
 	if (t == NULL)
 		return -ENOBUFS;
 	memcpy(t, &neigh_sysctl_template, sizeof(*t));
+	t->neigh_vars[0].data = &p->mcast_probes;
 	t->neigh_vars[1].data = &p->ucast_probes;
 	t->neigh_vars[2].data = &p->app_probes;
 	t->neigh_vars[3].data = &p->retrans_time;
-	t->neigh_vars[4].data = &p->reachable_time;
+	t->neigh_vars[4].data = &p->base_reachable_time;
 	t->neigh_vars[5].data = &p->delay_probe_time;
 	t->neigh_vars[6].data = &p->gc_staletime;
 	t->neigh_vars[7].data = &p->queue_len;
@@ -1256,7 +1269,7 @@
 	t->neigh_vars[11].data = &p->locktime;
 	if (dev) {
 		t->neigh_dev[0].procname = dev->name;
-		t->neigh_dev[0].ctl_name = dev->ifindex+1;
+		t->neigh_dev[0].ctl_name = dev->ifindex;
 		memset(&t->neigh_vars[12], 0, sizeof(ctl_table));
 	} else {
 		t->neigh_vars[12].data = (&p->locktime) + 1;
diff -u --recursive --new-file v2.1.89/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.89/linux/net/core/sock.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/sock.c	Tue Mar 10 09:47:26 1998
@@ -137,6 +137,8 @@
 __u32 sysctl_rmem_default = SK_RMEM_MAX;
 
 int sysctl_core_destroy_delay = SOCK_DESTROY_TIME;
+/* Maximal space eaten by iovec (still not made (2.1.88)!) plus some space */
+int sysctl_optmem_max = sizeof(unsigned long)*(2*UIO_MAXIOV + 512);
 
 /*
  *	This is meant for all protocols to use and covers goings on
@@ -472,11 +474,11 @@
  *	usage.
  */
  
-struct sock *sk_alloc(int family, int priority)
+struct sock *sk_alloc(int family, int priority, int zero_it)
 {
 	struct sock *sk = kmem_cache_alloc(sk_cachep, priority);
 
-	if(sk) {
+	if(sk && zero_it) {
 		memset(sk, 0, sizeof(struct sock));
 		sk->family = family;
 	}
@@ -561,34 +563,22 @@
 void *sock_kmalloc(struct sock *sk, int size, int priority)
 {
 	void *mem = NULL;
-	/* Always use wmem.. */
-	if (atomic_read(&sk->wmem_alloc)+size < sk->sndbuf) {
+	if (atomic_read(&sk->omem_alloc)+size < sysctl_optmem_max) {
 		/* First do the add, to avoid the race if kmalloc
  		 * might sleep.
 		 */
-		atomic_add(size, &sk->wmem_alloc);
+		atomic_add(size, &sk->omem_alloc);
 		mem = kmalloc(size, priority);
-		if (mem)
-			return mem; 
-		atomic_sub(size, &sk->wmem_alloc);
 	}
 	return mem;
 }
 
 void sock_kfree_s(struct sock *sk, void *mem, int size)
 {
-#if 1 /* Debug */
-	if (atomic_read(&sk->wmem_alloc) < size) {
-		printk(KERN_DEBUG "sock_kfree_s: mem not accounted.\n");
-		return;
-	}
-#endif
 	kfree_s(mem, size); 
-	atomic_sub(size, &sk->wmem_alloc);
-	sk->write_space(sk);
+	atomic_sub(size, &sk->omem_alloc);
 }
 
-
 /* FIXME: this is insane. We are trying suppose to be controlling how
  * how much space we have for data bytes, not packet headers.
  * This really points out that we need a better system for doing the
@@ -633,6 +623,30 @@
 	return(0);
 }
 
+/* It is almost wait_for_tcp_memory minus release_sock/lock_sock.
+   I think, these locks should be removed for datagram sockets.
+ */
+static void sock_wait_for_wmem(struct sock * sk)
+{
+	struct wait_queue wait = { current, NULL };
+
+	sk->socket->flags &= ~SO_NOSPACE;
+	add_wait_queue(sk->sleep, &wait);
+	for (;;) {
+		if (signal_pending(current))
+			break;
+		current->state = TASK_INTERRUPTIBLE;
+		if (atomic_read(&sk->wmem_alloc) < sk->sndbuf)
+			break;
+		if (sk->shutdown & SEND_SHUTDOWN)
+			break;
+		if (sk->err)
+			break;
+		schedule();
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(sk->sleep, &wait);
+}
 
 
 /*
@@ -641,94 +655,78 @@
 
 struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, unsigned long fallback, int noblock, int *errcode)
 {
+	int err;
 	struct sk_buff *skb;
 
-	do
-	{
-		if(sk->err!=0)
-		{
-			*errcode=xchg(&sk->err,0);
-			return NULL;
-		}
-		
-		if(sk->shutdown&SEND_SHUTDOWN)
-		{	
-			/*
-			 *	FIXME: Check 1003.1g should we deliver
-			 *	a signal here ???
-			 */
-			*errcode=-EPIPE;
-			return NULL;
-		}
-		
-		if(!fallback)
+	do {
+		if ((err = xchg(&sk->err,0)) != 0)
+			goto failure;
+
+		/*
+		 *	FIXME: Check 1003.1g should we deliver
+		 *	a signal here ???
+		 *
+		 *	Alan, could we solve this question once and forever?
+		 *
+		 *	I believe, datagram sockets should never
+		 *	generate SIGPIPE. Moreover, I DO think that
+		 *	TCP is allowed to generate it only on write()
+		 *	call, but never on send/sendto/sendmsg.
+		 *	(btw, Solaris generates it even on read() :-))
+		 *
+		 *	The reason is that SIGPIPE is global flag,
+		 *	so that library function using sockets (f.e. syslog()),
+		 *	must save/disable it on entry and restore on exit.
+		 *	As result, signal arriving for another thread will
+		 *	be lost. Generation it on write() is still necessary
+		 *	because a lot of stupid programs never check write()
+		 *	return value.
+		 *
+		 *	Seems, SIGPIPE is very bad idea, sort of gets().
+		 *	At least, we could have an option disabling
+		 *	this behaviour on per-socket and/or per-message base.
+		 *	BTW it is very easy - MSG_SIGPIPE flag, which
+		 *	always set by read/write and checked here.
+		 *						--ANK
+		 */
+
+		err = -EPIPE;
+		if (sk->shutdown&SEND_SHUTDOWN)
+			goto failure;
+
+		if (!fallback)
 			skb = sock_wmalloc(sk, size, 0, sk->allocation);
-		else
-		{
+		else {
 			/* The buffer get won't block, or use the atomic queue. It does
 			   produce annoying no free page messages still.... */
 			skb = sock_wmalloc(sk, size, 0 , GFP_BUFFER);
-			if(!skb)
+			if (!skb)
 				skb=sock_wmalloc(sk, fallback, 0, sk->allocation);
 		}
-		
+
 		/*
 		 *	This means we have too many buffers for this socket already.
 		 */
-		 
-		if(skb==NULL)
-		{
-			unsigned long tmp;
 
+		/* The following code is stolen "as is" from tcp.c */
+
+		if (skb==NULL) {
 			sk->socket->flags |= SO_NOSPACE;
-			if(noblock)
-			{
-				*errcode=-EAGAIN;
-				return NULL;
-			}
-			if(sk->shutdown&SEND_SHUTDOWN)
-			{
-				*errcode=-EPIPE;
-				return NULL;
-			}
-			tmp = atomic_read(&sk->wmem_alloc);
-			cli();
-			if(sk->shutdown&SEND_SHUTDOWN)
-			{
-				sti();
-				*errcode=-EPIPE;
-				return NULL;
-			}
-			
-#if 1
-			if( tmp <= atomic_read(&sk->wmem_alloc))
-#else
-			/* ANK: Line above seems either incorrect
-			 *	or useless. sk->wmem_alloc has a tiny chance to change
-			 *	between tmp = sk->w... and cli(),
-			 *	but it might(?) change earlier. In real life
-			 *	it does not (I never seen the message).
-			 *	In any case I'd delete this check at all, or
-			 *	change it to:
-			 */
-			if (atomic_read(&sk->wmem_alloc) >= sk->sndbuf) 
-#endif
-			{
-				sk->socket->flags &= ~SO_NOSPACE;
-				interruptible_sleep_on(sk->sleep);
-				if (signal_pending(current)) 
-				{
-					sti();
-					*errcode = -ERESTARTSYS;
-					return NULL;
-				}
-			}
-			sti();
+			err = -EAGAIN;
+			if (noblock)
+				goto failure;
+			err = -ERESTARTSYS;
+			if (signal_pending(current))
+				goto failure;
+			sock_wait_for_wmem(sk);
 		}
-	}
-	while(skb==NULL);
-		
+	} while (skb==NULL);
+
 	return skb;
+
+failure:
+	*errcode = err;
+	return NULL;
 }
 
 
diff -u --recursive --new-file v2.1.89/linux/net/core/sysctl_net_core.c linux/net/core/sysctl_net_core.c
--- v2.1.89/linux/net/core/sysctl_net_core.c	Tue Mar 10 10:03:36 1998
+++ linux/net/core/sysctl_net_core.c	Tue Mar 10 09:47:26 1998
@@ -22,6 +22,7 @@
 extern __u32 sysctl_rmem_default;
 
 extern int sysctl_core_destroy_delay;
+extern int sysctl_optmem_max;
 
 ctl_table core_table[] = {
 	{NET_CORE_WMEM_MAX, "wmem_max",
@@ -53,6 +54,9 @@
 	{NET_CORE_MSG_BURST, "message_burst",
 	 &net_msg_burst, sizeof(int), 0644, NULL,
 	 &proc_dointvec_jiffies},
+	{NET_CORE_OPTMEM_MAX, "optmem_max",
+	 &sysctl_optmem_max, sizeof(int), 0644, NULL,
+	 &proc_dointvec},
 	{ 0 }
 };
 #endif
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.89/linux/net/ipv4/af_inet.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/af_inet.c	Tue Mar 10 14:43:13 1998
@@ -5,7 +5,7 @@
  *
  *		AF_INET protocol family socket handler.
  *
- * Version:	$Id: af_inet.c,v 1.58 1997/10/29 20:27:21 kuznet Exp $
+ * Version:	$Id: af_inet.c,v 1.63 1998/03/08 05:56:12 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -52,6 +52,7 @@
  *	Willy Konynenberg	:	Transparent proxying support.
  *		David S. Miller	:	New socket lookup architecture.
  *					Some other random speedups.
+ *		Cyrus Durgin	:	Cleaned up file for kmod hacks.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -107,8 +108,8 @@
 #ifdef CONFIG_BRIDGE
 #include <net/br.h>
 #endif
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 #ifdef CONFIG_NET_RADIO
 #include <linux/wireless.h>
@@ -327,7 +328,7 @@
 		static int warned; 
 		if (net_families[AF_PACKET]==NULL)
 		{
-#if defined(CONFIG_KERNELD) && defined(CONFIG_PACKET_MODULE)
+#if defined(CONFIG_KMOD) && defined(CONFIG_PACKET_MODULE)
 			char module_name[30];
 			sprintf(module_name,"net-pf-%d", AF_PACKET);
 			request_module(module_name);
@@ -341,7 +342,7 @@
 	}
 
 	sock->state = SS_UNCONNECTED;
-	sk = sk_alloc(AF_INET, GFP_KERNEL);
+	sk = sk_alloc(AF_INET, GFP_KERNEL, 1);
 	if (sk == NULL) 
 		goto do_oom;
 
@@ -894,7 +895,7 @@
 		case SIOCDRARP:
 		case SIOCGRARP:
 		case SIOCSRARP:
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 			if (rarp_ioctl_hook == NULL)
 				request_module("rarp");
 #endif
@@ -928,7 +929,7 @@
 
 #ifdef CONFIG_DLCI_MODULE
 
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 			if (dlci_ioctl_hook == NULL)
 				request_module("dlci");
 #endif
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.1.89/linux/net/ipv4/arp.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/arp.c	Tue Mar 10 09:47:26 1998
@@ -1,6 +1,6 @@
 /* linux/net/inet/arp.c
  *
- * Version:	$Id: arp.c,v 1.58 1997/12/13 21:52:46 kuznet Exp $
+ * Version:	$Id: arp.c,v 1.65 1998/03/08 20:52:34 davem Exp $
  *
  * Copyright (C) 1994 by Florian  La Roche
  *
@@ -189,7 +189,7 @@
 	NULL,
 	parp_redo,
         { NULL, NULL, &arp_tbl, 0, NULL, NULL,
-		  30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 1*HZ, 64 },
+		  30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 1*HZ },
 	30*HZ, 128, 512, 1024,
 };
 
@@ -953,6 +953,10 @@
 		for (n=arp_tbl.hash_buckets[i]; n; n=n->next) {
 			struct device *dev = n->dev;
 			int hatype = dev->type;
+
+			/* Do not confuse users "arp -a" with magic entries */
+			if (!(n->nud_state&~NUD_NOARP))
+				continue;
 
 			/* I'd get great pleasure deleting
 			   this ugly code. Let's output it in hexadecimal format.
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v2.1.89/linux/net/ipv4/devinet.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/devinet.c	Tue Mar 10 14:43:13 1998
@@ -1,7 +1,7 @@
 /*
  *	NET3	IP device support routines.
  *
- *	Version: $Id: devinet.c,v 1.15 1997/12/13 21:52:47 kuznet Exp $
+ *	Version: $Id: devinet.c,v 1.19 1998/03/08 20:52:35 davem Exp $
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -19,6 +19,7 @@
  *
  *	Changes:
  *	        Alexey Kuznetsov:	pa_* fields are replaced with ifaddr lists.
+ 		Cyrus Durgin:		updated for kmod
  */
 
 #include <linux/config.h>
@@ -49,8 +50,8 @@
 #ifdef CONFIG_SYSCTL
 #include <linux/sysctl.h>
 #endif
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 #include <net/ip.h>
@@ -157,28 +158,32 @@
 inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, int destroy)
 {
 	struct in_ifaddr *ifa1 = *ifap;
-	struct in_ifaddr *ifa;
-
-	/* 1. Unlink it */
 
-	*ifap = ifa1->ifa_next;
-
-	/* 2. Deleting primary ifaddr forces deletion all secondaries */
+	/* 1. Deleting primary ifaddr forces deletion all secondaries */
 
 	if (!(ifa1->ifa_flags&IFA_F_SECONDARY)) {
-		while ((ifa=*ifap) != NULL) {
-			if (ifa1->ifa_mask != ifa->ifa_mask ||
+		struct in_ifaddr *ifa;
+		struct in_ifaddr **ifap1 = &ifa1->ifa_next;
+
+		while ((ifa=*ifap1) != NULL) {
+			if (!(ifa->ifa_flags&IFA_F_SECONDARY) ||
+			    ifa1->ifa_mask != ifa->ifa_mask ||
 			    !inet_ifa_match(ifa1->ifa_address, ifa)) {
-				ifap = &ifa->ifa_next;
+				ifap1 = &ifa->ifa_next;
 				continue;
 			}
-			*ifap = ifa->ifa_next;
+			*ifap1 = ifa->ifa_next;
 			rtmsg_ifa(RTM_DELADDR, ifa);
 			notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa);
 			inet_free_ifa(ifa);
 		}
 	}
 
+	/* 2. Unlink it */
+
+	*ifap = ifa1->ifa_next;
+
+
 	/* 3. Announce address deletion */
 
 	/* Send message first, then call notifier.
@@ -232,10 +237,9 @@
 		ifap = last_primary;
 	}
 
-	cli();
 	ifa->ifa_next = *ifap;
+	/* ATOMIC_SET */
 	*ifap = ifa;
-	sti();
 
 	/* Send message first, then call notifier.
 	   Notifier will trigger FIB update, so that
@@ -413,7 +417,7 @@
 		*colon = 0;
 #endif
 
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	dev_load(ifr.ifr_name);
 #endif
 
@@ -960,6 +964,8 @@
 	t->sysctl_header = register_sysctl_table(t->devinet_root_dir, 0);
 	if (t->sysctl_header == NULL)
 		kfree(t);
+	else
+		p->sysctl = t;
 }
 
 static void devinet_sysctl_unregister(struct ipv4_devconf *p)
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/fib_frontend.c linux/net/ipv4/fib_frontend.c
--- v2.1.89/linux/net/ipv4/fib_frontend.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/fib_frontend.c	Tue Mar 10 09:47:26 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: FIB frontend.
  *
- * Version:	$Id: fib_frontend.c,v 1.6 1997/12/13 21:52:48 kuznet Exp $
+ * Version:	$Id: fib_frontend.c,v 1.9 1998/03/08 20:52:36 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -151,7 +151,6 @@
 
 	memset(&key, 0, sizeof(key));
 	key.dst = addr;
-	key.scope = RT_SCOPE_UNIVERSE;
 
 	if (!local_table || local_table->tb_lookup(local_table, &key, &res)
 	    || res.type != RTN_LOCAL)
@@ -344,6 +343,10 @@
 	int s_t;
 	struct fib_table *tb;
 
+	if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
+	    ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
+		return ip_rt_dump(skb, cb);
+
 	s_t = cb->args[0];
 	if (s_t == 0)
 		s_t = cb->args[0] = RT_TABLE_MIN;
@@ -423,8 +426,13 @@
 	u32 addr = ifa->ifa_local;
 	u32 prefix = ifa->ifa_address&mask;
 
-	if (ifa->ifa_flags&IFA_F_SECONDARY)
+	if (ifa->ifa_flags&IFA_F_SECONDARY) {
 		prim = inet_ifa_byprefix(in_dev, prefix, mask);
+		if (prim == NULL) {
+			printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL\n");
+			return;
+		}
+	}
 
 	fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
 
@@ -435,7 +443,8 @@
 	if (ifa->ifa_broadcast && ifa->ifa_broadcast != 0xFFFFFFFF)
 		fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
 
-	if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY)) {
+	if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
+	    (prefix != addr || ifa->ifa_prefixlen < 32)) {
 		fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
 			  RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
 
@@ -464,8 +473,13 @@
 	if (!(ifa->ifa_flags&IFA_F_SECONDARY))
 		fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
 			  RTN_UNICAST, any, ifa->ifa_prefixlen, prim);
-	else 
+	else {
 		prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
+		if (prim == NULL) {
+			printk(KERN_DEBUG "fib_del_ifaddr: bug: prim == NULL\n");
+			return;
+		}
+	}
 
 	/* Deletion is more complicated than add.
 	   We should take care of not to delete too much :-)
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c
--- v2.1.89/linux/net/ipv4/fib_hash.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/fib_hash.c	Tue Mar 10 09:47:26 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 FIB: lookup engine and maintenance routines.
  *
- * Version:	$Id: fib_hash.c,v 1.1 1997/11/09 19:53:13 kuznet Exp $
+ * Version:	$Id: fib_hash.c,v 1.3 1998/03/08 05:56:16 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c
--- v2.1.89/linux/net/ipv4/fib_rules.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/fib_rules.c	Tue Mar 10 09:47:26 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: policy rules.
  *
- * Version:	$Id: fib_rules.c,v 1.2 1997/10/10 22:40:49 davem Exp $
+ * Version:	$Id: fib_rules.c,v 1.3 1998/03/08 05:56:17 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c
--- v2.1.89/linux/net/ipv4/fib_semantics.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/fib_semantics.c	Tue Mar 10 09:47:26 1998
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: semantics.
  *
- * Version:	$Id: fib_semantics.c,v 1.6 1997/12/13 21:52:49 kuznet Exp $
+ * Version:	$Id: fib_semantics.c,v 1.7 1998/03/08 05:56:18 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.1.89/linux/net/ipv4/icmp.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/icmp.c	Tue Mar 10 09:47:27 1998
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@cymru.net>
  *
- *	Version: $Id: icmp.c,v 1.36 1997/12/04 03:42:03 freitag Exp $
+ *	Version: $Id: icmp.c,v 1.39 1998/03/08 05:56:19 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -928,10 +928,8 @@
 			struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
 			sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex);
-			if (!sk) return 0;
-			if (sk->saddr != iph->saddr) return 0;
-			if (sk->daddr != iph->daddr) return 0;
-			if (sk->dummy_th.dest != th->dest) return 0;
+			if (!sk || (sk->state == TCP_LISTEN))
+				return 0;
 			/*
 			 * This packet came from us.
 			 */
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v2.1.89/linux/net/ipv4/igmp.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/igmp.c	Tue Mar 10 09:47:27 1998
@@ -8,7 +8,7 @@
  *	the older version didn't come out right using gcc 2.5.8, the newer one
  *	seems to fall out with gcc 2.6.2.
  *
- *	Version: $Id: igmp.c,v 1.24 1997/12/17 20:14:10 kuznet Exp $
+ *	Version: $Id: igmp.c,v 1.26 1998/03/08 05:56:19 davem Exp $
  *
  *	Authors:
  *		Alan Cox <Alan.Cox@linux.org>
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v2.1.89/linux/net/ipv4/ip_forward.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ip_forward.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		The IP forwarding functionality.
  *		
- * Version:	$Id: ip_forward.c,v 1.37 1997/12/18 17:01:11 kuznet Exp $
+ * Version:	$Id: ip_forward.c,v 1.40 1998/03/08 05:56:20 davem Exp $
  *
  * Authors:	see ip.c
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.1.89/linux/net/ipv4/ip_fragment.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ip_fragment.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.30 1997/12/29 19:52:32 kuznet Exp $
+ * Version:	$Id: ip_fragment.c,v 1.32 1998/03/08 05:56:21 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.1.89/linux/net/ipv4/ip_fw.c	Mon Feb 23 18:12:12 1998
+++ linux/net/ipv4/ip_fw.c	Mon Mar 16 10:36:01 1998
@@ -6,7 +6,7 @@
  *	license in recognition of the original copyright. 
  *				-- Alan Cox.
  *
- *	$Id: ip_fw.c,v 1.30 1997/12/19 12:06:27 freitag Exp $
+ *	$Id: ip_fw.c,v 1.33 1998/03/15 03:31:43 davem Exp $
  *
  *	Ported from BSD to Linux,
  *		Alan Cox 22/Nov/1994.
@@ -390,6 +390,39 @@
 		{
 			if(rif!=f->fw_viadev)
 				continue;	/* Mismatch */
+		}
+
+		/* This looks stupid, because we scan almost static
+		   list, searching for static key. However, this way seems
+		   to be only reasonable way of handling fw_via rules
+		   (btw bsd makes the same thing).
+
+		   It will not affect performance if you will follow
+		   the following simple rules:
+
+		   - if inteface is aliased, ALWAYS specify fw_viadev,
+		     so that previous check will guarantee, that we will
+		     not waste time when packet arrive on another interface.
+
+		   - avoid using fw_via.s_addr if fw_via.s_addr is owned
+		     by an aliased interface.
+
+		                                                       --ANK
+		 */
+		if (f->fw_via.s_addr && rif) {
+			struct in_ifaddr *ifa;
+
+			if (rif->ip_ptr == NULL)
+				continue;	/* Mismatch */
+
+			for (ifa = ((struct in_device*)(rif->ip_ptr))->ifa_list;
+			     ifa; ifa = ifa->ifa_next) {
+				if (ifa->ifa_local == f->fw_via.s_addr)
+					goto ifa_ok;
+			}
+			continue;	/* Mismatch */
+
+		ifa_ok:
 		}
 
 		/*
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.1.89/linux/net/ipv4/ip_input.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ip_input.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.25 1997/12/13 21:52:53 kuznet Exp $
+ * Version:	$Id: ip_input.c,v 1.27 1998/03/08 05:56:24 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_masq_mod.c linux/net/ipv4/ip_masq_mod.c
--- v2.1.89/linux/net/ipv4/ip_masq_mod.c	Wed Dec 10 09:45:16 1997
+++ linux/net/ipv4/ip_masq_mod.c	Tue Mar 10 14:43:13 1998
@@ -12,6 +12,8 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
+ * Changes:
+ *		Cyrus Durgin:		fixed kerneld stuff for kmod.
  */
 
 #include <linux/config.h>
@@ -21,8 +23,8 @@
 #include <linux/errno.h>
 #include <net/ip_masq.h>
 #include <net/ip_masq_mod.h>
-#ifdef CONFIG_KERNELD
-#include <linux/kerneld.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
 #endif
 
 EXPORT_SYMBOL(register_ip_masq_mod);
@@ -290,7 +292,7 @@
 int ip_masq_mod_ctl(int optname, struct ip_fw_masqctl *mctl, int optlen)
 {
 	struct ip_masq_mod * mmod;
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	char kmod_name[IP_MASQ_MOD_NMAX+8];
 #endif
 	/* tappo */
@@ -299,7 +301,7 @@
 	mmod = ip_masq_mod_getbyname(mctl->u.mod.name);
 	if (mmod)
 		return mmod->mmod_ctl(optname, mctl, optlen);
-#ifdef CONFIG_KERNELD
+#ifdef CONFIG_KMOD
 	sprintf(kmod_name,"ip_masq_%s", mctl->u.mod.name);
 
 	IP_MASQ_DEBUG(1, "About to request \"%s\" module\n", kmod_name);
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_masq_raudio.c linux/net/ipv4/ip_masq_raudio.c
--- v2.1.89/linux/net/ipv4/ip_masq_raudio.c	Mon Feb 23 18:12:12 1998
+++ linux/net/ipv4/ip_masq_raudio.c	Tue Mar 10 09:47:27 1998
@@ -2,7 +2,7 @@
  *		IP_MASQ_RAUDIO  - Real Audio masquerading module
  *
  *
- * Version:	@(#)$Id: ip_masq_raudio.c,v 1.8 1997/11/28 15:32:32 alan Exp $
+ * Version:	@(#)$Id: ip_masq_raudio.c,v 1.9 1998/02/23 02:50:19 davem Exp $
  *
  * Author:	Nigel Metheringham
  *		Real Time Streaming code by Progressive Networks
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_nat_dumb.c linux/net/ipv4/ip_nat_dumb.c
--- v2.1.89/linux/net/ipv4/ip_nat_dumb.c	Sun Nov 30 14:00:39 1997
+++ linux/net/ipv4/ip_nat_dumb.c	Mon Mar 16 10:36:01 1998
@@ -5,7 +5,7 @@
  *
  *		Dumb Network Address Translation.
  *
- * Version:	$Id: ip_nat_dumb.c,v 1.2 1997/10/10 22:41:05 davem Exp $
+ * Version:	$Id: ip_nat_dumb.c,v 1.3 1998/03/15 03:31:44 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -14,6 +14,9 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
+ * Fixes:
+ *		Rani Assaf	:	A zero checksum is a special case
+ *					only in UDP
  *
  * NOTE:	It is just working model of real NAT.
  */
@@ -49,7 +52,6 @@
 	u32 odaddr = iph->daddr;
 	u32 osaddr = iph->saddr;
 	u16	check;
-	u16	*cksum = NULL;
 
 	IPCB(skb)->flags |= IPSKB_TRANSLATED;
 
@@ -62,17 +64,23 @@
 	/* 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;
+		u16	*cksum;
+
+		switch(iph->protocol) {
+		case IPPROTO_TCP:
+			cksum  = (u16*)&((struct tcphdr*)(((char*)iph) + iph->ihl*4))->check;
+			check  = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~(*cksum));
+			*cksum = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+			break;
+		case IPPROTO_UDP:
+			cksum  = (u16*)&((struct udphdr*)(((char*)iph) + iph->ihl*4))->check;
+			if ((check = *cksum) != 0) {
+				check = csum_tcpudp_magic(iph->saddr, iph->daddr, 0, 0, ~check);
+				check = csum_tcpudp_magic(~osaddr, ~odaddr, 0, 0, ~check);
+				*cksum = check ? : 0xFFFF;
+			}
+		default:
+			break;
 		}
 	}
 	return 0;
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_options.c linux/net/ipv4/ip_options.c
--- v2.1.89/linux/net/ipv4/ip_options.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ip_options.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		The options processing module for ip.c
  *
- * Version:	$Id: ip_options.c,v 1.12 1997/10/10 22:41:08 davem Exp $
+ * Version:	$Id: ip_options.c,v 1.13 1998/02/12 07:43:12 davem Exp $
  *
  * Authors:	A.N.Kuznetsov
  *		
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v2.1.89/linux/net/ipv4/ip_output.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ip_output.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) output module.
  *
- * Version:	$Id: ip_output.c,v 1.45 1998/01/15 22:06:35 freitag Exp $
+ * Version:	$Id: ip_output.c,v 1.48 1998/03/08 05:56:25 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.89/linux/net/ipv4/ip_sockglue.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ip_sockglue.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		The IP to API glue.
  *		
- * Version:	$Id: ip_sockglue.c,v 1.30 1997/12/29 19:52:39 kuznet Exp $
+ * Version:	$Id: ip_sockglue.c,v 1.32 1998/03/08 05:56:26 davem Exp $
  *
  * Authors:	see ip.c
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- v2.1.89/linux/net/ipv4/ipconfig.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/ipconfig.c	Tue Mar 10 09:47:27 1998
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.6 1998/01/09 17:19:46 mj Exp $
+ *  $Id: ipconfig.c,v 1.11 1998/02/12 07:43:16 davem Exp $
  *
  *  Automatic Configuration of IP -- use BOOTP or RARP or user-supplied
  *  information to configure own IP address and routes.
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.1.89/linux/net/ipv4/ipip.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ipip.c	Tue Mar 10 09:47:27 1998
@@ -1,7 +1,7 @@
 /*
  *	Linux NET3:	IP/IP protocol decoder. 
  *
- *	Version: $Id: ipip.c,v 1.19 1997/11/08 17:50:21 kuznet Exp $
+ *	Version: $Id: ipip.c,v 1.22 1998/03/08 05:56:27 davem Exp $
  *
  *	Authors:
  *		Sam Lantinga (slouken@cs.ucdavis.edu)  02/01/95
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v2.1.89/linux/net/ipv4/ipmr.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/ipmr.c	Tue Mar 10 09:47:27 1998
@@ -9,7 +9,7 @@
  *	as published by the Free Software Foundation; either version
  *	2 of the License, or (at your option) any later version.
  *
- *	Version: $Id: ipmr.c,v 1.29 1997/12/13 21:52:55 kuznet Exp $
+ *	Version: $Id: ipmr.c,v 1.33 1998/03/08 20:52:37 davem Exp $
  *
  *	Fixes:
  *	Michael Chastain	:	Incorrect size of copying.
@@ -1351,6 +1351,7 @@
 	int ct;
 	struct rtnexthop *nhp;
 	struct device *dev = vif_table[c->mfc_parent].dev;
+	u8 *b = skb->tail;
 
 #ifdef CONFIG_RTNL_OLD_IFINFO
 	if (dev) {
@@ -1389,10 +1390,11 @@
 	return 1;
 
 rtattr_failure:
+	skb_trim(skb, b - skb->data);
 	return -EMSGSIZE;
 }
 
-int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm)
+int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait)
 {
 	struct mfc_cache *cache;
 	struct rtable *rt = (struct rtable*)skb->dst;
@@ -1400,10 +1402,16 @@
 	start_bh_atomic();
 	cache = ipmr_cache_find(rt->rt_src, rt->rt_dst);
 	if (cache==NULL || (cache->mfc_flags&MFC_QUEUED)) {
-		struct device *dev = skb->dev;
+		struct device *dev;
 		int vif;
 		int err;
 
+		if (nowait) {
+			end_bh_atomic();
+			return -EAGAIN;
+		}
+
+		dev = skb->dev;
 		if (dev == NULL || (vif = ipmr_find_vif(dev)) == ALL_VIFS) {
 			end_bh_atomic();
 			return -ENODEV;
@@ -1422,7 +1430,7 @@
 	 */
 	end_bh_atomic();
 
-	if (rtm->rtm_flags & RTM_F_NOTIFY)
+	if (!nowait && (rtm->rtm_flags&RTM_F_NOTIFY))
 		cache->mfc_flags |= MFC_NOTIFY;
 	return ipmr_fill_mroute(skb, cache, rtm);
 }
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.89/linux/net/ipv4/proc.c	Sun Nov 30 14:00:39 1997
+++ linux/net/ipv4/proc.c	Sat Mar 14 11:37:24 1998
@@ -7,7 +7,7 @@
  *		PROC file system.  It is mainly used for debugging and
  *		statistics.
  *
- * Version:	$Id: proc.c,v 1.23 1997/10/30 23:52:20 davem Exp $
+ * Version:	$Id: proc.c,v 1.26 1998/03/13 08:02:12 davem Exp $
  *
  * Authors:	Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Gerald J. Heim, <heim@peanuts.informatik.uni-tuebingen.de>
@@ -77,11 +77,12 @@
 	unsigned long  dest, src;
 	unsigned short destp, srcp;
 	int timer_active, timer_active1, timer_active2;
+	int tw_bucket = 0;
 	unsigned long timer_expires;
 	struct tcp_opt *tp = &sp->tp_pinfo.af_tcp;
 
 	dest  = sp->daddr;
-	src   = sp->saddr;
+	src   = sp->rcv_saddr;
 	destp = sp->dummy_th.dest;
 	srcp  = sp->dummy_th.source;
 	
@@ -96,30 +97,47 @@
 	
 	destp = ntohs(destp);
 	srcp  = ntohs(srcp);
-	timer_active1 = del_timer(&tp->retransmit_timer);
-	timer_active2 = del_timer(&sp->timer);
-	if (!timer_active1) tp->retransmit_timer.expires=0;
-	if (!timer_active2) sp->timer.expires=0;
-	timer_active=0;
-	timer_expires=(unsigned)-1;
+	if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
+		struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp;
+
+		tw_bucket	= 1;
+		timer_active1	= timer_active2 = 0;
+		timer_active	= 3;
+		timer_expires	= tw->timer.expires;
+	} else {
+		timer_active1 = del_timer(&tp->retransmit_timer);
+		timer_active2 = del_timer(&sp->timer);
+		if (!timer_active1) tp->retransmit_timer.expires=0;
+		if (!timer_active2) sp->timer.expires=0;
+		timer_active	= 0;
+		timer_expires	= (unsigned) -1;
+	}
 	if (timer_active1 && tp->retransmit_timer.expires < timer_expires) {
-		timer_active=timer_active1;
-		timer_expires=tp->retransmit_timer.expires;
+		timer_active	= 1;
+		timer_expires	= tp->retransmit_timer.expires;
 	}
 	if (timer_active2 && sp->timer.expires < timer_expires) {
-		timer_active=timer_active2;
-		timer_expires=sp->timer.expires;
-		}
+		timer_active	= 2;
+		timer_expires	= sp->timer.expires;
+	}
+	if(timer_active == 0)
+		timer_expires = jiffies;
 	sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
 		" %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
 		i, src, srcp, dest, destp, sp->state, 
-		format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc), 
-		format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
-				timer_active, timer_expires-jiffies,
-		tp->retransmits,
-		sp->socket ? sp->socket->inode->i_uid:0,
-		timer_active?sp->timeout:0,
-		sp->socket ? sp->socket->inode->i_ino:0);
+		(tw_bucket ?
+		 0 :
+		 (format == 0) ?
+		 tp->write_seq-tp->snd_una : atomic_read(&sp->wmem_alloc)),
+		(tw_bucket ?
+		 0 :
+		 (format == 0) ?
+		 tp->rcv_nxt-tp->copied_seq: atomic_read(&sp->rmem_alloc)),
+		timer_active, timer_expires-jiffies,
+		(tw_bucket ? 0 : tp->retransmits),
+		(!tw_bucket && sp->socket) ? sp->socket->inode->i_uid : 0,
+		(!tw_bucket && timer_active) ? sp->timeout : 0,
+		(!tw_bucket && sp->socket) ? sp->socket->inode->i_ino : 0);
 	
 	if (timer_active1) add_timer(&tp->retransmit_timer);
 	if (timer_active2) add_timer(&sp->timer);	
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/rarp.c linux/net/ipv4/rarp.c
--- v2.1.89/linux/net/ipv4/rarp.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/rarp.c	Tue Mar 10 09:47:27 1998
@@ -3,7 +3,7 @@
  * Copyright (C) 1994 by Ross Martin
  * Based on linux/net/inet/arp.c, Copyright (C) 1994 by Florian La Roche
  *
- * $Id: rarp.c,v 1.21 1997/10/27 09:13:16 geert Exp $
+ * $Id: rarp.c,v 1.24 1998/03/08 05:56:30 davem Exp $
  *
  * This module implements the Reverse Address Resolution Protocol 
  * (RARP, RFC 903), which is used to convert low level addresses such
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.1.89/linux/net/ipv4/raw.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/raw.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		RAW - implementation of IP "raw" sockets.
  *
- * Version:	$Id: raw.c,v 1.33 1997/12/27 20:41:15 kuznet Exp $
+ * Version:	$Id: raw.c,v 1.35 1998/03/08 05:56:32 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.1.89/linux/net/ipv4/route.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/route.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.36 1997/12/17 20:14:18 kuznet Exp $
+ * Version:	$Id: route.c,v 1.41 1998/03/08 20:52:38 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -167,7 +167,7 @@
 
 static struct rtable 	*rt_hash_table[RT_HASH_DIVISOR];
 
-static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth, u16 protocol);
+static struct rtable * rt_intern_hash(unsigned hash, struct rtable * rth);
 
 static __inline__ unsigned rt_hash_code(u32 daddr, u32 saddr, u8 tos)
 {
@@ -301,6 +301,8 @@
 	int i;
 	struct rtable * rth, * next;
 
+	rt_deadline = 0;
+
 	for (i=0; i<RT_HASH_DIVISOR; i++) {
 		int nr=0;
 
@@ -322,37 +324,41 @@
   
 void rt_cache_flush(int delay)
 {
+	unsigned long now = jiffies;
+	int user_mode = !in_interrupt();
+
 	if (delay < 0)
 		delay = ip_rt_min_delay;
 
 	start_bh_atomic();
 
 	if (del_timer(&rt_flush_timer) && delay > 0 && rt_deadline) {
-		long tmo = (long)(rt_deadline - rt_flush_timer.expires);
+		long tmo = (long)(rt_deadline - now);
 
 		/* If flush timer is already running
 		   and flush request is not immediate (delay > 0):
 
-		   if deadline is not achieved, prolongate timer to "dealy",
+		   if deadline is not achieved, prolongate timer to "delay",
 		   otherwise fire it at deadline time.
 		 */
 
+		if (user_mode && (long)(rt_deadline-now) < ip_rt_max_delay-ip_rt_min_delay)
+			tmo = 0;
+		
 		if (delay > tmo)
 			delay = tmo;
 	}
 
 	if (delay <= 0) {
-		rt_deadline = 0;
 		end_bh_atomic();
-
 		rt_run_flush(0);
 		return;
 	}
 
 	if (rt_deadline == 0)
-		rt_deadline = jiffies + ip_rt_max_delay;
+		rt_deadline = now + ip_rt_max_delay;
 
-	rt_flush_timer.expires = jiffies + delay;
+	rt_flush_timer.expires = now + delay;
 	add_timer(&rt_flush_timer);
 	end_bh_atomic();
 }
@@ -400,7 +406,7 @@
 	return (atomic_read(&ipv4_dst_ops.entries) > ip_rt_max_size);
 }
 
-static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt, u16 protocol)
+static struct rtable *rt_intern_hash(unsigned hash, struct rtable * rt)
 {
 	struct rtable	*rth, **rthp;
 	unsigned long	now = jiffies;
@@ -472,7 +478,9 @@
 		goto reject_redirect;
 
 	if (!IN_DEV_SHARED_MEDIA(in_dev)) {
-		if (ip_fib_check_default(new_gw, dev))
+		if (!inet_addr_onlink(in_dev, new_gw, old_gw))
+			goto reject_redirect;
+		if (IN_DEV_SEC_REDIRECTS(in_dev) && ip_fib_check_default(new_gw, dev))
 			goto reject_redirect;
 	} else {
 		if (inet_addr_type(new_gw) != RTN_UNICAST)
@@ -504,9 +512,13 @@
 				    rth->u.dst.dev != dev)
 					break;
 
+				dst_clone(&rth->u.dst);
+
 				rt = dst_alloc(sizeof(struct rtable), &ipv4_dst_ops);
-				if (rt == NULL)
+				if (rt == NULL) {
+					ip_rt_put(rth);
 					return;
+				}
 
 				/*
 				 * Copy all the information.
@@ -531,14 +543,16 @@
 					if (rt->u.dst.neighbour)
 						neigh_event_send(rt->u.dst.neighbour, NULL);
 					ip_rt_put(rt);
+					ip_rt_put(rth);
 					rt_free(rt);
 					break;
 				}
 
 				*rthp = rth->u.rt_next;
-				rt_free(rth);
-				rt = rt_intern_hash(hash, rt, ETH_P_IP);
+				rt = rt_intern_hash(hash, rt);
 				ip_rt_put(rt);
+				ip_rt_put(rth);
+				rt_free(rth);
 				break;
 			}
 		}
@@ -762,19 +776,45 @@
 	u32 src;
 	struct fib_result res;
 
-	if (rt->key.iif == 0) {
-		memcpy(addr, &rt->rt_src, 4);
-		return;
-	}
-	if (fib_lookup(&rt->key, &res) == 0) {
+	if (rt->key.iif == 0)
+		src = rt->rt_src;
+	else if (fib_lookup(&rt->key, &res) == 0)
 		src = FIB_RES_PREFSRC(res);
-		memcpy(addr, &src, 4);
-		return;
-	}
-	src = inet_select_addr(rt->u.dst.dev, rt->rt_gateway, RT_SCOPE_UNIVERSE);
+	else
+		src = inet_select_addr(rt->u.dst.dev, rt->rt_gateway, RT_SCOPE_UNIVERSE);
 	memcpy(addr, &src, 4);
 }
 
+static void rt_set_nexthop(struct rtable *rt, struct fib_result *res)
+{
+	struct fib_info *fi = res->fi;
+
+	if (fi) {
+		if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK)
+			rt->rt_gateway = FIB_RES_GW(*res);
+#ifndef CONFIG_RTNL_OLD_IFINFO
+		rt->u.dst.mxlock = fi->fib_metrics[RTAX_LOCK-1];
+		rt->u.dst.pmtu = fi->fib_mtu;
+		if (fi->fib_mtu == 0) {
+			rt->u.dst.pmtu = rt->u.dst.dev->mtu;
+			if (rt->u.dst.mxlock&(1<<RTAX_MTU) &&
+			    rt->rt_gateway != rt->rt_dst &&
+			    rt->u.dst.pmtu > 576)
+				rt->u.dst.pmtu = 576;
+		}
+#else
+		rt->u.dst.pmtu	= fi->fib_mtu ? : rt->u.dst.dev->mtu;
+#endif
+		rt->u.dst.window= fi->fib_window ? : 0;
+		rt->u.dst.rtt	= fi->fib_rtt ? : TCP_TIMEOUT_INIT;
+	} else {
+		rt->u.dst.pmtu	= rt->u.dst.dev->mtu;
+		rt->u.dst.window= 0;
+		rt->u.dst.rtt	= TCP_TIMEOUT_INIT;
+	}
+        rt->rt_type = res->type;
+}
+
 static int
 ip_route_input_mc(struct sk_buff *skb, u32 daddr, u32 saddr,
 		  u8 tos, struct device *dev, int our)
@@ -832,7 +872,7 @@
 #endif
 
 	hash = rt_hash_code(daddr, saddr^(dev->ifindex<<5), tos);
-	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, 0);
+	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth);
 	return 0;
 }
 
@@ -990,18 +1030,9 @@
 	rth->u.dst.input = ip_forward;
 	rth->u.dst.output = ip_output;
 
-	rth->u.dst.pmtu	= res.fi->fib_mtu ? : out_dev->dev->mtu;
-	rth->u.dst.window=res.fi->fib_window ? : 0;
-	rth->u.dst.rtt	= res.fi->fib_rtt ? : TCP_TIMEOUT_INIT;
-#ifndef CONFIG_RTNL_OLD_IFINFO
-	rth->u.dst.mxlock = res.fi->fib_metrics[RTAX_LOCK-1];
-#endif
-
-	if (FIB_RES_GW(res) && FIB_RES_NH(res).nh_scope == RT_SCOPE_LINK)
-		rth->rt_gateway	= FIB_RES_GW(res);
+	rt_set_nexthop(rth, &res);
 
 	rth->rt_flags = flags;
-	rth->rt_type = res.type;
 
 #ifdef CONFIG_NET_FASTROUTE
 	if (netdev_fastroute && !(flags&(RTCF_NAT|RTCF_MASQ|RTCF_DOREDIRECT))) {
@@ -1014,7 +1045,7 @@
 	}
 #endif
 
-	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, ntohs(skb->protocol));
+	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth);
 	return 0;
 
 brd_input:
@@ -1062,7 +1093,7 @@
 	}
 	rth->rt_flags 	= flags|RTCF_LOCAL;
 	rth->rt_type	= res.type;
-	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth, 0);
+	skb->dst = (struct dst_entry*)rt_intern_hash(hash, rth);
 	return 0;
 
 no_route:
@@ -1362,7 +1393,7 @@
 	rth->rt_dst_map	= key.dst;
 	rth->rt_src_map	= key.src;
 #endif
-	rth->rt_iif	= dev_out->ifindex;
+	rth->rt_iif	= oif ? : dev_out->ifindex;
 	rth->u.dst.dev	= dev_out;
 	rth->rt_gateway = key.dst;
 	rth->rt_spec_dst= key.src;
@@ -1388,24 +1419,12 @@
 #endif
 	}
 
-	if (res.fi) {
-		if (FIB_RES_GW(res) && FIB_RES_NH(res).nh_scope == RT_SCOPE_LINK)
-			rth->rt_gateway = FIB_RES_GW(res);
-		rth->u.dst.pmtu	= res.fi->fib_mtu ? : dev_out->mtu;
-		rth->u.dst.window=res.fi->fib_window ? : 0;
-		rth->u.dst.rtt	= res.fi->fib_rtt ? : TCP_TIMEOUT_INIT;
-#ifndef CONFIG_RTNL_OLD_IFINFO
-		rth->u.dst.mxlock = res.fi->fib_metrics[RTAX_LOCK-1];
-#endif
-	} else {
-		rth->u.dst.pmtu	= dev_out->mtu;
-		rth->u.dst.window=0;
-		rth->u.dst.rtt	= TCP_TIMEOUT_INIT;
-	}
+	rt_set_nexthop(rth, &res);
+
 	rth->rt_flags = flags;
-        rth->rt_type = res.type;
+
 	hash = rt_hash_code(daddr, saddr^(oif<<5), tos);
-	*rp = rt_intern_hash(hash, rth, ETH_P_IP);
+	*rp = rt_intern_hash(hash, rth);
 	return 0;
 }
 
@@ -1444,6 +1463,113 @@
 
 #ifdef CONFIG_RTNETLINK
 
+static int rt_fill_info(struct sk_buff *skb, pid_t pid, u32 seq, int event, int nowait)
+{
+	struct rtable *rt = (struct rtable*)skb->dst;
+	struct rtmsg *r;
+	struct nlmsghdr  *nlh;
+	unsigned char	 *b = skb->tail;
+	struct rta_cacheinfo ci;
+#ifdef CONFIG_IP_MROUTE
+	struct rtattr *eptr;
+#endif
+#ifdef CONFIG_RTNL_OLD_IFINFO
+	unsigned char 	 *o;
+#else
+	struct rtattr *mx;
+#endif
+
+	nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*r));
+	r = NLMSG_DATA(nlh);
+	nlh->nlmsg_flags = nowait ? NLM_F_MULTI : 0;
+	r->rtm_family = AF_INET;
+	r->rtm_dst_len = 32;
+	r->rtm_src_len = 32;
+	r->rtm_tos = rt->key.tos;
+	r->rtm_table = RT_TABLE_MAIN;
+	r->rtm_type = rt->rt_type;
+	r->rtm_scope = RT_SCOPE_UNIVERSE;
+	r->rtm_protocol = RTPROT_UNSPEC;
+	r->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED;
+#ifdef CONFIG_RTNL_OLD_IFINFO
+	r->rtm_nhs = 0;
+
+	o = skb->tail;
+#endif
+	RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst);
+	RTA_PUT(skb, RTA_SRC, 4, &rt->rt_src);
+	if (rt->u.dst.dev)
+		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
+	if (rt->rt_dst != rt->rt_gateway)
+		RTA_PUT(skb, RTA_GATEWAY, 4, &rt->rt_gateway);
+#ifdef CONFIG_RTNL_OLD_IFINFO
+	RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+	RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+	RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+#else
+	mx = (struct rtattr*)skb->tail;
+	RTA_PUT(skb, RTA_METRICS, 0, NULL);
+	if (rt->u.dst.mxlock)
+		RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock);
+	if (rt->u.dst.pmtu)
+		RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
+	if (rt->u.dst.window)
+		RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
+	if (rt->u.dst.rtt)
+		RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
+	mx->rta_len = skb->tail - (u8*)mx;
+	if (mx->rta_len == RTA_LENGTH(0))
+		skb_trim(skb, (u8*)mx - skb->data);
+#endif
+	RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst);
+	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
+	ci.rta_used = atomic_read(&rt->u.dst.refcnt);
+	ci.rta_clntref = atomic_read(&rt->u.dst.use);
+	ci.rta_expires = 0;
+	ci.rta_error = rt->u.dst.error;
+#ifdef CONFIG_IP_MROUTE
+	eptr = (struct rtattr*)skb->tail;
+#endif
+	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
+#ifdef CONFIG_RTNL_OLD_IFINFO
+	r->rtm_optlen = skb->tail - o;
+#endif
+	if (rt->key.iif) {
+#ifdef CONFIG_IP_MROUTE
+		u32 dst = rt->rt_dst;
+
+		if (MULTICAST(dst) && !LOCAL_MCAST(dst) && ipv4_devconf.mc_forwarding) {
+			int err = ipmr_get_route(skb, r, nowait);
+			if (err <= 0) {
+				if (!nowait) {
+					if (err == 0)
+						return 0;
+					goto nlmsg_failure;
+				} else {
+					if (err == -EMSGSIZE)
+						goto nlmsg_failure;
+					((struct rta_cacheinfo*)RTA_DATA(eptr))->rta_error = err;
+				}
+			}
+		} else
+#endif
+		{
+			RTA_PUT(skb, RTA_IIF, sizeof(int), &rt->key.iif);
+#ifdef CONFIG_RTNL_OLD_IFINFO
+			r->rtm_optlen = skb->tail - o;
+#endif
+		}
+	}
+
+	nlh->nlmsg_len = skb->tail - b;
+	return skb->len;
+
+nlmsg_failure:
+rtattr_failure:
+	skb_trim(skb, b - skb->data);
+	return -1;
+}
+
 int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg)
 {
 	struct rtattr **rta = arg;
@@ -1454,12 +1580,6 @@
 	int iif = 0;
 	int err;
 	struct sk_buff *skb;
-	struct rta_cacheinfo ci;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	unsigned char 	 *o;
-#else
-	struct rtattr *mx;
-#endif
 
 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 	if (skb == NULL)
@@ -1506,83 +1626,53 @@
 	if (rtm->rtm_flags & RTM_F_NOTIFY)
 		rt->rt_flags |= RTCF_NOTIFY;
 
-	nlh = NLMSG_PUT(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq,
-			RTM_NEWROUTE, sizeof(*rtm));
-	rtm = NLMSG_DATA(nlh);
-	nlh->nlmsg_flags = 0;
-	rtm->rtm_family = AF_INET;
-	rtm->rtm_dst_len = 32;
-	rtm->rtm_src_len = 32;
-	rtm->rtm_tos = rt->key.tos;
-	rtm->rtm_table = RT_TABLE_MAIN;
-	rtm->rtm_type = rt->rt_type;
-	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
-	rtm->rtm_protocol = RTPROT_UNSPEC;
-	rtm->rtm_flags = (rt->rt_flags&~0xFFFF) | RTM_F_CLONED;
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	rtm->rtm_nhs = 0;
+	NETLINK_CB(skb).pid = NETLINK_CB(in_skb).pid;
+
+	err = rt_fill_info(skb, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, RTM_NEWROUTE, 0);
+	if (err == 0)
+		return 0;
+	if (err < 0)
+		return -EMSGSIZE;
 
-	o = skb->tail;
-#endif
-	RTA_PUT(skb, RTA_DST, 4, &rt->rt_dst);
-	RTA_PUT(skb, RTA_SRC, 4, &rt->rt_src);
-	if (rt->u.dst.dev)
-		RTA_PUT(skb, RTA_OIF, sizeof(int), &rt->u.dst.dev->ifindex);
-	if (rt->rt_dst != rt->rt_gateway)
-		RTA_PUT(skb, RTA_GATEWAY, 4, &rt->rt_gateway);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	RTA_PUT(skb, RTA_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
-	RTA_PUT(skb, RTA_WINDOW, sizeof(unsigned), &rt->u.dst.window);
-	RTA_PUT(skb, RTA_RTT, sizeof(unsigned), &rt->u.dst.rtt);
-#else
-	mx = (struct rtattr*)skb->tail;
-	RTA_PUT(skb, RTA_METRICS, 0, NULL);
-	if (rt->u.dst.mxlock)
-		RTA_PUT(skb, RTAX_LOCK, sizeof(unsigned), &rt->u.dst.mxlock);
-	if (rt->u.dst.pmtu)
-		RTA_PUT(skb, RTAX_MTU, sizeof(unsigned), &rt->u.dst.pmtu);
-	if (rt->u.dst.window)
-		RTA_PUT(skb, RTAX_WINDOW, sizeof(unsigned), &rt->u.dst.window);
-	if (rt->u.dst.rtt)
-		RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
-	mx->rta_len = skb->tail - (u8*)mx;
-#endif
-	RTA_PUT(skb, RTA_PREFSRC, 4, &rt->rt_spec_dst);
-	ci.rta_lastuse = jiffies - rt->u.dst.lastuse;
-	ci.rta_used = atomic_read(&rt->u.dst.refcnt);
-	ci.rta_clntref = atomic_read(&rt->u.dst.use);
-	ci.rta_expires = 0;
-	ci.rta_error = rt->u.dst.error;
-	RTA_PUT(skb, RTA_CACHEINFO, sizeof(ci), &ci);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-	rtm->rtm_optlen = skb->tail - o;
-#endif
-	if (iif) {
-#ifdef CONFIG_IP_MROUTE
-		if (MULTICAST(dst) && !LOCAL_MCAST(dst) && ipv4_devconf.mc_forwarding) {
-			NETLINK_CB(skb).pid = NETLINK_CB(in_skb).pid;
-			err = ipmr_get_route(skb, rtm);
-			if (err <= 0)
-				return err;
-		} else
-#endif
-		{
-			RTA_PUT(skb, RTA_IIF, sizeof(int), &iif);
-#ifdef CONFIG_RTNL_OLD_IFINFO
-			rtm->rtm_optlen = skb->tail - o;
-#endif
-		}
-	}
-	nlh->nlmsg_len = skb->tail - (u8*)nlh;
 	err = netlink_unicast(rtnl, skb, NETLINK_CB(in_skb).pid, MSG_DONTWAIT);
 	if (err < 0)
 		return err;
 	return 0;
+}
 
-nlmsg_failure:
-rtattr_failure:
-	kfree_skb(skb);
-	return -EMSGSIZE;
+
+int ip_rt_dump(struct sk_buff *skb,  struct netlink_callback *cb)
+{
+	struct rtable *rt;
+	int h, s_h;
+	int idx, s_idx;
+
+	s_h = cb->args[0];
+	s_idx = idx = cb->args[1];
+	for (h=0; h < RT_HASH_DIVISOR; h++) {
+		if (h < s_h) continue;
+		if (h > s_h)
+			memset(&cb->args[1], 0, sizeof(cb->args) - sizeof(int));
+		start_bh_atomic();
+		for (rt = rt_hash_table[h], idx = 0; rt; rt = rt->u.rt_next, idx++) {
+			if (idx < s_idx)
+				continue;
+			skb->dst = dst_clone(&rt->u.dst);
+			if (rt_fill_info(skb, NETLINK_CB(cb->skb).pid,
+					 cb->nlh->nlmsg_seq, RTM_NEWROUTE, 1) <= 0) {
+				dst_release(xchg(&skb->dst, NULL));
+				end_bh_atomic();
+				goto done;
+			}
+			dst_release(xchg(&skb->dst, NULL));
+		}
+		end_bh_atomic();
+	}
+
+done:
+	cb->args[0] = h;
+	cb->args[1] = idx;
+	return skb->len;
 }
 
 #endif /* CONFIG_RTNETLINK */
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/syncookies.c linux/net/ipv4/syncookies.c
--- v2.1.89/linux/net/ipv4/syncookies.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/syncookies.c	Tue Mar 10 09:47:27 1998
@@ -9,7 +9,7 @@
  *      as published by the Free Software Foundation; either version
  *      2 of the License, or (at your option) any later version.
  * 
- *  $Id: syncookies.c,v 1.3 1997/09/16 17:16:21 freitag Exp $
+ *  $Id: syncookies.c,v 1.4 1998/03/08 05:56:34 davem Exp $
  *
  *  Missing: IPv6 support. 
  *           Some counter so that the Administrator can see when the machine
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v2.1.89/linux/net/ipv4/sysctl_net_ipv4.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/sysctl_net_ipv4.c	Mon Mar 16 10:36:01 1998
@@ -1,7 +1,7 @@
 /*
  * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
  *
- * $Id: sysctl_net_ipv4.c,v 1.25 1998/01/15 22:40:57 freitag Exp $
+ * $Id: sysctl_net_ipv4.c,v 1.28 1998/03/15 03:23:21 davem Exp $
  *
  * Begun April 1, 1996, Mike Shaver.
  * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
@@ -45,8 +45,6 @@
 
 extern int sysctl_tcp_cong_avoidance;
 extern int sysctl_tcp_hoe_retransmits;
-extern int sysctl_tcp_sack;
-extern int sysctl_tcp_tsack;
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
 extern int sysctl_tcp_keepalive_time;
@@ -57,7 +55,8 @@
 extern int sysctl_tcp_fin_timeout;
 extern int sysctl_tcp_syncookies;
 extern int sysctl_tcp_syn_retries;
-extern int sysctl_tcp_stdurg; 
+extern int sysctl_tcp_stdurg;
+extern int sysctl_tcp_rfc1337;
 extern int sysctl_tcp_syn_taildrop; 
 extern int sysctl_max_syn_backlog; 
 
@@ -99,12 +98,6 @@
         {NET_IPV4_TCP_HOE_RETRANSMITS, "tcp_hoe_retransmits",
          &sysctl_tcp_hoe_retransmits, sizeof(int), 0644, NULL,
          &proc_dointvec},
-        {NET_IPV4_TCP_SACK, "tcp_sack",
-         &sysctl_tcp_sack, sizeof(int), 0644, NULL,
-         &proc_dointvec},
-        {NET_IPV4_TCP_TSACK, "tcp_tsack",
-         &sysctl_tcp_tsack, sizeof(int), 0644, NULL,
-         &proc_dointvec},
         {NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps",
          &sysctl_tcp_timestamps, sizeof(int), 0644, NULL,
          &proc_dointvec},
@@ -161,6 +154,8 @@
 	 &sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec},
 #endif
 	{NET_TCP_STDURG, "tcp_stdurg", &sysctl_tcp_stdurg,
+	 sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_TCP_RFC1337, "tcp_rfc1337", &sysctl_tcp_rfc1337,
 	 sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_TCP_SYN_TAILDROP, "tcp_syn_taildrop", &sysctl_tcp_syn_taildrop,
 	 sizeof(int), 0644, NULL, &proc_dointvec},
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.89/linux/net/ipv4/tcp.c	Tue Mar 10 10:03:36 1998
+++ linux/net/ipv4/tcp.c	Mon Mar 16 10:36:01 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp.c,v 1.77 1998/01/15 22:40:18 freitag Exp $
+ * Version:	$Id: tcp.c,v 1.96 1998/03/16 02:25:55 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -169,7 +169,7 @@
  *					Fixed tcp_write_timeout: stuck close,
  *					and TCP syn retries gets used now.
  *		Mark Yarvis	:	In tcp_read_wakeup(), don't send an
- *					ack if stat is TCP_CLOSED.
+ *					ack if state is TCP_CLOSED.
  *		Alan Cox	:	Look up device on a retransmit - routes may
  *					change. Doesn't yet cope with MSS shrink right
  *					but its a start!
@@ -425,6 +425,8 @@
 struct tcp_mib	tcp_statistics;
 
 kmem_cache_t *tcp_openreq_cachep;
+kmem_cache_t *tcp_bucket_cachep;
+kmem_cache_t *tcp_timewait_cachep;
 
 /*
  *	Find someone to 'accept'. Must be called with
@@ -478,20 +480,6 @@
 }
 
 /*
- *	Enter the time wait state.
- */
-
-void tcp_time_wait(struct sock *sk)
-{
-	tcp_set_state(sk,TCP_TIME_WAIT);
-	sk->shutdown = SHUTDOWN_MASK;
-	if (!sk->dead)
-		sk->state_change(sk);
-	tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-}
-
-
-/*
  *	Walk down the receive queue counting readable data.
  *
  *	Must be called with the socket lock held.
@@ -512,7 +500,7 @@
 	  	return(0);
 	}
 
-	counted = sk->copied_seq;	/* Where we are at the moment */
+	counted = sk->tp_pinfo.af_tcp.copied_seq;	/* Where we are at the moment */
 	amount = 0;
 
 	/* Do until a push or until we are out of data. */
@@ -606,10 +594,10 @@
 		if (sk->shutdown & RCV_SHUTDOWN)
 			mask |= POLLHUP;
 		
-		if ((tp->rcv_nxt != sk->copied_seq) &&
-		    (sk->urg_seq != sk->copied_seq ||
-		     tp->rcv_nxt != sk->copied_seq+1 ||
-		     sk->urginline || !sk->urg_data))
+		if ((tp->rcv_nxt != tp->copied_seq) &&
+		    (tp->urg_seq != tp->copied_seq ||
+		     tp->rcv_nxt != tp->copied_seq+1 ||
+		     sk->urginline || !tp->urg_data))
 			mask |= POLLIN | POLLRDNORM;
 
 #if 1 /* This needs benchmarking and real world tests */
@@ -621,9 +609,9 @@
 		space = atomic_read(&sk->wmem_alloc) / 2;
 #endif
 		/* Always wake the user up when an error occured */
-		if (sock_wspace(sk) >= space)
+		if (sock_wspace(sk) >= space || sk->err)
 			mask |= POLLOUT | POLLWRNORM;
-		if (sk->urg_data)
+		if (tp->urg_data)
 		    	mask |= POLLPRI;
 	}
 	return mask;
@@ -649,7 +637,8 @@
 		}
 		case SIOCATMARK:
 		{
-			int answ = sk->urg_data && sk->urg_seq == sk->copied_seq;
+			struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+			int answ = tp->urg_data && tp->urg_seq == tp->copied_seq;
 			return put_user(answ,(int *) arg);
 		}
 		case TIOCOUTQ:
@@ -669,21 +658,38 @@
 /*
  *	Wait for a socket to get into the connected state
  */
-static void wait_for_tcp_connect(struct sock * sk)
+static int wait_for_tcp_connect(struct sock * sk, int flags)
 {
 	struct task_struct *tsk = current;
 	struct wait_queue wait = { tsk, NULL };
 
-	tsk->state = TASK_INTERRUPTIBLE;
-	add_wait_queue(sk->sleep, &wait);
-	release_sock(sk);
+	while((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) {
+		if(sk->err)
+			return sock_error(sk);
+		if((1 << sk->state) &
+		   ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
+			if(sk->keepopen)
+				send_sig(SIGPIPE, tsk, 0);
+			return -EPIPE;
+		}
+		if(flags & MSG_DONTWAIT)
+			return -EAGAIN;
+		if(signal_pending(tsk))
+			return -ERESTARTSYS;
 
-	if (((1 << sk->state) & ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT)) && sk->err == 0)
-		schedule();
+		tsk->state = TASK_INTERRUPTIBLE;
+		add_wait_queue(sk->sleep, &wait);
+		release_sock(sk);
 
-	tsk->state = TASK_RUNNING;
-	remove_wait_queue(sk->sleep, &wait);
-	lock_sock(sk);
+		if (((1 << sk->state) & ~(TCPF_ESTABLISHED|TCPF_CLOSE_WAIT)) &&
+		    sk->err == 0)
+			schedule();
+
+		tsk->state = TASK_RUNNING;
+		remove_wait_queue(sk->sleep, &wait);
+		lock_sock(sk);
+	}
+	return 0;
 }
 
 static inline int tcp_memory_free(struct sock *sk)
@@ -720,32 +726,6 @@
 	lock_sock(sk);
 }
 
-
-static int tcp_append_tail(struct sock *sk, struct sk_buff *skb, u8 *from,
-			   int tcp_size, int seglen)
-{
-	int fault;
-	int copy;
-
-	/* Add more stuff to the end of the skb. */
-	copy = min(sk->mss - tcp_size, skb_tailroom(skb));
-	copy = min(copy, seglen);
-
-	tcp_size += copy;
-
-	fault = copy_from_user(skb->tail, from, copy);
-	if (fault)
-		return -1;
-
-	skb_put(skb, copy);
-	skb->csum = csum_partial(skb->tail - tcp_size, tcp_size, 0);
-
-	sk->write_seq += copy;
-	skb->end_seq += copy;
-
-	return copy;
-}
-
 /*
  *	This routine copies from a user buffer into a socket,
  *	and starts the transmit system.
@@ -758,24 +738,9 @@
 	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
 
 	/* Wait for a connection to finish. */
-	while ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) {
-		if (sk->err)
-			return sock_error(sk);
-
-		if ((1 << sk->state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
-			if (sk->keepopen)
-				send_sig(SIGPIPE, current, 0);
-			return -EPIPE;
-		}
-		
-		if (flags&MSG_DONTWAIT)
-			return -EAGAIN;
-		
-		if (signal_pending(current))
-			return -ERESTARTSYS;
-		
-		wait_for_tcp_connect(sk);
-	}
+	if ((1 << sk->state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
+		if((err = wait_for_tcp_connect(sk, flags)) != 0)
+			return err;
 
 	/* Ok commence sending. */
 	while(--iovlen >= 0) {
@@ -785,41 +750,28 @@
 		iov++;
 
 		while(seglen > 0) {
-			unsigned int actual_win;
-			int copy;
-			int tmp;
+			int copy, tmp, queue_it;
 			struct sk_buff *skb;
 
 			if (err)
 				return -EFAULT;
 
 			/* Stop on errors. */
-			if (sk->err) {
-				if (copied) 
-					return copied;
-				return sock_error(sk);
-			}
+			if (sk->err)
+				goto do_sock_err;
 
 			/* Make sure that we are established. */
-			if (sk->shutdown & SEND_SHUTDOWN) {
-				if (copied)
-					return copied;
-				send_sig(SIGPIPE,current,0);
-				return -EPIPE;
-			}
+			if (sk->shutdown & SEND_SHUTDOWN)
+				goto do_shutdown;
 	
-			/* Now we need to check if we have a half built packet. */
-
-			/* If we have queued packets.. */
+			/* Now we need to check if we have a half
+			 * built packet we can tack some data onto.
+			 */
 			if (tp->send_head && !(flags & MSG_OOB)) {
-				int tcp_size;
-
-				/* Tail */
-
 				skb = sk->write_queue.prev;
-				tcp_size = skb->tail -
-					((unsigned char *)(skb->h.th) + tp->tcp_header_len);
-
+				copy = skb->tail -
+					((unsigned char *)(skb->h.th) +
+					 tp->tcp_header_len);
 				/* This window_seq test is somewhat dangerous
 				 * If the remote does SWS avoidance we should
 				 * queue the best we can if not we should in 
@@ -827,79 +779,92 @@
 				 * a method for detecting this would be most
 				 * welcome
 				 */
-				if (skb->end > skb->tail &&
-				    sk->mss - tcp_size > 0 &&
+				if (skb_tailroom(skb) > 0 &&
+				    (sk->mss - copy) > 0 &&
 				    tp->snd_nxt < skb->end_seq) {
-					int tcopy;
+					int last_byte_was_odd = (copy & 1);
 
-					tcopy = tcp_append_tail(sk, skb, from,
-							       tcp_size,
-							       seglen);
-					if (tcopy == -1)
-						return -EFAULT;
-
-					from += tcopy;
-					copied += tcopy;
-					seglen -= tcopy;
-
-					/*	FIXME: if we're nagling we
-					 *	should send here.
-					 */
+					copy = sk->mss - copy;
+					if(copy > skb_tailroom(skb))
+						copy = skb_tailroom(skb);
+					if(copy > seglen)
+						copy = seglen;
+					if(last_byte_was_odd) {
+						if(copy_from_user(skb_put(skb, copy),
+								  from, copy))
+							err = -EFAULT;
+						skb->csum = csum_partial(
+							(((unsigned char *)skb->h.th) +
+							 tp->tcp_header_len),
+							(skb->tail -
+							 (((unsigned char *)skb->h.th) +
+							  tp->tcp_header_len)), 0);
+					} else {
+						skb->csum =
+							csum_and_copy_from_user(
+							from, skb_put(skb, copy),
+							copy, skb->csum, &err);
+					}
+					tp->write_seq += copy;
+					skb->end_seq += copy;
+					from += copy;
+					copied += copy;
+					seglen -= copy;
 					continue;
 				}
 			}
 
-			/*   We also need to worry about the window.
-			 *   If window < 1/2 the maximum window we've seen from this
-			 *   host, don't use it.  This is sender side
-			 *   silly window prevention, as specified in RFC1122.
-			 *   (Note that this is different than earlier versions of
-			 *   SWS prevention, e.g. RFC813.).  What we actually do is
-			 *   use the whole MSS.  Since the results in the right
-			 *   edge of the packet being outside the window, it will
-			 *   be queued for later rather than sent.
+			/* We also need to worry about the window.  If
+			 * window < 1/2 the maximum window we've seen
+			 * from this host, don't use it.  This is
+			 * sender side silly window prevention, as
+			 * specified in RFC1122.  (Note that this is
+			 * different than earlier versions of SWS
+			 * prevention, e.g. RFC813.).  What we
+			 * actually do is use the whole MSS.  Since
+			 * the results in the right edge of the packet
+			 * being outside the window, it will be queued
+			 * for later rather than sent.
 			 */
-			copy = min(seglen, sk->mss);
-			actual_win = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
-
-			if (copy > actual_win &&
-			    (((int) actual_win) >= (tp->max_window >> 1)) &&
-			    actual_win)
-				copy = actual_win;
-
-			if (copy <= 0) {
-				printk(KERN_DEBUG "sendmsg: copy < 0\n");
-				return -EIO;
-			}
+			copy = tp->snd_wnd - (tp->snd_nxt - tp->snd_una);
+			if(copy >= (tp->max_window >> 1))
+				copy = min(copy, sk->mss);
+			else
+				copy = sk->mss;
+			if(copy > seglen)
+				copy = seglen;
 
-			/* If tp->packets_out > 0 segment will be nagled
-			 *  else we kick it right away.
-			 */
-			tmp = MAX_HEADER + sk->prot->max_header + 
+			tmp = MAX_HEADER + sk->prot->max_header +
 				sizeof(struct sk_buff) + 15;
-			if (copy < min(sk->mss, tp->max_window >> 1) && 
-			    !(flags & MSG_OOB) && tp->packets_out)
+			queue_it = 0;
+			if (copy < min(sk->mss, tp->max_window >> 1) &&
+			    !(flags & MSG_OOB)) {
 				tmp += min(sk->mss, tp->max_window);
-			else
-				tmp += copy;
 
+				/* What is happening here is that we want to
+				 * tack on later members of the users iovec
+				 * if possible into a single frame.  When we
+				 * leave this loop our caller checks to see if
+				 * we can send queued frames onto the wire.
+				 * See tcp_v[46]_sendmsg() for this.
+				 */
+				queue_it = 1;
+			} else {
+				tmp += copy;
+			}
 			skb = sock_wmalloc(sk, tmp, 0, GFP_KERNEL);
 
 			/* If we didn't get any memory, we need to sleep. */
 			if (skb == NULL) {
 				sk->socket->flags |= SO_NOSPACE;
 				if (flags&MSG_DONTWAIT) {
-					if (copied) 
-						return copied;
-					return -EAGAIN;
+					err = -EAGAIN;
+					goto do_interrupted;
 				}
-
 				if (signal_pending(current)) {
-					if (copied)
-						return copied;
-					return -ERESTARTSYS;
+					err = -ERESTARTSYS;
+					goto do_interrupted;
 				}
-
 				wait_for_tcp_memory(sk);
 				continue;
 			}
@@ -910,9 +875,8 @@
 			tmp = tp->af_specific->build_net_header(sk, skb);
 			if (tmp < 0) {
 				kfree_skb(skb);
-				if (copied)
-					return(copied);
-				return(tmp);
+				err = tmp;
+				goto do_interrupted;
 			}
 
 			skb->h.th =(struct tcphdr *)
@@ -920,7 +884,6 @@
 
 			seglen -= copy;
 			tcp_build_header_data(skb->h.th, sk, seglen || iovlen);
-			/* FIXME: still need to think about SACK options here. */
 
 			if (flags & MSG_OOB) {
 				skb->h.th->urg = 1;
@@ -933,21 +896,29 @@
 			from += copy;
 			copied += copy;
 
-			sk->write_seq += copy;
-
-			tcp_send_skb(sk, skb);
+			tp->write_seq += copy;
 
-			release_sock(sk);
-			lock_sock(sk);
+			tcp_send_skb(sk, skb, queue_it);
 		}
 	}
-
 	sk->err = 0;
-
 	if (err)
 		return -EFAULT;
-
 	return copied;
+
+do_sock_err:
+	if(copied)
+		return copied;
+	return sock_error(sk);
+do_shutdown:
+	if(copied)
+		return copied;
+	send_sig(SIGPIPE, current, 0);
+	return -EPIPE;
+do_interrupted:
+	if(copied)
+		return copied;
+	return err;
 }
 
 /*
@@ -980,7 +951,7 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 	/* No URG data to read. */
-	if (sk->urginline || !sk->urg_data || sk->urg_data == URG_READ)
+	if (sk->urginline || !tp->urg_data || tp->urg_data == URG_READ)
 		return -EINVAL;	/* Yes this is right ! */
 
 	if (sk->err)
@@ -1000,18 +971,10 @@
 	}
 
 	lock_sock(sk);
-	if (sk->urg_data & URG_VALID) {
-		char c = sk->urg_data;
+	if (tp->urg_data & URG_VALID) {
+		char c = tp->urg_data;
 		if (!(flags & MSG_PEEK))
-			sk->urg_data = URG_READ;
-			
-		if(len>0)
-		{
-			err = memcpy_toiovec(msg->msg_iov, &c, 1);
-			msg->msg_flags|=MSG_OOB;
-		}
-		else
-			msg->msg_flags|=MSG_TRUNC;
+			tp->urg_data = URG_READ;
 			
 		if(msg->msg_name)
 			tp->af_specific->addr2sockaddr(sk, (struct sockaddr *)
@@ -1023,6 +986,15 @@
 		/* Read urgent data. */
 		msg->msg_flags|=MSG_OOB;
 		release_sock(sk);
+
+		if(len>0)
+		{
+			err = memcpy_toiovec(msg->msg_iov, &c, 1);
+			msg->msg_flags|=MSG_OOB;
+		}
+		else
+			msg->msg_flags|=MSG_TRUNC;
+			
 		return err ? -EFAULT : 1;
 	}
 	release_sock(sk);
@@ -1044,45 +1016,37 @@
 
 static inline void tcp_eat_skb(struct sock *sk, struct sk_buff * skb)
 {
-	sk->tp_pinfo.af_tcp.delayed_acks++;
-
 	__skb_unlink(skb, &sk->receive_queue);
 	kfree_skb(skb);
 }
 
-
-static void cleanup_rbuf(struct sock *sk)
+/* Clean up the receive buffer for full frames taken by the user,
+ * then send an ACK if necessary.  COPIED is the number of bytes
+ * tcp_recvmsg has given to the user so far, it speeds up the
+ * calculation of whether or not we must ACK for the sake of
+ * a window update.
+ */
+static void cleanup_rbuf(struct sock *sk, int copied)
 {
 	struct sk_buff *skb;
-	struct tcp_opt *tp;
 	
 	/* NOTE! The socket must be locked, so that we don't get
 	 * a messed-up receive queue.
 	 */
 	while ((skb=skb_peek(&sk->receive_queue)) != NULL) {
-		if (!skb->used || atomic_read(&skb->users)>1)
+		if (!skb->used || atomic_read(&skb->users) > 1)
 			break;
 		tcp_eat_skb(sk, skb);
 	}
 
 	SOCK_DEBUG(sk, "sk->rspace = %lu\n", sock_rspace(sk));
 
-	tp = &(sk->tp_pinfo.af_tcp);
-
-  	/* We send a ACK if the sender is blocked
-  	 * else let tcp_data deal with the acking policy.
+  	/* We send an ACK if we can now advertise a non-zero window
+	 * which has been raised "significantly".
   	 */
-	if (tp->delayed_acks) {
-		__u32 rcv_wnd;
-
-	 	/* FIXME: double check this rule, then check against
-		 * other use of similar rules. Abtract if possible.
-		 */
-		rcv_wnd = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup);
-
-		if ((rcv_wnd < sk->mss) && (sock_rspace(sk) > rcv_wnd))
-			tcp_read_wakeup(sk);
-	}
+	if((copied > 0) &&
+	   (copied >= tcp_receive_window(&sk->tp_pinfo.af_tcp)))
+		tcp_read_wakeup(sk);
 }
 
 
@@ -1100,7 +1064,7 @@
 	volatile u32 *seq;	/* So gcc doesn't overoptimise */
 	unsigned long used;
 	int err = 0; 
-	int target = 1;		/* Read at least this may bytes */
+	int target = 1;		/* Read at least this many bytes */
 
 	if (sk->state == TCP_LISTEN)
 		return -ENOTCONN;
@@ -1113,8 +1077,8 @@
 	 *	the multi-reader case neatly (memcpy_to/fromfs might be
 	 *	inline and thus not flush cached variables otherwise).
 	 */
-	peek_seq = sk->copied_seq;
-	seq = &sk->copied_seq;
+	peek_seq = tp->copied_seq;
+	seq = &tp->copied_seq;
 	if (flags & MSG_PEEK)
 		seq = &peek_seq;
 		
@@ -1129,7 +1093,7 @@
 		u32 offset;
 
 		/* Are we at urgent data? Stop if we have read anything. */
-		if (copied && sk->urg_data && sk->urg_seq == *seq)
+		if (copied && tp->urg_data && tp->urg_seq == *seq)
 			break;
 
 		/* We need to check signals first, to get correct SIGURG
@@ -1200,7 +1164,7 @@
 			break;
 		}
 
-		cleanup_rbuf(sk);
+		cleanup_rbuf(sk, copied);
 		release_sock(sk);
 		sk->socket->flags |= SO_WAITDATA;
 		schedule();
@@ -1222,8 +1186,8 @@
 			used = len;
 
 		/* Do we have urgent data here? */
-		if (sk->urg_data) {
-			u32 urg_offset = sk->urg_seq - *seq;
+		if (tp->urg_data) {
+			u32 urg_offset = tp->urg_seq - *seq;
 			if (urg_offset < used) {
 				if (!urg_offset) {
 					if (!sk->urginline) {
@@ -1264,8 +1228,8 @@
 		 */
 		atomic_dec(&skb->users);
 
-		if (after(sk->copied_seq,sk->urg_seq))
-			sk->urg_data = 0;
+		if (after(tp->copied_seq,tp->urg_seq))
+			tp->urg_data = 0;
 		if (used + offset < skb->len)
 			continue;
 
@@ -1303,7 +1267,7 @@
 	current->state = TASK_RUNNING;
 
 	/* Clean up data we have read: This will do ACK frames. */
-	cleanup_rbuf(sk);
+	cleanup_rbuf(sk, copied);
 	release_sock(sk);
 	return copied;
 }
@@ -1356,8 +1320,7 @@
 	 *	reset mistake.
 	 */
 	if(dead && ns==TCP_FIN_WAIT2) {
-		int timer_active=del_timer(&sk->timer);
-		if(timer_active)
+		if(sk->timer.prev && del_timer(&sk->timer))
 			add_timer(&sk->timer);
 		else
 			tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
@@ -1410,6 +1373,7 @@
 void tcp_close(struct sock *sk, unsigned long timeout)
 {
 	struct sk_buff *skb;
+	int data_was_unread = 0;
 
 	/* We need to grab some memory, and put together a FIN,
 	 * and then put it into the queue to be sent.
@@ -1421,7 +1385,6 @@
 		tcp_close_pending(sk);
 		release_sock(sk);
 		sk->dead = 1;
-		sk->prot->unhash(sk);
 		return;
 	}
 
@@ -1435,14 +1398,30 @@
 	 *  descriptor close, not protocol-sourced closes, because the
 	 *  reader process may not have drained the data yet!
 	 */
-	while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
+	while((skb=skb_dequeue(&sk->receive_queue))!=NULL) {
+		data_was_unread++;
 		kfree_skb(skb);
+	}
 
-	/*  Timeout is not the same thing - however the code likes
-	 *  to send both the same way (sigh).
+	/* As outlined in draft-ietf-tcpimpl-prob-03.txt, section
+	 * 3.10, we send a RST here because data was lost.  To
+	 * witness the awful effects of the old behavior of always
+	 * doing a FIN, run an older 2.1.x kernel or 2.0.x, start
+	 * a bulk GET in an FTP client, suspend the process, wait
+	 * for the client to advertise a zero window, then kill -9
+	 * the FTP client, wheee...  Note: timeout is always zero
+	 * in such a case.
 	 */
-	if (tcp_close_state(sk,1)==1)
+	if(data_was_unread != 0) {
+		/* Unread data was tossed, zap the connection. */
+		tcp_set_state(sk, TCP_CLOSE);
+		tcp_send_active_reset(sk);
+	} else if (tcp_close_state(sk,1)) {
+		/* We FIN if the application ate all the data before
+		 * zapping the connection.
+		 */
 		tcp_send_fin(sk);
+	}
 
 	if (timeout) {
 		struct task_struct *tsk = current;
@@ -1470,8 +1449,7 @@
 	 * we may need to set up a timer.
          */
 	if (sk->state==TCP_FIN_WAIT2) {
-		int timer_active=del_timer(&sk->timer);
-		if(timer_active)
+		if(sk->timer.prev && del_timer(&sk->timer))
 			add_timer(&sk->timer);
 		else
 			tcp_reset_msl_timer(sk, TIME_CLOSE, sysctl_tcp_fin_timeout);
@@ -1479,9 +1457,6 @@
 
 	sk->dead = 1;
 	release_sock(sk);
-
-	if(sk->state == TCP_CLOSE)
-		sk->prot->unhash(sk);
 }
 
 /*
@@ -1538,13 +1513,12 @@
 	    /* If this is a non blocking socket don't sleep */
 	    error = EAGAIN;
 	    if (flags & O_NONBLOCK)
-		goto out;
+			goto out;
 	    
 	    error = ERESTARTSYS;
 	    req = wait_for_connect(sk, &prev);
 	    if (!req) 
-		goto out;
-	    error = 0; 
+			goto out;
 	}
 
 	tcp_synq_unlink(tp, req, prev);
@@ -1647,9 +1621,23 @@
 __initfunc(void tcp_init(void))
 {
 	tcp_openreq_cachep = kmem_cache_create("tcp_open_request",
-					       sizeof(struct open_request),
+						   sizeof(struct open_request),
 					       0, SLAB_HWCACHE_ALIGN,
 					       NULL, NULL);
 	if(!tcp_openreq_cachep)
 		panic("tcp_init: Cannot alloc open_request cache.");
+
+	tcp_bucket_cachep = kmem_cache_create("tcp_bind_bucket",
+					      sizeof(struct tcp_bind_bucket),
+					      0, SLAB_HWCACHE_ALIGN,
+					      NULL, NULL);
+	if(!tcp_bucket_cachep)
+		panic("tcp_init: Cannot alloc tcp_bind_bucket cache.");
+
+	tcp_timewait_cachep = kmem_cache_create("tcp_tw_bucket",
+						sizeof(struct tcp_tw_bucket),
+						0, SLAB_HWCACHE_ALIGN,
+						NULL, NULL);
+	if(!tcp_timewait_cachep)
+		panic("tcp_init: Cannot alloc tcp_tw_bucket cache.");
 }
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.89/linux/net/ipv4/tcp_input.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv4/tcp_input.c	Mon Mar 16 10:36:01 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_input.c,v 1.66 1998/01/15 22:40:29 freitag Exp $
+ * Version:	$Id: tcp_input.c,v 1.84 1998/03/15 03:23:20 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -67,57 +67,54 @@
 
 extern int sysctl_tcp_fin_timeout;
 
+/* These are on by default so the code paths get tested.
+ * For the final 2.2 this may be undone at our discretion. -DaveM
+ */
+int sysctl_tcp_timestamps = 1;
+int sysctl_tcp_window_scaling = 1;
+
 int sysctl_tcp_cong_avoidance;
 int sysctl_tcp_hoe_retransmits;
-int sysctl_tcp_sack;
-int sysctl_tcp_tsack;
-int sysctl_tcp_timestamps;
-int sysctl_tcp_window_scaling;
 int sysctl_tcp_syncookies = SYNC_INIT; 
 int sysctl_tcp_stdurg;
+int sysctl_tcp_rfc1337;
 
 static tcp_sys_cong_ctl_t tcp_sys_cong_ctl_f = &tcp_cong_avoid_vanj;
 
-/*
- *	Called each time to estimate the delayed ack timeout. This is
- *	how it should be done so a fast link isnt impacted by ack delay.
- *
- *	I think we need a medium deviation here also...
- *	The estimated value is changing to fast
+/* There is something which you must keep in mind when you analyze the
+ * behavior of the tp->ato delayed ack timeout interval.  When a
+ * connection starts up, we want to ack as quickly as possible.  The
+ * problem is that "good" TCP's do slow start at the beginning of data
+ * transmission.  The means that until we send the first few ACK's the
+ * sender will sit on his end and only queue most of his data, because
+ * he can only send snd_cwnd unacked packets at any given time.  For
+ * each ACK we send, he increments snd_cwnd and transmits more of his
+ * queue.  -DaveM
  */
- 
 static void tcp_delack_estimator(struct tcp_opt *tp)
 {
-	int m;
-
-	/* Delayed ACK time estimator. */
-	
-	m = jiffies - tp->lrcvtime;
-
-	tp->lrcvtime = jiffies;
+	if(tp->ato == 0) {
+		tp->lrcvtime = jiffies;
 
-	if (m < 0)
-		return;
-
-	/* if the mesured value is bigger than
-	 * twice the round trip time ignore it.
-	 */
-	if ((m << 2) <= tp->srtt) {
-		m -= (tp->iat >> 3);
-		tp->iat += m;
-
-		if (m <0)
-			m = -m;
-
-		m -= (tp->iat_mdev >> 2);
-		tp->iat_mdev += m;
+		/* Help sender leave slow start quickly,
+		 * this sets our initial ato value.
+		 */
+		tcp_enter_quickack_mode(tp);
+	} else {
+		int m = jiffies - tp->lrcvtime;
 
-		tp->ato = (tp->iat >> 3) + (tp->iat_mdev >> 2);
+		tp->lrcvtime = jiffies;
+		if(m <= 0)
+			m = 1;
+		if(m > tp->rto)
+			tp->ato = tp->rto;
+		else
+			tp->ato = (tp->ato >> 1) + m;
 
-		if (tp->ato < HZ/50)
-			tp->ato = HZ/50;
-	} else
-		tp->ato = 0;
+		/* We are not in "quick ack" mode. */
+		if(tp->ato <= (HZ/100))
+			tp->ato = ((HZ/100)*2);
+	}
 }
 
 /* Called to compute a smoothed rtt estimate. The data fed to this
@@ -132,9 +129,9 @@
 
 static __inline__ void tcp_rtt_estimator(struct tcp_opt *tp, __u32 mrtt)
 {
-	long m;
-	/*
-	 *	The following amusing code comes from Jacobson's
+	long m = mrtt; /* RTT */
+
+	/*	The following amusing code comes from Jacobson's
 	 *	article in SIGCOMM '88.  Note that rtt and mdev
 	 *	are scaled versions of rtt and mean deviation.
 	 *	This is designed to be as fast as possible 
@@ -143,12 +140,9 @@
 	 *	On a 1990 paper the rto value is changed to:
 	 *	RTO = rtt + 4 * mdev
 	 */
-
-	m = mrtt;  /* RTT */
-
+	if(m == 0)
+		m = 1;
 	if (tp->srtt != 0) {
-		if(m<=0)
-			m=1;		/* IS THIS RIGHT FOR <0 ??? */
 		m -= (tp->srtt >> 3);	/* m is now error in rtt est */
 		tp->srtt += m;		/* rtt = 7/8 rtt + 1/8 new */
 		if (m < 0)
@@ -202,19 +196,17 @@
 	 */
 	if (!before(end_seq,tp->last_ack_sent)) {
 		tp->ts_recent = tp->rcv_tsval;
-		/* FIXME: need a corse timestamp. Days uptime
-		 * would be good.
-		 */
 		tp->ts_recent_stamp = jiffies;
 	}
 }
 
+#define PAWS_24DAYS	(HZ * 60 * 60 * 24 * 24)
+
 extern __inline__ int tcp_paws_discard(struct tcp_opt *tp)
 {
-	/* FIXME: must check that ts_recent is not
- 	 * more than 24 days old here. Yuck.
- 	 */
-	return ((s32)(tp->rcv_tsval-tp->ts_recent) < 0);
+	/* ts_recent must be younger than 24 days */
+	return (((jiffies - tp->ts_recent_stamp) >= PAWS_24DAYS) ||
+		((s32)(tp->rcv_tsval-tp->ts_recent) < 0));
 }
 
 
@@ -257,8 +249,6 @@
 
 	/* We want the right error as BSD sees it (and indeed as we do). */
 	switch (sk->state) {
-		case TCP_TIME_WAIT:
-			break;
 		case TCP_SYN_SENT:
 			sk->err = ECONNREFUSED;
 			break;
@@ -268,23 +258,8 @@
 		default:
 			sk->err = ECONNRESET;
 	};
-#ifdef CONFIG_TCP_RFC1337
-	/*
-	 *	Time wait assassination protection [RFC1337]
-	 *
-	 *	This is a good idea, but causes more sockets to take time to close.
-	 *
-	 *	Ian Heavens has since shown this is an inadequate fix for the protocol
-	 *	bug in question.
-	 */
-	if(sk->state!=TCP_TIME_WAIT) {
-		tcp_set_state(sk,TCP_CLOSE);
-		sk->shutdown = SHUTDOWN_MASK;
-	}
-#else	
 	tcp_set_state(sk,TCP_CLOSE);
 	sk->shutdown = SHUTDOWN_MASK;
-#endif	
 	if (!sk->dead) 
 		sk->state_change(sk);
 }
@@ -302,7 +277,6 @@
 	int length=(th->doff*4)-sizeof(struct tcphdr);
 
 	ptr = (unsigned char *)(th + 1);
-	tp->sacks = 0;
 	tp->saw_tstamp = 0;
 
 	while(length>0) {
@@ -336,10 +310,6 @@
 								tp->snd_wscale = *(__u8 *)ptr;
 							}
 						break;
-					case TCPOPT_SACK_PERM:
-	  					if(opsize==TCPOLEN_SACK_PERM && th->syn)
-							if (sysctl_tcp_sack && !no_fancy)
-								tp->sack_ok = 1;
 					case TCPOPT_TIMESTAMP:
 	  					if(opsize==TCPOLEN_TIMESTAMP) {
 							/* Cheaper to set again then to
@@ -353,18 +323,6 @@
 							}
 						}
 						break;
-					case TCPOPT_SACK:
-						if (no_fancy || !sysctl_tcp_sack) 
-							break; 
-						tp->sacks = (opsize-2)>>3;
-						if (tp->sacks<<3 == opsize-2) {
-							int i;
-							for (i = 0; i < tp->sacks; i++) {
-								tp->left_sack[i] = ntohl(((__u32 *)ptr)[2*i]);
-								tp->right_sack[i] = ntohl(((__u32 *)ptr)[2*i+1]);
-							}
-						} else
-							tp->sacks = 0;
 	  			}
 	  			ptr+=opsize-2;
 	  			length-=opsize;
@@ -374,7 +332,7 @@
 
 /* Fast parse options. This hopes to only see timestamps.
  * If it is wrong it falls back on tcp_parse_option().
- * This should probably get extended for timestamps + SACK as well.
+ * This should probably get extended for timestamps as well.
  * Assembly code anyone? -- erics
  */
 static __inline__ int tcp_fast_parse_options(struct tcphdr *th, struct tcp_opt *tp)
@@ -384,14 +342,12 @@
 		return 0;
 	if (th->doff == sizeof(struct tcphdr)>>2) {
 		tp->saw_tstamp = 0;
-		tp->sacks = 0;
 		return 0;
-	} else if (th->doff == (sizeof(struct tcphdr)>>2)+3) {
+	} else if (th->doff == (sizeof(struct tcphdr)>>2)+(TCPOLEN_TSTAMP_ALIGNED>>2)) {
 		__u32 *ptr = (__u32 *)(th + 1);
-		if (*ptr == htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
-				  | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
+		if (*ptr == __constant_ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
+					     | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
 			tp->saw_tstamp = 1;
-			tp->sacks = 0;
 			tp->rcv_tsval = ntohl(*++ptr);
 			tp->rcv_tsecr = ntohl(*++ptr);
 			return 1;
@@ -401,89 +357,6 @@
 	return 1;
 }
 
-#if 0
-
-/*
- * This is the old fast retransmit code. It will go away eventually. -- erics
- */
-
-/* 
- *  See draft-stevens-tcpca-spec-01 for documentation.
- */
-
-static void tcp_fast_retrans(struct sock *sk, u32 ack, int not_dup)
-{
-	struct tcp_opt *tp=&(sk->tp_pinfo.af_tcp);
-
-	/* FIXME: if we are already retransmitting should this code
-	 * be skipped? [Floyd high_seq check sort of does this]
-	 * The case I'm worried about is falling into a fast
-	 * retransmit on a link with a congestion window of 1 or 2.
-	 * There was some evidence in 2.0.x that this was problem
-	 * on really slow links (1200 or 2400 baud). I need to
-	 * try this situation again and see what happens.
-	 */
-
-	/*
-	 * An ACK is a duplicate if:
-	 * (1) it has the same sequence number as the largest number we've 
-	 *     seen,
-	 * (2) it has the same window as the last ACK,
-	 * (3) we have outstanding data that has not been ACKed
-	 * (4) The packet was not carrying any data.
-	 * (5) [From Floyds paper on fast retransmit wars]
-	 *     The packet acked data after high_seq;
-	 */
-
-	if (ack == tp->snd_una && tp->packets_out && (not_dup == 0)) {
-		/* 1. When the third duplicate ack is received, set ssthresh 
-		 *    to one half the current congestion window, but no less 
-		 *    than two segments. Retransmit the missing segment.
-		 */
-		if (tp->high_seq == 0 || after(ack, tp->high_seq)) {
-			tp->dup_acks++;
-
-			if (tp->dup_acks == 3) {
-				tp->snd_ssthresh = max(tp->snd_cwnd >> 1, 2);
-				tp->snd_cwnd = tp->snd_ssthresh + 3;
-				tcp_do_retransmit(sk, 0);
-
-				/* Careful not to timeout just after fast
-				 * retransmit!
-				 */
-				tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
-			}
-		}
-
-		/* 2. Each time another duplicate ACK arrives, increment 
-		 *    cwnd by the segment size. [...] Transmit a packet...
-		 *
-		 *    Packet transmission will be done on normal flow processing
-		 *    since we're not in "retransmit mode".
-		 */
-		if (tp->dup_acks >= 3) {
-			tp->dup_acks++;
-			tp->snd_cwnd++;
-		}
-	} else {
-		/* 3. When the next ACK arrives that acknowledges new data,
-		 *    set cwnd to ssthresh.
-		 */
-		if (tp->dup_acks >= 3) {
-			tp->retrans_head = NULL;
-			tp->snd_cwnd = max(tp->snd_ssthresh, 1);
-			tp->retransmits = 0;
-		}
-		tp->dup_acks = 0;
-
-		/* FIXME: This is wrong if the new ack that arrives
-		 * is below the value for high_seq.
-		 */
-		tp->high_seq = 0;
-	}
-}
-#endif
-
 #define FLAG_DATA		0x01
 #define FLAG_WIN_UPDATE		0x02
 #define FLAG_DATA_ACKED		0x04
@@ -579,9 +452,8 @@
 				 * not indicate a packet left the system.
 				 * We can test this by just checking
 				 * if ack changed from snd_una, since
-				 * the only way to get here without changing
-				 * advancing from snd_una is if this was a
-				 * window update.
+				 * the only way to get here without advancing
+				 * from snd_una is if this was a window update.
 				 */
 				if (ack != tp->snd_una && before(ack,tp->high_seq)) {
                                 	tcp_do_retransmit(sk, 0);
@@ -596,9 +468,6 @@
 				clear_fast_retransmit(sk);
 			}
 		}
-	} else {
-		/* Clear any aborted fast retransmit starts. */
-		tp->dup_acks = 0;
 	}
 }
 
@@ -649,7 +518,6 @@
 
 	expected = (tp->snd_nxt - tp->snd_una) * inv_basertt;
 
-	/* XXX sk->mss should move into tcp_opt as well -DaveM */
 	inv_basebd = sk->mss * inv_basertt;
 
 	/* Slow Start */
@@ -731,13 +599,6 @@
 	int acked = 0;
 
 	while((skb=skb_peek(&sk->write_queue)) && (skb != tp->send_head)) {
-#ifdef TCP_DEBUG
-		/* Check for a bug. */
-		if (skb->next != (struct sk_buff*) &sk->write_queue &&
-		    after(skb->end_seq, skb->next->seq))
-			printk(KERN_DEBUG "INET: tcp_input.c: *** "
-			       "bug send_list out of order.\n");
-#endif								
 		/* If our packet is before the ack sequence we can
 		 * discard it as it's confirmed to have arrived the 
 		 * other end.
@@ -745,12 +606,15 @@
 		if (after(skb->end_seq, ack))
 			break;
 
-#if 0
-		SOCK_DEBUG(sk, "removing seg %x-%x from retransmit queue\n",
-			   skb->seq, skb->end_seq);
-#endif
-
-		acked = FLAG_DATA_ACKED;
+		/* Initial outgoing SYN's get put onto the write_queue
+		 * just like anything else we transmit.  It is not
+		 * true data, and if we misinform our callers that
+		 * this ACK acks real data, we will erroneously exit
+		 * connection startup slow start one packet too
+		 * quickly.  This is severely frowned upon behavior.
+		 */
+		if(!skb->h.th->syn)
+			acked = FLAG_DATA_ACKED;
 		
 		/* FIXME: packet counting may break if we have to
 		 * do packet "repackaging" for stacks that don't
@@ -766,11 +630,9 @@
 		kfree_skb(skb);
 	}
 
-	if (acked) {
+	if (acked)
 		tp->retrans_head = NULL;
-		if (!sk->dead)
-			sk->write_space(sk);
-	}
+
 	return acked;
 }
 
@@ -795,6 +657,66 @@
 	}
 }
  
+/* Read draft-ietf-tcplw-high-performance before mucking
+ * with this code. (Superceeds RFC1323)
+ */
+static void tcp_ack_saw_tstamp(struct sock *sk, struct tcp_opt *tp,
+			       u32 seq, u32 ack, int flag)
+{
+	__u32 seq_rtt = (jiffies-tp->rcv_tsecr);
+	tcp_rtt_estimator(tp, seq_rtt);
+	if (tp->retransmits) {
+		if (tp->packets_out == 0) {
+			tp->retransmits = 0;
+			tp->backoff = 0;
+			tcp_set_rto(tp);
+		} else {
+			/* Still retransmitting, use backoff */
+			tcp_set_rto(tp);
+			tp->rto = tp->rto << tp->backoff;
+		}
+	} else {
+		tcp_set_rto(tp);
+		if (flag & FLAG_DATA_ACKED)
+			(*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt);
+	}
+	/* NOTE: safe here so long as cong_ctl doesn't use rto */
+	tcp_bound_rto(tp);
+}
+
+static void tcp_ack_packets_out(struct sock *sk, struct tcp_opt *tp)
+{
+	struct sk_buff *skb;
+	long when;
+
+	skb = skb_peek(&sk->write_queue);
+	when = tp->rto - (jiffies - skb->when);
+
+	/* FIXME: This assumes that when we are retransmitting
+	 * we should only ever respond with one packet.
+	 * This means congestion windows should not grow
+	 * during recovery. In 2.0.X we allow the congestion
+	 * window to grow. It is not clear to me which
+	 * decision is correct. The RFCs should be double
+	 * checked as should the behavior of other stacks.
+	 * Also note that if we do want to allow the
+	 * congestion window to grow during retransmits
+	 * we have to fix the call to congestion window
+	 * updates so that it works during retransmission.
+	 */
+	if (tp->retransmits) {
+		tp->retrans_head = NULL;
+
+		/* This is tricky. We are retransmiting a 
+		 * segment of a window when congestion occured.
+		 */
+		tcp_do_retransmit(sk, 0);
+		tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+	} else {
+		tcp_reset_xmit_timer(sk, TIME_RETRANS, when);
+	}
+}
+
 /*
  *	This routine deals with incoming acks, but not outgoing ones.
  */
@@ -806,7 +728,6 @@
 	int flag = 0;
 	u32 seq = 0;
 	u32 seq_rtt = 0;
-	struct sk_buff *skb;
 
 	if(sk->zapped)
 		return(1);	/* Dead, can't ack any more so why bother */
@@ -838,7 +759,7 @@
 	 */
 	if (before(tp->snd_wl1, ack_seq) ||
 	    (tp->snd_wl1 == ack_seq && !after(tp->snd_wl2, ack))) {
-		unsigned long nwin = ntohs(th->window) << tp->snd_wscale;
+		u32 nwin = ntohs(th->window) << tp->snd_wscale;
 
 		if ((tp->snd_wl2 != ack) || (nwin > tp->snd_wnd)) {
 			flag |= FLAG_WIN_UPDATE;
@@ -869,28 +790,7 @@
 
 	/* If we have a timestamp, we always do rtt estimates. */
 	if (tp->saw_tstamp) {
-		/* Read draft-ietf-tcplw-high-performance before mucking
-		 * with this code. (Superceeds RFC1323)
-		 */
-		seq_rtt = (jiffies-tp->rcv_tsecr);
-		tcp_rtt_estimator(tp, seq_rtt);
-		if (tp->retransmits) {
-			if (tp->packets_out == 0) {
-				tp->retransmits = 0;
-				tp->backoff = 0;
-				tcp_set_rto(tp);
-			} else {
-				/* Still retransmitting, use backoff */
-				tcp_set_rto(tp);
-				tp->rto = tp->rto << tp->backoff;
-			}
-		} else {
-			tcp_set_rto(tp);
-			if (flag & FLAG_DATA_ACKED)
-				(*tcp_sys_cong_ctl_f)(sk, seq, ack, seq_rtt);
-		}
-		/* NOTE: safe here so long as cong_ctl doesn't use rto */
-		tcp_bound_rto(tp);
+		tcp_ack_saw_tstamp(sk, tp, seq, ack, flag);
 	} else {
 		/* If we were retransmiting don't count rtt estimate. */
 		if (tp->retransmits) {
@@ -916,51 +816,217 @@
 	}
 
 	if (tp->packets_out) {
-		if (flag & FLAG_DATA_ACKED) {
-			long when;
-
-			skb = skb_peek(&sk->write_queue);
-			when = tp->rto - (jiffies - skb->when);
-
-			/* FIXME: This assumes that when we are retransmitting
-			 * we should only ever respond with one packet.
-			 * This means congestion windows should not grow
-			 * during recovery. In 2.0.X we allow the congestion
-			 * window to grow. It is not clear to me which
-			 * decision is correct. The RFCs should be double
-			 * checked as should the behavior of other stacks.
-			 * Also note that if we do want to allow the
-			 * congestion window to grow during retransmits
-			 * we have to fix the call to congestion window
-			 * updates so that it works during retransmission.
-			 */
-			if (tp->retransmits) {
-				tp->retrans_head = NULL;
-
-				/* This is tricky. We are retransmiting a 
-				 * segment of a window when congestion occured.
-				 */
-				tcp_do_retransmit(sk, 0);
-				tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
-			} else
-				tcp_reset_xmit_timer(sk, TIME_RETRANS, when);
-		}
-	} else
+		if (flag & FLAG_DATA_ACKED)
+			tcp_ack_packets_out(sk, tp);
+	} else {
 		tcp_clear_xmit_timer(sk, TIME_RETRANS);
+	}
 
-	tcp_fast_retrans(sk, ack, (flag & (FLAG_DATA|FLAG_WIN_UPDATE)));
-
+	flag &= (FLAG_DATA | FLAG_WIN_UPDATE);
+	if ((ack == tp->snd_una	&& tp->packets_out && flag == 0) ||
+	    (tp->high_seq != 0)) {
+		tcp_fast_retrans(sk, ack, flag);
+	} else {
+		/* Clear any aborted fast retransmit starts. */
+		tp->dup_acks = 0;
+	}
 	/* Remember the highest ack received. */
 	tp->snd_una = ack;
-
 	return 1;
 
 uninteresting_ack:
-
 	SOCK_DEBUG(sk, "Ack ignored %u %u\n", ack, tp->snd_nxt);
 	return 0;
 }
 
+/* New-style handling of TIME_WAIT sockets. */
+static void tcp_timewait_kill(unsigned long __arg)
+{
+	struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)__arg;
+
+	/* Zap the timer. */
+	del_timer(&tw->timer);
+
+	/* Unlink from various places. */
+	if(tw->bind_next)
+		tw->bind_next->bind_pprev = tw->bind_pprev;
+	*(tw->bind_pprev) = tw->bind_next;
+	if(tw->tb->owners == NULL)
+		tcp_inc_slow_timer(TCP_SLT_BUCKETGC);
+
+	if(tw->next)
+		tw->next->pprev = tw->pprev;
+	*tw->pprev = tw->next;
+
+	/* We decremented the prot->inuse count when we entered TIME_WAIT
+	 * and the sock from which this came was destroyed.
+	 */
+	tw->sklist_next->sklist_prev = tw->sklist_prev;
+	tw->sklist_prev->sklist_next = tw->sklist_next;
+
+	/* Ok, now free it up. */
+	kmem_cache_free(tcp_timewait_cachep, tw);
+}
+
+/* We come here as a special case from the AF specific TCP input processing,
+ * and the SKB has no owner.  Essentially handling this is very simple,
+ * we just keep silently eating rx'd packets until none show up for the
+ * entire timeout period.  The only special cases are for BSD TIME_WAIT
+ * reconnects and SYN/RST bits being set in the TCP header.
+ */
+int tcp_timewait_state_process(struct tcp_tw_bucket *tw, struct sk_buff *skb,
+			       struct tcphdr *th, void *opt, __u16 len)
+{
+	/*	RFC 1122:
+	 *	"When a connection is [...] on TIME-WAIT state [...]
+	 *	[a TCP] MAY accept a new SYN from the remote TCP to
+	 *	reopen the connection directly, if it:
+	 *	
+	 *	(1)  assigns its initial sequence number for the new
+	 *	connection to be larger than the largest sequence
+	 *	number it used on the previous connection incarnation,
+	 *	and
+	 *
+	 *	(2)  returns to TIME-WAIT state if the SYN turns out 
+	 *	to be an old duplicate".
+	 */
+	if(th->syn && !th->rst && after(skb->seq, tw->rcv_nxt)) {
+		struct sock *sk;
+		struct tcp_func *af_specific = tw->af_specific;
+		__u32 isn;
+
+		isn = tw->rcv_nxt + 128000;
+		if(isn == 0)
+			isn++;
+		tcp_timewait_kill((unsigned long)tw);
+		sk = af_specific->get_sock(skb, th);
+		if(sk == NULL || !ipsec_sk_policy(sk,skb))
+			return 0;
+		skb_set_owner_r(skb, sk);
+		af_specific = sk->tp_pinfo.af_tcp.af_specific;
+		if(af_specific->conn_request(sk, skb, opt, isn) < 0)
+			return 1; /* Toss a reset back. */
+		return 0; /* Discard the frame. */
+	}
+
+	/* Check RST or SYN */
+	if(th->rst || th->syn) {
+		/* This is TIME_WAIT assasination, in two flavors.
+		 * Oh well... nobody has a sufficient solution to this
+		 * protocol bug yet.
+		 */
+		if(sysctl_tcp_rfc1337 == 0)
+			tcp_timewait_kill((unsigned long)tw);
+
+		if(!th->rst)
+			return 1; /* toss a reset back */
+	} else {
+		if(th->ack) {
+			/* In this case we must reset the TIMEWAIT timer. */
+			del_timer(&tw->timer);
+			tw->timer.expires = jiffies + TCP_TIMEWAIT_LEN;
+			add_timer(&tw->timer);
+		}
+	}
+	return 0; /* Discard the frame. */
+}
+
+/* Enter the time wait state.  This is always called from BH
+ * context.  Essentially we whip up a timewait bucket, copy the
+ * relevant info into it from the SK, and mess with hash chains
+ * and list linkage.
+ */
+static __inline__ void tcp_tw_hashdance(struct sock *sk, struct tcp_tw_bucket *tw)
+{
+	struct sock **head, *sktw;
+
+	/* Step 1: Remove SK from established hash. */
+	if(sk->next)
+		sk->next->pprev = sk->pprev;
+	*sk->pprev = sk->next;
+	sk->pprev = NULL;
+	tcp_reg_zap(sk);
+
+	/* Step 2: Put TW into bind hash where SK was. */
+	tw->tb = (struct tcp_bind_bucket *)sk->prev;
+	if((tw->bind_next = sk->bind_next) != NULL)
+		sk->bind_next->bind_pprev = &tw->bind_next;
+	tw->bind_pprev = sk->bind_pprev;
+	*sk->bind_pprev = (struct sock *)tw;
+
+	/* Step 3: Same for the protocol sklist. */
+	(tw->sklist_next = sk->sklist_next)->sklist_prev = (struct sock *)tw;
+	(tw->sklist_prev = sk->sklist_prev)->sklist_next = (struct sock *)tw;
+	sk->sklist_next = NULL;
+	sk->prot->inuse--;
+
+	/* Step 4: Hash TW into TIMEWAIT half of established hash table. */
+	head = &tcp_established_hash[sk->hashent + (TCP_HTABLE_SIZE/2)];
+	sktw = (struct sock *)tw;
+	if((sktw->next = *head) != NULL)
+		(*head)->pprev = &sktw->next;
+	*head = sktw;
+	sktw->pprev = head;
+}
+
+void tcp_time_wait(struct sock *sk)
+{
+	struct tcp_tw_bucket *tw;
+
+	tw = kmem_cache_alloc(tcp_timewait_cachep, SLAB_ATOMIC);
+	if(tw != NULL) {
+		/* Give us an identity. */
+		tw->daddr	= sk->daddr;
+		tw->rcv_saddr	= sk->rcv_saddr;
+		tw->bound_dev_if= sk->bound_dev_if;
+		tw->num		= sk->num;
+		tw->state	= TCP_TIME_WAIT;
+		tw->family	= sk->family;
+		tw->source	= sk->dummy_th.source;
+		tw->dest	= sk->dummy_th.dest;
+		tw->rcv_nxt	= sk->tp_pinfo.af_tcp.rcv_nxt;
+		tw->af_specific	= sk->tp_pinfo.af_tcp.af_specific;
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		if(tw->family == AF_INET6) {
+			memcpy(&tw->v6_daddr,
+			       &sk->net_pinfo.af_inet6.daddr,
+			       sizeof(struct in6_addr));
+			memcpy(&tw->v6_rcv_saddr,
+			       &sk->net_pinfo.af_inet6.rcv_saddr,
+			       sizeof(struct in6_addr));
+		}
+#endif
+		/* Linkage updates. */
+		tcp_tw_hashdance(sk, tw);
+
+		/* Get the TIME_WAIT timeout firing. */
+		init_timer(&tw->timer);
+		tw->timer.function = tcp_timewait_kill;
+		tw->timer.data = (unsigned long) tw;
+		tw->timer.expires = jiffies + TCP_TIMEWAIT_LEN;
+		add_timer(&tw->timer);
+
+		/* CLOSE the SK. */
+		if(sk->state == TCP_ESTABLISHED)
+			tcp_statistics.TcpCurrEstab--;
+		sk->state = TCP_CLOSE;
+		net_reset_timer(sk, TIME_DONE,
+				min(sk->tp_pinfo.af_tcp.srtt * 2, TCP_DONE_TIME));
+	} else {
+		/* Sorry, we're out of memory, just CLOSE this
+		 * socket up.  We've got bigger problems than
+		 * non-graceful socket closings.
+		 */
+		tcp_set_state(sk, TCP_CLOSE);
+	}
+
+	/* Prevent rcvmsg/sndmsg calls, and wake people up. */
+	sk->shutdown = SHUTDOWN_MASK;
+	if(!sk->dead)
+		sk->state_change(sk);
+}
+
 /*
  * 	Process the FIN bit. This now behaves as it is supposed to work
  *	and the FIN takes effect when it is validly part of sequence
@@ -976,17 +1042,9 @@
  *	If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT.
  */
  
-static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
+static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
 {
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-
-	if(sk->state == TCP_SYN_SENT) {
-		/* RFC793 says to drop the segment and return. */
-		return 1;
-	}
-
-	/* XXX This fin_seq thing should disappear... -DaveM */
-	tp->fin_seq = skb->end_seq;
+	sk->tp_pinfo.af_tcp.fin_seq = skb->end_seq;
 
 	tcp_send_ack(sk);
 
@@ -1013,12 +1071,6 @@
 		case TCP_LAST_ACK:
 			/* RFC793: Remain in the LAST-ACK state. */
 			break;
-		case TCP_TIME_WAIT:
-			/* Received a retransmission of the FIN,
-			 * restart the TIME_WAIT timer.
-			 */
-			tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-			break;
 
 		case TCP_FIN_WAIT1:
 			/* This case occurs when a simultaneous close
@@ -1035,21 +1087,15 @@
 			break;
 		case TCP_FIN_WAIT2:
 			/* Received a FIN -- send ACK and enter TIME_WAIT. */
-			tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-			sk->shutdown |= SHUTDOWN_MASK;
-			tcp_set_state(sk,TCP_TIME_WAIT);
-			break;
-		case TCP_CLOSE:
-			/* Already in CLOSE. */
+			tcp_time_wait(sk);
 			break;
 		default:
-			/* Only TCP_LISTEN is left, in that case we should never
-			 * reach this piece of code.
+			/* Only TCP_LISTEN and TCP_CLOSE are left, in these
+			 * cases we should never reach this piece of code.
 			 */
 			printk("tcp_fin: Impossible, sk->state=%d\n", sk->state);
 			break;
 	};
-	return 0;
 }
 
 /* This one checks to see if we can put data from the
@@ -1060,7 +1106,7 @@
 	struct sk_buff *skb;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	while ((skb = skb_peek(&sk->out_of_order_queue))) {
+	while ((skb = skb_peek(&tp->out_of_order_queue))) {
 		if (after(skb->seq, tp->rcv_nxt))
 			break;
 
@@ -1076,6 +1122,8 @@
 		skb_unlink(skb);
 		skb_queue_tail(&sk->receive_queue, skb);
 		tp->rcv_nxt = skb->end_seq;
+		if(skb->h.th->fin)
+			tcp_fin(skb, sk, skb->h.th);
 	}
 }
 
@@ -1094,8 +1142,12 @@
 		dst_confirm(sk->dst_cache);
 		skb_queue_tail(&sk->receive_queue, skb);
 		tp->rcv_nxt = skb->end_seq;
+		if(skb->h.th->fin)
+			tcp_fin(skb, sk, skb->h.th);
+		else
+			tp->delayed_acks++;
 		tcp_ofo_queue(sk);
-		if (skb_queue_len(&sk->out_of_order_queue) == 0)
+		if (skb_queue_len(&tp->out_of_order_queue) == 0)
 			tp->pred_flags = htonl((0x5010 << 16) | tp->snd_wnd);
 		return;
 	}
@@ -1104,8 +1156,7 @@
 	if (!after(skb->end_seq, tp->rcv_nxt)) {
 		/* A retransmit, 2nd most common case.  Force an imediate ack. */
 		SOCK_DEBUG(sk, "retransmit received: seq %X\n", skb->seq);
-
-		tp->delayed_acks = MAX_DELAY_ACK;
+		tcp_enter_quickack_mode(tp);
 		kfree_skb(skb);
 		return;
 	}
@@ -1119,7 +1170,8 @@
 	}
 
 	/* Ok. This is an out_of_order segment, force an ack. */
-	tp->delayed_acks = MAX_DELAY_ACK;
+	tp->delayed_acks++;
+	tcp_enter_quickack_mode(tp);
 
 	/* Disable header predition. */
 	tp->pred_flags = 0;
@@ -1127,10 +1179,10 @@
 	SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X\n",
 		   tp->rcv_nxt, skb->seq, skb->end_seq);
 
-	if (skb_peek(&sk->out_of_order_queue) == NULL) {
-		skb_queue_head(&sk->out_of_order_queue,skb);
+	if (skb_peek(&tp->out_of_order_queue) == NULL) {
+		skb_queue_head(&tp->out_of_order_queue,skb);
 	} else {
-		for(skb1=sk->out_of_order_queue.prev; ; skb1 = skb1->prev) {
+		for(skb1=tp->out_of_order_queue.prev; ; skb1 = skb1->prev) {
 			/* Already there. */
 			if (skb->seq == skb1->seq && skb->len >= skb1->len) {
  				skb_append(skb1, skb);
@@ -1145,8 +1197,8 @@
 			}
 
                         /* See if we've hit the start. If so insert. */
-			if (skb1 == skb_peek(&sk->out_of_order_queue)) {
-				skb_queue_head(&sk->out_of_order_queue,skb);
+			if (skb1 == skb_peek(&tp->out_of_order_queue)) {
+				skb_queue_head(&tp->out_of_order_queue,skb);
 				break;
 			}
 		}
@@ -1172,23 +1224,17 @@
         if (skb->len == 0 && !th->fin)
 		return(0);
 
-	/* FIXME: don't accept data after the received fin.
-	 *
-	 * Would checking snd_seq against fin_seq be enough?
-	 * If so, how do we handle that case exactly? -DaveM
-	 */
-
 	/* We no longer have anyone receiving data on this connection. */
 	tcp_data_queue(sk, skb);
 
-	if (before(tp->rcv_nxt, sk->copied_seq)) {
+	if (before(tp->rcv_nxt, tp->copied_seq)) {
 		printk(KERN_DEBUG "*** tcp.c:tcp_data bug acked < copied\n");
-		tp->rcv_nxt = sk->copied_seq;
+		tp->rcv_nxt = tp->copied_seq;
 	}
 
-	tp->delayed_acks++;
-
-	/* Now tell the user we may have some data. */
+	/* Above, tcp_data_queue() increments delayed_acks appropriately.
+	 * Now tell the user we may have some data.
+	 */
 	if (!sk->dead) {
 		SOCK_DEBUG(sk, "Data wakeup.\n");
 		sk->data_ready(sk,0);
@@ -1204,23 +1250,10 @@
 	if ((skb = tp->send_head)) {
 		if (!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
 		    tp->packets_out < tp->snd_cwnd ) {
-			/* Add more data to the send queue. */
-
-			/* FIXME: the congestion window is checked
-			 * again in tcp_write_xmit anyway?! -- erics
-			 *
-			 * I think it must, it bumps tp->packets_out for
-			 * each packet it fires onto the wire. -DaveM
-			 */
+			/* Put more data onto the wire. */
 			tcp_write_xmit(sk);
-			if(!sk->dead)
-				sk->write_space(sk);
 		} else if (tp->packets_out == 0 && !tp->pending) {
- 			/* Data to queue but no room. */
-
-			/* FIXME: Is it right to do a zero window probe into
-			 * a congestion window limited window??? -- erics
-			 */
+ 			/* Start probing the receivers window. */
  			tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
  		}
 	}
@@ -1240,12 +1273,24 @@
 	 *      - delay time <= 0.5 HZ
 	 *      - we don't have a window update to send
 	 *      - must send at least every 2 full sized packets
+	 *
+	 * With an extra heuristic to handle loss of packet
+	 * situations and also helping the sender leave slow
+	 * start in an expediant manner.
 	 */
 
-	if (tp->delayed_acks >= MAX_DELAY_ACK || tcp_raise_window(sk))
+	    /* Two full frames received or... */
+	if (((tp->rcv_nxt - tp->rcv_wup) >= (sk->mss << 1)) ||
+	    /* We will update the window "significantly" or... */
+	    tcp_raise_window(sk) ||
+	    /* We entered "quick ACK" mode */
+	    tcp_in_quickack_mode(tp)) {
+		/* Then ack it now */
 		tcp_send_ack(sk);
-	else
-		tcp_send_delayed_ack(sk, HZ/2);
+	} else {
+		/* Else, send delayed ack. */
+		tcp_send_delayed_ack(tp, HZ/2);
+	}
 }
 
 static __inline__ void tcp_ack_snd_check(struct sock *sk)
@@ -1279,11 +1324,11 @@
 	ptr += ntohl(th->seq);
 
 	/* Ignore urgent data that we've already seen and read. */
-	if (after(sk->copied_seq, ptr))
+	if (after(tp->copied_seq, ptr))
 		return;
 
 	/* Do we already have a newer (or duplicate) urgent pointer? */
-	if (sk->urg_data && !after(ptr, sk->urg_seq))
+	if (tp->urg_data && !after(ptr, tp->urg_seq))
 		return;
 
 	/* Tell the world about our new urgent pointer. */
@@ -1296,14 +1341,14 @@
 
 	/* We may be adding urgent data when the last byte read was
 	 * urgent. To do this requires some care. We cannot just ignore
-	 * sk->copied_seq since we would read the last urgent byte again
+	 * tp->copied_seq since we would read the last urgent byte again
 	 * as data, nor can we alter copied_seq until this data arrives
 	 * or we break the sematics of SIOCATMARK (and thus sockatmark())
 	 */
-	if (sk->urg_seq == sk->copied_seq)
-		sk->copied_seq++;	/* Move the copied sequence on correctly */
-	sk->urg_data = URG_NOTYET;
-	sk->urg_seq = ptr;
+	if (tp->urg_seq == tp->copied_seq)
+		tp->copied_seq++;	/* Move the copied sequence on correctly */
+	tp->urg_data = URG_NOTYET;
+	tp->urg_seq = ptr;
 
 	/* Disable header prediction. */
 	tp->pred_flags = 0;
@@ -1312,17 +1357,19 @@
 /* This is the 'fast' part of urgent handling. */
 static inline void tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long len)
 {
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+
 	/* Check if we get a new urgent pointer - normally not. */
 	if (th->urg)
 		tcp_check_urg(sk,th);
 
 	/* Do we wait for any urgent data? - normally not... */
-	if (sk->urg_data == URG_NOTYET) {
-		u32 ptr = sk->urg_seq - ntohl(th->seq) + (th->doff*4);
+	if (tp->urg_data == URG_NOTYET) {
+		u32 ptr = tp->urg_seq - ntohl(th->seq) + (th->doff*4);
 
 		/* Is the urgent pointer pointing into this packet? */	 
 		if (ptr < len) {
-			sk->urg_data = URG_VALID | *(ptr + (unsigned char *) th);
+			tp->urg_data = URG_VALID | *(ptr + (unsigned char *) th);
 			if (!sk->dead)
 				sk->data_ready(sk,0);
 		}
@@ -1335,33 +1382,39 @@
  */
 static void prune_queue(struct sock *sk)
 {
-	struct tcp_opt *tp; 
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp; 
 	struct sk_buff * skb;
 
-	SOCK_DEBUG(sk, "prune_queue: c=%x\n", sk->copied_seq);
+	SOCK_DEBUG(sk, "prune_queue: c=%x\n", tp->copied_seq);
 
 	/* First Clean the out_of_order queue. */
 	/* Start with the end because there are probably the least
 	 * useful packets (crossing fingers).
 	 */
-	while ((skb = skb_dequeue_tail(&sk->out_of_order_queue))) { 
+	while ((skb = skb_dequeue_tail(&tp->out_of_order_queue))) { 
 		kfree_skb(skb);
 		if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf)
 			return;
 	}
 	
-	tp = &sk->tp_pinfo.af_tcp;
-
 	/* Now continue with the receive queue if it wasn't enough */
 	while ((skb = skb_peek_tail(&sk->receive_queue))) {
+		/* Never toss anything when we've seen the FIN.
+		 * It's just too complex to recover from it.
+		 */
+		if(skb->h.th->fin)
+			break;
+
 		/* Never remove packets that have been already acked */
 		if (before(skb->end_seq, tp->last_ack_sent+1)) {
 			printk(KERN_DEBUG "prune_queue: hit acked data c=%x,%x,%x\n",
-				sk->copied_seq, skb->end_seq, tp->last_ack_sent);
+				tp->copied_seq, skb->end_seq, tp->last_ack_sent);
 			break; 
 		}
 		skb_unlink(skb);
 		tp->rcv_nxt = skb->seq;
+		SOCK_DEBUG(sk, "prune_queue: removing %x-%x (c=%x)\n",
+			   skb->seq, skb->end_seq, tp->copied_seq); 
 		kfree_skb(skb);
 		if (atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) 
 			break;
@@ -1429,7 +1482,6 @@
 			}
 		} else if (skb->ack_seq == tp->snd_una) {
 			/* Bulk data transfer: receiver */
-			
 			if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf) 
 				goto discard;
 			
@@ -1441,18 +1493,13 @@
 			skb_queue_tail(&sk->receive_queue, skb);
 			tp->rcv_nxt = skb->end_seq;
 
+			/* FIN bit check is not done since if FIN is set in
+			 * this frame, the pred_flags won't match up. -DaveM
+			 */
 			sk->data_ready(sk, 0);
 			tcp_delack_estimator(tp);
-
-#if 1			/* This checks for required window updates too. */
 			tp->delayed_acks++;
 			__tcp_ack_snd_check(sk);
-#else
-			if (tp->delayed_acks++ == 0)
-				tcp_send_delayed_ack(sk, HZ/2);
-			else
-				tcp_send_ack(sk);
-#endif
 			return 0;
 		}
 	}
@@ -1469,7 +1516,7 @@
 		}
 	}
 
-	if(th->syn && skb->seq != sk->syn_seq) {
+	if(th->syn && skb->seq != tp->syn_seq) {
 		SOCK_DEBUG(sk, "syn in established state\n");
 		tcp_statistics.TcpInErrs++;
 		tcp_reset(sk, skb);
@@ -1490,10 +1537,6 @@
 	/* step 7: process the segment text */
 	queued = tcp_data(skb, sk, len);
 
-	/* step 8: check the FIN bit */
-	if (th->fin)
-		(void) tcp_fin(skb, sk, th);
-
 	tcp_data_snd_check(sk);
 
 	/* If our receive queue has grown past its limits shrink it */
@@ -1657,19 +1700,19 @@
 			tp->snd_wnd = htons(th->window) << tp->snd_wscale;
 			tp->snd_wl1 = skb->seq;
 			tp->snd_wl2 = skb->ack_seq;
-
 			tp->fin_seq = skb->seq;
 
 			tcp_set_state(sk, TCP_ESTABLISHED);
 			tcp_parse_options(th,tp,0);
-			/* FIXME: need to make room for SACK still */
+
         		if (tp->wscale_ok == 0) {
                 		tp->snd_wscale = tp->rcv_wscale = 0;
                 		tp->window_clamp = min(tp->window_clamp,65535);
         		}
 			if (tp->tstamp_ok) {
-				tp->tcp_header_len = sizeof(struct tcphdr) + 12;	/* FIXME: Define constant! */
-				sk->dummy_th.doff += 3;		/* reserve space of options */
+				tp->tcp_header_len =
+					sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
+				sk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2);
 			} else
 				tp->tcp_header_len = sizeof(struct tcphdr);
 			if (tp->saw_tstamp) {
@@ -1680,14 +1723,30 @@
 			/* Can't be earlier, doff would be wrong. */
 			tcp_send_ack(sk);
 
-			if (tp->in_mss)
-				sk->mss = min(sk->mss, tp->in_mss);
-
-			/* Take out space for tcp options. */
-			sk->mss -= tp->tcp_header_len - sizeof(struct tcphdr);
+			/* Check for the case where we tried to advertise
+			 * a window including timestamp options, but did not
+			 * end up using them for this connection.
+			 */
+			if((tp->tstamp_ok == 0) && sysctl_tcp_timestamps)
+				sk->mss += TCPOLEN_TSTAMP_ALIGNED;
 			
+			/* Now limit it if the other end negotiated a smaller
+			 * value.
+			 */
+			if (tp->in_mss) {
+				int real_mss = tp->in_mss;
+
+				/* We store MSS locally with the timestamp bytes
+				 * subtracted, TCP's advertise it with them
+				 * included.  Account for this fact.
+				 */
+				if(tp->tstamp_ok)
+					real_mss -= TCPOLEN_TSTAMP_ALIGNED;
+				sk->mss = min(sk->mss, real_mss);
+			}
+
 			sk->dummy_th.dest = th->source;
-			sk->copied_seq = tp->rcv_nxt;
+			tp->copied_seq = tp->rcv_nxt;
 
 			if(!sk->dead) {
 				sk->state_change(sk);
@@ -1722,52 +1781,10 @@
 
 		}
 		break;
-
-	case TCP_TIME_WAIT:
-	        /*	RFC 1122:
-		 *	"When a connection is [...] on TIME-WAIT state [...]
-		 *	[a TCP] MAY accept a new SYN from the remote TCP to
-		 *	reopen the connection directly, if it:
-		 *	
-		 *	(1)  assigns its initial sequence number for the new
-                 *	connection to be larger than the largest sequence
-                 *	number it used on the previous connection incarnation,
-                 *	and
-		 *
-		 *	(2)  returns to TIME-WAIT state if the SYN turns out 
-		 *	to be an old duplicate".
-		 */
-		if (th->syn && !th->rst && after(skb->seq, tp->rcv_nxt)) {
-			__u32 isn;
-
-			skb_orphan(skb);
-                        sk->err = ECONNRESET;
-                        tcp_set_state(sk, TCP_CLOSE);
-                        sk->shutdown = SHUTDOWN_MASK;
-
-			isn = tp->rcv_nxt + 128000;
-			if (isn == 0)  
-				isn++; 
-
-			sk = tp->af_specific->get_sock(skb, th);
-
-			if (sk == NULL || !ipsec_sk_policy(sk,skb))
-				goto discard;
-
-			skb_set_owner_r(skb, sk);
-			tp = &sk->tp_pinfo.af_tcp;
-
-			if(tp->af_specific->conn_request(sk, skb, opt, isn) < 0)
-				return 1;
-
-			goto discard;
-		}
-
-		break;
 	}
 
 	/*   Parse the tcp_options present on this header.
-	 *   By this point we really only expect timestamps and SACKs.
+	 *   By this point we really only expect timestamps.
 	 *   Note that this really has to be here and not later for PAWS
 	 *   (RFC1323) to work.
 	 */
@@ -1819,7 +1836,7 @@
 	 *	original syn. 
 	 */
 
-	if (th->syn && skb->seq!=sk->syn_seq) {
+	if (th->syn && skb->seq!=tp->syn_seq) {
 		tcp_reset(sk, skb);
 		return 1;
 	}
@@ -1833,7 +1850,7 @@
 			if (acceptable) {
 				tcp_set_state(sk, TCP_ESTABLISHED);
 				sk->dummy_th.dest=th->source;
-				sk->copied_seq = tp->rcv_nxt;
+				tp->copied_seq = tp->rcv_nxt;
 
 				if(!sk->dead)
 					sk->state_change(sk);		
@@ -1850,7 +1867,7 @@
 			break;
 
 		case TCP_FIN_WAIT1:
-			if (tp->snd_una == sk->write_seq) {
+			if (tp->snd_una == tp->write_seq) {
 				sk->shutdown |= SEND_SHUTDOWN;
 				tcp_set_state(sk, TCP_FIN_WAIT2);
 				if (!sk->dead)
@@ -1861,12 +1878,12 @@
 			break;
 
 		case TCP_CLOSING:	
-			if (tp->snd_una == sk->write_seq)
+			if (tp->snd_una == tp->write_seq)
 				tcp_time_wait(sk);
 			break;
 
 		case TCP_LAST_ACK:
-			if (tp->snd_una == sk->write_seq) {
+			if (tp->snd_una == tp->write_seq) {
 				sk->shutdown = SHUTDOWN_MASK;
 				tcp_set_state(sk,TCP_CLOSE);
 				if (!sk->dead)
@@ -1874,13 +1891,6 @@
 				goto discard;
 			}
 			break;
-
-		case TCP_TIME_WAIT:
-			/* Keep us in TIME_WAIT until we stop getting 
-			 * packets, reset the timeout.
-			 */
-			tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-			break;
 		}
 	} else
 		goto discard;
@@ -1916,12 +1926,6 @@
 		if (atomic_read(&sk->rmem_alloc) > sk->rcvbuf)
 			prune_queue(sk);
 		break;
-	}
-
-	/* step 8: check the FIN bit */
-	if (th->fin) {
-		if(tcp_fin(skb, sk, th) != 0)
-			goto discard;
 	}
 
 	tcp_data_snd_check(sk);
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.89/linux/net/ipv4/tcp_ipv4.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv4/tcp_ipv4.c	Mon Mar 16 10:36:01 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.79 1998/01/15 22:40:47 freitag Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.109 1998/03/15 07:24:15 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -60,8 +60,6 @@
 
 #include <linux/inet.h>
 
-extern int sysctl_tcp_sack;
-extern int sysctl_tcp_tsack;
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
 extern int sysctl_tcp_syncookies;
@@ -89,16 +87,19 @@
  */
 struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
 
+/* Ok, let's try this, I give up, we do need a local binding
+ * TCP hash as well as the others for fast bind/connect.
+ */
+struct tcp_bind_bucket *tcp_bound_hash[TCP_BHTABLE_SIZE];
+
 /* All sockets in TCP_LISTEN state will be in here.  This is the only table
  * where wildcard'd TCP sockets can exist.  Hash function here is just local
  * port number.
  */
 struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
 
-/* Ok, let's try this, I give up, we do need a local binding
- * TCP hash as well as the others for fast bind/connect.
- */
-struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE];
+/* Register cache. */
+struct sock *tcp_regs[TCP_NUM_REGS];
 
 /*
  * This array holds the first and last local port number.
@@ -106,6 +107,7 @@
  * 32768-61000
  */
 int sysctl_local_port_range[2] = { 1024, 4999 };
+int tcp_port_rover = (1024 - 1);
 
 static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
 				 __u32 faddr, __u16 fport)
@@ -123,155 +125,135 @@
 	return tcp_hashfn(laddr, lport, faddr, fport);
 }
 
-static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum)
+/* Invariant, sk->num is non-zero. */
+void tcp_bucket_unlock(struct sock *sk)
 {
-	struct sock *sk2;
-	int retval = 0, sk_reuse = sk->reuse;
+	struct tcp_bind_bucket *tb;
+	unsigned short snum = sk->num;
 
 	SOCKHASH_LOCK();
-	sk2 = tcp_bound_hash[tcp_bhashfn(snum)];
-	for(; sk2 != NULL; sk2 = sk2->bind_next) {
-		if((sk2->num == snum) && (sk2 != sk)) {
-			unsigned char state = sk2->state;
-			int sk2_reuse = sk2->reuse;
-
-			/* Two sockets can be bound to the same port if they're
-			 * bound to different interfaces.
-			 */
-
-			if(sk->bound_dev_if != sk2->bound_dev_if)
-				continue;
-
-			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
-				if((!sk2_reuse)			||
-				   (!sk_reuse)			||
-				   (state == TCP_LISTEN)) {
-					retval = 1;
-					break;
-				}
-			} else if(sk2->rcv_saddr == sk->rcv_saddr) {
-				if((!sk_reuse)			||
-				   (!sk2_reuse)			||
-				   (state == TCP_LISTEN)) {
-					retval = 1;
-					break;
-				}
+	for(tb = tcp_bound_hash[tcp_bhashfn(snum)]; tb; tb = tb->next) {
+		if(tb->port == snum) {
+			if(tb->owners == NULL &&
+			   (tb->flags & TCPB_FLAG_LOCKED)) {
+				tb->flags &= ~TCPB_FLAG_LOCKED;
+				tcp_inc_slow_timer(TCP_SLT_BUCKETGC);
 			}
+			break;
 		}
 	}
 	SOCKHASH_UNLOCK();
-
-	return retval;
 }
 
-static __inline__ int tcp_lport_inuse(int num)
+struct tcp_bind_bucket *tcp_bucket_create(unsigned short snum)
 {
-	struct sock *sk = tcp_bound_hash[tcp_bhashfn(num)];
+	struct tcp_bind_bucket *tb;
 
-	for(; sk != NULL; sk = sk->bind_next) {
-		if(sk->num == num)
-			return 1;
+	tb = kmem_cache_alloc(tcp_bucket_cachep, SLAB_ATOMIC);
+	if(tb != NULL) {
+		struct tcp_bind_bucket **head =
+			&tcp_bound_hash[tcp_bhashfn(snum)];
+		tb->port = snum;
+		tb->flags = TCPB_FLAG_LOCKED;
+		tb->owners = NULL;
+		if((tb->next = *head) != NULL)
+			tb->next->pprev = &tb->next;
+		*head = tb;
+		tb->pprev = head;
 	}
-	return 0;
+	return tb;
 }
 
-/* Find a "good" local port, this is family independent.
- * There are several strategies working in unison here to
- * get the best possible performance.  The current socket
- * load is kept track of, if it is zero there is a strong
- * likely hood that there is a zero length chain we will
- * find with a small amount of searching, else the load is
- * what we shoot for for when the chains all have at least
- * one entry.  The base helps us walk the chains in an
- * order such that a good chain is found as quickly as possible.  -DaveM
- */
-unsigned short tcp_good_socknum(void)
+static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum)
 {
-	static int start = 0;
-	static int binding_contour = 0;
-	int best = 0;
-	int size = 32767; /* a big num. */
-	int retval = 0, i, end, bc;
+	struct tcp_bind_bucket *tb;
+	int result = 0;
 
 	SOCKHASH_LOCK();
-        if (start > sysctl_local_port_range[1] || start < sysctl_local_port_range[0])
-		start = sysctl_local_port_range[0];
-        i = tcp_bhashfn(start);
-        end = i + TCP_BHTABLE_SIZE;
-        bc = binding_contour;
-        do {
-                struct sock *sk = tcp_bound_hash[i&(TCP_BHTABLE_SIZE-1)];
-                if(!sk) {
-                        /* find the smallest value no smaller than start
-                         * that has this hash value.
-                         */
-                        retval = tcp_bhashnext(start-1,i&(TCP_BHTABLE_SIZE-1));
-
-                        /* Check for decreasing load. */
-                        if (bc != 0)
-                                binding_contour = 0;
-                        goto done;
-                } else {
-                        int j = 0;
-                        do { sk = sk->bind_next; } while (++j < size && sk);
-                        if (j < size) {
-                                best = i&(TCP_BHTABLE_SIZE-1);
-                                size = j;
-                                if (bc && size <= bc)
-                                        goto verify;
-                        }
-                }
-        } while(++i != end);
-        i = best;
+	for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	    (tb && (tb->port != snum));
+	    tb = tb->next)
+		;
+	if(tb && tb->owners) {
+		/* Fast path for reuse ports, see include/net/tcp.h for a very
+		 * detailed description of why this works, and why it is worth
+		 * the effort at all. -DaveM
+		 */
+		if((tb->flags & TCPB_FLAG_FASTREUSE)	&&
+		   (sk->reuse != 0)) {
+			goto go_like_smoke;
+		} else {
+			struct sock *sk2;
+			int sk_reuse = sk->reuse;
 
-        /* Socket load is increasing, adjust our load average. */
-        binding_contour = size;
-verify:
-        if (size < binding_contour)
-                binding_contour = size;
-
-        retval = tcp_bhashnext(start-1,i);
-
-	best = retval;	/* mark the starting point to avoid infinite loops */
-        while(tcp_lport_inuse(retval)) {
-               	retval = tcp_bhashnext(retval,i);
-		if (retval > sysctl_local_port_range[1]) /* Upper bound */
-			retval = tcp_bhashnext(sysctl_local_port_range[0],i);
-		if (retval == best) {
-			/* This hash chain is full. No answer. */
-			retval = 0;
-			break;
+			/* We must walk the whole port owner list in this case. -DaveM */
+			for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) {
+				if(sk->bound_dev_if == sk2->bound_dev_if) {
+					if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) {
+						if(!sk2->rcv_saddr		||
+						   !sk->rcv_saddr		||
+						   (sk2->rcv_saddr == sk->rcv_saddr))
+							break;
+					}
+				}
+			}
+			if(sk2 != NULL)
+				result = 1;
 		}
-        }
+	}
+	if((result == 0) &&
+	   (tb == NULL) &&
+	   (tcp_bucket_create(snum) == NULL))
+		result = 1;
+go_like_smoke:
+	SOCKHASH_UNLOCK();
+	return result;
+}
 
-done:
-        start = (retval + 1);
+unsigned short tcp_good_socknum(void)
+{
+	struct tcp_bind_bucket *tb;
+	int low = sysctl_local_port_range[0];
+	int high = sysctl_local_port_range[1];
+	int remaining = high - low;
+	int rover;
+
+	SOCKHASH_LOCK();
+	rover = tcp_port_rover;
+	do {
+		rover += 1;
+		if((rover < low) || (rover > high))
+			rover = low;
+		tb = tcp_bound_hash[tcp_bhashfn(rover)];
+		for( ; tb; tb = tb->next) {
+			if(tb->port == rover)
+				goto next;
+		}
+		break;
+	next:
+	} while(--remaining > 0);
+	tcp_port_rover = rover;
+	if((remaining <= 0) || (tcp_bucket_create(rover) == NULL))
+		rover = 0;
 	SOCKHASH_UNLOCK();
 
-	return retval;
+	return rover;
 }
 
 static void tcp_v4_hash(struct sock *sk)
 {
-	unsigned char state;
-
-	SOCKHASH_LOCK();
-	state = sk->state;
-	if(state != TCP_CLOSE || !sk->dead) {
+	if (sk->state != TCP_CLOSE) {
 		struct sock **skp;
 
-		if(state == TCP_LISTEN)
-			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
-		else
-			skp = &tcp_established_hash[tcp_sk_hashfn(sk)];
-
+		SOCKHASH_LOCK();
+		skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))];
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
 		*skp = sk;
 		sk->pprev = skp;
 		tcp_sk_bindify(sk);
+		SOCKHASH_UNLOCK();
 	}
-	SOCKHASH_UNLOCK();
 }
 
 static void tcp_v4_unhash(struct sock *sk)
@@ -282,6 +264,7 @@
 			sk->next->pprev = sk->pprev;
 		*sk->pprev = sk->next;
 		sk->pprev = NULL;
+		tcp_reg_zap(sk);
 		tcp_sk_unbindify(sk);
 	}
 	SOCKHASH_UNLOCK();
@@ -293,30 +276,27 @@
 
 	SOCKHASH_LOCK();
 	state = sk->state;
-	if(sk->pprev) {
+	if(sk->pprev != NULL) {
 		if(sk->next)
 			sk->next->pprev = sk->pprev;
 		*sk->pprev = sk->next;
 		sk->pprev = NULL;
-		tcp_sk_unbindify(sk);
+		tcp_reg_zap(sk);
 	}
-	if(state != TCP_CLOSE || !sk->dead) {
+	if(state != TCP_CLOSE) {
 		struct sock **skp;
 
-		if(state == TCP_LISTEN) {
+		if(state == TCP_LISTEN)
 			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
-		} else {
-			int hash= tcp_sk_hashfn(sk);
-			if(state == TCP_TIME_WAIT)
-				hash += (TCP_HTABLE_SIZE/2);
-			skp = &tcp_established_hash[hash];
-		}
+		else
+			skp = &tcp_established_hash[(sk->hashent = tcp_sk_hashfn(sk))];
 
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
 		*skp = sk;
 		sk->pprev = skp;
-		tcp_sk_bindify(sk);
+		if(state == TCP_LISTEN)
+			tcp_sk_bindify(sk);
 	}
 	SOCKHASH_UNLOCK();
 }
@@ -360,37 +340,64 @@
 	return result;
 }
 
+/* Until this is verified... -DaveM */
+/* #define USE_QUICKSYNS */
+
 /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
  * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ * It is assumed that this code only gets called from within NET_BH.
  */
 static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
-					   u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)
+					   u32 saddr, u16 sport,
+					   u32 daddr, u16 dport, int dif)
 {
 	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
-	int hash = tcp_hashfn(daddr, hnum, saddr, sport);
+	int hash;
+
+#ifdef USE_QUICKSYNS
+	/* Incomming connection short-cut. */
+	if (th && th->syn == 1 && th->ack == 0)
+		goto listener_shortcut;
+#endif
+
+	/* Check TCP register quick cache first. */
+	sk = TCP_RHASH(sport);
+	if(sk						&&
+	   sk->daddr		== saddr		&& /* remote address */
+	   sk->dummy_th.dest	== sport		&& /* remote port    */
+	   sk->num		== hnum			&& /* local port     */
+	   sk->rcv_saddr	== daddr		&& /* local address  */
+	   (!sk->bound_dev_if || sk->bound_dev_if == dif))
+		goto hit;
 
 	/* Optimize here for direct hit, only listening connections can
-	 * have wildcards anyways.  It is assumed that this code only
-	 * gets called from within NET_BH.
+	 * have wildcards anyways.
 	 */
-	for(sk = tcp_established_hash[hash]; sk; sk = sk->next)
+	hash = tcp_hashfn(daddr, hnum, saddr, sport);
+	for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
 		if(sk->daddr		== saddr		&& /* remote address */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
 		   sk->rcv_saddr	== daddr		&& /* local address  */
-		   (!sk->bound_dev_if || sk->bound_dev_if == dif))
+		   (!sk->bound_dev_if || sk->bound_dev_if == dif)) {
+			if (sk->state == TCP_ESTABLISHED)
+				TCP_RHASH(sport) = sk;
 			goto hit; /* You sunk my battleship! */
-
+		}
+	}
 	/* Must check for a TIME_WAIT'er before going to listener hash. */
-	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
+	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next) {
 		if(sk->daddr		== saddr		&& /* remote address */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
 		   sk->rcv_saddr	== daddr		&& /* local address  */
 		   (!sk->bound_dev_if || sk->bound_dev_if == dif))
 			goto hit;
-
+	}
+#ifdef USE_QUICKSYNS
+listener_shortcut:
+#endif
 	sk = tcp_v4_lookup_listener(daddr, hnum, dif);
 hit:
 	return sk;
@@ -402,20 +409,11 @@
 }
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
-#define secondlist(hpnum, sk, fpass) \
-({ struct sock *s1; if(!(sk) && (fpass)--) \
-	s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \
-   else \
-	s1 = (sk); \
-   s1; \
-})
-
-#define tcp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
-	secondlist((hpnum), tcp_bound_hash[tcp_bhashfn(hnum)],(fpass))
-
-#define tcp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
-	secondlist((hpnum),(sk)->bind_next,(fpass))
-
+/* Cleaned up a little and adapted to new bind bucket scheme.
+ * Oddly, this should increase performance here for
+ * transparent proxy, as tests within the inner loop have
+ * been eliminated. -DaveM
+ */
 static struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
 					unsigned short rnum, unsigned long laddr,
 					struct device *dev, unsigned short pnum,
@@ -436,51 +434,60 @@
 	}
 
 	/* This code must run only from NET_BH. */
-	for(s = tcp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
-	    s != NULL;
-	    s = tcp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
-		if(s->num == hnum || s->num == hpnum) {
-			int score = 0;
-			if(s->dead && (s->state == TCP_CLOSE))
+	{
+		struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hnum)];
+		for( ; (tb && tb->port != hnum); tb = tb->next)
+			;
+		if(tb == NULL)
+			goto next;
+		s = tb->owners;
+	}
+pass2:
+	for(; s; s = s->bind_next) {
+		int score = 0;
+		if(s->rcv_saddr) {
+			if((s->num != hpnum || s->rcv_saddr != paddr) &&
+			   (s->num != hnum || s->rcv_saddr != laddr))
 				continue;
-			if(s->rcv_saddr) {
-				if((s->num != hpnum || s->rcv_saddr != paddr) &&
-				   (s->num != hnum || s->rcv_saddr != laddr))
-					continue;
-				score++;
-			}
-			if(s->daddr) {
-				if(s->daddr != raddr)
-					continue;
-				score++;
-			}
-			if(s->dummy_th.dest) {
-				if(s->dummy_th.dest != rnum)
-					continue;
-				score++;
-			}
-			if(s->bound_dev_if) {
-				if(s->bound_dev_if != dif)
-					continue;
-				score++;
-			}
-			if(score == 4 && s->num == hnum) {
-				result = s;
-				break;
-			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
-					result = s;
-					badness = score;
-			}
+			score++;
+		}
+		if(s->daddr) {
+			if(s->daddr != raddr)
+				continue;
+			score++;
+		}
+		if(s->dummy_th.dest) {
+			if(s->dummy_th.dest != rnum)
+				continue;
+			score++;
+		}
+		if(s->bound_dev_if) {
+			if(s->bound_dev_if != dif)
+				continue;
+			score++;
+		}
+		if(score == 4 && s->num == hnum) {
+			result = s;
+			goto gotit;
+		} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+			result = s;
+			badness = score;
+		}
+	}
+next:
+	if(firstpass--) {
+		struct tcp_bind_bucket *tb = tcp_bound_hash[tcp_bhashfn(hpnum)];
+		for( ; (tb && tb->port != hpnum); tb = tb->next)
+			;
+		if(tb) {
+			s = tb->owners;
+			goto pass2;
 		}
 	}
+gotit:
 	return result;
 }
-
-#undef secondlist
-#undef tcp_v4_proxy_loop_init
-#undef tcp_v4_proxy_loop_next
-
-#endif
+#endif /* CONFIG_IP_TRANSPARENT_PROXY */
 
 static inline __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
 {
@@ -495,41 +502,35 @@
 
 /*
  * Check that a TCP address is unique, don't allow multiple
- * connects to/from the same address
+ * connects to/from the same address.  Actually we can optimize
+ * quite a bit, since the socket about to connect is still
+ * in TCP_CLOSE, a tcp_bind_bucket for the local port he will
+ * use will exist, with a NULL owners list.  So check for that.
+ * The good_socknum and verify_bind scheme we use makes this
+ * work.
  */
 
-static int tcp_unique_address(u32 saddr, u16 snum, u32 daddr, u16 dnum)
+static int tcp_unique_address(struct sock *sk)
 {
-	int retval = 1, hashent = tcp_hashfn(saddr, snum, daddr, dnum);
-	struct sock * sk;
+	struct tcp_bind_bucket *tb;
+	unsigned short snum = sk->num;
+	int retval = 1;
 
-	/* Make sure we are allowed to connect here.
-	 * But freeze the hash while we snoop around.
-	 */
+	/* Freeze the hash while we snoop around. */
 	SOCKHASH_LOCK();
-	sk = tcp_established_hash[hashent];
-	for (; sk != NULL; sk = sk->next) {
-		if(sk->daddr		== daddr		&& /* remote address */
-		   sk->dummy_th.dest	== dnum			&& /* remote port */
-		   sk->num		== snum			&& /* local port */
-		   sk->saddr		== saddr) {		   /* local address */
-			retval = 0;
-			goto out;
-		}
-	}
-
-	/* Must check TIME_WAIT'ers too. */
-	sk = tcp_established_hash[hashent + (TCP_HTABLE_SIZE/2)];
-	for (; sk != NULL; sk = sk->next) {
-		if(sk->daddr		== daddr		&& /* remote address */
-		   sk->dummy_th.dest	== dnum			&& /* remote port */
-		   sk->num		== snum			&& /* local port */
-		   sk->saddr		== saddr) {		   /* local address */
-			retval = 0;
-			goto out;
+	tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	for(; tb; tb = tb->next) {
+		if(tb->port == snum && tb->owners != NULL) {
+			/* Almost certainly the re-use port case, search the real hashes
+			 * so it actually scales.
+			 */
+			sk = __tcp_v4_lookup(NULL, sk->daddr, sk->dummy_th.dest,
+					     sk->rcv_saddr, snum, sk->bound_dev_if);
+			if((sk != NULL) && (sk->state != TCP_LISTEN))
+				retval = 0;
+			break;
 		}
 	}
-out:
 	SOCKHASH_UNLOCK();
 	return retval;
 }
@@ -578,8 +579,7 @@
 		return -ENETUNREACH;
 	}
 
-	if (!tcp_unique_address(rt->rt_src, sk->num, rt->rt_dst,
-				usin->sin_port)) {
+	if (!tcp_unique_address(sk)) {
 		ip_rt_put(rt);
 		return -EADDRNOTAVAIL;
 	}
@@ -587,7 +587,8 @@
 	lock_sock(sk);
 
 	/* Do this early, so there is less state to unwind on failure. */
-	buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);
+	buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)),
+			    0, GFP_KERNEL);
 	if (buff == NULL) {
 		release_sock(sk);
 		ip_rt_put(rt);
@@ -605,15 +606,13 @@
 
 	sk->dummy_th.dest = usin->sin_port;
 
-	sk->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
+	tp->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
 						   sk->dummy_th.source,
 						   usin->sin_port);
-
 	tp->snd_wnd = 0;
 	tp->snd_wl1 = 0;
-	tp->snd_wl2 = sk->write_seq;
-	tp->snd_una = sk->write_seq;
-
+	tp->snd_wl2 = tp->write_seq;
+	tp->snd_una = tp->write_seq;
 	tp->rcv_nxt = 0;
 
 	sk->err = 0;
@@ -635,14 +634,22 @@
 
 	/* No failure conditions can result past this point. */
 
+	/* We'll fix this up when we get a response from the other end.
+	 * See tcp_input.c:tcp_rcv_state_process case TCP_SYN_SENT.
+	 */
+	tp->tcp_header_len = sizeof(struct tcphdr) +
+		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
+
 	th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
 	buff->h.th = th;
 
 	memcpy(th,(void *)&(sk->dummy_th), sizeof(*th));
-	buff->seq = sk->write_seq++;
+	/* th->doff gets fixed up below if we tack on options. */
+
+	buff->seq = tp->write_seq++;
 	th->seq = htonl(buff->seq);
-	tp->snd_nxt = sk->write_seq;
-	buff->end_seq = sk->write_seq;
+	tp->snd_nxt = tp->write_seq;
+	buff->end_seq = tp->write_seq;
 	th->ack = 0;
 	th->syn = 1;
 
@@ -656,11 +663,9 @@
 	if(sk->mtu < 64)
 		sk->mtu = 64;	/* Sanity limit */
 
-	if (sk->user_mss)
-		sk->mss = sk->user_mss;
-	else
-		sk->mss = (sk->mtu - sizeof(struct iphdr) -
-			   sizeof(struct tcphdr));
+	sk->mss = (sk->mtu - sizeof(struct iphdr) - tp->tcp_header_len);
+	if(sk->user_mss)
+		sk->mss = min(sk->mss, sk->user_mss);
 
 	if (sk->mss < 1) {
 		printk(KERN_DEBUG "intial sk->mss below 1\n");
@@ -675,9 +680,8 @@
 		&tp->rcv_wscale);
 	th->window = htons(tp->rcv_wnd);
 
-	tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack,
-		sysctl_tcp_timestamps,
-		sysctl_tcp_window_scaling,tp->rcv_wscale);
+	tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps,
+		sysctl_tcp_window_scaling, tp->rcv_wscale);
 	buff->csum = 0;
 	th->doff = (sizeof(*th)+ tmp)>>2;
 
@@ -686,9 +690,10 @@
 	tcp_set_state(sk,TCP_SYN_SENT);
 
 	/* Socket identity change complete, no longer
-	 * in TCP_CLOSE, so rehash.
+	 * in TCP_CLOSE, so enter ourselves into the
+	 * hash tables.
 	 */
-	tcp_v4_rehash(sk);
+	tcp_v4_hash(sk);
 
 	tp->rto = rt->u.dst.rtt;
 
@@ -715,6 +720,7 @@
 
 static int tcp_v4_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
+	struct tcp_opt *tp;
 	int retval = -EINVAL;
 
 	/* Do sanity checking for sendmsg/sendto/send. */
@@ -740,7 +746,10 @@
 	lock_sock(sk);
 	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov,
 				msg->msg_flags);
-
+	/* Push out partial tail frames if needed. */
+	tp = &(sk->tp_pinfo.af_tcp);
+	if(tp->send_head && tcp_snd_test(sk, tp->send_head))
+		tcp_write_xmit(sk);
 	release_sock(sk);
 
 out:
@@ -854,7 +863,7 @@
 	th = (struct tcphdr*)(dp+(iph->ihl<<2));
 
 	sk = tcp_v4_lookup(iph->daddr, th->dest, iph->saddr, th->source, skb->dev->ifindex);
-	if (sk == NULL) {
+	if (sk == NULL || sk->state == TCP_TIME_WAIT) {
 		icmp_statistics.IcmpInErrors++;
 		return; 
 	}
@@ -1011,7 +1020,8 @@
 	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);
-	/* FIXME: should this carry an options packet? */
+
+	/* Do not place TCP options in a reset. */
 	ip_queue_xmit(skb1);
 	tcp_statistics.TcpOutSegs++;
 	tcp_statistics.TcpOutRsts++;
@@ -1063,6 +1073,14 @@
 	mss = (skb->dst->pmtu - sizeof(struct iphdr) - sizeof(struct tcphdr));
 	if (sk->user_mss)
 		mss = min(mss, sk->user_mss);
+	if(req->tstamp_ok)
+		mss -= TCPOLEN_TSTAMP_ALIGNED;
+	else
+		req->mss += TCPOLEN_TSTAMP_ALIGNED;
+
+	/* tcp_syn_build_options will do an skb_put() to obtain the TCP
+	 * options bytes below.
+	 */
 	skb->h.th = th = (struct tcphdr *) skb_put(skb, sizeof(struct tcphdr));
 
 	/* Don't offer more than they did.
@@ -1081,9 +1099,8 @@
 	memset(th, 0, sizeof(struct tcphdr));
 	th->syn = 1;
 	th->ack = 1;
-	th->source =
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
-	req->lcl_port; /* LVE */
+	th->source = req->lcl_port; /* LVE */
 #else
 	th->source = sk->dummy_th.source;
 #endif
@@ -1104,16 +1121,7 @@
 		req->rcv_wscale = rcv_wscale; 
 	}
 	th->window = htons(req->rcv_wnd);
-
-	/* XXX Partial csum of 4 byte quantity is itself! -DaveM
-	 * Yes, but it's a bit harder to special case now. It's
-	 * now computed inside the tcp_v4_send_check() to clean up
-	 * updating the options fields in the mainline send code.
-	 * If someone thinks this is really bad let me know and
-	 * I'll try to do it a different way. -- erics
-	 */
-
-	tmp = tcp_syn_build_options(skb, req->mss, req->sack_ok, req->tstamp_ok,
+	tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok,
 		req->wscale_ok,req->rcv_wscale);
 	skb->csum = 0;
 	th->doff = (sizeof(*th) + tmp)>>2;
@@ -1232,14 +1240,15 @@
 	req->rcv_wnd = 0;		/* So that tcp_send_synack() knows! */
 
 	req->rcv_isn = skb->seq;
- 	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
+ 	tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0;
 	tp.in_mss = 536;
 	tcp_parse_options(th,&tp,want_cookie);
-	if (tp.saw_tstamp)
-		req->ts_recent = tp.rcv_tsval;
 	req->mss = tp.in_mss;
+	if (tp.saw_tstamp) {
+		req->mss -= TCPOLEN_TSTAMP_ALIGNED;
+		req->ts_recent = tp.rcv_tsval;
+	}
 	req->tstamp_ok = tp.tstamp_ok;
-	req->sack_ok = tp.sack_ok;
 	req->snd_wscale = tp.snd_wscale;
 	req->wscale_ok = tp.wscale_ok;
 	req->rmt_port = th->source;
@@ -1289,6 +1298,113 @@
 	return 0;
 }
 
+/* This is not only more efficient than what we used to do, it eliminates
+ * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM
+ */
+struct sock *tcp_create_openreq_child(struct sock *sk, struct open_request *req, struct sk_buff *skb)
+{
+	struct sock *newsk = sk_alloc(AF_INET, GFP_ATOMIC, 0);
+
+	if(newsk != NULL) {
+		struct tcp_opt *newtp;
+
+		memcpy(newsk, sk, sizeof(*newsk));
+		newsk->sklist_next = NULL;
+		newsk->daddr = req->af.v4_req.rmt_addr;
+		newsk->rcv_saddr = req->af.v4_req.loc_addr;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		newsk->num = ntohs(skb->h.th->dest);
+#endif
+		newsk->state = TCP_SYN_RECV;
+
+		/* Clone the TCP header template */
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		newsk->dummy_th.source = req->lcl_port;
+#endif
+		newsk->dummy_th.dest = req->rmt_port;
+		newsk->dummy_th.ack = 1;
+		newsk->dummy_th.doff = sizeof(struct tcphdr)>>2;
+
+		newsk->sock_readers = 0;
+		atomic_set(&newsk->rmem_alloc, 0);
+		skb_queue_head_init(&newsk->receive_queue);
+		atomic_set(&newsk->wmem_alloc, 0);
+		skb_queue_head_init(&newsk->write_queue);
+		newsk->saddr = req->af.v4_req.loc_addr;
+
+		newsk->done = 0;
+		newsk->proc = 0;
+		newsk->pair = NULL;
+		skb_queue_head_init(&newsk->back_log);
+		skb_queue_head_init(&newsk->error_queue);
+
+		/* Now setup tcp_opt */
+		newtp = &(newsk->tp_pinfo.af_tcp);
+		newtp->pred_flags = 0;
+		newtp->rcv_nxt = req->rcv_isn + 1;
+		newtp->snd_nxt = req->snt_isn + 1;
+		newtp->snd_una = req->snt_isn + 1;
+		newtp->srtt = 0;
+		newtp->ato = 0;
+		newtp->snd_wl1 = req->rcv_isn;
+		newtp->snd_wl2 = req->snt_isn;
+		newtp->snd_wnd = ntohs(skb->h.th->window);
+		newtp->max_window = newtp->snd_wnd;
+		newtp->pending = 0;
+		newtp->retransmits = 0;
+		newtp->last_ack_sent = req->rcv_isn + 1;
+		newtp->backoff = 0;
+		newtp->mdev = TCP_TIMEOUT_INIT;
+		newtp->snd_cwnd = 1;
+		newtp->rto = TCP_TIMEOUT_INIT;
+		newtp->packets_out = 0;
+		newtp->high_seq = 0;
+		newtp->snd_ssthresh = 0x7fffffff;
+		newtp->snd_cwnd_cnt = 0;
+		newtp->dup_acks = 0;
+		newtp->delayed_acks = 0;
+		init_timer(&newtp->retransmit_timer);
+		newtp->retransmit_timer.function = &tcp_retransmit_timer;
+		newtp->retransmit_timer.data = (unsigned long) newsk;
+		init_timer(&newtp->delack_timer);
+		newtp->delack_timer.function = &tcp_delack_timer;
+		newtp->delack_timer.data = (unsigned long) newsk;
+		skb_queue_head_init(&newtp->out_of_order_queue);
+		newtp->send_head = newtp->retrans_head = NULL;
+		newtp->rcv_wup = req->rcv_isn + 1;
+		newtp->write_seq = req->snt_isn + 1;
+		newtp->copied_seq = req->rcv_isn + 1;
+
+		newtp->saw_tstamp = 0;
+		newtp->in_mss = 536;
+
+		init_timer(&newtp->probe_timer);
+		newtp->probe_timer.function = &tcp_probe_timer;
+		newtp->probe_timer.data = (unsigned long) newsk;
+		newtp->probes_out = 0;
+		newtp->syn_seq = req->rcv_isn;
+		newtp->fin_seq = req->rcv_isn;
+		newtp->urg_data = 0;
+		tcp_synq_init(newtp);
+		newtp->syn_backlog = 0;
+
+		/* Back to base struct sock members. */
+		newsk->err = 0;
+		newsk->ack_backlog = 0;
+		newsk->max_ack_backlog = SOMAXCONN;
+		newsk->priority = 1;
+
+		/* IP layer stuff */
+		newsk->opt = req->af.v4_req.opt;
+		newsk->timeout = 0;
+		init_timer(&newsk->timer);
+		newsk->timer.function = &net_timer;
+		newsk->timer.data = (unsigned long) newsk;
+		newsk->socket = NULL;
+	}
+	return newsk;
+}
+
 struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
 				   struct open_request *req,
 				   struct dst_entry *dst)
@@ -1301,98 +1417,14 @@
 	if (sk->ack_backlog > sk->max_ack_backlog)
 		goto exit; /* head drop */
 #endif
-	newsk = sk_alloc(AF_INET, GFP_ATOMIC);
+	newsk = tcp_create_openreq_child(sk, req, skb);
 	if (!newsk) 
 		goto exit;
 #ifdef NEW_LISTEN
 	sk->ack_backlog++;
 #endif
-	memcpy(newsk, sk, sizeof(*newsk));
-
-	/* Or else we die! -DaveM */
-	newsk->sklist_next = NULL;
-
-	newsk->opt = req->af.v4_req.opt; 
 
-	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 */
 	newtp = &(newsk->tp_pinfo.af_tcp);
-	newtp->send_head = NULL;
-	newtp->retrans_head = NULL;
-
-	newtp->pending = 0;
-
-	skb_queue_head_init(&newsk->back_log);
-
-	newsk->prot->init(newsk);
-
-	newtp->snd_cwnd_cnt = 0;
-	newtp->backoff = 0;
-	newsk->proc = 0;
-	newsk->done = 0;
-	newsk->pair = NULL;
-	atomic_set(&newsk->wmem_alloc, 0);
-	atomic_set(&newsk->rmem_alloc, 0);
-	newsk->localroute = sk->localroute;
-
-	newsk->err = 0;
-	newsk->shutdown = 0;
-	newsk->ack_backlog = 0;
-
-	newtp->fin_seq = req->rcv_isn;
-	newsk->syn_seq = req->rcv_isn;
-	newsk->state = TCP_SYN_RECV;
-	newsk->timeout = 0;
-
-	newsk->write_seq = req->snt_isn;
-
-	newtp->snd_wnd = ntohs(skb->h.th->window);
-	newtp->max_window = newtp->snd_wnd;
-	newtp->snd_wl1 = req->rcv_isn;
-	newtp->snd_wl2 = newsk->write_seq;
-	newtp->snd_una = newsk->write_seq++;
-	newtp->snd_nxt = newsk->write_seq;
-
-	newsk->urg_data = 0;
-	newtp->packets_out = 0;
-	newtp->retransmits = 0;
-	newsk->linger=0;
-	newsk->destroy = 0;
-	init_timer(&newsk->timer);
-	newsk->timer.data = (unsigned long) newsk;
-	newsk->timer.function = &net_timer;
-
-	tcp_init_xmit_timers(newsk);
-
-	newsk->dummy_th.source = 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-	req->lcl_port; /* LVE */
-#else
-	sk->dummy_th.source;
-#endif
-	newsk->dummy_th.dest = req->rmt_port;
-	newsk->sock_readers=0;
-
-	newtp->last_ack_sent = newtp->rcv_nxt = req->rcv_isn + 1;
-	newtp->rcv_wup = req->rcv_isn + 1;
-	newsk->copied_seq = req->rcv_isn + 1;
-
-	newsk->socket = NULL;
-
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-	/*
-	*      Deal with possibly redirected traffic by setting num to
-	*      the intended destination port of the received packet.
-        */
-	newsk->num = ntohs(skb->h.th->dest);
-#endif
-	newsk->daddr = req->af.v4_req.rmt_addr;
-	newsk->saddr = req->af.v4_req.loc_addr;
-	newsk->rcv_saddr = req->af.v4_req.loc_addr;
 
 	/* options / mss / route_cache */
 	if (dst == NULL) { 
@@ -1418,7 +1450,6 @@
 	if (newsk->mtu < 64)
 		newsk->mtu = 64;
 
-	newtp->sack_ok = req->sack_ok;
 	newtp->tstamp_ok = req->tstamp_ok;
 	newtp->window_clamp = req->window_clamp;
 	newtp->rcv_wnd = req->rcv_wnd;
@@ -1433,8 +1464,8 @@
 	if (newtp->tstamp_ok) {
 		newtp->ts_recent = req->ts_recent;
 		newtp->ts_recent_stamp = jiffies;
-		newtp->tcp_header_len = sizeof(struct tcphdr) + 12;	/* FIXME: define constant! */
-		newsk->dummy_th.doff += 3;
+		newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
+		newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2);
 	} else {
 		newtp->tcp_header_len = sizeof(struct tcphdr);
 	}
@@ -1446,13 +1477,13 @@
 	/* Make sure our mtu is adjusted for headers. */
 	newsk->mss = min(req->mss, snd_mss) + sizeof(struct tcphdr) - newtp->tcp_header_len;
 
-	tcp_v4_hash(newsk);
+	/* Must use the af_specific ops here for the case of IPv6 mapped. */
+	newsk->prot->hash(newsk);
 	add_to_prot_sklist(newsk);
 	return newsk;
 
 exit:
-	if (dst) 
-		dst_release(dst);
+	dst_release(dst);
 	return NULL;
 }
 
@@ -1623,6 +1654,8 @@
 
 	skb->used = 0;
 
+	if (sk->state == TCP_TIME_WAIT)
+		goto do_time_wait;
 	if (!sk->sock_readers)
 		return tcp_v4_do_rcv(sk, skb);
 
@@ -1636,6 +1669,12 @@
 	/* Discard frame. */
 	kfree_skb(skb);
   	return 0;
+
+do_time_wait:
+	if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
+				      skb, th, &(IPCB(skb)->opt), skb->len))
+		goto no_tcp_socket;
+	goto discard_it;
 }
 
 int tcp_v4_build_header(struct sock *sk, struct sk_buff *skb)
@@ -1770,33 +1809,21 @@
 	sizeof(struct sockaddr_in)
 };
 
+/* NOTE: A lot of things set to zero explicitly by call to
+ *       sk_alloc() so need not be done here.
+ */
 static int tcp_v4_init_sock(struct sock *sk)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	skb_queue_head_init(&sk->out_of_order_queue);
+	skb_queue_head_init(&tp->out_of_order_queue);
 	tcp_init_xmit_timers(sk);
 
-	tp->srtt  = 0;
 	tp->rto  = TCP_TIMEOUT_INIT;		/*TCP_WRITE_TIME*/
 	tp->mdev = TCP_TIMEOUT_INIT;
-
-	tp->ato = 0;
-	tp->iat = (HZ/5) << 3;
-
-	/* FIXME: tie this to sk->rcvbuf? (May be unnecessary) */
-	/* tp->rcv_wnd = 8192; */
-	tp->tstamp_ok = 0;
-	tp->sack_ok = 0;
-	tp->wscale_ok = 0;
 	tp->in_mss = 536;
-	tp->snd_wscale = 0;
-	tp->sacks = 0;
-	tp->saw_tstamp = 0;
-	tp->syn_backlog = 0;
 
-	/*
-	 * See draft-stevens-tcpca-spec-01 for discussion of the
+	/* See draft-stevens-tcpca-spec-01 for discussion of the
 	 * initialization of these values.
 	 */
 	tp->snd_cwnd = 1;
@@ -1804,9 +1831,7 @@
 
 	sk->priority = 1;
 	sk->state = TCP_CLOSE;
-
 	sk->max_ack_backlog = SOMAXCONN;
-
 	sk->mtu = 576;
 	sk->mss = 536;
 
@@ -1824,6 +1849,7 @@
 
 static int tcp_v4_destroy_sock(struct sock *sk)
 {
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
 
 	tcp_clear_xmit_timers(sk);
@@ -1836,8 +1862,16 @@
 		kfree_skb(skb);
 
 	/* Cleans up our, hopefuly empty, out_of_order_queue. */
-  	while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL)
+  	while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL)
 		kfree_skb(skb);
+
+	/* Clean up a locked TCP bind bucket, this only happens if a
+	 * port is allocated for a socket, but it never fully connects.
+	 * In which case we will find num to be non-zero and daddr to
+	 * be zero.
+	 */
+	if(sk->daddr == 0 && sk->num != 0)
+		tcp_bucket_unlock(sk);
 
 	return 0;
 }
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.89/linux/net/ipv4/tcp_output.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv4/tcp_output.c	Mon Mar 16 10:36:01 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_output.c,v 1.51 1998/01/15 22:40:39 freitag Exp $
+ * Version:	$Id: tcp_output.c,v 1.65 1998/03/15 12:07:03 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -34,8 +34,6 @@
 
 #include <net/tcp.h>
 
-extern int sysctl_tcp_sack;
-extern int sysctl_tcp_tsack;
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
 
@@ -45,7 +43,8 @@
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
 	tp->delayed_acks = 0;
-	sk->ack_backlog = 0;
+	if(tcp_in_quickack_mode(tp))
+		tp->ato = ((HZ/100)*2);
 	tcp_clear_xmit_timer(sk, TIME_DACK);
 }
 
@@ -58,69 +57,26 @@
 		tp->send_head = NULL;
 }
 
-static __inline__ int tcp_snd_test(struct sock *sk, struct sk_buff *skb)
-{
-	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
-	int nagle_check = 1;
-	int len;
-
-	/*	RFC 1122 - section 4.2.3.4
-	 *
-	 *	We must queue if
-	 *
-	 *	a) The right edge of this frame exceeds the window
-	 *	b) There are packets in flight and we have a small segment
-	 *	   [SWS avoidance and Nagle algorithm]
-	 *	   (part of SWS is done on packetization)
-	 *	c) We are retransmiting [Nagle]
-	 *	d) We have too many packets 'in flight'
-	 *
-	 * 	Don't use the nagle rule for urgent data.
-	 */
-	len = skb->end_seq - skb->seq;
-	if (!sk->nonagle && len < (sk->mss >> 1) && tp->packets_out && 
-	    !skb->h.th->urg)
-		nagle_check = 0;
-
-	return (nagle_check && tp->packets_out < tp->snd_cwnd &&
-		!after(skb->end_seq, tp->snd_una + tp->snd_wnd) &&
-		tp->retransmits == 0);
-}
-
 /*
  *	This is the main buffer sending routine. We queue the buffer
  *	having checked it is sane seeming.
  */
  
-void tcp_send_skb(struct sock *sk, struct sk_buff *skb)
+void tcp_send_skb(struct sock *sk, struct sk_buff *skb, int force_queue)
 {
-	struct tcphdr * th = skb->h.th;
+	struct tcphdr *th = skb->h.th;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	int size;
 
 	/* Length of packet (not counting length of pre-tcp headers). */
 	size = skb->len - ((unsigned char *) th - skb->data);
 
-	/* Sanity check it.. */
-	if (size < sizeof(struct tcphdr) || size > skb->len) {
-		printk(KERN_DEBUG "tcp_send_skb: bad skb "
-		       "(skb = %p, data = %p, th = %p, len = %u)\n",
-		       skb, skb->data, th, skb->len);
-		kfree_skb(skb);
-		return;
-	}
-
-	/* If we have queued a header size packet.. (these crash a few
-	 * tcp stacks if ack is not set)
-	 * FIXME: What is the equivalent below when we have options?
-	 */
-	if (size == sizeof(struct tcphdr)) {
-		/* If it's got a syn or fin discard. */
-		if(!th->syn && !th->fin) {
-			printk(KERN_DEBUG "tcp_send_skb: attempt to queue a bogon.\n");
-			kfree_skb(skb);
-			return;
-		}
+	/* If there is a FIN or a SYN we add it onto the size. */
+	if (th->fin || th->syn) {
+		if(th->syn)
+			size++;
+		if(th->fin)
+			size++;
 	}
 
 	/* Actual processing. */
@@ -129,14 +85,14 @@
 
 	skb_queue_tail(&sk->write_queue, skb);
 
-	if (tp->send_head == NULL && tcp_snd_test(sk, skb)) {
+	if (!force_queue && tp->send_head == NULL && tcp_snd_test(sk, skb)) {
 		struct sk_buff * buff;
 
 		/* This is going straight out. */
 		tp->last_ack_sent = tp->rcv_nxt;
 		th->ack_seq = htonl(tp->rcv_nxt);
 		th->window = htons(tcp_select_window(sk));
-		tcp_update_options((__u32 *)(th+1),tp);
+		tcp_update_options((__u32 *)(th + 1),tp);
 
 		tp->af_specific->send_check(sk, th, size, skb);
 
@@ -165,11 +121,10 @@
 	/* Remember where we must start sending. */
 	if (tp->send_head == NULL)
 		tp->send_head = skb;
-	if (tp->packets_out == 0 && !tp->pending) {
+	if (!force_queue && tp->packets_out == 0 && !tp->pending) {
 		tp->pending = TIME_PROBE0;
 		tcp_reset_xmit_timer(sk, TIME_PROBE0, tp->rto);
 	}
-	return;
 }
 
 /*
@@ -214,8 +169,6 @@
 	buff->h.th = nth;
 	memcpy(nth, th, tp->tcp_header_len);
 
-	/* FIXME: Make sure this gets tcp options right. */
-	
 	/* Correct the new header. */
 	buff->seq = skb->seq + len;
 	buff->end_seq = skb->end_seq;
@@ -281,14 +234,6 @@
 		tp->send_head = skb;
 		tp->packets_out--;
 		return -1;
-	} else {
-#if 0
-		/* If tcp_fragment succeded then
-		 * the send head is the resulting
-		 * fragment
-		 */
-		tp->send_head = skb->next;
-#endif
 	}
 	return 0;
 }
@@ -346,9 +291,10 @@
 			size = skb->len - (((unsigned char*)th) - skb->data);
 		}
 
-		tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
+		tp->last_ack_sent = tp->rcv_nxt;
+		th->ack_seq = htonl(tp->rcv_nxt);
 		th->window = rcv_wnd;
-		tcp_update_options((__u32 *)(th+1),tp);
+		tcp_update_options((__u32 *)(th + 1),tp);
 
 		tp->af_specific->send_check(sk, th, size, skb);
 
@@ -437,128 +383,44 @@
  * taken by headers, and the remaining space will be available for TCP data.
  * This should be accounted for correctly instead.
  */
-unsigned short tcp_select_window(struct sock *sk)
+u32 __tcp_select_window(struct sock *sk)
 {
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	int mss = sk->mss;
-	long free_space = sock_rspace(sk) / 2;
-	long window, cur_win;
+	unsigned int mss = sk->mss;
+	unsigned int free_space;
+	u32 window, cur_win;
 
+	free_space = (sk->rcvbuf - atomic_read(&sk->rmem_alloc)) / 2;
 	if (tp->window_clamp) {
 		free_space = min(tp->window_clamp, free_space);
 		mss = min(tp->window_clamp, mss);
-	} 
-#ifdef NO_ANK_FIX
-	/* I am tired of this message */
-	  else
-		printk(KERN_DEBUG "Clamp failure. Water leaking.\n");
-#endif
+	} else {
+		printk("tcp_select_window: tp->window_clamp == 0.\n");
+	}
 
 	if (mss < 1) {
 		mss = 1;
-		printk(KERN_DEBUG "tcp_select_window: mss fell to 0.\n");
+		printk("tcp_select_window: sk->mss fell to 0.\n");
 	}
 	
-	/* compute the actual window i.e.
-	 * old_window - received_bytes_on_that_win
-	 */
-	cur_win = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup);
-	window  = tp->rcv_wnd;
-
-	if (cur_win < 0) {
-		cur_win = 0;
-#ifdef NO_ANK_FIX
-	/* And this too. */
-		printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n",
-		       tp->rcv_wnd, tp->rcv_nxt, tp->rcv_wup);
-#endif
-	}
-
-	if (free_space < sk->rcvbuf/4 && free_space < mss/2)
+	cur_win = tcp_receive_window(tp);
+	if (free_space < sk->rcvbuf/4 && free_space < mss/2) {
 		window = 0;
-
-	/* Get the largest window that is a nice multiple of mss.
-	 * Window clamp already applied above.
-	 * If our current window offering is within 1 mss of the
-	 * free space we just keep it. This prevents the divide
-	 * and multiply from happening most of the time.
-	 * We also don't do any window rounding when the free space
-	 * is too small.
-	 */
-	if (window < free_space - mss && free_space > mss)
-		window = (free_space/mss)*mss;
-
-	/* Never shrink the offered window */
-	if (window < cur_win)
-		window = cur_win;
-
-	tp->rcv_wnd = window;
-	tp->rcv_wup = tp->rcv_nxt;
-	return window >> tp->rcv_wscale;	/* RFC1323 scaling applied */
-}
-
-#if 0
-/* Old algorithm for window selection */
-unsigned short tcp_select_window(struct sock *sk)
-{
-	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	int mss = sk->mss;
-	long free_space = sock_rspace(sk);
-	long window, cur_win, usable;
-
-	if (tp->window_clamp) {
-		free_space = min(tp->window_clamp, free_space);
-		mss = min(tp->window_clamp, mss);
-	}
-	
-	/* compute the actual window i.e.
-	 * old_window - received_bytes_on_that_win
-	 */
-	cur_win = tp->rcv_wnd - (tp->rcv_nxt - tp->rcv_wup);
-	window  = tp->rcv_wnd;
-
-	if (cur_win < 0) {
-		cur_win = 0;
-		printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n",
-		       tp->rcv_wnd, tp->rcv_nxt, tp->rcv_wup);
-	}
-
-	/* RFC 1122:
-	 * "the suggested [SWS] avoidance algoritm for the receiver is to keep
-	 *  RECV.NEXT + RCV.WIN fixed until:
-	 *  RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
-	 *
-	 * i.e. don't raise the right edge of the window until you can raise
-	 * it at least MSS bytes.
-	 */
-
-	usable = free_space - cur_win;
-	if (usable < 0)
-		usable = 0;
-
-	if (window < usable) {
-		/*	Window is not blocking the sender
-		 *	and we have enough free space for it
-		 */
-		if (cur_win > (sk->mss << 1))
-			goto out;
-	}
-       	
-	if (window >= usable) {
-		/*	We are offering too much, cut it down... 
-		 *	but don't shrink the window
-		 */
-		window = max(usable, cur_win);
 	} else {
-		while ((usable - window) >= mss)
-			window += mss;
+		/* Get the largest window that is a nice multiple of mss.
+		 * Window clamp already applied above.
+		 * If our current window offering is within 1 mss of the
+		 * free space we just keep it. This prevents the divide
+		 * and multiply from happening most of the time.
+		 * We also don't do any window rounding when the free space
+		 * is too small.
+		 */
+		window = tp->rcv_wnd;
+		if ((window <= (free_space - mss)) || (window > free_space))
+			window = (free_space/mss)*mss;
 	}
-out:
-	tp->rcv_wnd = window;
-	tp->rcv_wup = tp->rcv_nxt;
 	return window;
 }
-#endif
 
 static int tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *skb)
 {
@@ -729,84 +591,123 @@
 	}
 }
 
-/*
- *	Send a fin.
+/* Send a fin.  The caller locks the socket for us.  This cannot be
+ * allowed to fail queueing a FIN frame under any circumstances.
  */
-
 void tcp_send_fin(struct sock *sk)
 {
-	struct tcphdr *th =(struct tcphdr *)&sk->dummy_th;
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);	
-	struct tcphdr *t1;
-	struct sk_buff *buff;
-	int tmp;
 	
-	buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_KERNEL);
-	if (buff == NULL) {
-		/* FIXME: This is a disaster if it occurs. */
-		printk(KERN_INFO "tcp_send_fin: Impossible malloc failure");
-		return;
-	}
+	/* Optimization, tack on the FIN if we have a queue of
+	 * unsent frames.
+	 */
+	if(tp->send_head != NULL) {
+		struct sk_buff *tail = skb_peek_tail(&sk->write_queue);
+		struct tcphdr *th = tail->h.th;
+		int data_len;
+
+		/* Unfortunately tcp_write_xmit won't check for going over
+		 * the MSS due to the FIN sequence number, so we have to
+		 * watch out for it here.
+		 */
+		data_len = (tail->tail - (((unsigned char *)th)+tp->tcp_header_len));
+		if(data_len >= sk->mss)
+			goto build_new_frame; /* ho hum... */
+
+		/* tcp_write_xmit() will checksum the header etc. for us. */
+		th->fin = 1;
+		tail->end_seq++;
+	} else {
+		struct sk_buff *buff;
+		struct tcphdr *th;
 
-	/* Administrivia. */
-	buff->csum = 0;
+build_new_frame:
+		buff = sock_wmalloc(sk,
+				    (BASE_ACK_SIZE + tp->tcp_header_len +
+				     sizeof(struct sk_buff)),
+				    1, GFP_KERNEL);
+		if (buff == NULL) {
+			/* We can only fail due to low memory situations, not
+			 * due to going over our sndbuf limits (due to the
+			 * force flag passed to sock_wmalloc).  So just keep
+			 * trying.  We cannot allow this fail.  The socket is
+			 * still locked, so we need not check if the connection
+			 * was reset in the meantime etc.
+			 */
+			goto build_new_frame;
+		}
 
-	/* Put in the IP header and routing stuff. */
-	tmp = tp->af_specific->build_net_header(sk, buff);
-	if (tmp < 0) {
-		int t;
+		/* Administrivia. */
+		buff->csum = 0;
 
-  		/* FIXME: We must not throw this out. Eventually we must
-                 * put a FIN into the queue, otherwise it never gets queued.
-  		 */
-		kfree_skb(buff);
-		sk->write_seq++;
-		t = del_timer(&sk->timer);
-		if (t)
-			add_timer(&sk->timer);
-		else
-			tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-		return;
-	}
-	
-	/* We ought to check if the end of the queue is a buffer and
-	 * if so simply add the fin to that buffer, not send it ahead.
-	 */
-	t1 =(struct tcphdr *)skb_put(buff,tp->tcp_header_len);
-	buff->h.th =  t1;
-	tcp_build_options((__u32 *)(t1+1),tp);
-
-	memcpy(t1, th, sizeof(*t1));
-	buff->seq = sk->write_seq;
-	sk->write_seq++;
-	buff->end_seq = sk->write_seq;
-	t1->seq = htonl(buff->seq);
-	t1->ack_seq = htonl(tp->rcv_nxt);
-	t1->window = htons(tcp_select_window(sk));
-	t1->fin = 1;
-
-	tp->af_specific->send_check(sk, t1, tp->tcp_header_len, buff);
-
-	/* The fin can only be transmited after the data. */
-	skb_queue_tail(&sk->write_queue, buff);
- 	if (tp->send_head == NULL) {
-		/* FIXME: BUG! we need to check if the fin fits into the window
-		 * here. If not we need to do window probing (sick, but true)
+		/* Put in the IP header and routing stuff.
+		 *
+		 * FIXME:
+		 * We can fail if the interface for the route
+		 * this socket takes goes down right before
+		 * we get here.  ANK is there a way to point
+		 * this into a "black hole" route in such a
+		 * case?  Ideally, we should still be able to
+		 * queue this and let the retransmit timer
+		 * keep trying until the destination becomes
+		 * reachable once more.  -DaveM
 		 */
-		struct sk_buff *skb1;
+		if(tp->af_specific->build_net_header(sk, buff) < 0) {
+			kfree_skb(buff);
+			goto update_write_seq;
+		}
+		th = (struct tcphdr *) skb_put(buff, tp->tcp_header_len);
+		buff->h.th = th;
 
-		tp->packets_out++;
-		tp->snd_nxt = sk->write_seq;
-		buff->when = jiffies;
+		memcpy(th, (void *) &(sk->dummy_th), sizeof(*th));
+		th->seq = htonl(tp->write_seq);
+		th->fin = 1;
+		tcp_build_options((__u32 *)(th + 1), tp);
 
-		skb1 = skb_clone(buff, GFP_KERNEL);
-		if (skb1) {
-			skb_set_owner_w(skb1, sk);
-			tp->af_specific->queue_xmit(skb1);
-		}
+		/* This makes sure we do things like abide by the congestion
+		 * window and other constraints which prevent us from sending.
+		 */
+		tcp_send_skb(sk, buff, 0);
+	}
+update_write_seq:
+	/* So that we recognize the ACK coming back for
+	 * this FIN as being legitimate.
+	 */
+	tp->write_seq++;
+}
 
-                if (!tcp_timer_is_set(sk, TIME_RETRANS))
-			tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
+/* We get here when a process closes a file descriptor (either due to
+ * an explicit close() or as a byproduct of exit()'ing) and there
+ * was unread data in the receive queue.  This behavior is recommended
+ * by draft-ietf-tcpimpl-prob-03.txt section 3.10.  -DaveM
+ */
+void tcp_send_active_reset(struct sock *sk)
+{
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
+	struct sk_buff *skb;
+	struct tcphdr *th;
+
+again:
+	/* NOTE: No TCP options attached and we never retransmit this. */
+	skb = sock_wmalloc(sk, (BASE_ACK_SIZE + sizeof(*th)), 1, GFP_KERNEL);
+	if(skb == NULL)
+		goto again;
+	skb->csum = 0;
+	if(tp->af_specific->build_net_header(sk, skb) < 0) {
+		kfree_skb(skb);
+	} else {
+		th = (struct tcphdr *) skb_put(skb, sizeof(*th));
+		memcpy(th, &(sk->dummy_th), sizeof(*th));
+		th->seq = htonl(tp->write_seq);
+		th->rst = 1;
+		th->doff = sizeof(*th) / 4;
+		tp->last_ack_sent = tp->rcv_nxt;
+		th->ack_seq = htonl(tp->rcv_nxt);
+		th->window = htons(tcp_select_window(sk));
+		tp->af_specific->send_check(sk, th, sizeof(*th), skb);
+		tp->af_specific->queue_xmit(skb);
+		tcp_statistics.TcpOutSegs++;
+		tcp_statistics.TcpOutRsts++;
 	}
 }
 
@@ -814,6 +715,9 @@
  * a SYN packet that crossed the incoming SYN that caused this routine
  * to get called. If this assumption fails then the initial rcv_wnd
  * and rcv_wscale values will not be correct.
+ *
+ * XXX When you have time Dave, redo this to use tcp_send_skb() just
+ * XXX like tcp_send_fin() above now does.... -DaveM
  */
 int tcp_send_synack(struct sock *sk)
 {
@@ -823,7 +727,7 @@
 	struct tcphdr *th;
 	int tmp;
 	
-	skb = sock_wmalloc(sk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
+	skb = sock_wmalloc(sk, MAX_SYN_SIZE + sizeof(struct sk_buff), 1, GFP_ATOMIC);
 	if (skb == NULL) 
 		return -ENOMEM;
 
@@ -855,8 +759,7 @@
 	tp->last_ack_sent = th->ack_seq = htonl(tp->rcv_nxt);
 
 	tmp = tcp_syn_build_options(skb, sk->mss,
-		tp->sack_ok, tp->tstamp_ok,
-		tp->wscale_ok,tp->rcv_wscale);
+		tp->tstamp_ok, tp->wscale_ok, tp->rcv_wscale);
 	skb->csum = 0;
 	th->doff = (sizeof(*th) + tmp)>>2;
 
@@ -880,31 +783,24 @@
 }
 
 /*
- *	Set up the timers for sending a delayed ack..
- *
- *      rules for delaying an ack:
- *      - delay time <= 0.5 HZ
- *      - must send at least every 2 full sized packets
- *      - we don't have a window update to send
+ * Send out a delayed ack, the caller does the policy checking
+ * to see if we should even be here.  See tcp_input.c:tcp_ack_snd_check()
+ * for details.
  */
 
-void tcp_send_delayed_ack(struct sock * sk, int max_timeout)
+void tcp_send_delayed_ack(struct tcp_opt *tp, int max_timeout)
 {
-	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
-	unsigned long timeout, now;
+	unsigned long timeout;
 
-	/* Calculate new timeout. */
-	now = jiffies;
+	/* Stay within the limit we were given */
 	timeout = tp->ato;
-
-	if (timeout > max_timeout ||
-	    ((tp->rcv_nxt - tp->rcv_wup) > (sk->mss << 2)))
-		timeout = now;
-	else
-		timeout += now;
+	if (timeout > max_timeout)
+		timeout = max_timeout;
+	timeout += jiffies;
 
 	/* Use new timeout only if there wasn't a older one earlier. */
-	if (!del_timer(&tp->delack_timer) || timeout < tp->delack_timer.expires)
+	if ((!tp->delack_timer.prev || !del_timer(&tp->delack_timer)) ||
+	    (timeout < tp->delack_timer.expires))
 		tp->delack_timer.expires = timeout;
 
 	add_timer(&tp->delack_timer);
@@ -928,8 +824,6 @@
 
 	/* We need to grab some memory, and put together an ack,
 	 * and then put it into the queue to be sent.
-	 * FIXME: is it better to waste memory here and use a
-	 * constant sized ACK?
 	 */
 	buff = sock_wmalloc(sk, BASE_ACK_SIZE + tp->tcp_header_len, 1, GFP_ATOMIC);
 	if (buff == NULL) {
@@ -938,7 +832,7 @@
 		 *	bandwidth on slow links to send a spare ack than
 		 *	resend packets.
 		 */
-		tcp_send_delayed_ack(sk, HZ/2);
+		tcp_send_delayed_ack(tp, HZ/2);
 		return;
 	}
 
@@ -956,22 +850,16 @@
 
 	th = (struct tcphdr *)skb_put(buff,tp->tcp_header_len);
 	memcpy(th, &sk->dummy_th, sizeof(struct tcphdr));
-	tcp_build_options((__u32 *)(th+1),tp);
 
 	/* Swap the send and the receive. */
 	th->window	= ntohs(tcp_select_window(sk));
 	th->seq		= ntohl(tp->snd_nxt);
 	tp->last_ack_sent = tp->rcv_nxt;
 	th->ack_seq	= htonl(tp->rcv_nxt);
+	tcp_build_and_update_options((__u32 *)(th + 1), tp);
 
   	/* Fill in the packet and send it. */
 	tp->af_specific->send_check(sk, th, tp->tcp_header_len, buff);
-
-#if 0
-	SOCK_DEBUG(sk, "\rtcp_send_ack: seq %x ack %x\n",
-		   tp->snd_nxt, tp->rcv_nxt);
-#endif
-
 	tp->af_specific->queue_xmit(buff);
   	tcp_statistics.TcpOutSegs++;
 }
@@ -1017,6 +905,7 @@
 		}
 
 		th = skb->h.th;
+		tcp_update_options((__u32 *)(th + 1), tp);
 		tp->af_specific->send_check(sk, th, th->doff * 4 + win_size, skb);
 		buff = skb_clone(skb, GFP_ATOMIC);
 		if (buff == NULL)
@@ -1047,25 +936,19 @@
 			return;
 		}
 
-		t1 = (struct tcphdr *) skb_put(buff, sizeof(struct tcphdr));
+		t1 = (struct tcphdr *) skb_put(buff, tp->tcp_header_len);
 		memcpy(t1,(void *) &sk->dummy_th, sizeof(*t1));
-		/* FIXME: should zero window probes have SACK and/or TIMESTAMP data?
-		 * If so we have to tack them on here.
-		 */
 
 		/*	Use a previous sequence.
 		 *	This should cause the other end to send an ack.
 		 */
 	 
 		t1->seq = htonl(tp->snd_nxt-1);
-/*		t1->fin = 0;	-- We are sending a 'previous' sequence, and 0 bytes of data - thus no FIN bit */
 		t1->ack_seq = htonl(tp->rcv_nxt);
 		t1->window = htons(tcp_select_window(sk));
+		tcp_build_and_update_options((__u32 *)(t1 + 1), tp);
 
-		/* Value from dummy_th may be larger. */
-		t1->doff = sizeof(struct tcphdr)/4;
-
-		tp->af_specific->send_check(sk, t1, sizeof(*t1), buff);
+		tp->af_specific->send_check(sk, t1, tp->tcp_header_len, buff);
 	}
 
 	/* Send it. */
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.89/linux/net/ipv4/tcp_timer.c	Mon Jan 12 15:28:28 1998
+++ linux/net/ipv4/tcp_timer.c	Sat Mar 14 11:37:27 1998
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_timer.c,v 1.33 1997/12/13 21:53:01 kuznet Exp $
+ * Version:	$Id: tcp_timer.c,v 1.39 1998/03/13 08:02:17 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -31,6 +31,7 @@
 static void tcp_sltimer_handler(unsigned long);
 static void tcp_syn_recv_timer(unsigned long);
 static void tcp_keepalive(unsigned long data);
+static void tcp_bucketgc(unsigned long);
 
 struct timer_list	tcp_slow_timer = {
 	NULL, NULL,
@@ -41,7 +42,8 @@
 
 struct tcp_sl_timer tcp_slt_array[TCP_SLT_MAX] = {
 	{ATOMIC_INIT(0), TCP_SYNACK_PERIOD, 0, tcp_syn_recv_timer},/* SYNACK	*/
-	{ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive}   /* KEEPALIVE	*/
+	{ATOMIC_INIT(0), TCP_KEEPALIVE_PERIOD, 0, tcp_keepalive},  /* KEEPALIVE	*/
+	{ATOMIC_INIT(0), TCP_BUCKETGC_PERIOD, 0, tcp_bucketgc}     /* BUCKETGC	*/
 };
 
 const char timer_bug_msg[] = KERN_DEBUG "tcpbug: unknown timer value\n";
@@ -87,20 +89,24 @@
 		 * The delayed ack timer can be set if we are changing the
 		 * retransmit timer when removing acked frames.
 		 */
-		del_timer(&tp->probe_timer);
-		del_timer(&tp->retransmit_timer);
+		if(tp->probe_timer.prev)
+			del_timer(&tp->probe_timer);
+		if(tp->retransmit_timer.prev)
+			del_timer(&tp->retransmit_timer);
 		tp->retransmit_timer.expires=jiffies+when;
 		add_timer(&tp->retransmit_timer);
 		break;
 
 	case TIME_DACK:
-		del_timer(&tp->delack_timer);
+		if(tp->delack_timer.prev)
+			del_timer(&tp->delack_timer);
 		tp->delack_timer.expires=jiffies+when;
 		add_timer(&tp->delack_timer);
 		break;
 
 	case TIME_PROBE0:
-		del_timer(&tp->probe_timer);
+		if(tp->probe_timer.prev)
+			del_timer(&tp->probe_timer);
 		tp->probe_timer.expires=jiffies+when;
 		add_timer(&tp->probe_timer);
 		break;	
@@ -118,9 +124,12 @@
 {	
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
-	del_timer(&tp->retransmit_timer);
-	del_timer(&tp->delack_timer);
-	del_timer(&tp->probe_timer);
+	if(tp->retransmit_timer.prev)
+		del_timer(&tp->retransmit_timer);
+	if(tp->delack_timer.prev)
+		del_timer(&tp->delack_timer);
+	if(tp->probe_timer.prev)
+		del_timer(&tp->probe_timer);
 }
 
 static int tcp_write_err(struct sock *sk, int force)
@@ -131,9 +140,8 @@
 	tcp_clear_xmit_timers(sk);
 	
 	/* Time wait the socket. */
-	if (!force && (1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) {
-		tcp_set_state(sk,TCP_TIME_WAIT);
-		tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+	if (!force && ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING))) {
+		tcp_time_wait(sk);
 	} else {
 		/* Clean up time. */
 		tcp_set_state(sk, TCP_CLOSE);
@@ -173,9 +181,8 @@
 	return 1;
 }
 
-
-void tcp_delack_timer(unsigned long data) {
-
+void tcp_delack_timer(unsigned long data)
+{
 	struct sock *sk = (struct sock*)data;
 
 	if(sk->zapped)
@@ -185,8 +192,8 @@
 		tcp_read_wakeup(sk); 		
 }
 
-void tcp_probe_timer(unsigned long data) {
-
+void tcp_probe_timer(unsigned long data)
+{
 	struct sock *sk = (struct sock*)data;
 	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 
@@ -212,10 +219,9 @@
 			sk->err = ETIMEDOUT;
 		sk->error_report(sk);
 
-		/* Time wait the socket. */
 		if ((1<<sk->state) & (TCPF_FIN_WAIT1|TCPF_FIN_WAIT2|TCPF_CLOSING)) {
-			tcp_set_state(sk, TCP_TIME_WAIT);
-			tcp_reset_msl_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+			/* Time wait the socket. */
+			tcp_time_wait(sk);
 		} else {
 			/* Clean up time. */
 			tcp_set_state(sk, TCP_CLOSE);
@@ -250,6 +256,35 @@
 		}
 	}
 	return res;
+}
+
+/* Garbage collect TCP bind buckets. */
+static void tcp_bucketgc(unsigned long __unused)
+{
+	int i;
+
+	for(i = 0; i < TCP_BHTABLE_SIZE; i++) {
+		struct tcp_bind_bucket *tb = tcp_bound_hash[i];
+
+		while(tb) {
+			struct tcp_bind_bucket *next = tb->next;
+
+			if((tb->owners == NULL) &&
+			   !(tb->flags & TCPB_FLAG_LOCKED)) {
+				/* Eat timer reference. */
+				tcp_dec_slow_timer(TCP_SLT_BUCKETGC);
+
+				/* Unlink bucket. */
+				if(tb->next)
+					tb->next->pprev = tb->pprev;
+				*tb->pprev = tb->next;
+
+				/* Finally, free it up. */
+				kmem_cache_free(tcp_bucket_cachep, tb);
+			}
+			tb = next;
+		}
+	}
 }
 
 /*
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/timer.c linux/net/ipv4/timer.c
--- v2.1.89/linux/net/ipv4/timer.c	Sun Nov 30 14:00:39 1997
+++ linux/net/ipv4/timer.c	Sat Mar 14 11:37:27 1998
@@ -5,7 +5,7 @@
  *
  *		TIMER - implementation of software timers for IP.
  *
- * Version:	$Id: timer.c,v 1.7 1997/09/17 18:50:26 freitag Exp $
+ * Version:	$Id: timer.c,v 1.10 1998/03/13 08:02:18 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -52,76 +52,52 @@
 
 void net_delete_timer (struct sock *t)
 {
-	unsigned long flags;
-
-	save_flags (flags);
-	cli();
-
+	if(t->timer.prev)
+		del_timer (&t->timer);
 	t->timeout = 0;
-	del_timer (&t->timer);
-
-	restore_flags (flags);
 }
 
 void net_reset_timer (struct sock *t, int timeout, unsigned long len)
 {
 	net_delete_timer (t);
 	t->timeout = timeout;
-#if 1
-  /* FIXME: ??? */
-	if ((int) len < 0)	/* prevent close to infinite timers. THEY _DO_ */
-		len = 3;	/* happen (negative values ?) - don't ask me why ! -FB */
-#endif
 	t->timer.expires = jiffies+len;
 	add_timer (&t->timer);
 }
 
-
-/*
- *	Now we will only be called whenever we need to do
- *	something, but we must be sure to process all of the
- *	sockets that need it.
+/* Now we will only be called whenever we need to do
+ * something, but we must be sure to process all of the
+ * sockets that need it.
  */
-
 void net_timer (unsigned long data)
 {
 	struct sock *sk = (struct sock*)data;
 	int why = sk->timeout;
 
-	/* 
-	 * only process if socket is not in use
-	 */
-
-	if (sk->sock_readers)
-	{
+	/* Only process if socket is not in use. */
+	if (sk->sock_readers) {
 		sk->timer.expires = jiffies+HZ;
 		add_timer(&sk->timer);
-		sti();
 		return;
 	}
 
 	/* Always see if we need to send an ack. */
-
-	if (sk->ack_backlog && !sk->zapped) 
-	{
+	if (sk->tp_pinfo.af_tcp.delayed_acks && !sk->zapped) {
 		sk->prot->read_wakeup (sk);
-		if (! sk->dead)
-		sk->data_ready(sk,0);
+		if (!sk->dead)
+			sk->data_ready(sk,0);
 	}
 
 	/* Now we need to figure out why the socket was on the timer. */
-
-	switch (why) 
-	{
+	switch (why) {
 		case TIME_DONE:
-			/* If the socket hasn't been closed off, re-try a bit later */
+			/* If the socket hasn't been closed off, re-try a bit later. */
 			if (!sk->dead) {
 				net_reset_timer(sk, TIME_DONE, TCP_DONE_TIME);
 				break;
 			}
 
-			if (sk->state != TCP_CLOSE) 
-			{
+			if (sk->state != TCP_CLOSE) {
 				printk (KERN_DEBUG "non CLOSE socket in time_done\n");
 				break;
 			}
@@ -129,11 +105,9 @@
 			break;
 
 		case TIME_DESTROY:
-		/*
-		 *	We've waited for a while for all the memory associated with
-		 *	the socket to be freed.
-		 */
-
+			/* We've waited for a while for all the memory associated with
+			 * the socket to be freed.
+			 */
 			destroy_sock(sk);
 			break;
 
@@ -148,7 +122,8 @@
 			break;
 
 		default:
-			printk (KERN_DEBUG "net_timer: timer expired - reason %d is unknown\n", why);
+			/* I want to see these... */
+			printk ("net_timer: timer expired - reason %d is unknown\n", why);
 			break;
 	}
 }
diff -u --recursive --new-file v2.1.89/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.89/linux/net/ipv4/udp.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv4/udp.c	Thu Mar 12 10:32:04 1998
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.47 1997/12/27 20:41:16 kuznet Exp $
+ * Version:	$Id: udp.c,v 1.53 1998/03/12 03:20:00 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -828,7 +828,7 @@
 				 * of this packet since that is all
 				 * that will be read.
 				 */
-				amount = skb->tail - skb->h.raw;
+				amount = skb->len - sizeof(struct udphdr);
 			}
 			return put_user(amount, (int *)arg);
 		}
@@ -1033,17 +1033,18 @@
 
 /*
  *	Multicasts and broadcasts go to each listener.
+ *
+ *	Note: called only from the BH handler context,
+ *	so we don't need to lock the hashes.
  */
 static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
 				 u32 saddr, u32 daddr)
 {
 	struct sock *sk;
-	int given = 0;
 
-	SOCKHASH_LOCK();
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
 	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
-	if(sk) {
+	if (sk) {
 		struct sock *sknext = NULL;
 
 		do {
@@ -1058,10 +1059,7 @@
 				udp_deliver(sk, skb1);
 			sk = sknext;
 		} while(sknext);
-		given = 1;
-	}
-	SOCKHASH_UNLOCK();
-	if(!given)
+	} else
 		kfree_skb(skb);
 	return 0;
 }
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c
--- v2.1.89/linux/net/ipv6/addrconf.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/addrconf.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: addrconf.c,v 1.32 1997/12/27 20:41:18 kuznet Exp $
+ *	$Id: addrconf.c,v 1.37 1998/03/08 20:52:46 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -1753,6 +1753,8 @@
 	t->sysctl_header = register_sysctl_table(t->addrconf_root_dir, 0);
 	if (t->sysctl_header == NULL)
 		kfree(t);
+	else
+		p->sysctl = t;
 }
 
 static void addrconf_sysctl_unregister(struct ipv6_devconf *p)
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.89/linux/net/ipv6/af_inet6.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/af_inet6.c	Tue Mar 10 09:47:27 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.24 1997/12/13 21:53:08 kuznet Exp $
+ *	$Id: af_inet6.c,v 1.28 1998/03/08 05:56:49 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -71,7 +71,7 @@
 	struct sock *sk;
 	struct proto *prot;
 
-	sk = sk_alloc(AF_INET6, GFP_KERNEL);
+	sk = sk_alloc(AF_INET6, GFP_KERNEL, 1);
 	if (sk == NULL) 
 		goto do_oom;
 
@@ -139,8 +139,7 @@
 		 * creation time automatically shares.
 		 */
 		sk->dummy_th.source = ntohs(sk->num);
-		if(sk->prot->hash)
-			sk->prot->hash(sk);
+		sk->prot->hash(sk);
 		add_to_prot_sklist(sk);
 	}
 
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/exthdrs.c linux/net/ipv6/exthdrs.c
--- v2.1.89/linux/net/ipv6/exthdrs.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/exthdrs.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: exthdrs.c,v 1.4 1997/03/18 18:24:29 davem Exp $
+ *	$Id: exthdrs.c,v 1.5 1998/02/12 07:43:39 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.1.89/linux/net/ipv6/icmp.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/icmp.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *
- *	$Id: icmp.c,v 1.12 1997/12/13 21:53:10 kuznet Exp $
+ *	$Id: icmp.c,v 1.13 1998/02/12 07:43:41 davem Exp $
  *
  *	Based on net/ipv4/icmp.c
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/ip6_fib.c linux/net/ipv6/ip6_fib.c
--- v2.1.89/linux/net/ipv6/ip6_fib.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/ip6_fib.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fib.c,v 1.10 1997/12/13 21:53:10 kuznet Exp $
+ *	$Id: ip6_fib.c,v 1.11 1998/03/08 05:56:50 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/ip6_fw.c linux/net/ipv6/ip6_fw.c
--- v2.1.89/linux/net/ipv6/ip6_fw.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/ip6_fw.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_fw.c,v 1.8 1997/12/13 21:53:11 kuznet Exp $
+ *	$Id: ip6_fw.c,v 1.9 1998/02/12 07:43:42 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/ip6_input.c linux/net/ipv6/ip6_input.c
--- v2.1.89/linux/net/ipv6/ip6_input.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/ip6_input.c	Tue Mar 10 09:47:27 1998
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>
  *	Ian P. Morris		<I.P.Morris@soton.ac.uk>
  *
- *	$Id: ip6_input.c,v 1.7 1997/09/20 20:48:27 davem Exp $
+ *	$Id: ip6_input.c,v 1.8 1998/02/12 07:43:43 davem Exp $
  *
  *	Based in linux/net/ipv4/ip_input.c
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
--- v2.1.89/linux/net/ipv6/ip6_output.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/ip6_output.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: ip6_output.c,v 1.7 1997/12/29 19:52:46 kuznet Exp $
+ *	$Id: ip6_output.c,v 1.9 1998/03/08 05:56:50 davem Exp $
  *
  *	Based on linux/net/ipv4/ip_output.c
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c
--- v2.1.89/linux/net/ipv6/ipv6_sockglue.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/ipv6_sockglue.c	Tue Mar 10 09:47:27 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/net/ipv4/ip_sockglue.c
  *
- *	$Id: ipv6_sockglue.c,v 1.16 1997/12/13 21:53:13 kuznet Exp $
+ *	$Id: ipv6_sockglue.c,v 1.17 1998/03/08 05:56:51 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- v2.1.89/linux/net/ipv6/ndisc.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/ndisc.c	Tue Mar 10 09:47:27 1998
@@ -132,7 +132,7 @@
 	pndisc_destructor,
 	pndisc_redo,
         { NULL, NULL, &nd_tbl, 0, NULL, NULL,
-		  30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 0, 64 },
+		  30*HZ, 1*HZ, 60*HZ, 30*HZ, 5*HZ, 3, 3, 0, 3, 1*HZ, (8*HZ)/10, 64, 0 },
 	30*HZ, 128, 512, 1024,
 };
 
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/proc.c linux/net/ipv6/proc.c
--- v2.1.89/linux/net/ipv6/proc.c	Wed Apr 23 19:01:30 1997
+++ linux/net/ipv6/proc.c	Sat Mar 14 11:37:27 1998
@@ -7,7 +7,7 @@
  *		PROC file system.  This is very similar to the IPv4 version,
  *		except it reports the sockets in the INET6 address family.
  *
- * Version:	$Id: proc.c,v 1.4 1997/04/20 22:50:44 schenk Exp $
+ * Version:	$Id: proc.c,v 1.6 1998/03/13 08:02:19 davem Exp $
  *
  * Authors:	David S. Miller (davem@caip.rutgers.edu)
  *
@@ -21,6 +21,7 @@
 #include <linux/net.h>
 #include <linux/in6.h>
 #include <net/sock.h>
+#include <net/tcp.h>
 #include <net/transp_v6.h>
 
 /* This is the main implementation workhorse of all these routines. */
@@ -52,21 +53,35 @@
 	SOCKHASH_LOCK();
 	sp = pro->sklist_next;
 	while(sp != (struct sock *)pro) {
+		struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sp;
+		int tw_bucket = 0;
+
 		pos += 149;
 		if(pos < offset)
 			goto next;
 		tp = &(sp->tp_pinfo.af_tcp);
-		dest  = &sp->net_pinfo.af_inet6.daddr;
-		src   = &sp->net_pinfo.af_inet6.rcv_saddr;
+		if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
+			tw_bucket = 1;
+			dest  = &tw->v6_daddr;
+			src   = &tw->v6_rcv_saddr;
+		} else {
+			dest  = &sp->net_pinfo.af_inet6.daddr;
+			src   = &sp->net_pinfo.af_inet6.rcv_saddr;
+		}
 		destp = ntohs(sp->dummy_th.dest);
 		srcp  = ntohs(sp->dummy_th.source);
-
-		timer_active1 = del_timer(&tp->retransmit_timer);
-		timer_active2 = del_timer(&sp->timer);
-		if(!timer_active1) tp->retransmit_timer.expires = 0;
-		if(!timer_active2) sp->timer.expires = 0;
-		timer_active = 0;
-		timer_expires = (unsigned) -1;
+		if((format == 0) && (sp->state == TCP_TIME_WAIT)) {
+			timer_active1	= timer_active2 = 0;
+			timer_active	= 3;
+			timer_expires	= tw->timer.expires;
+		} else {
+			timer_active1 = del_timer(&tp->retransmit_timer);
+			timer_active2 = del_timer(&sp->timer);
+			if(!timer_active1) tp->retransmit_timer.expires = 0;
+			if(!timer_active2) sp->timer.expires = 0;
+			timer_active = 0;
+			timer_expires = (unsigned) -1;
+		}
 		if(timer_active1 && tp->retransmit_timer.expires < timer_expires) {
 			timer_active = timer_active1;
 			timer_expires = tp->retransmit_timer.expires;
@@ -75,6 +90,8 @@
 			timer_active = timer_active2;
 			timer_expires = sp->timer.expires;
 		}
+		if(timer_active == 0)
+			timer_expires = jiffies;
 		sprintf(tmpbuf, "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
 			"%02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
 			i,
@@ -83,13 +100,23 @@
 			dest->s6_addr32[0], dest->s6_addr32[1],
 			dest->s6_addr32[2], dest->s6_addr32[3], destp,
 			sp->state,
-			format==0?sp->write_seq-tp->snd_una:atomic_read(&sp->wmem_alloc),
-			format==0?tp->rcv_nxt-sp->copied_seq:atomic_read(&sp->rmem_alloc),
+			(tw_bucket ?
+			 0 :
+			 (format == 0) ?
+			 tp->write_seq-tp->snd_una :
+			 atomic_read(&sp->wmem_alloc)),
+			(tw_bucket ?
+			 0 :
+			 (format == 0) ?
+			 tp->rcv_nxt-tp->copied_seq :
+			 atomic_read(&sp->rmem_alloc)),
 			timer_active, timer_expires-jiffies,
-			tp->retransmits,
-			sp->socket ? sp->socket->inode->i_uid:0,
-			timer_active?sp->timeout:0,
-			sp->socket ? sp->socket->inode->i_ino:0);
+			(tw_bucket ? 0 : tp->retransmits),
+			((!tw_bucket && sp->socket) ?
+			 sp->socket->inode->i_uid : 0),
+			(!tw_bucket && timer_active) ? sp->timeout : 0,
+			((!tw_bucket && sp->socket) ?
+			 sp->socket->inode->i_ino : 0));
 
 		if(timer_active1) add_timer(&tp->retransmit_timer);
 		if(timer_active2) add_timer(&sp->timer);
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.89/linux/net/ipv6/raw.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/raw.c	Tue Mar 10 09:47:27 1998
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.16 1997/12/29 19:52:48 kuznet Exp $
+ *	$Id: raw.c,v 1.18 1998/03/08 05:56:54 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/reassembly.c linux/net/ipv6/reassembly.c
--- v2.1.89/linux/net/ipv6/reassembly.c	Thu Feb 12 20:56:14 1998
+++ linux/net/ipv6/reassembly.c	Tue Mar 10 09:47:27 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: reassembly.c,v 1.8 1997/12/29 19:52:50 kuznet Exp $
+ *	$Id: reassembly.c,v 1.9 1998/02/12 07:43:48 davem Exp $
  *
  *	Based on: net/ipv4/ip_fragment.c
  *
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/route.c linux/net/ipv6/route.c
--- v2.1.89/linux/net/ipv6/route.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/route.c	Mon Mar 16 10:36:01 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: route.c,v 1.19 1997/12/13 21:53:16 kuznet Exp $
+ *	$Id: route.c,v 1.25 1998/03/15 03:31:47 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -85,18 +85,18 @@
 };
 
 struct rt6_info ip6_null_entry = {
-	{{NULL, ATOMIC_INIT(0), ATOMIC_INIT(0), NULL,
+	{{NULL, ATOMIC_INIT(1), ATOMIC_INIT(1), NULL,
 	  -1, 0, 0, 0, 0, 0, 0, 0, 0,
 	  -ENETUNREACH, NULL, NULL,
 	  ip6_pkt_discard, ip6_pkt_discard, &ip6_dst_ops}},
 	NULL, {{{0}}}, 256, RTF_REJECT|RTF_NONEXTHOP, ~0U,
-	0, 255, {NULL}, {{{{0}}}, 128}, {{{{0}}}, 128}
+	255, 0, {NULL}, {{{{0}}}, 0}, {{{{0}}}, 0}
 };
 
 struct fib6_node ip6_routing_table = {
 	NULL, NULL, NULL, NULL,
 	&ip6_null_entry,
-	0, RTN_ROOT|RTN_TL_ROOT, 0
+	0, RTN_ROOT|RTN_TL_ROOT|RTN_RTINFO, 0
 };
 
 #ifdef CONFIG_RT6_POLICY
@@ -709,14 +709,14 @@
 	if (rt == NULL) {
 		RDBG(("dalloc fails, "));
 		*err = -ENOMEM;
-		goto out;
+		return NULL;
 	}
 
 	rt->u.dst.obsolete = -1;
 	rt->rt6i_expires = rtmsg->rtmsg_info;
 
 	addr_type = ipv6_addr_type(&rtmsg->rtmsg_dst);
-	
+
 	if (addr_type & IPV6_ADDR_MULTICAST) {
 		RDBG(("MCAST, "));
 		rt->u.dst.input = ip6_mc_input;
@@ -743,6 +743,21 @@
 	rt->rt6i_src.plen = rtmsg->rtmsg_src_len;
 	ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);
 
+	/* We cannot add true routes via loopback here,
+	   they would result in kernel looping; promote them to reject routes
+	 */
+	if ((rtmsg->rtmsg_flags&RTF_REJECT) ||
+	    (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) {
+		dev = dev_get("lo");
+		rt->u.dst.output = ip6_pkt_discard;
+		rt->u.dst.input = ip6_pkt_discard;
+		rt->u.dst.error = -ENETUNREACH;
+		rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP;
+		rt->rt6i_metric = rtmsg->rtmsg_metric;
+		rt->rt6i_dev = dev;
+		goto install_route;
+	}
+
 	if (rtmsg->rtmsg_flags & RTF_GATEWAY) {
 		struct in6_addr *gw_addr;
 		int gwa_type;
@@ -773,7 +788,7 @@
 			}
 			dev = grt->rt6i_dev;
 		}
-		if (dev == NULL) {
+		if (dev == NULL || (dev->flags&IFF_LOOPBACK)) {
 			*err = -EINVAL;
 			goto out;
 		}
@@ -805,6 +820,7 @@
 		rt->rt6i_hoplimit = ipv6_get_hoplimit(dev);
 	rt->rt6i_flags = rtmsg->rtmsg_flags;
 
+install_route:
 	RDBG(("rt6ins(%p) ", rt));
 
 	rt6_lock();
@@ -1421,6 +1437,7 @@
 int ip6_pkt_discard(struct sk_buff *skb)
 {	
 	ipv6_statistics.Ip6OutNoRoutes++;
+	icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev);
 	kfree_skb(skb);
 	return 0;
 }
@@ -1671,7 +1688,8 @@
 	rtmsg->rtmsg_dst_len = r->rtm_dst_len;
 	rtmsg->rtmsg_src_len = r->rtm_src_len;
 	rtmsg->rtmsg_flags = RTF_UP;
-	rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
+	if (r->rtm_type == RTN_UNREACHABLE)
+		rtmsg->rtmsg_flags |= RTF_REJECT;
 
 	if (rta[RTA_GATEWAY-1]) {
 		if (rta[RTA_GATEWAY-1]->rta_len != RTA_LENGTH(16))
@@ -1754,7 +1772,12 @@
 	rtm->rtm_src_len = rt->rt6i_src.plen;
 	rtm->rtm_tos = 0;
 	rtm->rtm_table = RT_TABLE_MAIN;
-	rtm->rtm_type = RTN_UNICAST;
+	if (rt->rt6i_flags&RTF_REJECT)
+		rtm->rtm_type = RTN_UNREACHABLE;
+	else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK))
+		rtm->rtm_type = RTN_LOCAL;
+	else
+		rtm->rtm_type = RTN_UNICAST;
 	rtm->rtm_flags = 0;
 	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
 #ifdef CONFIG_RTNL_OLD_IFINFO
@@ -1795,6 +1818,8 @@
 	if (rt->u.dst.rtt)
 		RTA_PUT(skb, RTAX_RTT, sizeof(unsigned), &rt->u.dst.rtt);
 	mx->rta_len = skb->tail - (u8*)mx;
+	if (mx->rta_len == RTA_LENGTH(0))
+		skb_trim(skb, (u8*)mx - skb->data);
 #endif
 	if (rt->u.dst.neighbour)
 		RTA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key);
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/sit.c linux/net/ipv6/sit.c
--- v2.1.89/linux/net/ipv6/sit.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/sit.c	Tue Mar 10 09:47:27 1998
@@ -6,7 +6,7 @@
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *	Alexey Kuznetsov	<kuznet@ms2.inr.ac.ru>
  *
- *	$Id: sit.c,v 1.24 1997/12/13 21:53:17 kuznet Exp $
+ *	$Id: sit.c,v 1.27 1998/03/08 05:56:57 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.89/linux/net/ipv6/tcp_ipv6.c	Thu Feb 12 20:56:15 1998
+++ linux/net/ipv6/tcp_ipv6.c	Mon Mar 16 10:36:01 1998
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.44 1997/12/13 21:53:18 kuznet Exp $
+ *	$Id: tcp_ipv6.c,v 1.60 1998/03/15 02:59:32 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -44,7 +44,6 @@
 
 #define ICMP_PARANOIA
 
-extern int sysctl_tcp_sack;
 extern int sysctl_tcp_timestamps;
 extern int sysctl_tcp_window_scaling;
 
@@ -86,62 +85,69 @@
 
 /* Grrr, addr_type already calculated by caller, but I don't want
  * to add some silly "cookie" argument to this method just for that.
+ * But it doesn't matter, the recalculation is in the rarest path
+ * this function ever takes.
  */
 static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
 {
-	struct sock *sk2;
-	int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
-	int retval = 0, sk_reuse = sk->reuse;
+	struct tcp_bind_bucket *tb;
+	int result = 0;
 
 	SOCKHASH_LOCK();
-	sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)];
-	for(; sk2 != NULL; sk2 = sk2->bind_next) {
-		if((sk2->num == snum) && (sk2 != sk)) {
-			unsigned char state = sk2->state;
-			int sk2_reuse = sk2->reuse;
-			if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
-				if((!sk2_reuse)			||
-				   (!sk_reuse)			||
-				   (state == TCP_LISTEN)) {
-					retval = 1;
-					break;
-				}
-			} else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-						 &sk2->net_pinfo.af_inet6.rcv_saddr)) {
-				if((!sk_reuse)			||
-				   (!sk2_reuse)			||
-				   (state == TCP_LISTEN)) {
-					retval = 1;
-					break;
+	for(tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	    (tb && (tb->port != snum));
+	    tb = tb->next)
+		;
+	if(tb && tb->owners) {
+		/* Fast path for reuse ports, see include/net/tcp.h for a very
+		 * detailed description of why this works, and why it is worth
+		 * the effort at all. -DaveM
+		 */
+		if((tb->flags & TCPB_FLAG_FASTREUSE)	&&
+		   (sk->reuse != 0)) {
+			goto go_like_smoke;
+		} else {
+			struct sock *sk2;
+			int sk_reuse = sk->reuse;
+			int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+
+			/* We must walk the whole port owner list in this case. -DaveM */
+			for(sk2 = tb->owners; sk2; sk2 = sk2->bind_next) {
+				if(!sk_reuse || !sk2->reuse || sk2->state == TCP_LISTEN) {
+					if(addr_type == IPV6_ADDR_ANY	||
+					   !sk2->rcv_saddr		||
+					   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+							  &sk2->net_pinfo.af_inet6.rcv_saddr))
+						break;
 				}
 			}
+			if(sk2 != NULL)
+				result = 1;
 		}
 	}
+	if((result == 0) &&
+	   (tb == NULL) &&
+	   (tcp_bucket_create(snum) == NULL))
+		result = 1;
+go_like_smoke:
 	SOCKHASH_UNLOCK();
-
-	return retval;
+	return result;
 }
 
 static void tcp_v6_hash(struct sock *sk)
 {
-	unsigned char state;
-
-	SOCKHASH_LOCK();
-	state = sk->state;
-	if(state != TCP_CLOSE) {
+	if(sk->state != TCP_CLOSE) {
 		struct sock **skp;
 
-		if(state == TCP_LISTEN)
-			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
-		else
-			skp = &tcp_established_hash[tcp_v6_sk_hashfn(sk)];
+		SOCKHASH_LOCK();
+		skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
 		*skp = sk;
 		sk->pprev = skp;
 		tcp_sk_bindify(sk);
+		SOCKHASH_UNLOCK();
 	}
-	SOCKHASH_UNLOCK();
 }
 
 static void tcp_v6_unhash(struct sock *sk)
@@ -153,6 +159,7 @@
 		*sk->pprev = sk->next;
 		sk->pprev = NULL;
 		tcp_sk_unbindify(sk);
+		tcp_reg_zap(sk);
 	}
 	SOCKHASH_UNLOCK();
 }
@@ -163,29 +170,27 @@
 
 	SOCKHASH_LOCK();
 	state = sk->state;
-	if(sk->pprev) {
+	if(sk->pprev != NULL) {
 		if(sk->next)
 			sk->next->pprev = sk->pprev;
 		*sk->pprev = sk->next;
 		sk->pprev = NULL;
-		tcp_sk_unbindify(sk);
+		tcp_reg_zap(sk);
 	}
 	if(state != TCP_CLOSE) {
 		struct sock **skp;
 
-		if(state == TCP_LISTEN) {
+		if(state == TCP_LISTEN)
 			skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
-		} else {
-			int hash = tcp_v6_sk_hashfn(sk);
-			if(state == TCP_TIME_WAIT)
-				hash += (TCP_HTABLE_SIZE/2);
-			skp = &tcp_established_hash[hash];
-		}
+		else
+			skp = &tcp_established_hash[(sk->hashent = tcp_v6_sk_hashfn(sk))];
+
 		if((sk->next = *skp) != NULL)
 			(*skp)->pprev = &sk->next;
 		*skp = sk;
 		sk->pprev = skp;
-		tcp_sk_bindify(sk);
+		if(state == TCP_LISTEN)
+			tcp_sk_bindify(sk);
 	}
 	SOCKHASH_UNLOCK();
 }
@@ -209,8 +214,12 @@
 	return result;
 }
 
+/* Until this is verified... -DaveM */
+/* #define USE_QUICKSYNS */
+
 /* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
  * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ * It is assumed that this code only gets called from within NET_BH.
  */
 static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
 					   struct in6_addr *saddr, u16 sport,
@@ -218,30 +227,53 @@
 {
 	unsigned short hnum = ntohs(dport);
 	struct sock *sk;
-	int hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
+	int hash;
+
+#ifdef USE_QUICKSYNS
+	/* Incomming connection short-cut. */
+	if (th && th->syn == 1 && th->ack == 0)
+		goto listener_shortcut;
+#endif
+
+	/* Check TCP register quick cache first. */
+	sk = TCP_RHASH(sport);
+	if(sk						&&
+	   sk->num		== hnum			&& /* local port     */
+	   sk->family		== AF_INET6		&& /* address family */
+	   sk->dummy_th.dest	== sport		&& /* remote port    */
+	   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
+	   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+		goto hit;
 
 	/* Optimize here for direct hit, only listening connections can
-	 * have wildcards anyways.  It is assumed that this code only
-	 * gets called from within NET_BH.
+	 * have wildcards anyways.
 	 */
-	for(sk = tcp_established_hash[hash]; sk; sk = sk->next)
+	hash = tcp_v6_hashfn(daddr, hnum, saddr, sport);
+	for(sk = tcp_established_hash[hash]; sk; sk = sk->next) {
 		/* For IPV6 do the cheaper port and family tests first. */
 		if(sk->num		== hnum			&& /* local port     */
 		   sk->family		== AF_INET6		&& /* address family */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
-		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr)) {
+			if (sk->state == TCP_ESTABLISHED)
+				TCP_RHASH(sport) = sk;
 			goto hit; /* You sunk my battleship! */
-
+		}
+	}
 	/* Must check for a TIME_WAIT'er before going to listener hash. */
 	for(sk = tcp_established_hash[hash+(TCP_HTABLE_SIZE/2)]; sk; sk = sk->next)
 		if(sk->num		== hnum			&& /* local port     */
 		   sk->family		== AF_INET6		&& /* address family */
-		   sk->dummy_th.dest	== sport		&& /* remote port    */
-		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
-		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
-			goto hit;
-
+		   sk->dummy_th.dest	== sport) {		   /* remote port    */
+			struct tcp_tw_bucket *tw = (struct tcp_tw_bucket *)sk;
+			if(!ipv6_addr_cmp(&tw->v6_daddr, saddr)	&&
+			   !ipv6_addr_cmp(&tw->v6_rcv_saddr, daddr))
+				goto hit;
+		}
+#ifdef USE_QUICKSYNS
+listener_shortcut:
+#endif
 	sk = tcp_v6_lookup_listener(daddr, hnum);
 hit:
 	return sk;
@@ -275,6 +307,33 @@
 					  skb->h.th->source);
 }
 
+static int tcp_v6_unique_address(struct sock *sk)
+{
+	struct tcp_bind_bucket *tb;
+	unsigned short snum = sk->num;
+	int retval = 1;
+
+	/* Freeze the hash while we snoop around. */
+	SOCKHASH_LOCK();
+	tb = tcp_bound_hash[tcp_bhashfn(snum)];
+	for(; tb; tb = tb->next) {
+		if(tb->port == snum && tb->owners != NULL) {
+			/* Almost certainly the re-use port case, search the real hashes
+			 * so it actually scales.  (we hope that all ipv6 ftp servers will
+			 * use passive ftp, I just cover this case for completeness)
+			 */
+			sk = __tcp_v6_lookup(NULL, &sk->net_pinfo.af_inet6.daddr,
+					     sk->dummy_th.dest,
+					     &sk->net_pinfo.af_inet6.rcv_saddr, snum);
+			if((sk != NULL) && (sk->state != TCP_LISTEN))
+				retval = 0;
+			break;
+		}
+	}
+	SOCKHASH_UNLOCK();
+	return retval;
+}
+
 static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, 
 			  int addr_len)
 {
@@ -390,7 +449,9 @@
 		ipv6_addr_copy(&np->saddr, saddr);
 	}
 
-	/* FIXME: Need to do tcp_v6_unique_address() here! -DaveM */
+	sk->dummy_th.dest = usin->sin6_port;
+	if (!tcp_v6_unique_address(sk))
+		return -EADDRNOTAVAIL;
 
 	/*
 	 *	Init variables
@@ -398,16 +459,15 @@
 
 	lock_sock(sk);
 
-	sk->dummy_th.dest = usin->sin6_port;
-	sk->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
+	tp->write_seq = secure_tcp_sequence_number(np->saddr.s6_addr32[3],
 						   np->daddr.s6_addr32[3],
 						   sk->dummy_th.source,
 						   sk->dummy_th.dest);
 
 	tp->snd_wnd = 0;
 	tp->snd_wl1 = 0;
-	tp->snd_wl2 = sk->write_seq;
-	tp->snd_una = sk->write_seq;
+	tp->snd_wl2 = tp->write_seq;
+	tp->snd_una = tp->write_seq;
 
 	tp->rcv_nxt = 0;
 
@@ -415,30 +475,35 @@
 
 	release_sock(sk);
 
-	buff = sock_wmalloc(sk, MAX_SYN_SIZE, 0, GFP_KERNEL);	
-
-	if (buff == NULL) 
+	buff = sock_wmalloc(sk, (MAX_SYN_SIZE + sizeof(struct sk_buff)),
+			    0, GFP_KERNEL);
+	if (buff == NULL) {
+		/* FIXME: Free route references etc??? */
 		return(-ENOMEM);
+	}
 
 	lock_sock(sk);
 
 	tcp_v6_build_header(sk, buff);
 
+	tp->tcp_header_len = sizeof(struct tcphdr) +
+		(sysctl_tcp_timestamps ? TCPOLEN_TSTAMP_ALIGNED : 0);
+
 	/* build the tcp header */
 	th = (struct tcphdr *) skb_put(buff,sizeof(struct tcphdr));
 	buff->h.th = th;
 
 	memcpy(th, (void *) &(sk->dummy_th), sizeof(*th));
-	buff->seq = sk->write_seq++;
+	buff->seq = tp->write_seq++;
 	th->seq = htonl(buff->seq);
-	tp->snd_nxt = sk->write_seq;
-	buff->end_seq = sk->write_seq;
+	tp->snd_nxt = tp->write_seq;
+	buff->end_seq = tp->write_seq;
 	th->ack = 0;
 	th->syn = 1;
 
 
 	sk->mtu = dst->pmtu;
-	sk->mss = sk->mtu - sizeof(struct ipv6hdr) - sizeof(struct tcphdr);
+	sk->mss = (sk->mtu - sizeof(struct ipv6hdr) - tp->tcp_header_len);
 
         if (sk->mss < 1) {
                 printk(KERN_DEBUG "intial ipv6 sk->mss below 1\n");
@@ -457,8 +522,7 @@
 	 *	Put in the TCP options to say MTU.
 	 */
 
-        tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_sack,
-                sysctl_tcp_timestamps,
+        tmp = tcp_syn_build_options(buff, sk->mss, sysctl_tcp_timestamps,
                 sysctl_tcp_window_scaling,tp->rcv_wscale);
         th->doff = sizeof(*th)/4 + (tmp>>2);
 	buff->csum = 0;
@@ -467,9 +531,10 @@
 	tcp_set_state(sk, TCP_SYN_SENT);
 
 	/* Socket identity change complete, no longer
-	 * in TCP_CLOSE, so rehash.
+	 * in TCP_CLOSE, so enter ourselves into the
+	 * hash tables.
 	 */
-	sk->prot->rehash(sk);
+	sk->prot->hash(sk);
 
 	/* FIXME: should use dcache->rtt if availiable */
 	tp->rto = TCP_TIMEOUT_INIT;
@@ -482,12 +547,12 @@
 	tp->packets_out++;
 	buff->when = jiffies;
 	skb1 = skb_clone(buff, GFP_KERNEL);
-	skb_set_owner_w(skb1, sk);
-
-	tcp_v6_xmit(skb1);
+	if(skb1 != NULL) {
+		skb_set_owner_w(skb1, sk);
+		tcp_v6_xmit(skb1);
+	}
 
 	/* Timer for repeating the SYN until an answer  */
-
 	tcp_reset_xmit_timer(sk, TIME_RETRANS, tp->rto);
 	tcp_statistics.TcpActiveOpens++;
 	tcp_statistics.TcpOutSegs++;
@@ -499,6 +564,7 @@
 
 static int tcp_v6_sendmsg(struct sock *sk, struct msghdr *msg, int len)
 {
+	struct tcp_opt *tp;
 	struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
 	int retval = -EINVAL;
 
@@ -530,7 +596,10 @@
 	lock_sock(sk);
 	retval = tcp_do_sendmsg(sk, msg->msg_iovlen, msg->msg_iov, 
 				msg->msg_flags);
-
+	/* Push out partial tail frames if needed. */
+	tp = &(sk->tp_pinfo.af_tcp);
+	if(tp->send_head && tcp_snd_test(sk, tp->send_head))
+		tcp_write_xmit(sk);
 	release_sock(sk);
 
 out:
@@ -555,7 +624,7 @@
 
 	sk = tcp_v6_lookup(daddr, th->dest, saddr, th->source);
 
-	if (sk == NULL) {
+	if (sk == NULL || sk->state == TCP_TIME_WAIT) {
 		/* XXX: Update ICMP error count */
 		return;
 	}
@@ -596,11 +665,14 @@
 			ip6_dst_store(sk, dst);
 		}
 
-		if (sk->dst_cache->error)
+		if (sk->dst_cache->error) {
 			sk->err_soft = sk->dst_cache->error;
-		else
+		} else {
+			/* FIXME: Reset sk->mss, taking into account TCP option
+			 *        bytes for timestamps. -DaveM
+			 */
 			sk->mtu = sk->dst_cache->pmtu;
-
+		}
 		if (sk->sock_readers) { /* remove later */
 			printk(KERN_DEBUG "tcp_v6_err: pmtu disc: socket locked.\n");
 			return;
@@ -713,11 +785,10 @@
 	 * match what happens under IPV4. Figure out the right thing to do.
 	 */
         req->mss = min(sk->mss, req->mss);
-
-        if (req->mss < 1) {
-                printk(KERN_DEBUG "initial req->mss below 1\n");
-                req->mss = 1;
-        }
+	if(sk->user_mss)
+		req->mss = min(req->mss, sk->user_mss);
+	if(req->tstamp_ok == 0)
+		req->mss += TCPOLEN_TSTAMP_ALIGNED;
 
 	if (req->rcv_wnd == 0) {
 		__u8 rcv_wscale;
@@ -732,7 +803,7 @@
 	}
 	th->window = htons(req->rcv_wnd);
 
-	tmp = tcp_syn_build_options(skb, req->mss, req->sack_ok, req->tstamp_ok,
+	tmp = tcp_syn_build_options(skb, req->mss, req->tstamp_ok,
 		req->wscale_ok,req->rcv_wscale);
 	skb->csum = 0;
 	th->doff = (sizeof(*th) + tmp)>>2;
@@ -740,9 +811,13 @@
 				 &req->af.v6_req.loc_addr, &req->af.v6_req.rmt_addr,
 				 csum_partial((char *)th, sizeof(*th)+tmp, skb->csum));
 
+	/* Actually we should not attach dst to socket in state LISTEN,
+	   it results in stale destination per listen socket and
+	   overflow of routing cache.
+	   (IPv4 has the same flaw with more unpleasant consequences.)
+	 */
 	ip6_dst_store(sk, dst);
 	ip6_xmit(sk, skb, &fl, req->af.v6_req.opt);
-	dst_release(dst);
 
 	tcp_statistics.TcpOutSegs++;
 }
@@ -801,14 +876,15 @@
 
 	req->rcv_isn = skb->seq;
 	req->snt_isn = isn;
-	tp.tstamp_ok = tp.sack_ok = tp.wscale_ok = tp.snd_wscale = 0;
+	tp.tstamp_ok = tp.wscale_ok = tp.snd_wscale = 0;
 	tp.in_mss = 536;
 	tcp_parse_options(skb->h.th,&tp,0);
-	if (tp.saw_tstamp)
-                req->ts_recent = tp.rcv_tsval;
         req->mss = tp.in_mss;
+	if (tp.saw_tstamp) {
+		req->mss -= TCPOLEN_TSTAMP_ALIGNED;
+                req->ts_recent = tp.rcv_tsval;
+	}
         req->tstamp_ok = tp.tstamp_ok;
-        req->sack_ok = tp.sack_ok;
         req->snd_wscale = tp.snd_wscale;
         req->wscale_ok = tp.wscale_ok;
 	req->rmt_port = skb->h.th->source;
@@ -879,92 +955,17 @@
 		return newsk;
 	}
 
-	newsk = sk_alloc(AF_INET6, GFP_ATOMIC);
+	newsk = tcp_create_openreq_child(sk, req, skb);
 	if (newsk == NULL) {
-	        if (dst)
-		    dst_release(dst);
+		dst_release(dst);
 		return NULL;
 	}
 
-	memcpy(newsk, sk, sizeof(*newsk));
-
-	/* Or else we die! -DaveM */
-	newsk->sklist_next = NULL;
-
-	newsk->opt = 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
-	 */
 
 	newtp = &(newsk->tp_pinfo.af_tcp);
-	np = &newsk->net_pinfo.af_inet6;
-
-	newtp->send_head = NULL;
-	newtp->retrans_head = NULL;
-
-	newtp->pending = 0;
-
-	skb_queue_head_init(&newsk->back_log);
-
-	newsk->prot->init(newsk);
-
-	newtp->snd_cwnd_cnt = 0;
-#if 0 /* Don't mess up the initialization we did in the init routine! */
-	newtp->snd_ssthresh = 0;
-#endif
-	newtp->backoff = 0;
-	newsk->proc = 0;
-	newsk->done = 0;
-	newsk->pair = NULL;
-	atomic_set(&newsk->wmem_alloc, 0);
-	atomic_set(&newsk->rmem_alloc, 0);
-	newsk->localroute = sk->localroute;
-
-	newsk->err = 0;
-	newsk->shutdown = 0;
-	newsk->ack_backlog = 0;
-
-	newtp->fin_seq = req->rcv_isn;
-	newsk->syn_seq = req->rcv_isn;
-	newsk->state = TCP_SYN_RECV;
-	newsk->timeout = 0;
-
-	newsk->write_seq = req->snt_isn;
-
-	newtp->snd_wnd = ntohs(skb->h.th->window);
-	newtp->max_window = newtp->snd_wnd;
-	newtp->snd_wl1 = req->rcv_isn;
-	newtp->snd_wl2 = newsk->write_seq;
-	newtp->snd_una = newsk->write_seq++;
-	newtp->snd_nxt = newsk->write_seq;
-
-	newsk->urg_data = 0;
-	newtp->packets_out = 0;
-	newtp->retransmits = 0;
-	newsk->linger=0;
-	newsk->destroy = 0;
-	init_timer(&newsk->timer);
-	newsk->timer.data = (unsigned long) newsk;
-	newsk->timer.function = &net_timer;
-
-	tcp_init_xmit_timers(newsk);
-
-	newsk->dummy_th.source = sk->dummy_th.source;
-	newsk->dummy_th.dest = req->rmt_port;
-	newsk->sock_readers=0;
-
-	newtp->rcv_nxt = req->rcv_isn + 1;
-	newtp->rcv_wup = req->rcv_isn + 1;
-	newsk->copied_seq = req->rcv_isn + 1;
-
-	newsk->socket = NULL;
 
+	np = &newsk->net_pinfo.af_inet6;
 	ipv6_addr_copy(&np->daddr, &req->af.v6_req.rmt_addr);
 	ipv6_addr_copy(&np->saddr, &req->af.v6_req.loc_addr);
 	ipv6_addr_copy(&np->rcv_saddr, &req->af.v6_req.loc_addr);
@@ -987,14 +988,22 @@
 
 	ip6_dst_store(newsk, dst);
 
-        newtp->sack_ok = req->sack_ok;
         newtp->tstamp_ok = req->tstamp_ok;
-        newtp->snd_wscale = req->snd_wscale;
+	newtp->window_clamp = req->window_clamp;
+	newtp->rcv_wnd = req->rcv_wnd;
 	newtp->wscale_ok = req->wscale_ok;
-        newtp->ts_recent = req->ts_recent;
+	if (newtp->wscale_ok) {
+		newtp->snd_wscale = req->snd_wscale;
+		newtp->rcv_wscale = req->rcv_wscale;
+	} else {
+		newtp->snd_wscale = newtp->rcv_wscale = 0;
+		newtp->window_clamp = min(newtp->window_clamp,65535);
+	}
         if (newtp->tstamp_ok) {
-                newtp->tcp_header_len = sizeof(struct tcphdr) + 12; /* FIXME: define the contant. */
-                newsk->dummy_th.doff += 3;
+		newtp->ts_recent = req->ts_recent;
+		newtp->ts_recent_stamp = jiffies;
+		newtp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;
+		newsk->dummy_th.doff += (TCPOLEN_TSTAMP_ALIGNED >> 2);
         } else {
                 newtp->tcp_header_len = sizeof(struct tcphdr);
         }
@@ -1006,7 +1015,6 @@
 
 	newsk->mss = min(req->mss+sizeof(struct tcphdr)-newtp->tcp_header_len,
 		(newsk->mtu - sizeof(struct ipv6hdr) - newtp->tcp_header_len));
-	/* XXX tp->window_clamp??? -DaveM */
 
 	newsk->daddr	= LOOPBACK4_IPV6;
 	newsk->saddr	= LOOPBACK4_IPV6;
@@ -1181,12 +1189,14 @@
 			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->used = 0;
+		if(sk->state == TCP_TIME_WAIT)
+			goto do_time_wait;
+
+		skb->sk = sk;
 	}
 
 	/*
@@ -1249,6 +1259,12 @@
 
 	kfree_skb(skb);
 	return 0;
+
+do_time_wait:
+	if(tcp_timewait_state_process((struct tcp_tw_bucket *)sk,
+				      skb, th, &(IPCB(skb)->opt), skb->len))
+		goto no_tcp_socket;
+	goto discard_it;
 }
 
 static int tcp_v6_rebuild_header(struct sock *sk, struct sk_buff *skb)
@@ -1384,51 +1400,34 @@
 	sizeof(struct sockaddr_in6)
 };
 
+/* NOTE: A lot of things set to zero explicitly by call to
+ *       sk_alloc() so need not be done here.
+ */
 static int tcp_v6_init_sock(struct sock *sk)
 {
 	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 
-	skb_queue_head_init(&sk->out_of_order_queue);
+	skb_queue_head_init(&tp->out_of_order_queue);
 	tcp_init_xmit_timers(sk);
 
-	tp->srtt  = 0;
 	tp->rto  = TCP_TIMEOUT_INIT;		/*TCP_WRITE_TIME*/
 	tp->mdev = TCP_TIMEOUT_INIT;
-
-	tp->ato = 0;
-	tp->iat = (HZ/5) << 3;
-	
-	/* FIXME: right thing? */
-	tp->rcv_wnd = 0;
 	tp->in_mss = 536;
-	/* tp->rcv_wnd = 8192; */
-	tp->tstamp_ok = 0;
-	tp->sack_ok = 0;
-	tp->wscale_ok = 0;
-	tp->snd_wscale = 0;
-	tp->sacks = 0;
-	tp->saw_tstamp = 0;
-	tp->syn_backlog = 0;
 
-	/* start with only sending one packet at a time. */
+	/* See draft-stevens-tcpca-spec-01 for discussion of the
+	 * initialization of these values.
+	 */
 	tp->snd_cwnd = 1;
 	tp->snd_ssthresh = 0x7fffffff;
 
-
-
 	sk->priority = 1;
 	sk->state = TCP_CLOSE;
-
 	sk->max_ack_backlog = SOMAXCONN;
-
 	sk->mtu = 576;
 	sk->mss = 536;
-
 	sk->dummy_th.doff = sizeof(sk->dummy_th)/4;
 
-	/*
-	 *	Speed up by setting some standard state for the dummy_th.
-	 */
+	/* Speed up by setting some standard state for the dummy_th. */
   	sk->dummy_th.ack=1;
   	sk->dummy_th.doff=sizeof(struct tcphdr)>>2;
 
@@ -1442,6 +1441,7 @@
 
 static int tcp_v6_destroy_sock(struct sock *sk)
 {
+	struct tcp_opt *tp = &(sk->tp_pinfo.af_tcp);
 	struct sk_buff *skb;
 
 	tcp_clear_xmit_timers(sk);
@@ -1460,15 +1460,22 @@
 	 *  Cleans up our, hopefuly empty, out_of_order_queue
 	 */
 
-  	while((skb = skb_dequeue(&sk->out_of_order_queue)) != NULL)
+  	while((skb = skb_dequeue(&tp->out_of_order_queue)) != NULL)
 		kfree_skb(skb);
 
 	/*
 	 *	Release destination entry
 	 */
 
-	dst_release(sk->dst_cache);
-	sk->dst_cache = NULL;
+	dst_release(xchg(&sk->dst_cache,NULL));
+
+	/* Clean up a locked TCP bind bucket, this only happens if a
+	 * port is allocated for a socket, but it never fully connects.
+	 * In which case we will find num to be non-zero and daddr to
+	 * be zero.
+	 */
+	if(ipv6_addr_any(&(sk->net_pinfo.af_inet6.daddr)) && sk->num != 0)
+		tcp_bucket_unlock(sk);
 
 	return 0;
 }
diff -u --recursive --new-file v2.1.89/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.89/linux/net/ipv6/udp.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipv6/udp.c	Thu Mar 12 10:32:04 1998
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.21 1997/12/29 19:52:52 kuznet Exp $
+ *	$Id: udp.c,v 1.24 1998/03/12 03:20:21 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -448,32 +448,43 @@
 	return NULL;
 }
 
+/*
+ * Note: called only from the BH handler context,
+ * so we don't need to lock the hashes.
+ */
 static void udpv6_mcast_deliver(struct udphdr *uh,
 				struct in6_addr *saddr, struct in6_addr *daddr,
 				struct sk_buff *skb)
 {
 	struct sock *sk, *sk2;
+	struct sk_buff *buff;
 
-	SOCKHASH_LOCK();
 	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
 	sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
-	if(sk) {
-		sk2 = sk;
-		while((sk2 = udp_v6_mcast_next(sk2->next,
-					       uh->dest, saddr,
-					       uh->source, daddr))) {
-			struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
-			if (buff && sock_queue_rcv_skb(sk2, buff) < 0) {
-				buff->sk = NULL;
-				kfree_skb(buff);
-			}
+	if (!sk)
+		goto free_skb;
+
+	buff = NULL;
+	sk2 = sk;
+	while((sk2 = udp_v6_mcast_next(sk2->next, uh->dest, saddr,
+						  uh->source, daddr))) {
+		if (!buff) {
+			buff = skb_clone(skb, GFP_ATOMIC);
+			if (!buff)
+				continue;
 		}
+		if (sock_queue_rcv_skb(sk2, buff) >= 0)
+			buff = NULL;
 	}
-	if(!sk || sock_queue_rcv_skb(sk, skb) < 0) {
+	if (buff) {
+		buff->sk = NULL;
+		kfree_skb(buff);
+	}
+	if (sock_queue_rcv_skb(sk, skb) < 0) {
+	free_skb:
 		skb->sk = NULL;
 		kfree_skb(skb);
 	}
-	SOCKHASH_UNLOCK();
 }
 
 int udpv6_rcv(struct sk_buff *skb, struct device *dev,
diff -u --recursive --new-file v2.1.89/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.1.89/linux/net/ipx/af_ipx.c	Tue Mar 10 10:03:37 1998
+++ linux/net/ipx/af_ipx.c	Tue Mar 10 09:47:28 1998
@@ -1713,7 +1713,7 @@
 static int ipx_create(struct socket *sock, int protocol)
 {
 	struct sock *sk;
-	sk=sk_alloc(AF_IPX, GFP_KERNEL);
+	sk=sk_alloc(AF_IPX, GFP_KERNEL, 1);
 	if(sk==NULL)
 		return(-ENOMEM);
 	switch(sock->type)
diff -u --recursive --new-file v2.1.89/linux/net/netbeui/af_netbeui.c linux/net/netbeui/af_netbeui.c
--- v2.1.89/linux/net/netbeui/af_netbeui.c	Thu Feb 12 20:56:15 1998
+++ linux/net/netbeui/af_netbeui.c	Tue Mar 10 09:47:28 1998
@@ -150,7 +150,7 @@
 static int netbeui_create(struct socket *sock, int protocol)
 {
 	netbeui_socket *sk;
-	sk=(netbeui_socket *)sk_alloc(GFP_KERNEL);
+	sk=(netbeui_socket *)sk_alloc(GFP_KERNEL, 1);
 	if(sk==NULL)
 		return(-ENOBUFS);
 	switch(sock->type)
diff -u --recursive --new-file v2.1.89/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- v2.1.89/linux/net/netlink/af_netlink.c	Tue Mar 10 10:03:37 1998
+++ linux/net/netlink/af_netlink.c	Tue Mar 10 09:47:28 1998
@@ -147,7 +147,7 @@
 
 	sock->ops = &netlink_ops;
 
-	sk = sk_alloc(AF_NETLINK, GFP_KERNEL);
+	sk = sk_alloc(AF_NETLINK, GFP_KERNEL, 1);
 	if (!sk)
 		return -ENOMEM;
 
diff -u --recursive --new-file v2.1.89/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v2.1.89/linux/net/netrom/af_netrom.c	Thu Feb 12 20:56:15 1998
+++ linux/net/netrom/af_netrom.c	Thu Mar 12 10:54:12 1998
@@ -98,7 +98,7 @@
 	struct sock *sk;
 	nr_cb *nr;
 
-	if ((sk = sk_alloc(AF_NETROM, GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_NETROM, GFP_ATOMIC, 1)) == NULL)
 		return NULL;
 
 	if ((nr = kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
@@ -759,6 +759,8 @@
 
 	newsk = skb->sk;
 	newsk->pair = NULL;
+	newsk->socket = newsock;
+	newsk->sleep = &newsock->wait;
 	sti();
 
 	/* Now attach up the new socket */
diff -u --recursive --new-file v2.1.89/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.89/linux/net/netsyms.c	Tue Mar 10 10:03:37 1998
+++ linux/net/netsyms.c	Sat Mar 14 11:37:30 1998
@@ -244,7 +244,6 @@
 EXPORT_SYMBOL(__release_sock);
 EXPORT_SYMBOL(net_timer);
 /* UDP/TCP exported functions for TCPv6 */
-EXPORT_SYMBOL(sysctl_tcp_sack);
 EXPORT_SYMBOL(sysctl_tcp_timestamps);
 EXPORT_SYMBOL(sysctl_tcp_window_scaling);
 EXPORT_SYMBOL(sock_rspace);
@@ -272,11 +271,15 @@
 EXPORT_SYMBOL(__tcp_inc_slow_timer);
 EXPORT_SYMBOL(tcp_statistics);
 EXPORT_SYMBOL(tcp_rcv_state_process);
+EXPORT_SYMBOL(tcp_timewait_state_process);
 EXPORT_SYMBOL(tcp_do_sendmsg);
 EXPORT_SYMBOL(tcp_v4_build_header);
 EXPORT_SYMBOL(tcp_v4_rebuild_header);
 EXPORT_SYMBOL(tcp_v4_send_check);
 EXPORT_SYMBOL(tcp_v4_conn_request);
+EXPORT_SYMBOL(tcp_create_openreq_child);
+EXPORT_SYMBOL(tcp_bucket_create);
+EXPORT_SYMBOL(tcp_bucket_unlock);
 EXPORT_SYMBOL(tcp_v4_syn_recv_sock);
 EXPORT_SYMBOL(tcp_v4_do_rcv);
 EXPORT_SYMBOL(tcp_v4_connect);
@@ -290,6 +293,11 @@
 EXPORT_SYMBOL(tcp_simple_retransmit);
 
 EXPORT_SYMBOL(xrlim_allow);
+
+EXPORT_SYMBOL(tcp_write_xmit);
+EXPORT_SYMBOL(dev_loopback_xmit);
+EXPORT_SYMBOL(tcp_regs);
+
 #endif
 
 #ifdef CONFIG_NETLINK
diff -u --recursive --new-file v2.1.89/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
--- v2.1.89/linux/net/packet/af_packet.c	Tue Mar 10 10:03:37 1998
+++ linux/net/packet/af_packet.c	Thu Mar 12 10:32:04 1998
@@ -35,6 +35,7 @@
  *		Alan Cox	:	sendmsg/recvmsg support.
  *		Alan Cox	:	Protocol setting support
  *	Alexey Kuznetsov	:	Untied from IPv4 stack.
+ *	Cyrus Durgin		:	Fixed kerneld for kmod.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -54,7 +55,7 @@
 #include <linux/netdevice.h>
 #include <linux/if_packet.h>
 #include <linux/wireless.h>
-#include <linux/kerneld.h>
+#include <linux/kmod.h>
 #include <net/ip.h>
 #include <net/protocol.h>
 #include <linux/skbuff.h>
@@ -710,7 +711,7 @@
 
 	sock->state = SS_UNCONNECTED;
 	MOD_INC_USE_COUNT;
-	sk = sk_alloc(AF_PACKET, GFP_KERNEL);
+	sk = sk_alloc(AF_PACKET, GFP_KERNEL, 1);
 	if (sk == NULL) {
 		MOD_DEC_USE_COUNT;
 		return -ENOBUFS;
@@ -831,9 +832,10 @@
 
 	/* We can't use skb_copy_datagram here */
 	err = memcpy_toiovec(msg->msg_iov, skb->data, copied);
-	if (err)
+	if (err) {
+		err = -EFAULT;
 		goto out_free;
-
+	}
 	sk->stamp=skb->stamp;
 
 	if (msg->msg_name)
diff -u --recursive --new-file v2.1.89/linux/net/rose/af_rose.c linux/net/rose/af_rose.c
--- v2.1.89/linux/net/rose/af_rose.c	Thu Feb 12 20:56:15 1998
+++ linux/net/rose/af_rose.c	Thu Mar 12 11:00:25 1998
@@ -149,7 +149,7 @@
 	struct sock *sk;
 	rose_cb *rose;
 
-	if ((sk = sk_alloc(AF_ROSE, GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_ROSE, GFP_ATOMIC, 1)) == NULL)
 		return NULL;
 
 	if ((rose = kmalloc(sizeof(*rose), GFP_ATOMIC)) == NULL) {
@@ -847,6 +847,8 @@
 
 	newsk = skb->sk;
 	newsk->pair = NULL;
+	newsk->socket = newsock;
+	newsk->sleep = &newsock->wait;
 	sti();
 
 	/* Now attach up the new socket */
diff -u --recursive --new-file v2.1.89/linux/net/socket.c linux/net/socket.c
--- v2.1.89/linux/net/socket.c	Tue Mar 10 10:03:37 1998
+++ linux/net/socket.c	Tue Mar 10 14:49:26 1998
@@ -76,8 +76,8 @@
 #include <linux/init.h>
 #include <linux/poll.h>
 
-#if defined(CONFIG_KERNELD) && defined(CONFIG_NET)
-#include <linux/kerneld.h>
+#if defined(CONFIG_KMOD) && defined(CONFIG_NET)
+#include <linux/kmod.h>
 #endif
 
 #include <asm/system.h>
@@ -577,7 +577,7 @@
 	if(family<0||family>=NPROTO)
 		return -EINVAL;
 		
-#if defined(CONFIG_KERNELD) && defined(CONFIG_NET)
+#if defined(CONFIG_KMOD) && defined(CONFIG_NET)
 	/* Attempt to load a protocol module if the find failed. 
 	 * 
 	 * 12/09/1996 Marcin: But! this makes REALLY only sense, if the user 
@@ -814,7 +814,7 @@
 	newsock = socki_lookup(inode);
 
 	if ((err = get_fd(inode)) < 0) 
-		goto out_inval;
+		goto out_release;
 	newsock->file = current->files->fd[err];
 
 	if (upeer_sockaddr)
@@ -835,8 +835,6 @@
 	unlock_kernel();
 	return err;
 
-out_inval:
-	err = -EINVAL;
 out_release:
 	sock_release(newsock);
 	goto out_put;
diff -u --recursive --new-file v2.1.89/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v2.1.89/linux/net/unix/af_unix.c	Tue Mar 10 10:03:37 1998
+++ linux/net/unix/af_unix.c	Thu Mar 12 10:32:04 1998
@@ -364,7 +364,7 @@
 		default:
 			return -ESOCKTNOSUPPORT;
 	}
-	sk = sk_alloc(AF_UNIX, GFP_KERNEL);
+	sk = sk_alloc(AF_UNIX, GFP_KERNEL, 1);
 	if (!sk)
 		return -ENOMEM;
 
@@ -1265,7 +1265,9 @@
 		}
 
 		chunk = min(skb->len, size);
-		/* N.B. This could fail with -EFAULT */
+		/* N.B. This could fail with a non-zero value (which means -EFAULT
+		 *      and the non-zero value is the number of bytes not copied).
+		 */
 		memcpy_toiovec(msg->msg_iov, skb->data, chunk);
 		copied += chunk;
 		size -= chunk;
diff -u --recursive --new-file v2.1.89/linux/net/x25/af_x25.c linux/net/x25/af_x25.c
--- v2.1.89/linux/net/x25/af_x25.c	Tue Mar 10 10:03:37 1998
+++ linux/net/x25/af_x25.c	Tue Mar 10 09:47:28 1998
@@ -423,7 +423,7 @@
 	struct sock *sk;
 	x25_cb *x25;
 
-	if ((sk = sk_alloc(AF_X25, GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(AF_X25, GFP_ATOMIC, 1)) == NULL)
 		return NULL;
 
 	if ((x25 = kmalloc(sizeof(*x25), GFP_ATOMIC)) == NULL) {
