diff -u --recursive --new-file v1.3.81/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.81/linux/Documentation/Configure.help	Sun Mar 31 00:13:16 1996
+++ linux/Documentation/Configure.help	Tue Apr  2 08:43:04 1996
@@ -642,13 +642,13 @@
 
 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
-  use it.  If you select Y here, the kernel will take care of this
-  all by itself, together with a user level daemon; "kerneld".
-  Note that "kerneld" will also automatically unload all unused
-  modules, so you don't have to use "rmmod" either.
+  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.
   There are some other "kernel callouts" that will be available
   later on, such as a user level "beeper" and a generic screen blanker.
   The "kerneld" daemon is included in "modules-1.2.8" and later.
@@ -882,7 +882,10 @@
   over the net. "Path MTU Discovery" means that, instead of always
   sending very small chunks, we start out sending big ones and if we
   then discover that some host along the way likes its chunks smaller,
-  we adjust to a smaller size. This is good, so say N.
+  we adjust to a smaller size. This is good, so most people say
+  N. However, some versions of DOS NCSA telnet (and other software)
+  are broken and can only connect to your Linux machine if you say Y
+  here.
 
 Disable NAGLE algorithm (normally enabled)
 CONFIG_TCP_NAGLE_OFF
@@ -1386,18 +1389,20 @@
 
 IOMEGA Parallel Port ZIP drive SCSI support
 CONFIG_SCSI_PPA
-  This driver supports the parallel port version of IOMEGA's ZIP
-  drive (a 100Mb removable media device).  For more information 
-  about this driver and how to use it you should read the file
-  drivers/scsi/README.ppa.  You should also read the SCSI-HOWTO,
-  which is available via anonymous ftp from sunsite.unc.edu in
-  the directory /pub/Linux/docs/HOWTO.  This driver is also available
-  as a module which can be inserted in and removed from the
-  running kernel whenever you want. If you want to compile it as a
-  module, say M here and read Documentation/modules.txt. Note that 
-  you can say N here if you have the SCSI version of the ZIP drive: 
-  it will be supported automatically if you enabled the generic
-  "SCSI disk support", above.
+  This driver supports the parallel port version of IOMEGA's ZIP drive
+  (a 100Mb removable media device).  For more information about this
+  driver and how to use it you should read the file
+  drivers/scsi/README.ppa.  You should also read the SCSI-HOWTO, which
+  is available via anonymous ftp from sunsite.unc.edu in the directory
+  /pub/Linux/docs/HOWTO.  This driver is also available as a module
+  which can be inserted in and removed from the running kernel
+  whenever you want. If you want to use any two of a parallel port ZIP
+  drive, a parallel printer or PLIP on the same parallel port, you
+  should compile the drivers as modules and only insert them as
+  needed. To compile this driver as a module, say M here and read
+  Documentation/modules.txt. Note that you can say N here if you have
+  the SCSI version of the ZIP drive: it will be supported
+  automatically if you enabled the generic "SCSI disk support", above.
 
 Network device support?
 CONFIG_NETDEVICES
@@ -1523,6 +1528,21 @@
   running in parallel. This is mainly useful if you intend your linux
   box to act as a dial-in PPP server. Most people can say N.
 
+STRIP (Starmode Radio IP) support
+CONFIG_STRIP
+  Say Y if you have a Metricom radio and intend to use Starmode Radio IP.
+  STRIP is a radio protocol developed for the MosquitoNet project
+  (http://mosquitonet.stanford.edu/) to send Internet traffic using Metricom
+  radios.  Metricom radios are small, battery powered, 100kbit/sec packet
+  radio transceivers, about the size and weight of a cellular telephone.
+  (You may also have heard them called "Metricom modems" but we avoid the
+  term "modem" because it misleads many people into thinking that you can
+  plug a Metricom modem into a phone line and use it as a modem.)
+  You can use STRIP on any Linux machine with a serial port, although
+  it is obviously most useful for people with laptop computers. If you
+  think you might get a Metricom radio in the future, there is no harm in
+  saying yes to STRIP now, except that it makes the kernel a bit bigger.
+
 Z8530 SCC kiss emulation driver for AX.2
 CONFIG_SCC
   These cards are used to connect your Linux box to an amateur radio
@@ -1573,14 +1593,15 @@
   from the running kernel whenever you want). If you want to compile
   it as a module, say M here and read Documentation/modules.txt.
 
-FRAD (Frame Relay Access Device) support
-CONFIG_FRAD
+Frame Relay (DLCI) support
+CONFIG_DLCI
   This is support for the frame relay protocol; frame relay is a fast
   low-cost way to connect to a remote internet access provider or to
-  form a private wide area network. The one physical line to your
-  local entry point or "switch" carries several logical
-  point-to-point connections to other sites connected to the frame
-  relay network. For a general explanation of the protocol, check out
+  form a private wide area network. The one physical line from your
+  box to the local "switch" (i.e. the entry point to the frame relay
+  network) can carry several logical point-to-point connections to
+  other computers connected to the frame relay network. For a general
+  explanation of the protocol, check out
   http://frame-relay.indiana.edu/4000/4000index.html on the WWW. (To
   browse the WWW, you need to have access to a machine on the Internet
   that has one of the programs lynx, netscape or Mosaic.) To use frame
@@ -2525,7 +2546,7 @@
 /proc filesystem support
 CONFIG_PROC_FS
   This is a virtual filesystem providing information about the status
-  of the system. "Virtual" means that it doesn't take any space on
+  of the system. "Virtual" means that it doesn't take up any space on
   your harddisk: the files are created on the fly when you access
   them. Also, you cannot read the files with less: you need to use
   more or cat. The filesystem is explained in the Kernel Hacker's
@@ -2683,15 +2704,15 @@
 
 Standard/generic serial support
 CONFIG_SERIAL
-  This selects whether you want to include the driver for the
-  standard (0x3F8, 0x2F8, etc.) serial ports. Most people will
-  say "y" here, so that they can use serial mice, modems and
-  similar devices. People who might say "n" here are those that
-  are setting up dedicated ethernet WWW/ftp servers, or users
-  that have one of the various bus mice instead of a serial mouse.
-  Note that the Cyclades and Stallion drivers do not need this
-  driver built in for them to work. They are completely independent
-  of each other.
+  This selects whether you want to include the driver for the standard
+  serial ports.  People who might say N here are those that are
+  setting up dedicated ethernet WWW/ftp servers, or users that have
+  one of the various bus mice instead of a serial mouse.  Note that
+  the Cyclades and Stallion drivers do not need this driver to be
+  built in for them to work. They are completely independent of each
+  other. Most people will say Y here however, so that they can use
+  serial mice, modems and similar devices connecting to the standard
+  serial ports.
 
 Cyclades async mux support
 CONFIG_CYCLADES
diff -u --recursive --new-file v1.3.81/linux/Documentation/cdrom/aztcd linux/Documentation/cdrom/aztcd
--- v1.3.81/linux/Documentation/cdrom/aztcd	Mon Dec 11 11:18:47 1995
+++ linux/Documentation/cdrom/aztcd	Tue Apr  2 08:24:06 1996
@@ -1,13 +1,13 @@
-$Id: README.aztcd,v 2.10 1995/12/03 11:55:14 root Exp root $
+$Id: README.aztcd,v 2.20 1996/03/12 18:31:33 root Exp root $
           Readme-File /usr/src/Documentation/cdrom/aztcd
            for Aztech CD-ROM CDA268-01A, ORCHID CD-3110,
                   OKANO/WEARNES CDD110, Conrad TXC
                            CD-ROM Driver 
-                       Version 2.0 and newer
+                       Version 2.2 and newer
                    (for other drives see 6.-8.)
 
 NOTE: THIS DRIVER WILL WORK WITH THE CD-ROM DRIVES LISTED, WHICH HAVE
-      A PROPRIETARY INTERFACE (implemented on a sound card or on a
+      A PROPRIETARY INTERFACE (implemented on a sound card or on an
       ISA-AT-bus card). 
       IT WILL DEFINITELY NOT WORK WITH CD-ROM DRIVES WITH *IDE*-INTERFACE,
       such as the Aztech CDA269-031SE !!! IF YOU'RE USING A CD-ROM DRIVE
@@ -40,9 +40,9 @@
 1. NOTE 
 This software has been successfully in alpha and beta test and is part of
 the standard kernel since kernel 1.1.8x since December 1994. It works with
-time with AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and
+with AZTECH CDA268-01A, ORCHID CDS-3110, ORCHID/WEARNES CDD110 and
 CONRAD TXC (Nr.99 31 23 -series 04) and has proven to be stable with kernel 
-versions 1.0.9 to 1.3.35. But with any software there still may be bugs in it. 
+versions 1.0.9 to 1.3.72. But with any software there still may be bugs in it. 
 So if you encounter problems, you are invited to help us improve this software. 
 Please send me a detailed bug report (see chapter BUG REPORTS). You are also 
 invited in helping us to increase the number of drives, which are supported.
@@ -84,7 +84,7 @@
 drive by
           mount -t iso9660 -r /dev/aztcd0 /mnt
 and use it as any other filesystem. (If this does not work, check if
-  /dev/aztcd0 and /mnt do exist and create them, if necessary by doing
+/dev/aztcd0 and /mnt do exist and create them, if necessary by doing
       mknod /dev/aztcd0 b 29 0
       mkdir /mnt                       
 
@@ -108,7 +108,7 @@
   CD268, an ORCHID CD-3110 or ORCHID/WEARNES CDD110 that's the only item you 
   have to set up. If you have a soundcard, read chapter 4.2.
   Users of other drives should read chapter OTHER DRIVES of this file.
-  You also can configure that address by LILO boot parameter aztcd=... 
+  You also can configure that address by kernel boot parameter aztcd=... 
 - There are some other points, which may be configured, e.g. auto-eject the
   CD when umounting a drive, tray locking etc., see aztcd.h for details.
 - Build a new kernel, configure it for 'Aztech/Orchid/Okano/Wearnes support' 
@@ -116,7 +116,7 @@
   'Aztech... support', if you want to use aztcd as a run time loadable module. 
   But in any case you must have the ISO9660-filesystem included in your
   kernel.
-- Activate the new kernel, normally this is done by running lilo (don't for-
+- Activate the new kernel, normally this is done by running LILO (don't for-
   get to configure it before and to keep a copy of your old kernel in case
   something goes wrong!).
 - Reboot
@@ -254,7 +254,7 @@
 snail mail address for such 'stuff' is
            Prof. Dr. W. Zimmermann
            Fachhochschule fuer Technik Esslingen
-           Fachbereich NT
+           Fachbereich IT
            Flandernstrasse 101
            D-73732 Esslingen
            Germany
@@ -460,8 +460,8 @@
 Reinhard Max delivered the information for the CDROM-interface of the
 SoundWave32 soundcards.
 
-Jochen Koch and Olaf Kaluza delivered the information for supporting Conrad's TXC
-drive.
+Jochen Kunz and Olaf Kaluza delivered the information for supporting Conrad's 
+TXC drive.
 
 Anybody, who is interested in these items should have a look at 'ftp.gwdg.de',
 directory 'pub/linux/cdrom' and at 'ftp.cdrom.com', directory 'pub/cdrom'.
@@ -472,11 +472,9 @@
 named 'cdplay.c'. It allows you to play audio CDs. You can play a specified 
 track, pause and resume or skip tracks forward and backwards. If you quit the 
 program without stopping the  drive, playing is continued. You can also 
-(mis)use cdplay to read and hexdump data disks.
-'cdplay.c' can be found as a separate file in package AZTECH.CDROM.Vxx.tgz.
-If you don't have it, you can find the code in the APPENDIX of this file,
-which you should cut out with an editor and store in a separate file
-'cdplay.c'. To compile it and make it executable, do
+(mis)use cdplay to read and hexdump data disks. You can find the code in the 
+APPENDIX of this file, which you should cut out with an editor and store in a 
+separate file 'cdplay.c'. To compile it and make it executable, do
   gcc -s -Wall -O2 -L/usr/lib cdplay.c -o /usr/local/bin/cdplay # compiles it
   chmod +755 /usr/local/bin/cdplay                              # makes it executable
   ln -s /dev/aztcd0 /dev/cdrom                                  # creates a link
@@ -503,18 +501,21 @@
 should have a backup copy of your system in a safe place (and you also
 should try before, how to restore from a backup copy)!
 
+A reworked and improved version called 'cdtester.c', which has yet more
+features for testing CDROM-drives can be found in
+/usr/src/linux/Documentation/cdrom/sbpcd, written by E.Moenkeberg.
 
 Werner Zimmermann
 Fachhochschule fuer Technik Esslingen
 (EMail: zimmerma@rz.fht-esslingen.de)
-October 21, 1995
+Maerz 16, 1995
 
 ---------------------------------------------------------------------------
 APPENDIX: Source code of cdplay.c
 
 /* Tiny Audio CD Player
 
-   Copyright 1994, 1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+   Copyright 1994, 1995, 1996 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
 
 This program originally was written to test the audio functions of the
 AZTECH.CDROM-driver, but it should work with every CD-ROM drive. Before 
@@ -564,7 +565,7 @@
          } azt;
   struct cdrom_volctrl  volctrl;
 
-  printf("\nMini-Audio CD-Player V0.71   (C) 1994,1995  W.Zimmermann\n");
+  printf("\nMini-Audio CD-Player V0.72   (C) 1994,1995,1996  W.Zimmermann\n");
   handle=open("/dev/cdrom",O_RDWR);
   ioctl(handle,CDROMRESUME);
   
diff -u --recursive --new-file v1.3.81/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v1.3.81/linux/Documentation/devices.tex	Sun Mar 24 20:07:00 1996
+++ linux/Documentation/devices.tex	Mon Apr  1 10:48:04 1996
@@ -42,7 +42,7 @@
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: March 9, 1996}
+\date{Last revised: April 1, 1996}
 \maketitle
 %
 \noindent
@@ -158,7 +158,8 @@
 \major{45}{}{char }{isdn4linux ISDN BRI driver}
 \major{46}{}{char }{Comtrol Rocketport serial card}
 \major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices}
-\major{48}{--59}{}{Unallocated}
+\major{48}{}{char }{Matter replicator}
+\major{49}{--59}{}{Unallocated}
 \major{60}{--63}{}{Local/experimental use}
 \major{64}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
@@ -411,6 +412,7 @@
 	\minor{131}{/dev/temperature}{Machine internal temperature}
 	\minor{132}{/dev/hwtrap}{Hardware fault trap}
 	\minor{133}{/dev/exttrp}{External device trap}
+	\minor{134}{/dev/apm_bios}{Advanced Power Management BIOS}
 \end{devicelist}
 
 \noindent
@@ -980,7 +982,15 @@
 \end{devicelist}
 
 \begin{devicelist}
-\major{48}{--59}{}{Unallocated}
+\major{48}{}{char }{Matter replicator}
+	\minor{0}{/dev/replicator}{Matter replicator}
+\end{devicelist}
+
+\noindent
+See RFC 1437 for the data format accepted by this device.
+
+\begin{devicelist}
+\major{49}{--59}{}{Unallocated}
 \end{devicelist}
 
 \begin{devicelist}
diff -u --recursive --new-file v1.3.81/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v1.3.81/linux/Documentation/devices.txt	Sun Mar 24 20:07:00 1996
+++ linux/Documentation/devices.txt	Mon Apr  1 10:48:04 1996
@@ -2,7 +2,7 @@
 
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		     Last revised: March 9, 1996
+		     Last revised: April 1, 1996
 
 This list is the successor to Rick Miller's Linux Device List, which
 he stopped maintaining when he got busy with other things in 1993.  It
@@ -237,6 +237,7 @@
 		131 = /dev/temperature	Machine internal temperature
 		132 = /dev/hwtrap	Hardware fault trap
 		133 = /dev/exttrp	External device trap
+		134 = /dev/apm_bios	Advanced Power Management BIOS
 
  11 char	Raw keyboard device
 		  0 = /dev/kbd		Raw keyboard device
@@ -672,7 +673,12 @@
 		  1 = /dev/cur1		Callout device corresponding to ttyR1
 		      ...
 
- 48-59		UNALLOCATED
+ 48 char	Matter replicator
+		  0 = /dev/replicator	Matter replicator
+
+		See RFC 1437 for the data format accepted by this device.
+
+ 49-59		UNALLOCATED
 
  60-63		LOCAL/EXPERIMENTAL USE
 		Allocated for local/experimental use.  For devices not
diff -u --recursive --new-file v1.3.81/linux/Documentation/nfsroot.txt linux/Documentation/nfsroot.txt
--- v1.3.81/linux/Documentation/nfsroot.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/nfsroot.txt	Mon Apr  1 10:46:29 1996
@@ -0,0 +1,204 @@
+Mounting the root filesystem via NFS (nfsroot)
+==============================================
+
+Written 1996 by Gero Kuhlmann <gero@gkminix.han.de>
+
+
+
+If you want to use a diskless system, as an X-terminal or printer
+server for example, you have to put your root filesystem onto a
+non-disk device. This can either be a ramdisk (see initrd.txt in
+this directory for further information) or a filesystem mounted
+via NFS. The following text describes on how to use NFS for the
+root filesystem. For the rest of this text 'client' means the
+diskless system, and 'server' means the NFS server.
+
+
+
+
+1.) Enabling nfsroot capabilities
+    -----------------------------
+
+In order to use nfsroot you have to select support for NFS during
+kernel configuration. Note that NFS cannot be loaded as a module
+in this case. The configuration script will then ask you wether
+you want to use nfsroot, and if yes what kind of auto configuration
+system you want to use. Selecting both BOOTP and RARP is safe.
+
+
+
+
+2.) Kernel command line
+    -------------------
+
+When the kernel has been loaded by a boot loader (either by loadlin,
+LILO or a network boot program) it has to be told what root fs device
+to use, and where to find the server and the name of the directory
+on the server to mount as root. This can be established by a couple
+of kernel command line parameters:
+
+
+root=/dev/nfs
+
+  This is necessary to enable the pseudo-NFS-device. Note that it's not a
+  real device but just a synonym to tell the kernel to use NFS instead of
+  a real device.
+
+
+nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
+
+  If the nfsroot parameter is NOT give on the command line, the default
+  "/tftpboot/%s" will be used.
+
+  <server-ip>	Specifies the IP address of the NFS server. If this field
+		is not given, the default address as determined by the
+		nfsaddrs variable (see below) is used. One use of this
+		parameter is for example to allow using different servers
+		for RARP and NFS. Usually you can leave this blank.
+
+  <root-dir>	Name of the directory on the server to mount as root. If
+		there is a "%s" token in the string, the token will be
+		replaced by the ASCII-representation of the client's IP
+		address.
+
+  <nfs-options>	Standard NFS options. All options are seperated by commas.
+		If the options field is not given, the following defaults
+		will be used:
+			port		= as given by server portmap daemon
+			rsize		= 1024
+			wsize		= 1024
+			timeo		= 7
+			retrans		= 3
+			acregmin	= 3
+			acregmax	= 60
+			acdirmin	= 30
+			acdirmax	= 60
+			flags		= hard, nointr, noposix, cto, ac
+
+
+nfsaddrs=<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>
+
+  If this parameter is missing on the kernel command line, all fields are
+  assumed to be empty, and the below mentioned defaults apply. In general
+  this means that the kernel tries to configure everything using both
+  RARP and BOOTP (depending on what has been enabled during kernel confi-
+  guration, and if both what protocol answer got in first).
+
+  <client-ip>	IP address of the client. If empty, the address will either
+		be determined by RARP or BOOTP. What protocol is used de-
+		pends on what has been enabled during kernel configuration
+		and on the <autoconf> parameter. If this parameter is not
+		empty, neither RARP nor BOOTP will be used.
+
+  <server-ip>	IP address of the NFS server. If RARP is used to determine
+		the client address and this parameter is NOT empty only
+		replies from the specified server are accepted. To use
+		different RARP and NFS server, specify your RARP server
+		here (or leave it blank), and specify your NFS server in
+		the nfsroot paremeter (see above). If this entry is blank
+		the address of the server is used which answered the RARP
+		or BOOTP request.
+
+  <gw-ip>	IP address of a gateway if the server in on a different
+		subnet. If this entry is empty no gateway is used and the
+		server is assumed to be on the local network, unless a
+		value has been received by BOOTP.
+
+  <netmask>	Netmask for local network interface. If this is empty,
+		the netmask is derived from the client IP address, un-
+		less a value has been received by BOOTP.
+
+  <hostname>	Name of the client. If empty, the client IP address is
+		used in ASCII-notation, or the value received by BOOTP.
+
+  <device>	Name of network device to use. If this is empty, all
+		devices are used for RARP requests, and the first one
+		found for BOOTP. For NFS the device is used on which
+		either RARP or BOOTP replies have been received. If
+		you only have one device you can safely leave this blank.
+
+  <autoconf>	Method to use for autoconfiguration. If this is either
+		'rarp' or 'bootp' the specified protocol is being used.
+		If the value is 'both' or empty, both protocols are used
+		so far as they have been enabled during kernel configura-
+		tion. 'none' means no autoconfiguration. In this case you
+		have to specify all necessary values in the fields before.
+
+  The <autoconf> parameter can appear alone as the value to the nfsaddrs
+  parameter (without all the ':' characters before) in which case auto-
+  configuration is used. However, the 'none' value is not available in
+  that case.
+
+
+
+
+3.) Kernel loader
+    -------------
+
+To get the kernel into memory different approaches can be used. They
+depend on what facilities are available:
+
+
+3.1)  Writing the kernel onto a floppy using dd:
+	As always you can just write the kernel onto a floppy using dd,
+	but then it's not possible to use kernel command lines at all.
+	To substitute the 'root=' parameter, create a dummy device on any
+	linux system with major number 0 and minor number 255 using mknod:
+
+		mknod /dev/boot255 c 0 255
+
+	Then copy the kernel zImage file onto a floppy using dd:
+
+		dd if=/usr/src/linux/arch/i386/boot/zImage of=/dev/fd0
+
+	And finally use rdev to set the root device:
+
+		rdev /dev/fd0 /dev/boot255
+
+	You can then remove the dummy device /dev/boot255 again. There
+	is no real device available for it.
+	The other two kernel command line parameters cannot be substi-
+	tuted with rdev. Therefore, using this method the kernel will
+	by default use RARP and/or BOOTP, and if it gets an answer via
+	RARP will mount the directory /tftpboot/<client-ip>/ as it's
+	root. If it got a BOOTP answer the directory name in that answer
+	is used.
+
+
+3.2) Using LILO
+	When using LILO you can specify all necessary command line
+	parameters with the 'append=' command in the LILO configuration
+	file. However, to use the 'root=' command you also need to
+	setup a dummy device as described in 3.1 above. How to use
+	LILO and it's 'append=' command please refer to the LILO
+	documentation.
+
+3.3) Using loadlin
+	When you want to boot Linux from a DOS command prompt without
+	having a local hard disk to mount as root, you can use loadlin.
+	I was told that it works, but haven't used it myself yet. In
+	general you should be able to create a kernel command line simi-
+	lar to how LILO is doing it. Please refer to the loadlin docu-
+	mentation for further information.
+
+3.4) Using a bootrom
+	This is probably the most elegant way of booting a diskless
+	client. With a bootrom the kernel gets loaded using the TFTP
+	protocol. As far as I know no commercial bootroms already
+	support booting Linux over the network, but there are two
+	free implementations of a bootrom available on sunsite.unc.edu
+	and it's mirrors. They are called 'netboot-nfs' and 'etherboot'.
+	Both contain everything you need to boot a diskless Linux client.
+
+
+
+
+4.) Credits
+    -------
+
+  The nfsroot code in the kernel has been written by me, Gero Kuhlmann
+  <gero@gkminix.han.de>, with the BOOTP code and a couple of bug fixes
+  contributed by Martin Mares <mj@k332.feld.cvut.cz>. In order to write
+  the initial version of nfsroot I would like to thank Jens-Uwe Mager
+  <jum@anubis.han.de> for his help.
+
diff -u --recursive --new-file v1.3.81/linux/Makefile linux/Makefile
--- v1.3.81/linux/Makefile	Sun Mar 31 00:13:16 1996
+++ linux/Makefile	Sun Mar 31 20:27:33 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 81
+SUBLEVEL = 82
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.81/linux/Rules.make linux/Rules.make
--- v1.3.81/linux/Rules.make	Wed Feb 28 08:44:10 1996
+++ linux/Rules.make	Tue Apr  2 08:43:04 1996
@@ -150,7 +150,7 @@
 	@if [ ! -x /sbin/genksyms ]; then echo "Please read: README.modules"; fi
 	$(CC) $(CFLAGS) -E -D__GENKSYMS__ $< | /sbin/genksyms -w $(MODINCL)
 
-$(SYMTAB_OBJS:.o=.ver): $(TOPDIR)/include/linux/autoconf.h
+$(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver)): $(TOPDIR)/include/linux/autoconf.h
 
 $(TOPDIR)/include/linux/modversions.h: $(addprefix $(MODINCL)/,$(SYMTAB_OBJS:.o=.ver))
 	@echo updating $(TOPDIR)/include/linux/modversions.h
diff -u --recursive --new-file v1.3.81/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.81/linux/arch/alpha/config.in	Sun Mar 31 00:13:16 1996
+++ linux/arch/alpha/config.in	Mon Apr  1 10:48:04 1996
@@ -15,6 +15,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
 fi
 endmenu
 
diff -u --recursive --new-file v1.3.81/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v1.3.81/linux/arch/alpha/defconfig	Mon Mar 25 11:56:11 1996
+++ linux/arch/alpha/defconfig	Mon Apr  1 13:40:33 1996
@@ -7,6 +7,7 @@
 #
 CONFIG_MODULES=y
 # CONFIG_MODVERSIONS is not set
+# CONFIG_KERNELD is not set
 
 #
 # General setup
@@ -130,10 +131,11 @@
 CONFIG_DUMMY=m
 # CONFIG_SLIP is not set
 # CONFIG_PPP is not set
+# CONFIG_STRIP is not set
 # CONFIG_SCC is not set
 # CONFIG_PLIP is not set
 # CONFIG_EQUALIZER is not set
-# CONFIG_FRAD is not set
+# CONFIG_DLCI is not set
 # CONFIG_NET_ALPHA is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_LANCE is not set
@@ -184,6 +186,7 @@
 # CONFIG_82C710_MOUSE is not set
 # CONFIG_MS_BUSMOUSE is not set
 # CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_UMISC is not set
 # CONFIG_QIC02_TAPE is not set
 # CONFIG_FTAPE is not set
 # CONFIG_APM is not set
diff -u --recursive --new-file v1.3.81/linux/arch/alpha/kernel/ksyms.c linux/arch/alpha/kernel/ksyms.c
--- v1.3.81/linux/arch/alpha/kernel/ksyms.c	Mon Mar  4 11:46:31 1996
+++ linux/arch/alpha/kernel/ksyms.c	Tue Apr  2 08:43:04 1996
@@ -33,6 +33,12 @@
 	X(_outb),
 	X(_outw),
 	X(_outl),
+	X(_readb),
+	X(_readw),
+	X(_readl),
+	X(_writeb),
+	X(_writew),
+	X(_writel),
 	X(__divl),
 	X(__reml),
 	X(__divq),
diff -u --recursive --new-file v1.3.81/linux/arch/alpha/kernel/setup.c linux/arch/alpha/kernel/setup.c
--- v1.3.81/linux/arch/alpha/kernel/setup.c	Sat Mar 16 13:52:12 1996
+++ linux/arch/alpha/kernel/setup.c	Mon Apr  1 17:00:03 1996
@@ -174,7 +174,8 @@
 		"ADU", "Cobra", "Ruby", "Flamingo", "5", "Jensen",
 		"Pelican", "8", "Sable", "AXPvme", "Noname",
 		"Turbolaser", "Avanti", "Mustang", "Alcor", "16",
-		"Mikasa", "18", "EB66", "EB64+"
+		"Mikasa", "18", "EB66", "EB64+", "21", "22", "23",
+		"24", "25", "EB164"
 	};
 	struct percpu_struct *cpu;
 	unsigned int cpu_index;
diff -u --recursive --new-file v1.3.81/linux/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- v1.3.81/linux/arch/i386/boot/setup.S	Thu Mar 28 17:34:33 1996
+++ linux/arch/i386/boot/setup.S	Sat Mar 30 20:58:57 1996
@@ -114,10 +114,12 @@
 	mov	dl,#0x81
 	int	0x13
 
+#ifdef SAFE_RESET_DISK_CONTROLLER
 ! Reset the disk controller.
 	mov	ax,#0x0000
 	mov	dl,#0x80
 	int	0x13
+#endif
 
 ! set DS=CS, we know that SETUPSEG == CS at this point
 	mov	ax,cs		! aka #SETUPSEG
diff -u --recursive --new-file v1.3.81/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.81/linux/arch/i386/defconfig	Sun Mar 31 00:13:16 1996
+++ linux/arch/i386/defconfig	Mon Apr  1 11:02:35 1996
@@ -88,10 +88,11 @@
 CONFIG_DUMMY=m
 # CONFIG_SLIP is not set
 # CONFIG_PPP is not set
+# CONFIG_STRIP is not set
 # CONFIG_SCC is not set
 # CONFIG_PLIP is not set
 # CONFIG_EQUALIZER is not set
-# CONFIG_FRAD is not set
+# CONFIG_DLCI is not set
 # CONFIG_NET_ALPHA is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_LANCE is not set
diff -u --recursive --new-file v1.3.81/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v1.3.81/linux/arch/i386/kernel/irq.c	Sun Mar 17 09:01:02 1996
+++ linux/arch/i386/kernel/irq.c	Mon Apr  1 10:46:29 1996
@@ -263,27 +263,28 @@
 	unsigned long sum_spins_sys_idle = 0;
 	unsigned long sum_smp_idle_count = 0;
 
-	for (i=0;i<=smp_num_cpus;i++) {
-		sum_spins+=smp_spins[i];
-		sum_spins_syscall+=smp_spins_syscall[i];
-		sum_spins_sys_idle+=smp_spins_sys_idle[i];
-		sum_smp_idle_count+=smp_idle_count[i];
+	for (i=0;i<smp_num_cpus;i++) {
+		int cpunum = cpu_logical_map[i];
+		sum_spins+=smp_spins[cpunum];
+		sum_spins_syscall+=smp_spins_syscall[cpunum];
+		sum_spins_sys_idle+=smp_spins_sys_idle[cpunum];
+		sum_smp_idle_count+=smp_idle_count[cpunum];
 	}
 
-	len += sprintf(buf+len,"CPUS: %10i \n", 
-		0==smp_num_cpus?1:smp_num_cpus);
+	len += sprintf(buf+len,"CPUS: %10i \n", smp_num_cpus);
 	len += sprintf(buf+len,"            SUM ");
 	for (i=0;i<smp_num_cpus;i++)
-		len += sprintf(buf+len,"        P%1d ",i);
+		len += sprintf(buf+len,"        P%1d ",cpu_logical_map[i]);
 	len += sprintf(buf+len,"\n");
 	for (i = 0 ; i < NR_IRQS ; i++) {
 		action = *(i + irq_action);
-		if (!action->handler)
+		if (!action || !action->handler)
 			continue;
 		len += sprintf(buf+len, "%3d: %10d ",
 			i, kstat.interrupts[i]);
 		for (j=0;j<smp_num_cpus;j++)
-			len+=sprintf(buf+len, "%10d ",int_count[j][i]);
+			len+=sprintf(buf+len, "%10d ",
+				int_count[cpu_logical_map[j]][i]);
 		len += sprintf(buf+len, "%c %s\n",
 			(action->flags & SA_INTERRUPT) ? '+' : ' ',
 			action->name);
@@ -297,7 +298,7 @@
 		sum_spins);
 
 	for (i=0;i<smp_num_cpus;i++)
-		len+=sprintf(buf+len," %10lu",smp_spins[i]);
+		len+=sprintf(buf+len," %10lu",smp_spins[cpu_logical_map[i]]);
 
 	len +=sprintf(buf+len,"   spins from int\n");
 
@@ -305,7 +306,7 @@
 		sum_spins_syscall);
 
 	for (i=0;i<smp_num_cpus;i++)
-		len+=sprintf(buf+len," %10lu",smp_spins_syscall[i]);
+		len+=sprintf(buf+len," %10lu",smp_spins_syscall[cpu_logical_map[i]]);
 
 	len +=sprintf(buf+len,"   spins from syscall\n");
 
@@ -313,13 +314,13 @@
 		sum_spins_sys_idle);
 
 	for (i=0;i<smp_num_cpus;i++)
-		len+=sprintf(buf+len," %10lu",smp_spins_sys_idle[i]);
+		len+=sprintf(buf+len," %10lu",smp_spins_sys_idle[cpu_logical_map[i]]);
 
 	len +=sprintf(buf+len,"   spins from sysidle\n");
 	len+=sprintf(buf+len,"IDLE %10lu",sum_smp_idle_count);
 
 	for (i=0;i<smp_num_cpus;i++)
-		len+=sprintf(buf+len," %10lu",smp_idle_count[i]);
+		len+=sprintf(buf+len," %10lu",smp_idle_count[cpu_logical_map[i]]);
 
 	len +=sprintf(buf+len,"   idle ticks\n");
 
diff -u --recursive --new-file v1.3.81/linux/drivers/block/floppy.c linux/drivers/block/floppy.c
--- v1.3.81/linux/drivers/block/floppy.c	Wed Mar 27 08:19:28 1996
+++ linux/drivers/block/floppy.c	Tue Apr  2 08:43:05 1996
@@ -2592,7 +2592,8 @@
 		floppy_off(current_drive);
 
 	if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
-		DPRINT("current not active!\n");
+		CLEAR_INTR;
+		unlock_fdc();
 		return;
 	}
 
@@ -3335,7 +3336,7 @@
 			       sizeof(struct floppy_drive_params));
 		if (UDP->cmos){
 			if (first)
-				printk("Floppy drive(s): ");
+				printk(KERN_INFO "Floppy drive(s): ");
 			else
 				printk(", ");
 			first=0;
@@ -3622,7 +3623,7 @@
 	if ((r = result()) <= 0x00)
 		return FDC_NONE;	/* No FDC present ??? */
 	if ((r==1) && (reply_buffer[0] == 0x80)){
-		printk("FDC %d is a 8272A\n",fdc);
+		printk(KERN_INFO "FDC %d is a 8272A\n",fdc);
 		return FDC_8272A;	/* 8272a/765 don't know DUMPREGS */
 	}
 	if (r != 10) {
@@ -3633,7 +3634,7 @@
 	output_byte(FD_VERSION);
 	r = result();
 	if ((r == 1) && (reply_buffer[0] == 0x80)){
-		printk("FDC %d is a 82072\n",fdc);
+		printk(KERN_INFO "FDC %d is a 82072\n",fdc);
 		return FDC_82072;		/* 82072 doesn't know VERSION */
 	}
 	if ((r != 1) || (reply_buffer[0] != 0x90)) {
@@ -3644,7 +3645,7 @@
 	output_byte(FD_UNLOCK);
 	r = result();
 	if ((r == 1) && (reply_buffer[0] == 0x80)){
-		printk("FDC %d is a pre-1991 82077\n", fdc);
+		printk(KERN_INFO "FDC %d is a pre-1991 82077\n", fdc);
 		return FDC_82077_ORIG;	/* Pre-1991 82077 doesn't know LOCK/UNLOCK */
 	}
 	if ((r != 1) || (reply_buffer[0] != 0x00)) {
@@ -3660,7 +3661,7 @@
 		return FDC_UNKNOWN;
 	}
 	if (reply_buffer[0] == 0x80) {
-		printk("FDC %d is a post-1991 82077\n",fdc);
+		printk(KERN_INFO "FDC %d is a post-1991 82077\n",fdc);
 		return FDC_82077;	/* Revised 82077AA passes all the tests */
 	}
 	switch (reply_buffer[0] >> 5) {
@@ -3672,23 +3673,23 @@
 				return FDC_UNKNOWN;
 			}
 			if (!(reply_buffer[0] & 0x40)) {
-				printk("FDC %d is a 3Volt 82078SL.\n",fdc);
+				printk(KERN_INFO "FDC %d is a 3Volt 82078SL.\n",fdc);
 				return FDC_82078;
 			}
 			/* Either a 82078-1 or a 82078SL running at 5Volt */
-			printk("FDC %d is a 82078-1.\n",fdc);
+			printk(KERN_INFO "FDC %d is a 82078-1.\n",fdc);
 			return FDC_82078_1;
 		case 0x1:
-			printk("FDC %d is a 44pin 82078\n",fdc);
+			printk(KERN_INFO "FDC %d is a 44pin 82078\n",fdc);
 			return FDC_82078;
 		case 0x2:
-			printk("FDC %d is a S82078B\n", fdc);
+			printk(KERN_INFO "FDC %d is a S82078B\n", fdc);
 			return FDC_S82078B;
 		case 0x3:
-			printk("FDC %d is a National Semiconductor PC87306\n", fdc);
+			printk(KERN_INFO "FDC %d is a National Semiconductor PC87306\n", fdc);
 			return FDC_87306;
 		default:
-			printk("FDC %d init: 82077 variant with PARTID=%d.\n",
+			printk(KERN_INFO "FDC %d init: 82077 variant with PARTID=%d.\n",
 			       fdc, reply_buffer[0] >> 5);
 			return FDC_82077_UNKN;
 	}
@@ -4061,7 +4062,7 @@
 #endif
 int init_module(void)
 {
-	printk("inserting floppy driver for %s\n", kernel_version);
+	printk(KERN_INFO "inserting floppy driver for %s\n", kernel_version);
 		
 	mod_setup("floppy=", floppy_setup);
 		
diff -u --recursive --new-file v1.3.81/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v1.3.81/linux/drivers/block/ide-cd.c	Sat Mar 16 13:52:13 1996
+++ linux/drivers/block/ide-cd.c	Mon Apr  1 10:46:32 1996
@@ -2105,6 +2105,14 @@
 	return 0;
       }
 
+    case CDROMCLOSETRAY:
+      {
+        if (drive->usage > 1)
+          return -EBUSY;
+
+        return cdrom_eject (drive, 1, NULL);
+      }
+
     case CDROMPAUSE:
       return cdrom_pause (drive, 1, NULL);
 
diff -u --recursive --new-file v1.3.81/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v1.3.81/linux/drivers/block/ide.c	Thu Mar 28 17:34:33 1996
+++ linux/drivers/block/ide.c	Sun Mar 31 00:14:26 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.35  Mar 23, 1996
+ *  linux/drivers/block/ide.c	Version 5.36  Mar 30, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -223,6 +223,7 @@
  *			fix cdrom ioctl problem from 5.33
  * Version 5.35		cosmetic changes
  *			fix cli() problem in try_to_identify()
+ * Version 5.36		fixes to optional PCMCIA support
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -3233,7 +3234,7 @@
 		hwif = &ide_hwifs[index];
 		if (hwif->present) {
 			if (hwif->io_base == io_base || hwif->ctl_port == ctl_port)
-				break;
+				break; /* this ide port already exists */
 		} else {
 			hwif->io_base = io_base;
 			hwif->ctl_port = ctl_port;
@@ -3241,10 +3242,10 @@
 			hwif->noprobe = 0;
 			if (!hwif_init(index))
 				break;
-			hwif->gd->real_devices = hwif->drives[0].name;
 			for (i = 0; i < hwif->gd->nr_real; i++)
 				revalidate_disk(MKDEV(hwif->major, i<<PARTN_BITS));
 			rc = index;
+			break;
 		}
 	}
 	restore_flags(flags);
@@ -3253,7 +3254,7 @@
 
 void ide_unregister (unsigned int index)
 {
-	struct gendisk *prev_gd, *gd;
+	struct gendisk *gd, **gdp;
 	ide_hwif_t *hwif, *g;
 	ide_hwgroup_t *hwgroup;
 	int irq_count = 0;
@@ -3284,6 +3285,14 @@
 		free_irq(hwif->irq, hwgroup);
 
 	/*
+	 * Note that we only release the standard ports,
+	 * and do not even try to handle any extra ports
+	 * allocated for weird IDE interface chipsets.
+	 */
+	release_region(hwif->io_base, 8);
+	release_region(hwif->ctl_port, 1);
+
+	/*
 	 * Remove us from the hwgroup, and free
 	 * the hwgroup if we were the only member
 	 */
@@ -3304,18 +3313,13 @@
 	kfree(blksize_size[hwif->major]);
 	blk_dev[hwif->major].request_fn = NULL;
 	blksize_size[hwif->major] = NULL;
-	gd = gendisk_head; prev_gd = NULL;
-	while (gd && (gd != hwif->gd)) {
-		prev_gd = gd;
-		gd = gd->next;
-	}
-	if (gd != hwif->gd)
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+		if (*gdp == hwif->gd)
+			break;
+	if (*gdp == NULL)
 		printk("gd not in disk chain!\n");
 	else {
-		if (prev_gd != NULL)
-			prev_gd->next = gd->next;
-		else
-			gendisk_head = gd->next;
+		gd = *gdp; *gdp = gd->next;
 		kfree(gd->sizes);
 		kfree(gd->part);
 		kfree(gd);
diff -u --recursive --new-file v1.3.81/linux/drivers/block/md.c linux/drivers/block/md.c
--- v1.3.81/linux/drivers/block/md.c	Sun Mar 24 20:07:00 1996
+++ linux/drivers/block/md.c	Mon Apr  1 12:58:34 1996
@@ -389,6 +389,7 @@
 static void do_md_request (void)
 {
   int minor;
+  long flags;
   struct request *req;
     
   while (1)
@@ -396,12 +397,13 @@
 #ifdef MD_COUNT_SIZE
     int reqsize, chunksize;
 #endif
-    
+
+    save_flags (flags);
     cli ();
     req = blk_dev[MD_MAJOR].current_request;
     if (!req || (req->rq_status == RQ_INACTIVE))
     {
-      sti ();
+      restore_flags (flags);
       return;
     }
     
@@ -414,7 +416,7 @@
 #endif
     
     blk_dev[MD_MAJOR].current_request = req->next;
-    sti ();
+    restore_flags (flags);
 
     minor = MINOR(req->rq_dev);
     if ((MAJOR(req->rq_dev) != MD_MAJOR) || (minor >= MAX_REAL))
@@ -486,7 +488,7 @@
 
       while (req && !found)
       {
-	if (req->rq_status!=RQ_ACTIVE)
+	if (req->rq_status!=RQ_ACTIVE && &blk_dev[major].plug!=req)
 	  printk ("Saw bad status request !\n");
 
 	if (req->rq_dev == dev &&
diff -u --recursive --new-file v1.3.81/linux/drivers/block/triton.c linux/drivers/block/triton.c
--- v1.3.81/linux/drivers/block/triton.c	Sat Mar 16 13:52:15 1996
+++ linux/drivers/block/triton.c	Mon Apr  1 10:46:31 1996
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/triton.c	Version 1.08  Mar 13, 1996
+ *  linux/drivers/block/triton.c	Version 1.09  Mar 31, 1996
  *
  *  Copyright (c) 1995-1996  Mark Lord
  *  May be copied or modified under the terms of the GNU General Public License
@@ -121,7 +121,7 @@
  * known to work fine with this interface under Linux.
  */
 const char *good_dma_drives[] = {"Micropolis 2112A",
-				 "Maxtor 71260 AT",
+				 /* "Maxtor 71260 AT", known-bad! */
 				 "CONNER CTMA 4000"};
 
 /*
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v1.3.81/linux/drivers/cdrom/aztcd.c	Mon Dec 11 11:18:47 1995
+++ linux/drivers/cdrom/aztcd.c	Tue Apr  2 08:43:05 1996
@@ -1,8 +1,8 @@
-#define AZT_VERSION "2.1"
-/*      $Id: aztcd.c,v 2.10 1995/12/03 11:55:09 root Exp root $
+#define AZT_VERSION "2.2"
+/*      $Id: aztcd.c,v 2.20 1996/03/12 18:31:23 root Exp root $
 	linux/drivers/block/aztcd.c - AztechCD268 CDROM driver
 
-	Copyright (C) 1994,1995 Werner Zimmermann (zimmerma@rz.fht-esslingen.de)
+	Copyright (C) 1994,95,96 Werner Zimmermann(zimmerma@rz.fht-esslingen.de)
 
 	based on Mitsumi CDROM driver by  Martin Hariss and preworks by
 	Eberhard Moenkeberg; contains contributions by Joe Nardone and Robby 
@@ -134,7 +134,7 @@
                 with kernel 1.3.33. Will definitely not work with older kernels.
                 Programming done by Linus himself.
                 Werner Zimmermann, October 11, 1995
-	V1.90   Support for Conrad TXC drives, thank's to Jochen Koch and Olaf Kaluza.
+	V1.90   Support for Conrad TXC drives, thank's to Jochen Kunz and Olaf Kaluza.
 	        Werner Zimmermann, October 21, 1995
         V2.00   Changed #include "blk.h" to <linux/blk.h> as the directory
                 structure was changed. README.aztcd is now /usr/src/docu-
@@ -1545,7 +1545,7 @@
 	  printk("aztcd: no Aztech CD-ROM Initialization");
           return -EIO;
 	}
-	printk("aztcd: Aztech,Orchid,Okano,Wearnes,TXC CD-ROM Driver (C) 1994,1995 W.Zimmermann\n");
+	printk("aztcd: Aztech,Orchid,Okano,Wearnes,Txc CD-ROM Driver (C) 1994,95,96 W.Zimmermann\n");
 	printk("aztcd: DriverVersion=%s BaseAddress=0x%x  For IDE/ATAPI-drives use ide-cd.c\n",AZT_VERSION,azt_port);
 	printk("aztcd: If you have problems, read /usr/src/linux/Documentation/cdrom/aztcd\n");
 
@@ -1562,7 +1562,7 @@
                  return -EIO;
 	   }
         else                
-           { printk("aztcd: Soundwave32 card detected at %x  Version %x\n",
+           { printk(KERN_INFO "aztcd: Soundwave32 card detected at %x  Version %x\n",
 	 	 AZT_SW32_BASE_ADDR, inw(AZT_SW32_ID_REG));
 	     outw(AZT_SW32_INIT,AZT_SW32_CONFIG_REG);
 	     for (count=0;count<10000;count++);          /*delay a bit*/    	 
@@ -1643,7 +1643,7 @@
 	 }
 	if (count>30) max_count=30;  /*print max.30 chars of the version string*/
 	else          max_count=count;
-	printk("aztcd: FirmwareVersion=");
+	printk(KERN_INFO "aztcd: FirmwareVersion=");
 	for (count=1;count<max_count;count++) printk("%c",result[count]);
 	printk("<<>> ");
 
@@ -2098,6 +2098,6 @@
       return;
     }
    release_region(azt_port,4);
-   printk("aztcd module released.\n");
+   printk(KERN_INFO "aztcd module released.\n");
 }   
 #endif MODULE
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/cdu31a.c linux/drivers/cdrom/cdu31a.c
--- v1.3.81/linux/drivers/cdrom/cdu31a.c	Wed Mar 27 08:19:28 1996
+++ linux/drivers/cdrom/cdu31a.c	Tue Apr  2 08:43:05 1996
@@ -3092,11 +3092,11 @@
 	 }
       }
       
-      printk("Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
+      printk(KERN_INFO "Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
 	     drive_config.vendor_id,
 	     drive_config.product_id,
 	     drive_config.product_rev_level);
-      printk("  Capabilities: %s",
+      printk(KERN_INFO "  Capabilities: %s",
 	     load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
       if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
       {
@@ -3173,6 +3173,6 @@
       free_irq(cdu31a_irq, NULL);
 
    release_region(cdu31a_port,4);
-   printk("cdu31a module released.\n");
+   printk(KERN_INFO "cdu31a module released.\n");
 }   
 #endif MODULE
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/cm206.c linux/drivers/cdrom/cm206.c
--- v1.3.81/linux/drivers/cdrom/cm206.c	Sun Mar  3 15:23:54 1996
+++ linux/drivers/cdrom/cm206.c	Tue Apr  2 08:43:05 1996
@@ -1119,7 +1119,7 @@
   uch e=0;
   long int size=sizeof(struct cm206_struct);
 
-  printk("cm206: v" VERSION);
+  printk(KERN_INFO "cm206: v" VERSION);
   cm206_base = probe_base_port(auto_probe ? 0 : cm206_base);
   if (!cm206_base) {
     printk(" can't find adapter!\n");
@@ -1151,7 +1151,7 @@
       return -EIO;
     }
   e = send_receive_polled(c_gimme);
-  printk("Firmware revision %d", e & dcf_revision_code);
+  printk(KERN_INFO "Firmware revision %d", e & dcf_revision_code);
   if (e & dcf_transfer_rate) printk(" double");
   else printk(" single");
   printk(" speed drive");
@@ -1176,7 +1176,7 @@
   cd->adapter_last = -1;
   cd->timer.function = cm206_timeout;
   cd->max_sectors = (inw(r_data_status) & ds_ram_size) ? 24 : 97;
-  printk("%d kB adapter memory available, "  
+  printk(KERN_INFO "%d kB adapter memory available, "  
 	 " %ld bytes kernel memory used.\n", cd->max_sectors*2, size);
   return 0;
 }
@@ -1212,7 +1212,7 @@
 void cleanup_module(void)
 {
   cleanup(4);
-  printk("cm206 removed\n");
+  printk(KERN_INFO "cm206 removed\n");
 }
       
 #else /* !MODULE */
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/gscd.c linux/drivers/cdrom/gscd.c
--- v1.3.81/linux/drivers/cdrom/gscd.c	Wed Nov  8 12:07:27 1995
+++ linux/drivers/cdrom/gscd.c	Tue Apr  2 08:43:05 1996
@@ -967,7 +967,7 @@
      }
      else
      {
-        printk ( "Happy GoldStar !\n" );
+        printk (KERN_INFO "Happy GoldStar !\n" );
         return 0;
      }    
 }
@@ -982,7 +982,7 @@
    }
 
    release_region (gscd_port,4);
-   printk( "GoldStar-module released.\n" );
+   printk(KERN_INFO "GoldStar-module released.\n" );
 }
 #endif
 
@@ -1001,8 +1001,8 @@
 int i;
 int result;
 
-        printk ("GSCD: version %s\n", GSCD_VERSION);
-        printk ("GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", gscd_port);
+        printk (KERN_INFO "GSCD: version %s\n", GSCD_VERSION);
+        printk (KERN_INFO "GSCD: Trying to detect a Goldstar R420 CD-ROM drive at 0x%X.\n", gscd_port);
 
         if (check_region(gscd_port, 4)) 
         {
@@ -1041,7 +1041,7 @@
         while ( drv_states[i] != 0 )
         {
            curr_drv_state = drv_states[i];
-           printk ( "GSCD: Reset unit %d ... ",i );
+           printk (KERN_INFO "GSCD: Reset unit %d ... ",i );
            cc_Reset ();
            printk ( "done\n" );
            i++;
@@ -1062,7 +1062,7 @@
 
 	request_region(gscd_port, 4, "gscd");
 
-        printk ( "GSCD: GoldStar CD-ROM Drive found.\n" );
+        printk (KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n" );
 	return 0;
 }
 
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/isp16.c linux/drivers/cdrom/isp16.c
--- v1.3.81/linux/drivers/cdrom/isp16.c	Tue Feb 13 10:30:25 1996
+++ linux/drivers/cdrom/isp16.c	Tue Apr  2 08:43:05 1996
@@ -93,7 +93,7 @@
 {
   u_char expected_drive;
 
-  printk("ISP16: configuration cdrom interface, version %d.%d.\n", ISP16_VERSION_MAJOR,
+  printk(KERN_INFO "ISP16: configuration cdrom interface, version %d.%d.\n", ISP16_VERSION_MAJOR,
     ISP16_VERSION_MINOR);
 
   if ( !strcmp(isp16_cdrom_type, "noisp16") ) {
@@ -111,7 +111,7 @@
     return(-EIO);
   }
 
-  printk("ISP16: cdrom interface (with OPTi 82C92%d chip) detected.\n",
+  printk(KERN_INFO "ISP16: cdrom interface (with OPTi 82C92%d chip) detected.\n",
     (isp16_type==2) ? 9 : 8);
 
   if ( !strcmp(isp16_cdrom_type, "Sanyo") )
@@ -132,7 +132,7 @@
     printk("ISP16: cdrom interface has not been properly configured.\n");
     return(-EIO);
   }
-  printk("ISP16: cdrom interface set up with io base 0x%03X, irq %d, dma %d,"
+  printk(KERN_INFO "ISP16: cdrom interface set up with io base 0x%03X, irq %d, dma %d,"
     " type %s.\n", isp16_cdrom_base, isp16_cdrom_irq, isp16_cdrom_dma,
     isp16_cdrom_type);
   return(0);
@@ -310,6 +310,6 @@
 void cleanup_module(void)
 {
 	release_region(ISP16_IO_BASE, ISP16_IO_SIZE);
-	printk("ISP16: module released.\n");
+	printk(KERN_INFO "ISP16: module released.\n");
 }
 #endif /* MODULE */
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/mcd.c linux/drivers/cdrom/mcd.c
--- v1.3.81/linux/drivers/cdrom/mcd.c	Thu Mar 21 08:55:44 1996
+++ linux/drivers/cdrom/mcd.c	Tue Apr  2 08:43:05 1996
@@ -1172,7 +1172,7 @@
           return -EIO;
 	}
 
-	printk("mcd=0x%x,%d: ", mcd_port, mcd_irq);
+	printk(KERN_INFO "mcd=0x%x,%d: ", mcd_port, mcd_irq);
 
 	if (register_blkdev(MAJOR_NR, "mcd", &mcd_fops) != 0)
 	{
@@ -1627,6 +1627,6 @@
      }
   release_region(mcd_port,4);
   free_irq(mcd_irq, NULL);
-  printk("mcd module released.\n");
+  printk(KERN_INFO "mcd module released.\n");
 }
 #endif MODULE
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/optcd.c linux/drivers/cdrom/optcd.c
--- v1.3.81/linux/drivers/cdrom/optcd.c	Fri Feb 23 09:39:34 1996
+++ linux/drivers/cdrom/optcd.c	Tue Apr  2 08:43:06 1996
@@ -2059,7 +2059,7 @@
 	read_ahead[MAJOR_NR] = 4;
 	request_region(optcd_port, 4, "optcd");
 
-	printk("optcd: DOLPHIN 8000 AT CDROM at 0x%x\n", optcd_port);
+	printk(KERN_INFO "optcd: DOLPHIN 8000 AT CDROM at 0x%x\n", optcd_port);
 	return 0;
 }
 
@@ -2078,6 +2078,6 @@
 		return;
 	}
 	release_region(optcd_port, 4);
-	printk("optcd: module released.\n");
+	printk(KERN_INFO "optcd: module released.\n");
 }
 #endif MODULE
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/sjcd.c linux/drivers/cdrom/sjcd.c
--- v1.3.81/linux/drivers/cdrom/sjcd.c	Tue Feb 13 10:30:25 1996
+++ linux/drivers/cdrom/sjcd.c	Tue Apr  2 08:43:06 1996
@@ -1447,7 +1447,7 @@
 int sjcd_init( void ){
   int i;
 
-  printk("SJCD: Sanyo CDR-H94A cdrom driver version %d.%d.\n", SJCD_VERSION_MAJOR,
+  printk(KERN_INFO "SJCD: Sanyo CDR-H94A cdrom driver version %d.%d.\n", SJCD_VERSION_MAJOR,
     SJCD_VERSION_MINOR);
 
 #if defined( SJCD_TRACE )
@@ -1473,7 +1473,7 @@
    * Check for card. Since we are booting now, we can't use standard
    * wait algorithm.
    */
-  printk( "SJCD: Resetting: " );
+  printk(KERN_INFO "SJCD: Resetting: " );
   sjcd_send_cmd( SCMD_RESET );
   for( i = 1000; i > 0 && !sjcd_status_valid; --i ){
     unsigned long timer;
@@ -1494,7 +1494,7 @@
   /*
    * Get and print out cdrom version.
    */
-  printk( "SJCD: Getting version: " );
+  printk(KERN_INFO "SJCD: Getting version: " );
   sjcd_send_cmd( SCMD_GET_VERSION );
   for( i = 1000; i > 0 && !sjcd_status_valid; --i ){
     unsigned long timer;
@@ -1525,7 +1525,7 @@
    * Check and print out the tray state. (if it is needed?).
    */
   if( !sjcd_status_valid ){
-    printk( "SJCD: Getting status: " );
+    printk(KERN_INFO "SJCD: Getting status: " );
     sjcd_send_cmd( SCMD_GET_STATUS );
     for( i = 1000; i > 0 && !sjcd_status_valid; --i ){
       unsigned long timer;
@@ -1544,7 +1544,7 @@
     } else printk( "\n" );
   }
 
-  printk("SJCD: Status: port=0x%x.\n", sjcd_base);
+  printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base);
 
   sjcd_present++;
   return( 0 );
@@ -1573,6 +1573,6 @@
   if ( sjcd_cleanup() )
     printk( "SJCD: module: cannot be removed.\n" );
   else
-    printk( "SJCD: module: removed.\n");
+    printk(KERN_INFO "SJCD: module: removed.\n");
 }
 #endif
diff -u --recursive --new-file v1.3.81/linux/drivers/cdrom/sonycd535.c linux/drivers/cdrom/sonycd535.c
--- v1.3.81/linux/drivers/cdrom/sonycd535.c	Wed Mar 27 08:19:28 1996
+++ linux/drivers/cdrom/sonycd535.c	Tue Apr  2 08:43:06 1996
@@ -1512,7 +1512,7 @@
 	sony535_irq_used = 0;
 
 #if DEBUG > 0
-	printk(CDU535_MESSAGE_NAME ": probing base address %03X\n",
+	printk(KERN_INFO CDU535_MESSAGE_NAME ": probing base address %03X\n",
 			sony535_cd_base_io);
 #endif
 	if (check_region(sony535_cd_base_io,4)) {
@@ -1579,7 +1579,7 @@
 				sony_buffer_size = SONY535_BUFFER_SIZE;
 				sony_buffer_sectors = sony_buffer_size / 2048;
 
-				printk(CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
+				printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
 					   drive_config.vendor_id,
 					   drive_config.product_id,
 					   drive_config.product_rev_level);
@@ -1684,6 +1684,6 @@
 	if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
 		printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
 	else
-		printk(CDU535_HANDLE " module released\n");
+		printk(KERN_INFO CDU535_HANDLE " module released\n");
 }
 #endif	/* MODULE */
diff -u --recursive --new-file v1.3.81/linux/drivers/char/atixlmouse.c linux/drivers/char/atixlmouse.c
--- v1.3.81/linux/drivers/char/atixlmouse.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/char/atixlmouse.c	Tue Apr  2 08:43:06 1996
@@ -207,7 +207,7 @@
 	b = inb( ATIXL_MSE_SIGNATURE_PORT );
 	c = inb( ATIXL_MSE_SIGNATURE_PORT );
 	if (( a != b ) && ( a == c ))
-		printk("\nATI Inport ");
+		printk(KERN_INFO "\nATI Inport ");
 	else{
 		mouse.present = 0;
 		return -EIO;
diff -u --recursive --new-file v1.3.81/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c
--- v1.3.81/linux/drivers/char/busmouse.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/char/busmouse.c	Tue Apr  2 08:43:06 1996
@@ -270,7 +270,7 @@
 	mouse.dx = 0;
 	mouse.dy = 0;
 	mouse.wait = NULL;
-	printk("Logitech bus mouse detected, using IRQ %d.\n",
+	printk(KERN_INFO "Logitech bus mouse detected, using IRQ %d.\n",
 	       mouse_irq);
 	misc_register(&bus_mouse);
 	return 0;
diff -u --recursive --new-file v1.3.81/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v1.3.81/linux/drivers/char/istallion.c	Wed Mar 27 08:19:28 1996
+++ linux/drivers/char/istallion.c	Tue Apr  2 08:43:06 1996
@@ -792,7 +792,7 @@
 	printk("cleanup_module()\n");
 #endif
 
-	printk("Unloading %s: version %s\n", stli_drvname, stli_drvversion);
+	printk(KERN_INFO "Unloading %s: version %s\n", stli_drvname, stli_drvversion);
 
 	save_flags(flags);
 	cli();
@@ -4499,7 +4499,7 @@
 
 int stli_init()
 {
-	printk("%s: version %s\n", stli_drvname, stli_drvversion);
+	printk(KERN_INFO "%s: version %s\n", stli_drvname, stli_drvversion);
 
 	stli_initbrds();
 
diff -u --recursive --new-file v1.3.81/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v1.3.81/linux/drivers/char/lp.c	Sun Mar 24 20:07:00 1996
+++ linux/drivers/char/lp.c	Tue Apr  2 08:43:06 1996
@@ -553,7 +553,7 @@
 	if (testvalue == LP_DUMMY) {
 		LP_F(offset) |= LP_EXIST;
 		lp_reset(offset);
-		printk("lp%d at 0x%04x, ", offset, base);
+		printk(KERN_INFO "lp%d at 0x%04x, ", offset, base);
 		request_region(base, size, "lp");
 		if (LP_IRQ(offset))
 			printk("(irq = %d)\n", LP_IRQ(offset));
diff -u --recursive --new-file v1.3.81/linux/drivers/char/misc.c linux/drivers/char/misc.c
--- v1.3.81/linux/drivers/char/misc.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/char/misc.c	Mon Apr  1 10:46:29 1996
@@ -20,15 +20,20 @@
  *
  * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
  *
- * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-May-96
+ * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
+ *
+ * Handling of mouse minor numbers for kerneld:
+ *  Idea by Jacques Gelinas <jack@solucorp.qc.ca>,
+ *  adapted by Bjorn Ekwall <bj0rn@blox.se>
+ *  corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/fs.h>
 #include <linux/errno.h>
 #include <linux/miscdevice.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
 #include <linux/malloc.h>
@@ -37,6 +42,9 @@
 
 #include <linux/tty.h> /* needed by selection.h */
 #include "selection.h" /* export its symbols */
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 /*
  * Head entry for the doubly linked miscdevice list
@@ -76,17 +84,25 @@
 	struct miscdevice *c = misc_list.next;
 	file->f_op = NULL;
 
-	while (c != &misc_list) {
-		if (c->minor == minor) {
-			file->f_op = c->fops;
-			break;
-		}
+	while ((c != &misc_list) && (c->minor != minor))
 		c = c->next;
+	if (c == &misc_list) {
+#ifdef CONFIG_KERNELD
+		char modname[20];
+		sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor);
+		request_module(modname);
+		c = misc_list.next;
+		while ((c != &misc_list) && (c->minor != minor))
+			c = c->next;
+		if (c == &misc_list)
+#endif
+			return -ENODEV;
 	}
 
-	if (file->f_op == NULL)
+	if ((file->f_op = c->fops))
+		return file->f_op->open(inode,file);
+	else
 		return -ENODEV;
-        return file->f_op->open(inode,file);
 }
 
 static struct file_operations misc_fops = {
@@ -145,7 +161,7 @@
 
 void cleanup_module(void)
 {
-	unregister_chrdev(MOUSE_MAJOR, "misc");
+	unregister_chrdev(MISC_MAJOR, "misc");
 }
 
 #endif
diff -u --recursive --new-file v1.3.81/linux/drivers/char/msbusmouse.c linux/drivers/char/msbusmouse.c
--- v1.3.81/linux/drivers/char/msbusmouse.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/char/msbusmouse.c	Tue Apr  2 08:43:06 1996
@@ -204,7 +204,7 @@
 		return -EIO;
 	MS_MSE_INT_OFF();
 	request_region(MS_MSE_CONTROL_PORT, 0x04, "MS Busmouse");
-	printk("Microsoft BusMouse detected and installed.\n");
+	printk(KERN_INFO "Microsoft BusMouse detected and installed.\n");
 	misc_register(&ms_bus_mouse);
 	return 0;
 }
diff -u --recursive --new-file v1.3.81/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v1.3.81/linux/drivers/char/psaux.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/char/psaux.c	Tue Apr  2 08:43:06 1996
@@ -491,7 +491,7 @@
 
 #ifdef CONFIG_82C710_MOUSE
 	if ((qp_found = probe_qp())) {
-		printk("82C710 type pointing device detected -- driver installed.\n");
+		printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n");
 /*		printk("82C710 address = %x (should be 0x310)\n", qp_data); */
 		qp_present = 1;
 		psaux_fops.write = write_qp;
@@ -500,7 +500,7 @@
 	} else
 #endif
 	if (aux_device_present == 0xaa) {
-		printk("PS/2 auxiliary pointing device detected -- driver installed.\n");
+		printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n");
 	 	aux_present = 1;
 		kbd_read_mask = AUX_OBUF_FULL;
 	} else {
diff -u --recursive --new-file v1.3.81/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v1.3.81/linux/drivers/char/stallion.c	Wed Mar 27 08:19:28 1996
+++ linux/drivers/char/stallion.c	Tue Apr  2 08:43:07 1996
@@ -580,7 +580,7 @@
 	printk("cleanup_module()\n");
 #endif
 
-	printk("Unloading %s: version %s\n", stl_drvname, stl_drvversion);
+	printk(KERN_INFO "Unloading %s: version %s\n", stl_drvname, stl_drvversion);
 
 	save_flags(flags);
 	cli();
@@ -3131,7 +3131,7 @@
 
 int stl_init(void)
 {
-	printk("%s: version %s\n", stl_drvname, stl_drvversion);
+	printk(KERN_INFO "%s: version %s\n", stl_drvname, stl_drvversion);
 
 	stl_initbrds();
 
diff -u --recursive --new-file v1.3.81/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
--- v1.3.81/linux/drivers/net/3c501.c	Fri Mar  1 07:50:41 1996
+++ linux/drivers/net/3c501.c	Mon Apr  1 10:46:29 1996
@@ -559,8 +559,9 @@
 			 *	FIXME: is there a logic to whether to keep on trying or
 			 *	reset immediately ?
 			 */
-			printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
-			   " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
+			if(el_debug>1)
+				printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
+			  		" gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
 			inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
 			dev->tbusy = 0;
 			mark_bh(NET_BH);
diff -u --recursive --new-file v1.3.81/linux/drivers/net/3c509.c linux/drivers/net/3c509.c
--- v1.3.81/linux/drivers/net/3c509.c	Thu Mar 21 14:31:22 1996
+++ linux/drivers/net/3c509.c	Mon Apr  1 10:46:29 1996
@@ -1,8 +1,8 @@
 /* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
 /*
-	Written 1993,1994 by Donald Becker.
+	Written 1993-1995 by Donald Becker.
 
-	Copyright 1994 by Donald Becker.
+	Copyright 1994,1995 by Donald Becker.
 	Copyright 1993 United States Government as represented by the
 	Director, National Security Agency.	 This software may be used and
 	distributed according to the terms of the GNU Public License,
@@ -22,13 +22,14 @@
 	packet latency but lower overhead.  If interrupts are disabled for an
 	unusually long time it could also result in missed packets, but in
 	practice this rarely happens.
-	
-	
+
+
 	FIXES:
-		Alan Cox:	Removed the 'Unexpected interrupt' bug.
+		Alan Cox:       Removed the 'Unexpected interrupt' bug.
+		Michael Meskes:	Upgraded to Donald Becker's version 1.07.
 */
 
-static const  char *version = "3c509.c:1.03 10/8/94 becker@cesdis.gsfc.nasa.gov\n";
+static char *version = "3c509.c:1.07 6/15/95 becker@cesdis.gsfc.nasa.gov\n";
 
 #include <linux/module.h>
 
@@ -64,7 +65,6 @@
 #define EL3_DATA 0x00
 #define EL3_CMD 0x0e
 #define EL3_STATUS 0x0e
-#define ID_PORT 0x100
 #define	 EEPROM_READ 0x80
 
 #define EL3_IO_EXTENT	16
@@ -78,11 +78,16 @@
 	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
 	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
 	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
-	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrMask = 14<<11,
-	SetReadZero = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
 	SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
 	StatsDisable = 22<<11, StopCoax = 23<<11,};
 
+enum c509status {
+	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+	IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
+
 /* The SetRxFilter command accepts the following classes: */
 enum RxFilter {
 	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
@@ -110,6 +115,7 @@
 	int head, size;
 	struct sk_buff *queue[SKB_QUEUE_SIZE];
 };
+static int id_port = 0x100;
 
 static ushort id_read_eeprom(int index);
 static ushort read_eeprom(short ioaddr, int index);
@@ -120,7 +126,9 @@
 static struct enet_statistics *el3_get_stats(struct device *dev);
 static int el3_rx(struct device *dev);
 static int el3_close(struct device *dev);
+#ifdef HAVE_MULTICAST
 static void set_multicast_list(struct device *dev);
+#endif
 
 
 
@@ -178,31 +186,46 @@
 	}
 #endif
 
+	/* Reset the ISA PnP mechanism on 3c509b. */
+	outb(0x02, 0x279);           /* Select PnP config control register. */
+	outb(0x02, 0xA79);           /* Return to WaitForKey state. */
+	/* Select an open I/O location at 0x1*0 to do contention select. */
+	for (id_port = 0x100; id_port < 0x200; id_port += 0x10) {
+		outb(0x00, id_port);
+		outb(0xff, id_port);
+		if (inb(id_port) & 0x01)
+			break;
+	}
+	if (id_port >= 0x200) {             /* GCC optimizes this test out. */
+		/* Rare -- do we really need a warning? */
+		printk(" WARNING: No I/O port available for 3c509 activation.\n");
+		return -ENODEV;
+	}
 	/* Next check for all ISA bus boards by sending the ID sequence to the
 	   ID_PORT.  We find cards past the first by setting the 'current_tag'
 	   on cards as they are found.  Cards with their tag set will not
 	   respond to subsequent ID sequences. */
 
-	if (check_region(ID_PORT,1)) {
+	if (check_region(id_port,1)) {
 	  static int once = 1;
-	  if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",ID_PORT);
-	  once = 0;
-	  return -ENODEV;
-	}
+	  if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",id_port);
+          once = 0;
+          return -ENODEV;
+        }
 
-	outb(0x00, ID_PORT);
-	outb(0x00, ID_PORT);
+	outb(0x00, id_port);
+	outb(0x00, id_port);
 	for(i = 0; i < 255; i++) {
-		outb(lrs_state, ID_PORT);
+		outb(lrs_state, id_port);
 		lrs_state <<= 1;
 		lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
 	}
 
 	/* For the first probe, clear all board's tag registers. */
 	if (current_tag == 0)
-		outb(0xd0, ID_PORT);
+		outb(0xd0, id_port);
 	else				/* Otherwise kill off already-found boards. */
-		outb(0xd8, ID_PORT);
+		outb(0xd8, id_port);
 
 	if (id_read_eeprom(7) != 0x6d50) {
 		return -ENODEV;
@@ -220,18 +243,21 @@
 		if_port = iobase >> 14;
 		ioaddr = 0x200 + ((iobase & 0x1f) << 4);
 	}
-	irq = id_read_eeprom(9) >> 12;
+	if (dev->irq > 1  &&  dev->irq < 16)
+		irq = dev->irq;
+	else
+		irq = id_read_eeprom(9) >> 12;
 
 	if (dev->base_addr != 0
-		&&	dev->base_addr != (unsigned short)ioaddr) {
+		&&      dev->base_addr != (unsigned short)ioaddr) {
 		return -ENODEV;
 	}
 
 	/* Set the adaptor tag so that the next card can be found. */
-	outb(0xd0 + ++current_tag, ID_PORT);
+	outb(0xd0 + ++current_tag, id_port);
 
 	/* Activate the adaptor at the EEPROM location. */
-	outb(0xff, ID_PORT);
+	outb((ioaddr >> 4) | 0xe0, id_port);
 
 	EL3WINDOW(0);
 	if (inw(ioaddr) != 0x6d50)
@@ -270,7 +296,9 @@
 	dev->hard_start_xmit = &el3_start_xmit;
 	dev->stop = &el3_close;
 	dev->get_stats = &el3_get_stats;
-	dev->set_multicast_list = &set_multicast_list;
+#ifdef HAVE_MULTICAST
+		dev->set_multicast_list = &set_multicast_list;
+#endif
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
@@ -298,14 +326,14 @@
 
 	/* Issue read command, and pause for at least 162 us. for it to complete.
 	   Assume extra-fast 16Mhz bus. */
-	outb(EEPROM_READ + index, ID_PORT);
+	outb(EEPROM_READ + index, id_port);
 
 	/* This should really be done by looking at one of the timer channels. */
 	for (timer = 0; timer < 162*4 + 400; timer++)
 		SLOW_DOWN_IO;
 
 	for (bit = 15; bit >= 0; bit--)
-		word = (word << 1) + (inb(ID_PORT) & 0x01);
+		word = (word << 1) + (inb(id_port) & 0x01);
 
 	if (el3_debug > 3)
 		printk("  3c509 EEPROM word %d %#4.4x.\n", index, word);
@@ -323,10 +351,12 @@
 
 	outw(TxReset, ioaddr + EL3_CMD);
 	outw(RxReset, ioaddr + EL3_CMD);
-	outw(SetReadZero | 0x00, ioaddr + EL3_CMD);
+	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
 
-	if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev))
+	if (request_irq(dev->irq, &el3_interrupt, 0, "3c509", dev)) {
+		irq2dev_map[dev->irq] = NULL;
 		return -EAGAIN;
+	}
 
 	EL3WINDOW(0);
 	if (el3_debug > 3)
@@ -336,7 +366,6 @@
 	/* Activate board: this is probably unnecessary. */
 	outw(0x0001, ioaddr + 4);
 
-
 	/* Set the IRQ line. */
 	outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
 
@@ -360,8 +389,8 @@
 	EL3WINDOW(6);
 	for (i = 0; i < 9; i++)
 		inb(ioaddr + i);
-	inb(ioaddr + 10);
-	inb(ioaddr + 12);
+	inw(ioaddr + 10);
+	inw(ioaddr + 12);
 
 	/* Switch to register set 1 for normal use. */
 	EL3WINDOW(1);
@@ -377,9 +406,12 @@
 	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
 	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
 	/* Allow status bits to be seen. */
-	outw(SetReadZero | 0xff, ioaddr + EL3_CMD);
-	outw(AckIntr | 0x69, ioaddr + EL3_CMD); /* Ack IRQ */
-	outw(SetIntrMask | 0x98, ioaddr + EL3_CMD); /* Set interrupt mask. */
+	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+	/* Ack all pending events, and set active indicator mask. */
+	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+		 ioaddr + EL3_CMD);
+	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull,
+		 ioaddr + EL3_CMD);
 
 	if (el3_debug > 3)
 		printk("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
@@ -389,40 +421,80 @@
 	return 0;					/* Always succeed */
 }
 
-static void
-el3_tx(struct device *dev)
+static int
+el3_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct el3_private *lp = (struct el3_private *)dev->priv;
 	int ioaddr = dev->base_addr;
-	struct sk_buff * skb;
 
-	if (el3_debug > 5)
-		printk("	TX room bit was handled.\n");
+	/* Transmitter timeout, serious problems. */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 40)
+			return 1;
+		printk("%s: transmit timed out, Tx_status %2.2x status %4.4x "
+			   "Tx FIFO room %d.\n",
+			   dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
+			   inw(ioaddr + TX_FREE));
+		lp->stats.tx_errors++;
+		dev->trans_start = jiffies;
+		/* Issue TX_RESET and TX_START commands. */
+		outw(TxReset, ioaddr + EL3_CMD);
+		outw(TxEnable, ioaddr + EL3_CMD);
+		dev->tbusy = 0;
+	}
 
-	outw(AckIntr | 0x08, ioaddr + EL3_CMD);
-	if (!lp->size)
-		return;
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
 
-	/* There's room in the FIFO for a full-sized packet. */
-	while (inw(ioaddr + TX_FREE) > 1536) {
-		skb = lp->queue[lp->head];
-		lp->head = (lp->head + 1) & (SKB_QUEUE_SIZE-1);
-		lp->size--;
+	if (skb->len <= 0)
+		return 0;
 
+	if (el3_debug > 4) {
+		printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n",
+			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+	}
+#if 0
+#ifndef final_version
+	{	/* Error-checking code, delete someday. */
+		ushort status = inw(ioaddr + EL3_STATUS);
+		if (status & 0x0001 		/* IRQ line active, missed one. */
+			&& inw(ioaddr + EL3_STATUS) & 1) { 			/* Make sure. */
+			printk("%s: Missed interrupt, status then %04x now %04x"
+				   "  Tx %2.2x Rx %4.4x.\n", dev->name, status,
+				   inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
+				   inw(ioaddr + RX_STATUS));
+			/* Fake interrupt trigger by masking, acknowledge interrupts. */
+			outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+			outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+				 ioaddr + EL3_CMD);
+			outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+		}
+	}
+#endif
+#endif
+	/* Avoid timer-based retransmission conflicts. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0)
+		printk("%s: Transmitter access conflict.\n", dev->name);
+	else {
 		/* Put out the doubleword header... */
 		outw(skb->len, ioaddr + TX_FIFO);
 		outw(0x00, ioaddr + TX_FIFO);
 		/* ... and the packet rounded to a doubleword. */
 		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
 
-		/* free the skb, we're done with it */
-		dev_kfree_skb(skb, FREE_WRITE);
 		dev->trans_start = jiffies;
-		if (!lp->size)
-			return;
+		if (inw(ioaddr + TX_FREE) > 1536) {
+			dev->tbusy = 0;
+		} else
+			/* Interrupt us when the FIFO has room for max-sized packet. */
+			outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
 	}
-	/* Interrupt us when the FIFO has room for max-sized packet. */
-	outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+
+	dev_kfree_skb (skb, FREE_WRITE);
+
 	/* Clear the Tx status stack. */
 	{
 		short tx_status;
@@ -435,44 +507,6 @@
 			outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
 		}
 	}
-}
-
-static int
-el3_start_xmit(struct sk_buff *skb, struct device *dev)
-{
-	struct el3_private *lp = (struct el3_private *)dev->priv;
-	unsigned long flags;
-	int ioaddr = dev->base_addr;
-
-	save_flags(flags);
-	cli();
-	/*
-	 * Do we have room in the send queue?
-	 */
-	if (lp->size < SKB_QUEUE_SIZE) {
-		int tail = (lp->head + lp->size) & (SKB_QUEUE_SIZE-1);
-		lp->queue[tail] = skb;
-		lp->size++;
-		/* fake a transmit interrupt to get it started */
-		el3_tx(dev);
-		restore_flags(flags);
-		return 0;
-	}
-
-	/*
-	 * No space, check if we might have timed out?
-	 */
-	if (jiffies - dev->trans_start > HZ) {
-		printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
-			   dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
-		dev->trans_start = jiffies;
-		/* Issue TX_RESET and TX_START commands. */
-		outw(TxReset, ioaddr + EL3_CMD);
-		outw(TxEnable, ioaddr + EL3_CMD);
-	}
-	/* free the skb, we might as well drop it on the floor */
-	dev_kfree_skb(skb, FREE_WRITE);
-	restore_flags(flags);
 	return 0;
 }
 
@@ -499,16 +533,40 @@
 	if (el3_debug > 4)
 		printk("%s: interrupt, status %4.4x.\n", dev->name, status);
 
-	while ((status = inw(ioaddr + EL3_STATUS)) & 0x91) {
+	while ((status = inw(ioaddr + EL3_STATUS)) &
+		   (IntLatch | RxComplete | StatsFull)) {
 
-		if (status & 0x10)
+		if (status & RxComplete)
 			el3_rx(dev);
 
-		if (status & 0x08)
-			el3_tx(dev);
-
-		if (status & 0x80)				/* Statistics full. */
-			update_stats(ioaddr, dev);
+		if (status & TxAvailable) {
+			if (el3_debug > 5)
+				printk("	TX room bit was handled.\n");
+			/* There's room in the FIFO for a full-sized packet. */
+			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		}
+		if (status & (AdapterFailure | RxEarly | StatsFull)) {
+			/* Handle all uncommon interrupts. */
+			if (status & StatsFull)				/* Empty statistics. */
+				update_stats(ioaddr, dev);
+			if (status & RxEarly) {				/* Rx early is unused. */
+				el3_rx(dev);
+				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+			}
+			if (status & AdapterFailure) {
+				/* Adapter failure requires Rx reset and reinit. */
+				outw(RxReset, ioaddr + EL3_CMD);
+				/* Set the Rx filter to the current state. */
+				outw(SetRxFilter | RxStation | RxBroadcast
+					 | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
+					 | (dev->flags & IFF_PROMISC ? RxProm : 0),
+					 ioaddr + EL3_CMD);
+				outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+			}
+		}
 
 		if (++i > 10) {
 			printk("%s: Infinite loop in interrupt, status %4.4x.\n",
@@ -518,8 +576,7 @@
 			break;
 		}
 		/* Acknowledge the IRQ. */
-		outw(AckIntr | 0x41, ioaddr + EL3_CMD); /* Ack IRQ */
-
+		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
 	}
 
 	if (el3_debug > 4) {
@@ -610,7 +667,7 @@
 					   pkt_len, rx_status);
 			if (skb != NULL) {
 				skb->dev = dev;
-				skb_reserve(skb,2);	/* Align IP on 16 byte boundaries */
+				skb_reserve(skb,2);     /* Align IP on 16 byte */
 
 				/* 'skb->data' points to the start of sk_buff data area. */
 				insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
@@ -635,11 +692,12 @@
 	return 0;
 }
 
-/* 
- *	Set or clear the multicast filter for this adaptor.
+#ifdef HAVE_MULTICAST
+/*
+ *     Set or clear the multicast filter for this adaptor.
  */
- 
-static void set_multicast_list(struct device *dev)
+static void
+set_multicast_list(struct device *dev)
 {
 	short ioaddr = dev->base_addr;
 	if (el3_debug > 1) {
@@ -649,18 +707,17 @@
 			printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count);
 		}
 	}
-	if (dev->flags&IFF_PROMISC) 
-	{
+	if (dev->flags&IFF_PROMISC) {
 		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
 			 ioaddr + EL3_CMD);
 	}
-	else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) 
-	{
-		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
-	} 
+	else if (dev->mc_count || (dev->flags&IFF_ALLMULTI)) {
+		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
+	}
 	else
-		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+                outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
 }
+#endif
 
 static int
 el3_close(struct device *dev)
@@ -703,22 +760,14 @@
 #ifdef MODULE
 static char devicename[9] = { 0, };
 static struct device dev_3c509 = {
-	devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-	0, 0, 0, 0,
-	0, 0,
-	0, 0, 0, NULL, el3_probe };
-
-static int io = 0;
-static int irq = 0;
+        devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+        0, 0, 0, 0,
+        0, 0,
+        0, 0, 0, NULL, el3_probe };
 
 int
 init_module(void)
 {
-	dev_3c509.base_addr = io;
-	dev_3c509.irq       = irq;
-	if (!EISA_bus && !io) {
-		printk("3c509: WARNING! Module load-time probing works reliably only for EISA-bus!\n");
-	}
 	if (register_netdev(&dev_3c509) != 0)
 		return -EIO;
 	return 0;
@@ -727,11 +776,16 @@
 void
 cleanup_module(void)
 {
-	unregister_netdev(&dev_3c509);
-	kfree_s(dev_3c509.priv,sizeof(struct el3_private));
-	dev_3c509.priv=NULL;
-	/* If we don't do this, we can't re-insmod it later. */
-	release_region(dev_3c509.base_addr, EL3_IO_EXTENT);
+	if (MOD_IN_USE)
+		printk("3c509: device busy, remove delayed\n");
+	else
+	{
+		unregister_netdev(&dev_3c509);
+		kfree_s(dev_3c509.priv,sizeof(struct el3_private));
+		dev_3c509.priv=NULL;
+		/* If we don't do this, we can't re-insmod it later. */
+		release_region(dev_3c509.base_addr, EL3_IO_EXTENT);
+	}
 }
 #endif /* MODULE */
 
diff -u --recursive --new-file v1.3.81/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.81/linux/drivers/net/Config.in	Mon Mar 25 08:58:20 1996
+++ linux/drivers/net/Config.in	Mon Apr  1 10:46:29 1996
@@ -11,15 +11,16 @@
 if [ ! "$CONFIG_PPP" = "n" ]; then
   comment 'CCP compressors for PPP are only built as modules.'
 fi
+tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
 tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
 tristate 'PLIP (parallel port) support' CONFIG_PLIP
 tristate 'EQL (serial line load balancing) support' CONFIG_EQUALIZER
 
-tristate 'FRAD (Frame Relay Access Device) support' CONFIG_FRAD
-if [ "$CONFIG_FRAD" = "y" -o "$CONFIG_FRAD" = "m" ]; then
+tristate 'DLCI (Frame relay) support' CONFIG_DLCI
+if [ "$CONFIG_DLCI" = "y" -o "$CONFIG_DLCI" = "m" ]; then
   int '  Max open DLCI' CONFIG_DLCI_COUNT 24
   int '  Max DLCI per device' CONFIG_DLCI_MAX 8
-  tristate '  Sangoma S502A FRAD support' CONFIG_SDLA
+  tristate '  SDLA (Sangoma S502/S508) support' CONFIG_SDLA
 fi
 
 bool 'Do you want to be offered ALPHA test drivers' CONFIG_NET_ALPHA
@@ -30,6 +31,9 @@
   tristate 'SMC 9194 support' CONFIG_SMC9194
 fi
 bool 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE
+if [ "$CONFIG_LANCE" = "y" ]; then
+  bool 'AMD PCInet32 (VLB and PCI) support' CONFIG_LANCE32
+fi  
 bool '3COM cards' CONFIG_NET_VENDOR_3COM
 if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
   tristate '3c501 support' CONFIG_EL1
@@ -97,3 +101,4 @@
   bool '  Enable arc0e (ARCnet "Ether-Encap" packet format)' CONFIG_ARCNET_ETH
   bool '  Enable arc0s (ARCnet RFC1051 packet format)' CONFIG_ARCNET_1051
 fi
+
diff -u --recursive --new-file v1.3.81/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.81/linux/drivers/net/Makefile	Sun Mar 31 00:13:17 1996
+++ linux/drivers/net/Makefile	Tue Apr  2 07:22:04 1996
@@ -178,6 +178,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_STRIP),y)
+L_OBJS += strip.o
+else
+  ifeq ($(CONFIG_STRIP),m)
+  M_OBJS += strip.o
+  endif
+endif
+
 ifeq ($(CONFIG_DE650),y)
 ETDRV_OBJS := $(L_OBJS) de650.o
 CONFIG_8390_BUILTIN = y
@@ -217,6 +225,9 @@
 
 ifeq ($(CONFIG_LANCE),y)
 L_OBJS += lance.o
+  ifeq ($(CONFIG_LANCE32),y)
+  L_OBJS += lance32.o
+  endif
 endif
 
 ifeq ($(CONFIG_AT1700),y)
@@ -431,10 +442,10 @@
   endif
 endif
 
-ifeq ($(CONFIG_FRAD),y)
+ifeq ($(CONFIG_DLCI),y)
 L_OBJS += dlci.o 
 else
-  ifeq ($(CONFIG_FRAD),m)
+  ifeq ($(CONFIG_DLCI),m)
   M_OBJS += dlci.o
   endif
 endif
@@ -480,6 +491,9 @@
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(PLIP_OPTS) -c $<
 
 slip.o:	slip.c CONFIG
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
+
+strip.o: strip.c CONFIG
 	$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
 
 dummy.o: dummy.c CONFIG
diff -u --recursive --new-file v1.3.81/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.3.81/linux/drivers/net/Space.c	Mon Mar 25 08:58:20 1996
+++ linux/drivers/net/Space.c	Mon Apr  1 10:46:29 1996
@@ -198,7 +198,7 @@
 
 /* This must be AFTER the various FRADs so it initializes FIRST! */
 	
-#ifdef CONFIG_FRAD
+#ifdef CONFIG_DLCI
     extern int dlci_init(struct device *);
     static struct device dlci_dev = { "dlci", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, dlci_init, };
 #   undef NEXT_DEV
@@ -279,6 +279,14 @@
 #define NEXT_DEV (&slip_bootstrap)
 #endif	/* SLIP */
   
+#if defined(CONFIG_STRIP)
+extern int strip_init_ctrl_dev(struct device *);
+static struct device strip_bootstrap = {
+    "strip_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, strip_init_ctrl_dev, };
+#undef NEXT_DEV
+#define NEXT_DEV (&strip_bootstrap)
+#endif   /* STRIP */
+    
 #if defined(CONFIG_PPP)
 extern int ppp_init(struct device *);
 static struct device ppp_bootstrap = {
diff -u --recursive --new-file v1.3.81/linux/drivers/net/lance.c linux/drivers/net/lance.c
--- v1.3.81/linux/drivers/net/lance.c	Sun Mar 10 09:28:56 1996
+++ linux/drivers/net/lance.c	Mon Apr  1 10:46:30 1996
@@ -32,7 +32,7 @@
                                                           SAW
 */
 
-static const char *version = "lance.c:v1.08.01 Mar 6 1996 saw@shade.msu.ru\n";
+static const char *version = "lance.c:v1.08.02 Mar 17 1996 tsbogend@bigbug.franken.de\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -159,6 +159,14 @@
 #define LANCE_KMALLOC(x) \
 	((void *) (((unsigned long)kmalloc((x)+7, GFP_DMA | GFP_KERNEL)+7) & ~7))
 
+/*
+ * Changes:
+ *	Thomas Bogendoerfer (tsbogend@bigbug.franken.de):
+ *	- added support for Linux/Alpha, but removed most of it, because
+ *        it worked only for the PCI chip. 
+ *      - added hook for the 32bit lance driver
+ */
+
 /* Set the number of Tx and Rx buffers, using Log_2(# buffers).
    Reasonable default values are 16 Tx buffers, and 16 Rx buffers.
    That translates to 4 and 4 (16 == 2^^4). */
@@ -186,25 +194,25 @@
 
 /* The LANCE Rx and Tx ring descriptors. */
 struct lance_rx_head {
-	int base;
-	short buf_length;			/* This length is 2s complement (negative)! */
-	short msg_length;			/* This length is "normal". */
+	s32 base;
+	s16 buf_length;			/* This length is 2s complement (negative)! */
+	s16 msg_length;			/* This length is "normal". */
 };
 
 struct lance_tx_head {
-	int	  base;
-	short length;				/* Length is 2s complement (negative)! */
-	short misc;
+	s32 base;
+	s16 length;				/* Length is 2s complement (negative)! */
+	s16 misc;
 };
 
 /* The LANCE initialization block, described in databook. */
 struct lance_init_block {
-	unsigned short mode;		/* Pre-set mode (reg. 15) */
-	unsigned char phys_addr[6]; /* Physical ethernet address */
-	unsigned filter[2];			/* Multicast filter (unused). */
+	u16 mode;		/* Pre-set mode (reg. 15) */
+	u8  phys_addr[6]; /* Physical ethernet address */
+	u32 filter[2];			/* Multicast filter (unused). */
 	/* Receive and transmit ring base, along with extra bits. */
-	unsigned rx_ring;			/* Tx and Rx ring base pointers */
-	unsigned tx_ring;
+	u32  rx_ring;			/* Tx and Rx ring base pointers */
+	u32  tx_ring;
 };
 
 struct lance_private {
@@ -224,7 +232,7 @@
 	struct enet_statistics stats;
 	unsigned char chip_version;	/* See lance_chip_type. */
 	char tx_full;
-	char lock;
+	unsigned long lock;
 };
 
 #define LANCE_MUST_PAD          0x00000001
@@ -290,7 +298,9 @@
 
 int lance_init(void)
 {
+#ifndef __alpha__    
 	int *port;
+#endif    
 
 	if (high_memory <= 16*1024*1024)
 		lance_need_isa_bounce_buffers = 0;
@@ -334,6 +344,8 @@
 	}
 #endif  /* defined(CONFIG_PCI) */
 
+/* On the Alpha don't look for PCnet chips on the ISA bus */
+#ifndef __alpha__
 	for (port = lance_portlist; *port; port++) {
 		int ioaddr = *port;
 
@@ -347,6 +359,7 @@
 				lance_probe1(ioaddr);
 		}
 	}
+#endif
 
 	return 0;
 }
@@ -363,6 +376,7 @@
 	int hp_builtin = 0;					/* HP on-board ethernet. */
 	static int did_version = 0;			/* Already printed version info. */
 
+#ifndef __alpha__
 	/* First we look for special cases.
 	   Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
 	   There are two HP versions, check the BIOS for the configuration port.
@@ -379,6 +393,7 @@
 	/* We also recognize the HP Vectra on-board here, but check below. */
 	hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00
 				&& inb(ioaddr+2) == 0x09);
+#endif
 
 	/* Reset the LANCE.	 */
 	reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
@@ -423,6 +438,15 @@
 	dev->base_addr = ioaddr;
 	request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name);
 
+#ifdef CONFIG_LANCE32
+        /* look if it's a PCI or VLB chip */
+        if (lance_version == PCNET_PCI || lance_version == PCNET_VLB) {
+	    extern void lance32_probe1 (struct device *dev, const char *chipname, int pci_irq_line);
+	    
+	    lance32_probe1 (dev, chipname, pci_irq_line);
+	    return;
+	}
+#endif    
 	/* Make certain the data structures used by the LANCE are aligned and DMAble. */
 	lp = (struct lance_private *) LANCE_KMALLOC(sizeof(*lp));
 	if (lance_debug > 6) printk(" (#0x%05lx)", (unsigned long)lp);
@@ -442,15 +466,15 @@
 		lp->init_block.phys_addr[i] = dev->dev_addr[i];
 	lp->init_block.filter[0] = 0x00000000;
 	lp->init_block.filter[1] = 0x00000000;
-	lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
-	lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
+	lp->init_block.rx_ring = ((u32)virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
+	lp->init_block.tx_ring = ((u32)virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
 
 	outw(0x0001, ioaddr+LANCE_ADDR);
 	inw(ioaddr+LANCE_ADDR);
-	outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
+	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
 	outw(0x0002, ioaddr+LANCE_ADDR);
 	inw(ioaddr+LANCE_ADDR);
-	outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
 	outw(0x0000, ioaddr+LANCE_ADDR);
 	inw(ioaddr+LANCE_ADDR);
 
@@ -617,15 +641,17 @@
 
 	if (lance_debug > 1)
 		printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
-			   dev->name, dev->irq, dev->dma, (int) lp->tx_ring, (int) lp->rx_ring,
-			   (int) &lp->init_block);
+			   dev->name, dev->irq, dev->dma,
+		           (u32) virt_to_bus(lp->tx_ring),
+		           (u32) virt_to_bus(lp->rx_ring),
+			   (u32) virt_to_bus(&lp->init_block));
 
 	lance_init_ring(dev);
 	/* Re-initialize the LANCE, and start it when done. */
 	outw(0x0001, ioaddr+LANCE_ADDR);
-	outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
+	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
 	outw(0x0002, ioaddr+LANCE_ADDR);
-	outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
 
 	outw(0x0004, ioaddr+LANCE_ADDR);
 	outw(0x0915, ioaddr+LANCE_DATA);
@@ -648,7 +674,7 @@
 
 	if (lance_debug > 2)
 		printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
-			   dev->name, i, (int) &lp->init_block, inw(ioaddr+LANCE_DATA));
+			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA));
 
 	return 0;					/* Always succeed */
 }
@@ -692,7 +718,7 @@
 	lp->dirty_rx = lp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		lp->rx_ring[i].base = (lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
+		lp->rx_ring[i].base = (u32)virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
 		lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
 	}
 	/* The Tx buffer address is filled in as needed, but we do need to clear
@@ -706,8 +732,8 @@
 		lp->init_block.phys_addr[i] = dev->dev_addr[i];
 	lp->init_block.filter[0] = 0x00000000;
 	lp->init_block.filter[1] = 0x00000000;
-	lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
-	lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
+	lp->init_block.rx_ring = ((u32)virt_to_bus(lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS;
+	lp->init_block.tx_ring = ((u32)virt_to_bus(lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS;
 }
 
 static void
@@ -815,17 +841,17 @@
 
 	/* If any part of this buffer is >16M we must copy it to a low-memory
 	   buffer. */
-	if ((int)(skb->data) + skb->len > 0x01000000) {
+	if ((u32)virt_to_bus(skb->data) + skb->len > 0x01000000) {
 		if (lance_debug > 5)
 			printk("%s: bouncing a high-memory packet (%#x).\n",
-				   dev->name, (int)(skb->data));
+				   dev->name, (u32)virt_to_bus(skb->data));
 		memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len);
 		lp->tx_ring[entry].base =
-			(int)(lp->tx_bounce_buffs + entry) | 0x83000000;
+			((u32)virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000;
 		dev_kfree_skb (skb, FREE_WRITE);
 	} else {
 		lp->tx_skbuff[entry] = skb;
-		lp->tx_ring[entry].base = (int)(skb->data) | 0x83000000;
+		lp->tx_ring[entry].base = ((u32)virt_to_bus(skb->data) & 0xffffff) | 0x83000000;
 	}
 	lp->cur_tx++;
 
@@ -1033,7 +1059,7 @@
 				skb_reserve(skb,2);	/* 16 byte align */
 				skb_put(skb,pkt_len);	/* Make room */
 				eth_copy_and_sum(skb,
-					(unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
+					(unsigned char *)bus_to_virt((lp->rx_ring[entry].base & 0x00ffffff)),
 					pkt_len,0);
 				skb->protocol=eth_type_trans(skb,dev);
 				netif_rx(skb);
diff -u --recursive --new-file v1.3.81/linux/drivers/net/lance32.c linux/drivers/net/lance32.c
--- v1.3.81/linux/drivers/net/lance32.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/lance32.c	Mon Apr  1 10:46:30 1996
@@ -0,0 +1,833 @@
+/* lance32.c: An AMD PCnet32 ethernet driver for linux. */
+/*
+ *      Copyright 1996 Thomas Bogendoerfer
+ * 
+ * 	Derived from the lance driver written 1993,1994,1995 by Donald Becker.
+ * 
+ * 	Copyright 1993 United States Government as represented by the
+ * 	Director, National Security Agency.
+ * 
+ * 	This software may be used and distributed according to the terms
+ * 	of the GNU Public License, incorporated herein by reference.
+ *
+ * 	This driver is for PCnet32 and PCnetPCI based ethercards
+ */
+
+static const char *version = "lance32.c:v0.02 17.3.96 tsbogend@bigbug.franken.de\n";
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef LANCE32_DEBUG
+int lance32_debug = LANCE32_DEBUG;
+#else
+int lance32_debug = 1;
+#endif
+
+/*
+ * 				Theory of Operation
+ * 
+ * This driver uses the same software structure as the normal lance
+ * driver. So look for a verbose description in lance.c. The differences
+ * to the normal lance driver is the use of the 32bit mode of PCnet32
+ * and PCnetPCI chips. Because these chips are 32bit chips, there is no
+ * 16MB limitation and we don't need bounce buffers.
+ */
+ 
+/*
+ * History:
+ * v0.01:  Initial version
+ *         only tested on Alpha Noname Board
+ * v0.02:  changed IRQ handling for new interrupt scheme (dev_id)
+ *         tested on a ASUS SP3G
+ */
+
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+ * That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4).
+ */
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 4
+#define LANCE_LOG_RX_BUFFERS 4
+#endif
+
+#define TX_RING_SIZE			(1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK		(TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS		((LANCE_LOG_TX_BUFFERS) << 12)
+
+#define RX_RING_SIZE			(1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK		(RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS		((LANCE_LOG_RX_BUFFERS) << 4)
+
+#define PKT_BUF_SZ		1544
+
+/* Offsets from base I/O address. */
+#define LANCE_DATA 0x10
+#define LANCE_ADDR 0x12
+#define LANCE_RESET 0x14
+#define LANCE_BUS_IF 0x16
+#define LANCE_TOTAL_SIZE 0x18
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance32_rx_head {
+	u32 base;
+	s16 buf_length;
+        s16 status;    
+	u32 msg_length;
+	u32 reserved;
+};
+	
+struct lance32_tx_head {
+	u32 base;
+	s16 length;
+        s16 status;
+	u32 misc;
+	u32 reserved;
+};
+
+
+/* The LANCE 32-Bit initialization block, described in databook. */
+struct lance32_init_block {
+	u16 mode;
+	u16 tlen_rlen;
+	u8  phys_addr[6];
+	u16 reserved;
+	u32 filter[2];
+	/* Receive and transmit ring base, along with extra bits. */    
+	u32 rx_ring;
+	u32 tx_ring;
+};
+
+struct lance32_private {
+	/* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */
+	struct lance32_rx_head   rx_ring[RX_RING_SIZE];
+	struct lance32_tx_head   tx_ring[TX_RING_SIZE];
+	struct lance32_init_block	init_block;
+	const char *name;
+	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
+	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	unsigned long rx_buffs;			/* Address of Rx and Tx buffers. */
+	int cur_rx, cur_tx;			/* The next free ring entry */
+	int dirty_rx, dirty_tx;		        /* The ring entries to be free()ed. */
+	int dma;
+	struct enet_statistics stats;
+	char tx_full;
+	unsigned long lock;
+};
+
+static int lance32_open(struct device *dev);
+static void lance32_init_ring(struct device *dev);
+static int lance32_start_xmit(struct sk_buff *skb, struct device *dev);
+static int lance32_rx(struct device *dev);
+static void lance32_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int lance32_close(struct device *dev);
+static struct enet_statistics *lance32_get_stats(struct device *dev);
+static void lance32_set_multicast_list(struct device *dev);
+
+
+
+/* lance32_probe1 */
+void lance32_probe1(struct device *dev, char *chipname, int pci_irq_line)
+{
+	struct lance32_private *lp;
+        int ioaddr = dev->base_addr;
+	short dma_channels;					/* Mark spuriously-busy DMA channels */    
+        int i;
+    
+	/* Make certain the data structures used by the LANCE are 16byte aligned and DMAble. */
+	lp = (struct lance32_private *) (((unsigned long)kmalloc(sizeof(*lp)+15, GFP_DMA | GFP_KERNEL)+15) & ~15);
+      
+	memset(lp, 0, sizeof(*lp));
+	dev->priv = lp;
+	lp->name = chipname;
+	lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL);
+
+	lp->init_block.mode = 0x0003;		/* Disable Rx and Tx. */
+        lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;    
+	for (i = 0; i < 6; i++)
+		lp->init_block.phys_addr[i] = dev->dev_addr[i];
+	lp->init_block.filter[0] = 0x00000000;
+	lp->init_block.filter[1] = 0x00000000;
+	lp->init_block.rx_ring = (u32)virt_to_bus(lp->rx_ring);
+	lp->init_block.tx_ring = (u32)virt_to_bus(lp->tx_ring);
+    
+	/* switch lance to 32bit mode */
+	outw(0x0014, ioaddr+LANCE_ADDR);
+	outw(0x0002, ioaddr+LANCE_BUS_IF);
+
+	outw(0x0001, ioaddr+LANCE_ADDR);
+	inw(ioaddr+LANCE_ADDR);
+	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
+	outw(0x0002, ioaddr+LANCE_ADDR);
+	inw(ioaddr+LANCE_ADDR);
+	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
+	outw(0x0000, ioaddr+LANCE_ADDR);
+	inw(ioaddr+LANCE_ADDR);
+    
+	if (pci_irq_line) {
+		dev->dma = 4;			/* Native bus-master, no DMA channel needed. */
+		dev->irq = pci_irq_line;
+	} else {
+		/* The DMA channel may be passed in PARAM1. */
+		if (dev->mem_start & 0x07)
+			dev->dma = dev->mem_start & 0x07;
+	}
+
+	if (dev->dma == 0) {
+		/* Read the DMA channel status register, so that we can avoid
+		   stuck DMA channels in the DMA detection below. */
+		dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
+			(inb(DMA2_STAT_REG) & 0xf0);
+	}
+	if (dev->irq >= 2)
+		printk(" assigned IRQ %d", dev->irq);
+	else {
+		/* To auto-IRQ we enable the initialization-done and DMA error
+		   interrupts. For ISA boards we get a DMA error, but VLB and PCI
+		   boards will work. */
+		autoirq_setup(0);
+
+		/* Trigger an initialization just for the interrupt. */
+		outw(0x0041, ioaddr+LANCE_DATA);
+
+		dev->irq = autoirq_report(1);
+		if (dev->irq)
+			printk(", probed IRQ %d", dev->irq);
+		else {
+			printk(", failed to detect IRQ line.\n");
+			return;
+		}
+
+		/* Check for the initialization done bit, 0x0100, which means
+		   that we don't need a DMA channel. */
+		if (inw(ioaddr+LANCE_DATA) & 0x0100)
+			dev->dma = 4;
+	}
+
+	if (dev->dma == 4) {
+		printk(", no DMA needed.\n");
+	} else if (dev->dma) {
+		if (request_dma(dev->dma, chipname)) {
+			printk("DMA %d allocation failed.\n", dev->dma);
+			return;
+		} else
+			printk(", assigned DMA %d.\n", dev->dma);
+	} else {			/* OK, we have to auto-DMA. */
+		for (i = 0; i < 4; i++) {
+			static const char dmas[] = { 5, 6, 7, 3 };
+			int dma = dmas[i];
+			int boguscnt;
+
+			/* Don't enable a permanently busy DMA channel, or the machine
+			   will hang. */
+			if (test_bit(dma, &dma_channels))
+				continue;
+			outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
+			if (request_dma(dma, chipname))
+				continue;
+			set_dma_mode(dma, DMA_MODE_CASCADE);
+			enable_dma(dma);
+
+			/* Trigger an initialization. */
+			outw(0x0001, ioaddr+LANCE_DATA);
+			for (boguscnt = 100; boguscnt > 0; --boguscnt)
+				if (inw(ioaddr+LANCE_DATA) & 0x0900)
+					break;
+			if (inw(ioaddr+LANCE_DATA) & 0x0100) {
+				dev->dma = dma;
+				printk(", DMA %d.\n", dev->dma);
+				break;
+			} else {
+				disable_dma(dma);
+				free_dma(dma);
+			}
+		}
+		if (i == 4) {			/* Failure: bail. */
+			printk("DMA detection failed.\n");
+			return;
+		}
+	}
+    
+        outw(0x0002, ioaddr+LANCE_ADDR);
+	outw(0x0002, ioaddr+LANCE_BUS_IF);
+
+	if (lance32_debug > 0)
+		printk(version);
+
+	/* The LANCE-specific entries in the device structure. */
+	dev->open = &lance32_open;
+	dev->hard_start_xmit = &lance32_start_xmit;
+	dev->stop = &lance32_close;
+	dev->get_stats = &lance32_get_stats;
+	dev->set_multicast_list = &lance32_set_multicast_list;
+
+	return;
+}
+
+
+static int
+lance32_open(struct device *dev)
+{
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int i;
+
+	if (dev->irq == 0 ||
+		request_irq(dev->irq, &lance32_interrupt, 0, lp->name, (void *)dev)) {
+		return -EAGAIN;
+	}
+
+	irq2dev_map[dev->irq] = dev;
+
+	/* Reset the LANCE */
+	inw(ioaddr+LANCE_RESET);
+
+	/* switch lance to 32bit mode */
+	outw(0x0014, ioaddr+LANCE_ADDR);
+	outw(0x0002, ioaddr+LANCE_BUS_IF);
+
+	/* The DMA controller is used as a no-operation slave, "cascade mode". */
+	if (dev->dma != 4) {
+		enable_dma(dev->dma);
+		set_dma_mode(dev->dma, DMA_MODE_CASCADE);
+	}
+
+	/* Turn on auto-select of media (AUI, BNC). */
+	outw(0x0002, ioaddr+LANCE_ADDR);
+	outw(0x0002, ioaddr+LANCE_BUS_IF);
+
+	if (lance32_debug > 1)
+		printk("%s: lance32_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
+			   dev->name, dev->irq, dev->dma,
+		           (u32) virt_to_bus(lp->tx_ring),
+		           (u32) virt_to_bus(lp->rx_ring),
+			   (u32) virt_to_bus(&lp->init_block));
+
+	lance32_init_ring(dev);
+	/* Re-initialize the LANCE, and start it when done. */
+	outw(0x0001, ioaddr+LANCE_ADDR);
+	outw((short) (u32) virt_to_bus(&lp->init_block), ioaddr+LANCE_DATA);
+	outw(0x0002, ioaddr+LANCE_ADDR);
+	outw(((u32)virt_to_bus(&lp->init_block)) >> 16, ioaddr+LANCE_DATA);
+
+	outw(0x0004, ioaddr+LANCE_ADDR);
+	outw(0x0915, ioaddr+LANCE_DATA);
+
+	outw(0x0000, ioaddr+LANCE_ADDR);
+	outw(0x0001, ioaddr+LANCE_DATA);
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+	i = 0;
+	while (i++ < 100)
+		if (inw(ioaddr+LANCE_DATA) & 0x0100)
+			break;
+	/* 
+	 * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+	 * reports that doing so triggers a bug in the '974.
+	 */
+ 	outw(0x0042, ioaddr+LANCE_DATA);
+
+	if (lance32_debug > 2)
+		printk("%s: LANCE32 open after %d ticks, init block %#x csr0 %4.4x.\n",
+			   dev->name, i, (u32) virt_to_bus(&lp->init_block), inw(ioaddr+LANCE_DATA));
+
+	return 0;					/* Always succeed */
+}
+
+/*
+ * The LANCE has been halted for one reason or another (busmaster memory
+ * arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
+ * etc.).  Modern LANCE variants always reload their ring-buffer
+ * configuration when restarted, so we must reinitialize our ring
+ * context before restarting.  As part of this reinitialization,
+ * find all packets still on the Tx ring and pretend that they had been
+ * sent (in effect, drop the packets on the floor) - the higher-level
+ * protocols will time out and retransmit.  It'd be better to shuffle
+ * these skbs to a temp list and then actually re-Tx them after
+ * restarting the chip, but I'm too lazy to do so right now.  dplatt@3do.com
+ */
+
+static void 
+lance32_purge_tx_ring(struct device *dev)
+{
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;
+	int i;
+
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (lp->tx_skbuff[i]) {
+			dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE);
+			lp->tx_skbuff[i] = NULL;
+		}
+	}
+}
+
+
+/* Initialize the LANCE Rx and Tx rings. */
+static void
+lance32_init_ring(struct device *dev)
+{
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;
+	int i;
+
+	lp->lock = 0, lp->tx_full = 0;
+	lp->cur_rx = lp->cur_tx = 0;
+	lp->dirty_rx = lp->dirty_tx = 0;
+
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		lp->rx_ring[i].base = (u32)virt_to_bus((char *)lp->rx_buffs + i*PKT_BUF_SZ);
+		lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
+	        lp->rx_ring[i].status = 0x8000;	    
+	}
+	/* The Tx buffer address is filled in as needed, but we do need to clear
+	   the upper ownership bit. */
+	for (i = 0; i < TX_RING_SIZE; i++) {
+	        lp->tx_ring[i].base = 0;
+	        lp->tx_ring[i].status = 0;
+	}
+
+	lp->init_block.mode = 0x0000;
+        lp->init_block.tlen_rlen = TX_RING_LEN_BITS | RX_RING_LEN_BITS;
+	for (i = 0; i < 6; i++)
+		lp->init_block.phys_addr[i] = dev->dev_addr[i];
+	lp->init_block.filter[0] = 0x00000000;
+	lp->init_block.filter[1] = 0x00000000;
+	lp->init_block.rx_ring = (u32)virt_to_bus(lp->rx_ring);
+	lp->init_block.tx_ring = (u32)virt_to_bus(lp->tx_ring);
+}
+
+static void
+lance32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
+{
+	lance32_purge_tx_ring(dev);
+	lance32_init_ring(dev);
+    
+	outw(0x0000,    dev->base_addr + LANCE_ADDR);
+	outw(csr0_bits, dev->base_addr + LANCE_DATA);
+}
+
+static int
+lance32_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int entry;
+	unsigned long flags;
+
+	/* Transmitter timeout, serious problems. */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 20)
+			return 1;
+		outw(0, ioaddr+LANCE_ADDR);
+		printk("%s: transmit timed out, status %4.4x, resetting.\n",
+			   dev->name, inw(ioaddr+LANCE_DATA));
+		outw(0x0004, ioaddr+LANCE_DATA);
+		lp->stats.tx_errors++;
+#ifndef final_version
+		{
+			int i;
+			printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
+				   lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
+				   lp->cur_rx);
+			for (i = 0 ; i < RX_RING_SIZE; i++)
+				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
+					   lp->rx_ring[i].msg_length);
+			for (i = 0 ; i < TX_RING_SIZE; i++)
+				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+					   lp->tx_ring[i].base, -lp->tx_ring[i].length,
+					   lp->tx_ring[i].misc);
+			printk("\n");
+		}
+#endif
+		lance32_restart(dev, 0x0043, 1);
+
+		dev->tbusy=0;
+		dev->trans_start = jiffies;
+
+		return 0;
+	}
+
+	if (skb == NULL) {
+		dev_tint(dev);
+		return 0;
+	}
+
+	if (skb->len <= 0)
+		return 0;
+
+	if (lance32_debug > 3) {
+		outw(0x0000, ioaddr+LANCE_ADDR);
+		printk("%s: lance32_start_xmit() called, csr0 %4.4x.\n", dev->name,
+			   inw(ioaddr+LANCE_DATA));
+		outw(0x0000, ioaddr+LANCE_DATA);
+	}
+
+	/* Block a timer-based transmit from overlapping.  This could better be
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+	if (set_bit(0, (void*)&dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+		return 1;
+	}
+
+	if (set_bit(0, (void*)&lp->lock) != 0) {
+		if (lance32_debug > 0)
+			printk("%s: tx queue lock!.\n", dev->name);
+		/* don't clear dev->tbusy flag. */
+		return 1;
+	}
+
+	/* Fill in a Tx ring entry */
+
+	/* Mask to ring buffer boundary. */
+	entry = lp->cur_tx & TX_RING_MOD_MASK;
+
+	/* Caution: the write order is important here, set the base address
+	   with the "ownership" bits last. */
+
+	lp->tx_ring[entry].length = -skb->len;
+
+	lp->tx_ring[entry].misc = 0x00000000;
+
+	lp->tx_skbuff[entry] = skb;
+	lp->tx_ring[entry].base = (u32)virt_to_bus(skb->data);
+        lp->tx_ring[entry].status = 0x8300;
+
+	lp->cur_tx++;
+
+	/* Trigger an immediate send poll. */
+	outw(0x0000, ioaddr+LANCE_ADDR);
+	outw(0x0048, ioaddr+LANCE_DATA);
+
+	dev->trans_start = jiffies;
+
+	save_flags(flags);
+	cli();
+	lp->lock = 0;
+	if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
+		dev->tbusy=0;
+	else
+		lp->tx_full = 1;
+	restore_flags(flags);
+
+	return 0;
+}
+
+/* The LANCE32 interrupt handler. */
+static void
+lance32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct device *dev = (struct device *)dev_id;
+	struct lance32_private *lp;
+	int csr0, ioaddr, boguscnt=10;
+	int must_restart;
+
+	if (dev == NULL) {
+		printk ("lance32_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	ioaddr = dev->base_addr;
+	lp = (struct lance32_private *)dev->priv;
+	if (dev->interrupt)
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+	dev->interrupt = 1;
+
+	outw(0x00, dev->base_addr + LANCE_ADDR);
+	while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600
+		   && --boguscnt >= 0) {
+		/* Acknowledge all of the current interrupt sources ASAP. */
+		outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
+
+		must_restart = 0;
+
+		if (lance32_debug > 5)
+			printk("%s: interrupt  csr0=%#2.2x new csr=%#2.2x.\n",
+				   dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
+
+		if (csr0 & 0x0400)			/* Rx interrupt */
+			lance32_rx(dev);
+
+		if (csr0 & 0x0200) {		/* Tx-done interrupt */
+			int dirty_tx = lp->dirty_tx;
+
+			while (dirty_tx < lp->cur_tx) {
+				int entry = dirty_tx & TX_RING_MOD_MASK;
+				int status = lp->tx_ring[entry].status;
+			
+				if (status < 0)
+					break;			/* It still hasn't been Txed */
+
+				lp->tx_ring[entry].base = 0;
+
+				if (status & 0x4000) {
+					/* There was an major error, log it. */
+					int err_status = lp->tx_ring[entry].misc;
+					lp->stats.tx_errors++;
+					if (err_status & 0x04000000) lp->stats.tx_aborted_errors++;
+					if (err_status & 0x08000000) lp->stats.tx_carrier_errors++;
+					if (err_status & 0x10000000) lp->stats.tx_window_errors++;
+					if (err_status & 0x40000000) {
+						/* Ackk!  On FIFO errors the Tx unit is turned off! */
+						lp->stats.tx_fifo_errors++;
+						/* Remove this verbosity later! */
+						printk("%s: Tx FIFO error! Status %4.4x.\n",
+							   dev->name, csr0);
+						/* Restart the chip. */
+						must_restart = 1;
+					}
+				} else {
+					if (status & 0x1800)
+						lp->stats.collisions++;
+					lp->stats.tx_packets++;
+				}
+
+				/* We must free the original skb */
+				if (lp->tx_skbuff[entry]) {
+					dev_kfree_skb(lp->tx_skbuff[entry],FREE_WRITE);
+					lp->tx_skbuff[entry] = 0;
+				}
+				dirty_tx++;
+			}
+
+#ifndef final_version
+			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+					   dirty_tx, lp->cur_tx, lp->tx_full);
+				dirty_tx += TX_RING_SIZE;
+			}
+#endif
+
+			if (lp->tx_full && dev->tbusy
+				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+				/* The ring is no longer full, clear tbusy. */
+				lp->tx_full = 0;
+				dev->tbusy = 0;
+				mark_bh(NET_BH);
+			}
+
+			lp->dirty_tx = dirty_tx;
+		}
+
+		/* Log misc errors. */
+		if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
+		if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
+		if (csr0 & 0x0800) {
+			printk("%s: Bus master arbitration failure, status %4.4x.\n",
+				   dev->name, csr0);
+			/* Restart the chip. */
+			must_restart = 1;
+		}
+
+		if (must_restart) {
+			/* stop the chip to clear the error condition, then restart */
+			outw(0x0000, dev->base_addr + LANCE_ADDR);
+			outw(0x0004, dev->base_addr + LANCE_DATA);
+			lance32_restart(dev, 0x0002, 0);
+		}
+	}
+
+    /* Clear any other interrupt, and set interrupt enable. */
+    outw(0x0000, dev->base_addr + LANCE_ADDR);
+    outw(0x7940, dev->base_addr + LANCE_DATA);
+
+	if (lance32_debug > 4)
+		printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
+			   dev->name, inw(ioaddr + LANCE_ADDR),
+			   inw(dev->base_addr + LANCE_DATA));
+
+	dev->interrupt = 0;
+	return;
+}
+
+static int
+lance32_rx(struct device *dev)
+{
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;
+	int entry = lp->cur_rx & RX_RING_MOD_MASK;
+	int i;
+		
+	/* If we own the next entry, it's a new packet. Send it up. */
+	while (lp->rx_ring[entry].status >= 0) {
+		int status = lp->rx_ring[entry].status >> 8;
+
+		if (status != 0x03) {			/* There was an error. */
+			/* There is a tricky error noted by John Murphy,
+			   <murf@perftech.com> to Russ Nelson: Even with full-sized
+			   buffers it's possible for a jabber packet to use two
+			   buffers, with only the last correctly noting the error. */
+			if (status & 0x01)	/* Only count a general error at the */
+				lp->stats.rx_errors++; /* end of a packet.*/
+			if (status & 0x20) lp->stats.rx_frame_errors++;
+			if (status & 0x10) lp->stats.rx_over_errors++;
+			if (status & 0x08) lp->stats.rx_crc_errors++;
+			if (status & 0x04) lp->stats.rx_fifo_errors++;
+			lp->rx_ring[entry].status &= 0x03ff;
+		}
+		else 
+		{
+			/* Malloc up new buffer, compatible with net-2e. */
+			short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
+			struct sk_buff *skb;
+			
+			if(pkt_len<60)
+			{
+				printk("%s: Runt packet!\n",dev->name);
+				lp->stats.rx_errors++;
+			}
+			else
+			{
+				skb = dev_alloc_skb(pkt_len+2);
+				if (skb == NULL) 
+				{
+					printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+					for (i=0; i < RX_RING_SIZE; i++)
+						if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].status < 0)
+							break;
+
+					if (i > RX_RING_SIZE -2) 
+					{
+						lp->stats.rx_dropped++;
+						lp->rx_ring[entry].status |= 0x8000;
+						lp->cur_rx++;
+					}
+					break;
+				}
+				skb->dev = dev;
+				skb_reserve(skb,2);	/* 16 byte align */
+				skb_put(skb,pkt_len);	/* Make room */
+				eth_copy_and_sum(skb,
+					(unsigned char *)bus_to_virt(lp->rx_ring[entry].base),
+					pkt_len,0);
+				skb->protocol=eth_type_trans(skb,dev);
+				netif_rx(skb);
+				lp->stats.rx_packets++;
+			}
+		}
+		/* The docs say that the buffer length isn't touched, but Andrew Boyd
+		   of QNX reports that some revs of the 79C965 clear it. */
+		lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
+		lp->rx_ring[entry].status |= 0x8000;
+		entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+	}
+
+	/* We should check that at least two ring entries are free.	 If not,
+	   we should free one and mark stats->rx_dropped++. */
+
+	return 0;
+}
+
+static int
+lance32_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	outw(112, ioaddr+LANCE_ADDR);
+	lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+
+	outw(0, ioaddr+LANCE_ADDR);
+
+	if (lance32_debug > 1)
+		printk("%s: Shutting down ethercard, status was %2.2x.\n",
+			   dev->name, inw(ioaddr+LANCE_DATA));
+
+	/* We stop the LANCE here -- it occasionally polls
+	   memory if we don't. */
+	outw(0x0004, ioaddr+LANCE_DATA);
+
+	if (dev->dma != 4)
+		disable_dma(dev->dma);
+
+	free_irq(dev->irq, dev);
+
+	irq2dev_map[dev->irq] = 0;
+
+	return 0;
+}
+
+static struct enet_statistics *
+lance32_get_stats(struct device *dev)
+{
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;
+	short ioaddr = dev->base_addr;
+	short saved_addr;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	saved_addr = inw(ioaddr+LANCE_ADDR);
+	outw(112, ioaddr+LANCE_ADDR);
+	lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+	outw(saved_addr, ioaddr+LANCE_ADDR);
+	restore_flags(flags);
+
+	return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+
+static void lance32_set_multicast_list(struct device *dev)
+{
+	short ioaddr = dev->base_addr;
+
+	outw(0, ioaddr+LANCE_ADDR);
+	outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance.	 */
+
+	if (dev->flags&IFF_PROMISC) {
+		/* Log any net taps. */
+		printk("%s: Promiscuous mode enabled.\n", dev->name);
+		outw(15, ioaddr+LANCE_ADDR);
+		outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
+	} else {
+		short multicast_table[4];
+		int i;
+		int num_addrs=dev->mc_count;
+		if(dev->flags&IFF_ALLMULTI)
+			num_addrs=1;
+		/* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
+		memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table));
+		for (i = 0; i < 4; i++) {
+			outw(8 + i, ioaddr+LANCE_ADDR);
+			outw(multicast_table[i], ioaddr+LANCE_DATA);
+		}
+		outw(15, ioaddr+LANCE_ADDR);
+		outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
+	}
+
+	lance32_restart(dev, 0x0142, 0); /*  Resume normal operation */
+
+}
+
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance32.c"
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v1.3.81/linux/drivers/net/plip.c linux/drivers/net/plip.c
--- v1.3.81/linux/drivers/net/plip.c	Fri Mar  1 07:50:45 1996
+++ linux/drivers/net/plip.c	Mon Apr  1 07:34:58 1996
@@ -290,12 +290,12 @@
 	nl->nibble	= PLIP_NIBBLE_WAIT;
 
 	/* Initialize task queue structures */
-	nl->immediate.next = &tq_last;
+	nl->immediate.next = NULL;
 	nl->immediate.sync = 0;
 	nl->immediate.routine = (void *)(void *)plip_bh;
 	nl->immediate.data = dev;
 
-	nl->deferred.next = &tq_last;
+	nl->deferred.next = NULL;
 	nl->deferred.sync = 0;
 	nl->deferred.routine = (void *)(void *)plip_kick_bh;
 	nl->deferred.data = dev;
diff -u --recursive --new-file v1.3.81/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.3.81/linux/drivers/net/ppp.c	Tue Mar  5 10:05:49 1996
+++ linux/drivers/net/ppp.c	Mon Apr  1 10:50:01 1996
@@ -620,10 +620,10 @@
 
 	ppp->rbuf->size -= 80;	/* reserve space for vj header expansion */
 
-	dev->mem_start	= (__u32) buf_base (new_wbuf);
-	dev->mem_end	= (__u32) (dev->mem_start + mtu);
-	dev->rmem_start = (__u32) buf_base (new_rbuf);
-	dev->rmem_end	= (__u32) (dev->rmem_start + mru);
+	dev->mem_start	= (unsigned long) buf_base (new_wbuf);
+	dev->mem_end	= (unsigned long) (dev->mem_start + mtu);
+	dev->rmem_start = (unsigned long) buf_base (new_rbuf);
+	dev->rmem_end	= (unsigned long) (dev->rmem_start + mru);
 /*
  *  Update the parameters for the new buffer sizes
  */
diff -u --recursive --new-file v1.3.81/linux/drivers/net/sdla.c linux/drivers/net/sdla.c
--- v1.3.81/linux/drivers/net/sdla.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/net/sdla.c	Mon Apr  1 10:46:30 1996
@@ -393,6 +393,12 @@
          printk(KERN_ERR "%s: Command timed out!\n", dev->name);
          break;
 
+      case SDLA_RET_CHANNEL_INACTIVE:
+      case SDLA_RET_DLCI_INACTIVE:
+      case SDLA_RET_NO_BUFF:
+         if (cmd == SDLA_INFORMATION_WRITE)
+            break;
+
       default: 
          /*
           * Further processing could be done here 
diff -u --recursive --new-file v1.3.81/linux/drivers/net/slhc.c linux/drivers/net/slhc.c
--- v1.3.81/linux/drivers/net/slhc.c	Wed Feb  7 08:55:37 1996
+++ linux/drivers/net/slhc.c	Tue Apr  2 08:43:07 1996
@@ -750,7 +750,7 @@
 
 int init_module(void)
 {
-	printk("CSLIP: code copyright 1989 Regents of the University of California\n");
+	printk(KERN_INFO "CSLIP: code copyright 1989 Regents of the University of California\n");
 	export_slhc_syms();
 	return 0;
 }
diff -u --recursive --new-file v1.3.81/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.3.81/linux/drivers/net/slip.c	Sun Mar 10 10:00:03 1996
+++ linux/drivers/net/slip.c	Tue Apr  2 08:43:07 1996
@@ -1201,7 +1201,7 @@
 
 	if (slip_maxdev < 4) slip_maxdev = 4; /* Sanity */
 
-	printk("SLIP: version %s (dynamic channels, max=%d)"
+	printk(KERN_INFO "SLIP: version %s (dynamic channels, max=%d)"
 #ifdef CONFIG_SLIP_MODE_SLIP6
 	       " (6 bit encapsulation enabled)"
 #endif
@@ -1211,10 +1211,10 @@
 	printk("CSLIP: code copyright 1989 Regents of the University of California.\n");
 #endif
 #ifdef CONFIG_AX25
-	printk("AX25: KISS encapsulation enabled.\n");
+	printk(KERN_INFO "AX25: KISS encapsulation enabled.\n");
 #endif
 #ifdef CONFIG_SLIP_SMART
-	printk("SLIP linefill/keepalive option.\n");
+	printk(KERN_INFO "SLIP linefill/keepalive option.\n");
 #endif	
 
 	slip_ctrls = (slip_ctrl_t **) kmalloc(sizeof(void*)*slip_maxdev, GFP_KERNEL);
diff -u --recursive --new-file v1.3.81/linux/drivers/net/strip.c linux/drivers/net/strip.c
--- v1.3.81/linux/drivers/net/strip.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/strip.c	Mon Apr  1 10:46:30 1996
@@ -0,0 +1,1494 @@
+/*
+ * Copyright 1996 The Board of Trustees of The Leland Stanford
+ * Junior University. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies.  Stanford University
+ * makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ *
+ * strip.c	This module implements Starmode Radio IP (STRIP)
+ *		for kernel-based devices like TTY.  It interfaces between a
+ *		raw TTY, and the kernel's INET protocol layers (via DDI).
+ *
+ * Version:	@(#)strip.c	0.9.1	3/6/95
+ *
+ * Author:	Stuart Cheshire <cheshire@cs.stanford.edu>
+ *
+ * Fixes:
+ *		Stuart Cheshire:
+ *			Original version converted from SLIP driver
+ *		Jonathan Stone:
+ *			change to 1.3 calling conventions
+ *		Stuart Cheshire:
+ *			v0.9 12th Feb 1996.
+ *			New byte stuffing (2+6 run-length encoding)
+ *			New watchdog timer task
+ *			New Protocol key (SIP0)
+ *			v0.9.1 3rd March 1996
+ *			Changed to dynamic device allocation
+ */
+
+#include <linux/config.h>
+#ifdef MODULE
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/if_strip.h>
+#include <net/arp.h>
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#endif
+
+#ifdef MODULE
+#define STRIP_VERSION    "0.9.1-STUART.CHESHIRE-MODULAR"
+#else
+#define STRIP_VERSION    "0.9.1-STUART.CHESHIRE"
+#endif
+
+#define STRIP_MTU 1024
+#define STRIP_MAGIC 0x5303
+
+/*
+ *	Do we still needs all these flags? 
+ */
+
+enum 
+{
+	STR_INUSE = 0,		/* Channel in use	*/
+	STR_ESCAPE,			/* ESC received		*/
+	STR_ERROR			/* Parity, etc. error	*/
+}
+STRIP_FLAGS;
+
+struct strip 
+{
+	int magic;
+	/*
+	 *	Other useful structures. 
+	 */
+
+	/*
+	 *	These are pointers to the malloc()ed frame buffers.
+	 */
+
+	unsigned char     *rx_buff;		/* buffer for received IP packet*/
+	unsigned char     *sx_buff;		/* buffer for received serial data*/
+	int                sx_count;		/* received serial data counter */
+	unsigned char     *tx_buff;		/* transmitter buffer  */
+	unsigned char     *tx_head;		/* pointer to next byte to XMIT */
+	int                tx_left;		/* bytes left in XMIT queue     */
+
+	/*
+	 *	STRIP interface statistics.
+	 */
+	 
+	unsigned long      rx_packets;		/* inbound frames counter	*/
+	unsigned long      tx_packets;		/* outbound frames counter	*/
+	unsigned long      rx_errors;		/* Parity, etc. errors		*/
+	unsigned long      tx_errors;		/* Planned stuff		*/
+	unsigned long      rx_dropped;		/* No memory for skb		*/
+	unsigned long      tx_dropped;		/* When MTU change		*/
+	unsigned long      rx_over_errors;	/* Frame bigger then STRIP buf. */
+
+	/*
+	 *	Internal variables.
+	 */
+	 
+	struct strip      *next;		/* The next struct in the list 	*/
+	struct strip     **referrer;		/* The pointer that points to us */
+	unsigned char      flags;		/* Flag values/ mode etc	*/
+	int                mtu;			/* Our mtu (to spot changes!)	*/
+	int                buffsize;		/* Max buffers sizes		*/
+	long               watchdog_doprobe;	/* Next time to test the radio	*/
+	long               watchdog_doreset;	/* Time to do next reset	*/
+	struct timer_list  idle_timer;
+
+	struct tty_struct *tty;			/* ptr to TTY structure		*/
+	char               if_name[8];		/* Dynamically generated name	*/
+	struct device       dev;		/* Our device stucture		*/
+};
+/************************************************************************/
+/* Utility routines for disabling and restoring interrupts		*/
+
+typedef unsigned long InterruptStatus;
+
+extern __inline__ InterruptStatus DisableInterrupts(void)
+{
+    InterruptStatus x;
+    save_flags(x);
+    cli();
+    return(x);
+}
+
+extern __inline__ void RestoreInterrupts(InterruptStatus x)
+{
+    restore_flags(x);
+}
+
+/************************************************************************/
+/* Useful structures and definitions					*/
+
+typedef struct {
+    __u8 c[32];
+} RadioName;
+
+typedef struct {
+    __u8 c[ 4];
+} MetricomKey;
+
+typedef union {
+    __u8 b[ 4];
+    __u32 l;
+} IPaddr;
+
+static const MetricomKey ProtocolKey = 
+{
+    {
+        "SIP0"
+    }
+};
+
+enum 
+{
+    FALSE = 0,
+    TRUE = 1
+};
+
+#define LONG_TIME 0x7FFFFFFF
+
+typedef struct 
+{
+	RadioName   name;	/* The address, with delimiters eg. *0000-1164* */
+	MetricomKey key;	/* Protocol type */
+} STRIP_Header;
+
+typedef struct 
+{
+	STRIP_Header h;
+	__u8 data[4];		/* Placeholder for payload (The IP packet) */
+} STRIP_Packet;
+
+/*
+ *	STRIP_ENCAP_SIZE of an IP packet is the STRIP header at the front,
+ *	byte-stuffing overhead of the payload, plus the CR at the end
+ */
+ 
+#define STRIP_ENCAP_SIZE(X) (sizeof(STRIP_Header) + (X)*65L/64L + 2)
+
+/*
+ *	Note: A Metricom packet looks like this: *<address>*<key><payload><CR>
+ *	eg. *0000-1164*SIP0<payload><CR>
+ */
+
+static struct strip *struct_strip_list = NULL;
+
+/************************************************************************/
+/* Byte stuffing/unstuffing routines					*/
+
+/* Stuffing scheme:
+ * 00    Unused (reserved character)
+ * 01-3F Run of 2-64 different characters
+ * 40-7F Run of 1-64 different characters plus a single zero at the end
+ * 80-BF Run of 1-64 of the same character
+ * C0-FF Run of 1-64 zeroes (ASCII 0)
+ */
+
+typedef enum 
+{
+    Stuff_Diff      = 0x00,
+    Stuff_DiffZero  = 0x40,
+    Stuff_Same      = 0x80,
+    Stuff_Zero      = 0xC0,
+    Stuff_NoCode    = 0xFF,	/* Special code, meaning no code selected */
+    
+    Stuff_CodeMask  = 0xC0,
+    Stuff_CountMask = 0x3F,
+    Stuff_MaxCount  = 0x3F,
+    Stuff_Magic     = 0x0D	/* The value we are eliminating */
+} StuffingCode;
+
+/* StuffData encodes the data starting at "src" for "length" bytes.
+ * It writes it to the buffer pointed to by "dst" (which must be at least
+ * as long as 1 + 65/64 of the input length). The output may be up to 1.6%
+ * larger than the input for pathological input, but will usually be smaller.
+ * StuffData returns the new value of the dst pointer as its result.
+ * "code_ptr_ptr" points to a "__u8 *" which is used to hold encoding state
+ * between calls, allowing an encoded packet to be incrementally built up
+ * from small parts. On the first call, the "__u8 *" pointed to should be
+ * initialized to NULL; between subsequent calls the calling routine should
+ * leave the value alone and simply pass it back unchanged so that the
+ * encoder can recover its current state.
+ */
+
+#define StuffData_FinishBlock(X) \
+(*code_ptr = (X) ^ Stuff_Magic, code = Stuff_NoCode)
+
+static __u8 *StuffData(__u8 *src, __u32 length, __u8 *dst, __u8 **code_ptr_ptr)
+{
+	__u8 *end = src + length;
+	__u8 *code_ptr = *code_ptr_ptr;
+ 	__u8 code = Stuff_NoCode, count = 0;
+    
+	if (!length) 
+		return(dst);
+    
+	if (code_ptr) 
+	{
+		/*
+		 *	Recover state from last call, if applicable 
+		 */
+		code  = *code_ptr & Stuff_CodeMask;
+		count = *code_ptr & Stuff_CountMask;
+	}
+
+	while (src < end) 
+	{
+		switch (code) 
+		{
+			/* Stuff_NoCode: If no current code, select one */
+			case Stuff_NoCode:
+				/* Record where we're going to put this code */
+				code_ptr = dst++;
+				count = 0;	/* Reset the count (zero means one instance) */
+				/* Tentatively start a new block */
+				if (*src == 0) 
+				{
+					code = Stuff_Zero;
+        				src++;
+				}
+				else
+				{
+					code = Stuff_Same;
+					*dst++ = *src++ ^ Stuff_Magic;
+				}
+				/* Note: We optimistically assume run of same -- */
+				/* which will be fixed later in Stuff_Same */
+				/* if it turns out not to be true. */
+				break;
+
+			/* Stuff_Zero: We already have at least one zero encoded */
+			case Stuff_Zero:
+                		/* If another zero, count it, else finish this code block */
+                		if (*src == 0) 
+                		{
+					count++;
+					src++;
+				}
+				else 
+				{
+					StuffData_FinishBlock(Stuff_Zero + count);
+        			}
+				break;
+
+			/* Stuff_Same: We already have at least one byte encoded */
+			case Stuff_Same:
+				/* If another one the same, count it */
+				if ((*src ^ Stuff_Magic) == code_ptr[1]) 
+				{
+					count++;
+					src++;
+					break;
+				}
+				/* else, this byte does not match this block. */
+        			/* If we already have two or more bytes encoded, */
+				/* finish this code block */
+				if (count) 
+				{
+					StuffData_FinishBlock(Stuff_Same + count);
+					break;
+				}
+				/* else, we only have one so far, */
+				/* so switch to Stuff_Diff code */
+				code = Stuff_Diff;
+				/* and fall through to Stuff_Diff case below */
+			 /* Stuff_Diff: We have at least two *different* bytes encoded */
+        		case Stuff_Diff:
+				/* If this is a zero, must encode a Stuff_DiffZero, */
+				/* and begin a new block */
+				if (*src == 0) 
+				{
+					StuffData_FinishBlock(Stuff_DiffZero + count);
+				}
+				/* else, if we have three in a row, it is worth starting */
+				/* a Stuff_Same block */
+				else if ((*src ^ Stuff_Magic)==dst[-1] && dst[-1]==dst[-2]) 
+				{
+				/* Back off the last two characters we encoded */
+				code += count-2;
+				/* Note: "Stuff_Diff + 0" is an illegal code */
+				if (code == Stuff_Diff + 0) 
+				{
+					code = Stuff_Same + 0;
+				}
+				StuffData_FinishBlock(code);
+				code_ptr = dst-2;
+				/* dst[-1] already holds the correct value */
+				count = 2;		/* 2 means three bytes encoded */
+				code = Stuff_Same;
+			}
+			/* else, another different byte, so add it to the block */
+			else 
+                	{
+                		*dst++ = *src ^ Stuff_Magic;
+                		count++;
+                	}
+                	src++;	/* Consume the byte */
+                	break;
+		}
+		if (count == Stuff_MaxCount) 
+		{
+			StuffData_FinishBlock(code + count);
+		}
+	}
+	if (code == Stuff_NoCode) 
+	{
+		*code_ptr_ptr = NULL;
+	}
+	else 
+	{
+		*code_ptr_ptr = code_ptr;
+		StuffData_FinishBlock(code + count);
+	}	
+	return(dst);
+}
+
+/* UnStuffData decodes the data at "src", up to (but not including) "end".
+It writes the decoded data into the buffer pointed to by "dst", up to a
+maximum of "dst_length", and returns the new value of "src" so that a
+follow-on call can read more data, continuing from where the first left off.
+
+There are three types of results:
+1. The source data runs out before extracting "dst_length" bytes:
+   UnStuffData returns NULL to indicate failure.
+2. The source data produces exactly "dst_length" bytes:
+   UnStuffData returns new_src = end to indicate that all bytes were consumed.
+3. "dst_length" bytes are extracted, with more remaining.
+   UnStuffData returns new_src < end to indicate that there are more bytes
+   to be read.
+
+Note: The decoding may be dstructive, in that it may alter the source
+data in the process of decoding it (this is necessary to allow a follow-on
+call to resume correctly). */
+
+static __u8 *UnStuffData(__u8 *src, __u8 *end, __u8 *dst, __u32 dst_length)
+{
+	__u8 *dst_end = dst + dst_length;
+	/* Sanity check */
+	if (!src || !end || !dst || !dst_length) 
+		return(NULL);
+	while (src < end && dst < dst_end) 
+	{
+		int count = (*src ^ Stuff_Magic) & Stuff_CountMask;
+		switch ((*src ^ Stuff_Magic) & Stuff_CodeMask) 
+		{
+			case Stuff_Diff:
+                		if (src+1+count >= end) 
+					return(NULL);
+		                do 
+		                {
+					*dst++ = *++src ^ Stuff_Magic;
+				}
+				while(--count >= 0 && dst < dst_end);
+				if (count < 0) 
+					src += 1;
+				else 
+				{
+					if (count == 0)
+						*src = Stuff_Same ^ Stuff_Magic;
+			                else
+						*src = (Stuff_Diff + count) ^ Stuff_Magic;
+		                }
+                		break;
+			case Stuff_DiffZero:
+				if (src+1+count >= end) 
+					return(NULL);
+				do 
+				{
+					*dst++ = *++src ^ Stuff_Magic;
+				}
+				while(--count >= 0 && dst < dst_end);
+				if (count < 0)
+					*src = Stuff_Zero ^ Stuff_Magic;
+        			else
+					*src = (Stuff_DiffZero + count) ^ Stuff_Magic;
+				break;
+			case Stuff_Same:
+				if (src+1 >= end)
+     					return(NULL);
+				do 
+				{
+					*dst++ = src[1] ^ Stuff_Magic;
+				}
+				while(--count >= 0 && dst < dst_end);
+				if (count < 0)
+					src += 2;
+		                else
+					*src = (Stuff_Same + count) ^ Stuff_Magic;
+				break;
+			case Stuff_Zero:
+				do 
+				{
+					*dst++ = 0;
+				}
+				while(--count >= 0 && dst < dst_end);
+				if (count < 0)
+					src += 1;
+				else
+					*src = (Stuff_Zero + count) ^ Stuff_Magic;
+		                break;
+		}
+	}
+	if (dst < dst_end) 
+		return(NULL);
+	else
+		return(src);
+}
+
+/************************************************************************/
+/* General routines for STRIP						*/
+
+/* MTU has been changed by the IP layer. Unfortunately we are not told
+ * about this, but we spot it ourselves and fix things up. We could be in
+ * an upcall from the tty driver, or in an ip packet queue.
+ */
+
+static void strip_changedmtu(struct strip *strip_info)
+{
+	struct device *dev = &strip_info->dev;
+	unsigned char *tbuff, *rbuff, *sbuff, *otbuff, *orbuff, *osbuff;
+	int len;
+	InterruptStatus intstat;
+
+	len = STRIP_ENCAP_SIZE(dev->mtu);
+	if (len < STRIP_ENCAP_SIZE(576)) 
+		len = STRIP_ENCAP_SIZE(576);
+
+	tbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+	rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+	sbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+	if (!tbuff || !rbuff || !sbuff) 
+	{
+        	printk("%s: unable to grow strip buffers, MTU change cancelled.\n",
+        		strip_info->dev.name);
+		dev->mtu = strip_info->mtu;
+		if (tbuff)
+			kfree(tbuff);
+		if (rbuff)
+			kfree(rbuff);
+		if (sbuff)
+			kfree(sbuff);
+		return;
+	}
+
+	intstat = DisableInterrupts();
+	otbuff = strip_info->tx_buff; strip_info->tx_buff = tbuff;
+	orbuff = strip_info->rx_buff; strip_info->rx_buff = rbuff;
+	osbuff = strip_info->sx_buff; strip_info->sx_buff = sbuff;
+	if (strip_info->tx_left) 
+	{
+		if (strip_info->tx_left <= len)
+			memcpy(strip_info->tx_buff, strip_info->tx_head, strip_info->tx_left);
+		else 
+		{
+			strip_info->tx_left = 0;
+			strip_info->tx_dropped++;
+		}
+	}
+	strip_info->tx_head = strip_info->tx_buff;
+
+	if (strip_info->sx_count) 
+	{
+		if (strip_info->sx_count <= len)
+			memcpy(strip_info->sx_buff, osbuff, strip_info->sx_count);
+		else
+		{
+			strip_info->sx_count = 0;
+			strip_info->rx_over_errors++;
+			set_bit(STR_ERROR, &strip_info->flags);
+		}
+	}
+
+	strip_info->mtu      = STRIP_ENCAP_SIZE(dev->mtu);
+	strip_info->buffsize = len;
+
+	RestoreInterrupts(intstat);
+
+	if (otbuff != NULL)
+		 kfree(otbuff);
+	if (orbuff != NULL)
+		kfree(orbuff);
+	if (osbuff != NULL)
+		kfree(osbuff);
+}
+
+static void strip_unlock(struct strip *strip_info)
+{
+	strip_info->idle_timer.expires  = jiffies + 2 * HZ;
+	add_timer(&strip_info->idle_timer);
+	if (!clear_bit(0, (void *)&strip_info->dev.tbusy))
+		printk("%s: trying to unlock already unlocked device!\n",
+			strip_info->dev.name);
+}
+
+/************************************************************************/
+/* Sending routines							*/
+
+static void ResetRadio(struct strip *strip_info)
+{	
+	static const char InitString[] = "ate0dt**starmode\r**";
+	strip_info->watchdog_doprobe = jiffies + 10 * HZ;
+	strip_info->watchdog_doreset = jiffies + 1 * HZ;
+	strip_info->tty->driver.write(strip_info->tty, 0,
+		(char *)InitString, sizeof(InitString)-1);
+}
+
+/*
+ * Called by the driver when there's room for more data.  If we have
+ * more packets to send, we send them here.
+ */
+
+static void strip_write_some_more(struct tty_struct *tty)
+{
+	InterruptStatus intstat;
+	int num_written;
+	struct strip *strip_info = (struct strip *) tty->disc_data;
+
+	/* First make sure we're connected. */
+	if (!strip_info || strip_info->magic != STRIP_MAGIC || !strip_info->dev.start)
+		return;
+
+	if (strip_info->tx_left > 0) 
+	{	/* If some data left, send it */
+		/* Must disable interrupts because othewise the write_wakeup might
+		 * happen before we've had a chance to update the tx_left and
+		 *  tx_head fields
+ 		 */
+		intstat = DisableInterrupts();
+		num_written = tty->driver.write(tty, 0, strip_info->tx_head, strip_info->tx_left);
+		strip_info->tx_left -= num_written;
+		strip_info->tx_head += num_written;
+		RestoreInterrupts(intstat);
+	}
+	else			/* Else start transmission of another packet */
+	{
+		tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		strip_unlock(strip_info);
+		mark_bh(NET_BH);
+	}
+}
+
+
+/* Encapsulate one IP datagram. */
+
+static unsigned char *strip_stuff(unsigned char *ptr, struct strip *strip_info, struct sk_buff *skb)
+{
+    __u8         *start;
+    __u8         *stuffstate = NULL;
+    unsigned char  *icp        = skb->data;
+    int             len        = skb->len;
+    MetricomAddress haddr;
+
+    if (len > strip_info->mtu) {		/* Sigh, shouldn't occur BUT ... */
+        printk("%s: Dropping oversized transmit packet!\n", strip_info->dev.name);
+        strip_info->tx_dropped++;
+        return(NULL);
+    }
+
+    if (!arp_query(haddr.c, skb->raddr, &strip_info->dev)) {
+        IPaddr a,b,c;
+        a.l = skb->raddr;
+        b.l = skb->saddr;
+        c.l = skb->daddr;
+        printk("%s: Unknown dest %d.%d.%d.%d s=%d.%d.%d.%d d=%d.%d.%d.%d\n",
+            strip_info->dev.name,
+            a.b[0], a.b[1], a.b[2], a.b[3],
+            b.b[0], b.b[1], b.b[2], b.b[3],
+            c.b[0], c.b[1], c.b[2], c.b[3]);
+        strip_info->tx_dropped++;
+        return(NULL);
+    }
+
+    *ptr++ = '*';
+    ptr[3] = '0' + haddr.s[0] % 10; haddr.s[0] /= 10;
+    ptr[2] = '0' + haddr.s[0] % 10; haddr.s[0] /= 10;
+    ptr[1] = '0' + haddr.s[0] % 10; haddr.s[0] /= 10;
+    ptr[0] = '0' + haddr.s[0] % 10;
+    ptr+=4;
+    *ptr++ = '-';
+    ptr[3] = '0' + haddr.s[1] % 10; haddr.s[1] /= 10;
+    ptr[2] = '0' + haddr.s[1] % 10; haddr.s[1] /= 10;
+    ptr[1] = '0' + haddr.s[1] % 10; haddr.s[1] /= 10;
+    ptr[0] = '0' + haddr.s[1] % 10;
+    ptr+=4;
+    *ptr++ = '*';
+    *ptr++ = ProtocolKey.c[0];				/* Protocol key */
+    *ptr++ = ProtocolKey.c[1];
+    *ptr++ = ProtocolKey.c[2];
+    *ptr++ = ProtocolKey.c[3];
+
+    start = ptr;
+    ptr = StuffData(icp, len, ptr, &stuffstate);	/* Make payload */
+
+    *ptr++ = 0x0D;					/* Put on final delimiter */
+    return(ptr);
+}
+
+/* Encapsulate one IP datagram and stuff into a TTY queue. */
+static void strip_send(struct strip *strip_info, struct sk_buff *skb)
+{
+    unsigned char *ptr;
+
+    /* See if someone has been ifconfigging */
+    if (strip_info->mtu != STRIP_ENCAP_SIZE(strip_info->dev.mtu))
+        strip_changedmtu(strip_info);
+
+    ptr = strip_info->tx_buff;
+
+    /* If we have a packet, encapsulate it and put it in the buffer */
+    if (skb) {
+        ptr = strip_stuff(ptr, strip_info, skb);
+        /* If error, unlock and return */
+        if (!ptr) { strip_unlock(strip_info); return; }
+        strip_info->tx_packets++;	/* Count another successful packet */
+    }
+
+    /* Set up the strip_info ready to send the data */
+    strip_info->tx_head =       strip_info->tx_buff;
+    strip_info->tx_left = ptr - strip_info->tx_buff;
+    strip_info->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+
+    /* If watchdog has expired, reset the radio */
+    if ((long)jiffies - strip_info->watchdog_doreset >= 0) {
+        printk("%s: No response: Resetting radio.\n", strip_info->dev.name);
+        ResetRadio(strip_info);
+        /* Note: if there's a packet to send, strip_write_some_more
+                 will do it after the reset has finished */
+        return;
+    }
+    
+    /* No reset.
+     * If it is time for another tickle, tack it on the end of the packet
+     */
+    if ((long)jiffies - strip_info->watchdog_doprobe >= 0) {
+        /* printk("%s: Routine radio test.\n", strip_info->dev.name); */
+        *ptr++ = '*';			/* Tickle to make radio protest */
+        *ptr++ = '*';
+        strip_info->tx_left += 2;
+        strip_info->watchdog_doprobe = jiffies + 10 * HZ;
+        strip_info->watchdog_doreset = jiffies + 1 * HZ;
+    }
+    
+    /* All ready. Start the transmission */
+    strip_write_some_more(strip_info->tty);
+}
+
+/* Encapsulate an IP datagram and kick it into a TTY queue. */
+static int strip_xmit(struct sk_buff *skb, struct device *dev)
+{
+    struct strip *strip_info = (struct strip *)(dev->priv);
+
+    if (!dev->start) {
+	printk("%s: xmit call when iface is down\n", dev->name);
+	return(1);
+    }
+    if (set_bit(0, (void *) &strip_info->dev.tbusy)) return(1);
+    del_timer(&strip_info->idle_timer);
+    strip_send(strip_info, skb);
+    if (skb) dev_kfree_skb(skb, FREE_WRITE);
+    return(0);
+}
+
+/* IdleTask periodically calls strip_xmit, so even when we have no IP packets
+   to send for an extended period of time, the watchdog processing still gets
+   done to ensure that the radio stays in Starmode */
+
+static void strip_IdleTask(unsigned long parameter)
+{
+	strip_xmit(NULL, (struct device *)parameter);
+}
+
+/************************************************************************/
+/* Receiving routines							*/
+
+static int strip_receive_room(struct tty_struct *tty)
+{
+	return 65536;  /* We can handle an infinite amount of data. :-) */
+}
+
+/* Send one completely decapsulated IP datagram to the IP layer. */
+
+static void strip_bump(struct strip *strip_info, __u16 packetlen)
+{
+	int count = sizeof(STRIP_Header) + packetlen;
+	struct sk_buff *skb = dev_alloc_skb(count);
+	if (skb == NULL) 
+	{
+		printk("%s: memory squeeze, dropping packet.\n", 
+			strip_info->dev.name);
+		strip_info->rx_dropped++;
+		return;
+	}
+	skb->dev = &strip_info->dev;
+	memcpy(skb_put(skb, count), strip_info->rx_buff, count);
+	skb->mac.raw=skb->data;
+	skb->protocol = htons(ETH_P_IP);
+	netif_rx(skb);
+	strip_info->rx_packets++;
+}
+
+static void RecvErr(char *msg, struct strip *strip_info)
+{
+	static const int MAX_RecvErr = 80;
+	__u8 *ptr = strip_info->sx_buff;
+	__u8 *end = strip_info->sx_buff + strip_info->sx_count;
+	__u8 pkt_text[MAX_RecvErr], *p = pkt_text;
+	*p++ = '\"';
+	while (ptr<end && p < &pkt_text[MAX_RecvErr-4]) 
+	{
+		if (*ptr == '\\') 
+		{
+			*p++ = '\\';
+			*p++ = '\\';
+		}
+		else 
+		{
+			if (*ptr >= 32 && *ptr <= 126) 
+				*p++ = *ptr;
+			else
+			{
+				sprintf(p, "\\%02X", *ptr);
+				p+= 3;
+			}
+		}
+	        ptr++;
+	}
+	if (ptr == end)
+	        *p++ = '\"';
+	*p++ = 0;
+	printk("%-13s%s\n", msg, pkt_text);
+	set_bit(STR_ERROR, &strip_info->flags);
+	strip_info->rx_errors++;
+}
+
+static void RecvErr_Message(struct strip *strip_info, __u8 *sendername, __u8 *msg)
+{
+	static const char ERR_001[] = "ERR_001 Not in StarMode!";
+	static const char ERR_002[] = "ERR_002 Remap handle";
+	static const char ERR_003[] = "ERR_003 Can't resolve name";
+	static const char ERR_004[] = "ERR_004 Name too small or missing";
+	static const char ERR_007[] = "ERR_007 Body too big";
+	static const char ERR_008[] = "ERR_008 Bad character in name";
+
+	if (!strncmp(msg, ERR_001, sizeof(ERR_001)-1))
+		printk("Radio %s is not in StarMode\n", sendername);
+	else if (!strncmp(msg, ERR_002, sizeof(ERR_002)-1)) 
+	{
+#ifdef notyet		/*Kernel doesn't have scanf!*/
+		int handle;
+		__u8 newname[64];
+		sscanf(msg, "ERR_002 Remap handle &%d to name %s", &handle, newname);
+		printk("Radio name %s is handle %d\n", newname, handle);
+#endif
+	}
+	else if (!strncmp(msg, ERR_003, sizeof(ERR_003)-1)) 
+		printk("Radio name <unspecified> is unknown (\"Can't resolve name\" error)\n");
+	else if (!strncmp(msg, ERR_004, sizeof(ERR_004)-1)) 
+        	strip_info->watchdog_doreset = jiffies + LONG_TIME;
+	 else if (!strncmp(msg, ERR_007, sizeof(ERR_007)-1)) 
+	 {
+		/*
+		 *	Note: This error knocks the radio back into 
+		 *	command mode. 
+		 */
+		printk("Error! Packet size <unspecified> is too big for radio.");
+		strip_info->watchdog_doreset = jiffies;		/* Do reset ASAP */
+	}
+	else if (!strncmp(msg, ERR_008, sizeof(ERR_008)-1)) 
+		printk("Name <unspecified> contains illegal character\n");
+	else 
+		RecvErr("Error Msg:", strip_info);
+}
+
+static void process_packet(struct strip *strip_info)
+{
+    __u8 *ptr = strip_info->sx_buff;
+    __u8 *end = strip_info->sx_buff + strip_info->sx_count;
+    __u8 *name, *name_end;
+    __u16 packetlen;
+
+    /* Ignore empty lines */
+    if (strip_info->sx_count == 0) return;
+
+    /* Catch 'OK' responses which show radio has fallen out of starmode */
+    if (strip_info->sx_count == 2 && ptr[0] == 'O' && ptr[1] == 'K') {
+        printk("%s: Radio is back in AT command mode: Will Reset\n",
+            strip_info->dev.name);
+        strip_info->watchdog_doreset = jiffies;		/* Do reset ASAP */
+        return;
+    }
+
+    /* Check for start of address marker, and then skip over it */
+    if (*ptr != '*') {
+        /* Catch other error messages */
+        if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_')
+            RecvErr_Message(strip_info, NULL, strip_info->sx_buff);
+        else RecvErr("No initial *", strip_info);
+        return;
+    }
+    ptr++;
+
+    /* Skip the return address */
+    name = ptr;
+    while (ptr < end && *ptr != '*') ptr++;
+
+    /* Check for end of address marker, and skip over it */
+    if (ptr == end) {
+        RecvErr("No second *", strip_info);
+        return;
+    }
+    name_end = ptr++;
+
+    /* Check for SRIP key, and skip over it */
+    if (ptr[0] != ProtocolKey.c[0] ||
+        ptr[1] != ProtocolKey.c[1] ||
+        ptr[2] != ProtocolKey.c[2] ||
+        ptr[3] != ProtocolKey.c[3]) {
+        if (ptr[0] == 'E' && ptr[1] == 'R' && ptr[2] == 'R' && ptr[3] == '_') { *name_end = 0; RecvErr_Message(strip_info, name, ptr); }
+        else RecvErr("Unrecognized protocol key", strip_info);
+        return;
+    }
+    ptr += 4;
+
+    /* Decode start of the IP packet header */
+    ptr = UnStuffData(ptr, end, strip_info->rx_buff, 4);
+    if (!ptr) {
+        RecvErr("Runt packet", strip_info);
+        return;
+    }
+
+    packetlen = ((__u16)strip_info->rx_buff[2] << 8) | strip_info->rx_buff[3];
+/*	printk("Packet %02X.%02X.%02X.%02X\n",
+        strip_info->rx_buff[0], strip_info->rx_buff[1],
+        strip_info->rx_buff[2], strip_info->rx_buff[3]);
+    printk("Got %d byte packet\n", packetlen);*/
+
+    /* Decode remainder of the IP packer */
+    ptr = UnStuffData(ptr, end, strip_info->rx_buff+4, packetlen-4);
+    if (!ptr) {
+        RecvErr("Runt packet", strip_info);
+        return;
+    }
+    strip_bump(strip_info, packetlen);
+
+    /* This turns out to be a mistake. Taking receipt of a valid packet as
+     * evidence that the radio is correctly in Starmode (and resetting the
+     * watchdog_doreset timer) is wrong.  It turns out that if the radio is
+     * in command mode, with character echo on, then the echo of the packet
+     * you sent coming back looks like a valid packet and fools this test.
+     * We should only accept the "ERR_004 Name too small or missing" message
+     * as evidence that the radio is correctly in Starmode.
+    strip_info->watchdog_doprobe = jiffies + 10 * HZ;
+    strip_info->watchdog_doreset = jiffies + LONG_TIME;
+     */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of STRIP data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing.
+ */
+static void
+strip_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+/*	struct timeval tv;*/
+    struct strip *strip_info = (struct strip *) tty->disc_data;
+    const unsigned char *end = cp + count;
+
+    if (!strip_info || strip_info->magic != STRIP_MAGIC || !strip_info->dev.start)
+        return;
+
+    /* Argh! mtu change time! - costs us the packet part received at the change */
+    if (strip_info->mtu != STRIP_ENCAP_SIZE(strip_info->dev.mtu))
+        strip_changedmtu(strip_info);
+
+/*	do_gettimeofday(&tv);
+    printk("**** strip_receive_buf: %3d bytes at %d.%06d\n",
+        count, tv.tv_sec % 100, tv.tv_usec);*/
+    /* Read the characters out of the buffer */
+    while (cp < end) {
+        if (fp && *fp++) {
+            if (!set_bit(STR_ERROR, &strip_info->flags)) strip_info->rx_errors++;
+        }
+        else if (*cp == 0x0D) {
+            /*printk("Cut a %d byte packet (%d bytes remaining)\n",
+                strip_info->sx_count, end-cp-1);*/
+            if (!clear_bit(STR_ERROR, &strip_info->flags))
+                process_packet(strip_info);
+            strip_info->sx_count = 0;
+        }
+        else if (!test_bit(STR_ERROR, &strip_info->flags) &&
+            (strip_info->sx_count > 0 || *cp != 0x0A))
+        {
+            /* (leading newline characters are ignored) */
+            if (strip_info->sx_count < strip_info->buffsize)
+                strip_info->sx_buff[strip_info->sx_count++] = *cp;
+            else
+            {
+                set_bit(STR_ERROR, &strip_info->flags);
+                strip_info->rx_over_errors++;
+            }
+        }
+        cp++;
+    }
+}
+
+/************************************************************************/
+/* General control routines						*/
+
+/*
+ *	 Create the Ethernet MAC header for an arbitrary protocol layer 
+ *
+ *	saddr=NULL	means use device source address
+ *	daddr=NULL	means leave destination address (eg unresolved arp)
+ */
+
+static int strip_header(struct sk_buff *skb, struct device *dev, 
+	unsigned short type, void *daddr, void *saddr, unsigned len)
+{
+	return(-dev->hard_header_len);
+}
+
+/*
+ *	Rebuild the Ethernet MAC header. This is called after an ARP
+ *	(or in future other address resolution) has completed on this
+ *	sk_buff. We now let ARP fill in the other fields.
+ */
+
+/* I think this should return zero if packet is ready to send, */
+/* or non-zero if it needs more time to do an address lookup   */
+
+static int strip_rebuild_header(void *buff, struct device *dev, 
+	unsigned long dst, struct sk_buff *skb)
+{
+/*	STRIP_Header *h = (STRIP_Header *)buff;*/
+
+#ifdef CONFIG_INET
+	/* I'll use arp_find when I understand it */
+	/* Arp find returns zero if if knows the address, or if it doesn't */
+	/* know the address it sends an ARP packet and returns non-zero */
+	/*return arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;*/
+	return(0);
+#else
+	return(0);
+#endif	
+}
+
+static int strip_set_dev_mac_address(struct device *dev, void *addr)
+{
+	memcpy(dev->dev_addr, addr, 7);
+	return 0;
+}
+
+static struct enet_statistics *strip_get_stats(struct device *dev)
+{
+	static struct enet_statistics stats;
+	struct strip *strip_info = (struct strip *)(dev->priv);
+
+	memset(&stats, 0, sizeof(struct enet_statistics));
+
+	stats.rx_packets     = strip_info->rx_packets;
+	stats.tx_packets     = strip_info->tx_packets;
+	stats.rx_dropped     = strip_info->rx_dropped;
+	stats.tx_dropped     = strip_info->tx_dropped;
+	stats.tx_errors      = strip_info->tx_errors;
+	stats.rx_errors      = strip_info->rx_errors;
+	stats.rx_over_errors = strip_info->rx_over_errors;
+	return(&stats);
+}
+
+/************************************************************************/
+/* Opening and closing							*/
+
+/*
+ * Here's the order things happen:
+ * When the user runs "slattach -p strip ..."
+ *  1. The TTY module calls strip_open
+ *  2. strip_open calls strip_alloc
+ *  3.                  strip_alloc calls register_netdev
+ *  4.                  register_netdev calls strip_dev_init
+ *  5. then strip_open finishes setting up the strip_info
+ *
+ * When the user runs "ifconfig st<x> up address netmask ..."
+ *  6. strip_open_low gets called
+ *
+ * When the user runs "ifconfig st<x> down"
+ *  7. strip_close_low gets called
+ *
+ * When the user kills the slattach process
+ *  8. strip_close gets called
+ *  9. strip_close calls dev_close
+ * 10. if the device is still up, then dev_close calls strip_close_low
+ * 11. strip_close calls strip_free
+ */
+
+/* Open the low-level part of the STRIP channel. Easy! */
+
+static int strip_open_low(struct device *dev)
+{
+	struct strip *strip_info = (struct strip *)(dev->priv);
+	unsigned long len;
+
+	if (strip_info->tty == NULL) 
+		return(-ENODEV);
+
+	/*
+	 * Allocate the STRIP frame buffers:
+	 *
+	 * rbuff	Receive buffer.
+	 * tbuff	Transmit buffer.
+	 * cbuff        Temporary compression buffer.
+	 */
+
+	len = STRIP_ENCAP_SIZE(dev->mtu);
+	if (len < STRIP_ENCAP_SIZE(576)) 
+		len = STRIP_ENCAP_SIZE(576);
+	strip_info->rx_buff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+	if (strip_info->rx_buff == NULL) 
+		goto norbuff;
+	strip_info->sx_buff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+	if (strip_info->sx_buff == NULL) 
+		goto nosbuff;
+	strip_info->tx_buff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+	if (strip_info->tx_buff == NULL) 
+		goto notbuff;
+
+	strip_info->flags   &= (1 << STR_INUSE); /* Clear ESCAPE & ERROR flags */
+	strip_info->mtu	 = STRIP_ENCAP_SIZE(dev->mtu);
+	strip_info->buffsize = len;
+	strip_info->sx_count = 0;
+	strip_info->tx_left  = 0;
+
+	/*
+	 *	Needed because address '0' is special 
+	 */
+	 
+	if (dev->pa_addr == 0) 
+		dev->pa_addr=ntohl(0xC0A80001);
+	dev->tbusy  = 0;
+	dev->start  = 1;
+
+	printk("%s: Initializing Radio.\n", strip_info->dev.name);
+	ResetRadio(strip_info);
+	strip_info->idle_timer.expires  = jiffies + 2 * HZ;
+	add_timer(&strip_info->idle_timer);
+	return(0);
+
+notbuff:
+	kfree(strip_info->sx_buff);
+nosbuff:
+	kfree(strip_info->rx_buff);
+norbuff:
+	return(-ENOMEM);
+}
+
+
+/*
+ *	Close the low-level part of the STRIP channel. Easy! 
+ */
+ 
+static int strip_close_low(struct device *dev)
+{
+	struct strip *strip_info = (struct strip *)(dev->priv);
+
+	if (strip_info->tty == NULL) 
+		return -EBUSY;
+	strip_info->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	dev->tbusy = 1;
+	dev->start = 0;
+    
+	/*
+	 *	Free all STRIP frame buffers.
+	 */
+	if (strip_info->rx_buff) 
+	{
+		kfree(strip_info->rx_buff);
+		strip_info->rx_buff = NULL;
+	}
+	if (strip_info->sx_buff) 
+	{
+		kfree(strip_info->sx_buff); 
+		strip_info->sx_buff = NULL;
+	}
+	if (strip_info->tx_buff) 
+	{
+		kfree(strip_info->tx_buff); 
+		strip_info->tx_buff = NULL; 
+	}
+	del_timer(&strip_info->idle_timer);
+	return 0;
+}
+
+/* 
+ *	This routine is called by DDI when the
+ *	(dyamically assigned) device is registered
+ */
+ 
+static int strip_dev_init(struct device *dev)
+{
+	int i;
+	
+	/*
+	 *	Finish setting up the DEVICE info. 
+	 */
+
+	dev->trans_start        = 0;
+	dev->last_rx            = 0;
+	dev->tx_queue_len       = 30; 	/* Drop after 30 frames queued */
+
+	dev->flags              = 0;
+	dev->family             = AF_INET;
+	dev->metric             = 0;
+	dev->mtu                = STRIP_MTU;
+	dev->type               = ARPHRD_METRICOM;        /* dtang */
+	dev->hard_header_len    = 8; /*sizeof(STRIP_Header);*/
+	/*
+	 *  dev->priv                 Already holds a pointer to our struct strip 
+	 */
+
+	dev->broadcast[0]       = 0;
+	dev->dev_addr[0]        = 0;
+	dev->addr_len           = sizeof(MetricomAddress);
+	dev->pa_addr            = 0;
+	dev->pa_brdaddr         = 0;
+	dev->pa_mask            = 0;
+	dev->pa_alen            = sizeof(unsigned long);
+
+	/*
+	 *	Pointer to the interface buffers. 
+	 */
+	 
+	for (i = 0; i < DEV_NUMBUFFS; i++) 
+		skb_queue_head_init(&dev->buffs[i]);
+
+	/*
+	 *	Pointers to interface service routines. 
+	 */
+
+	dev->open               = strip_open_low;
+	dev->stop               = strip_close_low;
+	dev->hard_start_xmit    = strip_xmit;
+	dev->hard_header        = strip_header;
+	dev->rebuild_header     = strip_rebuild_header;
+	/*  dev->type_trans            unused */
+	/*  dev->set_multicast_list   unused */
+	dev->set_mac_address    = strip_set_dev_mac_address;
+	/*  dev->do_ioctl             unused */
+	/*  dev->set_config           unused */
+	dev->get_stats          = strip_get_stats;
+	return 0;
+}
+
+/*
+ *	Free a STRIP channel. 
+ */
+ 
+static void strip_free(struct strip *strip_info)
+{
+	*(strip_info->referrer) = strip_info->next;
+	if (strip_info->next) 
+		strip_info->next->referrer = strip_info->referrer;
+	strip_info->magic = 0;
+	kfree(strip_info);
+}
+
+/* 
+ *	Allocate a new free STRIP channel 
+ */
+ 
+static struct strip *strip_alloc(void)
+{
+	int channel_id = 0;
+	struct strip **s = &struct_strip_list;
+	struct strip *strip_info = (struct strip *)
+		kmalloc(sizeof(struct strip), GFP_KERNEL);
+
+	if (!strip_info) 
+		return(NULL);	/* If no more memory, return */
+
+	/*
+	 *	Clear the allocated memory 
+	 */
+	 
+	memset(strip_info, 0, sizeof(struct strip));
+
+	/*
+	 *	Search the list to find where to put our new entry
+	 *	(and in the process decide what channel number it is
+	 *	going to be) 
+	 */
+	 
+	while (*s && (*s)->dev.base_addr == channel_id) 
+	{
+		channel_id++;
+		s = &(*s)->next;
+	}
+
+	/*
+	 *	Fill in the link pointers 
+	 */
+	 
+	strip_info->next = *s;
+	if (*s) 
+		(*s)->referrer = &strip_info->next;
+	strip_info->referrer = s;
+	*s = strip_info;
+
+	set_bit(STR_INUSE, &strip_info->flags);
+	strip_info->magic = STRIP_MAGIC;
+	strip_info->tty   = NULL;
+
+	init_timer(&strip_info->idle_timer);
+	strip_info->idle_timer.data     = (long)&strip_info->dev;
+	strip_info->idle_timer.function = strip_IdleTask;
+
+	sprintf(strip_info->if_name, "st%d", channel_id);
+	strip_info->dev.name         = strip_info->if_name;
+	strip_info->dev.base_addr    = channel_id;
+	strip_info->dev.priv         = (void*)strip_info;
+	strip_info->dev.next         = NULL;
+	strip_info->dev.init         = strip_dev_init;
+
+	return(strip_info);
+}
+
+/*
+ *	Open the high-level part of the STRIP channel.
+ *	This function is called by the TTY module when the
+ *	STRIP line discipline is called for.  Because we are
+ *	sure the tty line exists, we only have to link it to
+ *	a free STRIP channel...
+ */
+
+static int strip_open(struct tty_struct *tty)
+{
+	struct strip *strip_info = (struct strip *) tty->disc_data;
+
+	/*
+	 *	First make sure we're not already connected.
+	 */
+	
+	if (strip_info && strip_info->magic == STRIP_MAGIC) 
+		return -EEXIST;
+
+	/*
+	 *	OK.  Find a free STRIP channel to use. 
+	 */
+	 
+	if ((strip_info = strip_alloc()) == NULL) 
+		return -ENFILE;
+
+	/*
+	 *	Register our newly created device so it can be ifconfig'd
+	 * strip_dev_init() will be called as a side-effect
+	 */
+     
+	if (register_netdev(&strip_info->dev) != 0) 
+	{
+		printk("strip: register_netdev() failed.\n");
+		strip_free(strip_info);
+		return -ENFILE;
+	}
+
+	strip_info->tty = tty;
+	tty->disc_data = strip_info;
+	if (tty->driver.flush_buffer) 
+		tty->driver.flush_buffer(tty);
+	if (tty->ldisc.flush_buffer) 
+		tty->ldisc.flush_buffer(tty);
+
+	/*
+	 *	Restore default settings 
+	 */
+	 
+	strip_info->dev.type = ARPHRD_METRICOM;	/* dtang */
+
+	/*
+	 *	Set tty options 
+	 */
+
+	tty->termios->c_iflag |= IGNBRK |IGNPAR;/* Ignore breaks and parity errors. */
+	tty->termios->c_cflag |= CLOCAL;	/* Ignore modem control signals. */
+	tty->termios->c_cflag &= ~HUPCL;	/* Don't close on hup */
+
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+	/*
+	 *	Done.  We have linked the TTY line to a channel. 
+	 */
+	return(strip_info->dev.base_addr);
+}
+
+/*
+ * Close down a STRIP channel.
+ * This means flushing out any pending queues, and then restoring the
+ * TTY line discipline to what it was before it got hooked to STRIP
+ * (which usually is TTY again).
+ */
+static void strip_close(struct tty_struct *tty)
+{
+	struct strip *strip_info = (struct strip *) tty->disc_data;
+
+	/*
+	 *	First make sure we're connected. 
+	 */
+	 
+	if (!strip_info || strip_info->magic != STRIP_MAGIC) 
+		return;
+
+	dev_close(&strip_info->dev);
+	unregister_netdev(&strip_info->dev);
+    
+	tty->disc_data = 0;
+	strip_info->tty = NULL;
+	strip_free(strip_info);
+	tty->disc_data = NULL;
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+
+/************************************************************************/
+/* Perform I/O control calls on an active STRIP channel.       		*/
+
+static int strip_ioctl(struct tty_struct *tty, struct file *file, 
+	unsigned int cmd, unsigned long arg)
+{
+	struct strip *strip_info = (struct strip *) tty->disc_data;
+	int err;
+
+	/*
+	 *	First make sure we're connected. 
+	 */
+	 
+	if (!strip_info || strip_info->magic != STRIP_MAGIC) 
+		return -EINVAL;
+
+	switch(cmd) 
+	{
+		case SIOCGIFNAME:
+			err = verify_area(VERIFY_WRITE, (void*)arg, 16);
+			if (err)
+				return -err;
+			memcpy_tofs((void*)arg, strip_info->dev.name, 
+				strlen(strip_info->dev.name) + 1);
+			return 0;
+
+		case SIOCSIFHWADDR:
+        		return -EINVAL;
+
+		/*
+		 *	Allow stty to read, but not set, the serial port 
+		 */
+	 
+		case TCGETS:
+		case TCGETA:
+			return n_tty_ioctl(tty, (struct file *) file, cmd, 
+				(unsigned long) arg);
+
+		default:
+			return -ENOIOCTLCMD;
+	}
+}
+
+/************************************************************************/
+/* Initialization							*/
+
+/*
+ *	Initialize the STRIP driver.
+ *	This routine is called at boot time, to bootstrap the multi-channel
+ *	STRIP driver
+ */
+
+#ifdef MODULE
+static
+#endif
+int strip_init_ctrl_dev(struct device *dummy)
+{
+	static struct tty_ldisc strip_ldisc;
+	int status;
+	printk("STRIP: version %s (unlimited channels)\n", STRIP_VERSION);
+
+	/*
+	 *	Fill in our line protocol discipline, and register it
+	 */
+	 
+	memset(&strip_ldisc, 0, sizeof(strip_ldisc));
+	strip_ldisc.magic	= TTY_LDISC_MAGIC;
+	strip_ldisc.flags	= 0;
+	strip_ldisc.open	= strip_open;
+	strip_ldisc.close	= strip_close;
+	strip_ldisc.read	= NULL;
+	strip_ldisc.write	= NULL;
+	strip_ldisc.ioctl	= strip_ioctl;
+	strip_ldisc.select       = NULL;
+	strip_ldisc.receive_buf  = strip_receive_buf;
+	strip_ldisc.receive_room = strip_receive_room;
+	strip_ldisc.write_wakeup = strip_write_some_more;
+	status = tty_register_ldisc(N_STRIP, &strip_ldisc);
+	if (status != 0) 
+	{
+		printk("STRIP: can't register line discipline (err = %d)\n", status);
+	}
+
+#ifdef MODULE
+	 return status;
+#else
+	/* Return "not found", so that dev_init() will unlink
+	 * the placeholder device entry for us.
+	 */
+	return ENODEV;
+#endif
+}
+
+/************************************************************************/
+/* From here down is only used when compiled as an external module        */
+
+#ifdef MODULE
+
+int init_module(void)
+{
+    return strip_init_ctrl_dev(0);
+}
+
+void cleanup_module(void)
+{
+	int i;
+	while (struct_strip_list) 
+		strip_free(struct_strip_list);
+
+	if ((i = tty_register_ldisc(N_STRIP, NULL)))  
+		printk("STRIP: can't unregister line discipline (err = %d)\n", i);
+}
+#endif /* MODULE */
diff -u --recursive --new-file v1.3.81/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v1.3.81/linux/drivers/pci/pci.c	Thu Mar 28 17:34:34 1996
+++ linux/drivers/pci/pci.c	Mon Apr  1 11:04:10 1996
@@ -93,8 +93,8 @@
 	DEVICE( FD,		FD_36C70,	"TMC-18C30"),
 	DEVICE( SI,		SI_6201,	"6201"),
 	DEVICE( SI,		SI_6202,	"6202"),
-	DEVICE( SI,		SI_6205,	"6205"),
 	DEVICE( SI,		SI_503,		"85C503"),
+	DEVICE( SI,		SI_6205,	"6205"),
 	DEVICE( SI,		SI_501,		"85C501"),
 	DEVICE( SI,		SI_496,		"85C496"),
 	DEVICE( SI,		SI_601,		"85C601"),
diff -u --recursive --new-file v1.3.81/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- v1.3.81/linux/drivers/scsi/53c7,8xx.c	Fri Mar  1 07:50:50 1996
+++ linux/drivers/scsi/53c7,8xx.c	Tue Apr  2 09:01:55 1996
@@ -62,8 +62,15 @@
  *  the fourth byte from 50 to 25.
  */
 
+#include <linux/config.h>
+
+#ifdef CONFIG_SCSI_NCR53C7xx_sync
+#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_DISCONNECT|\
+	OPTION_SYNCHRONOUS|OPTION_ALWAYS_SYNCHRONOUS)
+#else
 #define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_DISCONNECT|\
 	OPTION_SYNCHRONOUS)
+#endif
 
 /*
  * Sponsored by 
@@ -635,7 +642,11 @@
 /* Template for "preferred" synchronous transfer parameters. */
 
 static const unsigned char sdtr_message[] = {
+#ifdef CONFIG_SCSI_NCR53C7xx_FAST
+    EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 25 /* *4ns */, 8 /* off */ 
+#else
     EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */ 
+#endif
 };
 
 /* Template to request asynchronous transfers */
diff -u --recursive --new-file v1.3.81/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.81/linux/drivers/scsi/Config.in	Thu Mar 14 12:57:45 1996
+++ linux/drivers/scsi/Config.in	Tue Apr  2 08:43:04 1996
@@ -36,6 +36,10 @@
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'NCR53c7,8xx SCSI support'  CONFIG_SCSI_NCR53C7xx $CONFIG_SCSI
 fi
+if [ "$CONFIG_SCSI_NCR53C7xx" != "n" ]; then
+  bool '   always negotiate synchronous transfers' CONFIG_SCSI_NCR53C7xx_sync
+  bool '   allow FAST-SCSI [10MHz]' CONFIG_SCSI_NCR53C7xx_FAST
+fi
 dep_tristate 'Always IN2000 SCSI support (test release)' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
 dep_tristate 'QLOGIC SCSI support' CONFIG_SCSI_QLOGIC $CONFIG_SCSI
diff -u --recursive --new-file v1.3.81/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v1.3.81/linux/drivers/scsi/aha1542.c	Fri Mar  1 07:50:51 1996
+++ linux/drivers/scsi/aha1542.c	Mon Apr  1 10:46:31 1996
@@ -646,7 +646,7 @@
       any2scsi(ccb[mbo].dataptr, buff);
     };
     ccb[mbo].idlun = (target&7)<<5 | direction | (lun & 7); /*SCSI Target Id*/
-    ccb[mbo].rsalen = 12;
+    ccb[mbo].rsalen = 16;
     ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
     ccb[mbo].commlinkid = 0;
 
diff -u --recursive --new-file v1.3.81/linux/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- v1.3.81/linux/drivers/scsi/scsi.c	Sat Mar 23 13:15:00 1996
+++ linux/drivers/scsi/scsi.c	Mon Apr  1 10:46:31 1996
@@ -19,6 +19,10 @@
  *
  *  Native multichannel and wide scsi support added 
  *  by Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ *
+ *  Added request_module("scsi_hostadapter") for kerneld:
+ *  (Put an "alias scsi_hostadapter your_hostadapter" in /etc/conf.modules)
+ *  Bjorn Ekwall  <bj0rn@blox.se>
  */
 
 /*
@@ -26,6 +30,8 @@
  * symbol tables.
  */
 #define _SCSI_SYMS_VER_
+
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <asm/system.h>
@@ -44,7 +50,9 @@
 #include "hosts.h"
 #include "constants.h"
 
-#include <linux/config.h>
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
 
 #undef USE_STATIC_SCSI_MEMORY
 
@@ -608,6 +616,15 @@
     return 0;     /* assume no peripheral if any sort of error */
 
   /*
+   * Check the peripheral qualifier field - this tells us whether LUNS
+   * are supported here or not.
+   */
+  if( (scsi_result[0] >> 5) == 3 )
+    {
+      return 0;     /* assume no peripheral if any sort of error */
+    }
+
+  /*
    * It would seem some TOSHIBA CDROM gets things wrong
    */
   if (!strncmp (scsi_result + 8, "TOSHIBA", 7) &&
@@ -3058,6 +3075,10 @@
 	
 	/* Load upper level device handler of some kind */
     case MODULE_SCSI_DEV:
+#ifdef CONFIG_KERNELD
+	if (scsi_hosts == NULL)
+		request_module("scsi_hostadapter");
+#endif
 	return scsi_register_device_module((struct Scsi_Device_Template *) ptr);
 	/* The rest of these are not yet implemented */
 	
diff -u --recursive --new-file v1.3.81/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v1.3.81/linux/drivers/scsi/seagate.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/scsi/seagate.c	Sat Mar 30 22:13:21 1996
@@ -1718,7 +1718,7 @@
  * 256 heads * 64 sectors. 
  */
 
-      if ((cylinders > 1024) || (sectors > 64)) 
+      if ((cylinders > 1024) || (sectors > 64)) {
 	/* The Seagate's seem to have some mapping
 	 * Multiple heads * sectors * cyl to get capacity
 	 * Then start rounding down. */
@@ -1730,7 +1730,7 @@
 	{
 		heads *= 2;	/* For some reason, they go in multiples */
 		cylinders = capacity / heads;
-	};
+	}
       }
       ip[0] = heads;
       ip[1] = sectors;
diff -u --recursive --new-file v1.3.81/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.3.81/linux/drivers/scsi/sr.c	Sun Mar 31 00:13:17 1996
+++ linux/drivers/scsi/sr.c	Tue Apr  2 08:43:04 1996
@@ -58,7 +58,8 @@
 static int * sr_blocksizes;
 
 static int sr_open(struct inode *, struct file *);
-static void get_sectorsize(int);
+void get_sectorsize(int);
+void sr_photocd(struct inode *);
 
 extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
 
@@ -291,9 +292,10 @@
 }
 
 /*
- * Here I tried to implement better support for PhotoCD's.
+ * Here I tried to implement support for multisession-CD's
  * 
- * Much of this has do be done with vendor-specific SCSI-commands.
+ * Much of this has do be done with vendor-specific SCSI-commands, becauce
+ * multisession is newer than the SCSI-II standard.
  * So I have to complete it step by step. Useful information is welcome.
  *
  * Actually works:
@@ -304,17 +306,17 @@
  *              work now without running the program "set_density"
  *              Multisession CD's are supported too.
  *
- *   kraxel@cs.tu-berlin.de (Gerd Knorr)
+ *   Gerd Knorr <kraxel@cs.tu-berlin.de> 
  */
 /*
  * 19950704 operator@melchior.cuivre.fdn.fr (Thomas Quinot)
  *
  *   - SONY:	Same as Nec.
  *
- *   - PIONEER: works with SONY code
+ *   - PIONEER: works with SONY code (may be others too ?)
  */
 
-static void sr_photocd(struct inode *inode)
+void sr_photocd(struct inode *inode)
 {
     unsigned long   sector,min,sec,frame;
     unsigned char   buf[40];    /* the buffer for the ioctl */
@@ -325,17 +327,17 @@
     
     if (scsi_CDs[MINOR(inode->i_rdev)].xa_flags & 0x02) {
 #ifdef DEBUG
-	printk("sr_photocd: CDROM and/or the driver does not support multisession CD's");
+	printk(KERN_DEBUG "sr_photocd: CDROM and/or driver do not support multisession CD's");
 #endif
 	return;
     }
     
     if (!suser()) {
-	/* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed for me.
-	 * That's why mpcd_sector will be initialized with zero, because I'm not
-	 * able to get the right value. Necessary only if access_count is 1, else
-	 * no disk change happened since the last call of this function and we can
-	 * keep the old value.
+	/* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed
+         * for me. That's why mpcd_sector will be initialized with zero,
+         * because I'm not able to get the right value. Necessary only if
+         * access_count is 1, else no disk change happened since the last
+         * call of this function and we can keep the old value.
 	 */
 	if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) {
 	    scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0;
@@ -353,7 +355,7 @@
 	
     case SCSI_MAN_NEC:
 #ifdef DEBUG
-	printk("sr_photocd: use NEC code\n");
+	printk(KERN_DEBUG "sr_photocd: use NEC code\n");
 #endif
 	memset(buf,0,40);
 	*((unsigned long*)buf)   = 0x0;   /* we send nothing...     */
@@ -364,11 +366,12 @@
 	rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
 			   SCSI_IOCTL_SEND_COMMAND, buf);
 	if (rc != 0) {
-	    printk("sr_photocd: ioctl error (NEC): 0x%x\n",rc);
+            if (rc != 0x28000002) /* drop "not ready" */
+                printk(KERN_WARNING"sr_photocd: ioctl error (NEC): 0x%x\n",rc);
 	    break;
 	}
 	if (rec[14] != 0 && rec[14] != 0xb0) {
-	    printk("sr_photocd: (NEC) Hmm, seems the CDROM doesn't support multisession CD's\n");
+	    printk(KERN_INFO"sr_photocd: (NEC) Hmm, seems the CDROM doesn't support multisession CD's\n");
 	    no_multi = 1;
 	    break;
 	}
@@ -379,14 +382,14 @@
 	is_xa  = (rec[14] == 0xb0);
 #ifdef DEBUG
 	if (sector) {
-	    printk("sr_photocd: multisession CD detected. start: %lu\n",sector);
+	    printk(KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector);
 	}
 #endif
 	break;
 	
     case SCSI_MAN_TOSHIBA:
 #ifdef DEBUG
-	printk("sr_photocd: use TOSHIBA code\n");
+	printk(KERN_DEBUG "sr_photocd: use TOSHIBA code\n");
 #endif
 	
 	/* we request some disc information (is it a XA-CD ?,
@@ -402,16 +405,15 @@
 	    if (rc == 0x28000002) {
 		/* Got a "not ready" - error. No chance to find out if this is
 		 * because there is no CD in the drive or because the drive
-		 * don't knows multisession CD's. So I need to do an extra check... */
-		if (kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
-				      SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
-		    printk("sr_photocd: drive not ready\n");
-		} else {
-		    printk("sr_photocd: (TOSHIBA) Hmm, seems the CDROM doesn't support multisession CD's\n");
+		 * don't knows multisession CD's. So I need to do an extra
+                 * check... */
+		if (!kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+				       SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
+		    printk(KERN_INFO "sr_photocd: (TOSHIBA) Hmm, seems the CDROM doesn't support multisession CD's\n");
 		    no_multi = 1;
 		}
 	    } else
-		printk("sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
+		printk(KERN_WARNING"sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
 	    break; /* if the first ioctl fails, we don't call the second one */
 	}
 	is_xa  = (rec[0] == 0x20);
@@ -422,7 +424,7 @@
 	if (sector) {
 	    sector -= CD_BLOCK_OFFSET;
 #ifdef DEBUG
-	    printk("sr_photocd: multisession CD detected: start: %lu\n",sector);
+	    printk(KERN_DEBUG "sr_photocd: multisession CD detected: start: %lu\n",sector);
 #endif
 	}
 	
@@ -436,17 +438,17 @@
 	rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
 			       SCSI_IOCTL_SEND_COMMAND, buf);
 	if (rc != 0) {
-	    printk("sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc);
+	    printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc);
 	    break;
 	}
 #ifdef DEBUG
-	printk("sr_photocd: get_density: 0x%x\n",rec[4]);
+	printk(KERN_DEBUG "sr_photocd: get_density: 0x%x\n",rec[4]);
 #endif
 	
 	/* ...and only if necessary a set_density */
 	if ((rec[4] != 0x81 && is_xa) || (rec[4] != 0 && !is_xa)) {
 #ifdef DEBUG
-	    printk("sr_photocd: doing set_density\n");
+	    printk(KERN_DEBUG "sr_photocd: doing set_density\n");
 #endif
 	    memset(buf,0,40);
 	    *((unsigned long*)buf)   = 12;  /* sending 12 bytes... */
@@ -454,16 +456,17 @@
 	    cmd[0] = 0x15;
 	    cmd[1] = (1 << 4);
 	    cmd[4] = 12;
-	    send = &cmd[6];                 /* this is a 6-Byte command          */
-	    send[ 3] = 0x08;                /* the data for the command          */
-	    send[ 4] = (is_xa) ? 0x81 : 0;  /* density 0x81 for XA-CD's, 0 else  */
+	    send = &cmd[6];                 /* this is a 6-Byte command    */
+	    send[ 3] = 0x08;                /* the data for the command    */
+	    send[ 4] = (is_xa) ? 0x81 : 0;  /* density 0x81 for XA, 0 else */
 	    send[10] = 0x08;
 	    rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
 				   SCSI_IOCTL_SEND_COMMAND, buf);
 	    if (rc != 0) {
-		printk("sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc);
+		printk(KERN_WARNING "sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc);
 	    }
-	    /* The set_density command may have changed the sector size or capacity. */
+	    /* The set_density command may have changed the
+             * sector size or capacity. */
 	    scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
 	}
 	break;
@@ -471,7 +474,7 @@
     case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */
     case SCSI_MAN_PIONEER:
 #ifdef DEBUG
-	printk("sr_photocd: use SONY/PIONEER code\n");
+	printk(KERN_DEBUG "sr_photocd: use SONY/PIONEER code\n");
 #endif
 	memset(buf,0,40);
 	*((unsigned long*)buf)   = 0x0;   /* we send nothing...     */
@@ -483,11 +486,12 @@
 			       SCSI_IOCTL_SEND_COMMAND, buf);
 	
 	if (rc != 0) {
-	    printk("sr_photocd: ioctl error (SONY): 0x%x\n",rc);
+            if (rc != 0x28000002) /* drop "not ready" */
+                printk(KERN_WARNING "sr_photocd: ioctl error (SONY): 0x%x\n",rc);
 	    break;
 	}
 	if ((rec[0] << 8) + rec[1] != 0x0a) {
-	    printk("sr_photocd: (SONY) Hmm, seems the CDROM doesn't support multisession CD's\n");
+	    printk(KERN_INFO "sr_photocd: (SONY) Hmm, seems the CDROM doesn't support multisession CD's\n");
 	    no_multi = 1;
 	    break;
 	}
@@ -495,7 +499,7 @@
 	is_xa = !!sector;
 #ifdef DEBUG
 	if (sector)
-	    printk ("sr_photocd: multisession CD detected. start: %lu\n",sector);
+	    printk (KERN_DEBUG "sr_photocd: multisession CD detected. start: %lu\n",sector);
 #endif
 	break;
 		
@@ -972,7 +976,7 @@
     }
 }
 
-static void get_sectorsize(int i){
+void get_sectorsize(int i){
     unsigned char cmd[10];
     unsigned char *buffer;
     int the_result, retries;
diff -u --recursive --new-file v1.3.81/linux/drivers/scsi/sr_ioctl.c linux/drivers/scsi/sr_ioctl.c
--- v1.3.81/linux/drivers/scsi/sr_ioctl.c	Tue Mar 19 08:58:59 1996
+++ linux/drivers/scsi/sr_ioctl.c	Mon Apr  1 10:46:31 1996
@@ -4,6 +4,7 @@
 #include <linux/fs.h>
 #include <asm/segment.h>
 #include <linux/errno.h>
+#include <linux/string.h>
 
 #include <linux/blk.h>
 #include "scsi.h"
@@ -13,6 +14,9 @@
 
 #include <linux/cdrom.h>
 
+extern void get_sectorsize(int);
+extern void sr_photocd(struct inode *);
+
 #define IOCTL_RETRIES 3
 /* The CDROM is fairly slow, so we need a little extra time */
 /* In fact, it is very slow if it has to spin up first */
@@ -203,7 +207,7 @@
 	char * buffer;
 	
 	sr_cmd[0] = SCMD_READ_TOC;
-	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02;    /* MSF format */
+	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
 	sr_cmd[6] = 0;
 	sr_cmd[7] = 0;              /* MSB of length (12) */
@@ -231,7 +235,7 @@
     case CDROMREADTOCENTRY:
     {
 	struct cdrom_tocentry tocentry;
-	char * buffer;
+	unsigned char * buffer;
 	
         err = verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_tocentry));
         if (err) return err;
@@ -239,7 +243,8 @@
 	memcpy_fromfs (&tocentry, (void *) arg, sizeof (struct cdrom_tocentry));
 	
 	sr_cmd[0] = SCMD_READ_TOC;
-	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02;    /* MSF format */
+	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) |
+          (tocentry.cdte_format == CDROM_MSF ? 0x02 : 0);
 	sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
 	sr_cmd[6] = tocentry.cdte_track;
 	sr_cmd[7] = 0;             /* MSB of length (12)  */
@@ -251,14 +256,17 @@
 	
 	result = do_ioctl (target, sr_cmd, buffer, 12);
 	
+        tocentry.cdte_ctrl = buffer[5] & 0xf;	
+        tocentry.cdte_adr = buffer[5] >> 4;
+        tocentry.cdte_datamode = (tocentry.cdte_ctrl & 0x04) ? 1 : 0;
 	if (tocentry.cdte_format == CDROM_MSF) {
 	    tocentry.cdte_addr.msf.minute = buffer[9];
 	    tocentry.cdte_addr.msf.second = buffer[10];
 	    tocentry.cdte_addr.msf.frame = buffer[11];
-	    tocentry.cdte_ctrl = buffer[5] & 0xf;
 	}
 	else
-	    tocentry.cdte_addr.lba = (int) buffer[0];
+	    tocentry.cdte_addr.lba = (((((buffer[8] << 8) + buffer[9]) << 8)
+                                       + buffer[10]) << 8) + buffer[11];
 	
 	scsi_free(buffer, 512);
 	
@@ -288,6 +296,25 @@
 	result = do_ioctl(target, sr_cmd, NULL, 255);
 	return result;
 	
+    case CDROMCLOSETRAY:
+	sr_cmd[0] = START_STOP;
+	sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5);
+	sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+	sr_cmd[4] = 0x03;
+	
+        if ((result = do_ioctl(target, sr_cmd, NULL, 255)))
+          return result;
+
+        /* Gather information about newly inserted disc */
+        check_disk_change (inode->i_rdev);
+        sr_ioctl (inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+        sr_photocd (inode);
+
+        if (scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
+          get_sectorsize (MINOR(inode->i_rdev));
+
+        return 0;
+
     case CDROMEJECT:
         /*
          * Allow 0 for access count for auto-eject feature.
@@ -455,6 +482,38 @@
 	if (err)
 	    return err;
 	memcpy_tofs ((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
+	return result;
+    }
+	
+    case CDROM_GET_UPC:
+    {
+	struct cdrom_mcn mcn;
+	char * buffer;
+	
+	sr_cmd[0] = SCMD_READ_SUBCHANNEL;
+	sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5);
+	sr_cmd[2] = 0x40;    /* I do want the subchannel info */
+	sr_cmd[3] = 0x02;    /* Give me medium catalog number info */
+	sr_cmd[4] = sr_cmd[5] = 0;
+	sr_cmd[6] = 0;
+	sr_cmd[7] = 0;
+	sr_cmd[8] = 24;
+	sr_cmd[9] = 0;
+	
+	buffer = (unsigned char*) scsi_malloc(512);
+	if(!buffer) return -ENOMEM;
+	
+	result = do_ioctl(target, sr_cmd, buffer, 24);
+	
+	memcpy (mcn.medium_catalog_number, buffer + 9, 13);
+        mcn.medium_catalog_number[13] = 0;
+
+	scsi_free(buffer, 512);
+	
+	err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_mcn));
+	if (err)
+	    return err;
+	memcpy_tofs ((void *) arg, &mcn, sizeof (struct cdrom_mcn));
 	return result;
     }
 	
diff -u --recursive --new-file v1.3.81/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v1.3.81/linux/drivers/sound/Readme.cards	Sun Mar 31 00:13:17 1996
+++ linux/drivers/sound/Readme.cards	Mon Apr  1 12:56:43 1996
@@ -16,8 +16,9 @@
 mail me and ask about these cards. The unsupported cards are:
 
 	- All PnP soundcards (SB PnP, GUS PnP, Soundscape PnP etc.)
-		(SB PnP in first 3.6-alpha version (Apr?), GUS PnP bit later,
-		Soundscape PnP propably much later, others ???).
+		(SB PnP in first 3.6-alpha version (Apr 96?), GUS PnP bit later,
+		Soundscape PnP propably much later, others ???). See
+	 	"Configuring PnP soundcards" below for some hints.
 	- Mwave soundcards and motherboards
 		(Version 3.6 or 3.7. Depends on how fast I get a Mwave
 		card and suitable documents for it).
@@ -37,6 +38,59 @@
 	- Yamaha OPL4 (on cards having _RAM_ for samples)
 		(Late 96?. Works as OPL3 with current driver versions)
 
+How to use sound without recompiling kernel and/or sound driver
+---------------------------------------------------------------
+
+There is commercial sound driver which should be released during Apr 96.
+It comes in precompiled form and doesn't require recompiling of kernel. See
+http://www.4Front-tech.com/uss.html for more info.
+
+Configuring PnP cards
+---------------------
+
+New versions of most soundcards use so called ISA PnP protocol for
+soft configuring their I/O, IRQ, DMA and shared memory resources.
+Currently at least cards made by Creative Technology (SB32 and SB32AWE
+PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and
+Aztech (some Sound Galazy models) use PnP technology. The CS4232 audio
+chip by Crystal Semiconductor (Intel Atlantis, HP Pavillon and many other
+motherboards) is also based on PnP technology but there is a "native" driver
+available for it (see information about CS4232 later in this document).
+
+PnP soundcards (as well as most other PnP ISA cards) are not supported
+by version 3.5 of this driver (Linux 1.3.xx and Linux 2.0.x). Proper
+support for them should be released during spring 96 
+(see http://personal.eunet.fi/pp/voxware for latest info).
+
+There is a method to get most of the PnP cards to work. The basic method
+is the following:
+
+1) Boot DOS so that card's DOS drivers have chance to initialize the
+card.
+2) _Cold_ boot to Linux by using "loadlin.exe". Hitting ctrl-alt-del
+works with older machines but causes hard reset of all cards on latest
+(Pentium) machines.
+3) If you have sound driver in Linux configured properly, the card should work
+now. "Proper" means here that I/O, IRQ and DMA settings are the same than in
+DOS. The hard part is to find which settings were used. See documentation of
+your card for more info.
+
+Windows 95 could work as well as DOS but running loadlin may be somehow 
+difficult. Propably you should "shut down" your machine to MS-DOS mode
+before running it.
+
+Some machines have BIOS utility for setting PnP resources. This is a good
+way to configure some cards. In this case you don't need to boot DOS/Win95
+prior starting Linux.
+Another way to initialize PnP cards without DOS/Win95 is a Linux based
+PnP isolation tool. When writing this there is a pre alpha test version
+of such tool available from ftp://ftp.demon.co.uk/pub/unix/linux/utils. The
+file is called isapnptools-*. Please note that this tool is just a temporary
+solution which may be incompatible with future kernel versions having proper
+support for PnP cards.
+These two methods don't work with GUS PnP which requires some additional
+initialization (cards DOS/Win95 driver does it). 
+
 Read this before trying to configure the driver
 -----------------------------------------------
 
@@ -805,8 +859,8 @@
 Audio Excel DSP16
 -----------------
 
-See comments in aedsp16.c.
-
+Support for this card is currently not functional. A new driver for it
+should be available later this year.
 
 PCMCIA cards
 ------------
@@ -836,8 +890,10 @@
 
 First of all. There is an easy way to make most soundcards to work
 with Linux. Just use the DOS based driver to initialize the card
-to a _known_ state. Then ctrl-alt-del to Linux. If Linux is configured
+to a _known_ state. Then use loadlin.exe to boot Linux. If Linux is configured
 to use the sama I/O, IRQ and DMA numbers than DOS, the card could work.
+(ctrl-alt-del can be used in place of loadlin.exe but it doesn't work with
+new motherboards). This method works also with all/most PnP soundcards.
 
 Don't get fooled with SB compatibility. Most cards are compatible with
 SB but that may require a TSR which is not possible with Linux. If
@@ -852,9 +908,9 @@
 to all low level drivers and be tested too, maybe with multiple
 operating systems). For this reason I have made a desicion to not support
 obsolete cards. It's possible that someone else makes a separately 
-distributed driver (diffs) for the card. Version v4.0 will be much more
+distributed driver (diffs) for the card. Version v3.6 will be much more
 modular so making separately distributed drivers will be easier with it.
-(The bad news is that v4.0 will not be available before late -96).
+(The bad news is that v3.6 will not be available before summer -96).
 
 Writing a driver for a new card is not possible if there are no 
 programming information available about the card. If you don't
@@ -864,42 +920,24 @@
 released technical details of the card. Do this before contacting me. I
 can only answer 'no' if there are no programming information available.
 
-Some companies don't give low level technical information about their
-products to public or at least their require signing a NDA.
-
-I have also made decicion to not accept code based on reverse engineering
+I have made decicion to not accept code based on reverse engineering
 to the driver. There are three main reasons: First I don't want to break
 relationships to sound card manufacturers. The second reason is that
-maintaining and supporting a driver withoun any specs will be a pain. The
-third reason is that why shoud we help such companies in selling their
-products to Linux users when they don't want to sell to Linux users
-at all?
-
-Unfortunately many of the leading soundcard manufacturers are not willing
-to co-operate with Linux/Unix community. For example: Creative Technology 
-doesn't give information about the ASP chip and the Emu synth chip of AWE32
-and SB32. Turtle Beach don't give information about any of their
-products. MediaVision requires NDA before they are willing to
-give information about the Jazz16 chip (fortunately Logitech gave
-the info about SM Wave).
-
-So at least the above three companies are out until they are willing to 
-release documentation about their products (the situation is the
-same with many DOS based freeware/shareware games and utilities). If
-you want to use Linux/Unix with their cards, please don't try to push
-me. It's a better idea to contact the manufacturer and explain that
-you want to use your card with Linux/Unix. You could also try to sell
-your card to somebody else and then buy a card that is supported by the driver.
-
-However it's possible that things change and a driver gets written
-for some of the banned cards. Please, don't send me messages asking if
-there is any plans to write a driver for the cards mentioned above. I 
-will put any news to sound driver's www home page (see below).
+maintaining and supporting a driver withoun any specs will be a pain.
+The third reason is that companies have freedom to refuse selling their
+products to other than Windows useres.
+
+Some companies don't give low level technical information about their
+products to public or at least their require signing a NDA. It's not
+possible to implement a freeware driver for them. However it's possible
+that support for such cards become available in the commercial version
+of this driver (see http://www.4Front-tech.com/uss.html for more info).
 
 There are some common audio chipsets that are not supported yet. For example
 Sierra Aria and IBM Mwave. It's possible that these architectures
 get some support in future but I can't make any promises. Just look
-at the home page for latest info.
+at the home page (http://personal.eunet.fi/pp/voxware/new_cards.html)
+for latest info.
 
 Information about unsupported soundcards and chipsets is welcome as well
 as free copies of soundcards, SDKs and operating systems.
@@ -909,4 +947,5 @@
 Hannu Savolainen
 hannu@voxware.pp.fi
 Sound driver's www home page: http://personal.eunet.fi/pp/voxware
+                   US mirror: http://www.4Front-Tech.com/usslite
 
diff -u --recursive --new-file v1.3.81/linux/fs/fat/inode.c linux/fs/fat/inode.c
--- v1.3.81/linux/fs/fat/inode.c	Mon Mar 11 11:25:57 1996
+++ linux/fs/fat/inode.c	Tue Apr  2 08:45:13 1996
@@ -502,7 +502,7 @@
 
 	error = inode_change_ok(inode, attr);
 	if (error)
-		return error;
+		return MSDOS_SB(inode->i_sb)->options.quiet ? 0 : error;
 
 	if (((attr->ia_valid & ATTR_UID) && 
 	     (attr->ia_uid != MSDOS_SB(inode->i_sb)->options.fs_uid)) ||
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/Makefile linux/fs/nfs/Makefile
--- v1.3.81/linux/fs/nfs/Makefile	Tue Feb 20 09:51:05 1996
+++ linux/fs/nfs/Makefile	Sun Mar 31 00:05:22 1996
@@ -8,7 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := nfs.o
-O_OBJS   := proc.o sock.o rpcsock.o inode.o file.o dir.o symlink.o
+O_OBJS   := proc.o sock.o rpcsock.o inode.o file.o bio.o \
+	    nfsiod.o dir.o symlink.o
 
 ifdef CONFIG_ROOT_NFS
 O_OBJS += nfsroot.o
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/README linux/fs/nfs/README
--- v1.3.81/linux/fs/nfs/README	Thu Jan  1 02:00:00 1970
+++ linux/fs/nfs/README	Sun Mar 31 20:27:59 1996
@@ -0,0 +1,114 @@
+
+
+    This is an NFS client for Linux that supports async RPC calls for
+    read-ahead (and hopefully soon, write-back) on regular files. 
+
+    The implementation uses a straightforward nfsiod scheme.  After
+    trying out a number of different concepts, I finally got back to
+    this concept, because everything else either didn't work or gave me
+    headaches. It's not flashy, but it works without hacking into any
+    other regions of the kernel.
+
+
+    HOW TO USE
+
+    This stuff compiles as a loadable module (I developed it on 1.3.77).
+    Simply type mkmodule, and insmod nfs.o. This will start for nfsiod's
+    at the same time (which will show up under the pesudonym of insmod in
+    ps-style listings).
+
+    Alternatively, you can put it right into the kernel: remove everything
+    from fs/nfs, move the Makefile and all *.c to this directory, and
+    copy all *.h files to include/linux.
+
+    After mounting, you should be able to watch (with tcpdump) several
+    RPC READ calls being placed simultaneously.
+
+
+    HOW IT WORKS
+
+    When a process reads from a file on an NFS volume, the following
+    happens:
+
+     *	nfs_file_read sets file->f_reada if more than 1K is
+    	read at once. It then calls generic_file_read.
+
+     *	generic_file_read requests one ore more pages via
+    	nfs_readpage.
+
+     *	nfs_readpage allocates a request slot with an nfsiod
+    	daemon, fills in the READ request, sends out the
+    	RPC call, kicks the daemon, and returns.
+    	If there's no free biod, nfs_readpage places the
+    	call directly, waiting for the reply (sync readpage).
+
+     *	nfsiod calls nfs_rpc_doio to collect the reply. If the
+    	call was successful, it sets page->uptodate and
+    	wakes up all processes waiting on page->wait;
+
+    This is the rough outline only. There are a few things to note:
+
+     *	Async RPC will not be tried when server->rsize < PAGE_SIZE.
+
+     *	When an error occurs, nfsiod has no way of returning
+    	the error code to the user process. Theerefore, it flags
+    	page->error and wakes up all processes waiting on that
+    	page (they usually do so from withing generic_readpage).
+
+    	generic_readpage finds that the page is still not
+    	uptodate, and calls nfs_readpage again. This time around,
+    	nfs_readpage notices that page->error is set and
+    	unconditionally does a synchronous RPC call.
+
+    	This area needs a lot of improvement, since read errors
+    	are not that uncommon (e.g. we have to retransmit calls
+    	if the fsuid is different from the ruid in order to
+    	cope with root squashing and stuff like this).
+
+	Retransmits with fsuid/ruid change should be handled by
+	nfsiod, but this doesn't come easily (a more general nfs_call
+	routine that does all this may be useful...)
+
+     *	To save some time on readaheads, we save one data copy
+    	by frobbing the page into the iovec passed to the
+	RPC code so that the networking layer copies the
+    	data into the page directly.
+
+    	This needs to be adjustable (different authentication
+    	flavors; AUTH_NULL versus AUTH_SHORT verifiers).
+
+     *	Currently, a fixed number of nfsiod's is spawned from
+    	within init_nfs_fs. This is problematic when running
+    	as a loadable module, because this will keep insmod's
+    	memory allocated. As a side-effect, you will see the
+    	nfsiod processes listed as several insmod's when doing
+    	a `ps.'
+
+     * 	This NFS client implements server congestion control via
+	Van Jacobson slow start as implemented in 44BSD. I haven't
+	checked how well this behaves, but since Rick Macklem did
+	it this way, it should be okay :-)
+
+
+    WISH LIST
+
+    After giving this thing some testing, I'd like to add some more
+    features:
+
+     *	Some sort of async write handling. True write-back doesn't
+	work with the current kernel (I think), because invalidate_pages
+	kills all pages, regardless of whether they're dirty or not.
+	Besides, this may require special bdflush treatment because
+	write caching on clients is really hairy.
+
+	Alternatively, a write-through scheme might be useful where
+	the client enqueues the request, but leaves collecting the
+	results to nfsiod. Again, we need a way to pass RPC errors
+	back to the application.
+
+     *	Support for different authentication flavors.
+
+     *	/proc/net/nfsclnt (for nfsstat, etc.).
+
+March 29, 1996
+Olaf Kirch <okir@monad.swb.de>
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/bio.c linux/fs/nfs/bio.c
--- v1.3.81/linux/fs/nfs/bio.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/nfs/bio.c	Sat Mar 30 23:54:01 1996
@@ -0,0 +1,164 @@
+/*
+ * linux/fs/nfs/bio.c
+ *
+ * Block I/O for NFS
+ *
+ * Partial copy of Linus' read cache modifications to fs/nfs/file.c
+ * modified for async RPC by okir@monad.swb.de
+ *
+ * We do an ugly hack here in order to return proper error codes to the
+ * user program when a read request failed. This is a huge problem because
+ * generic_file_read only checks the return value of inode->i_op->readpage()
+ * which is usually 0 for async RPC. To overcome this obstacle, we set
+ * the error bit of the page to 1 when an error occurs, and make nfs_readpage
+ * transmit requests synchronously when encountering this.
+ *
+ * Another possible solution to this problem may be to have a cache of recent
+ * RPC call results indexed by page pointer, or even a result code field
+ * in struct page.
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfsiod.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#undef DEBUG_BIO
+#ifdef DEBUG_BIO
+#define dprintk(args...)	printk(## args)
+#else
+#define dprintk(args...)	/* nothing */
+#endif
+
+static inline int
+do_read_nfs_sync(struct inode * inode, struct page * page)
+{
+	struct nfs_fattr fattr;
+	int		result, refresh = 0;
+	int		count = PAGE_SIZE;
+	int		rsize = NFS_SERVER(inode)->rsize;
+	char		*buf = (char *) page_address(page);
+	unsigned long	pos = page->offset;
+
+	dprintk("NFS: do_read_nfs_sync(%p)\n", page);
+
+	page->locked = 1;
+	page->error = 0;
+
+	do {
+		if (count < rsize)
+			rsize = count;
+		result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), 
+			pos, rsize, buf, &fattr);
+		dprintk("nfs_proc_read(%s, (%x,%lx), %ld, %d, %p) = %d\n",
+				NFS_SERVER(inode)->hostname,
+				inode->i_dev, inode->i_ino,
+				pos, rsize, buf, result);
+		if (result < 0)
+			break;
+		refresh = 1;
+		count -= result;
+		pos += result;
+		buf += result;
+		if (result < rsize)
+			break;
+	} while (count);
+
+	memset(buf, 0, count);
+	if (refresh) {
+		nfs_refresh_inode(inode, &fattr);
+		result = 0;
+		page->uptodate = 1;
+	}
+	page->locked = 0;
+	wake_up(&page->wait);
+	return result;
+}
+
+/*
+ * This is the callback from nfsiod telling us whether a reply was
+ * received or some error occurred (timeout or socket shutdown).
+ */
+static void
+nfs_read_cb(int result, struct nfsiod_req *req)
+{
+	struct page	*page = (struct page *) req->rq_cdata;
+	static int	succ = 0, fail = 0;
+
+	dprintk("BIO: received callback for page %p, result %d\n",
+			page, result);
+
+	if (result >= 0
+	 && (result = nfs_proc_read_reply(&req->rq_rpcreq)) >= 0) {
+		succ++;
+		page->uptodate = 1;
+	} else {
+		fail++;
+		printk("BIO: %d successful reads, %d failures\n", succ, fail);
+		page->error = 1;
+	}
+	page->locked = 0;
+	wake_up(&page->wait);
+	free_page(page_address(page));
+}
+
+static inline int
+do_read_nfs_async(struct inode *inode, struct page *page)
+{
+	struct nfsiod_req *req;
+	int		result = -1;	/* totally arbitrary */
+
+	dprintk("NFS: do_read_nfs_async(%p)\n", page);
+
+	page->locked = 1;
+	page->error = 0;
+
+	if (!(req = nfsiod_reserve(NFS_SERVER(inode), nfs_read_cb)))
+		goto done;
+	result = nfs_proc_read_request(&req->rq_rpcreq,
+			NFS_SERVER(inode), NFS_FH(inode),
+			page->offset, PAGE_SIZE, 
+			(__u32 *) page_address(page));
+	if (result >= 0) {
+		req->rq_cdata = page;
+		page->count++;
+		result = nfsiod_enqueue(req);
+		if (result >= 0)
+			dprintk("NFS: enqueued async READ request.\n");
+	}
+	if (result < 0) {
+		dprintk("NFS: deferring async READ request.\n");
+		nfsiod_release(req);
+		page->locked = 0;
+		wake_up(&page->wait);
+	}
+
+done:
+	return result < 0? result : 0;
+}
+
+int
+nfs_readpage(struct inode *inode, struct page *page)
+{
+	unsigned long	address;
+	int		error = -1;
+
+	dprintk("NFS: nfs_readpage %08lx\n", page_address(page));
+	address = page_address(page);
+	page->count++;
+	if (!page->error && NFS_SERVER(inode)->rsize >= PAGE_SIZE)
+		error = do_read_nfs_async(inode, page);
+	if (error < 0)		/* couldn't enqueue */
+		error = do_read_nfs_sync(inode, page);
+	free_page(address);
+	return error;
+}
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/cache.c linux/fs/nfs/cache.c
--- v1.3.81/linux/fs/nfs/cache.c	Thu Aug 31 08:09:06 1995
+++ linux/fs/nfs/cache.c	Thu Jan  1 02:00:00 1970
@@ -1,64 +0,0 @@
-
-void nfs_bl_cache_invalidate(nfs_cache *nh)
-{
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	if(nh->inuse)
-		nh->dead=1;
-	else
-	{
-		kfree_s(nh->data);
-		nh->data=NULL;
-		nh->free=1;
-	}
-	restore_flags(flags);
-}
-
-void nfs_bl_cache_revalidate(nfs_cache *nh, struct fattr fa)
-{
-	nh->fattr=fattr;
-	nh->time=jiffies;
-}
-
-/*
- *	Find a block in the cache. We know the cache is block sized in block
- *	aligned space.
- */
- 
-nfs_cache *nfs_cache_find(struct inode *inode, off_t pos)
-{
-	nfs_cache *nh=&nfs_cache_slot[0];
-	nfs_cache *ffree=NULL;
-	struct nfs_fattr fattr;
-	int ct=0;
-	while(ct<NH_CACHE_SIZE)
-	{
-		if(nh->inode_num==inode->i_no && !nh->dead&&!nh->free&&nh->file_pos==pos)
-		{
-			if(abs(jiffies-nh->time)<EXPIRE_CACHE)
-				return nh;
-			/*
-			 *	Revalidate
-			 */
-			
-			if(nfs_proc_getattr(NFS_SERVER(inode), NFS_FH(inode), &fattr))
-			{
-				nfs_bl_cache_invalidate(nh);
-				continue;	/* get attr failed */
-			}
-			if(nh->fattr.modified!=fattr.modified)
-			{
-				nfs_bl_cache_invalidate(nh);
-				continue;	/* cache is out of date */
-			}
-			nfs_refresh_inode(inode, fattr);
-			nh->fattr=fattr;
-			nfs_bl_cache_revalidate(nh);
-			return nh;
-		}
-		if(nh->free)
-			ffree=nh;
-	}
-	return ffree;
-}	
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/file.c linux/fs/nfs/file.c
--- v1.3.81/linux/fs/nfs/file.c	Fri Mar  8 16:28:42 1996
+++ linux/fs/nfs/file.c	Sat Mar 30 23:57:56 1996
@@ -33,7 +33,6 @@
 static int nfs_file_read(struct inode *, struct file *, char *, int);
 static int nfs_file_write(struct inode *, struct file *, const char *, int);
 static int nfs_fsync(struct inode *, struct file *);
-static int nfs_readpage(struct inode * inode, struct page * page);
 
 static struct file_operations nfs_file_operations = {
 	NULL,			/* lseek - default */
@@ -101,53 +100,6 @@
 static int nfs_fsync(struct inode *inode, struct file *file)
 {
 	return 0;
-}
-
-static inline int do_read_nfs(struct inode * inode, struct page * page,
-	char * buf, unsigned long pos)
-{
-	int result, refresh = 0;
-	int count = PAGE_SIZE;
-	int rsize = NFS_SERVER(inode)->rsize;
-	struct nfs_fattr fattr;
-
-	page->locked = 1;
-	do {
-		if (count < rsize)
-			rsize = count;
-		result = nfs_proc_read(NFS_SERVER(inode), NFS_FH(inode), 
-			pos, rsize, buf, &fattr);
-		if (result < 0)
-			break;
-		refresh = 1;
-		count -= result;
-		pos += result;
-		buf += result;
-		if (result < rsize)
-			break;
-	} while (count);
-
-	memset(buf, 0, count);
-	if (refresh) {
-		nfs_refresh_inode(inode, &fattr);
-		result = 0;
-		page->uptodate = 1;
-	}
-	page->locked = 0;
-	wake_up(&page->wait);
-	return result;
-}
-
-static int nfs_readpage(struct inode * inode, struct page * page)
-{
-	int error;
-	unsigned long address;
-
-	address = page_address(page);
-	page->count++;
-	error = do_read_nfs(inode, page, (char *) address, page->offset);
-	free_page(address);
-	return error;
 }
 
 static int nfs_file_write(struct inode *inode, struct file *file, const char *buf,
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/inode.c linux/fs/nfs/inode.c
--- v1.3.81/linux/fs/nfs/inode.c	Sun Mar 24 20:07:00 1996
+++ linux/fs/nfs/inode.c	Tue Apr  2 07:26:11 1996
@@ -17,16 +17,22 @@
 
 #include <linux/sched.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfsiod.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/smp.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
 
+/* This is for kernel_thread */
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+
 extern int close_fp(struct file *filp);
 
 static int nfs_notify_change(struct inode *, struct iattr *);
@@ -307,8 +313,39 @@
 	nfs_read_super, "nfs", 0, NULL
 };
 
+/*
+ * Start up an nfsiod process. This is an awful hack, because when running
+ * as a module, we will keep insmod's memory. Besides, the current->comm
+ * hack won't work in this case
+ * The best would be to have a syscall for nfs client control that (among
+ * other things) forks biod's.
+ * Alternatively, we might want to have the idle task spawn biod's on demand.
+ */
+static int run_nfsiod(void *dummy)
+{
+	int	ret;
+
+#ifdef __SMP__
+	lock_kernel();
+	syscall_count++;
+#endif
+
+	MOD_INC_USE_COUNT;
+	current->session = 1;
+	current->pgrp = 1;
+	sprintf(current->comm, "nfsiod");
+	ret = nfsiod();
+	MOD_DEC_USE_COUNT;
+	return ret;
+}
+
 int init_nfs_fs(void)
 {
+	/* Fork four biod's */
+	kernel_thread(run_nfsiod, NULL, 0);
+	kernel_thread(run_nfsiod, NULL, 0);
+	kernel_thread(run_nfsiod, NULL, 0);
+	kernel_thread(run_nfsiod, NULL, 0);
         return register_filesystem(&nfs_fs_type);
 }
 
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/nfsiod.c linux/fs/nfs/nfsiod.c
--- v1.3.81/linux/fs/nfs/nfsiod.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/nfs/nfsiod.c	Sat Mar 30 23:54:23 1996
@@ -0,0 +1,128 @@
+/*
+ * linux/fs/nfs/nfsiod.c
+ *
+ * Async NFS RPC call support.
+ *
+ * When a process wants to place an asynchronous RPC call, it reserves
+ * an nfsiod slot, fills in all necessary fields including the callback
+ * handler field, and enqueues the request.
+ *
+ * This will wake up nfsiod, which calls nfs_rpc_doio to collect the
+ * reply. It then dispatches the result to the caller via the callback
+ * function, including result value and request pointer. It then re-inserts
+ * itself into the free list.
+ *
+ * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
+ */
+
+#include <linux/sched.h>
+#include <linux/nfs_fs.h>
+#include <linux/errno.h>
+#include <linux/rpcsock.h>
+#include <linux/nfsiod.h>
+
+static struct nfsiod_req *	free_list = NULL;
+static int			active = 0;
+
+#undef DEBUG_NFSIOD
+#ifdef DEBUG_NFSIOD
+#define dprintk(args...)	printk(## args)
+#else
+#define dprintk(args...)	/* nothing */
+#endif
+
+
+/*
+ * Reserve an nfsiod slot and initialize the request struct
+ */
+struct nfsiod_req *
+nfsiod_reserve(struct nfs_server *server, nfsiod_done_fn_t callback)
+{
+	struct nfsiod_req	*req;
+
+	if (!(req = free_list)) {
+		dprintk("BIO: nfsiod_reserve: no free nfsiods\n");
+		return NULL;
+	}
+	free_list = req->rq_next;
+	memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
+
+	if (rpc_reserve(server->rsock, &req->rq_rpcreq, 1) < 0) {
+		dprintk("BIO: nfsiod_reserve failed to reserve RPC slot\n");
+		req->rq_next = free_list;
+		free_list = req;
+		return NULL;
+	}
+
+	req->rq_server = server;
+	req->rq_callback = callback;
+
+	return req;
+}
+
+void
+nfsiod_release(struct nfsiod_req *req)
+{
+	dprintk("BIO: nfsiod_release called\n");
+	rpc_release(req->rq_server->rsock, &req->rq_rpcreq);
+	memset(&req->rq_rpcreq, 0, sizeof(struct rpc_ioreq));
+	req->rq_next = free_list;
+	free_list = req;
+}
+
+/*
+ * Transmit a request and put it on nfsiod's list of pending requests.
+ */
+int
+nfsiod_enqueue(struct nfsiod_req *req)
+{
+	int	result;
+
+	dprintk("BIO: enqueuing request %p\n", &req->rq_rpcreq);
+	result = rpc_transmit(req->rq_server->rsock, &req->rq_rpcreq);
+	if (result < 0) {
+		dprintk("BIO: rpc_transmit returned %d\n", result);
+	} else {
+		dprintk("BIO: waking up nfsiod (%p)\n", req->rq_wait);
+		wake_up(&req->rq_wait);
+		schedule();
+	}
+	return result;
+}
+
+/*
+ * This is the main nfsiod loop.
+ */
+int
+nfsiod(void)
+{
+	struct nfsiod_req	request, *req = &request;
+	int			result;
+
+	dprintk("BIO: nfsiod %d starting\n", current->pid);
+	while (1) {
+		/* Insert request into free list */
+		memset(req, 0, sizeof(*req));
+		req->rq_next = free_list;
+		free_list = req;
+
+		/* Wait until user enqueues request */
+		dprintk("BIO: before: now %d nfsiod's active\n", active);
+		dprintk("BIO: nfsiod %d waiting\n", current->pid);
+		interruptible_sleep_on(&req->rq_wait);
+
+		if (current->signal & ~current->blocked)
+			break;
+		if (!req->rq_rpcreq.rq_slot)
+			continue;
+		dprintk("BIO: nfsiod %d woken up; calling nfs_rpc_doio.\n",
+				current->pid);
+		active++;
+		dprintk("BIO: before: now %d nfsiod's active\n", active);
+		result = nfs_rpc_doio(req->rq_server, &req->rq_rpcreq, 1);
+		req->rq_callback(result, req);
+		active--;
+	}
+
+	return 0;
+}
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.81/linux/fs/nfs/nfsroot.c	Thu Mar 28 17:34:34 1996
+++ linux/fs/nfs/nfsroot.c	Mon Apr  1 10:46:29 1996
@@ -38,6 +38,11 @@
  *				Linus so that I don' always have to cleanup
  *				_afterwards_ - thanks)
  *	Gero Kuhlmann	:	Last changes of Martin Mares undone.
+ *	Gero Kuhlmann	: 	RARP replies are tested for specified server
+ *				again. However, it's now possible to have
+ *				different RARP and NFS servers.
+ *	Gero Kuhlmann	:	"0.0.0.0" addresses from command line are
+ *				now mapped to INADDR_NONE.
  *
  */
 
@@ -116,6 +121,7 @@
 static int rarp_flag;			/* User said: Use RARP! */
 static int bootp_dev_count = 0;		/* Number of devices allowing BOOTP */
 static int rarp_dev_count = 0;		/* Number of devices allowing RARP */
+static struct sockaddr_in rarp_serv;	/* IP address of RARP server */
 
 #if defined(CONFIG_RNFS_BOOTP) || defined(CONFIG_RNFS_RARP)
 #define CONFIG_RNFS_DYNAMIC		/* Enable dynamic IP config */
@@ -312,8 +318,8 @@
 	}
 	/* Discard packets which are not from specified server. */
 	if (rarp_flag && !bootp_flag &&
-	    server.sin_addr.s_addr != INADDR_NONE &&
-	    server.sin_addr.s_addr != sip) {
+	    rarp_serv.sin_addr.s_addr != INADDR_NONE &&
+	    rarp_serv.sin_addr.s_addr != sip) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
 	}
@@ -1167,9 +1173,9 @@
 	int num = 0;
 
 	/* Clear all addresses and strings */
-	myaddr.sin_family = server.sin_family =
+	myaddr.sin_family = server.sin_family = rarp_serv.sin_family =
 	    gateway.sin_family = netmask.sin_family = AF_INET;
-	myaddr.sin_addr.s_addr = server.sin_addr.s_addr =
+	myaddr.sin_addr.s_addr = server.sin_addr.s_addr = rarp_serv.sin_addr.s_addr =
 	    gateway.sin_addr.s_addr = netmask.sin_addr.s_addr = INADDR_NONE;
 	system_utsname.nodename[0] = '\0';
 	system_utsname.domainname[0] = '\0';
@@ -1199,16 +1205,20 @@
 #endif
 			switch (num) {
 			case 0:
-				myaddr.sin_addr.s_addr = in_aton(ip);
+				if ((myaddr.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+					myaddr.sin_addr.s_addr = INADDR_NONE;
 				break;
 			case 1:
-				server.sin_addr.s_addr = in_aton(ip);
+				if ((server.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+					server.sin_addr.s_addr = INADDR_NONE;
 				break;
 			case 2:
-				gateway.sin_addr.s_addr = in_aton(ip);
+				if ((gateway.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+					gateway.sin_addr.s_addr = INADDR_NONE;
 				break;
 			case 3:
-				netmask.sin_addr.s_addr = in_aton(ip);
+				if ((netmask.sin_addr.s_addr = in_aton(ip)) == INADDR_ANY)
+					netmask.sin_addr.s_addr = INADDR_NONE;
 				break;
 			case 4:
 				if ((dp = strchr(ip, '.'))) {
@@ -1238,6 +1248,7 @@
 		ip = cp;
 		num++;
 	}
+	rarp_serv = server;
 }
 
 
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/proc.c linux/fs/nfs/proc.c
--- v1.3.81/linux/fs/nfs/proc.c	Tue Feb 20 09:49:41 1996
+++ linux/fs/nfs/proc.c	Sun Mar 31 00:09:49 1996
@@ -29,7 +29,7 @@
  * filesystem and type 'ls xyzzy' to turn on debugging.
  */
 
-#if 0
+#if 1
 #define NFS_PROC_DEBUG
 #endif
 
@@ -416,6 +416,80 @@
 	return status;
 }
 
+int
+nfs_proc_read_request(struct rpc_ioreq *req, struct nfs_server *server,
+			struct nfs_fh *fh, unsigned long offset,
+			unsigned long count, __u32 *buf)
+{
+	__u32	*p, *p0;
+	int	len;
+
+	PRINTK("NFS reqst read %ld @ %ld\n", count, offset);
+	if (!(p0 = nfs_rpc_alloc(NFS_SLACK_SPACE)))
+		return -EIO;
+
+	p = nfs_rpc_header(p0, NFSPROC_READ, 0);
+	p = xdr_encode_fhandle(p, fh);
+	*p++ = htonl(offset);
+	*p++ = htonl(count);
+	*p++ = htonl(count); /* traditional, could be any value */
+	req->rq_svec[0].iov_base = p0;
+	req->rq_svec[0].iov_len  = (p - p0) << 2;
+	req->rq_slen = (p - p0) << 2;
+	req->rq_snr = 1;
+
+	len = (6 + 1 + 17 + 1);		/* standard READ reply header */
+	req->rq_rvec[0].iov_base = p0;
+	req->rq_rvec[0].iov_len  = len << 2;
+	req->rq_rvec[1].iov_base = buf;
+	req->rq_rvec[1].iov_len  = count;
+	req->rq_rvec[2].iov_base = p0 + len;		/* spill buffer */
+	req->rq_rvec[2].iov_len  = (NFS_SLACK_SPACE - len) << 2;
+	req->rq_rlen = count + NFS_SLACK_SPACE;
+	req->rq_rnr = 3;
+
+	req->rq_addr = &server->toaddr;
+	req->rq_alen = sizeof(server->toaddr);
+
+	return 0;
+}
+
+int
+nfs_proc_read_reply(struct rpc_ioreq *req)
+{
+	struct nfs_fattr fattr;
+	int		status;
+	__u32		*p0, *p;
+	int		count;
+
+	p0 = (__u32 *) req->rq_rvec[0].iov_base;
+
+	if (!(p = nfs_rpc_verify(p0))) {
+		status = -errno_NFSERR_IO;
+	} else if ((status = ntohl(*p++)) == NFS_OK) {
+		p = xdr_decode_fattr(p, &fattr);
+		count = ntohl(*p++);
+		if (p != req->rq_rvec[2].iov_base) {
+			/* unexpected RPC reply header size. punt.
+			 * fixme: move iovec contents to align data
+			 * on page boundary and adjust RPC header size
+			 * guess. */
+			status = -errno_NFSERR_IO;
+			PRINTK("NFS reply read odd header size %d\n",
+					(p - p0) << 2);
+		} else {
+			status = count;
+			PRINTK("NFS reply read %d\n", count);
+		}
+	}
+	else {
+		PRINTK("NFS reply read failed = %d\n", status);
+		status = -nfs_stat_to_errno(status);
+	}
+	nfs_rpc_free(p0);
+	return status;
+}
+
 int nfs_proc_write(struct inode * inode, int offset,
 		   int count, const char *data, struct nfs_fattr *fattr)
 {
@@ -870,7 +944,7 @@
 
 	p++;
 	if ((n = ntohl(*p++)) != RPC_REPLY) {
-		printk("nfs_rpc_verify: not an RPC reply: %d\n", n);
+		printk("nfs_rpc_verify: not an RPC reply: %x\n", n);
 		return NULL;
 	}
 	if ((n = ntohl(*p++)) != RPC_MSG_ACCEPTED) {
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c
--- v1.3.81/linux/fs/nfs/rpcsock.c	Tue Dec 26 06:03:00 1995
+++ linux/fs/nfs/rpcsock.c	Sun Mar 31 00:34:56 1996
@@ -6,17 +6,27 @@
  *  this:
  *
  *  -	When a process places a call, it allocates a request slot if
- *	one is available. Otherwise, it sleeps on the backlog queue.
- *  -	The first process on the receive queue waits for the next RPC reply,
+ *	one is available. Otherwise, it sleeps on the backlog queue
+ *	(rpc_reserve).
+ *  -	Then, the message is transmitted via rpc_send (exported by name of
+ *	rpc_transmit).
+ *  -	Finally, the process waits for the call to complete (rpc_doio):
+ *	The first process on the receive queue waits for the next RPC packet,
  *	and peeks at the XID. If it finds a matching request, it receives
  *	the datagram on behalf of that process and wakes it up. Otherwise,
  *	the datagram is discarded.
  *  -	If the process having received the datagram was the first one on
  *	the receive queue, it wakes up the next one to listen for replies.
- *  -	It then removes itself from the request queue. If there are more
- *	callers waiting on the backlog queue, they are woken up, too.
+ *  -	It then removes itself from the request queue (rpc_release).
+ *	If there are more callers waiting on the backlog queue, they are
+ *	woken up, too.
  *
- *  Copyright (C) 1995, Olaf Kirch <okir@monad.swb.de>
+ * Mar 1996:
+ *  -	Split up large functions into smaller chunks as per Linus' coding
+ *	style. Found an interesting bug this way, too.
+ *  -	Added entry points for nfsiod.
+ *
+ *  Copyright (C) 1995, 1996, Olaf Kirch <okir@monad.swb.de>
  */
 
 #include <linux/types.h>
@@ -31,70 +41,115 @@
 #include <linux/mm.h>
 #include <linux/rpcsock.h>
 
+#include <linux/udp.h>
+#include <net/sock.h>
+
 #include <asm/segment.h>
 
 #define msleep(sec)	{ current->timeout = sec * HZ / 1000; \
 			  current->state = TASK_INTERRUPTIBLE; \
 			  schedule(); \
 			}
-			
-#ifdef DEBUG_NFS			
-#define dprintk(x)		printk(x)
+
+#undef DEBUG_RPC
+#ifdef DEBUG_RPC			
+#define dprintk(args...)	printk(## args)
 #else
-#define	dprintk(x)
+#define	dprintk(args...)
 #endif
 
+
+/*
+ * Insert new request into wait list. We make sure list is sorted by
+ * increasing timeout value.
+ */
 static inline void
 rpc_insque(struct rpc_sock *rsock, struct rpc_wait *slot)
 {
-	struct rpc_wait	*tmp;
+	struct rpc_wait	*next = rsock->pending;
 
-	if ((tmp = rsock->tail) != NULL) {
-		tmp->next = slot;
-	} else {
-		rsock->head = slot;
-	}
-	rsock->tail = slot;
-	slot->prev = tmp;
-	slot->next = NULL;
-	dprintk(("RPC: inserted %08lx into queue.\n", (long)slot));
-	dprintk(("RPC: head = %08lx, tail = %08lx.\n",
-			(long) rsock->head, (long) rsock->tail));
+	slot->w_next = next;
+	slot->w_prev = NULL;
+	if (next)
+		next->w_prev = slot;
+	rsock->pending = slot;
+	slot->w_queued = 1;
+
+	dprintk("RPC: inserted %p into queue\n", slot);
 }
 
+/*
+ * Remove request from request queue
+ */
 static inline void
 rpc_remque(struct rpc_sock *rsock, struct rpc_wait *slot)
 {
-	struct rpc_wait	*prev = slot->prev,
-			*next = slot->next;
+	struct rpc_wait	*prev = slot->w_prev,
+			*next = slot->w_next;
 
 	if (prev != NULL)
-		prev->next = next;
+		prev->w_next = next;
 	else
-		rsock->head = next;
+		rsock->pending = next;
 	if (next != NULL)
-		next->prev = prev;
-	else
-		rsock->tail = prev;
-	dprintk(("RPC: removed %08lx from queue.\n", (long)slot));
-	dprintk(("RPC: head = %08lx, tail = %08lx.\n",
-			(long) rsock->head, (long) rsock->tail));
+		next->w_prev = prev;
+
+	slot->w_queued = 0;
+	dprintk("RPC: removed %p from queue, head now %p.\n",
+			slot, rsock->pending);
 }
 
+/*
+ * Write data to socket.
+ */
 static inline int
-rpc_sendmsg(struct rpc_sock *rsock, struct msghdr *msg, int len)
+rpc_sendmsg(struct rpc_sock *rsock, struct iovec *iov, int nr, int len,
+				struct sockaddr *sap, int salen)
 {
 	struct socket	*sock = rsock->sock;
+	struct msghdr	msg;
 	unsigned long	oldfs;
 	int		result;
 
-	dprintk(("RPC: sending %d bytes (buf %p)\n", len, msg->msg_iov[0].iov_base));
+	msg.msg_iov	= iov;
+	msg.msg_iovlen	= nr;
+	msg.msg_name	= sap;
+	msg.msg_namelen = salen;
+	msg.msg_accrights = NULL;
+
+	oldfs = get_fs();
+	set_fs(get_ds());
+	result = sock->ops->sendmsg(sock, &msg, len, 0, 0);
+	set_fs(oldfs);
+
+	dprintk("RPC: rpc_sendmsg(iov %p, len %d) = %d\n", iov, len, result);
+	return result;
+}
+/*
+ * Read data from socket
+ */
+static inline int
+rpc_recvmsg(struct rpc_sock *rsock, struct iovec *iov,
+			int nr, int len, int flags)
+{
+	struct socket	*sock = rsock->sock;
+	struct sockaddr	sa;
+	struct msghdr	msg;
+	unsigned long	oldfs;
+	int		result, alen;
+
+	msg.msg_iov	= iov;
+	msg.msg_iovlen	= nr;
+	msg.msg_name	= &sa;
+	msg.msg_namelen = sizeof(sa);
+	msg.msg_accrights = NULL;
+
 	oldfs = get_fs();
 	set_fs(get_ds());
-	result = sock->ops->sendmsg(sock, msg, len, 0, 0);
+	result = sock->ops->recvmsg(sock, &msg, len, 1, flags, &alen);
 	set_fs(oldfs);
-	dprintk(("RPC: result = %d\n", result));
 
+	dprintk("RPC: rpc_recvmsg(iov %p, len %d) = %d\n", iov, len, result);
 	return result;
 }
 
@@ -109,7 +164,7 @@
 	struct file	*file = rsock->file;
 	select_table	wait_table;
 
-	dprintk(("RPC: selecting on socket...\n"));
+	dprintk("RPC: selecting on socket...\n");
 	wait_table.nr = 0;
 	wait_table.entry = &entry;
 	current->state = TASK_INTERRUPTIBLE;
@@ -125,232 +180,347 @@
 	} else if (wait_table.nr)
 		remove_wait_queue(entry.wait_address, &entry.wait);
 	current->state = TASK_RUNNING;
-	dprintk(("RPC: ...Okay, there appears to be some data.\n"));
+	dprintk("RPC: ...Okay, there appears to be some data.\n");
 	return 0;
 }
 
-static inline int
-rpc_recvmsg(struct rpc_sock *rsock, struct msghdr *msg, int len,int flags)
+/*
+ * Reserve an RPC call slot. nocwait determines whether we wait in case
+ * of congestion or not.
+ */
+int
+rpc_reserve(struct rpc_sock *rsock, struct rpc_ioreq *req, int nocwait)
 {
-	struct socket	*sock = rsock->sock;
-	struct sockaddr	sa;
-	int		alen = sizeof(sa);
-	unsigned long	oldfs;
-	int		result;
+	struct rpc_wait	*slot;
 
-	dprintk(("RPC: receiving %d bytes max (buf %p)\n", len, msg->msg_iov[0].iov_base));
-	oldfs = get_fs();
-	set_fs(get_ds());
-	result = sock->ops->recvmsg(sock, msg, len, 1, flags, &alen);
-	set_fs(oldfs);
-	dprintk(("RPC: result = %d\n", result));
+	req->rq_slot = NULL;
 
-#if 0
-	if (alen != salen || memcmp(&sa, sap, alen)) {
-		dprintk(("RPC: reply address mismatch... rejected.\n"));
-		result = -EAGAIN;
+	while (!(slot = rsock->free) || rsock->cong >= rsock->cwnd) {
+		if (nocwait) {
+			current->timeout = 0;
+			return -ENOBUFS;
+		}
+		dprintk("RPC: rpc_reserve waiting on backlog\n");
+		interruptible_sleep_on(&rsock->backlog);
+		if (current->timeout == 0)
+			return -ETIMEDOUT;
+		if (current->signal & ~current->blocked)
+			return -ERESTARTSYS;
+		if (rsock->shutdown)
+			return -EIO;
 	}
-#endif
 
-	return result;
+	rsock->free = slot->w_next;
+	rsock->cong += RPC_CWNDSCALE;	/* bump congestion value */
+
+	slot->w_queued = 0;
+	slot->w_gotit = 0;
+	slot->w_req = req;
+
+	dprintk("RPC: reserved slot %p\n", slot);
+	req->rq_slot = slot;
+	return 0;
+}
+
+/*
+ * Release an RPC call slot
+ */
+void
+rpc_release(struct rpc_sock *rsock, struct rpc_ioreq *req)
+{
+	struct rpc_wait	*slot = req->rq_slot;
+
+	if (slot != NULL) {
+		dprintk("RPC: release slot %p\n", slot);
+
+		/* Wake up the next receiver */
+		if (slot == rsock->pending && slot->w_next != NULL)
+			wake_up(&slot->w_next->w_wait);
+
+		/* remove slot from queue of pending */
+		if (slot->w_queued)
+			rpc_remque(rsock, slot);
+		slot->w_next = rsock->free;
+		rsock->free = slot;
+
+		/* decrease congestion value */
+		rsock->cong -= RPC_CWNDSCALE;
+		if (rsock->cong < rsock->cwnd && rsock->backlog)
+			wake_up(&rsock->backlog);
+		if (rsock->shutdown)
+			wake_up(&rsock->shutwait);
+
+		req->rq_slot = NULL;
+	}
+}
+
+/*
+ * Adjust RPC congestion window
+ */
+static void
+rpc_cwnd_adjust(struct rpc_sock *rsock, int timeout)
+{
+	unsigned long	cwnd = rsock->cwnd;
+
+	if (!timeout) {
+		if (rsock->cong >= cwnd) {
+			/* The (cwnd >> 1) term makes sure
+			 * the result gets rounded properly. */
+			cwnd += (RPC_CWNDSCALE * RPC_CWNDSCALE +
+					(cwnd >> 1)) / cwnd;
+			if (cwnd > RPC_MAXCWND)
+				cwnd = RPC_MAXCWND;
+		}
+	} else {
+		if ((cwnd >>= 1) < RPC_CWNDSCALE)
+			cwnd = RPC_CWNDSCALE;
+		dprintk("RPC: cwnd decrease %08lx\n", cwnd);
+	}
+	dprintk("RPC: cong %08lx, cwnd was %08lx, now %08lx\n",
+			rsock->cong, rsock->cwnd, cwnd);
+
+	rsock->cwnd = cwnd;
+}
+
+static inline void
+rpc_send_check(char *where, u32 *ptr)
+{
+	if (ptr[1] != 0 || ptr[2] != htonl(2) || ptr[3] != htonl(100003)) {
+		printk("RPC: %s sending evil packet:\n"
+		       "     %08x %08x %08x %08x %08x %08x %08x %08x\n",
+		       where,
+		       ptr[0], ptr[1], ptr[2], ptr[3],
+		       ptr[4], ptr[5], ptr[6], ptr[7]);
+	}
 }
 
 /*
  * Place the actual RPC call.
+ * We have to copy the iovec because sendmsg fiddles with its contents.
  */
-static int
-rpc_call_one(struct rpc_sock *rsock, struct rpc_wait *slot,
-		struct sockaddr *sap, int salen,
-		const int *sndbuf, int slen, int *rcvbuf, int rlen)
+static inline int
+rpc_send(struct rpc_sock *rsock, struct rpc_wait *slot)
 {
-	struct rpc_wait	*rovr = NULL;
-	int		result;
+	struct rpc_ioreq *req = slot->w_req;
+	struct iovec	iov[MAX_IOVEC];
+
+	if (rsock->shutdown)
+		return -EIO;
+
+	memcpy(iov, req->rq_svec, req->rq_snr * sizeof(iov[0]));
+	slot->w_xid = *(u32 *)(iov[0].iov_base);
+	if (!slot->w_queued)
+		rpc_insque(rsock, slot);
+
+	dprintk("rpc_send(%p, %x)\n", slot, slot->w_xid);
+	rpc_send_check("rpc_send", (u32 *) req->rq_svec[0].iov_base);
+	return rpc_sendmsg(rsock, iov, req->rq_snr, req->rq_slen,
+				req->rq_addr, req->rq_alen);
+}
+
+/*
+ * This is the same as rpc_send but for the functions exported to nfsiod
+ */
+int
+rpc_transmit(struct rpc_sock *rsock, struct rpc_ioreq *req)
+{
+	rpc_send_check("rpc_transmit", (u32 *) req->rq_svec[0].iov_base);
+	return rpc_send(rsock, req->rq_slot);
+}
+
+/*
+ * Receive and dispatch a single reply
+ */
+static inline int
+rpc_grok(struct rpc_sock *rsock)
+{
+	struct rpc_wait	*rovr;
+	struct rpc_ioreq *req;
+	struct iovec	iov[MAX_IOVEC];
 	u32		xid;
-	int		safe;
-	struct msghdr   msg;
-	struct iovec	iov;
-	
-	msg.msg_iov	=	&iov;
-	msg.msg_iovlen	=	1;
-	msg.msg_name	=	(void *)sap;
-	msg.msg_namelen	=	salen;
-	msg.msg_accrights =	NULL;
-	iov.iov_base	=	(void *)sndbuf;
-	iov.iov_len	=	slen;
-
-	dprintk(("RPC: placing one call, rsock = %08lx, slot = %08lx, "
-		"sap = %08lx, salen = %d, "
-		"sndbuf = %08lx, slen = %d, rcvbuf = %08lx, rlen = %d\n",
-		(long) rsock, (long) slot, (long) sap, 
-		salen, (long) sndbuf, slen, (long) rcvbuf, rlen));
-
-	result = rpc_sendmsg(rsock, &msg, slen);
-	if (result < 0)
-		return result;
+	int		safe, result;
 
-	do {
-		/* We are not the receiver. Wait on the side lines. */
-		if (rsock->head != slot) {
-			interruptible_sleep_on(&slot->wait);
-			if (slot->gotit)
-				break;
-			if (current->timeout != 0)
-				continue;
-			if (rsock->shutdown) {
-				printk("RPC: aborting call due to shutdown.\n");
-				return -EIO;
-			}
-			return -ETIMEDOUT;
+	iov[0].iov_base = (void *) &xid;
+	iov[0].iov_len  = sizeof(xid);
+	result = rpc_recvmsg(rsock, iov, 1, sizeof(xid), MSG_PEEK);
+
+	if (result < 0) {
+		switch (-result) {
+		case EAGAIN: case ECONNREFUSED:
+			return 0;
+		case ERESTARTSYS:
+			return result;
+		default:
+			dprintk("rpc_grok: recv error = %d\n", result);
 		}
-		
-		/* wait for data to arrive */
-		result = rpc_select(rsock);
-		if (result < 0) {
-			dprintk(("RPC: select error = %d\n", result));
+	}
+	if (result < 4) {
+		printk(KERN_WARNING "RPC: impossible RPC reply size %d\n",
+						result);
+		return 0;
+	}
+
+	dprintk("RPC: rpc_grok: got xid %08lx\n", (unsigned long) xid);
+
+	/* Look for the caller */
+	safe = 0;
+	for (rovr = rsock->pending; rovr; rovr = rovr->w_next) {
+		if (rovr->w_xid == xid)
+			break;
+		if (safe++ > RPC_MAXREQS) {
+			printk(KERN_WARNING "RPC: loop in request Q!!\n");
+			rovr = NULL;
 			break;
 		}
+	}
 
-		iov.iov_base=(void *)&xid;
-		iov.iov_len=sizeof(xid);
-		
-		result = rpc_recvmsg(rsock, &msg, sizeof(xid), MSG_PEEK);
-		if (result < 0) {
-			switch (-result) {
-			case EAGAIN: case ECONNREFUSED:
-				continue;
-			default:
-				dprintk(("rpc_call: recv error = %d\n", result));
-			case ERESTARTSYS:
-				return result;
-			}
-		}
+	if (!rovr || rovr->w_gotit) {
+		/* discard dgram */
+		dprintk("RPC: rpc_grok: %s.\n",
+			rovr? "duplicate reply" : "bad XID");
+		iov[0].iov_base = (void *) &xid;
+		iov[0].iov_len  = sizeof(xid);
+		rpc_recvmsg(rsock, iov, 1, sizeof(xid), 0);
+		return 0;
+	}
+	req = rovr->w_req;
 
-		/* Look for the caller */
-		safe = 0;
-		for (rovr = rsock->head; rovr; rovr = rovr->next) {
-			if (safe++ > NRREQS) {
-				printk("RPC: loop in request Q!!\n");
-				rovr = NULL;
-				break;
+	/* Now receive the reply... Copy the iovec first because of 
+	 * memcpy_fromiovec fiddling. */
+	memcpy(iov, req->rq_rvec, req->rq_rnr * sizeof(iov[0]));
+	result = rpc_recvmsg(rsock, iov, req->rq_rnr, req->rq_rlen, 0);
+	rovr->w_result = result;
+	rovr->w_gotit = 1;
+
+	/* ... and wake up the process */
+	wake_up(&rovr->w_wait);
+
+	return result;
+}
+
+/*
+ * Wait for the reply to our call.
+ */
+static int
+rpc_recv(struct rpc_sock *rsock, struct rpc_wait *slot)
+{
+	int	result;
+
+	do {
+		/* If we are not the receiver, wait on the sidelines */
+		dprintk("RPC: rpc_recv TP1\n");
+		while (rsock->pending != slot) {
+			if (!slot->w_gotit)
+				interruptible_sleep_on(&slot->w_wait);
+			if (slot->w_gotit) {
+				result = slot->w_result; /* quite important */
+				return result;
 			}
-			if (rovr->xid == xid)
-				break;
+			if (current->signal & ~current->blocked)
+				return -ERESTARTSYS;
+			if (rsock->shutdown)
+				return -EIO;
+			if (current->timeout == 0)
+				return -ETIMEDOUT;
 		}
 
-		if (!rovr || rovr->gotit) {
-			/* bad XID or duplicate reply, discard dgram */
-			dprintk(("RPC: bad XID or duplicate reply.\n"));
-			iov.iov_base=(void *)&xid;
-			iov.iov_len=sizeof(xid);
-			rpc_recvmsg(rsock, &msg, sizeof(xid),0);
-			continue;
+		/* Wait for data to arrive */
+		if ((result = rpc_select(rsock)) < 0) {
+			dprintk("RPC: select error = %d\n", result);
+			break;
 		}
-		rovr->gotit = 1;
 
-		/* Now receive the reply */
-		
-		iov.iov_base=rovr->buf;
-		iov.iov_len=rovr->len;
-		
-		result = rpc_recvmsg(rsock, &msg, rovr->len, 0);
-
-		/* If this is not for ourselves, wake up the caller */
-		if (rovr != slot)
-			wake_up(&rovr->wait);
-	} while (rovr != slot);
-
-	/* This is somewhat tricky. We rely on the fact that we are able to
-	 * remove ourselves from the queues before the next reader is scheduled,
-	 * otherwise it would find that we're still at the head of the queue
-	 * and go to sleep again.
-	 */
-	if (rsock->head == slot && slot->next != NULL)
-		wake_up(&slot->next->wait);
+		/* Receive and dispatch */
+		if ((result = rpc_grok(rsock)) < 0)
+			break;
+	} while (current->timeout && !slot->w_gotit);
 
-	return result;
+	return slot->w_gotit? result : -ETIMEDOUT;
 }
 
 /*
- * Generic RPC call routine. This handles retries and timeouts etc pp
+ * Generic RPC call routine. This handles retries and timeouts etc pp.
+ *
+ * If sent is non-null, it assumes the called has already sent out the
+ * message, so it won't need to do so unless a timeout occurs.
  */
 int
-rpc_call(struct rpc_sock *rsock, struct sockaddr *sap, int addrlen,
-		const int *sndbuf, int slen, int *rcvbuf, int rlen,
-		struct rpc_timeout *strategy, int flag)
-{
-	struct rpc_wait		*slot;
-	int			result, retries;
-	unsigned long		timeout;
+rpc_doio(struct rpc_sock *rsock, struct rpc_ioreq *req,
+			struct rpc_timeout *strategy, int sent)
+{
+	struct rpc_wait	*slot;
+	int		result, retries;
+	unsigned long	timeout;
 
-	timeout = strategy->init_timeout;
+	timeout = strategy->to_initval;
 	retries = 0;
-	slot = NULL;
+	slot = req->rq_slot;
 
 	do {
-		dprintk(("RPC call TP1\n"));
+		dprintk("RPC: rpc_doio: TP1 (req %p)\n", req);
 		current->timeout = jiffies + timeout;
 		if (slot == NULL) {
-			while ((slot = rsock->free) == NULL) {
-				if (!flag) {
-					current->timeout = 0;
-					return -ENOBUFS;
-				}
-				interruptible_sleep_on(&rsock->backlog);
-				if (current->timeout == 0) {
-					result = -ETIMEDOUT;
-					goto timedout;
-				}
-				if (rsock->shutdown) {
-					dprintk(("RPC: aborting call due to shutdown.\n"));
-					current->timeout = 0;
-					return -EIO;
-				}
-			}
-			dprintk(("RPC call TP2\n"));
-			slot->gotit = 0;
-			slot->xid = *(u32 *)sndbuf;
-			slot->buf = rcvbuf;
-			slot->len = rlen;
-			rsock->free = slot->next;
+			result = rpc_reserve(rsock, req, 0);
+			if (result == -ETIMEDOUT)
+				goto timedout;
+			if (result < 0)
+				break;
+			slot = req->rq_slot;
+			rpc_send_check("rpc_doio",
+				(u32 *) req->rq_svec[0].iov_base);
 			rpc_insque(rsock, slot);
 		}
 
-		dprintk(("RPC call TP3\n"));
-		result = rpc_call_one(rsock, slot, sap, addrlen,
-					sndbuf, slen, rcvbuf, rlen);
-		if (result != -ETIMEDOUT)
+		/* This check is for loopback NFS. Sometimes replies come
+		 * in before biod has called rpc_doio... */
+		if (slot->w_gotit) {
+			result = slot->w_result;
 			break;
+		}
+
+		dprintk("RPC: rpc_doio: TP2\n");
+		if (sent || (result = rpc_send(rsock, slot)) >= 0) {
+			result = rpc_recv(rsock, slot);
+			sent = 0;
+		}
+
+		if (result != -ETIMEDOUT) {
+			/* dprintk("RPC: rpc_recv returned %d\n", result); */
+			rpc_cwnd_adjust(rsock, 0);
+			break;
+		}
+
+		rpc_cwnd_adjust(rsock, 1);
 
 timedout:
-		dprintk(("RPC call TP4\n"));
-		dprintk(("RPC: rpc_call_one returned timeout.\n"));
-		if (strategy->exponential)
+		dprintk("RPC: rpc_recv returned timeout.\n");
+		if (strategy->to_exponential)
 			timeout <<= 1;
 		else
-			timeout += strategy->increment;
-		if (strategy->max_timeout && timeout >= strategy->max_timeout)
-			timeout = strategy->max_timeout;
-		if (strategy->retries && ++retries >= strategy->retries)
+			timeout += strategy->to_increment;
+		if (strategy->to_maxval && timeout >= strategy->to_maxval)
+			timeout = strategy->to_maxval;
+		if (strategy->to_retries && ++retries >= strategy->to_retries)
 			break;
 	} while (1);
 
-	dprintk(("RPC call TP5\n"));
+	dprintk("RPC: rpc_doio: TP3\n");
 	current->timeout = 0;
-	if (slot != NULL) {
-		dprintk(("RPC call TP6\n"));
-		rpc_remque(rsock, slot);
-		slot->next = rsock->free;
-		rsock->free = slot;
-
-		/* wake up tasks that haven't sent anything yet. (Waking
-		 * up the first one on the wait queue would be enough) */
-		if (rsock->backlog)
-			wake_up(&rsock->backlog);
-	}
+	return result;
+}
 
-	if (rsock->shutdown)
-		wake_up(&rsock->shutwait);
+/*
+ */
+int
+rpc_call(struct rpc_sock *rsock, struct rpc_ioreq *req,
+			struct rpc_timeout *strategy)
+{
+	int	result;
 
+	result = rpc_doio(rsock, req, strategy, 0);
+	if (req->rq_slot == NULL)
+		printk(KERN_WARNING "RPC: bad: rq_slot == NULL\n");
+	rpc_release(rsock, req);
 	return result;
 }
 
@@ -358,31 +528,35 @@
 rpc_makesock(struct file *file)
 {
 	struct rpc_sock	*rsock;
+	struct socket	*sock;
+	struct sock	*sk;
 	struct rpc_wait	*slot;
 	int		i;
 
-	dprintk(("RPC: make RPC socket...\n"));
+	dprintk("RPC: make RPC socket...\n");
+	sock = &file->f_inode->u.socket_i;
+	if (sock->type != SOCK_DGRAM || sock->ops->family != AF_INET) {
+		printk(KERN_WARNING "RPC: only UDP sockets supported\n");
+		return NULL;
+	}
+	sk = (struct sock *) sock->data;
+
 	if ((rsock = kmalloc(sizeof(struct rpc_sock), GFP_KERNEL)) == NULL)
 		return NULL;
 	memset(rsock, 0, sizeof(*rsock)); /* Nnnngh! */
 
-	rsock->sock = &file->f_inode->u.socket_i;
+	rsock->sock = sock;
+	rsock->inet = sk;
 	rsock->file = file;
+	rsock->cwnd = RPC_INITCWND;
 
+	dprintk("RPC: slots %p, %p, ...\n", rsock->waiting, rsock->waiting + 1);
 	rsock->free = rsock->waiting;
-	for (i = 0, slot = rsock->waiting; i < NRREQS-1; i++, slot++)
-		slot->next = slot + 1;
-	slot->next = NULL;
-
-	/* --- taken care of by memset above ---
-	rsock->backlog = NULL;
-	rsock->head = rsock->tail = NULL;
-
-	rsock->shutwait = NULL;
-	rsock->shutdown = 0;
-	 */
+	for (i = 0, slot = rsock->waiting; i < RPC_MAXREQS-1; i++, slot++)
+		slot->w_next = slot + 1;
+	slot->w_next = NULL;
 
-	dprintk(("RPC: made socket %08lx", (long) rsock));
+	dprintk("RPC: made socket %p\n", rsock);
 	return rsock;
 }
 
@@ -392,13 +566,13 @@
 	unsigned long	t0 = jiffies;
 
 	rsock->shutdown = 1;
-	while (rsock->head || rsock->backlog) {
+	while (rsock->pending || rsock->backlog) {
 		interruptible_sleep_on(&rsock->shutwait);
 		if (current->signal & ~current->blocked)
 			return -EINTR;
 #if 1
 		if (t0 && t0 - jiffies > 60 * HZ) {
-			printk("RPC: hanging in rpc_closesock.\n");
+			printk(KERN_WARNING "RPC: hanging in rpc_closesock.\n");
 			t0 = 0;
 		}
 #endif
diff -u --recursive --new-file v1.3.81/linux/fs/nfs/sock.c linux/fs/nfs/sock.c
--- v1.3.81/linux/fs/nfs/sock.c	Wed Feb  7 08:55:39 1996
+++ linux/fs/nfs/sock.c	Sun Mar 31 00:05:22 1996
@@ -30,35 +30,49 @@
 
 #include <asm/segment.h>
 
-/* JEJB/JSP 2/7/94
- * this must match the value of NFS_SLACK_SPACE in linux/fs/nfs/proc.c 
- * ***FIXME*** should probably put this in nfs_fs.h */
-#define NFS_SLACK_SPACE 1024
-
 #define _S(nr) (1<<((nr)-1))
 
 /*
- * We violate some modularity principles here by poking around
- * in some socket internals.  Besides having to call socket
- * functions from kernel-space instead of user space, the socket
- * interface does not lend itself well to being cleanly called
- * without a file descriptor.  Since the nfs calls can run on
- * behalf of any process, the superblock maintains a file pointer
- * to the server socket.
+ * Place a synchronous call to the NFS server, meaning that the process
+ * sleeps in rpc_call until it either receives a reply or a major timeout
+ * occurs.
+ * This is now merely a front-end to nfs_rpc_doio.
  */
-
 int
 nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size)
 {
+	struct rpc_ioreq	req;
+
+	size += 1024;		/* account for NFS slack space. ugly */
+
+	req.rq_addr = &server->toaddr;
+	req.rq_alen = sizeof(server->toaddr);
+	req.rq_slot = NULL;
+
+	req.rq_svec[0].iov_base = start;
+	req.rq_svec[0].iov_len = (end - start) << 2;
+	req.rq_slen = (end - start) << 2;
+	req.rq_snr = 1;
+	req.rq_rvec[0].iov_base = start;
+	req.rq_rvec[0].iov_len = size;
+	req.rq_rlen = size;
+	req.rq_rnr = 1;
+
+	return nfs_rpc_doio(server, &req, 0);
+}
+
+int
+nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *req, int async)
+{
 	struct rpc_timeout	timeout;
 	unsigned long		maxtimeo;
 	unsigned long		oldmask;
 	int			major_timeout_seen, result;
 
-	timeout.init_timeout = server->timeo;
-	timeout.max_timeout = maxtimeo = NFS_MAX_RPC_TIMEOUT*HZ/10;
-	timeout.retries = server->retrans;
-	timeout.exponential = 1;
+	timeout.to_initval = server->timeo;
+	timeout.to_maxval = NFS_MAX_RPC_TIMEOUT*HZ/10;
+	timeout.to_retries = server->retrans;
+	timeout.to_exponential = 1;
 
 	oldmask = current->blocked;
 	current->blocked |= ~(_S(SIGKILL)
@@ -68,17 +82,19 @@
 		| (current->sig->action[SIGQUIT - 1].sa_handler == SIG_DFL
 			? _S(SIGQUIT) : 0))
 		: 0));
+
 	major_timeout_seen = 0;
+	maxtimeo = timeout.to_maxval;
 
 	do {
-		result = rpc_call(server->rsock, 
-				&server->toaddr, sizeof(server->toaddr),
-				start, ((char *) end) - ((char *) start),
-				start, size + 1024,
-				&timeout, 1);
+		result = rpc_doio(server->rsock, req, &timeout, async);
+		rpc_release(server->rsock, req);	/* Release slot */
+
 		if (current->signal & ~current->blocked)
 			result = -ERESTARTSYS;
 		if (result == -ETIMEDOUT) {
+			if (async)
+				break;
 			if (server->flags & NFS_MOUNT_SOFT) {
 				printk("NFS server %s not responding, "
 					"timed out.\n", server->hostname);
@@ -90,8 +106,9 @@
 					"still trying.\n", server->hostname);
 				major_timeout_seen = 1;
 			}
-			if ((timeout.init_timeout <<= 1) >= maxtimeo)
-				timeout.init_timeout = maxtimeo;
+			if ((timeout.to_initval <<= 1) >= maxtimeo) {
+				timeout.to_initval = maxtimeo;
+			}
 		} else if (result < 0 && result != -ERESTARTSYS) {
 			printk("NFS: notice message: result = %d.\n", result);
 		}
diff -u --recursive --new-file v1.3.81/linux/include/asm-alpha/hwrpb.h linux/include/asm-alpha/hwrpb.h
--- v1.3.81/linux/include/asm-alpha/hwrpb.h	Tue Aug 29 10:15:52 1995
+++ linux/include/asm-alpha/hwrpb.h	Mon Apr  1 17:00:03 1996
@@ -37,6 +37,7 @@
 #define ST_DEC_EB64P		 20	/* EB64+ systype	*/
 #define ST_DEC_EB66P		-19	/* EB66 systype		*/
 #define ST_DEC_EBPC64		-20	/* Cabriolet (AlphaPC64) systype */
+#defien ST_DEC_EB164		 26	/* EB164 systype	*/
 
 struct pcb_struct {
 	unsigned long ksp;
diff -u --recursive --new-file v1.3.81/linux/include/asm-i386/system.h linux/include/asm-i386/system.h
--- v1.3.81/linux/include/asm-i386/system.h	Sun Mar  3 13:14:50 1996
+++ linux/include/asm-i386/system.h	Mon Apr  1 10:31:01 1996
@@ -192,9 +192,9 @@
 #define tas(ptr) (xchg((ptr),1))
 
 struct __xchg_dummy { unsigned long a[100]; };
-#define __xg(x) ((volatile struct __xchg_dummy *)(x))
+#define __xg(x) ((struct __xchg_dummy *)(x))
 
-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
+static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
 {
 	switch (size) {
 		case 1:
diff -u --recursive --new-file v1.3.81/linux/include/asm-i386/termios.h linux/include/asm-i386/termios.h
--- v1.3.81/linux/include/asm-i386/termios.h	Sun Mar 24 13:12:47 1996
+++ linux/include/asm-i386/termios.h	Mon Apr  1 10:46:30 1996
@@ -51,6 +51,7 @@
 #define N_SLIP		1
 #define N_MOUSE		2
 #define N_PPP		3
+#define N_STRIP		4
 
 #ifdef __KERNEL__
 
diff -u --recursive --new-file v1.3.81/linux/include/linux/ax25.h linux/include/linux/ax25.h
--- v1.3.81/linux/include/linux/ax25.h	Sat Mar 16 13:52:10 1996
+++ linux/include/linux/ax25.h	Mon Apr  1 10:46:30 1996
@@ -65,6 +65,7 @@
 #define	AX25_HDRINCL	8
 #define AX25_IDLE	9
 #define AX25_PACLEN	10
+#define AX25_IPMAXQUEUE 11
 
 #define AX25_KILL	99
 
@@ -109,6 +110,7 @@
 #define	AX25_VALUES_DIGI	12	/* Digipeat mode */
 #define AX25_VALUES_IDLE	13	/* mode vc idle timer */
 #define AX25_VALUES_PACLEN	14	/* AX.25 MTU */
+#define AX25_VALUES_IPMAXQUEUE  15	/* Maximum number of buffers enqueued */
 #define	AX25_MAX_VALUES		20
 
 struct ax25_parms_struct
diff -u --recursive --new-file v1.3.81/linux/include/linux/aztcd.h linux/include/linux/aztcd.h
--- v1.3.81/linux/include/linux/aztcd.h	Sat Nov 25 17:28:41 1995
+++ linux/include/linux/aztcd.h	Tue Apr  2 08:24:06 1996
@@ -1,4 +1,4 @@
-/* $Id: aztcd.h,v 2.0 1995/11/10 19:38:49 root Exp root $
+/* $Id: aztcd.h,v 2.20 1996/03/12 18:31:30 root Exp root $
  *
  * Definitions for a AztechCD268 CD-ROM interface
  *	Copyright (C) 1994, 1995  Werner Zimmermann
@@ -22,6 +22,7 @@
  *  History:	W.Zimmermann adaption to Aztech CD268-01A Version 1.3
  *		October 1994 Email: zimmerma@rz.fht-esslingen.de
  */
+
 
 /* *** change this to set the I/O port address of your CD-ROM drive*/
 #define AZT_BASE_ADDR		0x320
diff -u --recursive --new-file v1.3.81/linux/include/linux/cdrom.h linux/include/linux/cdrom.h
--- v1.3.81/linux/include/linux/cdrom.h	Fri Feb 23 09:39:34 1996
+++ linux/include/linux/cdrom.h	Mon Apr  1 10:46:31 1996
@@ -149,6 +149,10 @@
 	union cdrom_addr cdsc_reladdr;
 };
 
+struct cdrom_mcn {
+  u_char medium_catalog_number[14]; /* 13 ASCII digits, null-terminated */
+};
+
 /*
  * audio states (from SCSI-2, but seen with other drives, too)
  */
diff -u --recursive --new-file v1.3.81/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v1.3.81/linux/include/linux/if_arp.h	Mon Mar 25 08:58:22 1996
+++ linux/include/linux/if_arp.h	Mon Apr  1 10:46:30 1996
@@ -49,6 +49,7 @@
 #define ARPHRD_SKIP	771		/* SKIP vif			*/
 #define ARPHRD_LOOPBACK	772		/* Loopback device		*/
 #define ARPHRD_LOCALTLK 773		/* Localtalk device		*/
+#define ARPHRD_METRICOM	774		/* Metricom STRIP		*/
 
 /* ARP protocol opcodes. */
 #define	ARPOP_REQUEST	1		/* ARP request			*/
diff -u --recursive --new-file v1.3.81/linux/include/linux/if_strip.h linux/include/linux/if_strip.h
--- v1.3.81/linux/include/linux/if_strip.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/if_strip.h	Tue Apr  2 12:12:35 1996
@@ -0,0 +1,27 @@
+/*
+ * if_strip.h --
+ *
+ *      Definitions for the STRIP interface
+ *
+ * Copyright 1996 The Board of Trustees of The Leland Stanford
+ * Junior University. All Rights Reserved.
+ *
+ * Permission to use, copy, modify, and distribute this
+ * software and its documentation for any purpose and without
+ * fee is hereby granted, provided that the above copyright
+ * notice appear in all copies.  Stanford University
+ * makes no representations about the suitability of this
+ * software for any purpose.  It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#ifndef __LINUX_STRIP_H
+#define __LINUX_STRIP_H
+
+typedef union {
+	__u32 l;
+	__u16 s[2];
+	__u8  c[4];
+} MetricomAddress;
+
+#endif
diff -u --recursive --new-file v1.3.81/linux/include/linux/miscdevice.h linux/include/linux/miscdevice.h
--- v1.3.81/linux/include/linux/miscdevice.h	Sun Mar 31 00:13:18 1996
+++ linux/include/linux/miscdevice.h	Sun Mar 31 16:36:42 1996
@@ -17,7 +17,7 @@
 	struct miscdevice * next, * prev;
 };
 
-extern int mouse_register(struct miscdevice * misc);
-extern int mouse_deregister(struct miscdevice * misc);
+extern int misc_register(struct miscdevice * misc);
+extern int misc_deregister(struct miscdevice * misc);
 
 #endif
diff -u --recursive --new-file v1.3.81/linux/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- v1.3.81/linux/include/linux/nfs_fs.h	Thu Mar 28 17:34:34 1996
+++ linux/include/linux/nfs_fs.h	Sun Mar 31 00:05:22 1996
@@ -92,13 +92,20 @@
 			    int cookie, int count, struct nfs_entry *entry);
 extern int nfs_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
 			    struct nfs_fsinfo *res);
+extern int nfs_proc_read_request(struct rpc_ioreq *, struct nfs_server *,
+				 struct nfs_fh *, unsigned long offset,
+				 unsigned long count, __u32 *buf);
+extern int nfs_proc_read_reply(struct rpc_ioreq *);
 extern int *rpc_header(int *p, int procedure, int program, int version,
 				int uid, int gid, int *groups);
 extern int *rpc_verify(int *p);
 
 /* linux/fs/nfs/sock.c */
 
-extern int nfs_rpc_call(struct nfs_server *server, int *start, int *end, int size);
+extern int nfs_rpc_call(struct nfs_server *server, int *start,
+				int *end, int size);
+extern int nfs_rpc_doio(struct nfs_server *server, struct rpc_ioreq *,
+				int async);
 
 /* linux/fs/nfs/inode.c */
 
@@ -125,6 +132,10 @@
 /* linux/fs/nfs/mmap.c */
 
 extern int nfs_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma);
+
+/* linux/fs/nfs/bio.c */
+
+extern int nfs_readpage(struct inode *, struct page *);
 
 /* NFS root */
 
diff -u --recursive --new-file v1.3.81/linux/include/linux/nfsiod.h linux/include/linux/nfsiod.h
--- v1.3.81/linux/include/linux/nfsiod.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/nfsiod.h	Sun Mar 31 00:22:58 1996
@@ -0,0 +1,42 @@
+/*
+ * linux/include/linux/nfsiod.h
+ *
+ * Declarations for asynchronous NFS RPC calls.
+ *
+ */
+
+#ifndef _LINUX_NFSIOD_H
+#define _LINUX_NFSIOD_H
+
+#include <linux/rpcsock.h>
+
+#ifdef __KERNEL__
+
+/*
+ * This is the callback handler for nfsiod requests.
+ * Note that the callback procedure must NOT sleep.
+ */
+struct nfsiod_req;
+typedef void	(*nfsiod_done_fn_t)(int result, struct nfsiod_req *);
+
+/*
+ * This is the nfsiod request struct.
+ */
+struct nfsiod_req {
+	struct nfsiod_req *	rq_next;
+	struct nfsiod_req *	rq_prev;
+	struct nfs_server *	rq_server;
+	struct wait_queue *	rq_wait;
+	struct rpc_ioreq	rq_rpcreq;
+	nfsiod_done_fn_t	rq_callback;
+	void *			rq_cdata;
+};
+
+struct nfsiod_req *	nfsiod_reserve(struct nfs_server *, nfsiod_done_fn_t);
+void			nfsiod_release(struct nfsiod_req *);
+int			nfsiod_enqueue(struct nfsiod_req *);
+int			nfsiod(void);
+
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_NFSIOD_H */
diff -u --recursive --new-file v1.3.81/linux/include/linux/rpcsock.h linux/include/linux/rpcsock.h
--- v1.3.81/linux/include/linux/rpcsock.h	Mon Oct 23 10:49:11 1995
+++ linux/include/linux/rpcsock.h	Sun Mar 31 00:05:22 1996
@@ -1,54 +1,118 @@
 /*
  *  rpcsock.h	Declarations for the RPC call interface.
  *
- *  Coypright (C) 1995 Olaf Kirch <okir@monad.swb.de>
- *
+ *  Coypright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
  */
 
 
 #ifndef _LINUX_RPCSOCK_H
 #define _LINUX_RPCSOCK_H
 
-/* Maximum number of outstanding RPCs per socket.
- * With 32 slots, IP fragment reassembly would frequently
- * fail due to low memory.
+/*
+ * The rpcsock code maintains an estimate on the maximum number of out-
+ * standing RPC requests, using the congestion avoidance implemented in
+ * 44BSD. This is basically the Van Jacobson slow start algorithm: If a
+ * retransmit occurs, the congestion window is halved; otherwise, it is
+ * incremented by 1/cwnd when a reply is received and a full number of
+ * requests are outstanding.
+ *
+ * Upper procedures may check whether a request would block waiting for
+ * a free RPC slot by using the RPC_CONGESTED() macro.
+ *
+ * Note: on machines with low memory we should probably use a smaller
+ * MAXREQS value: At 32 outstanding reqs with 8 megs of RAM, fragment
+ * reassembly will frequently run out of memory.
  */
-#define NRREQS		16
+#define RPC_MAXREQS		32
+#define RPC_CWNDSCALE		256
+#define RPC_MAXCWND		(RPC_MAXREQS * RPC_CWNDSCALE)
+/* #define RPC_INITCWND		(RPC_MAXCWND / 2) */
+#define RPC_INITCWND		RPC_CWNDSCALE
+#define RPC_CONGESTED(rsock)	((rsock)->cong >= (rsock)->cwnd)
 
-/* This describes a timeout strategy */
+/* RPC reply header size: xid, direction, status, accept_status (verifier
+ * size computed separately)
+ */
+#define RPC_HDRSIZE		(4 * 4)
+
+/*
+ * This describes a timeout strategy
+ */
 struct rpc_timeout {
-	unsigned long		init_timeout,
-				max_timeout,
-				increment;
-	int			retries;
-	char			exponential;
+	unsigned long		to_initval,
+				to_maxval,
+				to_increment;
+	int			to_retries;
+	char			to_exponential;
 };
 
-/* Wait information */
+/*
+ * This describes a complete RPC request
+ */
+struct rpc_ioreq {
+	struct rpc_wait *	rq_slot;
+	struct sockaddr	*	rq_addr;
+	int			rq_alen;
+	struct iovec		rq_svec[MAX_IOVEC];
+	unsigned int		rq_snr;
+	unsigned long		rq_slen;
+	struct iovec		rq_rvec[MAX_IOVEC];
+	unsigned int		rq_rnr;
+	unsigned long		rq_rlen;
+};
+
+/*
+ * This is the callback handler for async RPC.
+ */
+struct rpc_wait;
+typedef void	(*rpc_callback_fn_t)(int, struct rpc_wait *, void *);
+
+/*
+ * Wait information. This struct defines all the state of an RPC
+ * request currently in flight.
+ */
 struct rpc_wait {
-	struct rpc_wait		*prev, *next;
-	struct wait_queue	*wait;
-	int			*buf;
-	int			len;
-	char			gotit;
-	__u32			xid;
+	struct rpc_sock *	w_sock;
+	struct rpc_wait *	w_prev;
+	struct rpc_wait *	w_next;
+	struct rpc_ioreq *	w_req;
+	int			w_result;
+	struct wait_queue *	w_wait;
+	rpc_callback_fn_t	w_handler;
+	void *			w_cdata;
+	char			w_queued;
+	char			w_gotit;
+	__u32			w_xid;
 };
 
 struct rpc_sock {
-	struct file		*file;
-	struct socket		*sock;
-	struct rpc_wait		waiting[NRREQS];
-	struct rpc_wait		*head, *tail, *free;
-	struct wait_queue	*backlog;
-	struct wait_queue	*shutwait;
+	struct file *		file;
+	struct socket *		sock;
+	struct sock *		inet;
+	struct rpc_wait		waiting[RPC_MAXREQS];
+	unsigned long		cong;
+	unsigned long		cwnd;
+	struct rpc_wait *	pending;
+	struct rpc_wait *	free;
+	struct wait_queue *	backlog;
+	struct wait_queue *	shutwait;
 	int			shutdown;
 };
 
 #ifdef __KERNEL__
 
-int			rpc_call(struct rpc_sock *, struct sockaddr *, int,
-				const int *, int, int *, int,
-				struct rpc_timeout *, int);
+/* rpc_call: Call synchronously */
+int			rpc_call(struct rpc_sock *, struct rpc_ioreq *,
+					 struct rpc_timeout *);
+/* These implement asynch calls for nfsiod: Process calls rpc_reserve and
+ * rpc_transmits, then passes the request to nfsiod, which collects the
+ * results via rpc_doio
+ */
+int			rpc_reserve(struct rpc_sock *, struct rpc_ioreq *, int);
+void			rpc_release(struct rpc_sock *, struct rpc_ioreq *);
+int			rpc_transmit(struct rpc_sock *, struct rpc_ioreq *);
+int			rpc_doio(struct rpc_sock *, struct rpc_ioreq *,
+					 struct rpc_timeout *, int);
 struct rpc_sock	*	rpc_makesock(struct file *);
 int			rpc_closesock(struct rpc_sock *);
 
diff -u --recursive --new-file v1.3.81/linux/include/linux/sysctl.h linux/include/linux/sysctl.h
--- v1.3.81/linux/include/linux/sysctl.h	Sun Mar 10 10:06:09 1996
+++ linux/include/linux/sysctl.h	Tue Apr  2 08:43:07 1996
@@ -69,6 +69,33 @@
 
 /* CTL_NET names: */
 
+/* /proc/sys/net/core */
+#define NET_CORE		0x01000000
+
+/* /proc/sys/net/ethernet */
+#define NET_ETHER		0x02000000
+
+/* /proc/sys/net/802 */
+#define NET_802         	0x03000000
+
+/* /proc/sys/net/unix */
+#define NET_UNIX		0x04000000
+
+/* /proc/sys/net/ipv4 */
+#define NET_IPV4		0x05000000
+
+/* /proc/sys/net/ipx */
+#define NET_IPX			0x06000000
+
+/* /proc/sys/net/appletalk */
+#define NET_ATALK		0x07000000
+
+/* /proc/sys/net/netrom */
+#define NET_NETROM		0x08000000
+
+/* /proc/sys/net/ax25 */
+#define NET_AX25		0x09000000
+
 /* CTL_PROC names: */
 
 /* CTL_FS names: */
diff -u --recursive --new-file v1.3.81/linux/include/linux/tqueue.h linux/include/linux/tqueue.h
--- v1.3.81/linux/include/linux/tqueue.h	Sat Mar 16 14:29:21 1996
+++ linux/include/linux/tqueue.h	Mon Apr  1 11:03:53 1996
@@ -16,12 +16,6 @@
 #include <asm/bitops.h>
 #include <asm/system.h>
 
-#ifdef INCLUDE_INLINE_FUNCS
-#define _INLINE_ extern
-#else
-#define _INLINE_ extern __inline__
-#endif
-
 /*
  * New proposed "bottom half" handlers:
  * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de
@@ -54,22 +48,15 @@
 
 typedef struct tq_struct * task_queue;
 
-#define DECLARE_TASK_QUEUE(q)  task_queue q = &tq_last
+#define DECLARE_TASK_QUEUE(q)  task_queue q = NULL
 
-extern struct tq_struct tq_last;
 extern task_queue tq_timer, tq_immediate, tq_scheduler;
 
-#ifdef INCLUDE_INLINE_FUNCS
-struct tq_struct tq_last = {
-	&tq_last, 0, 0, 0
-};
-#endif
-
 /*
  * To implement your own list of active bottom halfs, use the following
  * two definitions:
  *
- * struct tq_struct *my_bh = &tq_last;
+ * struct tq_struct *my_bh = NULL;
  * struct tq_struct run_my_bh = {
  *	0, 0, (void *)(void *) run_task_queue, &my_bh
  * };
@@ -92,7 +79,7 @@
  * "bh_list".  You may call this function only from an interrupt
  * handler or a bottom half handler.
  */
-_INLINE_ void queue_task_irq(struct tq_struct *bh_pointer,
+extern __inline__ void queue_task_irq(struct tq_struct *bh_pointer,
 			       task_queue *bh_list)
 {
 	if (!set_bit(0,&bh_pointer->sync)) {
@@ -105,7 +92,7 @@
  * queue_task_irq_off: put the bottom half handler "bh_pointer" on the list
  * "bh_list".  You may call this function only when interrupts are off.
  */
-_INLINE_ void queue_task_irq_off(struct tq_struct *bh_pointer,
+extern __inline__ void queue_task_irq_off(struct tq_struct *bh_pointer,
 				 task_queue *bh_list)
 {
 	if (!(bh_pointer->sync & 1)) {
@@ -119,7 +106,7 @@
 /*
  * queue_task: as queue_task_irq, but can be called from anywhere.
  */
-_INLINE_ void queue_task(struct tq_struct *bh_pointer,
+extern __inline__ void queue_task(struct tq_struct *bh_pointer,
 			   task_queue *bh_list)
 {
 	if (!set_bit(0,&bh_pointer->sync)) {
@@ -135,16 +122,15 @@
 /*
  * Call all "bottom halfs" on a given list.
  */
-_INLINE_ void run_task_queue(task_queue *list)
+extern __inline__ void run_task_queue(task_queue *list)
 {
-	register struct tq_struct *p;
+	struct tq_struct *p;
 
-	p = xchg(list,&tq_last);
-
-	while (p != &tq_last) {
+	p = xchg(list,NULL);
+	while (p) {
 		void *arg;
 		void (*f) (void *);
-		register struct tq_struct *save_p;
+		struct tq_struct *save_p;
 		arg    = p -> data;
 		f      = p -> routine;
 		save_p = p;
@@ -153,7 +139,5 @@
 		(*f)(arg);
 	}
 }
-
-#undef _INLINE_
 
 #endif /* _LINUX_TQUEUE_H */
diff -u --recursive --new-file v1.3.81/linux/include/net/ax25.h linux/include/net/ax25.h
--- v1.3.81/linux/include/net/ax25.h	Mon Mar 25 10:24:40 1996
+++ linux/include/net/ax25.h	Tue Apr  2 12:28:47 1996
@@ -126,6 +126,7 @@
 #define	AX25_DEF_N2		10
 #define AX25_DEF_IDLE		20
 #define AX25_DEF_PACLEN		256
+#define AX25_DEF_IPMAXQUEUE	1		/* 1 * ax25->window */
 #define	AX25_DEF_DIGI		(AX25_DIGI_INBAND|AX25_DIGI_XBAND)
 
 typedef struct ax25_uid_assoc {
@@ -153,6 +154,7 @@
 	unsigned short		t1, t2, t3, idle, rtt;
 	unsigned short		t1timer, t2timer, t3timer, idletimer;
 	unsigned short		paclen;
+	unsigned short		maxqueue;
 	unsigned short		fragno, fraglen;
 	ax25_digi		*digipeat;
 	struct sk_buff_head	write_queue;
@@ -235,6 +237,7 @@
 extern int  size_ax25_addr(ax25_digi *);
 extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
 extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
+extern int  ax25_queue_length(ax25_cb *, struct sk_buff *); /* dl1bke 960327 */
 extern void ax25_dama_on(ax25_cb *);	/* dl1bke 951121 */
 extern void ax25_dama_off(ax25_cb *);	/* dl1bke 951121 */
 
diff -u --recursive --new-file v1.3.81/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.81/linux/include/net/sock.h	Mon Mar 25 10:24:40 1996
+++ linux/include/net/sock.h	Tue Apr  2 12:28:47 1996
@@ -237,7 +237,7 @@
  */
  
 	volatile unsigned short	backoff;
-	volatile int		err, err_soft;	/* Soft holds errors that don't
+	int			err, err_soft;	/* Soft holds errors that don't
 						   cause failure but are the cause
 						   of a persistent failure not just
 						   'timed out' */
@@ -441,6 +441,8 @@
 }
 
 
+extern struct sock *		sk_alloc(int priority);
+extern void			sk_free(struct sock *sk);
 extern void			destroy_sock(struct sock *sk);
 extern unsigned short		get_new_socknum(struct proto *,
 						unsigned short);
diff -u --recursive --new-file v1.3.81/linux/init/main.c linux/init/main.c
--- v1.3.81/linux/init/main.c	Thu Mar 28 17:34:34 1996
+++ linux/init/main.c	Mon Apr  1 13:56:12 1996
@@ -434,6 +434,54 @@
 		((loops_per_sec+2500)/5000) % 100);
 }
 
+static void parse_root_dev(char * line)
+{
+	int base = 0;
+	static struct dev_name_struct {
+		const char *name;
+		const int num;
+	} devices[] = {
+		{ "nfs",     0x00ff },
+		{ "hda",     0x0300 },
+		{ "hdb",     0x0340 },
+		{ "hdc",     0x1600 },
+		{ "hdd",     0x1640 },
+		{ "sda",     0x0800 },
+		{ "sdb",     0x0810 },
+		{ "sdc",     0x0820 },
+		{ "sdd",     0x0830 },
+		{ "sde",     0x0840 },
+		{ "fd",      0x0200 },
+		{ "xda",     0x0d00 },
+		{ "xdb",     0x0d40 },
+		{ "ram",     0x0100 },
+		{ "scd",     0x0b00 },
+		{ "mcd",     0x1700 },
+		{ "cdu535",  0x1800 },
+		{ "aztcd",   0x1d00 },
+		{ "cm206cd", 0x2000 },
+		{ "gscd",    0x1000 },
+		{ "sbpcd",   0x1900 },
+		{ "sonycd",  0x1800 },
+		{ NULL, 0 }
+	};
+
+	if (strncmp(line,"/dev/",5) == 0) {
+		struct dev_name_struct *dev = devices;
+		line += 5;
+		do {
+			int len = strlen(dev->name);
+			if (strncmp(line,dev->name,len) == 0) {
+				line += len;
+				base = dev->num;
+				break;
+			}
+			dev++;
+		} while (dev->name);
+	}
+	ROOT_DEV = to_kdev_t(base + simple_strtoul(line,NULL,base?10:16));
+}
+
 /*
  * This is a simple kernel command line parsing function: it parses
  * the command line, and fills in the arguments/environment to init
@@ -447,11 +495,8 @@
 static void parse_options(char *line)
 {
 	char *next;
-	char *devnames[] = { "nfs", "hda", "hdb", "hdc", "hdd", "sda", "sdb",
-		"sdc", "sdd", "sde", "fd", "xda", "xdb", "ram", NULL };
-	int devnums[]    = { 0x0FF, 0x300, 0x340, 0x1600, 0x1640, 0x800,
-		0x810, 0x820, 0x830, 0x840, 0x200, 0xD00, 0xD40, 0x100, 0};
 	int args, envs;
+
 	if (!*line)
 		return;
 	args = 0;
@@ -464,22 +509,7 @@
 		 * check for kernel options first..
 		 */
 		if (!strncmp(line,"root=",5)) {
-			int n;
-			line += 5;
-			if (strncmp(line,"/dev/",5)) {
-				ROOT_DEV = to_kdev_t(
-					     simple_strtoul(line,NULL,16));
-				continue;
-			}
-			line += 5;
-			for (n = 0 ; devnames[n] ; n++) {
-				int len = strlen(devnames[n]);
-				if (!strncmp(line,devnames[n],len)) {
-					ROOT_DEV = to_kdev_t(devnums[n]+
-					     simple_strtoul(line+len,NULL,0));
-					break;
-				}
-			}
+			parse_root_dev(line+5);
 			continue;
 		}
 #ifdef CONFIG_ROOT_NFS
diff -u --recursive --new-file v1.3.81/linux/kernel/exit.c linux/kernel/exit.c
--- v1.3.81/linux/kernel/exit.c	Sat Mar 23 14:31:02 1996
+++ linux/kernel/exit.c	Sun Mar 31 20:39:57 1996
@@ -23,25 +23,28 @@
 
 int getrusage(struct task_struct *, int, struct rusage *);
 
-static int generate(unsigned long sig, struct task_struct * p)
+static inline void generate(unsigned long sig, struct task_struct * p)
 {
 	unsigned long mask = 1 << (sig-1);
 	struct sigaction * sa = sig + p->sig->action - 1;
 
-	/* always generate signals for traced processes ??? */
-	if (!(p->flags & PF_PTRACED)) {
+	/*
+	 * Optimize away the signal, if it's a signal that can
+	 * be handled immediately (ie non-blocked and untraced)
+	 * and that is ignored (either explicitly or by default)
+	 */
+	if (!(mask & p->blocked) && !(p->flags & PF_PTRACED)) {
 		/* don't bother with ignored signals (but SIGCHLD is special) */
 		if (sa->sa_handler == SIG_IGN && sig != SIGCHLD)
-			return 0;
+			return;
 		/* some signals are ignored by default.. (but SIGCONT already did its deed) */
 		if ((sa->sa_handler == SIG_DFL) &&
 		    (sig == SIGCONT || sig == SIGCHLD || sig == SIGWINCH || sig == SIGURG))
-			return 0;
+			return;
 	}
 	p->signal |= mask;
 	if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
 		wake_up_process(p);
-	return 1;
 }
 
 int send_sig(unsigned long sig,struct task_struct * p,int priv)
diff -u --recursive --new-file v1.3.81/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.81/linux/kernel/ksyms.c	Wed Mar 27 08:19:29 1996
+++ linux/kernel/ksyms.c	Mon Apr  1 07:34:13 1996
@@ -244,7 +244,6 @@
 	X(tq_timer),
 	X(tq_immediate),
 	X(tq_scheduler),
-	X(tq_last),
 	X(timer_active),
 	X(timer_table),
  	X(intr_count),
diff -u --recursive --new-file v1.3.81/linux/kernel/sched.c linux/kernel/sched.c
--- v1.3.81/linux/kernel/sched.c	Sun Mar 31 00:13:19 1996
+++ linux/kernel/sched.c	Mon Apr  1 10:32:07 1996
@@ -290,15 +290,15 @@
 
 /* check alarm, wake up any interruptible tasks that have got a signal */
 
-	if (intr_count) {
-		printk("Aiee: scheduling in interrupt\n");
-		return;
-	}
+	if (intr_count)
+		goto scheduling_in_interrupt;
+
 	if (bh_active & bh_mask) {
 		intr_count = 1;
 		do_bottom_half();
 		intr_count = 0;
 	}
+
 	run_task_queue(&tq_scheduler);
 
 	need_resched = 0;
@@ -393,6 +393,10 @@
 		if (timeout)
 			del_timer(&timer);
 	}
+	return;
+
+scheduling_in_interrupt:
+	printk("Aiee: scheduling in interrupt\n");
 }
 
 #ifndef __alpha__
@@ -1028,7 +1032,7 @@
 				prof_buffer[ip]++;
 		}
 	}
-	if (tq_timer != &tq_last)
+	if (tq_timer)
 		mark_bh(TQUEUE_BH);
 }
 
diff -u --recursive --new-file v1.3.81/linux/kernel/softirq.c linux/kernel/softirq.c
--- v1.3.81/linux/kernel/softirq.c	Thu Mar 28 17:34:35 1996
+++ linux/kernel/softirq.c	Mon Apr  1 08:36:26 1996
@@ -8,9 +8,6 @@
  * bottom_half handler need not be re-entrant.
  */
 
-#define INCLUDE_INLINE_FUNCS
-#include <linux/tqueue.h>
-
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/kernel_stat.h>
@@ -23,7 +20,6 @@
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/bitops.h>
-
 
 unsigned long intr_count = 0;
 
diff -u --recursive --new-file v1.3.81/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v1.3.81/linux/kernel/sysctl.c	Fri Mar 15 10:21:12 1996
+++ linux/kernel/sysctl.c	Tue Apr  2 08:43:07 1996
@@ -4,6 +4,7 @@
  * Begun 24 March 1995, Stephen Tweedie
  * Added /proc support, Dec 1995
  * Added bdflush entry and intvec min/max checking, 2/23/96, Tom Dyas.
+ * Added hooks for /proc/sys/net (minor, minor patch), 96/4/1, Mike Shaver.
  */
 
 #include <linux/config.h>
@@ -38,6 +39,7 @@
 
 static ctl_table kern_table[];
 static ctl_table vm_table[];
+extern ctl_table net_table[];
 
 /* /proc declarations: */
 
@@ -100,6 +102,7 @@
 static ctl_table root_table[] = {
 	{CTL_KERN, "kernel", NULL, 0, 0555, kern_table},
 	{CTL_VM, "vm", NULL, 0, 0555, vm_table},
+	{CTL_NET, "net", NULL, 0, 0555, net_table},
 	{0}
 };
 
diff -u --recursive --new-file v1.3.81/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.81/linux/mm/filemap.c	Thu Mar 28 17:34:35 1996
+++ linux/mm/filemap.c	Mon Apr  1 14:15:01 1996
@@ -367,9 +367,12 @@
 			while (ahead < max_ahead) {
 				ahead += PAGE_SIZE;
 				page_cache = try_to_read_ahead(inode, pos + ahead, page_cache);
+				if (!page->locked)
+					goto unlocked_page;
 			}
 			__wait_on_page(page);
 		}
+unlocked_page:
 		if (!page->uptodate)
 			goto read_page;
 		if (nr > inode->i_size - pos)
@@ -439,14 +442,11 @@
 
 	page = find_page(inode, offset);
 	if (page)
-		goto found_page;
+		goto found_page_dont_free;
 	new_page = __get_free_page(GFP_KERNEL);
 	page = find_page(inode, offset);
-	if (page) {
-		if (new_page)
-			free_page(new_page);
+	if (page)
 		goto found_page;
-	}
 	if (!new_page)
 		return 0;
 	page = mem_map + MAP_NR(new_page);
@@ -458,7 +458,12 @@
 	add_page_to_inode_queue(inode, page);
 	add_page_to_hash_queue(inode, page);
 	inode->i_op->readpage(inode, page);
+	if (page->locked)
+		new_page = try_to_read_ahead(inode, offset + PAGE_SIZE, 0);
 found_page:
+	if (new_page)
+		free_page(new_page);
+found_page_dont_free:
 	wait_on_page(page);
 	return page_address(page);
 }
diff -u --recursive --new-file v1.3.81/linux/net/802/Makefile linux/net/802/Makefile
--- v1.3.81/linux/net/802/Makefile	Sun Mar 31 00:13:19 1996
+++ linux/net/802/Makefile	Tue Apr  2 08:43:08 1996
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := 802.o
-O_OBJS	  = p8023.o
+O_OBJS	  = p8023.o sysctl_net_802.o
 
 ifdef CONFIG_TR
 O_OBJS += tr.o
diff -u --recursive --new-file v1.3.81/linux/net/802/sysctl_net_802.c linux/net/802/sysctl_net_802.c
--- v1.3.81/linux/net/802/sysctl_net_802.c	Thu Jan  1 02:00:00 1970
+++ linux/net/802/sysctl_net_802.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_802.c: sysctl interface to net 802 subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/802 directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table e802_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/Makefile linux/net/Makefile
--- v1.3.81/linux/net/Makefile	Mon Mar 25 08:58:23 1996
+++ linux/net/Makefile	Tue Apr  2 08:43:08 1996
@@ -41,7 +41,7 @@
 endif
 
 L_TARGET     := network.a
-L_OBJS	     := socket.o protocols.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
+L_OBJS	     := socket.o protocols.o sysctl_net.o $(join $(SUB_DIRS),$(SUB_DIRS:%=/%.o))
 ifeq ($(CONFIG_MODULES),y)
 LX_OBJS = netsyms.o
 endif
diff -u --recursive --new-file v1.3.81/linux/net/appletalk/Makefile linux/net/appletalk/Makefile
--- v1.3.81/linux/net/appletalk/Makefile	Mon Mar 25 08:58:23 1996
+++ linux/net/appletalk/Makefile	Tue Apr  2 08:43:08 1996
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := appletalk.o
-O_OBJS	 := aarp.o ddp.o
+O_OBJS	 := aarp.o ddp.o sysctl_net_atalk.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.81/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v1.3.81/linux/net/appletalk/ddp.c	Sun Mar 31 00:13:19 1996
+++ linux/net/appletalk/ddp.c	Tue Apr  2 08:43:07 1996
@@ -219,7 +219,7 @@
 	
 	if(sk->wmem_alloc == 0 && sk->rmem_alloc == 0 && sk->dead)
 	{
-		kfree_s(sk,sizeof(*sk));
+		sk_free(sk);
 		MOD_DEC_USE_COUNT;
 	}
 	else
@@ -1125,7 +1125,7 @@
 static int atalk_create(struct socket *sock, int protocol)
 {
 	atalk_socket *sk;
-	sk=(atalk_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
+	sk=(atalk_socket *)sk_alloc(GFP_KERNEL);
 	if(sk==NULL)
 		return(-ENOMEM);
 	switch(sock->type)
@@ -1137,7 +1137,7 @@
 		case SOCK_DGRAM:
 			break;
 		default:
-			kfree_s((void *)sk,sizeof(*sk));
+			sk_free((void *)sk);
 			return(-ESOCKTNOSUPPORT);
 	}
 
@@ -1364,7 +1364,7 @@
 static int atalk_accept(struct socket *sock, struct socket *newsock, int flags)
 {
 	if(newsock->data)
-		kfree_s(newsock->data,sizeof(atalk_socket));
+		sk_free(newsock->data);
 	return -EOPNOTSUPP;
 }
 
@@ -2064,7 +2064,7 @@
 		atalk_if_get_info
 	});
 
-	printk("Appletalk 0.17 for Linux NET3.034\n");
+	printk(KERN_INFO "Appletalk 0.17 for Linux NET3.034\n");
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v1.3.81/linux/net/appletalk/sysctl_net_atalk.c linux/net/appletalk/sysctl_net_atalk.c
--- v1.3.81/linux/net/appletalk/sysctl_net_atalk.c	Thu Jan  1 02:00:00 1970
+++ linux/net/appletalk/sysctl_net_atalk.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_atalk.c: sysctl interface to net Appletalk subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/atalk directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table atalk_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/ax25/Makefile linux/net/ax25/Makefile
--- v1.3.81/linux/net/ax25/Makefile	Wed Aug 16 13:03:17 1995
+++ linux/net/ax25/Makefile	Tue Apr  2 08:43:08 1996
@@ -9,7 +9,7 @@
 
 
 O_TARGET := ax25.o
-O_OBJS	 := af_ax25.o
+O_OBJS	 := af_ax25.o sysctl_net_ax25.o
 
 ifdef CONFIG_AX25
 O_OBJS	+= ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o
diff -u --recursive --new-file v1.3.81/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.81/linux/net/ax25/af_ax25.c	Mon Mar 25 08:58:24 1996
+++ linux/net/ax25/af_ax25.c	Mon Apr  1 10:46:30 1996
@@ -72,12 +72,15 @@
  *			Joerg(DL1BKE)		Added DAMA support, fixed (?) digipeating, fixed buffer locking
  *						for "virtual connect" mode... Result: Probably the
  *						"Most Buggiest Code You've Ever Seen" (TM)
- *			HaJo(DD8NE)		implementation of a T5 (idle) timer
- *			Joerg(DL1BKE)		renamed T5 to IDLE and changed behaviour:
+ *			HaJo(DD8NE)		Implementation of a T5 (idle) timer
+ *			Joerg(DL1BKE)		Renamed T5 to IDLE and changed behaviour:
  *						the timer gets reloaded on every received or transmited
  *						I frame for IP or NETROM. The idle timer is not active
  *						on "vanilla AX.25" connections. Furthermore added PACLEN
  *						to provide AX.25-layer based fragmentation (like WAMPES)
+ *      AX.25 032	Joerg(DL1BKE)		Fixed DAMA timeout error.
+ *						ax25_send_frame() limits the number of enqueued
+ *						datagrams per socket.
  *
  *	To do:
  *		Restructure the ax25_rcv code to be cleaner/faster and
@@ -634,6 +637,12 @@
 	  			printk("ax25_ctl_ioctl(): Warning --- huge paclen %d", (int) ax25_ctl.arg);
 	  		ax25->paclen = ax25_ctl.arg;
 	  		break;
+	  	case AX25_IPMAXQUEUE:
+	  		if (ax25_ctl.arg < 1)
+	  			return -EINVAL;
+	  			
+	  		ax25->maxqueue = ax25_ctl.arg;
+	  		break;
 	  	default:
 	  		return -EINVAL;
 	  }
@@ -667,6 +676,7 @@
 	ax25->t3      = AX25_DEF_T3 * PR_SLOWHZ;
 	ax25->n2      = AX25_DEF_N2;
 	ax25->paclen  = AX25_DEF_PACLEN;
+	ax25->maxqueue= AX25_DEF_IPMAXQUEUE;
 	ax25->idle    = 0;
 
 	ax25->modulus   = AX25_DEF_AXDEFMODE;
@@ -729,14 +739,15 @@
  */
 static void ax25_fillin_cb(ax25_cb *ax25, struct device *dev)
 {
-	ax25->device = dev;
+	ax25->device  = dev;
 
-	ax25->rtt    = ax25_dev_get_value(dev, AX25_VALUES_T1);
-	ax25->t1     = ax25_dev_get_value(dev, AX25_VALUES_T1);
-	ax25->t2     = ax25_dev_get_value(dev, AX25_VALUES_T2);
-	ax25->t3     = ax25_dev_get_value(dev, AX25_VALUES_T3);
-	ax25->n2     = ax25_dev_get_value(dev, AX25_VALUES_N2);
-	ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
+	ax25->rtt      = ax25_dev_get_value(dev, AX25_VALUES_T1);
+	ax25->t1       = ax25_dev_get_value(dev, AX25_VALUES_T1);
+	ax25->t2       = ax25_dev_get_value(dev, AX25_VALUES_T2);
+	ax25->t3       = ax25_dev_get_value(dev, AX25_VALUES_T3);
+	ax25->n2       = ax25_dev_get_value(dev, AX25_VALUES_N2);
+	ax25->paclen   = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
+	ax25->maxqueue = ax25_dev_get_value(dev, AX25_VALUES_IPMAXQUEUE);
 
 	ax25->dama_slave = 0;
 	ax25->idle = 0;
@@ -768,7 +779,11 @@
 			continue;
 
 		if (ax25cmp(&ax25->source_addr, src) == 0 && ax25cmp(&ax25->dest_addr, dest) == 0 && ax25->device == dev) {
-			ax25_output(ax25, skb);
+			if (ax25_queue_length(ax25, skb) > ax25->maxqueue * ax25->window) {
+				kfree_skb(skb, FREE_WRITE);
+			} else {
+				ax25_output(ax25, skb);
+			}
 			ax25->idletimer = ax25->idle;	/* dl1bke 960228 */
 			return 1;		/* It already existed */
 		}
@@ -1075,11 +1090,11 @@
 			return -ESOCKTNOSUPPORT;
 	}
 
-	if ((sk = (struct sock *)kmalloc(sizeof(*sk), GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
 		return -ENOMEM;
 
 	if ((ax25 = ax25_create_cb()) == NULL) {
-		kfree_s(sk, sizeof(*sk));
+		sk_free(sk);
 		return -ENOMEM;
 	}
 
@@ -1133,11 +1148,11 @@
 	struct sock *sk;
 	ax25_cb *ax25;
 
-	if ((sk = (struct sock *)kmalloc(sizeof(*sk), GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	if ((ax25 = ax25_create_cb()) == NULL) {
-		kfree_s(sk, sizeof(*sk));
+		sk_free(sk);
 		return NULL;
 	}
 
@@ -1152,7 +1167,7 @@
 		case SOCK_SEQPACKET:
 			break;
 		default:
-			kfree_s((void *)sk, sizeof(*sk));
+			sk_free(sk);
 			kfree_s((void *)ax25, sizeof(*ax25));
 			return NULL;
 	}
@@ -1206,7 +1221,7 @@
 	
 	if (osk->ax25->digipeat != NULL) {
 		if ((ax25->digipeat = (ax25_digi *)kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
-			kfree_s(sk, sizeof(*sk));
+			sk_free(sk);
 			kfree_s(ax25, sizeof(*ax25));
 			return NULL;
 		}
@@ -1268,9 +1283,12 @@
 			case AX25_STATE_4:
 				ax25_clear_queues(sk->ax25);
 				sk->ax25->n2count = 0;
-				if (!sk->ax25->dama_slave)
+				if (!sk->ax25->dama_slave) {
 					ax25_send_control(sk->ax25, DISC, POLLON, C_COMMAND);
-				sk->ax25->t3timer = 0;
+					sk->ax25->t3timer = 0;
+				} else {
+					sk->ax25->t3timer = sk->ax25->t3;	/* DAMA slave timeout */
+				}
 				sk->ax25->t1timer = sk->ax25->t1 = ax25_calculate_t1(sk->ax25);
 				sk->ax25->state   = AX25_STATE_2;
 				sk->state         = TCP_CLOSE;
@@ -1501,7 +1519,7 @@
 	struct sk_buff *skb;
 
 	if (newsock->data)
-		kfree_s(newsock->data, sizeof(struct sock));
+		sk_free(newsock->data);
 
 	newsock->data = NULL;
 	
@@ -2441,11 +2459,10 @@
 void ax25_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
 {
 	unsigned char *ptr;
-	int was_locked;
 	
 #ifdef CONFIG_FIREWALL
 	if (call_out_firewall(PF_AX25, skb, skb->data) != FW_ACCEPT) {
-		kfree_skb(skb, FREE_WRITE);
+		dev_kfree_skb(skb, FREE_WRITE);
 		return;
 	}
 #endif	
@@ -2459,8 +2476,7 @@
 
 		if(skb_headroom(skb) < AX25_BPQ_HEADER_LEN) {
 			printk("ax25_queue_xmit: not enough space to add BPQ Ether header\n");
-			skb->free = 1;
-			kfree_skb(skb, FREE_WRITE);
+			dev_kfree_skb(skb, FREE_WRITE);
 			return;
 		}
 
@@ -2472,32 +2488,14 @@
 		*ptr++ = (size + 5) / 256;
 
 		dev->hard_header(skb, dev, ETH_P_BPQ, bcast_addr, NULL, 0);
-
-		/* dl1bke 960201: see below. Note that the device driver should 
-		 * 		  copy the data into its own buffers, or strange
-		 *                things will happen again.
-		 */
-
-		was_locked = skb_device_locked(skb);
 		dev_queue_xmit(skb, dev, pri);
-		if (was_locked) skb_device_unlock(skb);
-
 		return;
 	} 
 #endif
 
 	ptr = skb_push(skb, 1);
 	*ptr++ = 0;			/* KISS */
-
-/* dl1bke 960201: dev_queue_xmit() will free the skb if it's not locked, so
- *                we need an additional variable to store its status.
- *		  sl_xmit() copies the data before returning, we can
- *		  remove the lock savely.
- */
-
-	was_locked = skb_device_locked(skb);
 	dev_queue_xmit(skb, dev, pri);
-	if (was_locked) skb_device_unlock(skb);
 }
 
 /*
@@ -2584,9 +2582,17 @@
 			 *	freeing it).
 			 */
 			struct sk_buff *ourskb=skb_clone(skb, GFP_ATOMIC);
+
+			if(ourskb==NULL) {
+				dev_kfree_skb(skb, FREE_WRITE);
+				return 1;
+			}
+
+			ourskb->sk = skb->sk;
+			if (ourskb->sk != NULL)
+				atomic_add(ourskb->truesize, &ourskb->sk->wmem_alloc);
+
 			dev_kfree_skb(skb, FREE_WRITE);
-			if(ourskb==NULL)
-				return 1;			
 			skb_pull(ourskb, AX25_HEADER_LEN - 1);	/* Keep PID */
 			ax25_send_frame(ourskb, (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
 			return 1;
@@ -2600,10 +2606,16 @@
   	bp[14] &= ~LAPB_C;
   	bp[14] |= LAPB_E;
   	bp[14] |= SSSID_SPARE;
+  	
+  	/*
+  	 * dl1bke 960317: we use ax25_queue_xmit here to allow mode datagram
+  	 *		  over ethernet. I don't know if this is valid, though.
+  	 */
 
 	ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev);
+	ax25_queue_xmit(skb, dev, SOPRI_NORMAL);
 
-  	return 0;
+  	return 1;
 }	
 
 #endif
diff -u --recursive --new-file v1.3.81/linux/net/ax25/ax25_in.c linux/net/ax25/ax25_in.c
--- v1.3.81/linux/net/ax25/ax25_in.c	Wed Mar 20 14:15:53 1996
+++ linux/net/ax25/ax25_in.c	Mon Apr  1 10:46:30 1996
@@ -29,7 +29,7 @@
  *					Added arbitrary protocol id support.
  *	AX.25 031	Joerg(DL1BKE)	Added DAMA support
  *			HaJo(DD8NE)	Added Idle Disc Timer T5
- *			Joerg(DL1BKE)   renamed it to "IDLE" with a slightly
+ *			Joerg(DL1BKE)   Renamed it to "IDLE" with a slightly
  *					different behaviour. Fixed defrag
  *					routine (I hope)
  */
diff -u --recursive --new-file v1.3.81/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v1.3.81/linux/net/ax25/ax25_out.c	Sun Mar 10 09:28:57 1996
+++ linux/net/ax25/ax25_out.c	Mon Apr  1 10:46:30 1996
@@ -26,8 +26,10 @@
  *					Added support for extended AX.25.
  *	AX.25 031	Joerg(DL1BKE)	Added DAMA support
  *
- *			Joerg(DL1BKE)	modified fragmenter to fragment vanilla 
+ *			Joerg(DL1BKE)	Modified fragmenter to fragment vanilla 
  *					AX.25 I-Frames. Added PACLEN parameter.
+ *			Joerg(DL1BKE)	Fixed a problem with buffer allocation
+ *					for fragments.
  */
 
 #include <linux/config.h>
@@ -61,7 +63,8 @@
 {
 	struct sk_buff *skbn;
 	unsigned char *p;
-	int err, frontlen, mtu, len, fragno, ka9qfrag, first = 1;
+	int frontlen, mtu, len, fragno, ka9qfrag, first = 1;
+	long flags;
 	
 	/*
 	 * dl1bke 960301: We use the new PACLEN parameter as MTU of the AX.25 layer.
@@ -97,15 +100,28 @@
 		frontlen = skb_headroom(skb);	/* Address space + CTRL */
 
 		while (skb->len > 0) {
-			if (skb->sk != NULL) {
-				if ((skbn = sock_alloc_send_skb(skb->sk, mtu + 2 + frontlen, 0, 0, &err)) == NULL)
-					return;
-			} else {
-				if ((skbn = alloc_skb(mtu + 2 + frontlen, GFP_ATOMIC)) == NULL)
-					return;
+			save_flags(flags); 
+			cli();
+			/* 
+			 * do _not_ use sock_alloc_send_skb, our socket may have
+			 * sk->shutdown set...
+			 *
+			 */
+			if ((skbn = alloc_skb(mtu + 2 + frontlen, GFP_ATOMIC)) == NULL) {
+				restore_flags(flags);
+				printk("ax25_output(): alloc_skb returned NULL\n");
+				if (skb_device_locked(skb))
+					skb_device_unlock(skb);
+				return;
 			}
 
 			skbn->sk   = skb->sk;
+			
+			if (skbn->sk)
+				atomic_add(skbn->truesize, &skbn->sk->wmem_alloc);
+			
+			restore_flags(flags);
+			
 			skbn->free = 1;
 			skbn->arp  = 1;
 
@@ -437,7 +453,7 @@
 	ax25->condition = 0x00;
 	ax25->n2count   = 0;
 
-	ax25->t3timer = 0;
+	ax25->t3timer = ax25->t3;
 	ax25->t2timer = 0;
 	ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 }
diff -u --recursive --new-file v1.3.81/linux/net/ax25/ax25_route.c linux/net/ax25/ax25_route.c
--- v1.3.81/linux/net/ax25/ax25_route.c	Sat Mar 16 13:52:11 1996
+++ linux/net/ax25/ax25_route.c	Mon Apr  1 10:46:30 1996
@@ -31,10 +31,10 @@
  *			Joerg(DL1BKE)	ax25_rt_build_path() find digipeater list and device by 
  *					destination call. Needed for IP routing via digipeater
  *			Jonathan(G4KLX)	Added routing for IP datagram packets.
- *			Joerg(DL1BKE)	changed routing for IP datagram and VC to use a default
+ *			Joerg(DL1BKE)	Changed routing for IP datagram and VC to use a default
  *					route if available. Does not overwrite default routes
  *					on route-table overflow anymore.
- *			Joerg(DL1BKE)	fixed AX.25 routing of IP datagram and VC, new ioctl()
+ *			Joerg(DL1BKE)	Fixed AX.25 routing of IP datagram and VC, new ioctl()
  *					"SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
  *					on routes.
  */
@@ -584,7 +584,8 @@
 	ax25_address src, dest;
 	unsigned char *bp;
 	int len;
-	
+
+	skb_pull(skb, 1);	/* skip KISS command */
 
 	ax25_rt = ax25_find_route(addr);
 	if (ax25_rt == NULL || ax25_rt->digipeat == NULL)
@@ -597,16 +598,14 @@
 	len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN;
 		
 	if (skb_headroom(skb) < len) {
-		printk("ax25_dg_build_path: not enough headroom for in skb\n");
+		printk("ax25_dg_build_path: not enough headroom for digis in skb\n");
 		return;
 	}
-
-	memcpy(&dest, skb->data + 1, AX25_ADDR_LEN);
-	memcpy(&src,  skb->data + 8, AX25_ADDR_LEN);
 	
+	memcpy(&dest, skb->data    , AX25_ADDR_LEN);
+	memcpy(&src,  skb->data + 7, AX25_ADDR_LEN);
 
 	bp = skb_push(skb, len);
-	*bp++ = 0x00;		/* KISS Data */
 
 	build_ax25_addr(bp, &src, &dest, ax25_rt->digipeat, C_COMMAND, MODULUS);
 }
@@ -696,6 +695,7 @@
 	ax25_dev->values[AX25_VALUES_N2]        = AX25_DEF_N2;
 	ax25_dev->values[AX25_VALUES_DIGI]      = AX25_DEF_DIGI;
 	ax25_dev->values[AX25_VALUES_PACLEN]	= AX25_DEF_PACLEN;
+	ax25_dev->values[AX25_VALUES_IPMAXQUEUE]= AX25_DEF_IPMAXQUEUE;
 
 	save_flags(flags);
 	cli();
@@ -784,10 +784,12 @@
 			if (ax25_parms.values[AX25_VALUES_N2] < 1 ||
 			    ax25_parms.values[AX25_VALUES_N2] > 31)
 				return -EINVAL;
-			if (ax25_parms.values[AX25_VALUES_PACLEN] < 16)
+			if (ax25_parms.values[AX25_VALUES_PACLEN] < 22)
 				return -EINVAL;
 			if ((ax25_parms.values[AX25_VALUES_DIGI] &
 			    ~(AX25_DIGI_INBAND | AX25_DIGI_XBAND)) != 0)
+				return -EINVAL;
+			if (ax25_parms.values[AX25_VALUES_IPMAXQUEUE] < 1)
 				return -EINVAL;
 			memcpy(ax25_dev->values, ax25_parms.values, AX25_MAX_VALUES * sizeof(short));
 			ax25_dev->values[AX25_VALUES_T1] *= PR_SLOWHZ;
diff -u --recursive --new-file v1.3.81/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v1.3.81/linux/net/ax25/ax25_subr.c	Wed Mar 20 14:15:19 1996
+++ linux/net/ax25/ax25_subr.c	Mon Apr  1 10:46:31 1996
@@ -29,7 +29,9 @@
  *					Thus we have ax25_kiss_cmd() now... ;-)
  *			Dave Brown(N2RJT)
  *					Killed a silly bug in the DAMA code.
- *			Joerg(DL1BKE)	found the real bug in ax25.h --- sorry.
+ *			Joerg(DL1BKE)	Found the real bug in ax25.h, sri.
+ *	AX.25 032	Joerg(DL1BKE)	Added ax25_queue_length to count the number of
+ *					enqeued buffers of a socket..
  */
 
 #include <linux/config.h>
@@ -471,6 +473,46 @@
 
 	/* Finish off */
 	out->lastrepeat = 0;
+}
+
+/*
+ *	count the number of buffers on a list belonging to the same
+ *	socket as skb
+ */
+
+static int ax25_list_length(struct sk_buff_head *list, struct sk_buff *skb)
+{
+	int count = 0;
+	long flags;
+	struct sk_buff *skbq;
+
+	save_flags(flags);
+	cli();
+
+	if (list == NULL) {
+		restore_flags(flags);
+                return 0;
+        }
+
+	skbq = (struct sk_buff *) list->next;
+
+	while (skbq != list) {
+		if (skb->sk == skbq->sk)
+			count++;
+		skbq = skbq->next;
+	}
+
+        restore_flags(flags);
+        return count;
+}
+
+/*
+ *	count the number of buffers of one socket on the write/ack-queue
+ */
+
+int ax25_queue_length(ax25_cb *ax25, struct sk_buff *skb)
+{
+	return ax25_list_length(&ax25->write_queue, skb)+ax25_list_length(&ax25->ack_queue, skb);
 }
 
 /*
diff -u --recursive --new-file v1.3.81/linux/net/ax25/ax25_timer.c linux/net/ax25/ax25_timer.c
--- v1.3.81/linux/net/ax25/ax25_timer.c	Sat Mar 16 13:52:11 1996
+++ linux/net/ax25/ax25_timer.c	Mon Apr  1 10:46:31 1996
@@ -18,6 +18,7 @@
  *					sock structure.
  *	AX.25 029	Alan(GW4PTS)	Switched to KA9Q constant names.
  *	AX.25 031	Joerg(DL1BKE)	Added DAMA support
+ *	AX.25 032	Joerg(DL1BKE)	Fixed DAMA timeout bug
  */
 
 #include <linux/config.h>
@@ -180,8 +181,12 @@
 		ax25_clear_queues(ax25);
 
 		ax25->n2count = 0;
-		if (!ax25->dama_slave)
+		if (!ax25->dama_slave) {
+			ax25->t3timer = 0;
 			ax25_send_control(ax25, DISC, POLLON, C_COMMAND);
+		} else {
+			ax25->t3timer = ax25->t3;
+		}
 		
 		/* state 1 or 2 should not happen, but... */
 		
@@ -190,7 +195,6 @@
 		else
 			ax25->state = AX25_STATE_2;
 
-		ax25->t3timer = 0;
 		ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
 
 		if (ax25->sk != NULL)
diff -u --recursive --new-file v1.3.81/linux/net/ax25/sysctl_net_ax25.c linux/net/ax25/sysctl_net_ax25.c
--- v1.3.81/linux/net/ax25/sysctl_net_ax25.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ax25/sysctl_net_ax25.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table ax25_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/core/Makefile linux/net/core/Makefile
--- v1.3.81/linux/net/core/Makefile	Tue Dec 12 07:05:07 1995
+++ linux/net/core/Makefile	Tue Apr  2 08:43:08 1996
@@ -9,7 +9,7 @@
 
 O_TARGET := core.o
 
-O_OBJS	:= sock.o skbuff.o iovec.o datagram.o 
+O_OBJS	:= sock.o skbuff.o iovec.o datagram.o sysctl_net_core.o
 
 ifdef CONFIG_NET
 
diff -u --recursive --new-file v1.3.81/linux/net/core/skbuff.c linux/net/core/skbuff.c
--- v1.3.81/linux/net/core/skbuff.c	Mon Mar 25 08:10:56 1996
+++ linux/net/core/skbuff.c	Sun Mar 31 18:02:42 1996
@@ -628,9 +628,9 @@
 			if (rw)
 				atomic_sub(skb->truesize, &sk->rmem_alloc);
 			else {
-				atomic_sub(skb->truesize, &sk->wmem_alloc);
 				if(!sk->dead)
 					sk->write_space(sk);
+				atomic_sub(skb->truesize, &sk->wmem_alloc);
 			}
 			kfree_skbmem(skb);
 		}
diff -u --recursive --new-file v1.3.81/linux/net/core/sock.c linux/net/core/sock.c
--- v1.3.81/linux/net/core/sock.c	Wed Mar 20 15:57:00 1996
+++ linux/net/core/sock.c	Mon Apr  1 10:46:31 1996
@@ -68,6 +68,8 @@
  *		Alan Cox	:	Allocator for a socket is settable.
  *		Alan Cox	:	SO_ERROR includes soft errors.
  *		Alan Cox	:	Allow NULL arguments on some SO_ opts
+ *		Alan Cox	: 	Generic socket allocation to make hooks
+ *					easier (suggested by Craig Metz).
  *
  * To Fix:
  *
@@ -324,6 +326,21 @@
   	return(0);
 }
 
+struct sock *sk_alloc(int priority)
+{
+	struct sock *sk=(struct sock *)kmalloc(sizeof(*sk), priority);
+	if(!sk)
+		return NULL;
+	memset(sk, 0, sizeof(*sk));
+	return sk;
+}
+
+void sk_free(struct sock *sk)
+{
+	kfree_s(sk,sizeof(*sk));
+}
+
+
 struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, int priority)
 {
 	if (sk) {
@@ -393,9 +410,9 @@
 	kfree_skbmem(skb);
 	if (sk) 
 	{
-		atomic_sub(s, &sk->wmem_alloc);
 		/* In case it might be waiting for more memory. */
 		sk->write_space(sk);
+		atomic_sub(s, &sk->wmem_alloc);
 	}
 }
 
diff -u --recursive --new-file v1.3.81/linux/net/core/sysctl_net_core.c linux/net/core/sysctl_net_core.c
--- v1.3.81/linux/net/core/sysctl_net_core.c	Thu Jan  1 02:00:00 1970
+++ linux/net/core/sysctl_net_core.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_core.c: sysctl interface to net core subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/core directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table core_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/ethernet/Makefile linux/net/ethernet/Makefile
--- v1.3.81/linux/net/ethernet/Makefile	Tue Aug 15 15:07:03 1995
+++ linux/net/ethernet/Makefile	Tue Apr  2 08:43:08 1996
@@ -9,7 +9,7 @@
 
 O_TARGET := ethernet.o
 
-OBJS	:= eth.o 
+OBJS	:= eth.o sysctl_net_ether.o
 
 ifdef CONFIG_IPX
 OBJ2	:= pe2.o
diff -u --recursive --new-file v1.3.81/linux/net/ethernet/sysctl_net_ether.c linux/net/ethernet/sysctl_net_ether.c
--- v1.3.81/linux/net/ethernet/sysctl_net_ether.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ethernet/sysctl_net_ether.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_ether.c: sysctl interface to net Ethernet subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/ether directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table ether_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/ipv4/Makefile linux/net/ipv4/Makefile
--- v1.3.81/linux/net/ipv4/Makefile	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/Makefile	Tue Apr  2 08:43:08 1996
@@ -12,7 +12,8 @@
 	     ip_input.o ip_fragment.o ip_forward.o ip_options.o \
 	     ip_output.o ip_sockglue.o \
 	     tcp.o tcp_input.o tcp_output.o tcp_timer.o \
-	     raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o ip_fw.o
+	     raw.o udp.o arp.o icmp.o devinet.o af_inet.o igmp.o ip_fw.o \
+	     sysctl_net_ipv4.o
 
 MOD_LIST_NAME := IPV4_MODULES
 M_OBJS :=
diff -u --recursive --new-file v1.3.81/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v1.3.81/linux/net/ipv4/af_inet.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/af_inet.c	Mon Apr  1 10:46:31 1996
@@ -407,7 +407,7 @@
 		 *	later once I know the bug is buried.
 		 */
 		tcp_cache_zap();
-		kfree_s((void *)sk,sizeof(*sk));
+		sk_free(sk);
 	} 
 	else 
 	{
@@ -578,7 +578,7 @@
 	struct proto *prot;
 	int err;
 
-	sk = (struct sock *) kmalloc(sizeof(*sk), GFP_KERNEL);
+	sk = sk_alloc(GFP_KERNEL);
 	if (sk == NULL) 
 		return(-ENOBUFS);
 	memset(sk,0,sizeof(*sk));	/* Efficient way to set most fields to zero */
@@ -591,7 +591,7 @@
 		case SOCK_SEQPACKET:
 			if (protocol && protocol != IPPROTO_TCP) 
 			{
-				kfree_s((void *)sk, sizeof(*sk));
+				sk_free(sk);
 				return(-EPROTONOSUPPORT);
 			}
 			protocol = IPPROTO_TCP;
@@ -602,7 +602,7 @@
 		case SOCK_DGRAM:
 			if (protocol && protocol != IPPROTO_UDP) 
 			{
-				kfree_s((void *)sk, sizeof(*sk));
+				sk_free(sk);
 				return(-EPROTONOSUPPORT);
 			}
 			protocol = IPPROTO_UDP;
@@ -613,12 +613,12 @@
 		case SOCK_RAW:
 			if (!suser()) 
 			{
-				kfree_s((void *)sk, sizeof(*sk));
+				sk_free(sk);
 				return(-EPERM);
 			}
 			if (!protocol) 
 			{
-				kfree_s((void *)sk, sizeof(*sk));
+				sk_free(sk);
 				return(-EPROTONOSUPPORT);
 			}
 			prot = &raw_prot;
@@ -629,12 +629,12 @@
 		case SOCK_PACKET:
 			if (!suser()) 
 			{
-				kfree_s((void *)sk, sizeof(*sk));
+				sk_free(sk);
 				return(-EPERM);
 			}
 			if (!protocol) 
 			{
-				kfree_s((void *)sk, sizeof(*sk));
+				sk_free(sk);
 				return(-EPROTONOSUPPORT);
 			}
 			prot = &packet_prot;
@@ -643,7 +643,7 @@
 			break;
 
 		default:
-			kfree_s((void *)sk, sizeof(*sk));
+			sk_free(sk);
 			return(-ESOCKTNOSUPPORT);
 	}
 	sk->socket = sock;
@@ -655,7 +655,6 @@
 	sk->allocation = GFP_KERNEL;
 	sk->sndbuf = SK_WMEM_MAX;
 	sk->rcvbuf = SK_RMEM_MAX;
-	sk->ato = HZ/3;
 	sk->rto = TCP_TIMEOUT_INIT;		/*TCP_WRITE_TIME*/
 	sk->cong_window = 1; /* start with only sending one packet at a time. */
 	sk->priority = 1;
diff -u --recursive --new-file v1.3.81/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.81/linux/net/ipv4/arp.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/arp.c	Mon Apr  1 10:46:31 1996
@@ -1023,6 +1023,7 @@
 #endif
 		case ARPHRD_ETHER:
 		case ARPHRD_ARCNET:
+		case ARPHRD_METRICOM:
 			if(arp->ar_pro != htons(ETH_P_IP))
 			{
 				kfree_skb(skb, FREE_READ);
diff -u --recursive --new-file v1.3.81/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v1.3.81/linux/net/ipv4/ip_masq.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipv4/ip_masq.c	Mon Apr  1 10:46:31 1996
@@ -346,7 +346,7 @@
                         save_flags(flags);
                         cli();
                 
-                        if (ip_masq_free_ports == 0) {
+                        if (*free_ports_p == 0) {
                                 restore_flags(flags);
                                 break;
                         }
diff -u --recursive --new-file v1.3.81/linux/net/ipv4/sysctl_net_ipv4.c linux/net/ipv4/sysctl_net_ipv4.c
--- v1.3.81/linux/net/ipv4/sysctl_net_ipv4.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipv4/sysctl_net_ipv4.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_ipv4.c: sysctl interface to net IPV4 subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/ipv4 directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table ipv4_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.81/linux/net/ipv4/tcp.c	Fri Mar 15 09:32:32 1996
+++ linux/net/ipv4/tcp.c	Sun Mar 31 20:07:11 1996
@@ -1439,6 +1439,17 @@
 			break;
 
 		/*
+		 * We need to check signals first, to get correct SIGURG
+		 * handling.
+		 */
+		if (current->signal & ~current->blocked) {
+			if (copied)
+				break;
+			copied = -ERESTARTSYS;
+			break;
+		}
+
+		/*
 		 *	Next get a buffer.
 		 */
 		 
@@ -1502,12 +1513,6 @@
 		schedule();
 		sk->socket->flags &= ~SO_WAITDATA;
 		lock_sock(sk);
-
-		if (current->signal & ~current->blocked) 
-		{
-			copied = -ERESTARTSYS;
-			break;
-		}
 		continue;
 
 	found_ok_skb:
diff -u --recursive --new-file v1.3.81/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v1.3.81/linux/net/ipv4/tcp_input.c	Thu Mar 21 17:08:06 1996
+++ linux/net/ipv4/tcp_input.c	Sun Mar 31 20:21:24 1996
@@ -398,7 +398,6 @@
 	newsk->send_tail = NULL;
 	skb_queue_head_init(&newsk->back_log);
 	newsk->rtt = 0;		/*TCP_CONNECT_TIME<<3*/
-	newsk->ato = HZ/3;
 	newsk->rto = TCP_TIMEOUT_INIT;
 	newsk->mdev = 0;
 	newsk->max_window = 0;
diff -u --recursive --new-file v1.3.81/linux/net/ipx/Makefile linux/net/ipx/Makefile
--- v1.3.81/linux/net/ipx/Makefile	Mon Mar 25 08:58:25 1996
+++ linux/net/ipx/Makefile	Tue Apr  2 08:43:08 1996
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := ipx.o
-O_OBJS   := af_ipx.o
+O_OBJS   := af_ipx.o sysctl_net_ipx.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.81/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.81/linux/net/ipx/af_ipx.c	Sun Mar 31 00:13:19 1996
+++ linux/net/ipx/af_ipx.c	Tue Apr  2 08:43:07 1996
@@ -42,6 +42,7 @@
  *	Revision 0.33:	Internal network support, routing changes, uses a
  *			protocol private area for ipx data.
  *	Revision 0.34:	Module support. <Jim Freeman>
+ *	Revision 0.35:  Checksum support. <Neil Turton>, hooked in by <Alan Cox>
  *
  * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
  *	Neither Greg Page nor Caldera, Inc. admit liability nor provide 
@@ -193,7 +194,7 @@
 		kfree_skb(skb,FREE_READ);
 	}
 	
-	kfree_s(sk,sizeof(*sk));
+	sk_free(sk);
 	MOD_DEC_USE_COUNT;
 }
 	
@@ -907,8 +908,8 @@
 	if(dev->addr_len>IPX_NODE_LEN)
 		return -EINVAL;
 
-	if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL) {
-
+	if ((intrfc = ipxitf_find_using_phys(dev, dlink_type)) == NULL) 
+	{
 		/* Ok now create */
 		intrfc=(ipx_interface *)kmalloc(sizeof(ipx_interface),GFP_ATOMIC);
 		if (intrfc==NULL)
@@ -924,9 +925,14 @@
 			ipx_primary_net = intrfc;
 		intrfc->if_internal = 0;
 		intrfc->if_ipx_offset = dev->hard_header_len + datalink->header_length;
-		memset(intrfc->if_node, 0, IPX_NODE_LEN);
-		memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), dev->dev_addr, dev->addr_len);
-
+		if(memcmp(idef->ipx_node, "\000\000\000\000\000\000", IPX_NODE_LEN)==0)
+		{
+			memset(intrfc->if_node, 0, IPX_NODE_LEN);
+			memcpy((char *)&(intrfc->if_node[IPX_NODE_LEN-dev->addr_len]), 
+				dev->dev_addr, dev->addr_len);
+		}
+		else
+			memcpy(intrfc->if_node, idef->ipx_node, IPX_NODE_LEN);
 		ipxitf_insert(intrfc);
 	}
 
@@ -1172,6 +1178,65 @@
 }
 
 /*
+ *	Checksum routine for IPX
+ */
+ 
+/* Note: We assume ipx_tctrl==0 and htons(length)==ipx_pktsize */
+
+static __u16 ipx_set_checksum(ipx_packet *packet,int length) 
+{
+	/* 
+	 *	NOTE: sum is a net byte order quantity, which optimizes the 
+	 *	loop. This only works on big and little endian machines. (I
+	 *	don't know of a machine that isn't.)
+	 */
+
+	__u32 sum=0;
+
+	/*
+	 *	Pointer to second word - We skip the checksum field
+	 */
+
+	__u16 *p=(__u16 *)&packet->ipx_pktsize;
+
+	/*
+	 *	Number of complete words 
+	 */
+
+	__u32 i=length>>1;
+
+	/*
+	 *	Loop through all complete words except the checksum field 
+	 */
+
+	while(--i)
+		sum+=*p++;
+
+	/*
+	 *	Add on the last part word if it exists 
+	 */
+
+	if(packet->ipx_pktsize&htons(1))
+		sum+=ntohs(0xff00)&*p;
+
+	/*
+	 *	Do final fixup 
+	 */
+	 
+	sum=(sum&0xffff)+(sum>>16);
+
+	/*
+	 *	It's a pitty there's no concept of carry in C 
+	 */
+
+	if(sum>=0x10000)
+		sum++;
+		
+	return ~sum;
+};
+
+ 
+/*
  *	Route an outgoing frame from a socket.
  */
 
@@ -1215,7 +1280,6 @@
 
 	/* Fill in IPX header */
 	ipx=(ipx_packet *)skb_put(skb,sizeof(ipx_packet));
-	ipx->ipx_checksum=0xFFFF;
 	ipx->ipx_pktsize=htons(len+sizeof(ipx_packet));
 	ipx->ipx_tctrl=0;
 	ipx->ipx_type=usipx->sipx_type;
@@ -1244,6 +1308,15 @@
 
 	memcpy_fromiovec(skb_put(skb,len),iov,len);
 
+	/*
+	 *	Apply checksum. Not allowed on 802.3 links.
+	 */
+	 
+	if(sk->no_check || intrfc->if_dlink_type!=IPX_FRAME_8023)
+		ipx->ipx_checksum=0xFFFF;
+	else
+		ipx->ipx_checksum=ipx_set_checksum(ipx, len+sizeof(ipx_packet));
+
 #ifdef CONFIG_FIREWALL	
 	if(call_out_firewall(PF_IPX, skb, ipx)!=FW_ACCEPT)
 	{
@@ -1608,11 +1681,10 @@
 	}
 }
 
-static int 
-ipx_create(struct socket *sock, int protocol)
+static int ipx_create(struct socket *sock, int protocol)
 {
 	ipx_socket *sk;
-	sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
+	sk=(ipx_socket *)sk_alloc(GFP_KERNEL);
 	if(sk==NULL)
 		return(-ENOMEM);
 	switch(sock->type)
@@ -1623,17 +1695,9 @@
 			kfree_s((void *)sk,sizeof(*sk));
 			return(-ESOCKTNOSUPPORT);
 	}
-	sk->dead=0;
-	sk->next=NULL;
-	sk->broadcast=0;
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
-	sk->wmem_alloc=0;
-	sk->rmem_alloc=0;
-	sk->users=0;
-	sk->shutdown=0;
 	sk->prot=NULL;	/* So we use default free mechanisms */
-	sk->err=0;
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
 	sk->send_head=NULL;
@@ -1641,15 +1705,8 @@
 	sk->state=TCP_CLOSE;
 	sk->socket=sock;
 	sk->type=sock->type;
-	sk->protinfo.af_ipx.type=0;		/* General user level IPX */
-	sk->debug=0;
-	sk->protinfo.af_ipx.intrfc = NULL;
-	memset(&sk->protinfo.af_ipx.dest_addr,'\0',
-		sizeof(sk->protinfo.af_ipx.dest_addr));
-	sk->protinfo.af_ipx.port = 0;
-	sk->protinfo.af_ipx.ncp_server = 0;
 	sk->mtu=IPX_MTU;
-	
+	sk->no_check = 1;		/* Checksum off by default */	
 	if(sock!=NULL)
 	{
 		sock->data=(void *)sk;
@@ -1965,23 +2022,26 @@
 	
 	ipx=(ipx_packet *)skb->h.raw;
 	
-	if(ipx->ipx_checksum!=IPX_NO_CHECKSUM) {
-		/* We don't do checksum options. We can't really. Novell don't seem to have documented them.
-		   If you need them try the XNS checksum since IPX is basically XNS in disguise. It might be
-		   the same... */
+	/* Too small */
+	
+	if(ntohs(ipx->ipx_pktsize)<sizeof(ipx_packet)) {
 		kfree_skb(skb,FREE_READ);
 		return 0;
 	}
 	
-	/* Too small */
-	if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet)) {
-		kfree_skb(skb,FREE_READ);
-		return 0;
+	if(ipx->ipx_checksum!=IPX_NO_CHECKSUM) 
+	{
+		if(ipx_set_checksum(ipx, ntohs(ipx->ipx_pktsize))!=ipx->ipx_checksum)
+		{
+			kfree_skb(skb,FREE_READ);
+			return 0;
+		}
 	}
 	
 	/* Determine what local ipx endpoint this is */
 	intrfc = ipxitf_find_using_phys(dev, pt->type);
-	if (intrfc == NULL) {
+	if (intrfc == NULL) 
+	{
 		if (ipxcfg_auto_create_interfaces) {
 			intrfc = ipxitf_auto_create(dev, pt->type);
 		}
@@ -2004,8 +2064,10 @@
 	struct sockaddr_ipx local_sipx;
 	int retval;
 
-	if (sk->zapped) return -EIO; /* Socket not bound */
-	if(flags) return -EINVAL;
+	if (sk->zapped) 
+		return -EIO; /* Socket not bound */
+	if(flags) 
+		return -EINVAL;
 		
 	if(usipx) 
 	{
@@ -2043,7 +2105,8 @@
 	}
 	
 	retval = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len);
-	if (retval < 0) return retval;
+	if (retval < 0) 
+		return retval;
 
 	return len;
 }
@@ -2278,8 +2341,8 @@
 	proc_net_register(&ipx_if_procinfo);
 	proc_net_register(&ipx_rt_procinfo);
 		
-	printk("Swansea University Computer Society IPX 0.34 for NET3.034\n");
-	printk("IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
+	printk(KERN_INFO "Swansea University Computer Society IPX 0.34 for NET3.034\n");
+	printk(KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n");
 }
 
 #ifdef MODULE
diff -u --recursive --new-file v1.3.81/linux/net/ipx/sysctl_net_ipx.c linux/net/ipx/sysctl_net_ipx.c
--- v1.3.81/linux/net/ipx/sysctl_net_ipx.c	Thu Jan  1 02:00:00 1970
+++ linux/net/ipx/sysctl_net_ipx.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_ipx.c: sysctl interface to net IPX subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/ipx directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table ipx_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/netrom/Makefile linux/net/netrom/Makefile
--- v1.3.81/linux/net/netrom/Makefile	Wed Sep 27 13:07:34 1995
+++ linux/net/netrom/Makefile	Tue Apr  2 08:43:08 1996
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := netrom.o
-O_OBJS	 := af_netrom.o
+O_OBJS	 := af_netrom.o sysctl_net_netrom.o
 
 ifdef CONFIG_NETROM
 O_OBJS	+= nr_dev.o nr_in.o nr_out.o nr_route.o nr_subr.o nr_timer.o
diff -u --recursive --new-file v1.3.81/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v1.3.81/linux/net/netrom/af_netrom.c	Mon Mar 25 08:58:26 1996
+++ linux/net/netrom/af_netrom.c	Mon Apr  1 10:46:31 1996
@@ -266,7 +266,7 @@
 		add_timer(&sk->timer);
 	} else {
 		kfree_s(sk->nr, sizeof(*sk->nr));
-		kfree_s(sk, sizeof(*sk));
+		sk_free(sk);
 	}
 
 	restore_flags(flags);
@@ -415,11 +415,11 @@
 	if (sock->type != SOCK_SEQPACKET || protocol != 0)
 		return -ESOCKTNOSUPPORT;
 
-	if ((sk = (struct sock *)kmalloc(sizeof(*sk), GFP_ATOMIC)) == NULL)
+	if ((sk = sk_alloc(GFP_ATOMIC)) == NULL)
 		return -ENOMEM;
 
 	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
-		kfree_s(sk, sizeof(*sk));
+		sk_free(sk);
 		return -ENOMEM;
 	}
 
@@ -516,11 +516,11 @@
 	if (osk->type != SOCK_SEQPACKET)
 		return NULL;
 
-	if ((sk = (struct sock *)kmalloc(sizeof(*sk), GFP_ATOMIC)) == NULL)
+	if ((sk = (struct sock *)sk_alloc(GFP_ATOMIC)) == NULL)
 		return NULL;
 
 	if ((nr = (nr_cb *)kmalloc(sizeof(*nr), GFP_ATOMIC)) == NULL) {
-		kfree_s(sk, sizeof(*sk));
+		sk_free(sk);
 		return NULL;
 	}
 
@@ -814,7 +814,7 @@
 	struct sk_buff *skb;
 
 	if (newsock->data)
-		kfree_s(newsock->data, sizeof(struct sock));
+		sk_free(newsock->data);
 
 	newsock->data = NULL;
 	
diff -u --recursive --new-file v1.3.81/linux/net/netrom/sysctl_net_netrom.c linux/net/netrom/sysctl_net_netrom.c
--- v1.3.81/linux/net/netrom/sysctl_net_netrom.c	Thu Jan  1 02:00:00 1970
+++ linux/net/netrom/sysctl_net_netrom.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_netrom.c: sysctl interface to net NETROM subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/netrom directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table netrom_table[] = {
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/sysctl_net.c linux/net/sysctl_net.c
--- v1.3.81/linux/net/sysctl_net.c	Thu Jan  1 02:00:00 1970
+++ linux/net/sysctl_net.c	Tue Apr  2 08:43:08 1996
@@ -0,0 +1,55 @@
+/* -*- linux-c -*-
+ * sysctl_net.c: sysctl interface to net subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net directories for each protocol family. [MS]
+ */
+
+#include <linux/config.h>
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+#ifdef CONFIG_INET
+extern ctl_table ipv4_table[];
+#endif
+
+#ifdef CONFIG_IPX
+extern ctl_table ipx_table[];
+#endif
+
+#ifdef CONFIG_ATALK
+extern ctl_table atalk_table[];
+#endif
+
+#ifdef CONFIG_NETROM
+extern ctl_table netrom_table[];
+#endif
+
+#ifdef CONFIG_AX25
+extern ctl_table ax25_table[];
+#endif
+
+extern ctl_table core_table[], ether_table[], e802_table[], unix_table[];
+
+ctl_table net_table[] = {
+	{NET_CORE,   "core",      NULL, 0, 0555, core_table},
+	{NET_ETHER,  "ethernet",  NULL, 0, 0555, ether_table},
+        {NET_802,    "802",       NULL, 0, 0555, e802_table},
+        {NET_UNIX,   "unix",      NULL, 0, 0555, unix_table},
+#ifdef CONFIG_INET
+	{NET_IPV4,   "ipv4",      NULL, 0, 0555, ipv4_table},
+#endif
+#ifdef CONFIG_IPX
+        {NET_IPX,    "ipx",       NULL, 0, 0555, ipx_table},
+#endif
+#ifdef CONFIG_ATALK
+        {NET_ATALK,  "appletalk", NULL, 0, 0555, atalk_table},
+#endif
+#ifdef CONFIG_NETROM
+	{NET_NETROM, "netrom",    NULL, 0, 0555, netrom_table},
+#endif
+#ifdef CONFIG_AX25
+	{NET_AX25,   "ax25",      NULL, 0, 0555, ax25_table},
+#endif
+	{0}
+};
diff -u --recursive --new-file v1.3.81/linux/net/unix/Makefile linux/net/unix/Makefile
--- v1.3.81/linux/net/unix/Makefile	Mon Mar  4 09:16:41 1996
+++ linux/net/unix/Makefile	Tue Apr  2 08:43:08 1996
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definition is now in the main makefile...
 
 O_TARGET := unix.o
-O_OBJS	 := af_unix.o garbage.o
+O_OBJS	 := af_unix.o garbage.o sysctl_net_unix.o
 
 include $(TOPDIR)/Rules.make
 
diff -u --recursive --new-file v1.3.81/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v1.3.81/linux/net/unix/af_unix.c	Mon Mar 25 08:58:28 1996
+++ linux/net/unix/af_unix.c	Mon Apr  1 10:46:31 1996
@@ -148,7 +148,7 @@
 	{
 		if(sk->protinfo.af_unix.name)
 			kfree(sk->protinfo.af_unix.name);
-		kfree_s(sk,sizeof(*sk));
+		sk_free(sk);
 		return;
 	}
 	
@@ -202,7 +202,7 @@
 	{
 		if(sk->protinfo.af_unix.name)
 			kfree(sk->protinfo.af_unix.name);
-		kfree_s(sk,sizeof(*sk));
+		sk_free(sk);
 	}
 	else
 	{
@@ -281,7 +281,7 @@
 	unix_socket *sk;
 	if(protocol && protocol != PF_UNIX)
 		return -EPROTONOSUPPORT;
-	sk=(unix_socket *)kmalloc(sizeof(*sk),GFP_KERNEL);
+	sk=(unix_socket *)sk_alloc(GFP_KERNEL);
 	if(sk==NULL)
 		return -ENOMEM;
 	switch(sock->type)
@@ -297,7 +297,7 @@
 		case SOCK_DGRAM:
 			break;
 		default:
-			kfree_s(sk,sizeof(*sk));
+			sk_free(sk);
 			return -ESOCKTNOSUPPORT;
 	}
 	sk->type=sock->type;
diff -u --recursive --new-file v1.3.81/linux/net/unix/sysctl_net_unix.c linux/net/unix/sysctl_net_unix.c
--- v1.3.81/linux/net/unix/sysctl_net_unix.c	Thu Jan  1 02:00:00 1970
+++ linux/net/unix/sysctl_net_unix.c	Tue Apr  2 09:03:35 1996
@@ -0,0 +1,13 @@
+/* -*- linux-c -*-
+ * sysctl_net_unix.c: sysctl interface to net af_unix subsystem.
+ *
+ * Begun April 1, 1996, Mike Shaver.
+ * Added /proc/sys/net/unix directory entry (empty =) ). [MS]
+ */
+
+#include <linux/mm.h>
+#include <linux/sysctl.h>
+
+ctl_table unix_table[] = {
+	{0}
+};
