diff -u --recursive --new-file v1.3.98/linux/CREDITS linux/CREDITS
--- v1.3.98/linux/CREDITS	Mon Apr 29 18:05:14 1996
+++ linux/CREDITS	Tue May  7 12:06:49 1996
@@ -122,6 +122,14 @@
 D: Author and maintainer of the QIC-02 tape driver
 S: The Netherlands
 
+N: Thomas Bogendoerfer
+E: tsbogend@bigbug.franken.de
+D: Lance32 driver
+D: strace for Linux/Alpha
+S: Baumgartenweg 5
+S: 91452 Wilhermsdorf
+S: Germany
+
 N: Ross Biro
 E: bir7@leland.Stanford.Edu
 D: Original author of the Linux networking code
@@ -424,14 +432,6 @@
 S: Toronto, Ontario, M5S 3A6 
 S: Canada
 
-N: Miquel van Smoorenburg
-E: miquels@cistron.nl
-D: Kernel and net hacker. Sysvinit, minicom. doing Debian stuff.
-S: Cistron Internet Services
-S: PO-Box 297
-S: 2400 AG, Alphen aan den Rijn
-S: The Netherlands
-
 N: Danny ter Haar
 E: dth@cistron.nl
 D: /proc/procinfo, reboot on panic , kernel pre-patch tester ;)
@@ -1085,6 +1085,14 @@
 D: HPFS filesystem
 S: Richardson, Texas
 S: USA
+
+N: Miquel van Smoorenburg
+E: miquels@cistron.nl
+D: Kernel and net hacker. Sysvinit, minicom. doing Debian stuff.
+S: Cistron Internet Services
+S: PO-Box 297
+S: 2400 AG, Alphen aan den Rijn
+S: The Netherlands
 
 N: Scott Snyder
 E: snyder@fnald0.fnal.gov
diff -u --recursive --new-file v1.3.98/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v1.3.98/linux/Documentation/Configure.help	Sun May  5 08:51:57 1996
+++ linux/Documentation/Configure.help	Mon May  6 12:26:01 1996
@@ -45,7 +45,7 @@
   internals are usually welcomed by the developers. Unless you intend
   to help test and develop a feature or driver that falls into this 
   category, you should probably say N here, which will cause this 
-  configure script to present you with less choices. If you say Y here,
+  configure script to present you with fewer choices. If you say Y here,
   you will be offered the choice of using features or drivers that are
   currently considered to be in the alpha-test phase.
 
@@ -1261,7 +1261,7 @@
   kernel whenever you want). If you want to compile it as a module,
   say M here and read Documentation/modules.txt.
 
-EATA-DMA (DPT, NEC, ATT, Olivetti for ISA, EISA, PCI) support
+EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support
 CONFIG_SCSI_EATA_DMA
   This is support for the EATA-DMA protocol compliant SCSI Host Adaptors
   like the SmartCache III/IV, SmartRAID controller families and the DPT 
diff -u --recursive --new-file v1.3.98/linux/Documentation/SMP.txt linux/Documentation/SMP.txt
--- v1.3.98/linux/Documentation/SMP.txt	Fri Apr 12 15:51:43 1996
+++ linux/Documentation/SMP.txt	Mon May  6 12:26:01 1996
@@ -14,7 +14,7 @@
 o	Clean up processor specific/independent split.
 o	Document it all.	[PARTLY DONE]
 o	Halt other CPU's on reset/panic doesn't always work.
-o	Dont waste page at 4K - dont need it now.(watch the GDT code).
+o	Don't waste page at 4K - don't need it now.(watch the GDT code).
 o	Dump bootup pages once booted somehow.
 o	Clean up warnings/volatiles.
 o	Fix load_TR() for non contiguous processor ids
diff -u --recursive --new-file v1.3.98/linux/Documentation/devices.tex linux/Documentation/devices.tex
--- v1.3.98/linux/Documentation/devices.tex	Sun Apr 21 19:21:58 1996
+++ linux/Documentation/devices.tex	Mon May  6 08:35:53 1996
@@ -42,7 +42,7 @@
 %
 \title{{\bf Linux Allocated Devices}}
 \author{Maintained by H. Peter Anvin $<$hpa@zytor.com$>$}
-\date{Last revised: April 20, 1996}
+\date{Last revised: May 5, 1996}
 \maketitle
 %
 \noindent
@@ -156,6 +156,7 @@
 \major{39}{}{char }{ML-16P experimental I/O board}
 \major{  }{}{block}{Reserved for Linux/AP+}
 \major{40}{}{char }{Matrox Meteor frame grabber}
+\major{  }{}{block}{Syquest EZ135 removable drive}
 \major{41}{}{char }{Yet Another Micro Monitor}
 \major{42}{}{}{Demo/sample use}
 \major{43}{}{char }{isdn4linux virtual modem}
@@ -165,7 +166,8 @@
 \major{47}{}{char }{Comtrol Rocketport serial card -- alternate devices}
 \major{48}{}{char }{SDL RISCom serial card}
 \major{49}{}{char }{SDL RISCom serial card -- alternate devices}
-\major{50}{--59}{}{Unallocated}
+\major{50}{}{char }{Reserved for GLINT}
+\major{51}{--59}{}{Unallocated}
 \major{60}{--63}{}{Local/experimental use}
 \major{64}{--119}{}{Unallocated}
 \major{120}{--127}{}{Local/experimental use}
@@ -942,7 +944,14 @@
 \begin{devicelist}
 \major{40}{}{char }{Matrox Meteor frame grabber}
 	\minor{0}{/dev/mmetfgrab}{Matrox Meteor frame grabber}
+\\
+\major{  }{}{block}{Syquest EZ135 removable drive}
+	\minor{0}{/dev/eza}{First EZ135 drive whole disk}
 \end{devicelist}
+
+\noindent
+Partitions are handled the same way as for IDE disks (see major number
+3).
 
 \begin{devicelist}
 \major{41}{}{char }{Yet Another Micro Monitor}
diff -u --recursive --new-file v1.3.98/linux/Documentation/devices.txt linux/Documentation/devices.txt
--- v1.3.98/linux/Documentation/devices.txt	Wed Apr 24 17:00:33 1996
+++ linux/Documentation/devices.txt	Mon May  6 08:35:53 1996
@@ -2,7 +2,7 @@
 
 	     Maintained by H. Peter Anvin <hpa@zytor.com>
 
-		     Last revised: April 20, 1996
+		      Last revised: May 5, 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
@@ -621,6 +621,8 @@
 		and "user level packet I/O."  This board is also
 		accessible as a standard networking "eth" device.
 
+    block	Reserved for Linux/AP+
+
  39 char	ML-16P experimental I/O board
 		  0 = /dev/ml16pa-a0	First card, first analog channel
 		  1 = /dev/ml16pa-a1	First card, second analog channel
@@ -643,7 +645,11 @@
 
  40 char	Matrox Meteor frame grabber
 		  0 = /dev/mmetfgrab	Matrox Meteor frame grabber
-    block	Reserved for Linux/AP+
+    block	Syquest EZ135 removable drive
+		  0 = /dev/eza		First EZ135 drive, whole disk
+
+		Partitions are handled in the same way as IDE disks
+		(see major number 3).
 
  41 char	Yet Another Micro Monitor
 		  0 = /dev/yamm		Yet Another Micro Monitor
diff -u --recursive --new-file v1.3.98/linux/Documentation/filesystems/affs.txt linux/Documentation/filesystems/affs.txt
--- v1.3.98/linux/Documentation/filesystems/affs.txt	Sat Apr 27 15:19:44 1996
+++ linux/Documentation/filesystems/affs.txt	Mon May  6 12:44:30 1996
@@ -1,3 +1,109 @@
+Amiga filesystems Overview
+==========================
+
+Not all varieties of the Amiga filesystems are supported.  The Amiga
+currently knows 6 different filesystems:
+
+DOS\0		The old or original filesystem, not really suited for
+		hard disks and normally not used on them, either.
+		Not supported.
+
+DOS\1		The original Fast File System. Supported.
+
+DOS\2		The old "international" filesystem. International means that
+		a bug has been fixed so that accented ("international") letters
+		in file names are case-insensitive, as they ought to be.
+		Not supported.
+
+DOS\3		The "international" Fast File System.  Supported.
+
+DOS\4		The original filesystem with directory cache. The directory
+		cache speeds up directory accesses on floppies considerably,
+		but slowes down file creation/deletion. Doesn't make much
+		sense on hard disks. Not supported.
+
+DOS\5		The Fast File System with directory cache. Not supported.
+
+All of the above filesystems allow block sizes from 512 to 32K bytes.
+Supported block sizes are: 512, 1024, 2048 and 4096 bytes. Larger blocks
+speed up almost everything with the expense of wasted disk space. The speed
+gain above 4K seems not really worth the price, so you don't lose too
+much here, either.
+  
+Mount options for the AFFS
+==========================
+
+protect		If this option is set, the protection bits cannot be altered.
+
+uid[=uid]	This sets the uid of the root directory (i. e. the mount point
+		to uid or to the uid of the current user, if the =uid is
+		ommitted.
+
+gid[=gid]	Same as above, but for gid.
+
+setuid[=uid]	This sets the owner of all files and directories in the file
+		system to uid or the uid of the current user, respectively.
+
+setgid[=gid]	Same as above, but for gid.
+
+use_mp		The uid and gid are taken from the now covered mount point
+		instead of the current user or value defined.
+
+mode=mode	Sets the mode flags to the given (octal) value, regardles
+		of the original permissions. Directories will get an x
+		permission, if the corresponding r bit is set.
+		This is useful since most of the plain AmigaOS files
+		will map to 600.
+
+reserved=num	Sets the number of reserved blocks at the start of the
+		partition to num. Default is 2.
+
+root=block	Sets the block number of the root block. This schould never
+		be neccessary.
+
+bs=blksize	Sets the blocksize to blksize. Valid block sizes are 512,
+		1024, 2048 and 4096. Like the root option, this should
+		never be neccessary, as the affs can figure it out itself.
+
+quiet		The file system will not return an error for disallowed
+		mode changes.
+
+Handling of the Users/Groups and protection flags
+=================================================
+
+Amiga -> Linux:
+
+The Amiga protection flags RWEDRWEDHSPARWED are handled as follows:
+
+  - R maps to r for user, group and others. On directories, R implies x.
+
+  - If both W and D are allowed, w will be set.
+
+  - If both R and S are set, x will be set.
+
+  - H, P and E are always retained and ignored under Linux.
+
+  - A is always reset when written.
+
+User id and group id will be used unless set[gu]id are given as mount
+options. Since most of the Amiga file systems are single user systems
+they will be owned by root.
+
+Linux -> Amiga:
+
+The Linux rwxrwxrwx file mode is handled as follows:
+
+  - r permission will set R for user, group and others.
+
+  - w permission will set W and D for user, group and others.
+
+  - x permission of the user will set S for plain files.
+
+  - All other flags (suid, sgid, ...) are ignored and will
+    not be retained.
+    
+Newly created files and directories will get the user and group id
+of the current user and a mode according to the umask.
 Linux can read, but not write, Amiga FFS partitions.
 
 Mount options are
diff -u --recursive --new-file v1.3.98/linux/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- v1.3.98/linux/Documentation/ioctl-number.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/ioctl-number.txt	Tue May  7 07:50:53 1996
@@ -0,0 +1,98 @@
+Ioctl Numbers
+6 May 1996
+Michael Chastain
+<mec@duracef.shout.net>
+
+If you are adding new ioctl's to the kernel, you should use the _IO
+macros defined in <linux/ioctl.h>:
+
+    _IO    an ioctl with no parameters
+    _IOW   an ioctl with write parameters (from user's point of view)
+    _IOR   an ioctl with read parameters (from user's point of view)
+    _IOWR  an ioctl with both write and read parameters.
+
+'Write' and 'read' are from the user's point of view.  This is like the
+system calls 'write' and 'read'.  For example, a SET_FOO ioctl would be
+_IOW, although the kernel would actually read data from user space; a
+GET_FOO ioctl would be _IOR, although the kernel would actually write
+data to user space.
+
+The first argument to _IO, _IOW, _IOR, or _IOWR is an identifying letter
+or number from the table below.  If you are writing a driver for a new
+device and need a letter, pick an unused letter.  You can register the
+letter by patching this file and submitting the patch to Linus Torvalds.
+Or you can e-mail me at <mec@duracef.shout.net> and I'll register one
+for you.
+
+The second argument to _IO, _IOW, _IOR, or _IOWR is a sequence number
+to distinguish ioctls from each other.  The third argument is a size
+of the structure going into the kernel or coming out of the kernel.
+
+Some devices use their major number as the identifier; this is not
+recommended.  Some devices are even more irregular and don't follow
+the convention at all.
+
+Following the convention is good because:
+
+(1) Keeping the ioctl's globally unique helps error checking:
+    if a program calls an ioctl on the wrong device, it will get an
+    error rather than some unexpected behaviour.
+
+(2) The 'strace' build procedure automatically finds ioctl numbers
+    defined with _IO, _IOW, _IOR, or _IOWR.
+
+(3) 'strace' can decode numbers back into useful names when the
+    numbers are unique.
+
+(4) People looking for ioctls can grep for them more easily when
+    the convention is used to define the ioctl numbers.
+
+(5) When following the convention, the driver code can use generic
+    code to call verify_area to validate parameters.
+
+This table is current to Linux 1.3.98.
+
+Ioctl	Include File		Comments
+========================================================
+0x00	linux/fs.h		only FIBMAP, FIGETBSZ
+0x00	linux/random.h		codes in 0x010800NN
+0x00	linux/mc146818rtc.h	conflict!
+0x02	linux/fd.h
+0x03	linux/hdreg.h
+0x04	linux/umsdos_fs.h
+0x06	linux/lp.h
+0x09	linux/md.h
+0x12	linux/fs.h
+0x20	linux/cm206.h
+0x22	linux/scc.h		conflict!
+0x22	scsi/sg.h		conflict!
+'A'	linux/apm_bios.h
+'C'	linux/soundcard.h
+'F'	linux/fb.h
+'I'	linux/isdn.h
+'K'	linux/kd.h
+'L'	linux/loop.h
+'M'	linux/soundcard.h
+'P'	linux/soundcard.h
+'Q'	linux/soundcard.h
+'S'	linux/cdrom.h		conflict!
+'S'	scsi/scsi.h		conflict!
+'S'	scsi/scsi_ioctl.h	conflict!
+'T'	linux/soundcard.h	conflict!
+'T'	asm/ioctls.h		conflict!
+'V'	linux/vt.h
+'Y'	linux/cyclades.h	codes in 0x004359NN
+'a'	various, see http://lrcwww.epfl.ch/linux-atm/magic.html
+'c'	linux/comstats.h
+'f'	linux/ext2_fs.h
+'m'	linux/mtio.h		conflict!
+'m'	linux/soundcard.h	conflict!
+'n'	linux/ncp_fs.h
+'r'	linux/msdos_fs.h
+'s'	linux/cdk.h
+'t'	linux/if_ppp.h		no conflict
+'t'	linux/isdn_ppp.h	no conflict
+'u'	linux/smb_fs.h
+'v'	linux/ext2_fs.h
+0x89	asm/sockios.h		no conflict
+0x89	linux/sockios.h		no conflict
diff -u --recursive --new-file v1.3.98/linux/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE
--- v1.3.98/linux/Documentation/isdn/INTERFACE	Fri Apr 12 15:51:44 1996
+++ linux/Documentation/isdn/INTERFACE	Mon May  6 12:26:01 1996
@@ -48,7 +48,7 @@
 
     To be preset by the HL-driver, if it supports sk_buff's. The driver
     should put here the amount of additional space needed in sk-buff's for
-    it's internal purposes. Drivers not supporting sk_buff's should put
+    its internal purposes. Drivers not supporting sk_buff's should put
     initialize this field to 0.
 
   void (*rcvcallb)(int, int, u_char*, int);
@@ -68,7 +68,7 @@
 
     This field will be set by LL. The HL-driver delivers received data-
     packets by calling this function. Upon calling, the HL-driver must
-    already have it's private data pulled off the head of the sk_buff.
+    already have its private data pulled off the head of the sk_buff.
 
     Parameter:
       int              driver-Id
@@ -83,8 +83,8 @@
     Parameter:
       isdn_ctrl*
 
-      The struct isdn_ctrl also defined in isdn_if. The exact meaning of it's
-      fields is described together with the descriptions of the possible
+      The struct isdn_ctrl also defined in isdn_if. The exact meanings of its
+      fields are described together with the descriptions of the possible
       events. Here is only a short description of the fields:
 
         driver  = driver Id.
@@ -196,7 +196,7 @@
   char id[20];
        ***CHANGE0.7: New since this version.
 
-   This string has to be preset by the HL-driver. It's purpose is for
+   This string has to be preset by the HL-driver. Its purpose is for
    identification of the driver by the user. Eg.: it is shown in the
    status-info of /dev/isdninfo. Furthermore it is used as Id for binding
    net-interfaces to a specific channel. If a string of length zero is
diff -u --recursive --new-file v1.3.98/linux/Documentation/isdn/README linux/Documentation/isdn/README
--- v1.3.98/linux/Documentation/isdn/README	Sun Apr 21 19:21:59 1996
+++ linux/Documentation/isdn/README	Mon May  6 12:26:01 1996
@@ -308,7 +308,7 @@
      3.2.1 Teles driver.
 
        The module teles.o can be configured during "insmod'ing" it by
-       appending it's parameters to the insmod-commandline. The following
+       appending its parameters to the insmod-commandline. The following
        syntax is accepted:
 
        io=m0,i0,p0,d0[,m1,i1,p1,d1 ... ,mn,in,pn,dn] teles_id=idstring
@@ -323,7 +323,7 @@
      3.2.2 ICN driver.
 
        The module icn.o can be configured during "insmod'ing" it by
-       appending it's parameters to the insmod-commandline. The following
+       appending its parameters to the insmod-commandline. The following
        syntax is accepted:
 
        portbase=p membase=m icn_id=idstring icn_id2=idstring2
@@ -347,7 +347,7 @@
 
 4. Device-inodes
 
-   The major and minor-numbers and it's names are described in
+   The major and minor-numbers and its names are described in
    Documentation/devices.txt. The major-numbers are:
 
      43 for the ISDN-tty's.
@@ -494,7 +494,7 @@
    cisco-h  A special-mode for communicating with a Cisco, which is configured
             to do "hdlc"
    ethernet No stripping. Packets are sent with full MAC-header.
-            The Ethernet-address of the interface is faked, from it's
+            The Ethernet-address of the interface is faked, from its
             IP-address: fc:fc:i1:i2:i3:i4, where i1-4 are the IP-addr.-values.
    syncppp  Synchronous PPP
 
@@ -553,7 +553,7 @@
 
    "isdnctrl sdelay <InterfaceName> secs."
    This sets the minimum time an Interface has to be fully loaded, until
-   it sends an dial-request to it's slave.
+   it sends a dial-request to its slave.
 
    "isdnctrl dial <InterfaceName>"
    Forces an interface to start dialing even if no packets are to be
diff -u --recursive --new-file v1.3.98/linux/Documentation/magic-number.txt linux/Documentation/magic-number.txt
--- v1.3.98/linux/Documentation/magic-number.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/magic-number.txt	Tue May  7 07:50:53 1996
@@ -0,0 +1,61 @@
+This file is a registry of magic numbers which are in use.  When you
+add a magic number to a structure, you should also add it to this
+file, since it is best if the magic numbers used by various structures
+are unique.
+
+It is a *very* good idea to protect kernel data structures with magic
+numbers.  This allows you to check at run time whether (a) a structure
+has been clobbered, or (b) you've passed the wrong structure to a
+routine.  This last is especially useful --- particularly when you are
+passing pointers to structures via a void * pointer.  The tty code,
+for example, does this frequently to pass driver-specific and line
+discipline-specific structures back and forth.
+
+The way to use magic numbers is to declare then at the beginning of
+the structure, like so:
+
+struct tty_ldisc {
+	int	magic;
+	...
+};
+
+Please follow this discipline when you are adding future enhancements
+to the kernel!  It has saved me countless hours of debugging,
+especially in the screw cases where an array has been overrun and
+structures following the array have been overwritten.  Using this
+discipline, these cases get detected quickly and safely.
+
+					Theodore Ts'o
+					31-Mar-94
+
+The magic table is current to Linux 1.3.98.
+
+					Michael Chastain
+					<mec@duracef.shout.net>
+					6 May 1996
+
+
+
+Magic Name          Number      Structure            File
+===========================================================================
+RISCOM8_MAGIC       0x0907      struct riscom_port   drivers/char/riscom8.h
+APM_BIOS_MAGIC      0x4101      struct apm_struct    include/linux/apm_bios.h
+CYCLADES_MAGIC      0x4359      struct cyclades_port include/linux/cyclades.h
+FASYNC_MAGIC        0x4601      struct fasync_struct include/linux/fs.h
+PTY_MAGIC           0x5001      struct pty_struct    drivers/char/pty.c
+PPP_MAGIC           0x5002      struct ppp_struct    include/linux/if_ppp.h
+SERIAL_MAGIC        0x5301      struct async_struct  include/linux/serial.h
+SLIP_MAGIC          0x5302      struct slip          drivers/net/slip.h
+STRIP_MAGIC         0x5303      struct strip         drivers/net/strip.c
+TTY_MAGIC           0x5401      struct tty_struct    include/linux/tty.h
+TTY_DRIVER_MAGIC    0x5402      struct tty_driver    include/linux/tty_driver.h
+TTY_LDISC_MAGIC     0x5403      struct tty_ldisc     include/linux/tty_ldisc.h
+SCC_MAGIC           0x8530      struct scc_channel   include/linux/scc.h
+ISDN_ASYNC_MAGIC    0x49344C01  modem_info           include/linux/isdn.h
+ISDN_NET_MAGIC      0x49344C02  isdn_net_ndev        include/linux/isdn.h
+STLI_BOARDMAGIC     0x4bc6c825  stlibrd_t            include/linux/istallion.h
+STLI_PORTMAGIC      0xe671c7a1  stliport_t           include/linux/istallion.h
+STL_PANELMAGIC      0x7ef621a1  stlpanel_t           include/linux/stallion.h
+STL_BOARDMAGIC      0xa2267f52  stlbrd_t             include/linux/stallion.h
+STL_PORTMAGIC       0x5a7182c9  stlport_t            include/linux/stallion.h
+PCXX_MAGIC          0x5c6df104  struct channel       drivers/char/pcxx.h
diff -u --recursive --new-file v1.3.98/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt
--- v1.3.98/linux/Documentation/networking/arcnet-hardware.txt	Fri Apr 12 15:51:44 1996
+++ linux/Documentation/networking/arcnet-hardware.txt	Mon May  6 12:26:01 1996
@@ -140,7 +140,7 @@
 5. An active hub to passive hub.
 
 Remember, that you can not connect two passive hubs together.  The power loss
-implied by such connection is too high for the net to operate reliably.
+implied by such a connection is too high for the net to operate reliably.
 
 An example of a typical ARCnet network:
 
@@ -2256,9 +2256,9 @@
 function, and the 8th switch is used to select "compatible" or "enhanced".
 When I got my two cards, one of them had this switch set to "enhanced". That
 card didn't work at all, it wasn't even recognized by the driver. The other
-card had this switch set to "compatible" and it behaved absolutely normal. I
+card had this switch set to "compatible" and it behaved absolutely normally. I
 guess that the switch on one of the cards, must have been changed accidently
-when the card was taken out of it's former host. The question remains
+when the card was taken out of its former host. The question remains
 unanswered, what is the purpose of the "enhanced" position?
 
 [Avery's note: "enhanced" probably either disables shared memory (use IO
diff -u --recursive --new-file v1.3.98/linux/Documentation/networking/framerelay.txt linux/Documentation/networking/framerelay.txt
--- v1.3.98/linux/Documentation/networking/framerelay.txt	Fri Apr 12 15:51:44 1996
+++ linux/Documentation/networking/framerelay.txt	Mon May  6 12:26:01 1996
@@ -1,6 +1,6 @@
 Frame Relay (FR) support for linux is built into a two tiered system of device 
 drivers.  The upper layer implements RFC1490 FR specification, and uses the
-Data Link Connection Identifier (DLCI) as it's hardware address.  Usually these
+Data Link Connection Identifier (DLCI) as its hardware address.  Usually these
 are assigned by your network supplier, they give you the number/numbers of
 the Virtual Connections (VC) assigned to you.
 
diff -u --recursive --new-file v1.3.98/linux/Documentation/nfsroot.txt linux/Documentation/nfsroot.txt
--- v1.3.98/linux/Documentation/nfsroot.txt	Fri Apr 12 15:51:44 1996
+++ linux/Documentation/nfsroot.txt	Mon May  6 12:26:01 1996
@@ -160,7 +160,7 @@
 	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
+	RARP will mount the directory /tftpboot/<client-ip>/ as its
 	root. If it got a BOOTP answer the directory name in that answer
 	is used.
 
@@ -169,8 +169,8 @@
 	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
+	set up a dummy device as described in 3.1 above. For how to use
+	LILO and its 'append=' command please refer to the LILO
 	documentation.
 
 3.3) Using loadlin
@@ -187,7 +187,7 @@
 	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'.
+	and its mirrors. They are called 'netboot-nfs' and 'etherboot'.
 	Both contain everything you need to boot a diskless Linux client.
 
 
diff -u --recursive --new-file v1.3.98/linux/Documentation/oops-tracing.txt linux/Documentation/oops-tracing.txt
--- v1.3.98/linux/Documentation/oops-tracing.txt	Sun May  5 08:51:57 1996
+++ linux/Documentation/oops-tracing.txt	Sun May  5 08:51:13 1996
@@ -19,7 +19,7 @@
 Oh, it helps if the report happens on a kernel that is compiled with the 
 same compiler and similar setups.
 
-The other thing to do is disassemble the "Code:" part of the bugreprot: 
+The other thing to do is disassemble the "Code:" part of the bug report: 
 ksymoops will do this too with the correct tools (and new version of 
 ksymoops), but if you don't have the tools you can just do a silly 
 program:
@@ -30,7 +30,7 @@
 and compile it with gcc -g and then do "disassemble str" (where the "XX" 
 stuff are the values reported by the Oops - you can just cut-and-paste 
 and do a replace of spaces to "\x" - that's what I do, as I'm too lazy 
-to write a prigram to automate this all).
+to write a program to automate this all).
 
 Finally, if you want to see where the code comes from, you can do
 
diff -u --recursive --new-file v1.3.98/linux/Documentation/ramdisk.txt linux/Documentation/ramdisk.txt
--- v1.3.98/linux/Documentation/ramdisk.txt	Sun May  5 08:51:57 1996
+++ linux/Documentation/ramdisk.txt	Mon May  6 12:26:01 1996
@@ -79,7 +79,7 @@
 	load_ramdisk=N
 	==============
 
-This parameter tells the kernel whether it is to try and load a
+This parameter tells the kernel whether it is to try to load a
 ramdisk image or not. Specifying "load_ramdisk=1" will tell the
 kernel to load a floppy into the ramdisk. The default value is
 zero, meaning that the kernel should not try to load a ramdisk.
@@ -194,7 +194,7 @@
 	dd if=/tmp/ram_image.gz of=/dev/fd0 bs=1k seek=400
 
 g) Use "rdev" to set the boot device, ramdisk offset, prompt flag, etc.
-   For ramdisk_start=400, load_ramdisk=1, ramdisk_start=400, one would
+   For prompt_ramdisk=1, load_ramdisk=1, ramdisk_start=400, one would
    have 2^15 + 2^14 + 400 = 49552.
 
 	rdev /dev/fd0 /dev/fd0
diff -u --recursive --new-file v1.3.98/linux/Documentation/rtc.txt linux/Documentation/rtc.txt
--- v1.3.98/linux/Documentation/rtc.txt	Sun May  5 08:51:57 1996
+++ linux/Documentation/rtc.txt	Tue May  7 08:18:50 1996
@@ -9,7 +9,7 @@
 
 However it can also be used to generate signals from a slow 2Hz to a
 relatively fast 8192Hz, in increments of powers of two. These signals
-are reported by interrupt number 8. (Oh! So *thats* what IRQ 8 is
+are reported by interrupt number 8. (Oh! So *that* is what IRQ 8 is
 for...) It can also function as a 24hr alarm, raising IRQ 8 when the
 alarm goes off. The alarm can also be programmed to only check any
 subset of the three programmable values, meaning that it could be set to
@@ -88,13 +88,12 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
-#include <time.h>
 
 void main(void) {
 
 int i, fd, retval, irqcount = 0;
 unsigned long tmp, data;
-struct tm rtc_tm;
+struct rtc_time rtc_tm;
 
 fd = open ("/dev/rtc", O_RDONLY);
 
diff -u --recursive --new-file v1.3.98/linux/MAGIC linux/MAGIC
--- v1.3.98/linux/MAGIC	Tue Apr 23 13:57:01 1996
+++ linux/MAGIC	Thu Jan  1 02:00:00 1970
@@ -1,105 +0,0 @@
-This file is a registry of magic numbers which are in use.  When you
-add a magic number to a structure, you should also add it to this
-file, since it is best if the magic numbers used by various structures
-are unique.
-
-It is a *very* good idea to protect kernel data structures with magic
-numbers.  This allows you to check at run time whether (a) a structure
-has been clobbered, or (b) you've passed the wrong structure to a
-routine.  This last is especially useful --- particularly when you are
-passing pointers to structures via a void * pointer.  The tty code,
-for example, does this frequently to pass driver-specific and line
-discipline-specific structures back and forth.
-
-The way to use magic numbers is to declare then at the beginning of
-the structure, like so:
-
-struct tty_ldisc {
-	int	magic;
-	...
-};
-
-Please follow this discipline when you are adding future enhancements
-to the kernel!  It has saved me countless hours of debugging,
-especially in the screw cases where an array has been overrun and
-structures following the array have been overwritten.  Using this
-discipline, these cases get detected quickly and safely.
-
-You should also register the magic number used by ioctls in the table
-below.  This allows ioctls on inappropriate devices to fail, rather than
-doing something completely unexpected.  The magic number is the first
-argument to the _IO family of macros (see include/linux/ioctl.h) or
-in general bits 8..15 of the ioctl number.  Where ioctls are done on
-devices with a major device number, it is recommended that you use the
-major device number as the ioctl magic value (e.g. hd, lp).
-
-					Theodore Ts'o
-					31-Mar-94
-
-The magic table is current to Linux 1.3.35.
-The ioctl table is current to Linux 1.3.35.
-For a complete list of kernel ioctl's, look for 'ioctl-list.X.Y.ZZ' on
-an ftp site, where 'X.Y.ZZ' is the kernel version for the ioctl list.
-
-					Michael Chastain
-					<mec@duracef.shout.net>
-					17-Oct-95
-
-
-
-
-Magic Name          Number  Structure            File
-===========================================================================
-APM_BIOS_MAGIC      0x4101  struct apm_struct    include/linux/apm_bios.h
-CYCLADES_MAGIC      0x4359  struct cyclades_port include/linux/cyclades.h
-FASYNC_MAGIC        0x4601  struct fasync_struct include/linux/fs.h
-PTY_MAGIC           0x5001  struct pty_struct    drivers/char/pty.c
-PPP_MAGIC           0x5002  struct ppp_struct    include/linux/if_ppp.h
-TTY_MAGIC           0x5401  struct tty_struct    include/linux/tty.h
-TTY_DRIVER_MAGIC    0x5402  struct tty_driver    include/linux/tty_driver.h
-TTY_LDISC_MAGIC     0x5403  struct tty_ldisc     include/linux/tty_ldisc.h
-SERIAL_MAGIC        0x5301  struct async_struct  include/linux/serial.h
-SLIP_MAGIC          0x5302  struct slip          drivers/net/slip.h
-SCC_MAGIC           0x8530  struct scc_channel   include/linux/scc.h
-RISCOM8_MAGIC       0x0907  struct riscom_port	 drivers/char/riscom8.h
-
-
-
-Ioctl	Include File		Comments
-========================================================
-0x00	linux/fs.h		only FIBMAP, FIGETBSZ
-0x00	linux/random.h		codes in 0x010800NN
-0x02	linux/fd.h
-0x03	linux/hdreg.h
-0x04	linux/umsdos_fs.h
-0x06	linux/lp.h
-0x09	linux/md.h
-0x12	linux/fs.h
-0x20	linux/cm206.h
-0x22	linux/scc.h
-'A'	linux/apm_bios.h
-'C'	linux/soundcard.h
-'I'	linux/isdn.h
-'K'	linux/kd.h
-'L'	linux/loop.h
-'M'	linux/soundcard.h
-'P'	linux/soundcard.h
-'Q'	linux/soundcard.h
-'S'	linux/cdrom.h		conflict!
-'S'	linux/scsi.h		conflict!
-'T'	linux/soundcard.h	conflict!
-'T'	asm/termios.h		conflict!
-'V'	linux/vt.h
-'Y'	linux/cyclades.h	codes in 0x004359NN
-'a'	various, see http://lrcwww.epfl.ch/linux-atm/magic.html
-'f'	linux/ext2_fs.h
-'m'	linux/mtio.h		conflict!
-'m'	linux/soundcard.h	conflict!
-'n'	linux/ncp_fs.h
-'s'	linux/cdk.h
-'t'	linux/if_ppp.h
-'u'	linux/smb_fs.h
-'u'	linux/smb_fs.h
-'v'	linux/ext2_fs.h
-0x89	asm/socket.h		no conflict
-0x89	linux/sockios.h		no conflict
diff -u --recursive --new-file v1.3.98/linux/MAINTAINERS linux/MAINTAINERS
--- v1.3.98/linux/MAINTAINERS	Sun May  5 08:51:57 1996
+++ linux/MAINTAINERS	Tue May  7 12:06:49 1996
@@ -7,13 +7,13 @@
 1.	Always _test_ your changes, however small, on at least 4 or
 	5 people, preferably many more.
 
-2.	Try and release a few ALPHA test versions to the net. Announce
+2.	Try to release a few ALPHA test versions to the net. Announce
 	them onto the kernel channel and await results. This is especially
-	important for device drivers, because often thats the only way
+	important for device drivers, because often that's the only way
 	you will find things like the fact version 3 firmware needs
 	a magic fix you didn't know about, or some clown changed the
 	chips on a board and not its name.  (Don't laugh!  Look at the
-	SMC etherpower for that).
+	SMC etherpower for that.)
 
 3.	Make sure your changes compile correctly in multiple
 	configurations.
@@ -30,7 +30,7 @@
 	your driver to get around a problem actual needs to become a
 	generalised kernel feature ready for next time.
 
-	PLEASE try and include any credit lines you want added with the
+	PLEASE try to include any credit lines you want added with the
 	patch. It avoids people being missed off by mistake and makes
 	it easier to know who wants adding and who doesn't.
 
@@ -74,6 +74,12 @@
 L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
+8390 NETWORK DRIVERS [WD80x3/SMC-ELITE, SMC-ULTRA, NE2000, 3C503, etc.]
+P:	Paul Gortmaker
+M	gpg109@rsphy1.anu.edu.au
+L:	linux-net@vger.rutgers.edu
+S:	Maintained
+
 ETHEREXPRESS NETWORK DRIVER
 P:	Philip Blundell
 M:	pjb27@cam.ac.uk
@@ -226,6 +232,12 @@
 L:	linux-ppp@vger.rutgers.edu
 S:	Maintained
 
+REAL TIME CLOCK DRIVER
+P:	Paul Gortmaker
+M	gpg109@rsphy1.anu.edu.au
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
 P:     Stuart Cheshire
 M:     cheshire@cs.stanford.edu
@@ -286,6 +298,12 @@
 P:	William Roadcap
 M:	roadcapw@cfw.com
 L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
+LANCE AND LANCE32 NETWORK DRIVER
+P:	Thomas Bogendoerfer
+M:	tsbogend@bigbug.franken.de
+L:	linux-net@vger.rutgers.edu
 S:	Maintained
 
 REST:
diff -u --recursive --new-file v1.3.98/linux/Makefile linux/Makefile
--- v1.3.98/linux/Makefile	Sun May  5 08:51:57 1996
+++ linux/Makefile	Sun May  5 08:51:39 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 98
+SUBLEVEL = 99
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.98/linux/README linux/README
--- v1.3.98/linux/README	Wed Apr 24 17:00:33 1996
+++ linux/README	Mon May  6 07:28:51 1996
@@ -24,9 +24,9 @@
 
 WHAT IS LINUX?
 
-  Linux is a Unix clone for 386/486-based PCs written from scratch by
-  Linus Torvalds with assistance from a loosely-knit team of hackers
-  across the Net.  It aims towards POSIX compliance. 
+  Linux is a Unix clone written from scratch by Linus Torvalds with
+  assistance from a loosely-knit team of hackers across the Net.
+  It aims towards POSIX compliance. 
 
   It has all the features you would expect in a modern fully-fledged
   Unix, including true multitasking, virtual memory, shared libraries,
@@ -35,6 +35,12 @@
 
   It is distributed under the GNU General Public License - see the
   accompanying COPYING file for more details. 
+
+ON WHAT HARDWARE DOES IT RUN?
+
+  Linux was first developed for 386/486-based PCs.  These days it also
+  runs on DEC Alphas, SUN Sparcs, M68000 machines (like Atari and Amiga),
+  MIPS and PowerPC.
 
 DOCUMENTATION:
 
diff -u --recursive --new-file v1.3.98/linux/arch/alpha/config.in linux/arch/alpha/config.in
--- v1.3.98/linux/arch/alpha/config.in	Sun May  5 08:51:58 1996
+++ linux/arch/alpha/config.in	Sun May  5 09:05:58 1996
@@ -82,7 +82,9 @@
 bool 'Echo console messages on /dev/ttyS1' CONFIG_SERIAL_ECHO
 if [ "$CONFIG_PCI" = "y" ]; then
   bool 'TGA Console Support' CONFIG_TGA_CONSOLE
-  bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
+  fi
 fi
 bool 'Networking support' CONFIG_NET
 bool 'System V IPC' CONFIG_SYSVIPC
diff -u --recursive --new-file v1.3.98/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c
--- v1.3.98/linux/arch/alpha/kernel/bios32.c	Sun Apr 21 12:39:00 1996
+++ linux/arch/alpha/kernel/bios32.c	Mon May  6 12:26:02 1996
@@ -442,7 +442,7 @@
 }
 
 /*
- * The SRM console *disables* the IDE interface, this code ensures its
+ * The SRM console *disables* the IDE interface, this code ensures it's
  * enabled.
  *
  * This code bangs on a control register of the 87312 Super I/O chip
@@ -565,7 +565,7 @@
 						  PCI_INTERRUPT_LINE, dev->irq);
 #endif
 			/*
-			 * if its a VGA, enable its BIOS ROM at C0000
+			 * if it's a VGA, enable its BIOS ROM at C0000
 			 */
 			if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
 			  pcibios_write_config_dword(dev->bus->number, dev->devfn,
@@ -799,7 +799,7 @@
 			  dev->bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device,
 			  slot, pin, pirq));
 		/*
-		 * if its a VGA, enable its BIOS ROM at C0000
+		 * if it's a VGA, enable its BIOS ROM at C0000
 		 */
 		if ((dev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
 			pcibios_write_config_dword(dev->bus->number, dev->devfn,
diff -u --recursive --new-file v1.3.98/linux/arch/i386/boot/Makefile linux/arch/i386/boot/Makefile
--- v1.3.98/linux/arch/i386/boot/Makefile	Sat Mar 16 13:52:09 1996
+++ linux/arch/i386/boot/Makefile	Sun May  5 09:05:58 1996
@@ -90,6 +90,7 @@
 
 clean:
 	rm -f bootsect setup
+	rm -f bbootsect
 	rm -f zImage tools/build compressed/vmlinux.out
 	rm -f bzImage tools/bbuild compressed/bvmlinux.out
 	@$(MAKE) -C compressed clean
diff -u --recursive --new-file v1.3.98/linux/arch/i386/config.in linux/arch/i386/config.in
--- v1.3.98/linux/arch/i386/config.in	Sun May  5 08:51:58 1996
+++ linux/arch/i386/config.in	Tue May  7 12:06:50 1996
@@ -26,14 +26,15 @@
 bool 'Limit memory to low 16MB' CONFIG_MAX_16M
 bool 'PCI bios support' CONFIG_PCI
 if [ "$CONFIG_PCI" = "y" ]; then
-  bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
+  fi
 fi
 bool 'System V IPC' CONFIG_SYSVIPC
 tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
-if [ "$CONFIG_BINFMT_ELF" = "y" ]; then
-  bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF
-fi
+bool 'Compile kernel as ELF - if your GCC is ELF-GCC' CONFIG_KERNEL_ELF
+
 choice 'Processor type' \
 	"386		CONFIG_M386	\
 	 486		CONFIG_M486	\
diff -u --recursive --new-file v1.3.98/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v1.3.98/linux/arch/i386/kernel/irq.c	Fri Apr 12 15:51:46 1996
+++ linux/arch/i386/kernel/irq.c	Mon May  6 12:26:02 1996
@@ -370,7 +370,7 @@
 {
 	struct irqaction * action = *(irq + irq_action);
 #ifdef __SMP__
-	/* IRQ 13 is allowed - thats a flush tlb */
+	/* IRQ 13 is allowed - that's a flush tlb */
 	if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13)
 		panic("fast_IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
 #endif
diff -u --recursive --new-file v1.3.98/linux/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- v1.3.98/linux/arch/i386/kernel/setup.c	Mon Apr 15 12:20:17 1996
+++ linux/arch/i386/kernel/setup.c	Mon May  6 07:05:57 1996
@@ -207,7 +207,8 @@
 static const char * i486model(unsigned int nr)
 {
 	static const char *model[] = {
-		"0", "DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB"
+		"0","DX","SX","DX/2","4","SX/2","6","DX/2-WB","DX/4","DX/4-WB",
+		"10","11","12","13","Am5x85-WT","Am5x86-WB"
 	};
 	if (nr < sizeof(model)/sizeof(char *))
 		return model[nr];
@@ -325,7 +326,7 @@
                                 }
                         }
                         len += sprintf(buffer+len,
-                                       "\nbogomips:\t: %lu.%02lu\n",
+                                       "\nbogomips\t: %lu.%02lu\n",
                                        CD(loops_per_sec)/500000,
                                        (CD(loops_per_sec)/5000) % 100);
 #ifdef __SMP__
diff -u --recursive --new-file v1.3.98/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v1.3.98/linux/arch/i386/kernel/signal.c	Mon Apr 29 18:05:15 1996
+++ linux/arch/i386/kernel/signal.c	Mon May  6 16:31:18 1996
@@ -43,11 +43,6 @@
 	}
 }
 
-/*
- * FIXME. We don't currently restore emulator state
- */
-#define restore_i387_soft(x) do { } while (0)
-
 static inline void restore_i387_hard(struct _fpstate *buf)
 {
 #ifdef __SMP__
@@ -121,11 +116,6 @@
 badframe:
 	do_exit(SIGSEGV);
 }
-
-/*
- * FIXME. We currently don't save 387 state if we use emulation
- */
-#define save_i387_soft(x) NULL
 
 static inline struct _fpstate * save_i387_hard(struct _fpstate * buf)
 {
diff -u --recursive --new-file v1.3.98/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c
--- v1.3.98/linux/arch/i386/kernel/smp.c	Fri Apr 12 15:51:46 1996
+++ linux/arch/i386/kernel/smp.c	Mon May  6 12:26:02 1996
@@ -56,7 +56,7 @@
 volatile int cpu_number_map[NR_CPUS];			/* which CPU maps to which logical number		*/
 volatile int cpu_logical_map[NR_CPUS];			/* which logical number maps to which CPU		*/
 volatile unsigned long cpu_callin_map[NR_CPUS] = {0,};	/* We always use 0 the rest is ready for parallel delivery */
-volatile unsigned long smp_invalidate_needed;		/* Used for the invalidate map thats also checked in the spinlock */
+volatile unsigned long smp_invalidate_needed;		/* Used for the invalidate map that's also checked in the spinlock */
 struct cpuinfo_x86 cpu_data[NR_CPUS];			/* Per cpu bogomips and other parameters 		*/
 static unsigned int num_processors = 1;			/* Internal processor count				*/
 static unsigned long io_apic_addr = 0xFEC00000;		/* Address of the I/O apic (not yet used) 		*/
@@ -448,7 +448,7 @@
 /*
  *	Currently trivial. Write the real->protected mode
  *	bootstrap into the page concerned. The caller
- *	has made sure its suitably aligned.
+ *	has made sure it's suitably aligned.
  */
  
 static void install_trampoline(unsigned char *mp)
@@ -1094,7 +1094,7 @@
 /*	printk("SMI-");*/
 	
 	/*
-	 *	The assignment is safe because its volatile so the compiler cannot reorder it,
+	 *	The assignment is safe because it's volatile so the compiler cannot reorder it,
 	 *	because the i586 has strict memory ordering and because only the kernel lock holder
 	 *	may issue a tlb flush. If you break any one of those three change this to an atomic
 	 *	bus locked or.
@@ -1104,7 +1104,7 @@
 	
 	/*
 	 *	Processors spinning on the lock will see this IRQ late. The smp_invalidate_needed map will
-	 *	ensure they dont do a spurious flush tlb or miss one.
+	 *	ensure they don't do a spurious flush tlb or miss one.
 	 */
 	 
 	save_flags(flags);
diff -u --recursive --new-file v1.3.98/linux/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- v1.3.98/linux/arch/i386/kernel/time.c	Sun May  5 08:51:58 1996
+++ linux/arch/i386/kernel/time.c	Mon May  6 12:26:02 1996
@@ -267,7 +267,7 @@
 	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	/* As we return to user mode fire off the other CPU schedulers.. this is 
 	   basically because we don't yet share IRQ's around. This message is
-	   rigged to be safe on the 386 - basically its a hack, so don't look
+	   rigged to be safe on the 386 - basically it's a hack, so don't look
 	   closely for now.. */
 	/*smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); */
 	    
diff -u --recursive --new-file v1.3.98/linux/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c
--- v1.3.98/linux/arch/i386/kernel/traps.c	Sat Apr 27 15:19:44 1996
+++ linux/arch/i386/kernel/traps.c	Mon May  6 16:41:22 1996
@@ -255,18 +255,11 @@
 	 */
 	__asm__ __volatile__("fnsave %0":"=m" (task->tss.i387.hard));
 	task->flags&=~PF_USEDFPU;
+	stts();
 
 	force_sig(SIGFPE, task);
 	task->tss.trap_no = 16;
 	task->tss.error_code = 0;
-
-	/*
-	 * Give the process a clean slate next time they use
-	 * the FPU (and if they haven't accepted the SIGFPE before
-	 * that, it's their problem..)
-	 */
-	stts();
-	task->used_math = 0;
 }
 
 asmlinkage void do_coprocessor_error(struct pt_regs * regs, long error_code)
diff -u --recursive --new-file v1.3.98/linux/arch/i386/math-emu/errors.c linux/arch/i386/math-emu/errors.c
--- v1.3.98/linux/arch/i386/math-emu/errors.c	Tue Aug  1 10:02:31 1995
+++ linux/arch/i386/math-emu/errors.c	Mon May  6 16:31:17 1996
@@ -3,9 +3,9 @@
  |                                                                           |
  |  The error handling functions for wm-FPU-emu                              |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1996                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@jacobi.maths.monash.edu.au                |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -82,7 +82,7 @@
 
 
 
-void emu_printall()
+void emu_printall(void)
 {
   int i;
   static const char *tag_desc[] = { "Valid", "Zero", "ERROR", "ERROR",
@@ -166,7 +166,8 @@
   for ( i = 0; i < 8; i++ )
     {
       FPU_REG *r = &st(i);
-      switch (r->tag)
+      char tagi = r->tag;
+      switch (tagi)
 	{
 	case TW_Empty:
 	  continue;
@@ -190,22 +191,13 @@
 		 r->exp - EXP_BIAS + 1);
 	  break;
 	default:
-	  printk("Whoops! Error in errors.c      ");
+	  printk("Whoops! Error in errors.c: tag%d is %d ", i, tagi);
+	  continue;
 	  break;
 	}
-      printk("%s\n", tag_desc[(int) (unsigned) r->tag]);
+      printk("%s\n", tag_desc[(int) (unsigned) tagi]);
     }
 
-#ifdef OBSOLETE
-  printk("[data] %c .%04lx %04lx %04lx %04lx e%+-6ld ",
-	 FPU_loaded_data.sign ? '-' : '+',
-	 (long)(FPU_loaded_data.sigh >> 16),
-	 (long)(FPU_loaded_data.sigh & 0xFFFF),
-	 (long)(FPU_loaded_data.sigl >> 16),
-	 (long)(FPU_loaded_data.sigl & 0xFFFF),
-	 FPU_loaded_data.exp - EXP_BIAS + 1);
-  printk("%s\n", tag_desc[(int) (unsigned) FPU_loaded_data.tag]);
-#endif OBSOLETE
   RE_ENTRANT_CHECK_ON;
 
 }
@@ -365,12 +357,8 @@
       /*
        * The 80486 generates an interrupt on the next non-control FPU
        * instruction. So we need some means of flagging it.
-       * We use the ES (Error Summary) bit for this, assuming that
-       * this is the way a real FPU does it (until I can check it out),
-       * if not, then some method such as the following kludge might
-       * be needed.
+       * We use the ES (Error Summary) bit for this.
        */
-/*      regs[0].tag |= TW_FPU_Interrupt; */
     }
   RE_ENTRANT_CHECK_ON;
 
@@ -568,7 +556,7 @@
       return !(control_word & CW_Precision);
     }
 
-  return !(control_word & CW_Overflow);
+  return 0;
 
 }
 
@@ -599,7 +587,7 @@
       return !(control_word & CW_Precision);
     }
 
-  return !(control_word & CW_Underflow);
+  return 0;
 
 }
 
diff -u --recursive --new-file v1.3.98/linux/arch/i386/math-emu/fpu_emu.h linux/arch/i386/math-emu/fpu_emu.h
--- v1.3.98/linux/arch/i386/math-emu/fpu_emu.h	Tue Aug  1 10:02:31 1995
+++ linux/arch/i386/math-emu/fpu_emu.h	Tue May  7 10:02:31 1996
@@ -57,6 +57,7 @@
 
 #ifndef __ASSEMBLY__
 
+#include <asm/sigcontext.h>	/* for struct _fpstate */
 #include <linux/math_emu.h>
 #include <linux/linkage.h>
 
diff -u --recursive --new-file v1.3.98/linux/arch/i386/math-emu/fpu_entry.c linux/arch/i386/math-emu/fpu_entry.c
--- v1.3.98/linux/arch/i386/math-emu/fpu_entry.c	Sun Nov 27 20:19:52 1994
+++ linux/arch/i386/math-emu/fpu_entry.c	Mon May  6 16:31:17 1996
@@ -1,11 +1,11 @@
 /*---------------------------------------------------------------------------+
  |  fpu_entry.c                                                              |
  |                                                                           |
- | The entry function for wm-FPU-emu                                         |
+ | The entry functions for wm-FPU-emu                                        |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1996                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@jacobi.maths.monash.edu.au                |
  |                                                                           |
  | See the files "README" and "COPYING" for further copyright and warranty   |
  | information.                                                              |
@@ -20,7 +20,8 @@
  +---------------------------------------------------------------------------*/
 
 /*---------------------------------------------------------------------------+
- | math_emulate() is the sole entry point for wm-FPU-emu                     |
+ | math_emulate(), restore_i387_soft() and save_i387_soft() are the only     |
+ | entry points for wm-FPU-emu.                                              |
  +---------------------------------------------------------------------------*/
 
 #include <linux/signal.h>
@@ -159,8 +160,9 @@
 	{
 	  /* Make sure that the registers are compatible
 	     with the assumptions of the emulator. */
-	  regs[i].exp = 0;
-	  regs[i].sigh = 0x80000000;
+	  if ( !((regs[i].exp == EXP_UNDER) && (regs[i].sigh == 0)
+		 && (regs[i].sigl == 0)) )
+	    regs[i].sigh |= 0x80000000;
 	}
       finit();
       current->used_math = 1;
@@ -283,24 +285,8 @@
 	  /*
 	   *  We need to simulate the action of the kernel to FPU
 	   *  interrupts here.
-	   *  Currently, the "real FPU" part of the kernel (0.99.10)
-	   *  clears the exception flags, sets the registers to empty,
-	   *  and passes information back to the interrupted process
-	   *  via the cs selector and operand selector, so we do the same.
 	   */
 	do_the_FPU_interrupt:
-	  instruction_address.selector = status_word();
-      	  operand_address.selector = tag_word();
-	  partial_status = 0;
-	  top = 0;
-	  {
-	    int r;
-	    for (r = 0; r < 8; r++)
-	      {
-		regs[r].tag = TW_Empty;
-	      }
-	  }
-
 	  FPU_EIP = FPU_ORIG_EIP;	/* Point to current FPU instruction. */
 
 	  RE_ENTRANT_CHECK_OFF;
@@ -687,4 +673,23 @@
 #ifdef PARANOID
       printk("ERROR: wm-FPU-emu math_abort failed!\n");
 #endif PARANOID
+}
+
+
+
+void restore_i387_soft(struct _fpstate *buf)
+{
+  fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0};
+
+  frstor(addr_modes, (char *)buf);
+}
+
+
+struct _fpstate * save_i387_soft(struct _fpstate * buf)
+{
+  fpu_addr_modes addr_modes = {{ 0, 0, PREFIX_DEFAULT }, 0};
+
+  fsave(addr_modes, (char *)buf);
+
+  return buf;
 }
diff -u --recursive --new-file v1.3.98/linux/arch/i386/math-emu/reg_ld_str.c linux/arch/i386/math-emu/reg_ld_str.c
--- v1.3.98/linux/arch/i386/math-emu/reg_ld_str.c	Fri Aug 19 08:54:01 1994
+++ linux/arch/i386/math-emu/reg_ld_str.c	Mon May  6 16:31:17 1996
@@ -3,9 +3,9 @@
  |                                                                           |
  | All of the functions which transfer data between user memory and FPU_REGs.|
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
- |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ | Copyright (C) 1992,1993,1994,1996                                         |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@jacobi.maths.monash.edu.au                |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -466,6 +466,7 @@
 
   if (st0_tag == TW_Valid)
     {
+      int precision_loss;
       int exp;
       FPU_REG tmp;
 
@@ -474,8 +475,6 @@
 
       if ( exp < DOUBLE_Emin )     /* It may be a denormal */
 	{
-	  int precision_loss;
-
 	  /* A denormal will always underflow. */
 #ifndef PECULIAR_486
 	  /* An 80486 is supposed to be able to generate
@@ -518,6 +517,7 @@
 	{
 	  if ( tmp.sigl & 0x000007ff )
 	    {
+	      precision_loss = 1;
 	      switch (control_word & CW_RC)
 		{
 		case RC_RND:
@@ -541,8 +541,6 @@
 	  
 	      if ( increment )
 		{
-		  set_precision_flag_up();
-
 		  if ( tmp.sigl >= 0xfffff800 )
 		    {
 		      /* the sigl part overflows */
@@ -566,9 +564,9 @@
 		      tmp.sigl += 0x00000800;
 		    }
 		}
-	      else
-		set_precision_flag_down();
 	    }
+	  else
+	    precision_loss = 0;
 	  
 	  l[0] = (tmp.sigl >> 11) | (tmp.sigh << 21);
 	  l[1] = ((tmp.sigh >> 11) & 0xfffff);
@@ -590,6 +588,13 @@
 	    }
 	  else
 	    {
+	      if ( precision_loss )
+		{
+		  if ( increment )
+		    set_precision_flag_up();
+		  else
+		    set_precision_flag_down();
+		}
 	      /* Add the exponent */
 	      l[1] |= (((exp+DOUBLE_Ebias) & 0x7ff) << 20);
 	    }
@@ -661,6 +666,7 @@
 
   if (st0_tag == TW_Valid)
     {
+      int precision_loss;
       int exp;
       FPU_REG tmp;
 
@@ -669,8 +675,6 @@
 
       if ( exp < SINGLE_Emin )
 	{
-	  int precision_loss;
-
 	  /* A denormal will always underflow. */
 #ifndef PECULIAR_486
 	  /* An 80486 is supposed to be able to generate
@@ -715,6 +719,7 @@
 	      unsigned long sigh = tmp.sigh;
 	      unsigned long sigl = tmp.sigl;
 	      
+	      precision_loss = 1;
 	      switch (control_word & CW_RC)
 		{
 		case RC_RND:
@@ -740,8 +745,6 @@
 	  
 	      if (increment)
 		{
-		  set_precision_flag_up();
-
 		  if ( sigh >= 0xffffff00 )
 		    {
 		      /* The sigh part overflows */
@@ -758,10 +761,11 @@
 		}
 	      else
 		{
-		  set_precision_flag_down();
 		  tmp.sigh &= 0xffffff00;  /* Finish the truncation */
 		}
 	    }
+	  else
+	    precision_loss = 0;
 
 	  templ = (tmp.sigh >> 8) & 0x007fffff;
 
@@ -780,7 +784,17 @@
 	      templ = 0x7f800000;
 	    }
 	  else
-	    templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
+	    {
+	      if ( precision_loss )
+		{
+		  if ( increment )
+		    set_precision_flag_up();
+		  else
+		    set_precision_flag_down();
+		}
+	      /* Add the exponent */
+	      templ |= ((exp+SINGLE_Ebias) & 0xff) << 23;
+	    }
 	}
     }
   else if (st0_tag == TW_Zero)
diff -u --recursive --new-file v1.3.98/linux/arch/i386/math-emu/version.h linux/arch/i386/math-emu/version.h
--- v1.3.98/linux/arch/i386/math-emu/version.h	Tue Mar 19 13:45:02 1996
+++ linux/arch/i386/math-emu/version.h	Mon May  6 16:31:17 1996
@@ -3,10 +3,10 @@
  |                                                                           |
  |                                                                           |
  | Copyright (C) 1992,1993,1994,1996                                         |
- |                     W. Metzenthen, 22 Parker St, Ormond, Vic 3163,        |
- |                     Australia.  E-mail   billm@jacobi.maths.monash.edu.au |
+ |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
+ |                  E-mail   billm@jacobi.maths.monash.edu.au                |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
 
-#define FPU_VERSION "wm-FPU-emu version 1.21"
+#define FPU_VERSION "wm-FPU-emu version 1.22"
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/amiga/Makefile linux/arch/m68k/amiga/Makefile
--- v1.3.98/linux/arch/m68k/amiga/Makefile	Tue Apr 23 13:57:02 1996
+++ linux/arch/m68k/amiga/Makefile	Mon May  6 12:44:30 1996
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := amiga.o
-O_OBJS	 := config.o amikeyb.o amiints.o amipart.o \
+O_OBJS	 := config.o amikeyb.o amiints.o \
 	   chipram.o amisound.o amifb.o zorro.o
 OX_OBJS = ksyms.o
 
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/amiga/amipart.c linux/arch/m68k/amiga/amipart.c
--- v1.3.98/linux/arch/m68k/amiga/amipart.c	Tue Apr 23 13:57:02 1996
+++ linux/arch/m68k/amiga/amipart.c	Thu Jan  1 02:00:00 1970
@@ -1,119 +0,0 @@
-/*
- * linux/amiga/amipart.c
- *
- * Amiga partition checking driver for 680x0 Linux
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file README.legal in the main directory of this archive
- * for more details.
- */
-
-#include <linux/fs.h>
-#include <linux/genhd.h>
-#include <linux/kernel.h>
-
-#include <asm/amigardb.h>
-#include <asm/machdep.h>
-
-extern int current_minor;
-
-static ulong checksum (ulong *ptr, int len)
-{
-	ulong sum;
-	int cnt;
-
-	for (sum = 0, cnt = 0; cnt < len; cnt++)
-		sum += ptr[cnt];
-
-	return sum;
-}
-
-/* XXX */
-/* int current_minor = 0; */
-
-void amiga_check_partition(struct gendisk *hd, kdev_t dev)
-{
-	int i, minor = current_minor, m_lim = current_minor + hd->max_p;
-	struct buffer_head *bh;
-	struct RigidDiskBlock *rdb;
-	struct PartitionBlock *pb;
-	ulong bnum, partsect;
-
-	for (bnum = 0; bnum < RDB_LOCATION_LIMIT/2; bnum++) {
-		if (!(bh = bread(dev,bnum,1024))) {
-			printk (" unable to read block %ld\n", bnum);
-			return;
-		}
-#ifdef DEBUG
-		printk ("block read, press mousebutton to continue\n");
-		waitbut();
-#endif
-		rdb = (struct RigidDiskBlock *)bh->b_data;
-		if (rdb->rdb_ID == IDNAME_RIGIDDISK)
-			break;
-		rdb = (struct RigidDiskBlock *)&bh->b_data[512];
-		if (rdb->rdb_ID == IDNAME_RIGIDDISK)
-			break;
-		brelse (bh);
-	}
-	if (bnum == RDB_LOCATION_LIMIT/2) {
-		/* no RDB on the disk! */
-		printk (" unable to find RigidDiskBlock\n");
-		return;
-	}
-
-	/* checksum the RigidDiskBlock */
-	if (checksum ((ulong *)rdb, rdb->rdb_SummedLongs) != 0) {
-		printk (" RDB checksum bad\n");
-		return;
-	}
-
-	printk("  %s%c:", hd->major_name, 'a'+(minor >> hd->minor_shift));
-
-	partsect = rdb->rdb_PartitionList;
-	brelse (bh);
-
-	for (i = 1; minor < m_lim && partsect != 0xffffffff; minor++, i++)
-	{
-		ulong *env;
-
-		if (!(bh = bread(dev,partsect/2,1024))) {
-			printk (" block %ld read failed\n", partsect);
-			return;
-		}
-#ifdef DEBUG
-		printk ("block read, press mousebutton to continue\n");
-		waitbut();
-#endif
-		pb = (struct PartitionBlock *)bh->b_data;
-		if (partsect & 1)
-			pb = (struct PartitionBlock *)&bh->b_data[512];
-		if (pb->pb_ID != IDNAME_PARTITION) {
-			printk (" block %ld Not a partition block (%#lx)\n",
-				partsect, pb->pb_ID);
-			brelse (bh);
-			return;
-		}
-		if (checksum ((ulong *)pb, pb->pb_SummedLongs) != 0) {
-			printk (" block %ld checksum bad\n", partsect);
-			brelse (bh);
-			return;
-		}
-
-		env = pb->pb_Environment;
-
-		hd->part[minor].start_sect = env[DE_LOWCYL]
-			* env[DE_NUMHEADS] * env[DE_BLKSPERTRACK];
-		hd->part[minor].nr_sects = (env[DE_UPPERCYL]
-					    - env[DE_LOWCYL] + 1)
-			* env[DE_NUMHEADS] * env[DE_BLKSPERTRACK];
-
-		printk(" %s%c%d", hd->major_name,
-		       'a'+(minor >> hd->minor_shift), i);
-
-		partsect = pb->pb_Next;
-		brelse (bh);
-	}
-
-	printk ("\n");
-}
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/amiga/config.c linux/arch/m68k/amiga/config.c
--- v1.3.98/linux/arch/m68k/amiga/config.c	Sat Apr 27 15:19:45 1996
+++ linux/arch/m68k/amiga/config.c	Mon May  6 12:44:30 1996
@@ -49,7 +49,6 @@
 extern void a2000_gettod (int *, int *, int *, int *, int *, int *);
 extern int amiga_hwclk (int, struct hwclk_time *);
 extern int amiga_set_clock_mmss (unsigned long);
-extern void amiga_check_partition (struct gendisk *hd, unsigned int dev);
 extern void amiga_mksound( unsigned int count, unsigned int ticks );
 #ifdef CONFIG_BLK_DEV_FD
 extern int amiga_floppy_init (void);
@@ -288,7 +287,6 @@
   }
   mach_hwclk           = amiga_hwclk;
   mach_set_clock_mmss  = amiga_set_clock_mmss;
-  mach_check_partition = amiga_check_partition;
   mach_mksound         = amiga_mksound;
 #ifdef CONFIG_BLK_DEV_FD
   mach_floppy_init     = amiga_floppy_init;
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/atari/Makefile linux/arch/m68k/atari/Makefile
--- v1.3.98/linux/arch/m68k/atari/Makefile	Tue Apr 23 13:57:02 1996
+++ linux/arch/m68k/atari/Makefile	Mon May  6 12:44:30 1996
@@ -11,7 +11,7 @@
 
 O_TARGET := atari.o
 O_OBJS	 := config.o atakeyb.o ataints.o \
-            atapart.o stdma.o atasound.o joystick.o stram.o atafb.o
+            stdma.o atasound.o joystick.o stram.o atafb.o
 OX_OBJS = ksyms.o
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/atari/atafb.c linux/arch/m68k/atari/atafb.c
--- v1.3.98/linux/arch/m68k/atari/atafb.c	Sat Apr 27 15:19:45 1996
+++ linux/arch/m68k/atari/atafb.c	Mon May  6 12:26:02 1996
@@ -1399,7 +1399,7 @@
 	if (hw->ste_mode || mon_type!=F_MON_VGA)
 		var->right_margin = prescale * (hw->hht + 2 - hw->hde) - hde_off;
 	else
-		/* cant use this in ste_mode, because hbb is +1 off */
+		/* can't use this in ste_mode, because hbb is +1 off */
 		var->right_margin = prescale * (hw->hht + 2 - hw->hbb);
 	var->hsync_len = prescale * (hw->hht + 2 - hw->hss);
 
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/atari/atapart.c linux/arch/m68k/atari/atapart.c
--- v1.3.98/linux/arch/m68k/atari/atapart.c	Sat Apr 27 15:19:45 1996
+++ linux/arch/m68k/atari/atapart.c	Thu Jan  1 02:00:00 1970
@@ -1,158 +0,0 @@
-/*
- * linux/atari/atapart.c
- *
- * Atari partition checking driver for 680x0 Linux
- * Written by Andreas Schwab (schwab@ls5.informatik.uni-dortmund.de)
- *
- * 5/3/94 Roman Hodek:
- *   Added some sanity checks
- *   Linux device names start from 1 not 0, so I changed the initial value
- *   for i to 1.
- *
- * 10/09/94 Guenther Kelleter:
- *   Added support for ICD/Supra partition info.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file README.legal in the main directory of this archive
- * for more details.
- */
-
-#include <linux/fs.h>
-#include <linux/genhd.h>
-#include <linux/kernel.h>
-
-#include <asm/atari_rootsec.h>
-
-/* ++guenther: this should be settable by the user ("make config")?.
- */
-#define ICD_PARTS
-
-extern int current_minor;
-
-void
-atari_check_partition (struct gendisk *hd, unsigned int dev)
-{
-  int i, minor = current_minor, m_lim = current_minor + hd->max_p;
-  struct buffer_head *bh;
-  struct rootsector *rs;
-  struct partition_info *pi;
-  ulong extensect;
-#ifdef ICD_PARTS
-  int part_fmt = 0; /* 0:unknown, 1:AHDI, 2:ICD/Supra */
-#endif
-
-  bh = bread (dev, 0, 1024);
-  if (!bh)
-    {
-      printk (" unable to read block 0\n");
-      return;
-    }
-
-  rs = (struct rootsector *) bh->b_data;
-  printk ("  %s%c:", hd->major_name, 'a' + (minor >> hd->minor_shift));
-
-  pi = &rs->part[0];
-  for (i = 1; pi < &rs->part[4] && minor < m_lim; i++, minor++, pi++)
-    {
-      if (pi->flg & 1)
-  /* active partition */
-  {
-    if (memcmp (pi->id, "XGM", 3) == 0)
-      /* extension partition */
-      {
-        struct rootsector *xrs;
-        struct buffer_head *xbh;
-        ulong partsect;
-
-#ifdef ICD_PARTS
-        part_fmt = 1;
-#endif
-        partsect = extensect = pi->st;
-        while (1)
-      {
-        xbh = bread (dev, partsect / 2, 1024);
-        if (!xbh)
-          {
-            printk (" block %ld read failed\n", partsect);
-            return;
-          }
-        if (partsect & 1)
-          xrs = (struct rootsector *) &xbh->b_data[512];
-        else
-          xrs = (struct rootsector *) &xbh->b_data[0];
-
-		/* ++roman: sanity check: bit 0 of flg field must be set */
-		if (!(xrs->part[0].flg & 1)) {
-			printk( "\nFirst sub-partition in extended partition is not valid!\n" );
-			break;
-		}
-		
-        hd->part[minor].start_sect = partsect + xrs->part[0].st;
-        hd->part[minor].nr_sects = xrs->part[0].siz;
-        printk (" %s%c%d", hd->major_name,
-            'a' + (minor >> hd->minor_shift), i);
-
-		if (!(xrs->part[1].flg & 1)) {
-			/* end of linked partition list */
-			brelse( xbh );
-			break;
-		}
-		if (memcmp( xrs->part[1].id, "XGM", 3 ) != 0) {
-			printk( "\nID of extended partition is not XGM!\n" );
-			brelse( xbh );
-			break;
-		}
-		
-		partsect = xrs->part[1].st + extensect;
-        brelse (xbh);
-        i++;
-        minor++;
-        if (minor >= m_lim) {
-			printk( "\nMaximum number of partitions reached!\n" );
-			break;
-		}
-      }
-      }
-    else
-      {
-        /* we don't care about other id's */
-        hd->part[minor].start_sect = pi->st;
-        hd->part[minor].nr_sects = pi->siz;
-        printk (" %s%c%d", hd->major_name,
-            'a' + (minor >> hd->minor_shift), i);
-
-      }
-  }
-    }
-#ifdef ICD_PARTS
-  if ( part_fmt!=1 ) /* no extended partitions -> test ICD-format */
-  {
-    pi = &rs->icdpart[0];
-    /* sanity check: no ICD format if first partition invalid */
-    if (memcmp (pi->id, "GEM", 3) == 0 ||
-        memcmp (pi->id, "BGM", 3) == 0 ||
-        memcmp (pi->id, "RAW", 3) == 0 )
-    {
-      for (i = 1; pi < &rs->icdpart[8] && minor < m_lim; i++, minor++, pi++)
-      {
-        /* accept only GEM,BGM,RAW partitions */
-        if (pi->flg & 1 && 
-            (memcmp (pi->id, "GEM", 3) == 0 ||
-             memcmp (pi->id, "BGM", 3) == 0 ||
-             memcmp (pi->id, "RAW", 3) == 0) )
-        {
-          part_fmt = 2;
-          hd->part[minor].start_sect = pi->st;
-          hd->part[minor].nr_sects = pi->siz;
-          printk (" %s%c%d", hd->major_name,
-              'a' + (minor >> hd->minor_shift), i);
-        }
-      }
-    }
-  }
-#endif
-  brelse (bh);
-
-  printk ("\n");
-}
-
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/atari/config.c linux/arch/m68k/atari/config.c
--- v1.3.98/linux/arch/m68k/atari/config.c	Sat Apr 27 15:19:45 1996
+++ linux/arch/m68k/atari/config.c	Mon May  6 12:44:30 1996
@@ -60,7 +60,6 @@
 extern int atari_hwclk (int, struct hwclk_time *);
 extern int atari_mste_set_clock_mmss (unsigned long);
 extern int atari_set_clock_mmss (unsigned long);
-extern void atari_check_partition (struct gendisk *hd, unsigned int dev);
 extern void atari_mksound( unsigned int count, unsigned int ticks );
 extern void atari_reset( void );
 #ifdef CONFIG_BLK_DEV_FD
@@ -214,7 +213,6 @@
     mach_disable_irq     = atari_disable_irq;
     mach_get_irq_list	 = atari_get_irq_list;
     mach_gettimeoffset   = atari_gettimeoffset;
-    mach_check_partition = atari_check_partition;
     mach_mksound         = atari_mksound;
     mach_reset           = atari_reset;
 #ifdef CONFIG_BLK_DEV_FD
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/config.in linux/arch/m68k/config.in
--- v1.3.98/linux/arch/m68k/config.in	Tue Apr 23 13:57:03 1996
+++ linux/arch/m68k/config.in	Mon May  6 12:44:30 1996
@@ -143,6 +143,17 @@
 if [ "$CONFIG_ATARI" = "y" ]; then
   tristate 'Atari mouse support' CONFIG_ATARIMOUSE
 fi
+if [ "$CONFIG_ATARI" = y ]; then
+  tristate 'Atari MFP serial support' CONFIG_ATARI_MFPSER
+  tristate 'Atari SCC serial support' CONFIG_ATARI_SCC
+  tristate 'Atari MIDI serial support' CONFIG_ATARI_MIDI
+fi
+if [ "$CONFIG_AMIGA" = y ]; then
+  tristate 'Amiga builtin serial support' CONFIG_AMIGA_BUILTIN_SERIAL
+  bool 'GVP IO-Extender support' CONFIG_GVPIOEXT
+  tristate 'Multiface Card III serial support' CONFIG_MULTIFACE_III_TTY
+fi
+bool 'Support for user serial device modules' CONFIG_USERIAL
 bool 'Watchdog Timer Support'	CONFIG_WATCHDOG
 if [ "$CONFIG_WATCHDOG" != "n" ]; then
   bool '   Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/console/fbcon.c linux/arch/m68k/console/fbcon.c
--- v1.3.98/linux/arch/m68k/console/fbcon.c	Sat Apr 27 15:19:46 1996
+++ linux/arch/m68k/console/fbcon.c	Mon May  6 12:26:02 1996
@@ -721,7 +721,7 @@
  *
  * Not a great fan of assembler for the sake of it, but I think
  * that these routines are at least 10 times faster than their C
- * equivalents for large blits, and thats important to the lowest level of
+ * equivalents for large blits, and that's important to the lowest level of
  * a graphics driver. Question is whether some scheme with the blitter
  * would be faster. I suspect not for simple text system - not much
  * asynchrony.
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
--- v1.3.98/linux/arch/m68k/defconfig	Sat Apr 27 15:19:46 1996
+++ linux/arch/m68k/defconfig	Mon May  6 12:44:30 1996
@@ -17,7 +17,7 @@
 # CONFIG_MAC is not set
 CONFIG_FPSP_040=y
 # CONFIG_IFPSP_060 is not set
-# CONFIG_NET is not set
+CONFIG_NET=y
 CONFIG_SYSVIPC=y
 CONFIG_BINFMT_AOUT=y
 CONFIG_BINFMT_ELF=y
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/fpsp040/skeleton.S linux/arch/m68k/fpsp040/skeleton.S
--- v1.3.98/linux/arch/m68k/fpsp040/skeleton.S	Sat Apr 27 15:19:46 1996
+++ linux/arch/m68k/fpsp040/skeleton.S	Mon May  6 12:44:30 1996
@@ -452,7 +452,7 @@
 	moveq	#-1,%d0
 	movel	%d0,%sp@(LOFF_ORIG_D0)    | indicate stack frame not for syscall
 	bral	SYMBOL_NAME(ret_from_exception)	| deliver signals, reschedule etc..
-	
+
 
 |
 |	mem_write --- write to user or supervisor address space
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/ifpsp060/ilsp.doc linux/arch/m68k/ifpsp060/ilsp.doc
--- v1.3.98/linux/arch/m68k/ifpsp060/ilsp.doc	Tue Apr 23 13:57:07 1996
+++ linux/arch/m68k/ifpsp060/ilsp.doc	Mon May  6 12:26:02 1996
@@ -126,7 +126,7 @@
 Exception reporting:
 --------------------
 If the instruction being emulated is a divide and the source
-operand is a zero, then the library routine, as it's last
+operand is a zero, then the library routine, as its last
 instruction, executes an implemented divide using a zero
 source operand so that an "Integer Divide-by-Zero" exception
 will be taken. Although the exception stack frame will not
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/kernel/console.c linux/arch/m68k/kernel/console.c
--- v1.3.98/linux/arch/m68k/kernel/console.c	Sat Apr 27 15:19:47 1996
+++ linux/arch/m68k/kernel/console.c	Mon May  6 12:26:02 1996
@@ -2049,7 +2049,7 @@
    /* undraw cursor first */
    hide_cursor(currcons);
 
-   /* Contrived structure to try and emulate original need_wrap behaviour
+   /* Contrived structure to try to emulate original need_wrap behaviour
     * Problems caused when we have need_wrap set on '\n' character */
    
    while ((c = *(b++)) != 0) {
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/kernel/head.S linux/arch/m68k/kernel/head.S
--- v1.3.98/linux/arch/m68k/kernel/head.S	Sat Apr 27 15:19:47 1996
+++ linux/arch/m68k/kernel/head.S	Mon May  6 12:44:30 1996
@@ -14,6 +14,8 @@
 ** 94/11/14 Andreas Schwab: put kernel at PAGESIZE
 ** 94/11/18 Andreas Schwab: remove identity mapping of STRAM for Atari 
 ** ++ Bjoern & Roman: ATARI-68040 support for the Medusa
+** 96/04/26 G|nther Kelleter: fixed identity mapping for Falcon with
+**                            Magnum- and FX-alternate ram
 **
 ** This file is subject to the terms and conditions of the GNU General Public
 ** License.  See the file README.legal in the main directory of this archive
@@ -146,6 +148,16 @@
 	movew	#0x2700,%sr
 
 /*
+ * Setup initial stack pointer
+ */
+	lea	%pc@(SYMBOL_NAME(_start)),%sp
+
+/* clear the fpu */
+	lea	%pc@(mmu),%a0
+	clrl	%a0@
+	frestore %a0@
+
+/*
  * Copy bootinfo from position after BSS to final resting place
  */
 	lea	%pc@(SYMBOL_NAME(_end)),%a0
@@ -651,8 +663,8 @@
 	
 	.word	0xf4d8		/* CINVA I/D */
 	.word	0xf518		/* pflusha	*/
-	.long	0x4e7bc807	/* movec a5,srp */
-	.long	0x4e7bc806	/* movec a5,urp */
+	.long	0x4e7bc807	/* movec a4,srp */
+	.long	0x4e7bc806	/* movec a4,urp */
 	movel	#(TC_ENABLE+TC_PAGE4K),%d0
 
 	/* setup registers for jumping MMU enabling code */
@@ -723,12 +735,36 @@
 	/* cleanup is needed; note it */
 	moveq	#1,%d5
 
+	/* tt0 doesn't work if physical and virtual address of kernel is in
+	 * the same 16M area (Falcon with Magnum/FX, kernel in alternate ram)
+	 */
 	movel	%a0,%d2
 	andl	#0xff000000,%d2		/* logical address base */
+	jeq	1f
 	orw	#0x8143,%d2		/* add in magic bits */
 	lea	%pc@(mmu),%a0
 	movel	%d2,%a0@
 	pmove	%a0@,%tt0
+	jra	Lnophys2
+1:
+	/* Transparent translation through kernel pointer table
+	 * Requires that this code until after MMU enabling lies in
+	 * the 256K page around %a0
+	 */
+	movel	%a0,%d2
+	moveq	#ROOT_INDEX_SHIFT,%d1
+	lsrl	%d1,%d2
+	movel	%a4@(%d2:l:4),%d0
+	andw	#0xfff0,%d0
+	movel	%d0,%a1
+	movel	%a0,%d2
+	andl	#0x01ffffff,%d2
+	moveq	#(ROOT_INDEX_SHIFT-7),%d1
+	lsrl	%d1,%d2
+	movel	%a0,%d0
+	addql	#PAGEDESC,%d0
+	movel	%a1@(%d2:l:4),%a2	/* save old_entry */
+	movel	%d0,%a1@(%d2:l:4)
 
 Lnophys2:
 	/*
@@ -832,11 +868,28 @@
 	cmpl	#MACH_ATARI,%d4
 	jne	Lnoclean
 
+	movel	%a3,%d2
+	andl	#0xff000000,%d2
+	jeq	1f
 	/* clear transparent translation register */
 	lea	%pc@(mmu),%a0
 	clrl	%a0@
 	pmove	%a0@,%tt0
 	jra	Lnoclean
+1:
+	movel	%a3,%d2
+	moveq	#ROOT_INDEX_SHIFT,%d1
+	lsrl	%d1,%d2
+	movel	%a4@(%d2:l:4),%d0
+	andw	#0xfff0,%d0
+	subl	%a3,%d0		/* to virtual address */
+	movel	%d0,%a0
+	movel	%a3,%d2
+	andl	#0x01ffffff,%d2
+	moveq	#(ROOT_INDEX_SHIFT-7),%d1
+	lsrl	%d1,%d2
+	movel	%a2,%a0@(%d2:l:4)	/* restore old entry */
+	jra	Lnoclean
 
 Lclean030:
 	movel	%a0,%d2		/* a0 contains physical start address */
@@ -1177,7 +1230,11 @@
 	rts
 #endif
 
-	.align 512	/*
+#ifdef __ELF__
+	.align 512
+#else
+	.align 9
+#endif			/*
 			 * MMU root-pointers need to be 512-byte
 			 * aligned on the 680[46]0 - Jes
 			 */
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/kernel/setup.c linux/arch/m68k/kernel/setup.c
--- v1.3.98/linux/arch/m68k/kernel/setup.c	Tue Apr 23 13:57:08 1996
+++ linux/arch/m68k/kernel/setup.c	Mon May  6 12:44:30 1996
@@ -62,7 +62,6 @@
 void (*mach_gettod) (int*, int*, int*, int*, int*, int*);
 int (*mach_hwclk) (int, struct hwclk_time*) = NULL;
 int (*mach_set_clock_mmss) (unsigned long) = NULL;
-void (*mach_check_partition) (struct gendisk *, unsigned int);
 void (*mach_mksound)( unsigned int count, unsigned int ticks );
 void (*mach_reset)( void );
 void (*waitbut)(void) = dummy_waitbut;
@@ -296,10 +295,6 @@
 #endif
     } /* boot_info.machtype */
 
-#if 0 /* ++1.3++ */
-    len += get_serial_list (buffer + len);
-#endif /* ++1.3++ */
-
     return(len);
 }
 
@@ -322,21 +317,6 @@
 unsigned long arch_kbd_init(void)
 {
 	return mach_keyb_init();
-}
-
-int rs_init(void)
-{
-	return 0;
-}
-
-struct serial_struct;
-int register_serial(struct serial_struct *req)
-{
-    return -1;
-}
-
-void unregister_serial(int line)
-{
 }
 
 void arch_gettod(int *year, int *mon, int *day, int *hour,
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/kernel/traps.c linux/arch/m68k/kernel/traps.c
--- v1.3.98/linux/arch/m68k/kernel/traps.c	Sat Apr 27 15:19:47 1996
+++ linux/arch/m68k/kernel/traps.c	Mon May  6 12:44:30 1996
@@ -392,7 +392,7 @@
 			}
 			printk ("BAD KERNEL BUSERR\n");
 			die_if_kernel("Oops",&fp->ptregs,0);
-			send_sig(SIGSEGV, current, 1);
+			force_sig(SIGSEGV, current);
 			user_space_fault = 0;
 		}
 	} else {
@@ -447,7 +447,7 @@
 			    !(ssw & RW) ? "write" : "read", addr,
 			    fp->ptregs.pc);
 		    die_if_kernel("Oops",&fp->ptregs,mmusr);
-		    send_sig(SIGSEGV, current, 1);
+		    force_sig(SIGSEGV, current);
 		    return;
 	    } else {
 #ifdef DEBUG
@@ -478,7 +478,7 @@
 		    printk("Unknown SIGSEGV - 1\n");
 #endif
 		    die_if_kernel("Oops",&fp->ptregs,mmusr);
-		    send_sig(SIGSEGV, current, 1);
+		    force_sig(SIGSEGV, current);
 		    return;
 	    }
 
@@ -576,7 +576,7 @@
 		printk("Unknown SIGSEGV - 2\n");
 #endif
 		die_if_kernel("Oops",&fp->ptregs,mmusr);
-		send_sig(SIGSEGV, current, 1);
+		force_sig(SIGSEGV, current);
 		return;
 	} else {
 #ifdef DEBUG
@@ -609,7 +609,7 @@
 		printk("Unknown SIGSEGV - 3\n");
 #endif
 		die_if_kernel("Oops",&fp->ptregs,mmusr);
-		send_sig(SIGSEGV, current, 1);
+		force_sig(SIGSEGV, current);
 		return;
 	}
 
@@ -644,7 +644,7 @@
 #if DEBUG
 	  printk("Unknown SIGSEGV - 4\n");
 #endif
-	  send_sig(SIGSEGV, current, 1);
+	  force_sig(SIGSEGV, current);
 	}
 }
 
@@ -871,7 +871,7 @@
 		break;
 	}
 
-	send_sig (sig, current, 1);
+	force_sig (sig, current);
 }
 
 asmlinkage void set_esp0 (unsigned long ssp)
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/mm/fault.c linux/arch/m68k/mm/fault.c
--- v1.3.98/linux/arch/m68k/mm/fault.c	Tue Apr 23 13:57:08 1996
+++ linux/arch/m68k/mm/fault.c	Mon May  6 12:44:30 1996
@@ -93,7 +93,7 @@
 bad_area:
 	if (user_mode(regs)) {
 		/* User memory access */
-		send_sig (SIGSEGV, current, 1);
+		force_sig (SIGSEGV, current);
 		return 1;
 	}
 
diff -u --recursive --new-file v1.3.98/linux/arch/m68k/mm/memory.c linux/arch/m68k/mm/memory.c
--- v1.3.98/linux/arch/m68k/mm/memory.c	Sat Apr 27 15:19:48 1996
+++ linux/arch/m68k/mm/memory.c	Mon May  6 12:44:30 1996
@@ -506,7 +506,7 @@
 		      : : "i" (FLUSH_I)
 		      : "d0");
 }
-
+#if 1
 void flush_cache_all(void)
 {
     if (m68k_is040or060 >= 4)
@@ -515,26 +515,53 @@
 	asm volatile ("movec %/cacr,%/d0\n\t"
 		      "oriw %0,%/d0\n\t"
 		      "movec %/d0,%/cacr"
-		      : : "i" (FLUSH_I)
+		      : : "i" (FLUSH_I_AND_D)
 		      : "d0");
 }
 
-void flush_page_to_ram (unsigned long addr)
+void flush_cache_mm(struct mm_struct *mm){
+
+    if (mm == current->mm)
+        flush_cache_all();
+}
+
+void flush_cache_range(struct mm_struct *mm, unsigned long start,
+		       unsigned long end){
+    if (mm == current->mm)
+        cache_push_v(start, end-start);
+}
+
+void flush_cache_page (struct vm_area_struct *vma, unsigned long vaddr)
 {
-    if (m68k_is040or060 == 4)
-        pushv040(addr);
+    if (m68k_is040or060 >= 4)
+        pushv040(vaddr); /*
+			  * the 040 always invalidates the I-cache when
+			  * pushing its contents to ram.
+			  */
+
+    /* 68030/68020 have no writeback cache; still need to clear icache. */
+    else /* 68030 or 68020 */
+        asm volatile ("movec %/cacr,%/d0\n\t"
+		      "oriw %0,%/d0\n\t"
+		      "movec %/d0,%/cacr"
+		      : : "i" (FLUSH_I_AND_D)
+		      : "d0");
+}
 
-    else if (m68k_is040or060 == 6)
-        push040(VTOP(addr)); /* someone mentioned that pushv060 doesn't work */
+void flush_page_to_ram (unsigned long vaddr)
+{
+    if (m68k_is040or060 >= 4)
+        pushcl040(VTOP(vaddr));
 
     /* 68030/68020 have no writeback cache; still need to clear icache. */
     else /* 68030 or 68020 */
 	asm volatile ("movec %/cacr,%/d0\n\t"
 		      "oriw %0,%/d0\n\t"
 		      "movec %/d0,%/cacr"
-		      : : "i" (FLUSH_I)
+		      : : "i" (FLUSH_I_AND_D)
 		      : "d0");
 }
+#endif
 
 #undef clear040
 #undef push040
diff -u --recursive --new-file v1.3.98/linux/arch/mips/config.in linux/arch/mips/config.in
--- v1.3.98/linux/arch/mips/config.in	Fri Apr 12 15:51:46 1996
+++ linux/arch/mips/config.in	Sun May  5 09:05:58 1996
@@ -45,7 +45,9 @@
 bool 'Networking support' CONFIG_NET
 #bool 'PCI bios support' CONFIG_PCI
 #if [ "$CONFIG_PCI" = "y" ]; then
-#  bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
+#  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+#    bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE
+#  fi
 #fi
 bool 'System V IPC' CONFIG_SYSVIPC
 endmenu
diff -u --recursive --new-file v1.3.98/linux/arch/mips/kernel/head.S linux/arch/mips/kernel/head.S
--- v1.3.98/linux/arch/mips/kernel/head.S	Fri Apr 12 15:51:46 1996
+++ linux/arch/mips/kernel/head.S	Mon May  6 12:26:03 1996
@@ -49,8 +49,8 @@
 		eret
 		/*
 		 * Workaround for R4000 bug.  For explanation see MIPS
-		 * docs.  Note that this that obscure that it wont almost
-		 * never happen.  Well, but Mips writes about it's bugs.
+		 * docs.  Note that this is so obscure that it will almost
+		 * never happen.  Well, but Mips writes about its bugs.
 		 */
 		nop
 		eret
diff -u --recursive --new-file v1.3.98/linux/arch/mips/kernel/time.c linux/arch/mips/kernel/time.c
--- v1.3.98/linux/arch/mips/kernel/time.c	Wed Dec 13 12:39:44 1995
+++ linux/arch/mips/kernel/time.c	Mon May  6 12:26:03 1996
@@ -202,7 +202,7 @@
 	    last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
 	/* As we return to user mode fire off the other CPU schedulers.. this is 
 	   basically because we don't yet share IRQ's around. This message is
-	   rigged to be safe on the 386 - basically its a hack, so don't look
+	   rigged to be safe on the 386 - basically it's a hack, so don't look
 	   closely for now.. */
 	smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 0); 
 }
diff -u --recursive --new-file v1.3.98/linux/arch/mips/mm/init.c linux/arch/mips/mm/init.c
--- v1.3.98/linux/arch/mips/mm/init.c	Fri Apr 12 15:51:47 1996
+++ linux/arch/mips/mm/init.c	Mon May  6 12:26:03 1996
@@ -168,7 +168,7 @@
 	{
 		/*
 		 * The page copied most is the COW empty_zero_page.  Since we
-		 * know it's contents we can avoid the writeback reading of
+		 * know its contents we can avoid the writeback reading of
 		 * the page.  Speeds up the standard case a lot.
 		 */
 		__zeropage(to);
diff -u --recursive --new-file v1.3.98/linux/arch/ppc/config.in linux/arch/ppc/config.in
--- v1.3.98/linux/arch/ppc/config.in	Tue Feb 13 10:30:24 1996
+++ linux/arch/ppc/config.in	Sun May  5 09:05:58 1996
@@ -29,7 +29,9 @@
 #bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 bool 'PCI bios support' CONFIG_PCI y
 if [ "$CONFIG_PCI" = "y" ]; then
-  bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE n
+  if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+    bool '   PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE n
+  fi
   if [ "$CONFIG_BLK_DEV_IDE" = "y" ]; then
     bool '   PCI Triton IDE Bus Master DMA support' CONFIG_BLK_DEV_TRITON y
   fi
diff -u --recursive --new-file v1.3.98/linux/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- v1.3.98/linux/arch/ppc/kernel/head.S	Fri Apr 12 15:51:47 1996
+++ linux/arch/ppc/kernel/head.S	Mon May  6 12:26:03 1996
@@ -1287,8 +1287,8 @@
 	
 /*
  * This routine switches between two different tasks.  The process
- * state of one is saved on it's kernel stack.  Then the state
- * of the other is restored from it's kernel stack.  The memory
+ * state of one is saved on its kernel stack.  Then the state
+ * of the other is restored from its kernel stack.  The memory
  * management hardware is updated to the second process's state.
  * Finally, we can return to the second process, via the 'return'.
  *
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/boot/README linux/arch/sparc/boot/README
--- v1.3.98/linux/arch/sparc/boot/README	Wed Apr 24 17:00:34 1996
+++ linux/arch/sparc/boot/README	Mon May  6 12:26:03 1996
@@ -4,7 +4,7 @@
 off of the root partition but also be able to netboot.  This means
 that it knows about RPC and NFS (bleech, yuck, eeewwwww!!) so that it
 can remote mount the root directory to fetch the kernel.  Also it must
-be able to ARP for it's IP address and who it's boot server is.  I
+be able to ARP for its IP address and who its boot server is.  I
 think I'm getting sick.
 
 Regardless for now I will concentrate on the low-level stuff necessary
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/kernel/head.S linux/arch/sparc/kernel/head.S
--- v1.3.98/linux/arch/sparc/kernel/head.S	Sat Apr 27 15:19:48 1996
+++ linux/arch/sparc/kernel/head.S	Mon May  6 12:26:03 1996
@@ -497,7 +497,7 @@
 		std	%g4, [%g3 + 0x8]	! Copy proms handler
 
 /* Must determine whether we are on a sun4c MMU, SRMMU, or SUN4/400 MUTANT
- * MMU so we can remap ourselves properly.  DONT TOUCH %l0 thru %l5 in these
+ * MMU so we can remap ourselves properly.  DON'T TOUCH %l0 thru %l5 in these
  * remapping routines, we need their values afterwards!
  */
 		/* Now check whether we are already mapped, if we
@@ -571,7 +571,7 @@
 		/* Grrr, why does it seem like every other load/store
 		 * on the sun4m is in some ASI space...
 		 * Fine with me, let's get the pointer to the level 1
-		 * page table directory and fetch it's entry.
+		 * page table directory and fetch its entry.
 		 */
 		lda	[%g4] ASI_M_BYPASS, %o1		! This is a level 1 ptr
 		srl	%o1, 0x4, %o1			! Clear low 4 bits
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/kernel/irq.c linux/arch/sparc/kernel/irq.c
--- v1.3.98/linux/arch/sparc/kernel/irq.c	Sat Apr 27 15:19:48 1996
+++ linux/arch/sparc/kernel/irq.c	Mon May  6 12:26:03 1996
@@ -81,7 +81,7 @@
  * equally sucky but at least we'll never try to free statically allocated
  * space or call kmalloc before kmalloc_init :(.
  * 
- * In fact its the timer10 that attaches first.. then timer14
+ * In fact it's the timer10 that attaches first.. then timer14
  * then kmalloc_init is called.. then the tty interrupts attach.
  * hmmm....
  *
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/kernel/sparc-stub.c linux/arch/sparc/kernel/sparc-stub.c
--- v1.3.98/linux/arch/sparc/kernel/sparc-stub.c	Sat Apr 27 15:19:48 1996
+++ linux/arch/sparc/kernel/sparc-stub.c	Mon May  6 12:26:03 1996
@@ -13,7 +13,7 @@
 		THIS SOFTWARE IS NOT COPYRIGHTED
 
    HP offers the following for use in the public domain.  HP makes no
-   warranty with regard to the software or it's performance and the
+   warranty with regard to the software or its performance and the
    user accepts the software "AS IS" with all faults.
 
    HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/kernel/sun4m_irq.c linux/arch/sparc/kernel/sun4m_irq.c
--- v1.3.98/linux/arch/sparc/kernel/sun4m_irq.c	Wed Apr 24 17:00:35 1996
+++ linux/arch/sparc/kernel/sun4m_irq.c	Mon May  6 12:26:03 1996
@@ -41,10 +41,10 @@
 /* These tables only apply for interrupts greater than 15..
  * 
  * any intr value below 0x10 is considered to be a soft-int
- * this may be useful or it may not.. but thats how I've done it.
+ * this may be useful or it may not.. but that's how I've done it.
  * and it won't clash with what OBP is telling us about devices.
  *
- * take an encoded intr value and lookup if its valid
+ * take an encoded intr value and lookup if it's valid
  * then get the mask bits that match from irq_mask
  */
 static unsigned char irq_xlate[32] = {
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/kernel/switch.S linux/arch/sparc/kernel/switch.S
--- v1.3.98/linux/arch/sparc/kernel/switch.S	Sun Apr 21 19:22:00 1996
+++ linux/arch/sparc/kernel/switch.S	Mon May  6 12:26:03 1996
@@ -66,7 +66,7 @@
 
 #ifdef __SMP__
 	/* Because of nasty register windows this is the only way
-	 * to start a processor into it's cpu_idle() thread.
+	 * to start a processor into its cpu_idle() thread.
 	 */
 
 	.globl	C_LABEL(sparc_cpusched)
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/kernel/wof.S linux/arch/sparc/kernel/wof.S
--- v1.3.98/linux/arch/sparc/kernel/wof.S	Sun Apr 21 19:22:01 1996
+++ linux/arch/sparc/kernel/wof.S	Mon May  6 12:26:03 1996
@@ -130,7 +130,7 @@
 	/* Wow, user windows have to be dealt with, this is dirty
 	 * and messy as all hell.  And difficult to follow if you
 	 * are approaching the infamous register window trap handling
-	 * problem for the first time. DONT LOOK!
+	 * problem for the first time. DON'T LOOK!
 	 *
 	 * Note that how the execution path works out, the new %wim
 	 * will be left for us in the global temporary register,
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c
--- v1.3.98/linux/arch/sparc/mm/srmmu.c	Sat Apr 27 15:19:49 1996
+++ linux/arch/sparc/mm/srmmu.c	Mon May  6 12:26:03 1996
@@ -1834,7 +1834,7 @@
 	}
 #endif
 	if(!num_contexts) {
-		prom_printf("Something wrong, cant find cpu node in paging_init.\n");
+		prom_printf("Something wrong, can't find cpu node in paging_init.\n");
 		prom_halt();
 	}
 		
diff -u --recursive --new-file v1.3.98/linux/arch/sparc/prom/tree.c linux/arch/sparc/prom/tree.c
--- v1.3.98/linux/arch/sparc/prom/tree.c	Sun Apr 21 19:22:02 1996
+++ linux/arch/sparc/prom/tree.c	Mon May  6 12:26:04 1996
@@ -83,7 +83,7 @@
 	return prom_nodeops->no_getprop(node, prop, buffer);
 }
 
-/* Acquire an integer property and return it's value.  Returns -1
+/* Acquire an integer property and return its value.  Returns -1
  * on failure.
  */
 int
diff -u --recursive --new-file v1.3.98/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v1.3.98/linux/drivers/block/Config.in	Fri Apr 12 15:51:49 1996
+++ linux/drivers/block/Config.in	Sun May  5 09:09:59 1996
@@ -42,7 +42,7 @@
   define_bool CONFIG_BLK_DEV_HD y
 fi
 
-bool 'XT harddisk support' CONFIG_BLK_DEV_XD
+tristate 'XT harddisk support' CONFIG_BLK_DEV_XD
 bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD
 if [ "$CONFIG_BLK_DEV_MD" = "y" ]; then
   tristate '   Linear (append) mode' CONFIG_MD_LINEAR
diff -u --recursive --new-file v1.3.98/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v1.3.98/linux/drivers/block/Makefile	Sat Mar 16 13:52:13 1996
+++ linux/drivers/block/Makefile	Sun May  5 09:09:40 1996
@@ -99,6 +99,10 @@
 
 ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
+else
+  ifeq ($(CONFIG_BLK_DEV_XD),m)
+  M_OBJS += xd.o
+  endif
 endif
 
 ifeq ($(CONFIG_BLK_DEV_MD),y)
diff -u --recursive --new-file v1.3.98/linux/drivers/block/README.ide linux/drivers/block/README.ide
--- v1.3.98/linux/drivers/block/README.ide	Sun May  5 08:51:58 1996
+++ linux/drivers/block/README.ide	Mon May  6 12:26:04 1996
@@ -485,7 +485,7 @@
 I agreed that you might be able to concoct a benchmark that was affected,
 but it has had no real world effect for me or a lot of other people.  Disabling
 IDE prefetch has the effect of a small increase in PCI bus busy at a time
-when the CPU is giving all it's CPU cycles to the IDE driver (because the
+when the CPU is giving all its CPU cycles to the IDE driver (because the
 RZ1000 can't run DMA and the driver has to be in a PIO loop) and therefore
 the CPU can't do much of anything else anyway.
 
diff -u --recursive --new-file v1.3.98/linux/drivers/block/amiflop.c linux/drivers/block/amiflop.c
--- v1.3.98/linux/drivers/block/amiflop.c	Sat Apr 27 15:19:50 1996
+++ linux/drivers/block/amiflop.c	Mon May  6 12:44:30 1996
@@ -21,7 +21,7 @@
  *  - added command line and machine based default for "silent" df0
  *
  *  december 1995 adapted for 1.2.13pl4 by Joerg Dorchain
- *  - works but I think its inefficient. (look in redo_fd_request)
+ *  - works but I think it's inefficient. (look in redo_fd_request)
  *    But the changes were very efficient. (only three and a half lines)
  *
  *  january 1995 added special ioctl for tracking down read/write problems
@@ -675,7 +675,7 @@
               len_desc;/* 2 */
 unsigned short crc;     /* on 68000 we got an alignment problem, 
                            but this compiler solves it  by adding silently 
-                           adding a pad byte so data wont fit
+                           adding a pad byte so data won't fit
                            and this cost about 3h to discover.... */
 unsigned char gap1[22];     /* for longword-alignedness (0x4e) */
 };
@@ -1780,3 +1780,12 @@
 
   return 0;
 }
+
+#ifndef MODULE
+/*
+ * This is just a dummy function to keep fs/super.c happy.
+ */
+void floppy_eject(void)
+{
+}
+#endif
diff -u --recursive --new-file v1.3.98/linux/drivers/block/ataflop.c linux/drivers/block/ataflop.c
--- v1.3.98/linux/drivers/block/ataflop.c	Sat Apr 27 15:19:50 1996
+++ linux/drivers/block/ataflop.c	Mon May  6 12:44:30 1996
@@ -1945,4 +1945,12 @@
 	timer_table[FLOPPY_TIMER].fn = 0;
 	kfree (DMABuffer);
 }
+#else
+/*
+ * This is just a dummy function to keep fs/super.c happy.
+ */
+void floppy_eject(void)
+{
+}
 #endif
+
diff -u --recursive --new-file v1.3.98/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.98/linux/drivers/block/ll_rw_blk.c	Wed Apr 24 17:00:36 1996
+++ linux/drivers/block/ll_rw_blk.c	Mon May  6 07:28:51 1996
@@ -206,18 +206,18 @@
 	else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
 }
 
-static inline void drive_stat_acct(int cmd, unsigned long nr_sectors, short disk_index)
+static inline void drive_stat_acct(int cmd, unsigned long nr_sectors,
+                                   short disk_index)
 {
 	kstat.dk_drive[disk_index]++;
 	if (cmd == READ) {
 		kstat.dk_drive_rio[disk_index]++;
 		kstat.dk_drive_rblk[disk_index] += nr_sectors;
-	}
-	else if (cmd == WRITE) {
+	} else if (cmd == WRITE) {
 		kstat.dk_drive_wio[disk_index]++;
 		kstat.dk_drive_wblk[disk_index] += nr_sectors;
 	} else
-		printk("drive_stat_acct: cmd not R/W?\n");
+		printk(KERN_ERR "drive_stat_acct: cmd not R/W?\n");
 }
 
 /*
@@ -289,9 +289,15 @@
 	if (blk_size[major])
 		if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
 			bh->b_state = 0;
-			printk("attempt to access beyond end of device\n");
-			printk("%s: rw=%d, want=%d, limit=%d\n", kdevname(bh->b_rdev),
-			 rw, (sector + count)>>1, blk_size[major][MINOR(bh->b_rdev)]);
+                        /* This may well happen - the kernel calls bread()
+                           without checking the size of the device, e.g.,
+                           when mounting a device. */
+			printk(KERN_INFO
+                               "attempt to access beyond end of device\n");
+			printk(KERN_INFO "%s: rw=%d, want=%d, limit=%d\n",
+                               kdevname(bh->b_rdev), rw,
+                               (sector + count)>>1,
+                               blk_size[major][MINOR(bh->b_rdev)]);
 			return;
 		}
 	/* Uhhuh.. Nasty dead-lock possible here.. */
@@ -330,7 +336,8 @@
 			max_req = (NR_REQUEST * 2) / 3;
 			break;
 		default:
-			printk("make_request: bad block dev cmd, must be R/W/RA/WA\n");
+			printk(KERN_ERR "make_request: bad block dev cmd,"
+                               " must be R/W/RA/WA\n");
 			unlock_buffer(bh);
 			return;
 	}
@@ -442,13 +449,13 @@
 		bh++;
 		if (--nr <= 0)
 			return;
-	};
+	}
 
 	dev = NULL;
 	if ((major = MAJOR(bh[0]->b_dev)) < MAX_BLKDEV)
 		dev = blk_dev + major;
 	if (!dev || !dev->request_fn) {
-		printk(
+		printk(KERN_ERR
 	"ll_rw_block: Trying to read nonexistent block-device %s (%ld)\n",
 		kdevname(bh[0]->b_dev), bh[0]->b_blocknr);
 		goto sorry;
@@ -465,7 +472,7 @@
 	/* Verify requested block sizes.  */
 	for (i = 0; i < nr; i++) {
 		if (bh[i] && bh[i]->b_size != correct_size) {
-			printk("ll_rw_block: device %s: "
+			printk(KERN_NOTICE "ll_rw_block: device %s: "
 			       "only %d-char blocks implemented (%lu)\n",
 			       kdevname(bh[0]->b_dev),
 			       correct_size, bh[i]->b_size);
@@ -484,7 +491,7 @@
 	}
 
 	if ((rw == WRITE || rw == WRITEA) && is_read_only(bh[0]->b_dev)) {
-		printk("Can't write to read-only device %s\n",
+		printk(KERN_NOTICE "Can't write to read-only device %s\n",
 		       kdevname(bh[0]->b_dev));
 		goto sorry;
 	}
@@ -519,7 +526,8 @@
 	struct semaphore sem = MUTEX_LOCKED;
 
 	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
-		printk("ll_rw_swap_file: trying to swap nonexistent block-device\n");
+		printk(KERN_NOTICE "ll_rw_swap_file: trying to swap to"
+                                   " nonexistent block-device\n");
 		return;
 	}
 	switch (rw) {
@@ -527,7 +535,8 @@
 			break;
 		case WRITE:
 			if (is_read_only(dev)) {
-				printk("Can't swap to read-only device %s\n",
+				printk(KERN_NOTICE
+                                       "Can't swap to read-only device %s\n",
 					kdevname(dev));
 				return;
 			}
@@ -547,7 +556,8 @@
 			if (major==MD_MAJOR &&
 			    md_map (MINOR(dev), &rdev,
 				    &rsector, buffersize >> 9)) {
-			        printk ("Bad md_map in ll_rw_page_size\n");
+			        printk (KERN_ERR
+                                        "Bad md_map in ll_rw_page_size\n");
 				return;
 			}
 #endif
diff -u --recursive --new-file v1.3.98/linux/drivers/block/loop.c linux/drivers/block/loop.c
--- v1.3.98/linux/drivers/block/loop.c	Wed Apr 24 17:00:37 1996
+++ linux/drivers/block/loop.c	Mon May  6 07:28:51 1996
@@ -133,12 +133,12 @@
 	int	size;
 
 	if (S_ISREG(lo->lo_inode->i_mode))
-		size = (lo->lo_inode->i_size - lo->lo_offset) / 1024;
+		size = (lo->lo_inode->i_size - lo->lo_offset) / BLOCK_SIZE;
 	else {
-		if (blk_size[MAJOR(lo->lo_device)])
-			size = ((blk_size[MAJOR(lo->lo_device)]
-				 [MINOR(lo->lo_device)]) -
-				(lo->lo_offset/1024));
+		kdev_t lodev = lo->lo_device;
+		if (blk_size[MAJOR(lodev)])
+			size = blk_size[MAJOR(lodev)][MINOR(lodev)] -
+                                lo->lo_offset / BLOCK_SIZE;
 		else
 			size = MAX_DISK_SIZE;
 	}
@@ -307,11 +307,15 @@
 static int loop_set_status(struct loop_device *lo, struct loop_info *arg)
 {
 	struct loop_info info;
+	int err;
 
 	if (!lo->lo_inode)
 		return -ENXIO;
 	if (!arg)
 		return -EINVAL;
+	err = verify_area(VERIFY_READ, arg, sizeof(info));
+	if (err)
+		return err;
 	memcpy_fromfs(&info, arg, sizeof(info));
 	if ((unsigned int) info.lo_encrypt_key_size > LO_KEY_SIZE)
 		return -EINVAL;
@@ -349,11 +353,15 @@
 static int loop_get_status(struct loop_device *lo, struct loop_info *arg)
 {
 	struct loop_info	info;
+	int err;
 	
 	if (!lo->lo_inode)
 		return -ENXIO;
 	if (!arg)
 		return -EINVAL;
+	err = verify_area(VERIFY_WRITE, arg, sizeof(info));
+	if (err)
+		return err;
 	memset(&info, 0, sizeof(info));
 	info.lo_number = lo->lo_number;
 	info.lo_device = kdev_t_to_nr(lo->lo_inode->i_dev);
diff -u --recursive --new-file v1.3.98/linux/drivers/block/xd.c linux/drivers/block/xd.c
--- v1.3.98/linux/drivers/block/xd.c	Sun Mar 31 00:13:16 1996
+++ linux/drivers/block/xd.c	Sun May  5 09:08:29 1996
@@ -17,6 +17,9 @@
  *   interrupts enabled and Linus didn't want to enable them in that first
  *   phase. xd_geninit() is the place to do these kinds of things anyway,
  *   he says.
+ *
+ * Modularized: 04/10/96 by Todd Fries, tfries@umr.edu
+ *
  */
 
 
@@ -64,6 +67,7 @@
 
 static XD_SIGNATURE xd_sigs[] = {
 	{ 0x0000,"Override geometry handler",NULL,xd_override_init_drive,"n unknown" }, /* Pat Mackinlay, pat@it.com.au */
+	{ 0x000B,"CRD18A   Not an IBM rom. (C) Copyright Data Technology Corp. 05/31/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Todd Fries, tfries@umr.edu */
 	{ 0x000B,"CXD23A Not an IBM ROM (C)Copyright Data Technology Corp 12/03/88",xd_dtc_init_controller,xd_dtc_init_drive," DTC 5150X" }, /* Pat Mackinlay, pat@it.com.au */
 	{ 0x0008,"07/15/86 (C) Copyright 1986 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1002AWX1" }, /* Ian Justman, citrus!ianj@csusac.ecs.csus.edu */
 	{ 0x0008,"06/24/88 (C) Copyright 1988 Western Digital Corp",xd_wd_init_controller,xd_wd_init_drive," Western Digital 1004A27X" }, /* Dave Thaler, thalerd@engin.umich.edu */
@@ -89,7 +93,11 @@
 	6,		/* Bits to shift to get real from partition */
 	1 << 6,		/* Number of partitions per real */
 	XD_MAXDRIVES,	/* maximum number of real */
-	xd_geninit,	/* init function */
+#ifdef MODULE
+	NULL,		/* called from init_module */
+#else
+        xd_geninit,     /* init function */
+#endif
 	xd,		/* hd struct */
 	xd_sizes,	/* block sizes */
 	0,		/* number */
@@ -726,4 +734,24 @@
 	if (xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 2))
 		printk("xd_setparam: error setting characteristics for drive %d\n",drive);
 }
+
+
+#ifdef MODULE
+int init_module(void)
+{
+	int error = xd_init();
+	if (!error)
+	{
+		printk(KERN_INFO "XD: Loaded as a module.\n");
+		xd_geninit(&(struct gendisk) { 0,0,0,0,0,0,0,0,0,0,0 });
+	}
+        
+	return error;
+}
+
+void cleanup_module(void)
+{
+	unregister_blkdev(MAJOR_NR, "xd");
+}
+#endif /* MODULE */
 
diff -u --recursive --new-file v1.3.98/linux/drivers/cdrom/aztcd.c linux/drivers/cdrom/aztcd.c
--- v1.3.98/linux/drivers/cdrom/aztcd.c	Sun May  5 08:51:59 1996
+++ linux/drivers/cdrom/aztcd.c	Sun May  5 08:51:13 1996
@@ -150,7 +150,7 @@
                 Werner Zimmermann, April 29, 96
         V2.40   Reorganized the placement of functions in the source code file
                 to reflect the layered approach; did not actually change code
-                Werner Zimmermann, Mai 1, 96
+                Werner Zimmermann, May 1, 96
 */
 #include <linux/module.h>
 #include <linux/errno.h>
diff -u --recursive --new-file v1.3.98/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v1.3.98/linux/drivers/char/ChangeLog	Sun May  5 08:51:59 1996
+++ linux/drivers/char/ChangeLog	Sun May  5 08:51:13 1996
@@ -1,6 +1,6 @@
 Wed Apr 24 14:02:04 1996  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
-	* random.c (add_timer_randomness): Use 2nd derivitive as well to
+	* random.c (add_timer_randomness): Use 2nd derivative as well to
 		better estimate entropy.
 
 		(rand_initialize): Explicitly initialize all the pointers
diff -u --recursive --new-file v1.3.98/linux/drivers/char/console.c linux/drivers/char/console.c
--- v1.3.98/linux/drivers/char/console.c	Wed Mar 20 11:39:51 1996
+++ linux/drivers/char/console.c	Mon May  6 07:28:51 1996
@@ -864,7 +864,7 @@
 				toggle_meta = 0;
 				break;
 			case 11: /* ANSI X3.64-1979 (SCO-ish?)
-				  * Select first alternate font, let's
+				  * Select first alternate font, lets
 				  * chars < 32 be displayed as ROM chars.
 				  */
 				translate = set_translate(IBMPC_MAP);
diff -u --recursive --new-file v1.3.98/linux/drivers/char/fbmem.c linux/drivers/char/fbmem.c
--- v1.3.98/linux/drivers/char/fbmem.c	Tue Apr 23 13:57:09 1996
+++ linux/drivers/char/fbmem.c	Mon May  6 12:44:31 1996
@@ -45,6 +45,10 @@
 	if (!current->tty)
 		return fg_console;
 
+	if (current->tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
+		/* XXX Should report error here? */
+		return fg_console;
+
 	if (MINOR(current->tty->device) < 1)
 		return fg_console;
 
@@ -180,6 +184,18 @@
 			if (i) return i;
 		}
 		return (fb->fb_get_cmap(&cmap, 0, PROC_CONSOLE()));
+	case FBIOPAN_DISPLAY:
+		i = verify_area(VERIFY_WRITE, (void *) arg, 
+				sizeof(struct fb_var_screeninfo));
+		if (i) return i;
+		memcpy_fromfs(&var, (void *) arg, sizeof(var));
+		i=fb->fb_pan_display(&var, PROC_CONSOLE());
+		memcpy_tofs((void *) arg, &var, sizeof(var));
+		fbidx=GET_FB_IDX(inode->i_rdev);
+		vidx=GET_FB_VAR_IDX(inode->i_rdev);
+		if (! i && vidx)
+			registered_fb_var[fbidx][vidx-1]=var;
+		return i;
 	default:
 		return (fb->fb_ioctl(inode, file, cmd, arg, PROC_CONSOLE()));
 	}
@@ -199,8 +215,11 @@
 	vma->vm_offset += fix.smem_start;
 	if (vma->vm_offset & ~PAGE_MASK)
 		return -ENXIO;
-	if (boot_info.cputype & CPU_68040)
+	if (m68k_is040or060) {
 		pgprot_val(vma->vm_page_prot) &= _CACHEMASK040;
+		/* Use write-through cache mode */
+		pgprot_val(vma->vm_page_prot) |= _PAGE_CACHE040W;
+	}
 	if (remap_page_range(vma->vm_start, vma->vm_offset,
 			     vma->vm_end - vma->vm_start, vma->vm_page_prot))
 		return -EAGAIN;
diff -u --recursive --new-file v1.3.98/linux/drivers/char/ftape/ftape-read.c linux/drivers/char/ftape/ftape-read.c
--- v1.3.98/linux/drivers/char/ftape/ftape-read.c	Thu Mar 14 11:53:45 1996
+++ linux/drivers/char/ftape/ftape-read.c	Mon May  6 12:26:05 1996
@@ -606,8 +606,8 @@
 		 *  get more than 29 Kb from it (As it only contains this much).
 		 *  This works only for sequential access, so random access should
 		 *  stay away from this `last' segment.
-		 *  Note: ftape_seg_pos points to the next segment what will be
-		 *        read, so it's one too hight here!
+		 *  Note: ftape_seg_pos points to the next segment that will be
+		 *        read, so it's one too high here!
 		 */
 		if (!eof_mark && ftape_seg_pos - 1 >= ftape_last_segment.id) {
 			TRACEi(5, "remaining of last segment:", remaining);
diff -u --recursive --new-file v1.3.98/linux/drivers/char/ftape/kernel-interface.c linux/drivers/char/ftape/kernel-interface.c
--- v1.3.98/linux/drivers/char/ftape/kernel-interface.c	Sat Mar 16 23:13:55 1996
+++ linux/drivers/char/ftape/kernel-interface.c	Mon May  6 12:26:05 1996
@@ -45,7 +45,7 @@
 /*      Global vars.
  */
 
-/* Allocating a 96Kb DMAable buffer in one chunk wont work due to
+/* Allocating a 96Kb DMAable buffer in one chunk won't work due to
  * memory fragmentation.  To avoid this, it is broken up into
  * NR_BUFFERS chunks of 32Kbyte. --khp
  */
diff -u --recursive --new-file v1.3.98/linux/drivers/char/istallion.c linux/drivers/char/istallion.c
--- v1.3.98/linux/drivers/char/istallion.c	Wed Apr 24 17:00:38 1996
+++ linux/drivers/char/istallion.c	Mon May  6 12:26:05 1996
@@ -3386,7 +3386,7 @@
 	}
 
 /*
- *	The per-board operations structure is all setup, so now lets go
+ *	The per-board operations structure is all set up, so now let's go
  *	and get the board operational. Firstly initialize board configuration
  *	registers. Then if we are using the higher 1Mb support then set up
  *	the memory mapping info so we can get at the boards shared memory.
@@ -3542,7 +3542,7 @@
 	}
 
 /*
- *	The per-board operations structure is all setup, so now lets go
+ *	The per-board operations structure is all set up, so now let's go
  *	and get the board operational. Firstly initialize board configuration
  *	registers. Then if we are using the higher 1Mb support then set up
  *	the memory mapping info so we can get at the boards shared memory.
diff -u --recursive --new-file v1.3.98/linux/drivers/char/lp.c linux/drivers/char/lp.c
--- v1.3.98/linux/drivers/char/lp.c	Sun May  5 08:51:59 1996
+++ linux/drivers/char/lp.c	Mon May  6 13:20:19 1996
@@ -183,14 +183,14 @@
 				if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
 					 LP_STAT(minor).maxrun = lp_table[minor].runchars;
 				status = LP_S(minor);
-				if ((status & LP_OFFL) || !(status & LP_PSELECD)) {
-					printk(KERN_INFO "lp%d off-line\n", minor);
-					if (LP_F(minor) & LP_ABORT)
-						return rc?rc:-EIO;
-				} else if ((status & LP_POUTPA)) {
+				if ((status & LP_POUTPA)) {
 					printk(KERN_INFO "lp%d out of paper\n", minor);
 					if (LP_F(minor) & LP_ABORT)
 						return rc?rc:-ENOSPC;
+				} else if (!(status & LP_PSELECD)) {
+					printk(KERN_INFO "lp%d off-line\n", minor);
+					if (LP_F(minor) & LP_ABORT)
+						return rc?rc:-EIO;
 				} else if (!(status & LP_PERRORP)) {
 					printk(KERN_ERR "lp%d printer error\n", minor);
 					if (LP_F(minor) & LP_ABORT)
@@ -248,18 +248,18 @@
 				 LP_STAT(minor).maxrun = lp_table[minor].runchars;
 			status = LP_S(minor);
 
-			if ((status & LP_OFFL) || !(status & LP_PSELECD)) {
-				printk(KERN_INFO "lp%d off-line\n", minor);
+			if (status & LP_POUTPA) {
+				printk(KERN_INFO "lp%d out of paper\n", minor);
 				if(LP_F(minor) & LP_ABORT)
-					return temp-buf?temp-buf:-EIO;
+					return temp-buf?temp-buf:-ENOSPC;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
 				schedule();
 			} else
-			if (status & LP_POUTPA) {
-				printk(KERN_INFO "lp%d out of paper\n", minor);
+			if (!(status & LP_PSELECD)) {
+				printk(KERN_INFO "lp%d off-line\n", minor);
 				if(LP_F(minor) & LP_ABORT)
-					return temp-buf?temp-buf:-ENOSPC;
+					return temp-buf?temp-buf:-EIO;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
 				schedule();
diff -u --recursive --new-file v1.3.98/linux/drivers/char/lp_m68k.c linux/drivers/char/lp_m68k.c
--- v1.3.98/linux/drivers/char/lp_m68k.c	Sat Apr 27 15:19:51 1996
+++ linux/drivers/char/lp_m68k.c	Mon May  6 12:49:43 1996
@@ -236,13 +236,13 @@
 	   been printed at all. */
 	
 	if (lp_table[dev].lp_has_pout(dev)) {
-	  printk("lp%d: paper-out\n",dev);
+	  printk(KERN_NOTICE "lp%d: paper-out\n",dev);
 	  if (!rc) rc = -ENOSPC;
 	} else if (!lp_table[dev].lp_is_online(dev)) {
-	  printk("lp%d: off-line\n",dev);
+	  printk(KERN_NOTICE "lp%d: off-line\n",dev);
 	  if (!rc) rc = -EIO;
 	} else if (lp_table[dev].lp_is_busy(dev)) {
-	  printk("lp%d: on fire\n",dev);
+	  printk(KERN_NOTICE "lp%d: on fire\n",dev);
 	  if (!rc) rc = -EIO;
 	}
 	if (lp_table[dev].flags & LP_ABORT)
@@ -294,14 +294,14 @@
 #endif
 		} else { /* if printer timed out */
 			if (lp_table[dev].lp_has_pout(dev)) {
-				printk("lp%d: out of paper\n",dev);
+				printk(KERN_NOTICE "lp%d: out of paper\n",dev);
 				if (lp_table[dev].flags & LP_ABORT)
 					return temp - buf ? temp-buf : -ENOSPC;
 				current->state = TASK_INTERRUPTIBLE;
 				current->timeout = jiffies + LP_TIMEOUT_POLLED;
 				schedule();
 			} else if (!lp_table[dev].lp_is_online(dev)) {
-				printk("lp%d: off-line\n",dev);
+				printk(KERN_NOTICE "lp%d: off-line\n",dev);
 				if (lp_table[dev].flags & LP_ABORT)
 					return temp - buf ? temp-buf : -EIO;
 				current->state = TASK_INTERRUPTIBLE;
@@ -310,7 +310,7 @@
 			} else
 	                /* not offline or out of paper. on fire? */
 			if (lp_table[dev].lp_is_busy(dev)) {
-				printk("lp%d: on fire\n",dev);
+				printk(KERN_NOTICE "lp%d: on fire\n",dev);
 				if (lp_table[dev].flags & LP_ABORT)
 					return temp - buf ? temp-buf : -EFAULT;
 				current->state = TASK_INTERRUPTIBLE;
@@ -447,7 +447,7 @@
 
 #if WHICH_DRIVER == FORCE_POLLING
 	lp_irq = 0;
-	printk("lp_init: lp using polling driver\n");
+	printk(KERN_INFO "lp_init: lp using polling driver\n");
 #else
 
 #ifdef CONFIG_AMIGA
@@ -462,13 +462,13 @@
 #endif
 
 	if (lp_irq)
-		printk("lp_init: lp using interrupt\n");
+		printk(KERN_INFO "lp_init: lp using interrupt\n");
 	else
 
 #if WHICH_DRIVER == PREFER_INTERRUPT
-		printk("lp_init: lp using polling driver\n");
+		printk(KERN_INFO "lp_init: lp using polling driver\n");
 #else
-		printk("lp_init: cant get interrupt, and polling driver not configured\n");
+		printk(KERN_WARNING "lp_init: can't get interrupt, and polling driver not configured\n");
 #endif
 #endif
 
diff -u --recursive --new-file v1.3.98/linux/drivers/char/pcxx.c linux/drivers/char/pcxx.c
--- v1.3.98/linux/drivers/char/pcxx.c	Fri Apr 19 10:07:58 1996
+++ linux/drivers/char/pcxx.c	Mon May  6 12:26:06 1996
@@ -38,7 +38,7 @@
    19200 = 57600
    38400 = 115K
    The driver supports the native 57.6K and 115K Baudrates under Linux, but
-   some distributions like Slackware 3.0 dont like these high baudrates.
+   some distributions like Slackware 3.0 don't like these high baudrates.
 */
 
 #include <linux/mm.h>
@@ -1602,7 +1602,7 @@
 	{
 		ch->digiext.digi_flags |= DIGI_FAST;
 		res |= FEP_HUPCL;
-		/* This gets strange but if we dont do this we will get 78600
+		/* This gets strange but if we don't do this we will get 78600
 		 * instead of 115200. 57600 is mapped to 50 baud yielding 57600 in
 		 * FAST mode. 115200 is mapped to 75. We need to map it to 110 to
 		 * do 115K
diff -u --recursive --new-file v1.3.98/linux/drivers/char/psaux.c linux/drivers/char/psaux.c
--- v1.3.98/linux/drivers/char/psaux.c	Tue Apr  2 13:32:20 1996
+++ linux/drivers/char/psaux.c	Tue May  7 12:30:49 1996
@@ -315,13 +315,13 @@
 		aux_count--;
 		return -EBUSY;
 	}
+	MOD_INC_USE_COUNT;
 	poll_aux_status();
 	outb_p(AUX_ENABLE,AUX_COMMAND);		/* Enable Aux */
 	aux_write_dev(AUX_ENABLE_DEV);		/* enable aux device */
 	aux_write_cmd(AUX_INTS_ON);		/* enable controller ints */
 	poll_aux_status();
 	aux_ready = 0;
-	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -u --recursive --new-file v1.3.98/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- v1.3.98/linux/drivers/char/pty.c	Sat Apr 27 15:19:51 1996
+++ linux/drivers/char/pty.c	Mon May  6 14:56:04 1996
@@ -181,7 +181,9 @@
 
 int pty_open(struct tty_struct *tty, struct file * filp)
 {
+#if PTY_SLAVE_WAITS_ON_OPEN
 	struct wait_queue wait = { current, NULL };
+#endif
 	int	retval;
 	int	line;
 	struct	pty_struct *pty;
diff -u --recursive --new-file v1.3.98/linux/drivers/char/random.c linux/drivers/char/random.c
--- v1.3.98/linux/drivers/char/random.c	Sun May  5 08:51:59 1996
+++ linux/drivers/char/random.c	Sun May  5 08:51:13 1996
@@ -169,7 +169,7 @@
  * 	fi
  *	dd if=/dev/urandom of=/etc/random-seed count=1
  *
- * and the following lines in an approproate script which is run as
+ * and the following lines in an appropriate script which is run as
  * the system is shutdown:
  * 
  *	# Carry a random seed from shut-down to start-up
@@ -574,7 +574,7 @@
 #define HASH_TRANSFORM SHATransform
 
 /*
- * SHA transform algorith, taken from code written by Peter Gutman,
+ * SHA transform algorithm, taken from code written by Peter Gutman,
  * and apparently in the public domain.
  */
 
@@ -615,7 +615,7 @@
     E = digest[ 4 ];
     memcpy( eData, data, 16*sizeof(__u32));
 
-    /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
+    /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */
     subRound( A, B, C, D, E, f1, K1, eData[  0 ] );
     subRound( E, A, B, C, D, f1, K1, eData[  1 ] );
     subRound( D, E, A, B, C, f1, K1, eData[  2 ] );
diff -u --recursive --new-file v1.3.98/linux/drivers/char/rtc.c linux/drivers/char/rtc.c
--- v1.3.98/linux/drivers/char/rtc.c	Sun May  5 08:51:59 1996
+++ linux/drivers/char/rtc.c	Tue May  7 08:18:50 1996
@@ -30,7 +30,7 @@
  *
  */
 
-#define RTC_VERSION		"1.05"
+#define RTC_VERSION		"1.06"
 
 #define RTC_IRQ 	8	/* Can't see this changing soon.	*/
 #define RTC_IO_BASE	0x70	/* Or this...				*/
@@ -56,8 +56,6 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 
-#include <time.h>
-
 /*
  *	We sponge a minor off of the misc major. No need slurping
  *	up another valuable major dev number for this.
@@ -81,14 +79,14 @@
 static int rtc_select(struct inode *inode, struct file *file,
 			int sel_type, select_table *wait);
 
-void get_rtc_time (struct tm *rtc_tm);
-void get_rtc_alm_time (struct tm *alm_tm);
+void get_rtc_time (struct rtc_time *rtc_tm);
+void get_rtc_alm_time (struct rtc_time *alm_tm);
 void rtc_dropped_irq(unsigned long data);
 
-inline void set_rtc_irq_bit(unsigned char bit);
-inline void mask_rtc_irq_bit(unsigned char bit);
+void set_rtc_irq_bit(unsigned char bit);
+void mask_rtc_irq_bit(unsigned char bit);
 
-unsigned char rtc_is_updating(void);
+static inline unsigned char rtc_is_updating(void);
 
 /*
  *	Bits in rtc_status. (7 bits of room for future expansion)
@@ -221,10 +219,7 @@
 			 * than 64Hz of interrupts on a multi-user machine.
 			 */
 			if ((rtc_freq > 64) && (!suser()))
-				return -EPERM;
-
-			if (rtc_freq == 0)
-				return -EINVAL;
+				return -EACCES;
 
 			if (!(rtc_status & RTC_TIMER_ON)) {
 				rtc_status |= RTC_TIMER_ON;
@@ -247,39 +242,39 @@
 		case RTC_ALM_READ:	/* Read the present alarm time */
 		{
 			/*
-			 * This returns a struct tm. Reading >= 0xc0 means
-			 * "don't care" or "match all". Only the tm_hour,
+			 * This returns a struct rtc_time. Reading >= 0xc0
+			 * means "don't care" or "match all". Only the tm_hour,
 			 * tm_min, and tm_sec values are filled in.
 			 */
 			int retval;
-			struct tm alm_tm;
+			struct rtc_time alm_tm;
 
-			retval = verify_area(VERIFY_WRITE, (struct tm*)arg, sizeof(struct tm));
+			retval = verify_area(VERIFY_WRITE, (struct rtc_time*)arg, sizeof(struct rtc_time));
 			if (retval != 0 )
 				return retval;
 
 			get_rtc_alm_time(&alm_tm);
 
-			memcpy_tofs((struct tm*)arg, &alm_tm, sizeof(struct tm));
+			memcpy_tofs((struct rtc_time*)arg, &alm_tm, sizeof(struct rtc_time));
 			
 			return 0;
 		}
 		case RTC_ALM_SET:	/* Store a time into the alarm */
 		{
 			/*
-			 * This expects a struct tm. Writing 0xff means
+			 * This expects a struct rtc_time. Writing 0xff means
 			 * "don't care" or "match all". Only the tm_hour,
 			 * tm_min and tm_sec are used.
 			 */
 			int retval;
 			unsigned char hrs, min, sec;
-			struct tm alm_tm;
+			struct rtc_time alm_tm;
 
-			retval = verify_area(VERIFY_READ, (struct tm*)arg, sizeof(struct tm));
+			retval = verify_area(VERIFY_READ, (struct rtc_time*)arg, sizeof(struct rtc_time));
 			if (retval != 0 )
 				return retval;
 
-			memcpy_fromfs(&alm_tm, (struct tm*)arg, sizeof(struct tm));
+			memcpy_fromfs(&alm_tm, (struct rtc_time*)arg, sizeof(struct rtc_time));
 
 			hrs = alm_tm.tm_hour;
 			min = alm_tm.tm_min;
@@ -313,33 +308,33 @@
 		case RTC_RD_TIME:	/* Read the time/date from RTC	*/
 		{
 			int retval;
-			struct tm rtc_tm;
+			struct rtc_time rtc_tm;
 			
-			retval = verify_area(VERIFY_WRITE, (struct tm*)arg, sizeof(struct tm));
+			retval = verify_area(VERIFY_WRITE, (struct rtc_time*)arg, sizeof(struct rtc_time));
 			if (retval !=0 )
 				return retval;
 
 			get_rtc_time(&rtc_tm);
-			memcpy_tofs((struct tm*)arg, &rtc_tm, sizeof(struct tm));
+			memcpy_tofs((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time));
 			return 0;
 		}
 		case RTC_SET_TIME:	/* Set the RTC */
 		{
 			int retval;
-			struct tm rtc_tm;
+			struct rtc_time rtc_tm;
 			unsigned char mon, day, hrs, min, sec, leap_yr;
 			unsigned char save_control, save_freq_select;
 			unsigned int yrs;
 			unsigned long flags;
 			
 			if (!suser())
-				return -EPERM;
+				return -EACCES;
 
-			retval = verify_area(VERIFY_READ, (struct tm*)arg, sizeof(struct tm));
+			retval = verify_area(VERIFY_READ, (struct rtc_time*)arg, sizeof(struct rtc_time));
 			if (retval !=0 )
 				return retval;
 
-			memcpy_fromfs(&rtc_tm, (struct tm*)arg, sizeof(struct tm));
+			memcpy_fromfs(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time));
 
 			yrs = rtc_tm.tm_year + 1900;
 			mon = rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
@@ -424,7 +419,7 @@
 			 * than 64Hz of interrupts on a multi-user machine.
 			 */
 			if ((arg > 64) && (!suser()))
-				return -EPERM;
+				return -EACCES;
 
 			while (arg > (1<<tmp))
 				tmp++;
@@ -432,7 +427,7 @@
 			/*
 			 * Check that the input was really a power of 2.
 			 */
-			if ((arg != 0) && (arg != (1<<tmp)))
+			if (arg != (1<<tmp))
 				return -EINVAL;
 
 			rtc_freq = arg;
@@ -440,13 +435,6 @@
 			save_flags(flags);
 			cli();
 			val = CMOS_READ(RTC_FREQ_SELECT) & 0xf0;
-
-			if (arg == 0) {
-				CMOS_WRITE(val, RTC_FREQ_SELECT);
-				restore_flags(flags);
-				return 0;
-			}
-
 			val |= (16 - tmp);
 			CMOS_WRITE(val, RTC_FREQ_SELECT);
 			restore_flags(flags);
@@ -557,9 +545,10 @@
 	rtc_wait = NULL;
 	save_flags(flags);
 	cli();
-	rtc_freq = CMOS_READ(RTC_FREQ_SELECT) & 0x0F;
+	/* Initialize periodic freq. to CMOS reset default, which is 1024Hz */
+	CMOS_WRITE(((CMOS_READ(RTC_FREQ_SELECT) & 0xF0) | 0x06), RTC_FREQ_SELECT);
 	restore_flags(flags);
-	rtc_freq = (rtc_freq ? (65536/(1<<rtc_freq)) : 0);
+	rtc_freq = 1024;
 	return 0;
 }
 
@@ -599,7 +588,7 @@
 int get_rtc_status(char *buf)
 {
 	char *p;
-	struct tm tm;
+	struct rtc_time tm;
 	unsigned char batt, ctrl;
 	unsigned long flags;
 
@@ -650,7 +639,7 @@
 		"DST_enable\t: %s\n"
 		"BCD\t\t: %s\n"
 		"24hr\t\t: %s\n"
-		"sqare_wave\t: %s\n"
+		"square_wave\t: %s\n"
 		"alarm_IRQ\t: %s\n"
 		"update_IRQ\t: %s\n"
 		"periodic_IRQ\t: %s\n"
@@ -672,7 +661,7 @@
 /*
  * Returns true if a clock update is in progress
  */
-inline unsigned char rtc_is_updating(void)
+static inline unsigned char rtc_is_updating(void)
 {
 	unsigned long flags;
 	unsigned char uip;
@@ -684,7 +673,7 @@
 	return uip;
 }
 
-void get_rtc_time(struct tm *rtc_tm)
+void get_rtc_time(struct rtc_time *rtc_tm)
 {
 
 	unsigned long flags, uip_watchdog = jiffies;
@@ -733,7 +722,7 @@
 
 	/*
 	 * Account for differences between how the RTC uses the values
-	 * and how they are defined in a struct tm;
+	 * and how they are defined in a struct rtc_time;
 	 */
 	if (rtc_tm->tm_year <= 69)
 		rtc_tm->tm_year += 100;
@@ -741,7 +730,7 @@
 	rtc_tm->tm_mon--;
 }
 
-void get_rtc_alm_time(struct tm *alm_tm)
+void get_rtc_alm_time(struct rtc_time *alm_tm)
 {
 	unsigned long flags;
 	unsigned char ctrl;
@@ -775,7 +764,7 @@
  * We also clear out any old irq data after an ioctl() that
  * meddles the interrupt enable/disable bits.
  */
-inline void mask_rtc_irq_bit(unsigned char bit)
+void mask_rtc_irq_bit(unsigned char bit)
 {
 	unsigned char val;
 	unsigned long flags;
@@ -790,7 +779,7 @@
 	rtc_irq_data = 0;
 }
 
-inline void set_rtc_irq_bit(unsigned char bit)
+void set_rtc_irq_bit(unsigned char bit)
 {
 	unsigned char val;
 	unsigned long flags;
diff -u --recursive --new-file v1.3.98/linux/drivers/char/stallion.c linux/drivers/char/stallion.c
--- v1.3.98/linux/drivers/char/stallion.c	Wed Apr 24 17:00:38 1996
+++ linux/drivers/char/stallion.c	Mon May  6 12:26:06 1996
@@ -495,7 +495,7 @@
 /*
  *	Free up all allocated resources used by the ports. This includes
  *	memory and interrupts. As part of this process we will also do
- *	a hangup on every open port - to try and flush out any processes
+ *	a hangup on every open port - to try to flush out any processes
  *	hanging onto ports.
  */
 	i = tty_unregister_driver(&stl_serial);
@@ -2383,7 +2383,7 @@
 /*****************************************************************************/
 
 /*
- *	Try and find and initialize all the ports on a panel. We don't care
+ *	Try to find and initialize all the ports on a panel. We don't care
  *	what sort of board these ports are on - since the port io registers
  *	are almost identical when dealing with ports.
  */
@@ -2861,7 +2861,7 @@
 
 #ifdef CONFIG_PCI
 /*
- *	If the PCI BIOS support is compiled in then lets go looking for
+ *	If the PCI BIOS support is compiled in then let's go looking for
  *	ECH-PCI boards.
  */
 	stl_findpcibrds();
diff -u --recursive --new-file v1.3.98/linux/drivers/char/vc_screen.c linux/drivers/char/vc_screen.c
--- v1.3.98/linux/drivers/char/vc_screen.c	Tue Oct 10 10:31:02 1995
+++ linux/drivers/char/vc_screen.c	Tue May  7 07:50:53 1996
@@ -149,7 +149,7 @@
 		org = screen_pos(cons, p, viewed);
 		while (count-- > 0) {
 			scr_writew((scr_readw(org) & 0xff00) |
-				   get_user(buf++), org);
+				   get_user((const unsigned char*)buf++), org);
 			org++;
 		}
 	} else {
@@ -175,7 +175,7 @@
 		}
 		if (count > 0)
 			scr_writew((scr_readw(org) & 0xff00) |
-				   get_user(buf++), org);
+				   get_user((const unsigned char*)buf++), org);
 	}
 	written = buf - buf0;
 	file->f_pos += written;
diff -u --recursive --new-file v1.3.98/linux/drivers/char/vga.c linux/drivers/char/vga.c
--- v1.3.98/linux/drivers/char/vga.c	Fri Mar 15 11:01:46 1996
+++ linux/drivers/char/vga.c	Tue May  7 07:50:53 1996
@@ -29,6 +29,10 @@
  * Colour palette handling, by Simon Tatham
  * 17-Jun-95 <sgt20@cam.ac.uk>
  *
+ * if 512 char mode is already enabled don't re-enable it,
+ * because it causes screen to flicker, by Mitja Horvat
+ * 5-May-96 <mitja.horvat@guest.arnes.si>
+ *
  */
 
 #include <linux/sched.h>
@@ -308,6 +312,7 @@
 set_get_font(char * arg, int set, int ch512)
 {
 #ifdef CAN_LOAD_EGA_FONTS
+	static int ch512enabled = 0;
 	int i;
 	char *charmap;
 	int beg;
@@ -432,8 +437,11 @@
 	outb_p( 0x10, gr_port_val );    /* enable even-odd addressing */
 	outb_p( 0x06, gr_port_reg );
 	outb_p( beg, gr_port_val );     /* map starts at b800:0 or b000:0 */
-	if (set)			/* attribute controller */
+
+	/* if 512 char mode is already enabled don't re-enable it. */
+	if ((set)&&(ch512!=ch512enabled))	/* attribute controller */
 	  {
+	    ch512enabled=ch512;
 	    /* 256-char: enable intensity bit
 	       512-char: disable intensity bit */
 	    inb_p( video_port_status );	/* clear address flip-flop */
diff -u --recursive --new-file v1.3.98/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v1.3.98/linux/drivers/isdn/isdn_common.c	Sun Apr 21 19:22:06 1996
+++ linux/drivers/isdn/isdn_common.c	Mon May  6 12:26:06 1996
@@ -489,7 +489,7 @@
                                 break;
 			if ((mi = dev->m_idx[i]) >= 0) {
 				/* Schedule CONNECT-Message to any tty, waiting for it and
-				 * set DCD-bit of it's modem-status.
+				 * set DCD-bit of its modem-status.
 				 */
 				if (dev->mdm.info[mi].flags &
 				    (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) {
diff -u --recursive --new-file v1.3.98/linux/drivers/isdn/isdn_net.c linux/drivers/isdn/isdn_net.c
--- v1.3.98/linux/drivers/isdn/isdn_net.c	Sun Apr 21 19:22:06 1996
+++ linux/drivers/isdn/isdn_net.c	Mon May  6 12:26:06 1996
@@ -46,7 +46,7 @@
  * Correct ARP-handling for ETHERNET-encapsulation.
  *
  * Revision 1.2  1996/01/22 05:05:12  fritz
- * Changed returncode-logic for isdn_net_start_xmit() and it's
+ * Changed returncode-logic for isdn_net_start_xmit() and its
  * helper-functions.
  * Changed handling of buildheader for RAWIP and ETHERNET-encapsulation.
  *
@@ -195,7 +195,7 @@
 /*
  * Handle status-messages from ISDN-interfacecard.
  * This function is called from within the main-status-dispatcher
- * isdn_status_callback, which itself is called from the lowlevel-driver.
+ * isdn_status_callback, which itself is called from the low-level driver.
  * Return: 1 = Event handled, 0 = not for us or unknown Event.
  */
 int
@@ -1200,7 +1200,7 @@
                         return 0;
                 }
                 /*
-                 *      Try and get ARP to resolve the header.
+                 *      Try to get ARP to resolve the header.
                  */
 #ifdef CONFIG_INET       
                 ret = arp_find((unsigned char *)&(eth->h_dest), dst, dev, dev->pa_addr,skb)? 1 : 0;
@@ -1775,7 +1775,7 @@
 }
 
 /*
- * Allocate a new network-interface and initialize it's data structures.
+ * Allocate a new network-interface and initialize its data structures.
  */
 char *
 isdn_net_new(char *name, struct device *master)
diff -u --recursive --new-file v1.3.98/linux/drivers/isdn/isdn_ppp.c linux/drivers/isdn/isdn_ppp.c
--- v1.3.98/linux/drivers/isdn/isdn_ppp.c	Sun Apr 21 19:22:06 1996
+++ linux/drivers/isdn/isdn_ppp.c	Mon May  6 12:26:07 1996
@@ -802,10 +802,12 @@
 	isdn_net_local *lp = nd->queue;
 	int proto = PPP_IP;	/* 0x21 */
 	struct ippp_struct *ipt = ippp_table[lp->ppp_minor];
+#if defined(CONFIG_ISDN_PPP_VJ) || defined(CONFIG_ISDN_MPP)
 	struct ippp_struct *ipts = ippp_table[lp->netdev->local.ppp_minor];
+#endif
 
         /* If packet is to be resent, it has already been processed and
-         * therefore it's first bytes are already initialized. In this case
+         * therefore its first bytes are already initialized. In this case
          * send it immediately ...
          */
         if (*((unsigned long *)skb->data) != 0)
diff -u --recursive --new-file v1.3.98/linux/drivers/net/3c501.c linux/drivers/net/3c501.c
--- v1.3.98/linux/drivers/net/3c501.c	Tue Apr  2 13:32:20 1996
+++ linux/drivers/net/3c501.c	Mon May  6 12:26:07 1996
@@ -68,7 +68,7 @@
     
     	The combination of slow receive restart and no real multicast
     filter makes the board unusable with a kernel compiled for IP
-    multicasting in a real multicast environment. Thats down to the board, 
+    multicasting in a real multicast environment. That's down to the board, 
     but even with no multicast programs running a multicast IP kernel is
     in group 224.0.0.1 and you will therefore be listening to all multicasts.
     One nv conference running over that ethernet and you can give up.
diff -u --recursive --new-file v1.3.98/linux/drivers/net/8390.c linux/drivers/net/8390.c
--- v1.3.98/linux/drivers/net/8390.c	Fri Mar  1 07:50:42 1996
+++ linux/drivers/net/8390.c	Mon May  6 11:41:57 1996
@@ -16,28 +16,30 @@
   This is not a complete driver, it must be combined with board-specific
   code such as ne.c, wd.c, 3c503.c, etc.
 
+  Seeing how at least eight drivers use this code, (not counting the
+  PCMCIA ones either) it is easy to break some card by what seems like
+  a simple innocent change. Please contact me or Donald if you think
+  you have found something that needs changing. -- PG
+
+
   Changelog:
 
   Paul Gortmaker	: remove set_bit lock, other cleanups.
   Paul Gortmaker	: add ei_get_8390_hdr() so we can pass skb's to 
 			  ei_block_input() for eth_io_copy_and_sum().
+  Paul Gortmaker	: exchange static int ei_pingpong for a #define,
+			  also add better Tx error handling.
 
-  */
 
-static const char *version =
-    "8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-
-/*
-  Braindamage remaining:
-  Much of this code should have been cleaned up, but every attempt 
-  has broken some clone part.
-  
   Sources:
   The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
   */
 
-#include <linux/module.h>
+static const char *version =
+    "8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
 
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
@@ -87,18 +89,10 @@
 #else
 int ei_debug = 1;
 #endif
-#ifdef EI_PINGPONG
-static int ei_pingpong = 1;
-#else
-static int ei_pingpong = 0;
-#endif
-
-/* Max number of packets received at one Intr.
-   Currently this may only be examined by a kernel debugger. */
-static int high_water_mark = 0;
 
 /* Index to functions. */
 static void ei_tx_intr(struct device *dev);
+static void ei_tx_err(struct device *dev);
 static void ei_receive(struct device *dev);
 static void ei_rx_overrun(struct device *dev);
 
@@ -141,8 +135,8 @@
 {
     int e8390_base = dev->base_addr;
     struct ei_device *ei_local = (struct ei_device *) dev->priv;
-    int length, send_length;
-    
+    int length, send_length, output_page;
+
 /*
  *  We normally shouldn't be called if dev->tbusy is set, but the
  *  existing code does anyway. If it has been too long since the
@@ -161,6 +155,12 @@
 			return 1;
 		}
 
+		/*
+		 * Note that if the Tx posted a TX_ERR interrupt, then the
+		 * error will have been handled from the interrupt handler.
+		 * and not here.
+		 */
+
 		printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
 		   dev->name, (txsr & ENTSR_ABT) ? "excess collisions." :
 		   (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar);
@@ -199,53 +199,77 @@
 
     send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
 
-    if (ei_local->pingpong) {
-		int output_page;
-		if (ei_local->tx1 == 0) {
-			output_page = ei_local->tx_start_page;
-			ei_local->tx1 = send_length;
-			if (ei_debug  &&  ei_local->tx2 > 0)
-				printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
-					   dev->name, ei_local->tx2, ei_local->lasttx,
-					   ei_local->txing);
-		} else if (ei_local->tx2 == 0) {
-			output_page = ei_local->tx_start_page + 6;
-			ei_local->tx2 = send_length;
-			if (ei_debug  &&  ei_local->tx1 > 0)
-				printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
-					   dev->name, ei_local->tx1, ei_local->lasttx,
-					   ei_local->txing);
-		} else {	/* We should never get here. */
-			if (ei_debug)
-				printk("%s: No Tx buffers free. irq=%d tx1=%d tx2=%d last=%d\n",
-					dev->name, dev->interrupt, ei_local->tx1, 
-					ei_local->tx2, ei_local->lasttx);
-			ei_local->irqlock = 0;
-			dev->tbusy = 1;
-			outb_p(ENISR_ALL, e8390_base + EN0_IMR);
-			return 1;
-		}
-		ei_block_output(dev, length, skb->data, output_page);
-		if (! ei_local->txing) {
-			ei_local->txing = 1;
-			NS8390_trigger_send(dev, send_length, output_page);
-			dev->trans_start = jiffies;
-			if (output_page == ei_local->tx_start_page)
-				ei_local->tx1 = -1, ei_local->lasttx = -1;
-			else
-				ei_local->tx2 = -1, ei_local->lasttx = -2;
-		} else
-			ei_local->txqueue++;
-
-		dev->tbusy = (ei_local->tx1  &&  ei_local->tx2);
-    } else {  /* No pingpong, just a single Tx buffer. */
-		ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
-		ei_local->txing = 1;
-		NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
-		dev->trans_start = jiffies;
-		dev->tbusy = 1;
+#ifdef EI_PINGPONG
+
+    /*
+     * We have two Tx slots available for use. Find the first free
+     * slot, and then perform some sanity checks. With two Tx bufs,
+     * you get very close to transmitting back-to-back packets. With
+     * only one Tx buf, the transmitter sits idle while you reload the
+     * card, leaving a substantial gap between each transmitted packet.
+     */
+
+    if (ei_local->tx1 == 0) {
+	output_page = ei_local->tx_start_page;
+	ei_local->tx1 = send_length;
+	if (ei_debug  &&  ei_local->tx2 > 0)
+		printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
+			dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing);
+    } else if (ei_local->tx2 == 0) {
+	output_page = ei_local->tx_start_page + TX_1X_PAGES;
+	ei_local->tx2 = send_length;
+	if (ei_debug  &&  ei_local->tx1 > 0)
+		printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
+			dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing);
+    } else {	/* We should never get here. */
+	if (ei_debug)
+		printk("%s: No Tx buffers free! irq=%d tx1=%d tx2=%d last=%d\n",
+			dev->name, dev->interrupt, ei_local->tx1, ei_local->tx2, ei_local->lasttx);
+	ei_local->irqlock = 0;
+	dev->tbusy = 1;
+	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+	return 1;
     }
-    
+
+    /*
+     * Okay, now upload the packet and trigger a send if the transmitter
+     * isn't already sending. If it is busy, the interrupt handler will
+     * trigger the send later, upon receiving a Tx done interrupt.
+     */
+
+    ei_block_output(dev, length, skb->data, output_page);
+    if (! ei_local->txing) {
+	ei_local->txing = 1;
+	NS8390_trigger_send(dev, send_length, output_page);
+	dev->trans_start = jiffies;
+	if (output_page == ei_local->tx_start_page) {
+		ei_local->tx1 = -1;
+		ei_local->lasttx = -1;
+	} else {
+		ei_local->tx2 = -1;
+		ei_local->lasttx = -2;
+	}
+    } else
+	ei_local->txqueue++;
+
+    dev->tbusy = (ei_local->tx1  &&  ei_local->tx2);
+
+#else	/* EI_PINGPONG */
+
+    /*
+     * Only one Tx buffer in use. You need two Tx bufs to come close to
+     * back-to-back transmits. Expect a 20 -> 25% performance hit on
+     * reasonable hardware if you only use one Tx buffer.
+     */
+
+    ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
+    ei_local->txing = 1;
+    NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
+    dev->trans_start = jiffies;
+    dev->tbusy = 1;
+
+#endif	/* EI_PINGPONG */
+
     /* Turn 8390 interrupts back on. */
     ei_local->irqlock = 0;
     outb_p(ENISR_ALL, e8390_base + EN0_IMR);
@@ -305,18 +329,17 @@
 		/* Push the next to-transmit packet through. */
 		if (interrupts & ENISR_TX) {
 			ei_tx_intr(dev);
-		} else if (interrupts & ENISR_COUNTERS) {
+		} else if (interrupts & ENISR_TX_ERR) {
+			ei_tx_err(dev);
+		}
+
+		if (interrupts & ENISR_COUNTERS) {
 			ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
 			ei_local->stat.rx_crc_errors   += inb_p(e8390_base + EN0_COUNTER1);
 			ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
 			outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
 		}
 		
-		/* Ignore the transmit errs and reset intr for now. */
-		if (interrupts & ENISR_TX_ERR) {
-			outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
-		}
-
 		/* Ignore any RDC interrupts that make it back to here. */
 		if (interrupts & ENISR_RDC) {
 			outb_p(ENISR_RDC, e8390_base + EN0_ISR);
@@ -340,6 +363,43 @@
     return;
 }
 
+/*
+ * A transmitter error has happened. Most likely excess collisions (which
+ * is a fairly normal condition). If the error is one where the Tx will
+ * have been aborted, we try and send another one right away, instead of
+ * letting the failed packet sit and collect dust in the Tx buffer. This
+ * is a much better solution as it avoids kernel based Tx timeouts, and
+ * an unnecessary card reset.
+ */
+
+static void ei_tx_err(struct device *dev)
+{
+    int e8390_base = dev->base_addr;
+    unsigned char txsr = inb_p(e8390_base+EN0_TSR);
+    unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+    printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr);
+    if (txsr & ENTSR_ABT)
+		printk("excess-collisions ");
+    if (txsr & ENTSR_ND)
+		printk("non-deferral ");
+    if (txsr & ENTSR_CRS)
+		printk("lost-carrier ");
+    if (txsr & ENTSR_FU)
+		printk("FIFO-underrun ");
+    if (txsr & ENTSR_CDH)
+		printk("lost-heartbeat ");
+    printk("\n");
+#endif
+
+    outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+
+    if (tx_was_aborted)
+		ei_tx_intr(dev);
+
+}
+
 /* We have finished a transmit: check for errors and then trigger the next
    packet to be sent. */
 static void ei_tx_intr(struct device *dev)
@@ -349,58 +409,68 @@
     struct ei_device *ei_local = (struct ei_device *) dev->priv;
     
     outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
-    
-    if (ei_local->pingpong) {
-		ei_local->txqueue--;
-		if (ei_local->tx1 < 0) {
-			if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
-				printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
-					   ei_local->name, ei_local->lasttx, ei_local->tx1);
-			ei_local->tx1 = 0;
-			dev->tbusy = 0;
-			if (ei_local->tx2 > 0) {
-				ei_local->txing = 1;
-				NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
-				dev->trans_start = jiffies;
-				ei_local->tx2 = -1,
-				ei_local->lasttx = 2;
-			} else
-				ei_local->lasttx = 20, ei_local->txing = 0;
-		} else if (ei_local->tx2 < 0) {
-			if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2)
-				printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
-					   ei_local->name, ei_local->lasttx, ei_local->tx2);
-			ei_local->tx2 = 0;
-			dev->tbusy = 0;
-			if (ei_local->tx1 > 0) {
-				ei_local->txing = 1;
-				NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
-				dev->trans_start = jiffies;
-				ei_local->tx1 = -1;
-				ei_local->lasttx = 1;
-			} else
-				ei_local->lasttx = 10, ei_local->txing = 0;
-		} else
-			printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
-				   dev->name, ei_local->lasttx);
-    } else {
-		ei_local->txing = 0;
-		dev->tbusy = 0;
-    }
+
+#ifdef EI_PINGPONG
+
+    /*
+     * There are two Tx buffers, see which one finished, and trigger
+     * the send of another one if it exists.
+     */
+    ei_local->txqueue--;
+    if (ei_local->tx1 < 0) {
+	if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+		printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
+			   ei_local->name, ei_local->lasttx, ei_local->tx1);
+	ei_local->tx1 = 0;
+	dev->tbusy = 0;
+	if (ei_local->tx2 > 0) {
+		ei_local->txing = 1;
+		NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+		dev->trans_start = jiffies;
+		ei_local->tx2 = -1,
+		ei_local->lasttx = 2;
+	} else
+		ei_local->lasttx = 20, ei_local->txing = 0;
+    } else if (ei_local->tx2 < 0) {
+	if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2)
+		printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
+			   ei_local->name, ei_local->lasttx, ei_local->tx2);
+	ei_local->tx2 = 0;
+	dev->tbusy = 0;
+	if (ei_local->tx1 > 0) {
+		ei_local->txing = 1;
+		NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+		dev->trans_start = jiffies;
+		ei_local->tx1 = -1;
+		ei_local->lasttx = 1;
+	} else
+		ei_local->lasttx = 10, ei_local->txing = 0;
+    } else
+	printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
+		   dev->name, ei_local->lasttx);
+
+#else	/* EI_PINGPONG */
+    /*
+     *  Single Tx buffer: mark it free so another packet can be loaded.
+     */
+    ei_local->txing = 0;
+    dev->tbusy = 0;
+#endif
 
     /* Minimize Tx latency: update the statistics after we restart TXing. */
-	if (status & ENTSR_COL) ei_local->stat.collisions++;
+    if (status & ENTSR_COL)
+	ei_local->stat.collisions++;
     if (status & ENTSR_PTX)
-		ei_local->stat.tx_packets++;
+	ei_local->stat.tx_packets++;
     else {
-		ei_local->stat.tx_errors++;
-		if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++;
-		if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++;
-		if (status & ENTSR_FU)  ei_local->stat.tx_fifo_errors++;
-		if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++;
-		if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++;
-	}
-    
+	ei_local->stat.tx_errors++;
+	if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++;
+	if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++;
+	if (status & ENTSR_FU)  ei_local->stat.tx_fifo_errors++;
+	if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++;
+	if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++;
+    }
+
     mark_bh (NET_BH);
 }
 
@@ -410,7 +480,8 @@
 {
     int e8390_base = dev->base_addr;
     struct ei_device *ei_local = (struct ei_device *) dev->priv;
-    int rxing_page, this_frame, next_frame, current_offset;
+    unsigned char rxing_page, this_frame, next_frame;
+    unsigned short current_offset;
     int rx_pkt_count = 0;
     struct e8390_pkt_hdr rx_frame;
     int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
@@ -502,13 +573,6 @@
 		ei_local->current_page = next_frame;
 		outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
     }
-    /* If any worth-while packets have been received, netif_rx()
-       has done a mark_bh(NET_BH) for us and will work on them
-       when we get to the bottom-half routine. */
-
-	/* Record the maximum Rx packet queue. */
-	if (rx_pkt_count > high_water_mark)
-		high_water_mark = rx_pkt_count;
 
     /* We used to also ack ENISR_OVER here, but that would sometimes mask
     a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
@@ -607,7 +671,6 @@
 			return -ENOMEM;
 		memset(dev->priv, 0, sizeof(struct ei_device));
 		ei_local = (struct ei_device *)dev->priv;
-		ei_local->pingpong = ei_pingpong;
     }
     
     dev->hard_start_xmit = &ei_start_xmit;
diff -u --recursive --new-file v1.3.98/linux/drivers/net/8390.h linux/drivers/net/8390.h
--- v1.3.98/linux/drivers/net/8390.h	Mon Mar 25 10:27:57 1996
+++ linux/drivers/net/8390.h	Tue May  7 14:19:33 1996
@@ -13,7 +13,15 @@
 
 #define TX_2X_PAGES 12
 #define TX_1X_PAGES 6
-#define TX_PAGES (ei_status.pingpong ? TX_2X_PAGES : TX_1X_PAGES)
+
+/* Should always use two Tx slots to get back-to-back transmits. */
+#define EI_PINGPONG
+
+#ifdef EI_PINGPONG
+#define TX_PAGES TX_2X_PAGES
+#else
+#define TX_PAGES TX_1X_PAGES
+#endif
 
 #define ETHER_ADDR_LEN 6
 
@@ -56,7 +64,6 @@
   unsigned txing:1;		/* Transmit Active */
   unsigned irqlock:1;		/* 8390's intrs disabled when '1'. */
   unsigned dmaing:1;		/* Remote DMA Active */
-  unsigned pingpong:1;		/* Using the ping-pong driver */
   unsigned char tx_start_page, rx_start_page, stop_page;
   unsigned char current_page;	/* Read pointer in buffer  */
   unsigned char interface_num;	/* Net port (AUI, 10bT.) to use. */
diff -u --recursive --new-file v1.3.98/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.98/linux/drivers/net/Makefile	Sat Apr 27 15:19:53 1996
+++ linux/drivers/net/Makefile	Mon May  6 12:44:31 1996
@@ -466,6 +466,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_HYDRA),y)
+L_OBJS += hydra.o
+else
+  ifeq ($(CONFIG_HYDRA),m)
+  M_OBJS += hydra.o
+  endif
+endif
+
 ifeq ($(CONFIG_SDLA),y)
 L_OBJS += sdla.o
 else
diff -u --recursive --new-file v1.3.98/linux/drivers/net/README.eql linux/drivers/net/README.eql
--- v1.3.98/linux/drivers/net/README.eql	Fri Apr 12 15:51:54 1996
+++ linux/drivers/net/README.eql	Mon May  6 12:26:07 1996
@@ -29,7 +29,7 @@
   my testing so far, the Livingston PortMaster 2e's load-balancing is a
   good 1 to 2 KB/s slower than the test machine working with a 28.8 Kbps
   and 14.4 Kbps connection.  However, I am not sure that it really is
-  the PortMaster, or if its Linux's TCP drivers. I'm told that Linux's
+  the PortMaster, or if it's Linux's TCP drivers. I'm told that Linux's
   TCP implementation is pretty fast though.--)
 
 
diff -u --recursive --new-file v1.3.98/linux/drivers/net/README.tunnel linux/drivers/net/README.tunnel
--- v1.3.98/linux/drivers/net/README.tunnel	Sun Jan 14 16:22:17 1996
+++ linux/drivers/net/README.tunnel	Mon May  6 12:26:07 1996
@@ -6,7 +6,7 @@
 	A network tunneling driver encapsulates packets of one 
 protocol type within packets of another protocol type.  It sends
 them out over the network to a relay (or destination) where the
-packet is unwrapped and is forwarded to it's ultimate destination.
+packet is unwrapped and is forwarded to its ultimate destination.
 Packet tunneling is useful in situations where you want to route
 packets of a non-standard protocol type over the common network.
 A good example of this is 'IPX encapsulation', in which IPX packets
@@ -41,7 +41,7 @@
 based on the Linux loopback driver written (in part) by Ross Biro,
 Fred N. van Kempen, and Donald Becker.  After the driver is 
 loaded, it can be set up as any other network interface, using 
-ifconfig.  The tunnel device is given it's own IP address, which
+ifconfig.  The tunnel device is given its own IP address, which
 can match that of the machine, and also is given a pointopoint
 address.  This pointopoint address is the address of the machine
 providing the decapsulating endpoint for the IP tunnel.  After
@@ -54,7 +54,7 @@
 	The decapsulating endpoint must have loaded the ipip.o
 decapsulator module for it to understand IP-in-IP encapsulation.
 This module takes any IP-in-IP packet that is destined for the local
-machine, unwraps it, and sends it on it's way, using standard 
+machine, unwraps it, and sends it on its way, using standard 
 routing rules.  The current implementation of IP decapsulation does
 no checking on the packet, other than making sure wrapper is bound
 for the local machine.
@@ -106,11 +106,11 @@
 and every incoming IP-in-IP packet bound for the local machine is
 unwrapped and re-routed normally.
 The only difference in the two machines setup shown above is that 
-machine A set it's tunnel address to one existing on machine B's
+machine A set its tunnel address to one existing on machine B's
 network, while B set a route to machine A's tunnel device address
 through the tunnel.  This is because machine A wants to have a new
 address on network B, and machine B is simply acting as a proxy 
-for machine A.  Machine A needs it's tunnel address to be on network 
+for machine A.  Machine A needs its tunnel address to be on network 
 B so that when packets from machine B are unwrapped, the Linux 
 routing system knows that the address is a local one.  Due to a 
 feature of Linux, any packets received locally, bound for another
diff -u --recursive --new-file v1.3.98/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.3.98/linux/drivers/net/Space.c	Sat Apr 27 15:19:53 1996
+++ linux/drivers/net/Space.c	Mon May  6 12:44:31 1996
@@ -77,6 +77,7 @@
 extern int atarilance_probe(struct device *);
 extern int a2065_probe(struct device *);
 extern int ariadne_probe(struct device *);
+extern int hydra_probe(struct device *);
 
 /* Detachable devices ("pocket adaptors") */
 extern int atp_init(struct device *);
@@ -202,6 +203,9 @@
 #endif
 #ifdef CONFIG_ARIADNE		/* Village Tronic Ariadne Ethernet Board */
 	&& ariadne_probe(dev)
+#endif
+#ifdef CONFIG_HYDRA		/* Hydra Systems Amiganet Ethernet board */
+	&& hydra_probe(dev)
 #endif
 #ifdef CONFIG_SUNLANCE
 	&& sparc_lance_probe(dev)
diff -u --recursive --new-file v1.3.98/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c
--- v1.3.98/linux/drivers/net/arcnet.c	Sun Apr 21 19:22:09 1996
+++ linux/drivers/net/arcnet.c	Mon May  6 12:26:07 1996
@@ -2678,7 +2678,7 @@
 	}
 
 	/*
-	 * Try and get ARP to resolve the header.
+	 * Try to get ARP to resolve the header.
 	 */
 #ifdef CONFIG_INET	 
 	BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
@@ -3132,7 +3132,7 @@
 	}
 
 	/*
-	 * Try and get ARP to resolve the header.
+	 * Try to get ARP to resolve the header.
 	 */
 #ifdef CONFIG_INET	 
 	return arp_find(&(head->daddr), dst, dev, dev->pa_addr, skb)? 1 : 0;
diff -u --recursive --new-file v1.3.98/linux/drivers/net/dgrs.c linux/drivers/net/dgrs.c
--- v1.3.98/linux/drivers/net/dgrs.c	Sun Apr 21 19:22:09 1996
+++ linux/drivers/net/dgrs.c	Mon May  6 12:26:07 1996
@@ -193,7 +193,7 @@
 	ulong	x;
 
 	/*
-	 *	If Space.c says not to use DMA, or if its not a PLX based
+	 *	If Space.c says not to use DMA, or if it's not a PLX based
 	 *	PCI board, or if the expansion ROM space is not PCI
 	 *	configured, then return false.
 	 */
@@ -250,10 +250,10 @@
  *	Initiate DMA using PLX part on PCI board.  Spin the
  *	processor until completed.  All addresses are physical!
  *
- *	If pciaddr is NULL, then its a chaining DMA, and lcladdr is
+ *	If pciaddr is NULL, then it's a chaining DMA, and lcladdr is
  *	the address of the first DMA descriptor in the chain.
  *
- *	If pciaddr is not NULL, then its a single DMA.
+ *	If pciaddr is not NULL, then it's a single DMA.
  *
  *	In either case, "lcladdr" must have been fixed up to make
  *	sure the MSB isn't set using the S2DMA macro before passing
@@ -373,13 +373,13 @@
 	/*
 	 *	There are three modes here for doing the packet copy.
 	 *	If we have DMA, and the packet is "long", we use the
-	 *	chaining mode of DMA.  If its shorter, we use single
+	 *	chaining mode of DMA.  If it's shorter, we use single
 	 *	DMA's.  Otherwise, we use memcpy().
 	 */
 	if (priv->use_dma && priv->dmadesc_h && len > 64)
 	{
 		/*
-		 *	If we can use DMA and its a long frame, copy it using
+		 *	If we can use DMA and it's a long frame, copy it using
 		 *	DMA chaining.
 		 */
 		DMACHAIN	*ddp_h;	/* Host virtual DMA desc. pointer */
@@ -451,7 +451,7 @@
 	else if (priv->use_dma)
 	{
 		/*
-		 *	If we can use DMA and its a shorter frame, copy it
+		 *	If we can use DMA and it's a shorter frame, copy it
 		 *	using single DMA transfers.
 		 */
 		uchar		*phys_p;
diff -u --recursive --new-file v1.3.98/linux/drivers/net/dgrs_driver.c linux/drivers/net/dgrs_driver.c
--- v1.3.98/linux/drivers/net/dgrs_driver.c	Sun Apr 21 19:22:09 1996
+++ linux/drivers/net/dgrs_driver.c	Mon May  6 12:26:08 1996
@@ -193,7 +193,7 @@
 	ulong	x;
 
 	/*
-	 *	If Space.c says not to use DMA, or if its not a PLX based
+	 *	If Space.c says not to use DMA, or if it's not a PLX based
 	 *	PCI board, or if the expansion ROM space is not PCI
 	 *	configured, then return false.
 	 */
@@ -250,10 +250,10 @@
  *	Initiate DMA using PLX part on PCI board.  Spin the
  *	processor until completed.  All addresses are physical!
  *
- *	If pciaddr is NULL, then its a chaining DMA, and lcladdr is
+ *	If pciaddr is NULL, then it's a chaining DMA, and lcladdr is
  *	the address of the first DMA descriptor in the chain.
  *
- *	If pciaddr is not NULL, then its a single DMA.
+ *	If pciaddr is not NULL, then it's a single DMA.
  *
  *	In either case, "lcladdr" must have been fixed up to make
  *	sure the MSB isn't set using the S2DMA macro before passing
@@ -373,13 +373,13 @@
 	/*
 	 *	There are three modes here for doing the packet copy.
 	 *	If we have DMA, and the packet is "long", we use the
-	 *	chaining mode of DMA.  If its shorter, we use single
+	 *	chaining mode of DMA.  If it's shorter, we use single
 	 *	DMA's.  Otherwise, we use memcpy().
 	 */
 	if (priv->use_dma && priv->dmadesc_h && len > 64)
 	{
 		/*
-		 *	If we can use DMA and its a long frame, copy it using
+		 *	If we can use DMA and it's a long frame, copy it using
 		 *	DMA chaining.
 		 */
 		DMACHAIN	*ddp_h;	/* Host virtual DMA desc. pointer */
@@ -451,7 +451,7 @@
 	else if (priv->use_dma)
 	{
 		/*
-		 *	If we can use DMA and its a shorter frame, copy it
+		 *	If we can use DMA and it's a shorter frame, copy it
 		 *	using single DMA transfers.
 		 */
 		uchar		*phys_p;
diff -u --recursive --new-file v1.3.98/linux/drivers/net/dlci.c linux/drivers/net/dlci.c
--- v1.3.98/linux/drivers/net/dlci.c	Wed Apr 17 09:06:31 1996
+++ linux/drivers/net/dlci.c	Tue May  7 12:06:50 1996
@@ -286,10 +286,6 @@
    int                 err, i;
    char                buf[10];
 
-   err = verify_area(VERIFY_READ, new, sizeof(*new));
-   if (err)
-      return(err);
-
    err = verify_area(VERIFY_WRITE, new, sizeof(*new));
    if (err)
       return(err);
diff -u --recursive --new-file v1.3.98/linux/drivers/net/dummy.c linux/drivers/net/dummy.c
--- v1.3.98/linux/drivers/net/dummy.c	Thu Nov  9 08:24:06 1995
+++ linux/drivers/net/dummy.c	Mon May  6 12:26:08 1996
@@ -24,7 +24,7 @@
 	misguided packets.
 
 			Nick Holloway, 27th May 1994
-	[I tweaked this explanation a little but thats all]
+	[I tweaked this explanation a little but that's all]
 			Alan Cox, 30th May 1994
 */
 
diff -u --recursive --new-file v1.3.98/linux/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- v1.3.98/linux/drivers/net/eexpress.c	Wed Apr 17 09:06:31 1996
+++ linux/drivers/net/eexpress.c	Mon May  6 12:26:08 1996
@@ -903,7 +903,7 @@
 
 /* 
  * This should never happen. It is called when some higher
- * routine detects the CU has stopped, to try and restart
+ * routine detects the CU has stopped, to try to restart
  * it from the last packet we knew we were working on,
  * or the idle loop if we had finished for the time.
  */
diff -u --recursive --new-file v1.3.98/linux/drivers/net/eql.c linux/drivers/net/eql.c
--- v1.3.98/linux/drivers/net/eql.c	Sun Apr 21 12:39:01 1996
+++ linux/drivers/net/eql.c	Mon May  6 12:26:08 1996
@@ -66,7 +66,7 @@
  * printk's trimmed out.
  *
  * Revision 3.6  1995/01/19  21:49:56  guru
- * This is working pretty well. I gained 1 K/s in speed.. now its just
+ * This is working pretty well. I gained 1 K/s in speed.. now it's just
  * robustness and printk's to be diked out.
  *
  * Revision 3.5  1995/01/18  22:29:59  guru
diff -u --recursive --new-file v1.3.98/linux/drivers/net/eth16i.c linux/drivers/net/eth16i.c
--- v1.3.98/linux/drivers/net/eth16i.c	Fri Apr 12 15:51:55 1996
+++ linux/drivers/net/eth16i.c	Mon May  6 12:26:08 1996
@@ -400,7 +400,7 @@
   /* 
      Now it seems that we have found a ethernet chip in this particular
      ioaddr. The MB86985 chip has this feature, that when you read a 
-     certain register it will increase it's io base address to next
+     certain register it will increase its io base address to next
      configurable slot. Now when we have found the chip, first thing is
      to make sure that the chip's ioaddr will hold still here.
   */
diff -u --recursive --new-file v1.3.98/linux/drivers/net/hp100.h linux/drivers/net/hp100.h
--- v1.3.98/linux/drivers/net/hp100.h	Fri Apr 12 15:51:56 1996
+++ linux/drivers/net/hp100.h	Mon May  6 12:26:08 1996
@@ -128,7 +128,7 @@
 #define HP100_EE_EN		0x1000	/* 0:Disable,1:Enable EEPROM writing */
 #define HP100_BM_WRITE		0x0800	/* 0:Slave, 1:Bus Master for Tx data */
 #define HP100_BM_READ		0x0400	/* 0:Slave, 1:Bus Master for Rx data */
-#define HP100_TRI_INT		0x0200	/* 0:Dont, 1:Do tri-state the int */
+#define HP100_TRI_INT		0x0200	/* 0:Don't, 1:Do tri-state the int */
 #define HP100_MEM_EN		0x0040	/* Config program set this to */
                                         /*   0:Disable, 1:Enable mem map. */
                                         /*   See MMAP_DIS. */
diff -u --recursive --new-file v1.3.98/linux/drivers/net/hydra.c linux/drivers/net/hydra.c
--- v1.3.98/linux/drivers/net/hydra.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/hydra.c	Mon May  6 12:44:31 1996
@@ -0,0 +1,655 @@
+/* Linux/68k Hydra Amiganet board driver v2.1 BETA                          */
+/* copyleft by Topi Kanerva (topi@susanna.oulu.fi)                          */
+/* also some code & lots of fixes by Timo Rossi (trossi@cc.jyu.fi)          */
+
+/* The code is mostly based on the linux/68k Ariadne driver                 */
+/* copyrighted by Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be) */
+/* and Peter De Schrijver (Peter.DeSchrijver@linux.cc.kuleuven.ac.be)       */
+
+/* This file is subject to the terms and conditions of the GNU General      */
+/* Public License.  See the file README.legal in the main directory of the  */
+/* Linux/68k distribution for more details.                                 */
+
+/* The Amiganet is a Zorro-II board made by Hydra Systems. It contains a    */
+/* NS8390 NIC (network interface controller) clone, 16 or 64K on-board RAM  */
+/* and 10BASE-2 (thin coax) and AUI connectors.                             */
+
+
+#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/netdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/bootinfo.h>
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/zorro.h>
+
+#include "hydra.h"
+
+
+#define HYDRA_DEBUG
+#undef HAVE_MULTICAST
+
+#define HYDRA_VERSION "v2.1 BETA"
+
+struct device *init_etherdev(struct device *dev, int sizeof_private, unsigned long *mem_startp);
+
+#undef HYDRA_DEBUG        /* define this for (lots of) debugging information */
+
+#if 0                         /* currently hardwired to one transmit buffer */
+ #define TX_RING_SIZE	5
+ #define RX_RING_SIZE	16
+#else
+ #define TX_RING_SIZE 1
+ #define RX_RING_SIZE 8
+#endif
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1518
+#define ETHER_ADDR_LEN 6
+
+
+/*
+ *   let's define here nice macros for writing and reading NIC registers
+ *
+ * the CIA accesses here are uses to make sure the minimum time
+ * requirement between NIC chip selects is met.
+ */
+#define WRITE_REG(reg, val) (ciaa.pra, ((u_char)(*(nicbase+(reg))=val)))
+#define READ_REG(reg) (ciaa.pra, ((u_char)(*(nicbase+(reg)))))
+
+/* mask value for the interrupts we use */
+#define NIC_INTS (ISR_PRX | ISR_PTX | ISR_RXE | ISR_TXE | ISR_OVW | ISR_CNT)
+
+/* only broadcasts, no promiscuous mode for now */
+#define NIC_RCRBITS (0)
+
+/*
+ *   Private Device Data
+ */
+struct hydra_private
+    {
+    u_char *hydra_base;
+    u_char *hydra_nic_base;
+    u_short tx_page_start;
+    u_short rx_page_start;
+    u_short rx_page_stop;
+    u_short next_pkt;
+    struct enet_statistics stats;
+    };
+
+static int hydra_open(struct device *dev);
+static int hydra_start_xmit(struct sk_buff *skb, struct device *dev);
+static void hydra_interrupt(int irq, struct pt_regs *fp, void *data);
+static void __inline__ hydra_rx(struct device *dev, struct hydra_private *priv, u_char *nicbase);
+static int hydra_close(struct device *dev);
+static struct enet_statistics *hydra_get_stats(struct device *dev);
+#ifdef HAVE_MULTICAST
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
+
+
+/* this is now coherent to the C version below, */
+/* compile the source with -D__USE_ASM__ if you */
+/* want it - it'll only be some 10% faster thou */
+
+#if defined (__GNUC__) && defined (__mc68000__) && defined (USE_ASM)
+
+static __inline__ void *memcpyw(u_short *dest, u_short *src, int len)
+    {
+    __asm__("   move.l %0,%/a1; move.l %1,%/a0; move.l %2,%/d0 \n\t"
+	    "   cmpi.l #2,%/d0 \n\t"
+	    "1: bcs.s  2f \n\t"
+            "   move.w %/a0@+,%/a1@+ \n\t"
+	    "   subq.l #2,%/d0 \n\t"
+	    "   bra.s  1b \n\t"
+	    "2: cmpi.l #1,%/d0 \n\t"
+	    "   bne.s  3f \n\t"
+	    "   move.w %/a0@,%/d0 \n\t"
+	    "   swap.w %/d0 \n\t"
+	    "   move.b %/d0,%/a1@ \n\t"
+	    "3: moveq  #0,%/d0 \n\t"
+	  :
+	  : "g" (dest), "g" (src), "g" (len)
+	  : "a1", "a0", "d0");
+    return;
+}
+
+#else
+
+/* hydra memory can only be read or written as words or longwords.  */
+/* that will mean that we'll have to write a special memcpy for it. */
+/* this one here relies on the fact that _writes_ to hydra memory   */
+/* are guaranteed to be of even length. (reads can be arbitrary)    */
+
+static void memcpyw(u_short *dest, u_short *src, int len)
+{
+  if(len & 1)
+    len++;
+
+  while (len >= 2) {
+    *(dest++) = *(src++);
+    len -= 2;
+  }
+    
+}
+
+#endif
+
+unsigned long hydra_probe(struct device *dev)
+    {
+    struct hydra_private *priv;
+    u_long board;
+    int key;
+    struct ConfigDev *cd;
+    int j;
+
+#ifdef HYDRA_DEBUG
+ printk("hydra_probe(%x)\n", dev);
+#endif
+
+    if ((key = zorro_find(MANUF_HYDRA_SYSTEMS, PROD_AMIGANET, 0, 0))) {
+	cd = zorro_get_board(key);
+	if((board = (u_long) cd->cd_BoardAddr))
+	    {
+	    for(j = 0; j < ETHER_ADDR_LEN; j++)
+                dev->dev_addr[j] = *((u_char *)ZTWO_VADDR(board + HYDRA_ADDRPROM + 2*j));
+	    
+	    printk("%s: hydra at 0x%08x, address %02x:%02x:%02x:%02x:%02x:%02x (hydra.c " HYDRA_VERSION ")\n",
+		   dev->name, (int)board, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+		   dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+	    init_etherdev(dev, 0, NULL);
+	    
+	    dev->priv = kmalloc(sizeof(struct hydra_private), GFP_KERNEL);
+	    priv = (struct hydra_private *)dev->priv;
+	    memset(priv, 0, sizeof(struct hydra_private));
+	    
+	    priv->hydra_base = (u_char *) ZTWO_VADDR(board);
+	    priv->hydra_nic_base = (u_char *) ZTWO_VADDR(board) + HYDRA_NIC_BASE;
+	    
+	    dev->open = &hydra_open;
+	    dev->stop = &hydra_close;
+	    dev->hard_start_xmit = &hydra_start_xmit;
+	    dev->get_stats = &hydra_get_stats;
+#ifdef HAVE_MULTICAST
+	    dev->set_multicast_list = &hydra_set_multicast_list;
+#endif
+	    zorro_config_board(key, 0);
+	    return(0);
+            }
+        }
+    return(ENODEV);
+    }
+
+
+static int hydra_open(struct device *dev)
+  {
+    struct hydra_private *priv = (struct hydra_private *)dev->priv;
+    static int interruptinstalled = 0;
+    u_char volatile *nicbase = priv->hydra_nic_base;
+#ifdef HAVE_MULTICAST
+    int i;
+#endif
+    
+#ifdef HYDRA_DEBUG
+ printk("hydra_open(0x%x)\n", dev);
+#endif
+
+    /* first, initialize the private structure */
+    priv->tx_page_start = 0;   /* these are 256 byte buffers for NS8390 */
+    priv->rx_page_start = 6;
+    priv->rx_page_stop  = 62;  /* these values are hard coded for now */
+
+    /* Reset the NS8390 NIC */
+    WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_STOP);
+
+    /* be sure that the NIC is in stopped state */
+    while(!(READ_REG(NIC_ISR) & ISR_RST));
+
+    /* word transfer, big endian bytes, loopback, FIFO threshold 4 bytes */
+    WRITE_REG(NIC_DCR, DCR_WTS | DCR_BOS | DCR_LS | DCR_FT0);
+
+    /* clear remote byte count registers */
+    WRITE_REG(NIC_RBCR0, 0);
+    WRITE_REG(NIC_RBCR1, 0);
+
+    /* accept packets addressed to this card and also broadcast packets */
+    WRITE_REG(NIC_RCR, NIC_RCRBITS);
+
+    /* enable loopback mode 1 */
+    WRITE_REG(NIC_TCR, TCR_LB1);
+
+    /* initialize receive buffer ring */
+    WRITE_REG(NIC_PSTART, priv->rx_page_start);
+    WRITE_REG(NIC_PSTOP, priv->rx_page_stop);
+    WRITE_REG(NIC_BNDRY, priv->rx_page_start);
+
+    /* clear interrupts */
+    WRITE_REG(NIC_ISR, 0xff);
+
+    /* enable interrupts */
+    WRITE_REG(NIC_IMR, NIC_INTS);
+
+    /* set the ethernet hardware address */
+    WRITE_REG(NIC_CR, CR_PAGE1 | CR_NODMA | CR_STOP); /* goto page 1 */
+
+    WRITE_REG(NIC_PAR0, dev->dev_addr[0]);
+    WRITE_REG(NIC_PAR1, dev->dev_addr[1]);
+    WRITE_REG(NIC_PAR2, dev->dev_addr[2]);
+    WRITE_REG(NIC_PAR3, dev->dev_addr[3]);
+    WRITE_REG(NIC_PAR4, dev->dev_addr[4]);
+    WRITE_REG(NIC_PAR5, dev->dev_addr[5]);
+
+#ifdef HAVE_MULTICAST
+    /* clear multicast hash table */
+    for(i = 0; i < 8; i++)
+      WRITE_REG(NIC_MAR0 + 2*i, 0);
+#endif
+
+    priv->next_pkt = priv->rx_page_start+1; /* init our s/w variable */
+    WRITE_REG(NIC_CURR, priv->next_pkt);    /* set the next buf for current */
+
+    /* goto page 0, start NIC */
+    WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_START);
+
+    /* take interface out of loopback */
+    WRITE_REG(NIC_TCR, 0);
+
+    dev->tbusy = 0;
+    dev->interrupt = 0;
+    dev->start = 1;
+    
+    if(!interruptinstalled) {
+      if(!add_isr(IRQ_AMIGA_PORTS, hydra_interrupt, 0, dev,
+				  "Hydra Ethernet"))
+	return(-EAGAIN);
+      interruptinstalled = 1;
+    }
+
+    return(0);
+  }
+
+
+static int hydra_close(struct device *dev)
+{
+  struct hydra_private *priv = (struct hydra_private *)dev->priv;
+  u_char volatile *nicbase = priv->hydra_nic_base;
+  int n = 5000;
+
+  dev->start = 0;
+  dev->tbusy = 1;
+
+#ifdef HYDRA_DEBUG
+  printk("%s: Shutting down ethercard\n", dev->name);
+  printk("%s: %d packets missed\n", dev->name, priv->stats.rx_missed_errors);
+#endif
+
+  WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_STOP);
+
+  /* wait for NIC to stop (what a nice timeout..) */
+  while(((READ_REG(NIC_ISR) & ISR_RST) == 0) && --n);
+    
+  return(0);
+}
+
+
+static void hydra_interrupt(int irq, struct pt_regs *fp, void *data)
+    {
+    u_char volatile *nicbase;
+  
+    struct device *dev = (struct device *) data;
+    struct hydra_private *priv;
+    u_short intbits;
+
+    if(dev == NULL)
+        {
+        printk("hydra_interrupt(): irq for unknown device\n");
+        return;
+        }
+
+/* this is not likely a problem - i think */
+    if(dev->interrupt)
+        printk("%s: re-entering the interrupt handler\n", dev->name);
+
+    dev->interrupt = 1;
+
+    priv = (struct hydra_private *) dev->priv;
+    nicbase = (u_char *) priv->hydra_nic_base;
+
+    /* select page 0 */
+    WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
+
+    intbits = READ_REG(NIC_ISR) & NIC_INTS;
+    if(intbits == 0)
+        {
+        dev->interrupt = 0;
+        return;
+        }
+
+	/* acknowledge all interrupts, by clearing the interrupt flag */
+	WRITE_REG(NIC_ISR, intbits);
+
+	if((intbits & ISR_PTX) && !(intbits & ISR_TXE))
+	    {
+	    dev->tbusy = 0;
+	    mark_bh(NET_BH);
+	    }
+	
+	if((intbits & ISR_PRX) && !(intbits & ISR_RXE))/* packet received OK */
+	      hydra_rx(dev, priv, nicbase);
+
+        if(intbits & ISR_TXE)
+	    priv->stats.tx_errors++;
+
+        if(intbits & ISR_RXE)
+	    priv->stats.rx_errors++;
+
+	if(intbits & ISR_CNT) {
+	  /*
+	   * read the tally counters and (currently) ignore the values
+	   * might be useful because of bugs of some versions of the 8390 NIC
+	   */
+#ifdef HYDRA_DEBUG
+	  printk("hydra_interrupt(): ISR_CNT\n");
+#endif
+	  (void)READ_REG(NIC_CNTR0);
+	  (void)READ_REG(NIC_CNTR1);
+	  (void)READ_REG(NIC_CNTR2);
+	}
+	
+	if(intbits & ISR_OVW)
+	    {
+            #ifdef HYDRA_DEBUG
+	    WRITE_REG(NIC_CR, CR_PAGE1 | CR_START | CR_NODMA);
+/* another one just too much for me to comprehend - basically this could  */
+/* only occur because of invalid access to hydra ram, thus invalidating  */
+/* the interrupt bits read - in average usage these do not occur at all */
+	    printk("hydra_interrupt(): overwrite warning, NIC_ISR %02x, NIC_CURR %02x\n",
+		 intbits, READ_REG(NIC_CURR));
+	    WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
+            #endif
+	    
+
+	    /* overwrite warning occured, stop NIC & check the BOUNDARY pointer */
+	    /* FIXME - real overwrite handling needed !! */
+
+	    printk("hydra_interrupt(): overwrite warning, resetting NIC\n");
+	    WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_STOP);
+	    while(!(READ_REG(NIC_ISR) & ISR_RST));
+	    /* wait for NIC to reset */
+	    WRITE_REG(NIC_DCR, DCR_WTS | DCR_BOS | DCR_LS | DCR_FT0);
+	    WRITE_REG(NIC_RBCR0, 0);
+	    WRITE_REG(NIC_RBCR1, 0);
+	    WRITE_REG(NIC_RCR, NIC_RCRBITS);
+	    WRITE_REG(NIC_TCR, TCR_LB1);
+	    WRITE_REG(NIC_PSTART, priv->rx_page_start);
+	    WRITE_REG(NIC_PSTOP, priv->rx_page_stop);
+	    WRITE_REG(NIC_BNDRY, priv->rx_page_start);
+	    WRITE_REG(NIC_ISR, 0xff);
+	    WRITE_REG(NIC_IMR, NIC_INTS);
+/* currently this _won't_ reset my hydra, even though it is */
+/* basically the same code as in the board init - any ideas? */
+
+	    priv->next_pkt = priv->rx_page_start+1; /* init our s/w variable */
+	    WRITE_REG(NIC_CURR, priv->next_pkt);    /* set the next buf for current */
+	    
+	    WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_START);
+	    
+	    WRITE_REG(NIC_TCR, 0);
+	    }
+
+    dev->interrupt = 0;
+    return;
+    }
+
+
+/*
+ * packet transmit routine
+ */
+static int hydra_start_xmit(struct sk_buff *skb, struct device *dev)
+    {
+    struct hydra_private *priv = (struct hydra_private *)dev->priv;
+    u_char volatile *nicbase = priv->hydra_nic_base;
+    int len, len1;
+
+	/* Transmitter timeout, serious problems. */
+
+    if(dev->tbusy)
+	{
+        int tickssofar = jiffies - dev->trans_start;
+	if(tickssofar < 20)
+	    return(1);
+	WRITE_REG(NIC_CR, CR_STOP);
+	printk("%s: transmit timed out, status %4.4x, resetting.\n", dev->name, 0);
+	priv->stats.tx_errors++;
+
+
+	dev->tbusy = 0;
+	dev->trans_start = jiffies;
+
+	return(0);
+	}
+
+
+    if(skb == NULL)
+        {
+	dev_tint(dev);
+	return(0);
+	}
+
+    if((len = skb->len) <= 0)
+	return(0);
+
+    /* fill in a tx ring entry */
+    
+#ifdef HYDRA_DEBUG
+ printk("TX pkt type 0x%04x from ", ((u_short *)skb->data)[6]);
+	{
+		int i;
+		u_char *ptr = &((u_char *)skb->data)[6];
+		for (i = 0; i < 6; i++)
+			printk("%02x", ptr[i]);
+	}
+	printk(" to ");
+	{
+		int i;
+		u_char *ptr = (u_char *)skb->data;
+		for (i = 0; i < 6; i++)
+			printk("%02x", ptr[i]);
+	}
+	printk(" data 0x%08x len %d\n", (int)skb->data, len);
+#endif
+
+    /*
+     * make sure that the packet size is at least the minimum
+     * allowed ethernet packet length.
+     * (possibly should also clear the unused space...)
+     * note: minimum packet length is 64, including CRC
+     */
+    len1 = len;
+    if(len < (ETHER_MIN_LEN-4))
+	len = (ETHER_MIN_LEN-1);
+
+    /* make sure we've got an even number of bytes to copy to hydra's mem */
+    if(len & 1) len++;
+
+    if((u_long)(priv->hydra_base + (priv->tx_page_start << 8)) < 0x80000000)
+      printk("weirdness: memcpyw(txbuf, skbdata, len): txbuf = %0x\n", (priv->hydra_base+(priv->tx_page_start<<8)));
+
+    /* copy the packet data to the transmit buffer
+       in the ethernet card RAM */
+    memcpyw((u_short *)(priv->hydra_base + (priv->tx_page_start << 8)),
+	    (u_short *)skb->data, len);
+    /* clear the unused space */
+/*    for(; len1<len; len1++)
+      (u_short)*(priv->hydra_base + (priv->tx_page_start<<8) + len1) = 0;
+*/
+    dev_kfree_skb(skb, FREE_WRITE);
+
+    priv->stats.tx_packets++;
+
+    cli();
+    /* make sure we are on the correct page */
+    WRITE_REG(NIC_CR, CR_PAGE0 | CR_NODMA | CR_START);
+
+    /* here we configure the transmit page start register etc */
+    /* notice that this code is hardwired to one transmit buffer */
+    WRITE_REG(NIC_TPSR, priv->tx_page_start);
+    WRITE_REG(NIC_TBCR0, len & 0xff);
+    WRITE_REG(NIC_TBCR1, len >> 8);
+
+    /* commit the packet to the wire */
+    WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA | CR_TXP);
+    sti();
+
+    dev->trans_start = jiffies;
+
+    return(0);
+    }
+
+
+static void __inline__ hydra_rx(struct device *dev, struct hydra_private *priv, u_char *nicbase)
+    {
+    u_short volatile *board_ram_ptr;
+    struct sk_buff *skb;
+    int hdr_next_pkt, pkt_len, len1, boundary;
+
+
+    /* remove packet(s) from the ring and commit them to TCP layer */
+    WRITE_REG(NIC_CR, CR_PAGE1 | CR_NODMA | CR_START); /* page 1 */
+    while(priv->next_pkt != READ_REG(NIC_CURR)) /* should read this only once? */
+      {
+	board_ram_ptr = (u_short *)(priv->hydra_base + (priv->next_pkt << 8));
+	
+#ifdef HYDRA_DEBUG
+	printk("next_pkt = 0x%x, board_ram_ptr = 0x%x\n", priv->next_pkt, board_ram_ptr);
+#endif
+	
+	/* the following must be done with two steps, or
+	   GCC optimizes it to a byte access to Hydra memory,
+	   which doesn't work... */
+	hdr_next_pkt = board_ram_ptr[0];
+	hdr_next_pkt >>= 8;
+	
+	pkt_len = board_ram_ptr[1];
+	pkt_len = ((pkt_len >> 8) | ((pkt_len & 0xff) << 8));
+	
+#ifdef HYDRA_DEBUG
+	printk("hydra_interrupt(): hdr_next_pkt = 0x%02x, len = %d\n", hdr_next_pkt, pkt_len);
+#endif
+	
+	if(pkt_len >= ETHER_MIN_LEN && pkt_len <= ETHER_MAX_LEN)
+	  {
+	    /* note that board_ram_ptr is u_short */
+	    /* CRC is not included in the packet length */
+	    
+	    pkt_len -= 4;
+	    skb = dev_alloc_skb(pkt_len+2);
+	    if(skb == NULL)
+	      {
+		printk("%s: memory squeeze, dropping packet.\n", dev->name);
+		priv->stats.rx_dropped++;
+	      }
+	    else
+	      {
+		skb->dev = dev;
+		skb_reserve(skb, 2);
+		
+		if(hdr_next_pkt < priv->next_pkt && hdr_next_pkt != priv->rx_page_start)
+		  {
+		    /* here, the packet is wrapped */
+		    len1 = ((priv->rx_page_stop - priv->next_pkt)<<8)-4;
+		    
+		    memcpyw((u_short *)skb_put(skb, len1), (u_short *)(board_ram_ptr+2), len1);
+		    memcpyw((u_short *)skb_put(skb, pkt_len-len1),  (u_short *)(priv->hydra_base+(priv->rx_page_start<<8)), pkt_len-len1);
+		    
+#ifdef HYDRA_DEBUG
+		    printk("wrapped packet: %d/%d bytes\n", len1, pkt_len-len1);
+#endif
+		  }  /* ... here, packet is not wrapped */
+		else memcpyw((u_short *) skb_put(skb, pkt_len), (u_short *)(board_ram_ptr+2), pkt_len);
+	      }
+	    /* if(skb == NULL) ... */
+	  }
+	else
+	  {
+	    WRITE_REG(NIC_CR, CR_PAGE1 | CR_START | CR_NODMA);
+	    printk("hydra_interrupt(): invalid packet len: %d, NIC_CURR = %02x\n", pkt_len, READ_REG(NIC_CURR));
+/*
+this is the error i kept getting until i switched to 0.9.10. it still doesn't
+mean that the bug would have gone away - so be alarmed. the packet is likely
+being fetched from a wrong memory location - but why - dunno
+   
+note-for-v2.1: not really problem anymore. hasn't been for a long time.
+*/
+	    
+	    WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
+	    /* should probably reset the NIC here ?? */
+	    
+	    hydra_open(dev);  /* FIXME - i shouldn't really be doing this. */
+	    return;
+	  }
+	
+	/* now, update the next_pkt pointer */
+	if(hdr_next_pkt < priv->rx_page_stop) priv->next_pkt = hdr_next_pkt;
+	else printk("hydra_interrupt(): invalid next_pkt pointer %d\n", hdr_next_pkt);
+	
+	/* update the boundary pointer */
+	boundary = priv->next_pkt - 1;
+	if(boundary < priv->rx_page_start)
+	  boundary = priv->rx_page_stop - 1;
+	
+	/* set NIC to page 0 to update the NIC_BNDRY register */
+	WRITE_REG(NIC_CR, CR_PAGE0 | CR_START | CR_NODMA);
+	WRITE_REG(NIC_BNDRY, boundary);
+	
+	/* select page1 to access the NIC_CURR register */
+	WRITE_REG(NIC_CR, CR_PAGE1 | CR_START | CR_NODMA);
+	
+	
+	skb->protocol = eth_type_trans(skb, dev);
+	netif_rx(skb);
+	priv->stats.rx_packets++;
+	
+      }
+    return;
+    }
+    
+
+static struct enet_statistics *hydra_get_stats(struct device *dev)
+{
+	struct hydra_private *priv = (struct hydra_private *)dev->priv;
+#if 0
+	u_char *board = priv->hydra_base; 
+
+	short saved_addr;
+#endif
+/* currently does nothing :) i'll finish this later */
+
+	return(&priv->stats);
+}
+
+#ifdef HAVE_MULTICAST
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
+    {
+    struct hydra_private *priv = (struct hydra_private *)dev->priv;
+    u_char *board = priv->hydra_base;
+
+    /* yes, this code is also waiting for someone to complete.. :) */
+    /* (personally i don't care about multicasts at all :) */
+    return;
+    }
+#endif
+
diff -u --recursive --new-file v1.3.98/linux/drivers/net/hydra.h linux/drivers/net/hydra.h
--- v1.3.98/linux/drivers/net/hydra.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/hydra.h	Mon May  6 12:44:31 1996
@@ -0,0 +1,177 @@
+/*	$Linux: hydra.h,v 1.0 1994/10/26 02:03:47 cgd Exp $	*/
+
+/*
+ * Copyright (c) 1994 Timo Rossi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by  Timo Rossi
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * The Hydra Systems card uses the National Semiconductor
+ * 8390 NIC (Network Interface Controller) chip, located
+ * at card base address + 0xffe1. NIC registers are accessible
+ * only at odd byte addresses, so the register offsets must
+ * be multiplied by two.
+ *
+ * Card address PROM is located at card base + 0xffc0 (even byte addresses)
+ *
+ * RAM starts at the card base address, and is 16K or 64K.
+ * The current Amiga NetBSD hydra driver is hardwired for 16K.
+ * It seems that the RAM should be accessed as words or longwords only.
+ *
+ */
+
+/* adapted for Linux by Topi Kanerva 03/29/95
+   with original author's permission          */
+
+#define HYDRA_NIC_BASE 0xffe1
+
+/* Page0 registers */
+
+#define NIC_CR     0       /* Command register   */
+#define NIC_PSTART (1*2)   /* Page start (write) */
+#define NIC_PSTOP  (2*2)   /* Page stop (write)  */
+#define NIC_BNDRY  (3*2)   /* Boundary pointer   */
+#define NIC_TSR    (4*2)   /* Transmit status (read) */
+#define NIC_TPSR   (4*2)   /* Transmit page start (write) */
+#define NIC_NCR    (5*2)   /* Number of collisions, read  */
+#define NIC_TBCR0  (5*2)   /* Transmit byte count low (write)  */
+#define NIC_FIFO   (6*2)   /* FIFO reg. (read)   */
+#define NIC_TBCR1  (6*2)   /* Transmit byte count high (write) */
+#define NIC_ISR    (7*2)   /* Interrupt status register */
+#define NIC_RBCR0  (0xa*2) /* Remote byte count low (write)  */
+#define NIC_RBCR1  (0xb*2) /* Remote byte count high (write) */
+#define NIC_RSR    (0xc*2) /* Receive status (read)  */
+#define NIC_RCR    (0xc*2) /* Receive config (write) */
+#define NIC_CNTR0  (0xd*2) /* Frame alignment error count (read) */
+#define NIC_TCR    (0xd*2) /* Transmit config (write)  */
+#define NIC_CNTR1  (0xe*2) /* CRC error counter (read) */
+#define NIC_DCR    (0xe*2) /* Data config (write) */
+#define NIC_CNTR2  (0xf*2) /* missed packet counter (read) */
+#define NIC_IMR    (0xf*2) /* Interrupt mask reg. (write)  */
+
+/* Page1 registers */
+
+#define NIC_PAR0   (1*2)   /* Physical address */
+#define NIC_PAR1   (2*2)
+#define NIC_PAR2   (3*2)
+#define NIC_PAR3   (4*2)
+#define NIC_PAR4   (5*2)
+#define NIC_PAR5   (6*2)
+#define NIC_CURR   (7*2)   /* Current RX ring-buffer page */
+#define NIC_MAR0   (8*2)   /* Multicast address */
+#define NIC_MAR1   (9*2)
+#define NIC_MAR2   (0xa*2)
+#define NIC_MAR3   (0xb*2)
+#define NIC_MAR4   (0xc*2)
+#define NIC_MAR5   (0xd*2)
+#define NIC_MAR6   (0xe*2)
+#define NIC_MAR7   (0xf*2)
+
+/* Command register definitions */
+
+#define CR_STOP   0x01 /* Stop -- software reset command */
+#define CR_START  0x02 /* Start */
+#define CR_TXP   0x04 /* Transmit packet */
+
+#define CR_RD0    0x08 /* Remote DMA cmd */
+#define CR_RD1    0x10
+#define CR_RD2    0x20
+
+#define CR_NODMA  CR_RD2
+
+#define CR_PS0    0x40 /* Page select */
+#define CR_PS1    0x80
+
+#define CR_PAGE0  0
+#define CR_PAGE1  CR_PS0
+#define CR_PAGE2  CR_PS1
+
+/* Interrupt status reg. definitions */
+
+#define ISR_PRX   0x01 /* Packet received without errors */
+#define ISR_PTX   0x02 /* Packet transmitted without errors */
+#define ISR_RXE   0x04 /* Receive error  */
+#define ISR_TXE   0x08 /* Transmit error */
+#define ISR_OVW   0x10 /* Ring buffer overrrun */
+#define ISR_CNT   0x20 /* Counter overflow    */
+#define ISR_RDC   0x40 /* Remote DMA compile */
+#define ISR_RST   0x80 /* Reset status      */
+
+/* Data config reg. definitions */
+
+#define DCR_WTS   0x01 /* Word transfer select  */
+#define DCR_BOS   0x02 /* Byte order select     */
+#define DCR_LAS   0x04 /* Long address select   */
+#define DCR_LS    0x08 /* Loopback select       */
+#define DCR_AR    0x10 /* Auto-init remote      */
+#define DCR_FT0   0x20 /* FIFO threshold select */
+#define DCR_FT1   0x40
+
+/* Transmit config reg. definitions */
+
+#define TCR_CRC  0x01 /* Inhibit CRC */
+#define TCR_LB0  0x02 /* Loopback control */
+#define TCR_LB1  0x04
+#define TCR_ATD  0x08 /* Auto transmit disable */
+#define TCR_OFST 0x10 /* Collision offset enable */
+
+/* Transmit status reg. definitions */
+
+#define TSR_PTX  0x01 /* Packet transmitted */
+#define TSR_COL  0x04 /* Transmit collided */
+#define TSR_ABT  0x08 /* Transmit aborted */
+#define TSR_CRS  0x10 /* Carrier sense lost */
+#define TSR_FU   0x20 /* FIFO underrun */
+#define TSR_CDH  0x40 /* CD Heartbeat */
+#define TSR_OWC  0x80 /* Out of Window Collision */
+
+/* Receiver config register definitions */
+
+#define RCR_SEP  0x01 /* Save errored packets */
+#define RCR_AR   0x02 /* Accept runt packets */
+#define RCR_AB   0x04 /* Accept broadcast */
+#define RCR_AM   0x08 /* Accept multicast */
+#define RCR_PRO  0x10 /* Promiscuous mode */
+#define RCR_MON  0x20 /* Monitor mode */
+
+/* Receiver status register definitions */
+
+#define RSR_PRX  0x01 /* Packet received without error */
+#define RSR_CRC  0x02 /* CRC error */
+#define RSR_FAE  0x04 /* Frame alignment error */
+#define RSR_FO   0x08 /* FIFO overrun */
+#define RSR_MPA  0x10 /* Missed packet */
+#define RSR_PHY  0x20 /* Physical address */
+#define RSR_DIS  0x40 /* Received disabled */
+#define RSR_DFR  0x80 /* Deferring (jabber) */
+
+/* Hydra System card address PROM offset */
+
+#define HYDRA_ADDRPROM 0xffc0
+
+
diff -u --recursive --new-file v1.3.98/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v1.3.98/linux/drivers/net/ibmtr.c	Fri Apr 12 15:51:56 1996
+++ linux/drivers/net/ibmtr.c	Mon May  6 12:26:08 1996
@@ -333,7 +333,7 @@
 	ti->mmio= t_mmio;
 
 	dev->priv = ti;     /* this seems like the logical use of the
-                         field ... lets try some empirical tests
+                         field ... let's try some empirical tests
                          using the token-info structure -- that
                          should fit with out future hope of multiple
                          adapter support as well /dwm   */
diff -u --recursive --new-file v1.3.98/linux/drivers/net/lance32.c linux/drivers/net/lance32.c
--- v1.3.98/linux/drivers/net/lance32.c	Tue Apr  2 13:32:21 1996
+++ linux/drivers/net/lance32.c	Tue May  7 12:06:50 1996
@@ -13,7 +13,7 @@
  * 	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";
+static const char *version = "lance32.c:v0.10 28.4.96 tsbogend@bigbug.franken.de\n";
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -55,6 +55,11 @@
  *         only tested on Alpha Noname Board
  * v0.02:  changed IRQ handling for new interrupt scheme (dev_id)
  *         tested on a ASUS SP3G
+ * v0.10:  fixed a odd problem with the 79C794 in a Compaq Deskpro XL
+ *         looks like the 974 doesn't like stopping and restarting in a
+ *         short period of time; now we do a reinit of the lance; the
+ *         bug was triggered by doing ifconfig eth0 <ip> broadcast <addr>
+ *         and hangs the machine (thanks to Klaus Liedl for debugging)
  */
 
 
@@ -320,7 +325,11 @@
 		           (u32) virt_to_bus(lp->rx_ring),
 			   (u32) virt_to_bus(&lp->init_block));
 
+	lp->init_block.mode = 0x0000;
+	lp->init_block.filter[0] = 0x00000000;
+	lp->init_block.filter[1] = 0x00000000;
 	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);
@@ -404,12 +413,9 @@
 	        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);
 }
@@ -417,11 +423,21 @@
 static void
 lance32_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
 {
+        int i;
+	int ioaddr = dev->base_addr;
+    
 	lance32_purge_tx_ring(dev);
 	lance32_init_ring(dev);
     
-	outw(0x0000,    dev->base_addr + LANCE_ADDR);
-	outw(csr0_bits, dev->base_addr + LANCE_DATA);
+	outw(0x0000, ioaddr + LANCE_ADDR);
+        /* ReInit Ring */
+        outw(0x0001, ioaddr + LANCE_DATA);
+	i = 0;
+	while (i++ < 100)
+		if (inw(ioaddr+LANCE_DATA) & 0x0100)
+			break;
+
+	outw(csr0_bits, ioaddr + LANCE_DATA);
 }
 
 static int
@@ -459,7 +475,7 @@
 			printk("\n");
 		}
 #endif
-		lance32_restart(dev, 0x0043, 1);
+		lance32_restart(dev, 0x0042, 1);
 
 		dev->tbusy=0;
 		dev->trans_start = jiffies;
@@ -794,32 +810,25 @@
 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.	 */
+	struct lance32_private *lp = (struct lance32_private *)dev->priv;    
 
 	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 */
+	        lp->init_block.mode = 0x8000;
 	} 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 */
+		memset(lp->init_block.filter , (num_addrs == 0) ? 0 : -1, sizeof(lp->init_block.filter));
+	        lp->init_block.mode = 0x0000;	        
 	}
+    
+	outw(0, ioaddr+LANCE_ADDR);
+	outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance.	 */
 
-	lance32_restart(dev, 0x0142, 0); /*  Resume normal operation */
+	lance32_restart(dev, 0x0042, 0); /*  Resume normal operation */
 
 }
 
diff -u --recursive --new-file v1.3.98/linux/drivers/net/loopback.c linux/drivers/net/loopback.c
--- v1.3.98/linux/drivers/net/loopback.c	Mon Mar 25 08:58:21 1996
+++ linux/drivers/net/loopback.c	Mon May  6 12:26:08 1996
@@ -15,8 +15,8 @@
  *		Alan Cox	:	Rejig for NET3.029 snap #3
  *		Alan Cox	: 	Fixed NET3.029 bugs and sped up
  *		Larry McVoy	:	Tiny tweak to double performance
- *		Alan Cox	:	Backed out LMV's tweak - the linux mm cant
- *					take it...
+ *		Alan Cox	:	Backed out LMV's tweak - the linux mm
+ *					can't take it...
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
diff -u --recursive --new-file v1.3.98/linux/drivers/net/ne.c linux/drivers/net/ne.c
--- v1.3.98/linux/drivers/net/ne.c	Fri Mar  1 07:50:44 1996
+++ linux/drivers/net/ne.c	Mon May  6 11:41:58 1996
@@ -23,6 +23,7 @@
     Paul Gortmaker	: new reset code, reset card after probe at boot.
     Paul Gortmaker	: multiple card support for module users.
     Paul Gortmaker	: Support for PCI ne2k clones, similar to lance.c
+    Paul Gortmaker	: Allow users with bad cards to avoid full probe.
 
 */
 
@@ -78,6 +79,7 @@
     {"NN1000", "NN2000",  {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
     {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}},  /* Outlaw 4-Dimension cards. */
     {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
+    {"ET-100","ET-200", {0x00, 0x45, 0x54}}, /* YANG and YA clone */
     {0,}
 };
 #endif
@@ -206,7 +208,7 @@
     int wordlength = 2;
     const char *name = NULL;
     int start_page, stop_page;
-    int neX000, ctron;
+    int neX000, ctron, bad_card;
     int reg0 = inb_p(ioaddr);
     static unsigned version_printed = 0;
 
@@ -232,6 +234,14 @@
 
     printk("NE*000 ethercard probe at %#3x:", ioaddr);
 
+    /* A user with a poor card that fails to ack the reset, or that
+       does not have a valid 0x57,0x57 signature can still use this
+       without having to recompile. Specifying an i/o address along
+       with an otherwise unused dev->mem_end value of "0xBAD" will 
+       cause the driver to skip these parts of the probe. */
+
+    bad_card = ((dev->base_addr != 0) && (dev->mem_end == 0xbad));
+
     /* Reset card. Who knows what dain-bramaged state it was left in. */
     {	unsigned long reset_start_time = jiffies;
 
@@ -240,8 +250,13 @@
 
 	while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
 		if (jiffies - reset_start_time > 2*HZ/100) {
-			printk(" not found (no reset ack).\n");
-			return ENODEV;
+			if (bad_card) {
+				printk(" (warning: no reset ack)");
+				break;
+			} else {
+				printk(" not found (no reset ack).\n");
+				return ENODEV;
+			}
 		}
 
 	outb_p(0xff, ioaddr + EN0_ISR);		/* Ack all intr. */
@@ -297,7 +312,7 @@
     ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
 
     /* Set up the rest of the parameters. */
-    if (neX000) {
+    if (neX000 || bad_card) {
 	name = (wordlength == 2) ? "NE2000" : "NE1000";
     } else if (ctron) {
 	name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
diff -u --recursive --new-file v1.3.98/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c
--- v1.3.98/linux/drivers/net/new_tunnel.c	Sun May  5 08:52:00 1996
+++ linux/drivers/net/new_tunnel.c	Tue May  7 15:21:39 1996
@@ -62,6 +62,7 @@
 */
 
 #include <linux/module.h>
+#include <linux/config.h>	/* for CONFIG_IP_FORWARD */
 
 /* Only two headers!! :-) */
 #include <net/ip.h>
@@ -172,7 +173,8 @@
 		printk ( KERN_INFO "%s: Packet with no route!\n", dev->name);
 		dev->tbusy=0;
 		stats->tx_errors++;
-		return(1);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
 	}
 
 	/*
@@ -191,7 +193,8 @@
 		ip_rt_put(rt);
 		dev->tbusy=0;
 		stats->tx_errors++;
-		return(1);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
 	}
 	ip_rt_put(rt);
 
@@ -203,7 +206,8 @@
 		printk ( KERN_INFO "%s: Can't reach target gateway!\n", dev->name);
 		dev->tbusy=0;
 		stats->tx_errors++;
-		return(1);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
 	}
 	tdev = rt->rt_dev;
 
@@ -214,7 +218,8 @@
 		ip_rt_put(rt);
 		dev->tbusy=0;
 		stats->tx_errors++;
-		return(1);
+		dev_kfree_skb(skb, FREE_WRITE);
+		return 0;
 	}
 
 #ifdef TUNNEL_DEBUG
@@ -243,7 +248,8 @@
 			ip_rt_put(rt);
   			dev->tbusy = 0;
   			stats->tx_dropped++;
-			return(1);
+			dev_kfree_skb(skb, FREE_WRITE);
+			return 0;
 		}
 		new_skb->free = 1;
 
@@ -259,8 +265,7 @@
 		 */
 		new_skb->ip_hdr = (struct iphdr *) skb_put(new_skb, skb->len);
 		memcpy(new_skb->ip_hdr, skb->data, skb->len);
-		/* Is this necessary? */
-		memcpy(new_skb->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+		memset(new_skb->proto_priv, 0, sizeof(skb->proto_priv));
 
 		/* Tack on our header */
 		new_skb->h.iph = (struct iphdr *) skb_push(new_skb, tunnel_hlen);
@@ -299,7 +304,9 @@
 	 *	If ip_forward() made a copy, it will return 1 so we can free.
 	 */
 
-	if (ip_forward(skb, dev, 0, target) == 1)
+#ifdef CONFIG_IP_FORWARD
+	if (ip_forward(skb, dev, 0, target))
+#endif
 		kfree_skb(skb, FREE_WRITE);
 
 	/*
diff -u --recursive --new-file v1.3.98/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v1.3.98/linux/drivers/net/ppp.c	Wed Apr 24 17:00:40 1996
+++ linux/drivers/net/ppp.c	Mon May  6 07:28:51 1996
@@ -91,7 +91,7 @@
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/if_arp.h>
-#include "slhc.h"
+#include <net/slhc_vj.h>
 
 #define fcstab	ppp_crc16_table		/* Name of the table in the kernel */
 #include <linux/ppp_defs.h>
diff -u --recursive --new-file v1.3.98/linux/drivers/net/sdla.c linux/drivers/net/sdla.c
--- v1.3.98/linux/drivers/net/sdla.c	Wed Apr 17 09:06:32 1996
+++ linux/drivers/net/sdla.c	Mon May  6 12:26:08 1996
@@ -955,7 +955,7 @@
    }
 
    memset(&intr, 0, sizeof(intr));
-   /* lets start up the reception */
+   /* let's start up the reception */
    switch(flp->type)
    {
       case SDLA_S502A:
@@ -1028,7 +1028,7 @@
 
    sdla_cmd(dev, SDLA_ENABLE_COMMUNICATIONS, 0, 0, NULL, 0, NULL, NULL);
 
-   /* lets start up the reception */
+   /* let's start up the reception */
    memset(&intr, 0, sizeof(intr));
    switch(flp->type)
    {
diff -u --recursive --new-file v1.3.98/linux/drivers/net/seeq8005.c linux/drivers/net/seeq8005.c
--- v1.3.98/linux/drivers/net/seeq8005.c	Fri Apr 12 15:51:56 1996
+++ linux/drivers/net/seeq8005.c	Mon May  6 12:26:08 1996
@@ -611,7 +611,7 @@
 set_multicast_list(struct device *dev)
 {
 /*
- * I _could_ do upto 6 addresses here, but wont (yet?)
+ * I _could_ do up to 6 addresses here, but won't (yet?)
  */
 
 #if 0
diff -u --recursive --new-file v1.3.98/linux/drivers/net/seeq8005.h linux/drivers/net/seeq8005.h
--- v1.3.98/linux/drivers/net/seeq8005.h	Fri Apr 12 15:51:57 1996
+++ linux/drivers/net/seeq8005.h	Mon May  6 12:26:09 1996
@@ -119,7 +119,7 @@
 #define SEEQCFG2_NO_PREAM	(0x0080)	/* 1= user supplies Xmit preamble bytes */
 #define SEEQCFG2_ADDR_LEN	(0x0100)	/* 1= 2byte addresses */
 #define SEEQCFG2_REC_CRC	(0x0200)	/* 0= received packets will have CRC stripped from them */
-#define SEEQCFG2_XMIT_NO_CRC	(0x0400)	/* dont xmit CRC with each packet (user supplies it) */
+#define SEEQCFG2_XMIT_NO_CRC	(0x0400)	/* don't xmit CRC with each packet (user supplies it) */
 #define SEEQCFG2_LOOPBACK	(0x0800)
 #define SEEQCFG2_CTRLO		(0x1000)
 #define SEEQCFG2_RESET		(0x8000)	/* software Hard-reset bit */
diff -u --recursive --new-file v1.3.98/linux/drivers/net/sk_g16.c linux/drivers/net/sk_g16.c
--- v1.3.98/linux/drivers/net/sk_g16.c	Wed Apr 17 09:06:32 1996
+++ linux/drivers/net/sk_g16.c	Mon May  6 12:26:09 1996
@@ -112,7 +112,7 @@
 
 /* 
  * In POS3 are bits A14-A19 of the address bus. These bits can be set
- * to choose the RAM address. Thats why we only can choose the RAM address
+ * to choose the RAM address. That's why we only can choose the RAM address
  * in 16KB steps.
  */
 
@@ -1441,7 +1441,7 @@
         /* 
          * Here I have a problem.
          * I only know that there must be one or up to 15 collisions.
-         * Thats why TX_MORE is set, because after 16 attempts TX_RTRY
+         * That's why TX_MORE is set, because after 16 attempts TX_RTRY
          * will be set which means couldn't send packet aborted transfer.
          *
          * First I did not have this in but then I thought at minimum
diff -u --recursive --new-file v1.3.98/linux/drivers/net/slhc.c linux/drivers/net/slhc.c
--- v1.3.98/linux/drivers/net/slhc.c	Tue Apr  2 13:32:21 1996
+++ linux/drivers/net/slhc.c	Mon May  6 07:28:51 1996
@@ -78,7 +78,7 @@
 #include <asm/segment.h>
 #include <linux/mm.h>
 #include <net/checksum.h>
-#include "slhc.h"
+#include <net/slhc_vj.h>
 
 #ifdef __alpha__
 # include <asm/unaligned.h>
diff -u --recursive --new-file v1.3.98/linux/drivers/net/slhc.h linux/drivers/net/slhc.h
--- v1.3.98/linux/drivers/net/slhc.h	Wed Aug 10 19:26:18 1994
+++ linux/drivers/net/slhc.h	Thu Jan  1 02:00:00 1970
@@ -1,187 +0,0 @@
-#ifndef _SLHC_H
-#define _SLHC_H
-/*
- * Definitions for tcp compression routines.
- *
- * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
- *
- * Copyright (c) 1989 Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the University of California, Berkeley.  The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
- *	- Initial distribution.
- *
- *
- * modified for KA9Q Internet Software Package by
- * Katie Stevens (dkstevens@ucdavis.edu)
- * University of California, Davis
- * Computing Services
- *	- 01-31-90	initial adaptation
- *
- *	- Feb 1991	Bill_Simpson@um.cc.umich.edu
- *			variable number of conversation slots
- *			allow zero or one slots
- *			separate routines
- *			status display
- */
-
-/*
- * Compressed packet format:
- *
- * The first octet contains the packet type (top 3 bits), TCP
- * 'push' bit, and flags that indicate which of the 4 TCP sequence
- * numbers have changed (bottom 5 bits).  The next octet is a
- * conversation number that associates a saved IP/TCP header with
- * the compressed packet.  The next two octets are the TCP checksum
- * from the original datagram.  The next 0 to 15 octets are
- * sequence number changes, one change per bit set in the header
- * (there may be no changes and there are two special cases where
- * the receiver implicitly knows what changed -- see below).
- *
- * There are 5 numbers which can change (they are always inserted
- * in the following order): TCP urgent pointer, window,
- * acknowledgment, sequence number and IP ID.  (The urgent pointer
- * is different from the others in that its value is sent, not the
- * change in value.)  Since typical use of SLIP links is biased
- * toward small packets (see comments on MTU/MSS below), changes
- * use a variable length coding with one octet for numbers in the
- * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
- * range 256 - 65535 or 0.  (If the change in sequence number or
- * ack is more than 65535, an uncompressed packet is sent.)
- */
-
-/*
- * Packet types (must not conflict with IP protocol version)
- *
- * The top nibble of the first octet is the packet type.  There are
- * three possible types: IP (not proto TCP or tcp with one of the
- * control flags set); uncompressed TCP (a normal IP/TCP packet but
- * with the 8-bit protocol field replaced by an 8-bit connection id --
- * this type of packet syncs the sender & receiver); and compressed
- * TCP (described above).
- *
- * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
- * is logically part of the 4-bit "changes" field that follows.  Top
- * three bits are actual packet type.  For backward compatibility
- * and in the interest of conserving bits, numbers are chosen so the
- * IP protocol version number (4) which normally appears in this nibble
- * means "IP packet".
- */
-
-/* SLIP compression masks for len/vers byte */
-#define SL_TYPE_IP 0x40
-#define SL_TYPE_UNCOMPRESSED_TCP 0x70
-#define SL_TYPE_COMPRESSED_TCP 0x80
-#define SL_TYPE_ERROR 0x00
-
-/* Bits in first octet of compressed packet */
-#define NEW_C	0x40	/* flag bits for what changed in a packet */
-#define NEW_I	0x20
-#define NEW_S	0x08
-#define NEW_A	0x04
-#define NEW_W	0x02
-#define NEW_U	0x01
-
-/* reserved, special-case values of above */
-#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
-#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
-#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
-
-#define TCP_PUSH_BIT 0x10
-
-/*
- * data type and sizes conversion assumptions:
- *
- *	VJ code		KA9Q style	generic
- *	u_char		byte_t		unsigned char	 8 bits
- *	u_short		int16		unsigned short	16 bits
- *	u_int		int16		unsigned short	16 bits
- *	u_long		unsigned long	unsigned long	32 bits
- *	int		int32		long		32 bits
- */
-
-typedef unsigned char byte_t;
-typedef unsigned long int32;
-
-/*
- * "state" data for each active tcp conversation on the wire.  This is
- * basically a copy of the entire IP/TCP header from the last packet
- * we saw from the conversation together with a small identifier
- * the transmit & receive ends of the line use to locate saved header.
- */
-struct cstate {
-	byte_t	cs_this;	/* connection id number (xmit) */
-	struct cstate *next;	/* next in ring (xmit) */
-	struct iphdr cs_ip;	/* ip/tcp hdr from most recent packet */
-	struct tcphdr cs_tcp;
-	unsigned char cs_ipopt[64];
-	unsigned char cs_tcpopt[64];
-	int cs_hsize;
-};
-#define NULLSLSTATE	(struct cstate *)0
-
-/*
- * all the state data for one serial line (we need one of these per line).
- */
-struct slcompress {
-	struct cstate *tstate;	/* transmit connection states (array)*/
-	struct cstate *rstate;	/* receive connection states (array)*/
-
-	byte_t tslot_limit;	/* highest transmit slot id (0-l)*/
-	byte_t rslot_limit;	/* highest receive slot id (0-l)*/
-
-	byte_t xmit_oldest;	/* oldest xmit in ring */
-	byte_t xmit_current;	/* most recent xmit id */
-	byte_t recv_current;	/* most recent rcvd id */
-
-	byte_t flags;
-#define SLF_TOSS	0x01	/* tossing rcvd frames until id received */
-
-	int32 sls_o_nontcp;	/* outbound non-TCP packets */
-	int32 sls_o_tcp;	/* outbound TCP packets */
-	int32 sls_o_uncompressed;	/* outbound uncompressed packets */
-	int32 sls_o_compressed;	/* outbound compressed packets */
-	int32 sls_o_searches;	/* searches for connection state */
-	int32 sls_o_misses;	/* times couldn't find conn. state */
-
-	int32 sls_i_uncompressed;	/* inbound uncompressed packets */
-	int32 sls_i_compressed;	/* inbound compressed packets */
-	int32 sls_i_error;	/* inbound error packets */
-	int32 sls_i_tossed;	/* inbound packets tossed because of error */
-
-	int32 sls_i_runt;
-	int32 sls_i_badcheck;
-};
-#define NULLSLCOMPR	(struct slcompress *)0
-
-#define __ARGS(x) x
-
-/* In slhc.c: */
-struct slcompress *slhc_init __ARGS((int rslots, int tslots));
-void slhc_free __ARGS((struct slcompress *comp));
-
-int slhc_compress __ARGS((struct slcompress *comp, unsigned char *icp,
-			  int isize, unsigned char *ocp, unsigned char **cpp,
-			  int compress_cid));
-int slhc_uncompress __ARGS((struct slcompress *comp, unsigned char *icp,
-			    int isize));
-int slhc_remember __ARGS((struct slcompress *comp, unsigned char *icp,
-			  int isize));
-int slhc_toss __ARGS((struct slcompress *comp));
-
-void slhc_i_status __ARGS((struct slcompress *comp));
-void slhc_o_status __ARGS((struct slcompress *comp));
-
-#endif	/* _SLHC_H */
diff -u --recursive --new-file v1.3.98/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.3.98/linux/drivers/net/slip.c	Fri Apr 12 15:51:57 1996
+++ linux/drivers/net/slip.c	Mon May  6 07:28:51 1996
@@ -81,7 +81,7 @@
 #ifdef CONFIG_INET
 #include <linux/ip.h>
 #include <linux/tcp.h>
-#include "slhc.h"
+#include <net/slhc_vj.h>
 #endif
 
 #ifdef MODULE
diff -u --recursive --new-file v1.3.98/linux/drivers/net/smc-ultra.c linux/drivers/net/smc-ultra.c
--- v1.3.98/linux/drivers/net/smc-ultra.c	Fri Mar  1 07:50:46 1996
+++ linux/drivers/net/smc-ultra.c	Tue May  7 10:40:09 1996
@@ -1,6 +1,8 @@
 /* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
 /*
-	Written 1993,1994,1995 by Donald Becker.
+	This is a driver for the SMC Ultra and SMC EtherEZ ISA ethercards.
+
+	Written 1993-1996 by Donald Becker.
 
 	Copyright 1993 United States Government as represented by the
 	Director, National Security Agency.
@@ -12,8 +14,6 @@
 	Center of Excellence in Space Data and Information Sciences
 		Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-	This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
-
 	This driver uses the cards in the 8390-compatible, shared memory mode.
 	Most of the run-time complexity is handled by the generic code in
 	8390.c.  The code in this file is responsible for
@@ -32,17 +32,18 @@
 	transfers to avoid a bug in early version of the card that corrupted
 	data transferred by a AHA1542.
 
-	This driver does not support the programmed-I/O data transfer mode of
-	the EtherEZ.  That support (if available) is smc-ez.c.  Nor does it
-	use the non-8390-compatible "Altego" mode. (No support currently planned.)
+	This driver now supports the programmed-I/O (PIO) data transfer mode of
+	the EtherEZ. It does not use the non-8390-compatible "Altego" mode.
+	That support (if available) is smc-ez.c.
 
 	Changelog:
 
 	Paul Gortmaker	: multiple card support for module users.
+	Donald Becker	: 4/17/96 PIO support, minor potential problems avoided.
 */
 
 static const char *version =
-	"smc-ultra.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+	"smc-ultra.c:v1.99 4/17/96 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
 
 
 #include <linux/module.h>
@@ -73,6 +74,12 @@
 						  struct sk_buff *skb, int ring_offset);
 static void ultra_block_output(struct device *dev, int count,
 							const unsigned char *buf, const start_page);
+static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, 
+						int ring_page);
+static void ultra_pio_input(struct device *dev, int count,
+						  struct sk_buff *skb, int ring_offset);
+static void ultra_pio_output(struct device *dev, int count,
+							const unsigned char *buf, const start_page);
 static int ultra_close_card(struct device *dev);
 
 
@@ -81,8 +88,11 @@
 #define ULTRA_CMDREG	0		/* Offset to ASIC command register. */
 #define	 ULTRA_RESET	0x80	/* Board reset, in ULTRA_CMDREG. */
 #define	 ULTRA_MEMENB	0x40	/* Enable the shared memory. */
+#define IOPD	0x02			/* I/O Pipe Data (16 bits), PIO operation. */
+#define IOPA	0x07			/* I/O Pipe Address for PIO operation. */
 #define ULTRA_NIC_OFFSET  16	/* NIC register offset from the base_addr. */
 #define ULTRA_IO_EXTENT 32
+#define EN0_ERWCNT		0x08	/* Early receive warning count. */
 
 /*	Probe for the Ultra.  This looks like a 8013 with the station
 	address PROM at I/O ports <base>+8 to <base>+13, with a checksum
@@ -123,7 +133,7 @@
 	unsigned char eeprom_irq = 0;
 	static unsigned version_printed = 0;
 	/* Values from various config regs. */
-	unsigned char num_pages, irqreg, addr;
+	unsigned char num_pages, irqreg, addr, piomode;
 	unsigned char idreg = inb(ioaddr + 7);
 	unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
 
@@ -162,8 +172,9 @@
 
 	/* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
 	outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
-	irqreg = inb(ioaddr + 0xd);
+	piomode = inb(ioaddr + 0x8);
 	addr = inb(ioaddr + 0xb);
+	irqreg = inb(ioaddr + 0xd);
 
 	/* Switch back to the station address register set so that the MS-DOS driver
 	   can find the card after a warm boot. */
@@ -214,13 +225,20 @@
 	dev->mem_end = dev->rmem_end
 		= dev->mem_start + (ei_status.stop_page - START_PG)*256;
 
-	printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
-		   dev->irq, dev->mem_start, dev->mem_end-1);
-
+	if (piomode) {
+		printk(",%s IRQ %d programmed-I/O mode.\n",
+			   eeprom_irq ? "EEPROM" : "assigned ", dev->irq);
+		ei_status.block_input = &ultra_pio_input;
+		ei_status.block_output = &ultra_pio_output;
+		ei_status.get_8390_hdr = &ultra_pio_get_hdr;
+	} else {
+		printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
+			   dev->irq, dev->mem_start, dev->mem_end-1);
+		ei_status.block_input = &ultra_block_input;
+		ei_status.block_output = &ultra_block_output;
+		ei_status.get_8390_hdr = &ultra_get_8390_hdr;
+	}
 	ei_status.reset_8390 = &ultra_reset_8390;
-	ei_status.block_input = &ultra_block_input;
-	ei_status.block_output = &ultra_block_output;
-	ei_status.get_8390_hdr = &ultra_get_8390_hdr;
 	dev->open = &ultra_open;
 	dev->stop = &ultra_close_card;
 	NS8390_init(dev, 0);
@@ -236,9 +254,16 @@
 	if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name, NULL))
 		return -EAGAIN;
 
-	outb(ULTRA_MEMENB, ioaddr);	/* Enable memory, 16 bit mode. */
+	outb(0x00, ioaddr);	/* Disable shared memory for safety. */
 	outb(0x80, ioaddr + 5);
-	outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */
+	if (ei_status.block_input == &ultra_pio_input)
+		outb(0x11, ioaddr + 6);		/* Enable interrupts and PIO. */
+	else
+		outb(0x01, ioaddr + 6);		/* Enable interrupts and memory. */
+	/* Set the early receive warning level in window 0 high enough not
+	   to receive ERW interrupts. */
+	outb_p(E8390_NODMA+E8390_PAGE0, dev->base_addr);
+	outb(0xff, dev->base_addr + EN0_ERWCNT);
 	ei_open(dev);
 	MOD_INC_USE_COUNT;
 	return 0;
@@ -253,7 +278,12 @@
 	if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
 	ei_status.txing = 0;
 
-	outb(ULTRA_MEMENB, cmd_port);
+	outb(0x00, cmd_port);	/* Disable shared memory for safety. */
+	outb(0x80, cmd_port + 5);
+	if (ei_status.block_input == &ultra_pio_input)
+		outb(0x11, cmd_port + 6);		/* Enable interrupts and PIO. */
+	else
+		outb(0x01, cmd_port + 6);		/* Enable interrupts and memory. */
 
 	if (ei_debug > 1) printk("reset done\n");
 	return;
@@ -266,7 +296,6 @@
 static void
 ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 {
-
 	unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8);
 
 	outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);	/* shmem on */
@@ -316,6 +345,49 @@
 	memcpy_toio(shmem, buf, count);
 
 	outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+/* The identical operations for programmed I/O cards.
+   The PIO model is trivial to use: the 16 bit start address is written
+   byte-sequentially to IOPA, with no intervening I/O operations, and the
+   data is read or written to the IOPD data port.
+   The only potential complication is that the address register is shared
+   must be always be rewritten between each read/write direction change.
+   This is no problem for us, as the 8390 code ensures that we are single
+   threaded. */
+static void ultra_pio_get_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, 
+						int ring_page)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
+	outb(ring_page, ioaddr + IOPA);
+	insw(ioaddr + IOPD, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+static void ultra_pio_input(struct device *dev, int count,
+						  struct sk_buff *skb, int ring_offset)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+    char *buf = skb->data;
+
+	/* For now set the address again, although it should already be correct. */
+	outb(ring_offset, ioaddr + IOPA);	/* Set the address, LSB first. */
+	outb(ring_offset >> 8, ioaddr + IOPA);
+	insw(ioaddr + IOPD, buf, (count+1)>>1);
+#ifdef notdef
+	/* We don't need this -- skbuffs are padded to at least word alignment. */
+	if (count & 0x01) {
+		buf[count-1] = inb(ioaddr + IOPD);
+#endif
+}
+
+static void ultra_pio_output(struct device *dev, int count,
+							const unsigned char *buf, const start_page)
+{
+	int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+	outb(0x00, ioaddr + IOPA);	/* Set the address, LSB first. */
+	outb(start_page, ioaddr + IOPA);
+	outsw(ioaddr + IOPD, buf, (count+1)>>1);
 }
 
 static int
diff -u --recursive --new-file v1.3.98/linux/drivers/sbus/char/sunkeymap.map linux/drivers/sbus/char/sunkeymap.map
--- v1.3.98/linux/drivers/sbus/char/sunkeymap.map	Sat Apr 27 15:19:56 1996
+++ linux/drivers/sbus/char/sunkeymap.map	Mon May  6 12:26:09 1996
@@ -273,7 +273,7 @@
 # keycode 0x7f is special and it means 'all keys released' and is
 # taken care of within the sun keyboard driver itself
 keycode 0x7f =
-# Thats all folks...
+# That's all folks...
 string F1 = "\033[[A"
 string F2 = "\033[[B"
 string F3 = "\033[[C"
diff -u --recursive --new-file v1.3.98/linux/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- v1.3.98/linux/drivers/sbus/char/sunserial.c	Sun May  5 08:52:00 1996
+++ linux/drivers/sbus/char/sunserial.c	Mon May  6 12:26:09 1996
@@ -467,7 +467,7 @@
 	}
 
 	if((info->xmit_cnt <= 0) || info->tty->stopped) {
-		/* Thats peculiar... */
+		/* That's peculiar... */
 		info->zs_channel->control = RES_Tx_P;
 		udelay(5);
 		goto clear_and_return;
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- v1.3.98/linux/drivers/scsi/53c7,8xx.c	Wed Apr 17 09:06:32 1996
+++ linux/drivers/scsi/53c7,8xx.c	Mon May  6 12:26:10 1996
@@ -345,7 +345,7 @@
  * 001c46d4 : 0x001c5ea0 0x000011f8
  *
  *	Changed the print code in the phase_mismatch handler so
- *	that we call print_lots to try and diagnose this.
+ *	that we call print_lots to try to diagnose this.
  *
  */
 
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/ChangeLog linux/drivers/scsi/ChangeLog
--- v1.3.98/linux/drivers/scsi/ChangeLog	Fri Apr 19 10:07:59 1996
+++ linux/drivers/scsi/ChangeLog	Mon May  6 12:26:10 1996
@@ -1233,7 +1233,7 @@
 	* scsi.c (scan_scsis): Add new devices to end of linked list, not
 	to the beginning.
 
-	* scsi.h (SCSI_SLEEP): Remove brain dead hack to try and save
+	* scsi.h (SCSI_SLEEP): Remove brain dead hack to try to save
 	the task state before sleeping.
 
 Sat Jul  9 15:01:03 1994  Eric Youngdale  (eric@esp22)
@@ -1724,7 +1724,7 @@
     * constants.c, constants.h: New files with ascii descriptions of
     various conditions.
 
-    * Makefile: Do not try and count the number of low-level drivers,
+    * Makefile: Do not try to count the number of low-level drivers,
     just generate the list of .o files.
 
     * aha1542.c: Replace 16 with sizeof(SCpnt->sense_buffer).  Add tests
@@ -1819,7 +1819,7 @@
 
     * sr.c: Increase timeout to 500 from 250.  Add entry for sync in
     dispatch table (supply NULL).  If we do not have a sectorsize,
-    try and get it in the sd_open function.  Add new function just to
+    try to get it in the sd_open function.  Add new function just to
     obtain sectorsize.
 
     * sr.h: Add needs_sector_size semaphore.
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.98/linux/drivers/scsi/Config.in	Sun Apr 21 12:39:01 1996
+++ linux/drivers/scsi/Config.in	Mon May  6 07:34:20 1996
@@ -21,7 +21,7 @@
 dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
 dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
-dep_tristate 'EATA-DMA (DPT, NEC, ATT, Olivetti) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
+dep_tristate 'EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
 dep_tristate 'UltraStor 14F/34F support' CONFIG_SCSI_U14_34F $CONFIG_SCSI
 dep_tristate 'Future Domain 16xx SCSI support' CONFIG_SCSI_FUTURE_DOMAIN $CONFIG_SCSI
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v1.3.98/linux/drivers/scsi/README.st	Sun May  5 08:52:00 1996
+++ linux/drivers/scsi/README.st	Sun May  5 08:51:13 1996
@@ -274,7 +274,7 @@
 The following ioctls use the structure mtlocation that contains both
 the block number and the partition number. These ioctls are available
 only for SCSI-2 tape drives and the block number is the
-device-indepent logical block number defined by the standard.
+device-independent logical block number defined by the standard.
 
 MTGETLOC Returns the current block and partition number.
 MTSETLOC Sets the tape to the block and partition specified by the
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v1.3.98/linux/drivers/scsi/aha1542.c	Mon Apr 15 12:20:19 1996
+++ linux/drivers/scsi/aha1542.c	Mon May  6 12:26:10 1996
@@ -1214,7 +1214,7 @@
 	setup_mailboxes(SCpnt->host->io_port, SCpnt->host);
 
 	/*
-	 * Now try and pick up the pieces.  Restart all commands
+	 * Now try to pick up the pieces.  Restart all commands
 	 * that are currently active on the bus, and reset all of
 	 * the datastructures.  We have some time to kill while
 	 * things settle down, so print a nice message.
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v1.3.98/linux/drivers/scsi/aic7xxx.c	Sun May  5 08:52:00 1996
+++ linux/drivers/scsi/aic7xxx.c	Mon May  6 12:26:10 1996
@@ -4483,12 +4483,12 @@
        * The target we select to send the message to may be entirely
        * different than the target pointed to by the scb that timed
        * out.  If the command is in the QINFIFO or the waiting for
-       * selection list, its not tying up the bus and isn't responsible
+       * selection list, it's not tying up the bus and isn't responsible
        * for the delay so we pick off the active command which should
-       * be the SCB selected by SCBPTR.  If its disconnected or active,
+       * be the SCB selected by SCBPTR.  If it's disconnected or active,
        * we device reset the target scbp points to.  Although it may
        * be that this target is not responsible for the delay, it may
-       * may also be that we're timing out on a command that just takes
+       * also be that we're timing out on a command that just takes
        * too much time, so we try the bus device reset there first.
        */
       active_scb = inb(SCBPTR + base);
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/eata_dma.c linux/drivers/scsi/eata_dma.c
--- v1.3.98/linux/drivers/scsi/eata_dma.c	Sun May  5 08:52:00 1996
+++ linux/drivers/scsi/eata_dma.c	Mon May  6 07:34:20 1996
@@ -8,8 +8,15 @@
  *							    *
  *  The driver currently:				    *
  *	-supports all ISA based EATA-DMA boards		    *
+ *       like PM2011, PM2021, PM2041, PM3021                *
  *	-supports all EISA based EATA-DMA boards	    *
+ *       like PM2012B, PM2022, PM2122, PM2322, PM2042,      *
+ *            PM3122, PM3222, PM3332                        *
  *	-supports all PCI based EATA-DMA boards		    *
+ *       like PM2024, PM2124, PM2044, PM2144, PM3224,       *
+ *            PM3334                                        *
+ *      -supports the Wide, Ultra Wide and Differential     *
+ *       versions of the boards                             *
  *	-supports multiple HBAs with & without IRQ sharing  *
  *	-supports all SCSI channels on multi channel boards *
  *	-needs identical IDs on all channels of a HBA	    * 
@@ -19,8 +26,9 @@
  *      -provides rudimentary latency measurement           * 
  *       possibilities via /proc/scsi/eata_dma/<hostnum>    *
  *							    *
- *  (c)1993,94,95 Michael Neuffer			    *
- *		  neuffer@goofy.zdv.uni-mainz.de	    *
+ *  (c)1993-96 Michael Neuffer			            *
+ *             mike@i-Connect.Net                           *
+ *	       neuffer@mail.uni-mainz.de	            *
  *							    *
  *  This program is free software; you can redistribute it  *
  *  and/or modify it under the terms of the GNU General	    *
@@ -45,10 +53,11 @@
  * very helpful and tried to give me all the infos and	    *
  * support I need.					    *
  *							    *
- * Thanks also to Greg Hosler who did a lot of testing and  *
- * found quite a number of bugs during the development.	    *
+ * Thanks also to Simon Shapiro, Greg Hosler and Mike       *
+ * Jagdis who did a lot of testing and found quite a number *
+ * of bugs during the development.                          *
  ************************************************************
- *  last change: 95/04/27                 OS: Linux 1.3.95  *
+ *  last change: 95/05/05                 OS: Linux 1.3.98  *
  ************************************************************/
 
 /* Look in eata_dma.h for configuration and revision information */
@@ -63,6 +72,7 @@
 #include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
+#include <linux/delay.h>
 #include <asm/byteorder.h>
 #include <asm/types.h>
 #include <asm/io.h>
@@ -132,7 +142,7 @@
     else reg_IRQ[sh->irq]--;
     
     scsi_init_free((void *)status, 512);
-    scsi_init_free((void *)dma_scratch, 512);
+    scsi_init_free((void *)dma_scratch - 4, 1024);
     for (i = 0; i < sh->can_queue; i++){ /* Free all SG arrays */
 	if(SD(sh)->ccb[i].sg_list != NULL)
 	    scsi_init_free((void *) SD(sh)->ccb[i].sg_list, 
@@ -149,12 +159,89 @@
 #endif
 
 
+inline void eata_latency_in(struct eata_ccb *cp, hostdata *hd)
+{
+    uint time;
+    time = jiffies - cp->timestamp;
+    if(hd->all_lat[1] > time)
+        hd->all_lat[1] = time;
+    if(hd->all_lat[2] < time)
+        hd->all_lat[2] = time;
+    hd->all_lat[3] += time;
+    hd->all_lat[0]++;
+    if((cp->rw_latency) == WRITE) { /* was WRITE */
+        if(hd->writes_lat[cp->sizeindex][1] > time)
+	    hd->writes_lat[cp->sizeindex][1] = time;
+	if(hd->writes_lat[cp->sizeindex][2] < time)
+	    hd->writes_lat[cp->sizeindex][2] = time;
+	hd->writes_lat[cp->sizeindex][3] += time;
+	hd->writes_lat[cp->sizeindex][0]++;
+    } else if((cp->rw_latency) == READ) {
+        if(hd->reads_lat[cp->sizeindex][1] > time)
+	    hd->reads_lat[cp->sizeindex][1] = time;
+	if(hd->reads_lat[cp->sizeindex][2] < time)
+	    hd->reads_lat[cp->sizeindex][2] = time;
+	hd->reads_lat[cp->sizeindex][3] += time;
+	hd->reads_lat[cp->sizeindex][0]++;
+    }
+} 
+
+inline void eata_latency_out(struct eata_ccb *cp, Scsi_Cmnd *cmd)
+{
+    int x, z;
+    short *sho;
+    long *lon;
+    x = 0;	                        /* just to keep GCC quiet */ 
+    cp->timestamp = jiffies;	        /* For latency measurements */
+    switch(cmd->cmnd[0]) {
+    case WRITE_6:   
+        x = cmd->cmnd[4]/2; 
+	cp->rw_latency = WRITE;
+	break;
+    case READ_6:    
+        x = cmd->cmnd[4]/2; 
+	cp->rw_latency = READ;
+	break;
+    case WRITE_10:   
+        sho = (short *) &cmd->cmnd[7];
+	x = ntohs(*sho)/2;	      
+	cp->rw_latency = WRITE;
+	break;
+    case READ_10:
+        sho = (short *) &cmd->cmnd[7];
+	x = ntohs(*sho)/2;	      
+	cp->rw_latency = READ;
+	break;
+    case WRITE_12:   
+        lon = (long *) &cmd->cmnd[6];
+	x = ntohl(*lon)/2;	      
+	cp->rw_latency = WRITE;
+	break;
+    case READ_12:
+        lon = (long *) &cmd->cmnd[6];
+	x = ntohl(*lon)/2;	      
+	cp->rw_latency = READ;
+	break;
+    default:
+        cp->rw_latency = OTHER;
+	break;
+    }
+    if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10 || 
+	cmd->cmnd[0] == WRITE_12 || cmd->cmnd[0] == READ_6 || 
+	cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == READ_12) {
+        for(z = 0; (x > (1 << z)) && (z <= 11); z++) 
+	    /* nothing */;
+	cp->sizeindex = z;
+    } 
+}
+
+
 void eata_int_handler(int irq, void *dev_id, struct pt_regs * regs)
 {
     uint i, result = 0;
     uint hba_stat, scsi_stat, eata_stat;
     Scsi_Cmnd *cmd;
-    struct eata_ccb *cp;
+    struct eata_ccb *ccb;
     struct eata_sp *sp;
     uint base;
     ulong flags;
@@ -173,16 +260,16 @@
 	    int_counter++;
 	    
 	    sp = &SD(sh)->sp;
-	    cp = sp->ccb;
+	    ccb = sp->ccb;
 	    
-	    if(cp == NULL) {
+	    if(ccb == NULL) {
 		eata_stat = inb((uint)sh->base + HA_RSTATUS);
 		printk("eata_dma: int_handler, Spurious IRQ %d "
 		       "received. CCB pointer not set.\n", irq);
 		break;
 	    }
 
-	    cmd = cp->cmd;
+	    cmd = ccb->cmd;
 	    base = (uint) cmd->host->base;
        	    hba_stat = sp->hba_stat;
 	    
@@ -190,23 +277,27 @@
 	    
 	    if (sp->EOC == FALSE) {
 		eata_stat = inb(base + HA_RSTATUS);
-		printk("eata_dma: int_handler, board: %x cmd %lx returned "
-		       "unfinished.\nEATA: %x HBA: %x SCSI: %x spadr %lx "
-		       "spadrirq %lx, irq%d\n", base, (long)cp, eata_stat, 
-		       hba_stat, scsi_stat,(long)&status, (long)&status[irq], 
-		       irq);
-		DBG(DBG_DELAY, DEL2(800));
+		printk(KERN_WARNING "eata_dma: int_handler, board: %x cmd %lx "
+		       "returned unfinished.\n"
+		       "EATA: %x HBA: %x SCSI: %x spadr %lx spadrirq %lx, "
+		       "irq%d\n", base, (long)ccb, eata_stat, hba_stat, 
+		       scsi_stat,(long)&status, (long)&status[irq], irq);
+		cmd->result = DID_ERROR << 16;
+		ccb->status = FREE;
+		cmd->scsi_done(cmd);
 		break;
 	    } 
 	    
-	    if (cp->status == LOCKED) {
-		cp->status = FREE;
+	    if (ccb->status == LOCKED) {
+		ccb->status = FREE;
 		eata_stat = inb(base + HA_RSTATUS);
 		printk("eata_dma: int_handler, freeing locked queueslot\n");
-		DBG(DBG_INTR && DBG_DELAY, DEL2(800));
+		DBG(DBG_INTR && DBG_DELAY, DELAY(1));
 		break;
 	    }
 	    
+	    sp->EOC = FALSE; /* Clean out this flag */
+
 	    eata_stat = inb(base + HA_RSTATUS); 
 	    DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, "
 				 "target: %x, lun: %x, ea_s: %#.2x, hba_s: "
@@ -215,52 +306,22 @@
 	    
 	    switch (hba_stat) {
 	    case HA_NO_ERROR:	/* NO Error */
-		if (scsi_stat == CONDITION_GOOD
-		    && cmd->device->type == TYPE_DISK
-		    && (HD(cmd)->t_state[cp->cp_channel][cp->cp_id] == RESET))
-		    result = DID_BUS_BUSY << 16;	    
-		else if (scsi_stat == GOOD) {
-		    HD(cmd)->t_state[cp->cp_channel][cp->cp_id] = OK;
-		    if(HD(cmd)->do_latency == TRUE && cp->timestamp) {
-			uint time;
-			time = jiffies - cp->timestamp;
-			if((cp->rw_latency) == TRUE) { /* was WRITE */
-			    if(HD(cmd)->writes_lat[cp->sizeindex][1] > time)
-				HD(cmd)->writes_lat[cp->sizeindex][1] = time;
-			    if(HD(cmd)->writes_lat[cp->sizeindex][2] < time)
-				HD(cmd)->writes_lat[cp->sizeindex][2] = time;
-			    HD(cmd)->writes_lat[cp->sizeindex][3] += time;
-			    HD(cmd)->writes_lat[cp->sizeindex][0]++;
-			} else {
-			    if(HD(cmd)->reads_lat[cp->sizeindex][1] > time)
-				HD(cmd)->reads_lat[cp->sizeindex][1] = time;
-			    if(HD(cmd)->reads_lat[cp->sizeindex][2] < time)
-				HD(cmd)->reads_lat[cp->sizeindex][2] = time;
-			    HD(cmd)->reads_lat[cp->sizeindex][3] += time;
-			    HD(cmd)->reads_lat[cp->sizeindex][0]++;
-			}
-		    }
-		}
-		else if (scsi_stat == CHECK_CONDITION
-			 && cmd->device->type == TYPE_DISK
-			 && (cmd->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
-		    result = DID_BUS_BUSY << 16;
-		else
-		    result = DID_OK << 16;
-		HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] = OK;
+	        HD(cmd)->t_state[ccb->cp_channel][ccb->cp_id] = OK;
+		if(HD(cmd)->do_latency == TRUE && ccb->timestamp) 
+		    eata_latency_in(ccb, HD(cmd));
+		result = DID_OK << 16;
+		HD(cmd)->t_timeout[ccb->cp_channel][ccb->cp_id] = OK;
 		break;
 	    case HA_ERR_SEL_TO:	        /* Selection Timeout */
-		result = DID_BAD_TARGET << 16;  
-		break;
 	    case HA_ERR_CMD_TO:	        /* Command Timeout   */
-		if (HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] > 1)
-		    result = DID_ERROR << 16;
-		else {
-		    result = DID_TIME_OUT << 16;
-		    HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id]++;
-		}
+		result = DID_TIME_OUT << 16;
+		break;
+	    case HA_BUS_RESET:		/* SCSI Bus Reset Received */
+		result = DID_RESET << 16;
+		DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: BUS RESET "
+				       "received on cmd %ld\n", 
+				       HD(cmd)->HBA_number, cmd->pid));
 		break;
-	    case HA_ERR_RESET:		/* SCSI Bus Reset Received */
 	    case HA_INIT_POWERUP:	/* Initial Controller Power-up */
 		if (cmd->device->type != TYPE_TAPE)
 		    result = DID_BUS_BUSY << 16;
@@ -268,15 +329,39 @@
 		    result = DID_ERROR << 16;
 		
 		for (i = 0; i < MAXTARGET; i++)
-		    HD(cmd)->t_state[cp->cp_channel][i] = RESET;
+		    HD(cmd)->t_state[ccb->cp_channel][i] = RESET;
+		DBG(DBG_STATUS, printk(KERN_DEBUG "scsi%d: cmd pid %ld "
+				       "returned with INIT_POWERUP\n", 
+				       HD(cmd)->HBA_number, cmd->pid));
+		break;
+	    case HA_CP_ABORT_NA:
+	    case HA_CP_ABORTED:
+		result = DID_ABORT << 16;
+		DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: aborted cmd "
+				       "returned\n", HD(cmd)->HBA_number));
+ 		break;
+	    case HA_CP_RESET_NA:
+	    case HA_CP_RESET:
+	        HD(cmd)->t_state[cmd->channel][cmd->target] = OK; 
+	        HD(cmd)->resetlevel[cmd->channel] = 0; 
+		result = DID_RESET << 16;
+		DBG(DBG_STATUS, printk(KERN_WARNING "scsi%d: reseted cmd "
+				       "pid %ldreturned\n", 
+				       HD(cmd)->HBA_number, cmd->pid));
+	    case HA_SCSI_HUNG:	        /* SCSI Hung                 */
+	        printk(KERN_ERR "scsi%d: SCSI hung\n", HD(cmd)->HBA_number);
+		result = DID_ERROR << 16;
+		break;
+	    case HA_RSENSE_FAIL:        /* Auto Request-Sense Failed */
+	        DBG(DBG_STATUS, printk(KERN_ERR "scsi%d: Auto Request Sense "
+				       "Failed\n", HD(cmd)->HBA_number));
+		result = DID_ERROR << 16;
 		break;
 	    case HA_UNX_BUSPHASE:	/* Unexpected Bus Phase */
 	    case HA_UNX_BUS_FREE:	/* Unexpected Bus Free */
 	    case HA_BUS_PARITY:	        /* Bus Parity Error */
-	    case HA_SCSI_HUNG:	        /* SCSI Hung */
 	    case HA_UNX_MSGRJCT:	/* Unexpected Message Reject */
 	    case HA_RESET_STUCK:        /* SCSI Bus Reset Stuck */
-	    case HA_RSENSE_FAIL:        /* Auto Request-Sense Failed */
 	    case HA_PARITY_ERR:	        /* Controller Ram Parity */
 	    default:
 		result = DID_ERROR << 16;
@@ -293,10 +378,10 @@
 		       cmd->device->channel, cmd->device->id, cmd->device->lun,
 		       cmd->pid, eata_stat, hba_stat, scsi_stat, 
 		       cmd->sense_buffer[2] & 0xf, cmd->result); 
-	    DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+	    DBG(DBG_INTR&&DBG_DELAY,DELAY(1));
 #endif
 	    
-	    cp->status = FREE;	    /* now we can release the slot  */
+	    ccb->status = FREE;	    /* now we can release the slot  */
 	    cmd->scsi_done(cmd);
 	}
     }
@@ -314,41 +399,55 @@
 	    return(FALSE);
 
     /* And now the address in nice little byte chunks */
+#ifdef __LITTLE_ENDIAN
     outb( addr & 0x000000ff,	    base + HA_WDMAADDR);
     outb((addr & 0x0000ff00) >> 8,  base + HA_WDMAADDR + 1);
     outb((addr & 0x00ff0000) >> 16, base + HA_WDMAADDR + 2);
     outb((addr & 0xff000000) >> 24, base + HA_WDMAADDR + 3);
+#else
+    outb((addr & 0xff000000) >> 24, base + HA_WDMAADDR);
+    outb((addr & 0x00ff0000) >> 16, base + HA_WDMAADDR + 1);
+    outb((addr & 0x0000ff00) >> 8,  base + HA_WDMAADDR + 2);
+    outb((addr & 0x000000ff),       base + HA_WDMAADDR + 3);
+#endif
     outb(command, base + HA_WCOMMAND);
     return(TRUE);
 }
 
-#if 0 
-inline int eata_send_immediate(u32 addr, u32 base, u8 cmnd, u8 cmnd2, u8 id, 
-			       u8 lun)
+inline int eata_send_immediate(u32 base, u32 addr, u8 ifc, u8 code, u8 code2)
 {
+    outb(0x0, base + HA_WDMAADDR - 1);
     if(addr){
-	outb( addr & 0x000000ff,	base + HA_WDMAADDR);
-	outb((addr & 0x0000ff00) >> 8,	base + HA_WDMAADDR + 1);
+#ifdef __LITTLE_ENDIAN
+        outb( addr & 0x000000ff,        base + HA_WDMAADDR);
+	outb((addr & 0x0000ff00) >> 8,  base + HA_WDMAADDR + 1);
 	outb((addr & 0x00ff0000) >> 16, base + HA_WDMAADDR + 2);
 	outb((addr & 0xff000000) >> 24, base + HA_WDMAADDR + 3);
+#else
+	outb((addr & 0xff000000) >> 24, base + HA_WDMAADDR);
+	outb((addr & 0x00ff0000) >> 16, base + HA_WDMAADDR + 1);
+	outb((addr & 0x0000ff00) >> 8,  base + HA_WDMAADDR + 2);
+	outb((addr & 0x000000ff),       base + HA_WDMAADDR + 3);
+#endif
     } else {
-	outb(id,  base + HA_WSUBCODE);
-	outb(lun, base + HA_WSUBLUN);
+        outb(0x0, base + HA_WDMAADDR);
+        outb(0x0, base + HA_WDMAADDR + 1);
+ 	outb(code2, base + HA_WCODE2);
+	outb(code,  base + HA_WCODE);
     }
     
-    outb(cmnd2, base + HA_WCOMMAND2);
-    outb(cmnd,	base + HA_WCOMMAND);
+    outb(ifc, base + HA_WIFC);
+    outb(EATA_CMD_IMMEDIATE, base + HA_WCOMMAND);
     return(TRUE);
 }
-#endif
 
 int eata_queue(Scsi_Cmnd * cmd, void (* done) (Scsi_Cmnd *))
 {
     unsigned int i, x, y;
-    u32 flags;
+    ulong flags;
     hostdata *hd;
     struct Scsi_Host *sh;
-    struct eata_ccb *cp;
+    struct eata_ccb *ccb;
     struct scatterlist *sl;
     
     save_flags(flags);
@@ -359,6 +458,15 @@
     hd = HD(cmd);
     sh = cmd->host;
     
+#if 1
+    if (cmd->cmnd[0] == REQUEST_SENSE && cmd->sense_buffer[0] != 0) {
+        DBG(DBG_REQSENSE, printk(KERN_DEBUG "Tried to REQUEST SENSE\n"));
+	cmd->result = DID_OK << 16;
+	done(cmd);
+	return(0);
+    }
+#endif
+
     /* check for free slot */
     for (y = hd->last_ccb + 1, x = 0; x < sh->can_queue; x++, y++) { 
 	if (y >= sh->can_queue)
@@ -378,52 +486,19 @@
 	restore_flags(flags);
 	return(0);
     }
-    cp = &hd->ccb[y];
+    ccb = &hd->ccb[y];
     
-    memset(cp, 0, sizeof(struct eata_ccb) - sizeof(struct eata_sg_list *));
+    memset(ccb, 0, sizeof(struct eata_ccb) - sizeof(struct eata_sg_list *));
     
-    cp->status = USED;			/* claim free slot */
+    ccb->status = USED;			/* claim free slot */
     
     DBG(DBG_QUEUE, printk("eata_queue pid %ld, target: %x, lun: %x, y %d\n",
 			  cmd->pid, cmd->target, cmd->lun, y));
-    DBG(DBG_QUEUE && DBG_DELAY, DEL2(250));
+    DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
     
-    if(hd->do_latency == TRUE) {
-	int x, z;
-	short *sho;
-	long *lon;
-	x = 0;	/* just to keep GCC quiet */ 
-	if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10 || 
-	    cmd->cmnd[0] == WRITE_12 || cmd->cmnd[0] == READ_6 || 
-	    cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == READ_12) {
-	    
-	    cp->timestamp = jiffies;	/* For latency measurements */
-	    switch(cmd->cmnd[0]) {
-	    case WRITE_6:   
-	    case READ_6:    
-		x = cmd->cmnd[4]/2; 
-		break;
-	    case WRITE_10:   
-	    case READ_10:
-		sho = (short *) &cmd->cmnd[7];
-		x = ntohs(*sho)/2;	      
-		break;
-	    case WRITE_12:   
-	    case READ_12:
-		lon = (long *) &cmd->cmnd[6];
-		x = ntohl(*lon)/2;	      
-		break;
-	    }
+    if(hd->do_latency == TRUE) 
+        eata_latency_out(ccb, cmd);
 
-	    for(z = 0; (x > (1 << z)) && (z <= 11); z++) 
-		/* nothing */;
-	    cp->sizeindex = z;
-	    if (cmd->cmnd[0] ==	WRITE_6 || cmd->cmnd[0] == WRITE_10 || 
-		cmd->cmnd[0] ==	WRITE_12){
-		cp->rw_latency = TRUE;
-	    }
-	}
-    }
     cmd->scsi_done = (void *)done;
     
     switch (cmd->cmnd[0]) {
@@ -438,73 +513,73 @@
     case WRITE_12:	    case WRITE_VERIFY_12: case SET_WINDOW: 
     case MEDIUM_SCAN:	    case SEND_VOLUME_TAG:	     
     case 0xea:	    /* alternate number for WRITE LONG */
-	cp->DataOut = TRUE;	/* Output mode */
+	ccb->DataOut = TRUE;	/* Output mode */
 	break;
     case TEST_UNIT_READY:
     default:
-	cp->DataIn = TRUE;	/* Input mode  */
+	ccb->DataIn = TRUE;	/* Input mode  */
     }
 
     /* FIXME: This will will have to be changed once the midlevel driver 
      *        allows different HBA IDs on every channel.
      */
     if (cmd->target == sh->this_id) 
-	cp->Interpret = TRUE;	/* Interpret command */
+	ccb->Interpret = TRUE;	/* Interpret command */
 
     if (cmd->use_sg) {
-	cp->scatter = TRUE;	/* SG mode     */
-	if (cp->sg_list == NULL) {
-	    cp->sg_list = kmalloc(sh->sg_tablesize * sizeof(struct eata_sg_list),
+	ccb->scatter = TRUE;	/* SG mode     */
+	if (ccb->sg_list == NULL) {
+	    ccb->sg_list = kmalloc(sh->sg_tablesize * sizeof(struct eata_sg_list),
 				  GFP_ATOMIC | GFP_DMA);
 	}
-	if (cp->sg_list == NULL)
+	if (ccb->sg_list == NULL)
 	    panic("eata_dma: Run out of DMA memory for SG lists !\n");
-	cp->cp_dataDMA = htonl(virt_to_bus(cp->sg_list)); 
+	ccb->cp_dataDMA = htonl(virt_to_bus(ccb->sg_list)); 
 	
-	cp->cp_datalen = htonl(cmd->use_sg * sizeof(struct eata_sg_list));
+	ccb->cp_datalen = htonl(cmd->use_sg * sizeof(struct eata_sg_list));
 	sl=(struct scatterlist *)cmd->request_buffer;
 	for(i = 0; i < cmd->use_sg; i++, sl++){
-	    cp->sg_list[i].data = htonl(virt_to_bus(sl->address));
-	    cp->sg_list[i].len = htonl((u32) sl->length);
+	    ccb->sg_list[i].data = htonl(virt_to_bus(sl->address));
+	    ccb->sg_list[i].len = htonl((u32) sl->length);
 	}
     } else {
-	cp->scatter = FALSE;
-	cp->cp_datalen = htonl(cmd->request_bufflen);
-	cp->cp_dataDMA = htonl(virt_to_bus(cmd->request_buffer));
+	ccb->scatter = FALSE;
+	ccb->cp_datalen = htonl(cmd->request_bufflen);
+	ccb->cp_dataDMA = htonl(virt_to_bus(cmd->request_buffer));
     }
     
-    cp->Auto_Req_Sen = TRUE;
-    cp->cp_reqDMA = htonl(virt_to_bus(cmd->sense_buffer));
-    cp->reqlen = sizeof(cmd->sense_buffer);
-    
-    cp->cp_id = cmd->target;
-    cp->cp_channel = cmd->channel;
-    cp->cp_lun = cmd->lun;
-    cp->cp_dispri = TRUE;
-    cp->cp_identify = TRUE;
-    memcpy(cp->cp_cdb, cmd->cmnd, cmd->cmd_len);
+    ccb->Auto_Req_Sen = TRUE;
+    ccb->cp_reqDMA = htonl(virt_to_bus(cmd->sense_buffer));
+    ccb->reqlen = sizeof(cmd->sense_buffer);
+    
+    ccb->cp_id = cmd->target;
+    ccb->cp_channel = cmd->channel;
+    ccb->cp_lun = cmd->lun;
+    ccb->cp_dispri = TRUE;
+    ccb->cp_identify = TRUE;
+    memcpy(ccb->cp_cdb, cmd->cmnd, cmd->cmd_len);
     
-    cp->cp_statDMA = htonl(virt_to_bus(&(hd->sp)));
+    ccb->cp_statDMA = htonl(virt_to_bus(&(hd->sp)));
     
-    cp->cp_viraddr = cp; /* This will be passed thru, so we don't need to 
+    ccb->cp_viraddr = ccb; /* This will be passed thru, so we don't need to 
                           * convert it */
-    cp->cmd = cmd;
+    ccb->cmd = cmd;
     cmd->host_scribble = (char *)&hd->ccb[y];	
     
-    if(eata_send_command((u32) cp, (u32) sh->base, EATA_CMD_DMA_SEND_CP) == FALSE) {
+    if(eata_send_command((u32) ccb, (u32) sh->base, EATA_CMD_DMA_SEND_CP) == FALSE) {
 	cmd->result = DID_BUS_BUSY << 16;
 	DBG(DBG_QUEUE && DBG_ABNORM, 
 	    printk("eata_queue target %d, pid %ld, HBA busy, "
 		   "returning DID_BUS_BUSY\n",cmd->target, cmd->pid));
 	done(cmd);
-	cp->status = FREE;    
+	ccb->status = FREE;    
 	restore_flags(flags);
 	return(0);
     }
     DBG(DBG_QUEUE, printk("Queued base %#.4x pid: %ld target: %x lun: %x "
 			 "slot %d irq %d\n", (s32)sh->base, cmd->pid, 
 			 cmd->target, cmd->lun, y, sh->irq));
-    DBG(DBG_QUEUE && DBG_DELAY, DEL2(200));
+    DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
     restore_flags(flags);
     return(0);
 }
@@ -521,26 +596,26 @@
     DBG(DBG_ABNORM, printk("eata_abort called pid: %ld target: %x lun: %x"
 			   " reason %x\n", cmd->pid, cmd->target, cmd->lun, 
 			   cmd->abort_reason));
-    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 
     while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY) {
 	if (--loop == 0) {
 	    printk("eata_dma: abort, timeout error.\n");
+	    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	    restore_flags(flags);
-	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
 	    return (SCSI_ABORT_ERROR);
 	}
     }
     if (CD(cmd)->status == RESET) {
-	restore_flags(flags);
 	printk("eata_dma: abort, command reset error.\n");
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+	restore_flags(flags);
 	return (SCSI_ABORT_ERROR);
     }
     if (CD(cmd)->status == LOCKED) {
-	restore_flags(flags);
 	DBG(DBG_ABNORM, printk("eata_dma: abort, queue slot locked.\n"));
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+	restore_flags(flags);
 	return (SCSI_ABORT_NOT_RUNNING);
     }
     if (CD(cmd)->status == USED) {
@@ -557,11 +632,11 @@
     panic("eata_dma: abort: invalid slot status\n");
 }
 
-int eata_reset(Scsi_Cmnd * cmd, int resetflags)
+int eata_reset(Scsi_Cmnd * cmd, unsigned int resetflags)
 {
     ushort x, z; 
-    ulong time, limit = 0;
-    ulong loop = R_LIMIT;
+    ulong limit = 0;
+    ulong loop = loops_per_sec / 3;
     ulong flags;
     unchar success = FALSE;
     Scsi_Cmnd *sp; 
@@ -576,7 +651,7 @@
     if (HD(cmd)->state == RESET) {
 	printk("eata_reset: exit, already in reset.\n");
 	restore_flags(flags);
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_RESET_ERROR);
     }
     
@@ -584,7 +659,7 @@
 	if (--loop == 0) {
 	    printk("eata_reset: exit, timeout error.\n");
 	    restore_flags(flags);
-	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	    return (SCSI_RESET_ERROR);
 	}
  
@@ -602,17 +677,17 @@
 	if (HD(cmd)->ccb[x].status == LOCKED) {
 	    HD(cmd)->ccb[x].status = FREE;
 	    printk("eata_reset: locked slot %d forced free.\n", x);
-	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	    continue;
 	}
 	sp = HD(cmd)->ccb[x].cmd;
 	HD(cmd)->ccb[x].status = RESET;
 	printk("eata_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	
 	if (sp == NULL)
 	    panic("eata_reset: slot %d, sp==NULL.\n", x);
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	
 	if (sp == cmd)
 	    success = TRUE;
@@ -624,19 +699,12 @@
     
     DBG(DBG_ABNORM, printk("eata_reset: board reset done, enabling interrupts.\n"));
     HD(cmd)->state = RESET;
-    
-    restore_flags(flags);
-    
-    time = jiffies;
-    while (jiffies < (time + (3 * HZ)) || limit++ < 10000000)
-	/* As time goes by... */;
-    
-    save_flags(flags);
-    cli();
+
+    DELAY(1); 
     
     DBG(DBG_ABNORM, printk("eata_reset: interrupts disabled, loops %ld.\n", 
 			   limit));
-    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
     
     for (x = 0; x < cmd->host->can_queue; x++) {
 	
@@ -652,10 +720,9 @@
 	
 	printk("eata_reset: slot %d locked, DID_RESET, pid %ld done.\n",
 	       x, sp->pid);
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
-	restore_flags(flags);
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
+
 	sp->scsi_done(sp);
-	cli();
     }
     
     HD(cmd)->state = FALSE;
@@ -663,11 +730,11 @@
     
     if (success) {
 	DBG(DBG_ABNORM, printk("eata_reset: exit, success.\n"));
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_RESET_SUCCESS);
     } else {
 	DBG(DBG_ABNORM, printk("eata_reset: exit, wakeup.\n"));
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_RESET_PUNT);
     }
 }
@@ -677,7 +744,8 @@
  *
  * At the moment the algorithm is rather simple
  */
-static void eata_select_queue_depths(struct Scsi_Host *host, Scsi_Device *devicelist)
+static void eata_select_queue_depths(struct Scsi_Host *host, 
+				     Scsi_Device *devicelist)
 {
     Scsi_Device *device;
     int devcount = 0; 
@@ -745,13 +813,34 @@
 	    } else /* ISA forces us to limit the QS because of bounce buffers*/
 	        device->queue_depth = 2; /* I know this is cruel */
 
-	    printk(KERN_INFO "scsi%d: queue depth for target %d on channel %d set "
-		   "to %d\n", host->host_no, device->id, device->channel,
+	    printk(KERN_INFO "scsi%d: queue depth for target %d on channel %d "
+		   "set to %d\n", host->host_no, device->id, device->channel,
 		   device->queue_depth);
 	}
     }
 }
 
+int check_blink_state(long base)
+{
+    ushort loops = 10;
+    u32 blinkindicator;
+    u32 state = 0x12345678;
+    u32 oldstate = 0;
+
+    blinkindicator = htonl(0x54504442);
+    while ((loops--) && (state != oldstate)) {
+	oldstate = state;
+	state = inl((uint) base + 1);
+    }
+
+    DBG(DBG_BLINK, printk("Did Blink check. Status: %d\n",
+	      (state == oldstate) && (state == blinkindicator)));
+
+    if ((state == oldstate) && (state == blinkindicator))
+	return(TRUE);
+    else
+	return (FALSE);
+}
 
 char * get_board_data(u32 base, u32 irq, u32 id)
 {
@@ -802,8 +891,8 @@
     while (fake_int_happened == FALSE && jiffies <= i) 
 	barrier();
     
-    DBG(DBG_INTR3, printk("fake_int_result: %#x hbastat %#x scsistat %#x,"
-			  " buff %p sp %p\n",
+    DBG(DBG_INTR3, printk(KERN_DEBUG "fake_int_result: %#x hbastat %#x "
+			  "scsistat %#x, buff %p sp %p\n",
 			  fake_int_result, (u32) (sp->hba_stat /*& 0x7f*/), 
 			  (u32) sp->scsi_stat, buff, sp));
 
@@ -811,38 +900,17 @@
     scsi_init_free((void *)sp, sizeof(struct eata_sp));
     
     if ((fake_int_result & HA_SERROR) || jiffies > i){
+	printk(KERN_WARNING "eata_dma: trying to reset HBA at %x to clear "
+	       "possible blink state\n", base); 
 	/* hard reset the HBA  */
 	inb((u32) (base) + HA_RSTATUS);
 	eata_send_command(0, base, EATA_CMD_RESET);
-	i = jiffies;
-	while (jiffies < (i + (3 * HZ)) && limit++ < 10000000) 
-	    barrier();
+	DELAY(1);
 	return (NULL);
     } else
 	return (buff);
 }
-    
-int check_blink_state(long base)
-{
-    ushort loops = 10;
-    u32 blinkindicator;
-    u32 state = 0x12345678;
-    u32 oldstate = 0;
-
-    blinkindicator = htonl(0x54504442);
-    while ((loops--) && (state != oldstate)) {
-	oldstate = state;
-	state = inl((uint) base + 1);
-    }
-
-    DBG(DBG_BLINK, printk("Did Blink check. Status: %d\n",
-	      (state == oldstate) && (state == blinkindicator)));
 
-    if ((state == oldstate) && (state == blinkindicator))
-	return(TRUE);
-    else
-	return (FALSE);
-}
 
 int get_conf_PIO(u32 base, struct get_conf *buf)
 {
@@ -857,6 +925,10 @@
     while (inb(base + HA_RSTATUS) & HA_SBUSY)
 	if (--loop == 0) 
 	    return (FALSE);
+
+    fake_int_base = (struct eata_register *) base;
+    fake_int_result = FALSE;
+    fake_int_happened = FALSE;
        
     DBG(DBG_PIO && DBG_PROBE,
 	printk("Issuing PIO READ CONFIG to HBA at %#x\n", base));
@@ -890,6 +962,7 @@
     return (FALSE);
 }
 
+
 void print_config(struct get_conf *gc)
 {
     printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d DMAS:%d\n",
@@ -929,7 +1002,7 @@
     }
     if(gc->HAA_valid == FALSE || ntohl(gc->len) < 0x22) 
 	gc->MAX_CHAN = 0;
-    
+
     if (reg_IRQ[gc->IRQ] == FALSE) {	/* Interrupt already registered ? */
 	if (!request_irq(gc->IRQ, (void *) eata_fake_int_handler, SA_INTERRUPT,
 			 "eata_dma", NULL)){
@@ -948,13 +1021,27 @@
 	} else
 	    reg_IRQ[gc->IRQ]++;
     }
-    
+
+ 
+    /* If DMA is supported but DMA_valid isn't set to indicate that
+     * the channel number is given we must have pre 2.0 firmware (1.7?)
+     * which leaves us to guess since the "newer ones" also don't set the 
+     * DMA_valid bit.
+     */
+    if (gc->DMA_support && !gc->DMA_valid && gc->DMA_channel) {
+      printk(KERN_WARNING "eata_dma: If you are using a pre 2.0 firmware "
+	     "please update it !\n"
+	     "          You can get new firmware releases from ftp.dpt.com\n");
+	gc->DMA_channel = (base == 0x1f0 ? 3 /* DMA=5 */ : 2 /* DMA=6 */);
+	gc->DMA_valid = TRUE;
+    }
+
     /* if gc->DMA_valid it must be an ISA HBA and we have to register it */
     dma_channel = BUSMASTER;
     if (gc->DMA_valid) {
 	if (request_dma(dma_channel = (8 - gc->DMA_channel) & 7, "eata_dma")) {
-	    printk("Unable to allocate DMA channel %d for ISA HBA at %#.4x.\n",
-		   dma_channel, base);
+	    printk(KERN_WARNING "Unable to allocate DMA channel %d for ISA HBA"
+		   " at %#.4x.\n", dma_channel, base);
 	    reg_IRQ[gc->IRQ]--;
 	    if (reg_IRQ[gc->IRQ] == 0)
 		free_irq(gc->IRQ, NULL);
@@ -963,23 +1050,27 @@
 	    return (FALSE);
 	}
     }
- 
-#if !(NEWSTUFF)
+
+    if (dma_channel != BUSMASTER) {
+	disable_dma(dma_channel);
+	clear_dma_ff(dma_channel);
+	set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+	enable_dma(dma_channel);
+    }
+
     if (bustype != IS_EISA && bustype != IS_ISA)
-#endif
 	buff = get_board_data(base, gc->IRQ, gc->scsi_id[3]);
 
     if (buff == NULL) {
-#if !(NEWSTUFF)
 	if (bustype == IS_EISA || bustype == IS_ISA) {
 	    bugs = bugs || BROKEN_INQUIRY;
 	} else {
-#endif
 	    if (gc->DMA_support == FALSE)
-		printk("HBA at %#.4x doesn't support DMA. Sorry\n", base);
+		printk(KERN_WARNING "HBA at %#.4x doesn't support DMA. "
+		       "Sorry\n", base);
 	    else
-		printk("HBA at %#.4x does not react on INQUIRY. Sorry.\n", 
-		       base);
+		printk(KERN_WARNING "HBA at %#.4x does not react on INQUIRY. "
+		       "Sorry.\n", base);
 	    if (gc->DMA_valid) 
 		free_dma(dma_channel);
 	    reg_IRQ[gc->IRQ]--;
@@ -988,14 +1079,12 @@
 	    if (gc->IRQ_TR == FALSE)
 		reg_IRQL[gc->IRQ] = FALSE; 
 	    return (FALSE);
-#if !(NEWSTUFF)
 	}
-#endif
     }
-    
+ 
     if (gc->DMA_support == FALSE && buff != NULL)  
-	printk("HBA %.12sat %#.4x doesn't set the DMA_support flag correctly.\n",
-	       &buff[16], base);
+	printk(KERN_WARNING "HBA %.12sat %#.4x doesn't set the DMA_support "
+	       "flag correctly.\n", &buff[16], base);
     
     request_region(base, 9, "eata_dma"); /* We already checked the 
 					  * availability, so this
@@ -1004,8 +1093,10 @@
     
     if(ntohs(gc->queuesiz) == 0) {
 	gc->queuesiz = ntohs(64);
-	printk("Warning: Queue size has to be corrected. Assuming 64 queueslots\n"
-	       "         This might be a PM2012B with a defective Firmware\n");
+	printk(KERN_WARNING "Warning: Queue size has to be corrected. Assuming"
+	       " 64 queueslots\n"
+	       "         This might be a PM2012B with a defective Firmware\n"
+	       "         Contact DPT support@dpt.com for an upgrade\n");
     }
 
     size = sizeof(hostdata) + ((sizeof(struct eata_ccb) + sizeof(long)) 
@@ -1015,7 +1106,45 @@
 
     sh = scsi_register(tpnt, size);
     
-    if(sh == NULL) {
+    if(sh != NULL) {
+
+        hd = SD(sh);		   
+
+	memset(hd->reads, 0, sizeof(u32) * 26); 
+	
+	sh->select_queue_depths = eata_select_queue_depths;
+	
+	hd->bustype = bustype;
+
+	/*
+	 * If we are using a ISA board, we can't use extended SG,
+	 * because we would need exessive amounts of memory for
+	 * bounce buffers.
+	 */
+	if (gc->SG_64K==TRUE && ntohs(gc->SGsiz)==64 && hd->bustype!=IS_ISA){
+	    sh->sg_tablesize = SG_SIZE_BIG;
+	} else {
+	    sh->sg_tablesize = ntohs(gc->SGsiz);
+	    if (sh->sg_tablesize > SG_SIZE || sh->sg_tablesize == 0) {
+	        if (sh->sg_tablesize == 0)
+		    printk(KERN_WARNING "Warning: SG size had to be fixed.\n"
+			   "This might be a PM2012 with a defective Firmware"
+			   "\nContact DPT support@dpt.com for an upgrade\n");
+		sh->sg_tablesize = SG_SIZE;
+	    }
+	}
+	hd->sgsize = sh->sg_tablesize;
+    }
+
+    if(sh != NULL) {
+        sh->can_queue = hd->queuesize = ntohs(gc->queuesiz);
+       	sh->cmd_per_lun = 0;
+    }
+
+    if(sh == NULL) { 
+        DBG(DBG_REGISTER, printk(KERN_NOTICE "eata_dma: couldn't register HBA"
+				 " at%x \n", base));
+	scsi_unregister(sh);
 	if (gc->DMA_valid) 
 	    free_dma(dma_channel);
 	
@@ -1026,64 +1155,52 @@
 	    reg_IRQL[gc->IRQ] = FALSE; 
 	return (FALSE);
     }
-    
-    hd = SD(sh);		   
-    
-    memset(hd->ccb, 0, sizeof(struct eata_ccb) * ntohs(gc->queuesiz));
-    memset(hd->reads, 0, sizeof(u32) * 26); 
 
+    
     hd->broken_INQUIRY = (bugs & BROKEN_INQUIRY);
 
     if(hd->broken_INQUIRY == TRUE) {
-	strcpy(SD(sh)->vendor, "DPT");
-	strcpy(SD(sh)->name, "??????????");
-	strcpy(SD(sh)->revision, "???.?");
+	strcpy(hd->vendor, "DPT");
+	strcpy(hd->name, "??????????");
+	strcpy(hd->revision, "???.?");
+        hd->firmware_revision = 0;
     } else {	
-	strncpy(SD(sh)->vendor, &buff[8], 8);
-	SD(sh)->vendor[8] = 0;
-	strncpy(SD(sh)->name, &buff[16], 17);
-	SD(sh)->name[17] = 0;
-	SD(sh)->revision[0] = buff[32];
-	SD(sh)->revision[1] = buff[33];
-	SD(sh)->revision[2] = buff[34];
-	SD(sh)->revision[3] = '.';
-	SD(sh)->revision[4] = buff[35];
-	SD(sh)->revision[5] = 0;
-    }
+	strncpy(hd->vendor, &buff[8], 8);
+	hd->vendor[8] = 0;
+	strncpy(hd->name, &buff[16], 17);
+	hd->name[17] = 0;
+	hd->revision[0] = buff[32];
+	hd->revision[1] = buff[33];
+	hd->revision[2] = buff[34];
+	hd->revision[3] = '.';
+	hd->revision[4] = buff[35];
+	hd->revision[5] = 0;
+        hd->firmware_revision = (buff[32] << 24) + (buff[33] << 16) 
+	                            + (buff[34] << 8) + buff[35]; 
+    }
+
+    if (hd->firmware_revision >= (('0'<<24) + ('7'<<16) + ('G'<< 8) + '0'))
+        hd->immediate_support = 1;
+    else 
+        hd->immediate_support = 0;
 
     switch (ntohl(gc->len)) {
     case 0x1c:
-	SD(sh)->EATA_revision = 'a';
+	hd->EATA_revision = 'a';
 	break;
     case 0x1e:
-	SD(sh)->EATA_revision = 'b';
+	hd->EATA_revision = 'b';
 	break;
     case 0x22:
-	SD(sh)->EATA_revision = 'c';
+	hd->EATA_revision = 'c';
 	break;
     case 0x24:
-	SD(sh)->EATA_revision = 'z';		
+	hd->EATA_revision = 'z';		
     default:
-	SD(sh)->EATA_revision = '?';
+	hd->EATA_revision = '?';
     }
 
-    if(ntohl(gc->len) >= 0x22) {
-	if (gc->is_PCI == TRUE)
-	    hd->bustype = IS_PCI;
-	else if (gc->is_EISA == TRUE)
-	    hd->bustype = IS_EISA;
-	else
-	    hd->bustype = IS_ISA;
-    } else if(hd->broken_INQUIRY == FALSE) {
-	if (buff[21] == '4')
-	    hd->bustype = IS_PCI;
-	else if (buff[21] == '2')
-	    hd->bustype = IS_EISA;
-	else
-	    hd->bustype = IS_ISA;
-    } else 
-	hd->bustype = bustype;
-    
+
     if(ntohl(gc->len) >= 0x22) {
 	sh->max_id = gc->MAX_ID + 1;
 	sh->max_lun = gc->MAX_LUN + 1;
@@ -1092,6 +1209,7 @@
 	sh->max_lun = 8;
     }
 
+    hd->HBA_number = sh->host_no;
     hd->channel = gc->MAX_CHAN;	    
     sh->max_channel = gc->MAX_CHAN; 
     sh->unique_id = base;
@@ -1105,34 +1223,7 @@
      * SCSI midlevel code should support different HBA ids on every channel
      */
     sh->this_id = gc->scsi_id[3];
-
-    hd->queuesize = ntohs(gc->queuesiz);
-    sh->can_queue = ntohs(gc->queuesiz);
-    sh->cmd_per_lun = 0;
-    sh->select_queue_depths = eata_select_queue_depths;
     
-    /* FIXME:
-     * SG should be allocated more dynamically 
-     */
-    /*
-     * If we are using a ISA board, we can't use extended SG,
-     * because we would need excessive amounts of memory for
-     * bounce buffers.
-     */
-    if (gc->SG_64K == TRUE && ntohs(gc->SGsiz) == 64 && hd->bustype != IS_ISA){
-	sh->sg_tablesize = SG_SIZE_BIG;
-	sh->use_clustering = TRUE;
-    } else {
-	sh->sg_tablesize = ntohs(gc->SGsiz);
-	sh->use_clustering = TRUE;
-	if (sh->sg_tablesize > SG_SIZE || sh->sg_tablesize == 0) {
-	    sh->sg_tablesize = SG_SIZE;
-	    if (ntohs(gc->SGsiz) == 0)
-		printk("Warning: SG size had to be corrected.\n"
-		       "This might be a PM2012 with a defective Firmware\n");
-	}
-    }
-
     if (gc->SECOND)
 	hd->primary = FALSE;
     else
@@ -1150,6 +1241,7 @@
 	hd->writes_lat[x][1] = 0xffffffff;
 	hd->reads_lat[x][1] = 0xffffffff;
     }
+    hd->all_lat[1] = 0xffffffff;
 
     hd->next = NULL;	/* build a linked list of all HBAs */
     hd->prev = last_HBA;
@@ -1164,6 +1256,7 @@
 }
 
 
+
 void find_EISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
 {
     u32 base;
@@ -1320,7 +1413,7 @@
 			    continue;  /* break; */
 			} else if (check_blink_state(base) == TRUE) {
 			    printk("eata_dma: HBA is in BLINK state.\n"
-				   "Consult your HBAs Manual to correct this.\n");
+				   "Consult your HBAs manual to correct this.\n");
 			}
 		    }
 		}
@@ -1350,13 +1443,15 @@
     tpnt->proc_dir = &proc_scsi_eata_dma;
 
     status = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
-    dma_scratch = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
+    dma_scratch = scsi_init_malloc(1024, GFP_ATOMIC | GFP_DMA);
 
     if(status == NULL || dma_scratch == NULL) {
 	printk("eata_dma: can't allocate enough memory to probe for hosts !\n");
 	return(0);
     }
 
+    dma_scratch += 4;
+
     find_PCI(&gc, tpnt);
     
     find_EISA(&gc, tpnt);
@@ -1364,41 +1459,45 @@
     find_ISA(&gc, tpnt);
     
     for (i = 0; i <= MAXIRQ; i++) { /* Now that we know what we have, we     */
-	if (reg_IRQ[i]){            /* exchange the interrupt handler which  */
+	if (reg_IRQ[i] >= 1){       /* exchange the interrupt handler which  */
 	    free_irq(i, NULL);      /* we used for probing with the real one */
-	    request_irq(i, (void *)(eata_int_handler), SA_INTERRUPT, "eata_dma", NULL);
+	    request_irq(i, (void *)(eata_int_handler), SA_INTERRUPT, 
+			"eata_dma", NULL);
 	}
     }
+
     HBA_ptr = first_HBA;
-    
+
     if (registered_HBAs != 0) {
-	printk("EATA (Extended Attachment) driver version: %d.%d%s\n"
-	       "developed in co-operation with DPT\n"		  
-	       "(c) 1993-95 Michael Neuffer, neuffer@goofy.zdv.uni-mainz.de\n",
-	       VER_MAJOR, VER_MINOR, VER_SUB);
-	printk("Registered HBAs:");
-	printk("\nHBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: DMA: Ch: "
-	       "ID: Pr: QS: SG: CPL:\n");
-	for (i = 1; i <= registered_HBAs; i++) {
-	    printk("scsi%-2d: %.10s v%s 2.0%c  %s %#.4x   %2d",
+        printk("EATA (Extended Attachment) driver version: %d.%d%s"
+               "\ndeveloped in co-operation with DPT\n"
+               "(c) 1993-96 Michael Neuffer, mike@i-Connect.Net\n",
+               VER_MAJOR, VER_MINOR, VER_SUB);
+        printk("Registered HBAs:");
+        printk("\nHBA no. Boardtype    Revis  EATA Bus  BaseIO IRQ"
+               " DMA Ch ID Pr QS  S/G IS\n");
+        for (i = 1; i <= registered_HBAs; i++) {
+    	    printk("scsi%-2d: %.12s v%s 2.0%c %s %#.4x  %2d",
 		   HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
 		   SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')? 
 		   "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
 		   (u32) HBA_ptr->base, HBA_ptr->irq);
 	    if(HBA_ptr->dma_channel != BUSMASTER)
-		printk("   %2x ", HBA_ptr->dma_channel);
+		printk("  %2x ", HBA_ptr->dma_channel);
 	    else
-		printk("  %s", "BMST");
-	    printk("  %d   %d   %c  %2d  %2d   %2d\n", SD(HBA_ptr)->channel, 
-		   HBA_ptr->this_id, (SD(HBA_ptr)->primary == TRUE)?'Y':'N', 
-		   HBA_ptr->can_queue, HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
+		printk(" %s", "BMST");
+	    printk(" %d  %d  %c %3d %3d %c\n", 
+		   SD(HBA_ptr)->channel+1, HBA_ptr->this_id, 
+		   (SD(HBA_ptr)->primary == TRUE)?'Y':'N', 
+		   HBA_ptr->can_queue, HBA_ptr->sg_tablesize, 
+		   (SD(HBA_ptr)->immediate_support == TRUE)?'Y':'N'); 
 	    HBA_ptr = SD(HBA_ptr)->next;
 	}
     } else {
 	scsi_init_free((void *)status, 512);
     }
 
-    scsi_init_free((void *)dma_scratch, 512);
+    scsi_init_free((void *)dma_scratch - 4, 1024);
 
     DBG(DPT_DEBUG, DELAY(12));
 
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/eata_dma.h linux/drivers/scsi/eata_dma.h
--- v1.3.98/linux/drivers/scsi/eata_dma.h	Sun May  5 08:52:00 1996
+++ linux/drivers/scsi/eata_dma.h	Mon May  6 07:34:20 1996
@@ -1,11 +1,12 @@
 /********************************************************
 * Header file for eata_dma.c Linux EATA-DMA SCSI driver *
-* (c) 1993,94,95 Michael Neuffer                        *
+* (c) 1993-96 Michael Neuffer                           *
+*             mike@i-Connect.Net                        *
+*             neuffer@mail.uni-mainz.de                 *
 *********************************************************
-* last change: 95/07/18                                 *
+* last change: 96/05/05                                 *
 ********************************************************/
 
-
 #ifndef _EATA_DMA_H
 #define _EATA_DMA_H
 
@@ -16,7 +17,7 @@
 
 #define VER_MAJOR 2
 #define VER_MINOR 5
-#define VER_SUB   "8a"
+#define VER_SUB   "8d"
 
 
 /************************************************************************
@@ -47,6 +48,9 @@
 #define DBG_INTR        0       /* Trace interrupt service routine.     */
 #define DBG_INTR2       0       /* Trace interrupt service routine.     */
 #define DBG_INTR3       0       /* Trace get_board_data interrupts.     */
+#define DBG_REQSENSE    0       /* Trace request sense commands         */     
+#define DBG_RESET       0       /* Trace reset calls                    */     
+#define DBG_STATUS      0       /* Trace status generation              */
 #define DBG_PROC        0       /* Debug proc-fs related statistics     */
 #define DBG_PROC_WRITE  0
 #define DBG_REGISTER    0       /* */
@@ -65,7 +69,7 @@
 int eata_command(Scsi_Cmnd *);
 int eata_queue(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
 int eata_abort(Scsi_Cmnd *);
-int eata_reset(Scsi_Cmnd *);
+int eata_reset(Scsi_Cmnd *, unsigned int);
 int eata_proc_info(char *, char **, off_t, int, int, int);
 #ifdef MODULE
 int eata_release(struct Scsi_Host *);
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/eata_generic.h linux/drivers/scsi/eata_generic.h
--- v1.3.98/linux/drivers/scsi/eata_generic.h	Sun May  5 08:52:00 1996
+++ linux/drivers/scsi/eata_generic.h	Mon May  6 07:34:20 1996
@@ -1,9 +1,11 @@
 /********************************************************
 * Header file for eata_dma.c and eata_pio.c		*
 * Linux EATA SCSI drivers				*
-* (c) 1993,94,95 Michael Neuffer			*
+* (c) 1993-96 Michael Neuffer                           *
+*             mike@i-Connect.Net                        *
+*             neuffer@mail.uni-mainz.de                 *
 *********************************************************
-* last change: 95/11/07					*
+* last change: 95/05/05                                 *
 ********************************************************/
 
 
@@ -56,35 +58,43 @@
 #define NEC_ID2         0xa3
 #define NEC_ID3         0x82
 
+ 
+#define EATA_CP_SIZE	 44
 
-#define EATA_CP_SIZE	44
-
-#define MAX_PCI_DEVICES 32	       /* Maximum # Of Devices Per Bus	 */
-#define MAX_METHOD_2	16	       /* Max Devices For Method 2	 */
-#define MAX_PCI_BUS	16	       /* Maximum # Of Busses Allowed	 */
+#define MAX_PCI_DEVICES  32	       /* Maximum # Of Devices Per Bus	 */
+#define MAX_METHOD_2	 16	       /* Max Devices For Method 2	 */
+#define MAX_PCI_BUS	 16	       /* Maximum # Of Busses Allowed	 */
 
-#define SG_SIZE		64 
-#define SG_SIZE_BIG	252	       /* max. 8096 elements, 64k */
+#define SG_SIZE		 64 
+#define SG_SIZE_BIG	 252	       /* max. 8096 elements, 64k */
 
 #define TYPE_DISK_QUEUE  16
 #define TYPE_TAPE_QUEUE  4
 #define TYPE_ROM_QUEUE   4
 #define TYPE_OTHER_QUEUE 2
 
-#define FREE	   0
-#define OK	   0
-#define NO_TIMEOUT 0
-#define USED	   1
-#define TIMEOUT	   2
-#define RESET	   4
-#define LOCKED	   8
+#define FREE	         0
+#define OK	         0
+#define NO_TIMEOUT       0
+#define USED	         1
+#define TIMEOUT	         2
+#define RESET	         4
+#define LOCKED	         8
+#define ABORTED          16
+
+#define READ             0
+#define WRITE            1
+#define OTHER            2
 
 #define HD(cmd)	 ((hostdata *)&(cmd->host->hostdata))
 #define CD(cmd)	 ((struct eata_ccb *)(cmd->host_scribble))
 #define SD(host) ((hostdata *)&(host->hostdata))
 
-#define DELAY(x) { __u32 i; i = jiffies + (x * HZ); while (jiffies < i) barrier(); }
-#define DEL2(x)	 { __u32 i; for (i = 0; i < 0xffff * x; i++); }
+#define DELAY(x) { __u32 i; ulong flags;          \
+                   save_flags(flags); sti();      \
+                   i = jiffies + (x * HZ);        \
+                   while (jiffies < i) barrier(); \
+                   restore_flags(flags); }
 
 /***********************************************
  *    EATA Command & Register definitions      *
@@ -122,13 +132,14 @@
 
 
 #define HA_WCOMMAND    0x07	   /* command register offset	*/
-#define HA_WCOMMAND2   0x06	   /* immediate command offset	*/
-#define HA_WSUBCODE    0x05 
-#define HA_WSUBLUN     0x04 
+#define HA_WIFC        0x06	   /* immediate command offset  */
+#define HA_WCODE       0x05 
+#define HA_WCODE2      0x04 
 #define HA_WDMAADDR    0x02	   /* DMA address LSB offset	*/  
 #define HA_RAUXSTAT    0x08	   /* aux status register offset*/
 #define HA_RSTATUS     0x07	   /* status register offset	*/
 #define HA_RDATA       0x00	   /* data register (16bit)	*/
+#define HA_WDATA       0x00	   /* data register (16bit)	*/
 
 #define HA_ABUSY       0x01	   /* aux busy bit		*/
 #define HA_AIRQ	       0x02	   /* aux IRQ pending bit	*/
@@ -149,7 +160,7 @@
 #define HA_NO_ERROR	 0x00	/* No Error				*/
 #define HA_ERR_SEL_TO	 0x01	/* Selection Timeout			*/
 #define HA_ERR_CMD_TO	 0x02	/* Command Timeout			*/
-#define HA_ERR_RESET	 0x03	/* SCSI Bus Reset Received		*/
+#define HA_BUS_RESET	 0x03	/* SCSI Bus Reset Received		*/
 #define HA_INIT_POWERUP	 0x04	/* Initial Controller Power-up		*/
 #define HA_UNX_BUSPHASE	 0x05	/* Unexpected Bus Phase			*/
 #define HA_UNX_BUS_FREE	 0x06	/* Unexpected Bus Free			*/
@@ -247,7 +258,8 @@
 	 ID_qest:1,	      /* Raidnum ID is questionable		*/
 	  is_PCI:1,	      /* HBA is PCI				*/
 	 is_EISA:1;	      /* HBA is EISA				*/
-    __u8 unused[478]; 
+    __u8 RAIDNUM;             /* unique HBA identifier                  */
+    __u8 unused[474]; 
 };
 
 struct eata_sg_list
@@ -322,27 +334,34 @@
     __u8   name[18];
     __u8   revision[6];
     __u8   EATA_revision;
+    __u32  firmware_revision;
+    __u8   HBA_number;
     __u8   bustype;		 /* bustype of HBA	       */
     __u8   channel;		 /* # of avail. scsi channels  */
     __u8   state;		 /* state of HBA	       */
     __u8   primary;		 /* true if primary	       */
-    __u8   broken_INQUIRY:1;	 /* This is an EISA HBA with   *
+    __u8        more_support:1,  /* HBA supports MORE flag     */
+           immediate_support:1,  /* HBA supports IMMEDIATE CMDs*/
+              broken_INQUIRY:1;	 /* This is an EISA HBA with   *
 				  * broken INQUIRY	       */
     __u8   do_latency;		 /* Latency measurement flag   */
     __u32  reads[13];
     __u32  writes[13];
     __u32  reads_lat[12][4];
     __u32  writes_lat[12][4];
+    __u32  all_lat[4];
 				 /* state of Target (RESET,..) */
     __u8   t_state[MAXCHANNEL][MAXTARGET];   
 				 /* timeouts on target	       */
     __u32  t_timeout[MAXCHANNEL][MAXTARGET]; 
+    __u8   resetlevel[MAXCHANNEL]; 
     __u32  last_ccb;		 /* Last used ccb	       */
     __u32  cplen;		 /* size of CP in words	       */
     __u16  cppadlen;		 /* pad length of cp in words  */
-    int    queuesize;
+    __u16  queuesize;
+    __u16  sgsize;               /* # of entries in the SG list*/
+    __u16  devflags;		 /* bits set for detected devices */
     __u8   hostid;		 /* SCSI ID of HBA	       */
-    __u8   devflags;		 /* bits set for detected devices */
     __u8   moresupport;		 /* HBA supports MORE flag     */
     struct Scsi_Host *next;	    
     struct Scsi_Host *prev;
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/eata_pio.c linux/drivers/scsi/eata_pio.c
--- v1.3.98/linux/drivers/scsi/eata_pio.c	Sun May  5 08:52:00 1996
+++ linux/drivers/scsi/eata_pio.c	Mon May  6 07:34:20 1996
@@ -242,7 +242,7 @@
 	    eata_stat = inb(base + HA_RSTATUS);
 	    printk(KERN_NOTICE "eata_pio: int_handler, freeing locked "
                    "queueslot\n");
-	    DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+	    DBG(DBG_INTR&&DBG_DELAY,DELAY(1));
 	    restore_flags(flags);
 	    return;
 	}
@@ -251,7 +251,7 @@
 	if (stat != 0x50) 
 	    printk(KERN_DEBUG "stat: %#.2x, result: %#.8x\n", stat, 
                    cmd->result); 
-	DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+	DBG(DBG_INTR&&DBG_DELAY,DELAY(1));
 #endif
 	
 	cp->status = FREE;   /* now we can release the slot  */
@@ -322,7 +322,7 @@
 
     DBG(DBG_QUEUE, printk(KERN_DEBUG "eata_pio_queue pid %ld, target: %x, lun:"
                           " %x, y %d\n", cmd->pid, cmd->target, cmd->lun, y));
-    DBG(DBG_QUEUE && DBG_DELAY, DEL2(250));
+    DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
     
     cmd->scsi_done = (void *)done;
     
@@ -396,7 +396,7 @@
     DBG(DBG_QUEUE,printk(KERN_DEBUG "Queued base %#.4lx pid: %ld target: %x "
                          "lun: %x slot %d irq %d\n", (long)sh->base, cmd->pid, 
 			 cmd->target, cmd->lun, y, sh->irq));
-    DBG(DBG_QUEUE && DBG_DELAY, DEL2(200));
+    DBG(DBG_QUEUE && DBG_DELAY, DELAY(1));
     
     restore_flags(flags);
     return (0);
@@ -413,14 +413,14 @@
     DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_abort called pid: %ld "
                            "target: %x lun: %x reason %x\n", cmd->pid, 
                            cmd->target, cmd->lun, cmd->abort_reason));
-    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
     
     
     while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
 	if (--loop == 0) {
 	    printk(KERN_WARNING "eata_pio: abort, timeout error.\n");
 	    restore_flags(flags);
-	    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	    return (SCSI_ABORT_ERROR);
 	}
     if (CD(cmd)->status == FREE) {
@@ -436,21 +436,21 @@
     if (CD(cmd)->status == RESET) {
 	restore_flags(flags);
 	printk(KERN_WARNING "eata_pio: abort, command reset error.\n");
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_ABORT_ERROR);
     }
     if (CD(cmd)->status == LOCKED) {
 	restore_flags(flags);
 	DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio: abort, queue slot "
                                "locked.\n"));
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_ABORT_NOT_RUNNING);
     }
     restore_flags(flags);
     panic("eata_pio: abort: invalid slot status\n");
 }
 
-int eata_pio_reset(Scsi_Cmnd * cmd, int dummy)
+int eata_pio_reset(Scsi_Cmnd * cmd, unsigned int dummy)
 {
     uint x, z, time, limit = 0;
     ulong flags;
@@ -467,7 +467,7 @@
     if (HD(cmd)->state == RESET) {
 	printk(KERN_WARNING "eata_pio_reset: exit, already in reset.\n");
 	restore_flags(flags);
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_RESET_ERROR);
     }
     
@@ -487,11 +487,11 @@
 	HD(cmd)->ccb[x].status = RESET;
 	printk(KERN_WARNING "eata_pio_reset: slot %d in reset, pid %ld.\n", x,
                sp->pid);
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	
 	if (sp == NULL)
 	    panic("eata_pio_reset: slot %d, sp==NULL.\n", x);
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
     }
     
     /* hard reset the HBA  */
@@ -505,7 +505,7 @@
     
     DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: interrupts disabled, "
                            "loops %d.\n", limit));
-    DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+    DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
     
     for (x = 0; x < cmd->host->can_queue; x++) {
 	
@@ -517,7 +517,7 @@
 	sp->result = DID_RESET << 16;
 	
 	/* This mailbox is terminated */
-	printk(KERN_WARNING "eata_pio_reset: resetted ccb %d.\n",x);
+	printk(KERN_WARNING "eata_pio_reset: reset ccb %d.\n",x);
 	HD(cmd)->ccb[x].status = FREE;
 	
 	restore_flags(flags);
@@ -530,11 +530,11 @@
     
     if (success) { /* hmmm... */
 	DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, success.\n"));
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_RESET_SUCCESS);
     } else {
 	DBG(DBG_ABNORM, printk(KERN_WARNING "eata_pio_reset: exit, wakeup.\n"));
-	DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+	DBG(DBG_ABNORM && DBG_DELAY, DELAY(1));
 	return (SCSI_RESET_PUNT);
     }
 }
@@ -718,7 +718,7 @@
 	    if (!gc->IRQ_TR)
 		reg_IRQL[gc->IRQ] = TRUE;   /* IRQ is edge triggered */
 	} else {
-	    printk("Couldn't allocate IRQ %d, Sorry.", gc->IRQ);
+	    printk("Couldn't allocate IRQ %d, Sorry.\n", gc->IRQ);
 	    return (FALSE);
 	}
     } else {            /* More than one HBA on this IRQ */
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/eata_pio.h linux/drivers/scsi/eata_pio.h
--- v1.3.98/linux/drivers/scsi/eata_pio.h	Sun May  5 08:52:01 1996
+++ linux/drivers/scsi/eata_pio.h	Mon May  6 07:34:20 1996
@@ -65,7 +65,7 @@
 int eata_pio_command(Scsi_Cmnd *);
 int eata_pio_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int eata_pio_abort(Scsi_Cmnd *);
-int eata_pio_reset(Scsi_Cmnd *);
+int eata_pio_reset(Scsi_Cmnd *, unsigned int);
 int eata_pio_proc_info(char *, char **, off_t, int, int, int);
 #ifdef MODULE
 int eata_pio_release(struct Scsi_Host *);
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- v1.3.98/linux/drivers/scsi/fdomain.c	Sun May  5 08:52:01 1996
+++ linux/drivers/scsi/fdomain.c	Mon May  6 12:26:11 1996
@@ -594,7 +594,7 @@
 #else
 
 				/* That should have worked, but appears to
-				   have problems.  Lets assume it is an
+				   have problems.  Let's assume it is an
 				   18c30 if the RAM is disabled. */
 
       if (inb( port + Configuration2 ) & 0x02) {
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.98/linux/drivers/scsi/hosts.c	Sat Apr 27 15:19:57 1996
+++ linux/drivers/scsi/hosts.c	Mon May  6 12:26:11 1996
@@ -332,7 +332,7 @@
 
 /* We call this when we come across a new host adapter. We only do this
  * once we are 100% sure that we want to use this host adapter -  it is a
- * pain to reverse this, so we try and avoid it 
+ * pain to reverse this, so we try to avoid it 
  */
 
 struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/pas16.c linux/drivers/scsi/pas16.c
--- v1.3.98/linux/drivers/scsi/pas16.c	Sat Apr 13 18:22:07 1996
+++ linux/drivers/scsi/pas16.c	Mon May  6 12:26:11 1996
@@ -311,7 +311,7 @@
 
     /* Mediavision has some new model boards that return ID bits
      * that indicate a SCSI interface, but they're not (LMS).  We'll
-     * put in an additional test to try and weed them out.
+     * put in an additional test to try to weed them out.
      */
 
     outb( 0x01, io_port + WAIT_STATE ); 	/* 1 Wait state */
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c
--- v1.3.98/linux/drivers/scsi/scsi_debug.c	Sun Jan 14 16:48:28 1996
+++ linux/drivers/scsi/scsi_debug.c	Mon May  6 12:26:11 1996
@@ -422,7 +422,7 @@
 	    do_done[i] = done;
     }
     else
-	printk("scsi_debug_queuecommand: done cant be NULL\n");
+	printk("scsi_debug_queuecommand: done can't be NULL\n");
     
 #ifdef IMMEDIATE
     if( !scsi_debug_lockup )
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v1.3.98/linux/drivers/scsi/sd.c	Sun May  5 08:52:02 1996
+++ linux/drivers/scsi/sd.c	Mon May  6 12:26:11 1996
@@ -128,7 +128,7 @@
 
 	if(!rscsi_disks[target].device->access_count)
 	    sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
-    };
+    }
 
     /*
      * See if we are requesting a non-existent partition.  Do this
@@ -250,8 +250,8 @@
 			memcpy(sgpnt[i].alt_address, sgpnt[i].address, 
 			       sgpnt[i].length);
 		    scsi_free(sgpnt[i].address, sgpnt[i].length);
-		};
-	    };
+		}
+	    }
 
 	    /* Free list of scatter-gather pointers */
 	    scsi_free(SCpnt->buffer, SCpnt->sglist_len);  
@@ -265,8 +265,8 @@
 		    memcpy(SCpnt->request.buffer, SCpnt->buffer,
 			   SCpnt->bufflen);
 		scsi_free(SCpnt->buffer, SCpnt->bufflen);
-	    };
-	};
+	    }
+	}
 	/*
 	 * If multiple sectors are requested in one buffer, then
 	 * they will have been finished off by the first command.
@@ -307,8 +307,8 @@
 #endif
 	    if (sgpnt[i].alt_address) {
 		scsi_free(sgpnt[i].address, sgpnt[i].length);
-	    };
-	};
+	    }
+	}
 	scsi_free(SCpnt->buffer, SCpnt->sglist_len);  /* Free list of scatter-gather pointers */
     } else {
 #ifdef DEBUG
@@ -317,7 +317,7 @@
 #endif
 	if (SCpnt->buffer != SCpnt->request.buffer)
 	    scsi_free(SCpnt->buffer, SCpnt->bufflen);
-    };
+    }
     
     /*
      * Now, if we were good little boys and girls, Santa left us a request
@@ -417,7 +417,7 @@
 	if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
 	    restore_flags(flags);
 	    return;
-	};
+	}
 	
 	INIT_SCSI_REQUEST;
         SDev = rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device;
@@ -468,11 +468,11 @@
 	restore_flags(flags);
 
 	/* This is a performance enhancement. We dig down into the request 
-	 * list and try and find a queueable request (i.e. device not busy, 
+	 * list and try to find a queueable request (i.e. device not busy, 
 	 * and host able to accept another command. If we find one, then we 
 	 * queue it. This can make a big difference on systems with more than 
 	 * one disk drive.  We want to have the interrupts off when monkeying 
-	 * with the request list, because otherwise the kernel might try and 
+	 * with the request list, because otherwise the kernel might try to 
 	 * slip in a request in between somewhere. 
 	 */
 
@@ -486,21 +486,21 @@
 		if(SCpnt) break;
 		req1 = req;
 		req = req->next;
-	    };
+	    }
 	    if (SCpnt && req->rq_status == RQ_INACTIVE) {
 		if (req == CURRENT) 
 		    CURRENT = CURRENT->next;
 		else
 		    req1->next = req->next;
-	    };
+	    }
 	    restore_flags(flags);
-	};
+	}
 	
 	if (!SCpnt) return; /* Could not find anything to do */
 	
 	/* Queue command */
 	requeue_sd_request(SCpnt);
-    };  /* While */
+    }  /* While */
 }    
 
 static void requeue_sd_request (Scsi_Cmnd * SCpnt)
@@ -610,7 +610,7 @@
 	if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
 	    bounce_buffer = (char *) scsi_malloc(bounce_size);
 	if(!bounce_buffer) contiguous = 0;
-    };
+    }
     
     if(contiguous && SCpnt->request.bh && SCpnt->request.bh->b_reqnext)
 	for(bh = SCpnt->request.bh, bhp = bh->b_reqnext; bhp; bh = bhp, 
@@ -620,7 +620,7 @@
 		contiguous = 0;
 		break;
 	    } 
-	};
+	}
     if (!SCpnt->request.bh || contiguous) {
 	
 	/* case of page request (i.e. raw device), or unlinked buffer */
@@ -667,11 +667,11 @@
 		((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
 		if (count < SCpnt->host->sg_tablesize) count++;
 		else break;
-	    };
+	    }
 	    this_count += (bh->b_size >> 9);
 	    bhp = bh;
 	    bh = bh->b_reqnext;
-	};
+	}
 #if 0
 	if(SCpnt->host->unchecked_isa_dma &&
 	   ((unsigned int) SCpnt->request.bh->b_data-1) == ISA_DMA_THRESHOLD) count--;
@@ -710,7 +710,7 @@
 		    ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) &&
 		    !sgpnt[count].alt_address) {
 		    sgpnt[count].alt_address = sgpnt[count].address;
-		    /* We try and avoid exhausting the DMA pool, since it is 
+		    /* We try to avoid exhausting the DMA pool, since it is 
 		     * easier to control usage here. In other places we might 
 		     * have a more pressing need, and we would be screwed if 
 		     * we ran out */
@@ -719,7 +719,7 @@
 		    } else {
 			sgpnt[count].address = 
 			    (char *) scsi_malloc(sgpnt[count].length);
-		    };
+		    }
 		    /* If we start running low on DMA buffers, we abort the 
 		     * scatter-gather operation, and free all of the memory 
 		     * we have allocated.  We want to ensure that all scsi 
@@ -733,7 +733,7 @@
 			    if(sgpnt[count].alt_address) 
 				scsi_free(sgpnt[count].address, 
 					  sgpnt[count].length);
-			};
+			}
 			this_count = SCpnt->request.current_nr_sectors;
 			buff = SCpnt->request.buffer;
 			SCpnt->use_sg = 0;
@@ -742,9 +742,8 @@
 			SCpnt->use_sg = count;
 			this_count = counted -= bh->b_size >> 9;
 			break;
-		    };
-		    
-		};
+		    }		    
+		}
 		
 		/* Only cluster buffers if we know that we can supply DMA 
 		 * buffers large enough to satisfy the request. Do not cluster
@@ -766,21 +765,21 @@
 		    else {
 			tmp = NULL;
 			max_sg = SCpnt->use_sg;
-		    };
+		    }
 		    if(tmp){
 			scsi_free(sgpnt[count].address, sgpnt[count].length);
 			sgpnt[count].address = tmp;
 			count--;
 			continue;
-		    };
+		    }
 		    
 		    /* If we are allowed another sg chain, then increment 
 		     * counter so we can insert it.  Otherwise we will end 
 		     up truncating */
 		    
 		    if (SCpnt->use_sg < max_sg) SCpnt->use_sg++;
-		};  /* contiguous buffers */
-	    }; /* for loop */
+		}  /* contiguous buffers */
+	    } /* for loop */
 	    
 	    /* This is actually how many we are going to transfer */
 	    this_count = counted; 
@@ -795,7 +794,7 @@
 		while(bh){
 		    printk("[%p %lx] ", bh->b_data, bh->b_size);
 		    bh = bh->b_reqnext;
-		};
+		}
 		if(SCpnt->use_sg < 16)
 		    for(count=0; count<SCpnt->use_sg; count++)
 			printk("{%d:%p %p %d}  ", count,
@@ -803,15 +802,15 @@
 			       sgpnt[count].alt_address,
 			       sgpnt[count].length);
 		panic("Ooops");
-	    };
+	    }
 	    
 	    if (SCpnt->request.cmd == WRITE)
 		for(count=0; count<SCpnt->use_sg; count++)
 		    if(sgpnt[count].alt_address)
 			memcpy(sgpnt[count].address, sgpnt[count].alt_address, 
 			       sgpnt[count].length);
-	};  /* Able to malloc sgpnt */
-    };  /* Host adapter capable of scatter-gather */
+	}  /* Able to malloc sgpnt */
+    }  /* Host adapter capable of scatter-gather */
     
     /* Now handle the possibility of DMA to addresses > 16Mb */
     
@@ -826,11 +825,11 @@
 		this_count = SCpnt->request.current_nr_sectors;
 		buff = (char *) scsi_malloc(this_count << 9);
 		if(!buff) panic("Ran out of DMA buffers.");
-	    };
+	    }
 	    if (SCpnt->request.cmd == WRITE)
 		memcpy(buff, (char *)SCpnt->request.buffer, this_count << 9);
-	};
-    };
+	}
+    }
 #ifdef DEBUG
     printk("sd%c : %s %d/%d 512 byte blocks.\n", 
 	   'a' + devm,
@@ -845,12 +844,12 @@
 	if(this_count & 1) panic("sd.c:Bad block number requested");
 	block = block >> 1;
 	this_count = this_count >> 1;
-    };
+    }
     
     if (rscsi_disks[dev].sector_size == 256){
 	block = block << 1;
 	this_count = this_count << 1;
-    };
+    }
     
     if (((this_count > 0xff) ||  (block > 0x1fffff)) && rscsi_disks[dev].ten)
     {
@@ -906,7 +905,7 @@
 	!rscsi_disks[target].device) {
 	printk("SCSI disk request error: invalid device.\n");
 	return 0;
-    };
+    }
     
     if(!rscsi_disks[target].device->removable) return 0;
     
@@ -922,7 +921,7 @@
 	rscsi_disks[target].device->changed = 1;
 	return 1; /* This will force a flush, if called from
 		   * check_disk_change */
-    };
+    }
     
     /* 
      * for removable scsi disk ( FLOPTICAL ) we have to recognise the
@@ -1033,7 +1032,7 @@
 		time1 = jiffies;
 		while(jiffies < time1 + HZ); /* Wait 1 second for next try */
 		printk( "." );
-	    };
+	    }
 	} while(the_result && spintime && spintime+100*HZ > jiffies);
 	if (spintime) {
 	    if (the_result)
@@ -1041,8 +1040,7 @@
 	    else
 		printk( "ready\n" );
 	}
-    };  /* !MODULE_FLAG */
-    
+    }  /* !MODULE_FLAG */
     
     retries = 3;
     do {
@@ -1153,7 +1151,7 @@
 		rscsi_disks[i].device = NULL;
 		sd_template.nr_dev--;
 		return i;
-	    };
+	    }
 	}
     {
 	/*
@@ -1399,7 +1397,7 @@
 	restore_flags(flags);
 	printk("Device busy for revalidation (usage=%d)\n", USAGE);
 	return -EBUSY;
-    };
+    }
     DEVICE_BUSY = 1;
     restore_flags(flags);
     
@@ -1419,7 +1417,7 @@
          * the partition table.
          */
         blksize_size[MAJOR_NR][minor] = 1024;
-    };
+    }
     
 #ifdef MAYBE_REINIT
     MAYBE_REINIT;
@@ -1461,7 +1459,7 @@
 		sd_gendisk.part[minor].start_sect = 0;
 		sd_gendisk.part[minor].nr_sects = 0;
 		sd_sizes[minor] = 0;
-	    };
+	    }
 	    
 	    dpnt->has_part_table = 0;
 	    dpnt->device = NULL;
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/seagate.c linux/drivers/scsi/seagate.c
--- v1.3.98/linux/drivers/scsi/seagate.c	Sun May  5 08:52:02 1996
+++ linux/drivers/scsi/seagate.c	Mon May  6 12:26:11 1996
@@ -303,7 +303,7 @@
 		}
 
       /* If the user specified the controller type from the command line,
-	 controller_type will be non-zero, so don't try and detect one */
+	 controller_type will be non-zero, so don't try to detect one */
 
 	if (!controller_type) {
 #ifdef OVERRIDE
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- v1.3.98/linux/drivers/scsi/sr.c	Sun May  5 08:52:02 1996
+++ linux/drivers/scsi/sr.c	Mon May  6 12:26:11 1996
@@ -603,11 +603,11 @@
 	restore_flags(flags);
 	
 	/* This is a performance enhancement.  We dig down into the request list and
-	 * try and find a queueable request (i.e. device not busy, and host able to
+	 * try to find a queueable request (i.e. device not busy, and host able to
 	 * accept another command.  If we find one, then we queue it. This can
 	 * make a big difference on systems with more than one disk drive.  We want
 	 * to have the interrupts off when monkeying with the request list, because
-	 * otherwise the kernel might try and slip in a request in between somewhere. */
+	 * otherwise the kernel might try to slip in a request in between somewhere. */
 	
 	if (!SCpnt && sr_template.nr_dev > 1){
 	    struct request *req1;
@@ -789,7 +789,7 @@
 		if (((long) sgpnt[count].address) + sgpnt[count].length > ISA_DMA_THRESHOLD &&
 		  SCpnt->host->unchecked_isa_dma) {
 		    sgpnt[count].alt_address = sgpnt[count].address;
-		    /* We try and avoid exhausting the DMA pool, since it is easier
+		    /* We try to avoid exhausting the DMA pool, since it is easier
 		     * to control usage here.  In other places we might have a more
 		     * pressing need, and we would be screwed if we ran out */
 		    if(dma_free_sectors < (sgpnt[count].length >> 9) + 5) {
diff -u --recursive --new-file v1.3.98/linux/drivers/scsi/wd33c93.h linux/drivers/scsi/wd33c93.h
--- v1.3.98/linux/drivers/scsi/wd33c93.h	Sat Apr 27 15:19:57 1996
+++ linux/drivers/scsi/wd33c93.h	Mon May  6 12:44:31 1996
@@ -259,7 +259,7 @@
 /* defines for hostdata->level2 */
 /* NOTE: only the first 3 are implemented so far */
 
-/*  (The first 8 bits are reserved for compatibility. They function
+#define L2_NONE      1  /* no combination commands - we get lots of ints */
 #define L2_SELECT    2  /* start with SEL_ATN_XFER, but never resume it */
 #define L2_BASIC     3  /* resume after STATUS ints & RDP messages */
 #define L2_DATA      4  /* resume after DATA_IN/OUT ints */
diff -u --recursive --new-file v1.3.98/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG
--- v1.3.98/linux/drivers/sound/CHANGELOG	Fri Apr 12 15:52:02 1996
+++ linux/drivers/sound/CHANGELOG	Mon May  6 12:26:11 1996
@@ -255,7 +255,7 @@
 
 Since 2.1
 - Preliminary support for SB16. 
-  - The SB16 mixer is supported in it's native mode.
+  - The SB16 mixer is supported in its native mode.
   - Digitized voice capability up to 44.1 kHz/8 bit/mono
     (16 bit and stereo support coming in the next release).
 - Fixed some bugs in the digitized voice driver for PAS16.
diff -u --recursive --new-file v1.3.98/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v1.3.98/linux/drivers/sound/Readme.cards	Fri Apr 12 15:52:02 1996
+++ linux/drivers/sound/Readme.cards	Mon May  6 12:26:11 1996
@@ -893,10 +893,10 @@
 Please check which version of sound driver you are using before 
 complaining that your card is not supported. It's possible that you are 
 using a driver version which was released months before your card was
-introduced. Driver's release date is listed after it's version number
+introduced. Driver's release date is listed after its version number
 in "cat /dev/sndstat" printout and in file linux/drivers/sound/soundvers.h.
 
-First of all. There is an easy way to make most soundcards to work
+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 use loadlin.exe to boot Linux. If Linux is configured
 to use the same I/O, IRQ and DMA numbers than DOS, the card could work.
diff -u --recursive --new-file v1.3.98/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v1.3.98/linux/drivers/sound/ad1848.c	Wed Apr 24 17:00:41 1996
+++ linux/drivers/sound/ad1848.c	Mon May  6 12:26:12 1996
@@ -1187,7 +1187,7 @@
      * Check that the I/O address is in use.
      *
      * The bit 0x80 of the base I/O port is known to be 0 after the
-     * chip has performed it's power on initialization. Just assume
+     * chip has performed its power-on initialization. Just assume
      * this has happened before the OS is starting.
      *
      * If the I/O address is unused, it typically returns 0xff.
@@ -1233,7 +1233,7 @@
     }
 
   /*
-     * The indirect register I12 has some read only bits. Lets
+     * The indirect register I12 has some read only bits. Let's
      * try to change them.
    */
 
diff -u --recursive --new-file v1.3.98/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v1.3.98/linux/drivers/sound/gus_wave.c	Sat Apr 13 18:22:07 1996
+++ linux/drivers/sound/gus_wave.c	Mon May  6 12:26:12 1996
@@ -359,7 +359,7 @@
 
   gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
   gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
-  /* Could writing twice fix problems with GUS_VOICE_POS() ? Lets try... */
+  /* Could writing twice fix problems with GUS_VOICE_POS() ? Let's try... */
   gus_delay ();
   gus_write16 (reg, (unsigned short) ((address >> 7) & 0xffff));
   gus_write16 (reg + 1, (unsigned short) ((address << 9) & 0xffff));
@@ -3128,7 +3128,7 @@
     sound_num_blocks++;;
   if (samples == NULL)
     {
-      printk ("GUS Error: Cant allocate memory for instrument tables\n");
+      printk ("GUS Error: Can't allocate memory for instrument tables\n");
       return mem_start;
     }
 
diff -u --recursive --new-file v1.3.98/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v1.3.98/linux/drivers/sound/mpu401.c	Fri Apr 12 15:52:03 1996
+++ linux/drivers/sound/mpu401.c	Mon May  6 12:26:12 1996
@@ -1348,7 +1348,7 @@
   /*
      * The MPU-401 supports just a limited set of possible timebase values.
      * Since the applications require more choices, the driver has to
-     * program the HW to do it's best and to convert between the HW and
+     * program the HW to do its best and to convert between the HW and
      * actual timebases.
    */
 
diff -u --recursive --new-file v1.3.98/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c
--- v1.3.98/linux/drivers/sound/sound_switch.c	Mon Mar 25 09:25:27 1996
+++ linux/drivers/sound/sound_switch.c	Mon May  6 07:28:52 1996
@@ -407,8 +407,6 @@
 int
 sound_open_sw (int dev, struct fileinfo *file)
 {
-  int             retval;
-
   DEB (printk ("sound_open_sw(dev=%d)\n", dev));
 
   if ((dev >= SND_NDEVS) || (dev < 0))
@@ -438,25 +436,37 @@
 #ifdef CONFIG_SEQUENCER
     case SND_DEV_SEQ:
     case SND_DEV_SEQ2:
+    {
+      int retval;
+
       if ((retval = sequencer_open (dev, file)) < 0)
 	return retval;
       break;
+    }
 #endif
 
 #ifdef CONFIG_MIDI
     case SND_DEV_MIDIN:
+    {
+      int retval;
+
       if ((retval = MIDIbuf_open (dev, file)) < 0)
 	return retval;
       break;
+    }
 #endif
 
 #ifdef CONFIG_AUDIO
     case SND_DEV_DSP:
     case SND_DEV_DSP16:
     case SND_DEV_AUDIO:
+    {
+      int retval;
+
       if ((retval = audio_open (dev, file)) < 0)
 	return retval;
       break;
+    }
 #endif
 
     default:
diff -u --recursive --new-file v1.3.98/linux/fs/Config.in linux/fs/Config.in
--- v1.3.98/linux/fs/Config.in	Sun May  5 08:52:02 1996
+++ linux/fs/Config.in	Mon May  6 12:44:31 1996
@@ -34,7 +34,10 @@
 tristate 'ISO9660 cdrom filesystem support' CONFIG_ISO9660_FS
 tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
 tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
-tristate 'AFFS filesystem support (read only)' CONFIG_AFFS_FS
+tristate 'Amiga FFS filesystem support (EXPERIMENTAL)' CONFIG_AFFS_FS
+if [ "$CONFIG_AFFS_FS" = "y" -o "$CONFIG_AFFS_FS" = "m" ]; then
+  define_bool CONFIG_AMIGA_PARTITION y
+fi
 tristate 'UFS filesystem support (read only)' CONFIG_UFS_FS
 if [ "$CONFIG_UFS_FS" != "n" ]; then
   bool "BSD disklabel (FreeBSD partition tables) support" CONFIG_BSD_DISKLABEL
diff -u --recursive --new-file v1.3.98/linux/fs/affs/Makefile linux/fs/affs/Makefile
--- v1.3.98/linux/fs/affs/Makefile	Tue Apr 23 13:57:11 1996
+++ linux/fs/affs/Makefile	Mon May  6 12:44:31 1996
@@ -1,5 +1,5 @@
 #
-# Makefile for the linux amiga fast filesystem routines.
+# Makefile for the linux affs-filesystem routines.
 #
 # Note! Dependencies are done automagically by 'make dep', which also
 # removes any old dependencies. DON'T put your own dependencies here
@@ -8,7 +8,7 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := affs.o
-O_OBJS   := namei.o inode.o file.o dir.o amigaffs.o symlink.o
+O_OBJS   := namei.o inode.o file.o dir.o amigaffs.o bitmap.o symlink.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.98/linux/fs/affs/amigaffs.c linux/fs/affs/amigaffs.c
--- v1.3.98/linux/fs/affs/amigaffs.c	Sat Apr 27 15:19:57 1996
+++ linux/fs/affs/amigaffs.c	Mon May  6 14:36:05 1996
@@ -1,89 +1,57 @@
 /*
  *  linux/fs/affs/amigaffs.c
  *
- *  (C) 1996  Stefan Reinauer - Modified to compile as Module
+ *  (c) 1996  Hans-Joachim Widmaier - Modified for larger blocks.
  *
  *  (C) 1993  Ray Burr - Amiga FFS filesystem.
  *
  */
 
-
-#include <linux/module.h>
-
-#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/sched.h>
 #include <linux/affs_fs.h>
+#include <linux/string.h>
+#include <linux/locks.h>
 #include <linux/mm.h>
+#include <linux/amigaffs.h>
 
-#include "amigaffs.h"
+extern struct timezone sys_tz;
 
 /*
  * Functions for accessing Amiga-FFS structures.
  *
  */
 
-/* Get key entry number ENTRY_POS from the header block pointed to
-   by DATA.  If ENTRY_POS is invalid, -1 is returned.  This is
-   used to get entries from file and directory headers as well
-   as extension and root blocks.  In the current FFS specs, these
-   tables are defined to be the same size in all of these. */
-
-int affs_get_key_entry (int bsize, void *data, int entry_pos)
-{
-	struct dir_front *dir_front = (struct dir_front *)data;
-	int key, hash_table_size;
-
-	hash_table_size = MIDBLOCK_LONGS (bsize);
-	key = 0;
-	if (entry_pos >= 0 && entry_pos < hash_table_size)
-		key = swap_long (dir_front->hash_table[entry_pos]);
-
-	return key;
-}
-
 /* Find the next used hash entry at or after *HASH_POS in a directory's hash
    table.  *HASH_POS is assigned that entry's number.  DIR_DATA points to
    the directory header block in memory.  If there are no more entries,
    0 is returned.  Otherwise, the key number in the next used hash slot
    is returned. */
 
-int affs_find_next_hash_entry (int bsize, void *dir_data, int *hash_pos)
+int
+affs_find_next_hash_entry(int hsize, void *dir_data, ULONG *hash_pos)
 {
-	struct dir_front *dir_front = (struct dir_front *)dir_data;
-	int i, hash_table_size;
+	struct dir_front *dir_front = dir_data;
+	ULONG i;
 
-	hash_table_size = MIDBLOCK_LONGS (bsize);
-	if (*hash_pos < 0 || *hash_pos >= hash_table_size)
-		return -1;
-	for (i = *hash_pos; i < hash_table_size; i++)
-		if (dir_front->hash_table[i] != 0)
+	for (i = *hash_pos; i < hsize; i++)
+		if (dir_front->hashtable[i] != 0)
 			break;
-	if (i == hash_table_size)
+	if (i >= hsize)
 		return 0;
 	*hash_pos = i;
-	return swap_long (dir_front->hash_table[i]);
-}
-
-/* Get the hash_chain (next file header key in hash chain) entry from a
-   file header block in memory pointed to by FH_DATA. */
-
-int affs_get_fh_hash_link (int bsize, void *fh_data)
-{
-	struct file_end *file_end;
-	int key;
-
-	file_end = GET_END_PTR (struct file_end, fh_data, bsize);
-	key = swap_long (file_end->hash_chain);
-	return key;
+	return htonl(dir_front->hashtable[i]);
 }
 
 /* Set *NAME to point to the file name in a file header block in memory
    pointed to by FH_DATA.  The length of the name is returned. */
 
-int affs_get_file_name (int bsize, void *fh_data, char **name)
+int
+affs_get_file_name(int bsize, void *fh_data, char **name)
 {
 	struct file_end *file_end;
 
-	file_end = GET_END_PTR (struct file_end, fh_data, bsize);
+	file_end = GET_END_PTR(struct file_end, fh_data, bsize);
 	if (file_end->file_name[0] == 0
 	    || file_end->file_name[0] > 30) {
 		printk ("affs_get_file_name: OOPS! bad filename\n");
@@ -96,57 +64,210 @@
         return file_end->file_name[0];
 }
 
-/* Get the key number of the first extension block for the file
-   header pointed to by FH_DATA. */
+/* Find the predecessor in the hash chain */
 
-int affs_get_extension (int bsize, void *fh_data)
+int
+affs_fix_hash_pred(struct inode *startino, int startoffset, LONG key, LONG newkey)
 {
-	struct file_end *file_end;
-	int key;
+	struct buffer_head	*bh = NULL;
+	ULONG			 nextkey;
+	LONG			 ptype, stype;
+	int			 retval;
+
+	nextkey = startino->i_ino;
+	retval  = -ENOENT;
+	lock_super(startino->i_sb);
+	while (1) {
+		pr_debug("AFFS: fix_hash_pred(): next key=%d, offset=%d\n", nextkey, startoffset);
+		if (nextkey == 0)
+			break;
+		if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
+			break;
+		if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
+		    || ptype != T_SHORT || (stype != ST_FILE && stype != ST_USERDIR &&
+					    stype != ST_LINKFILE && stype != ST_LINKDIR &&
+					    stype != ST_ROOT && stype != ST_SOFTLINK)) {
+			printk("AFFS: bad block found in link chain (ptype=%d, stype=%d)\n",
+			       ptype,stype);
+			affs_brelse(bh);
+			break;
+		}
+		nextkey = htonl(((ULONG *)bh->b_data)[startoffset]);
+		if (nextkey == key) {
+			((ULONG *)bh->b_data)[startoffset] = newkey;
+			affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+			mark_buffer_dirty(bh,1);
+			affs_brelse(bh);
+			retval = 0;
+			break;
+		}
+		affs_brelse(bh);
+		startoffset = AFFS_I2BSIZE(startino) / 4 - 4;
+	}
+	unlock_super(startino->i_sb);
 
-	file_end = GET_END_PTR (struct file_end, fh_data, bsize);
-	key = swap_long (file_end->extension);
-	return key;
+	return retval;
+}
+
+/* Remove inode from link chain */
+
+int
+affs_fix_link_pred(struct inode *startino, LONG key, LONG newkey)
+{
+	struct buffer_head	*bh = NULL;
+	ULONG			 nextkey;
+	ULONG			 offset;
+	LONG			 etype = 0;
+	LONG			 ptype, stype;
+	int			 retval;
+
+	offset  = AFFS_I2BSIZE(startino) / 4 - 10;
+	nextkey = startino->i_ino;
+	retval  = -ENOENT;
+	lock_super(startino->i_sb);
+	while (1) {
+		if (nextkey == 0)
+			break;
+		pr_debug("AFFS: find_link_pred(): next key=%d\n", nextkey));
+		if (!(bh = affs_bread(startino->i_dev,nextkey,AFFS_I2BSIZE(startino))))
+			break;
+		if (affs_checksum_block(AFFS_I2BSIZE(startino),bh->b_data,&ptype,&stype)
+		    || ptype != T_SHORT) {
+			affs_brelse(bh);
+			break;
+		}
+		if (!etype) {
+			if (stype != ST_FILE && stype != ST_USERDIR) {
+				affs_brelse(bh);
+				break;
+			}
+			if (stype == ST_FILE)
+				etype = ST_LINKFILE;
+			else
+				etype = ST_LINKDIR;
+		} else if (stype != etype) {
+			affs_brelse(bh);
+			retval = -EPERM;
+			break;
+		}
+		nextkey = htonl(((ULONG *)bh->b_data)[offset]);
+		if (nextkey == key) {
+			FILE_END(bh->b_data,startino)->link_chain = newkey;
+			affs_fix_checksum(AFFS_I2BSIZE(startino),bh->b_data,5);
+			mark_buffer_dirty(bh,1);
+			affs_brelse(bh);
+			retval = 0;
+			break;
+		}
+		affs_brelse(bh);
+	}
+	unlock_super(startino->i_sb);
+	return retval;
 }
 
 /* Checksum a block, do various consistency checks and optionally return
    the blocks type number.  DATA points to the block.  If their pointers
    are non-null, *PTYPE and *STYPE are set to the primary and secondary
-   block types respectively.  Returns non-zero if the block is not
-   consistent. */
+   block types respectively, *HASHSIZE is set to the size of the hashtable
+   (which lets us calculate the block size).
+   Returns non-zero if the block is not consistent. */
 
-int affs_checksum_block (int bsize, void *data, int *ptype, int *stype)
+ULONG
+affs_checksum_block(int bsize, void *data, LONG *ptype, LONG *stype)
 {
+	ULONG sum;
+	ULONG *p;
+
+	bsize /= 4;
 	if (ptype)
-		*ptype = swap_long (((long *) data)[0]);
+		*ptype = htonl(((LONG *)data)[0]);
 	if (stype)
-		*stype = swap_long (((long *) data)[bsize / 4 - 1]);
-	return 0;
-}
+		*stype = htonl(((LONG *)data)[bsize - 1]);
 
-static struct file_system_type affs_fs_type = {
-        affs_read_super, "affs", 1, NULL
-};
+	sum    = 0;
+	p      = data;
+	while (bsize--)
+		sum += htonl(*p++);
+	return sum;
+}
 
-int init_affs_fs(void)
+void
+affs_fix_checksum(int bsize, void *data, int cspos)
 {
-        return register_filesystem(&affs_fs_type);
+	ULONG	 ocs;
+	ULONG	 cs;
+
+	cs   = affs_checksum_block(bsize,data,NULL,NULL);
+	ocs  = htonl (((ULONG *)data)[cspos]);
+	ocs -= cs;
+	((ULONG *)data)[cspos] = htonl(ocs);
 }
 
-#ifdef MODULE
-int init_module(void)
+void
+secs_to_datestamp(int secs, struct DateStamp *ds)
 {
-	int status;
+	ULONG	 days;
+	ULONG	 minute;
 
-	if ((status = init_affs_fs()) == 0)
-	        register_symtab(0);
-	return status;
+	secs -= sys_tz.tz_minuteswest * 60 +((8 * 365 + 2) * 24 * 60 * 60);
+	if (secs < 0)
+		secs = 0;
+	days    = secs / 86400;
+	secs   -= days * 86400;
+	minute  = secs / 60;
+	secs   -= minute * 60;
+
+	ds->ds_Days   = htonl(days);
+	ds->ds_Minute = htonl(minute);
+	ds->ds_Tick   = htonl(secs * 50);
 }
 
-void cleanup_module(void)
+int
+prot_to_mode(ULONG prot)
 {
-	unregister_filesystem(&affs_fs_type);
+	int	 mode = 0;
+
+	if (AFFS_UMAYWRITE(prot))
+		mode |= S_IWUSR;
+	if (AFFS_UMAYREAD(prot))
+		mode |= S_IRUSR;
+	if (AFFS_UMAYEXECUTE(prot))
+		mode |= S_IXUSR;
+	if (AFFS_GMAYWRITE(prot))
+		mode |= S_IWGRP;
+	if (AFFS_GMAYREAD(prot))
+		mode |= S_IRGRP;
+	if (AFFS_GMAYEXECUTE(prot))
+		mode |= S_IXGRP;
+	if (AFFS_OMAYWRITE(prot))
+		mode |= S_IWOTH;
+	if (AFFS_OMAYREAD(prot))
+		mode |= S_IROTH;
+	if (AFFS_OMAYEXECUTE(prot))
+		mode |= S_IXOTH;
+	
+	return mode;
 }
 
-#endif
+ULONG
+mode_to_prot(int mode)
+{
+	ULONG	 prot = 0;
 
+	if (mode & S_IXUSR)
+		prot |= FIBF_SCRIPT;
+	if (mode & S_IRUSR)
+		prot |= FIBF_READ;
+	if (mode & S_IWUSR)
+		prot |= FIBF_WRITE | FIBF_DELETE;
+	if (mode & S_IRGRP)
+		prot |= FIBF_GRP_READ;
+	if (mode & S_IWGRP)
+		prot |= FIBF_GRP_WRITE;
+	if (mode & S_IROTH)
+		prot |= FIBF_OTR_READ;
+	if (mode & S_IWOTH)
+		prot |= FIBF_OTR_WRITE;
+	
+	return prot;
+}
diff -u --recursive --new-file v1.3.98/linux/fs/affs/amigaffs.h linux/fs/affs/amigaffs.h
--- v1.3.98/linux/fs/affs/amigaffs.h	Sat Apr 27 15:19:57 1996
+++ linux/fs/affs/amigaffs.h	Thu Jan  1 02:00:00 1970
@@ -1,206 +0,0 @@
-#ifndef AMIGAFFS_H
-#define AMIGAFFS_H
-
-/* Ugly macros to make the code pretty. */
-
-#define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st))))
-
-#define MIDBLOCK_LONGS(sz) ((sz - sizeof (struct dir_front) \
-			     - sizeof (struct dir_end)) / 4)
-
-static __inline__ unsigned long
-swap_long (unsigned long x)
-{
-#ifdef __i386__   /* bad check... should be endian check */
-  unsigned char *px;
-
-  px = (unsigned char *) &x;
-  return (  (px[3] <<  0)
-	  | (px[2] <<  8)
-	  | (px[1] << 16)
-	  | (px[0] << 24));
-#else
-  return x;
-#endif
-}
-
-typedef unsigned long	ULONG;
-typedef unsigned short	UWORD;
-typedef unsigned char	UBYTE;
-
-typedef long		LONG;
-typedef short		WORD;
-typedef char		BYTE;
-
-struct DateStamp
-{
-  ULONG ds_Days;
-  ULONG ds_Minute;
-  ULONG ds_Tick;
-};
-
-#define T_SHORT 	2
-#define T_LIST		16
-#define T_DATA		8
-
-#define ST_FILE 	-3
-#define ST_USERDIR	2
-#define ST_ROOT 	1
-#define ST_SOFTLINK	3
-#define ST_LINKFILE	-4
-#define ST_LINKDIR	4
-
-#define PROT_ARCHIVE        (1<<4)
-#define PROT_READ           (1<<3)
-#define PROT_WRITE          (1<<2)
-#define PROT_EXECUTE        (1<<1)
-#define PROT_DELETE         (1<<0)
-
-#define PROT_OTR_READ       (1<<15)
-#define PROT_OTR_WRITE      (1<<14)
-#define PROT_OTR_EXECUTE    (1<<13)
-#define PROT_OTR_DELETE     (1<<12)
-
-#define PROT_GRP_READ       (1<<11)
-#define PROT_GRP_WRITE      (1<<10)
-#define PROT_GRP_EXECUTE    (1<<9)
-#define PROT_GRP_DELETE     (1<<8)
-
-struct ffs_root_front
-{
-  LONG primary_type;
-  ULONG spare1[2];
-  ULONG hash_size;
-  ULONG spare2;
-  ULONG checksum;
-};
-
-struct ffs_root_end
-{
-  LONG bm_flag;
-  ULONG bm_keys[25];
-  ULONG bm_extend;
-  struct DateStamp dir_altered;
-  UBYTE disk_name[40];
-  struct DateStamp disk_altered;
-  struct DateStamp disk_made;
-  ULONG spare1[3];
-  LONG secondary_type;
-};
-
-struct dir_front
-{
-  LONG primary_type;
-  ULONG own_key;
-  ULONG spare1[3];
-  ULONG checksum;
-  ULONG hash_table[0];
-};
-
-struct dir_end
-{
-  ULONG spare1;
-  UWORD uid;
-  UWORD gid;
-  ULONG protect;
-  ULONG spare2;
-  UBYTE comment[92];
-  struct DateStamp created;
-  UBYTE dir_name[64];
-  ULONG hash_chain;
-  ULONG parent;
-  ULONG spare3;
-  LONG secondary_type;
-};
-
-struct file_front
-{
-  LONG primary_type;
-  ULONG own_key;
-  ULONG block_count;
-  ULONG unknown1;
-  ULONG first_data;
-  ULONG checksum;
-  ULONG blocks[0];
-};
-
-struct file_end
-{
-  ULONG spare1;
-  UWORD uid;
-  UWORD gid;
-  ULONG protect;
-  ULONG byte_size;
-  UBYTE comment[92];
-  struct DateStamp created;
-  UBYTE file_name[64];
-  ULONG hash_chain;
-  ULONG parent;
-  ULONG extension;
-  LONG secondary_type;
-};
-
-struct data_front
-{
-  LONG primary_type;
-  ULONG header;
-  ULONG seq_num;
-  ULONG data_size;
-  ULONG next_data;
-  ULONG checksum;
-};
-
-struct symlink_front
-{
-  LONG primary_type;
-  ULONG own_key;
-  LONG unused[3];
-  ULONG checksum;
-  UBYTE symname[0];
-};
-
-struct symlink_end
-{
-  ULONG spare1;
-  UWORD uid;
-  UWORD gid;
-  ULONG protect;
-  ULONG spare2;
-  UBYTE comment[92];
-  struct DateStamp created;
-  UBYTE link_name[64];
-  ULONG hash_chain;
-  ULONG parent;
-  ULONG spare3;
-  LONG secondary_type;
-};
-
-struct hardlink_front
-{
-  LONG primary_type;
-  ULONG own_key;
-  LONG unused[3];
-  ULONG checksum;
-};
-
-struct hardlink_end
-{
-  ULONG spare1;
-  UWORD uid;
-  UWORD gid;
-  ULONG protect;
-  ULONG spare2;
-  UBYTE comment[92];
-  struct DateStamp created;
-  UBYTE link_name[32];
-  ULONG spare3;
-  ULONG original;
-  ULONG link_chain;
-  ULONG spare4[5];
-  ULONG hash_chain;
-  ULONG parent;
-  ULONG spare5;
-  LONG secondary_type;
-};
-
-#endif
diff -u --recursive --new-file v1.3.98/linux/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- v1.3.98/linux/fs/affs/bitmap.c	Thu Jan  1 02:00:00 1970
+++ linux/fs/affs/bitmap.c	Mon May  6 14:36:05 1996
@@ -0,0 +1,332 @@
+/*
+ *  linux/fs/affs/bitmap.c
+ *
+ *  (c) 1996 Hans-Joachim Widmaier
+ */
+
+/* bitmap.c contains the code that handles the inode and block bitmaps */
+
+#include <linux/sched.h>
+#include <linux/affs_fs.h>
+#include <linux/stat.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/amigaffs.h>
+#include <linux/locks.h>
+
+#include <asm/bitops.h>
+
+static int nibblemap[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+
+int
+affs_count_free_bits(int blocksize, const UBYTE *data)
+{
+  int	 free;
+  int	 i;
+
+  free = 0;
+  for (i = 0; i < blocksize; i++) {
+    free  += nibblemap[data[i] & 0xF] + nibblemap[(data[i]>>4) & 0xF];
+  }
+
+  return free;
+}
+
+int
+affs_count_free_blocks(struct super_block *s)
+{
+	int	 free;
+	int	 i;
+
+	pr_debug("AFFS: count_free_blocks()\n");
+
+	free = 0;
+	if (s->u.affs_sb.s_flags & SF_BM_VALID) {
+		for (i = 0; i < s->u.affs_sb.s_bm_count; i++) {
+			free += s->u.affs_sb.s_bitmap[i].bm_free;
+		}
+	}
+	return free;
+}
+
+void
+affs_free_block(struct super_block *sb, LONG block)
+{
+	int			 bmap;
+	int			 bit;
+	ULONG			 blk;
+	struct affs_bm_info	*bm;
+
+	pr_debug("AFFS: free_block(%d)\n",block);
+
+	blk    = block - sb->u.affs_sb.s_reserved;
+	bmap   = blk / (sb->s_blocksize * 8 - 32);
+	bit    = blk % (sb->s_blocksize * 8 - 32);
+	bm     = &sb->u.affs_sb.s_bitmap[bmap];
+	if (bmap >= sb->u.affs_sb.s_bm_count || bit >= bm->bm_size) {
+		printk("AFFS: free_block(): block %d outside partition.\n",block);
+		return;
+	}
+	blk  = 0;
+	set_bit(bit & 31,&blk);
+
+	lock_super(sb);
+	if (set_bit(bit ^ BO_EXBITS,bm->bm_bh->b_data + 4))
+		printk("AFFS: free_block(): block %d is already free.\n",block);
+	else {
+		bm->bm_free++;
+		((ULONG *)bm->bm_bh->b_data)[0] = ntohl(htonl(((ULONG *)bm->bm_bh->b_data)[0]) - blk);
+		mark_buffer_dirty(bm->bm_bh,1);
+		sb->s_dirt = 1;
+	}
+	unlock_super(sb);
+}
+
+static ULONG
+affs_balloc(struct inode *inode, int zone_no)
+{
+	ULONG			 w;
+	ULONG			*bm;
+	int			 fb;
+	int			 i;
+	int			 fwb;
+	ULONG			 block;
+	struct affs_zone	*zone;
+	struct super_block	*sb;
+
+	sb   = inode->i_sb;
+	zone = &sb->u.affs_sb.s_zones[zone_no];
+
+	if (!zone || !zone->z_bm || !zone->z_bm->bm_bh)
+		return 0;
+	
+	pr_debug("AFFS: balloc(inode=%lu,zone=%d)\n",inode->i_ino,zone_no);
+
+	bm = (ULONG *)zone->z_bm->bm_bh->b_data;
+repeat:
+	fb = (zone->z_bm->bm_size + 31) >> 5;
+	for (i = zone->z_start; i <= fb; i++) {
+		if (bm[i])
+			goto found;
+	}
+	return 0;
+
+found:
+	fwb = zone->z_bm->bm_firstblk + (i - 1) * 32;
+	lock_super(sb);
+	zone->z_start = i;
+	w   = htonl(bm[i]);
+	fb  = find_first_one_bit(&w,32);
+	if (fb > 31 || !clear_bit(fb ^ BO_EXBITS,&bm[i])) {
+		unlock_super(sb);
+		printk("AFFS: balloc(): empty block disappeared somehow\n");
+		goto repeat;
+	}
+	block = fwb + fb;
+	zone->z_bm->bm_free--;
+
+	/* prealloc as much as possible within this word, but not for headers */
+
+	if (zone_no) {
+		while (inode->u.affs_i.i_pa_cnt < MAX_PREALLOC && ++fb < 32) {
+			fb = find_next_one_bit(&w,32,fb);
+			if (fb > 31)
+				break;
+			if (!clear_bit(fb ^ BO_EXBITS,&bm[i])) {
+				printk("AFFS: balloc(): empty block disappeared\n");
+				break;
+			}
+			inode->u.affs_i.i_data[inode->u.affs_i.i_pa_last++] = fwb + fb;
+			inode->u.affs_i.i_pa_last &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_cnt++;
+			zone->z_bm->bm_free--;
+		}
+	}
+	w    -= htonl(bm[i]);
+	bm[0] = ntohl(htonl(bm[0]) + w);
+	unlock_super(sb);
+	mark_buffer_dirty(zone->z_bm->bm_bh,1);
+
+	return block;
+}
+
+static void
+affs_find_new_zone(struct super_block *sb,struct affs_zone *z, int minfree, int start)
+{
+	struct affs_bm_info	*bm;
+	int			 offs;
+	int			 zone;
+	int			 free;
+	int			 len;
+
+	pr_debug("AFFS: find_new_zone()\n");
+
+	lock_super(sb);
+
+	zone    = start;
+	z->z_bm = NULL;
+	while (1) {
+		if (zone >= sb->u.affs_sb.s_num_zones) {
+			zone = 0;
+			continue;
+		}
+
+		if (!set_bit(zone,sb->u.affs_sb.s_zonemap)) {
+			bm   = &sb->u.affs_sb.s_bitmap[zone >> (sb->s_blocksize_bits - 8)];
+			offs = zone * 256 & (sb->s_blocksize - 1);
+			len  = bm->bm_size / 8 - offs;
+			if (len > 256)
+				len = 256;
+			offs += 4;
+			free  = affs_count_free_bits(len,(char *)bm->bm_bh->b_data + offs);
+			if (free && (100 * free) / (len * 8) > minfree) {
+				z->z_bm       = bm;
+				z->z_start    = offs / 4;
+				z->z_ino      = 0;
+				z->z_zone_no  = zone;
+				pr_debug("  ++ found zone (%d) in bh %d at offset %d with %d free blocks\n",
+					 zone,(zone >> (sb->s_blocksize_bits - 8)),offs,free);
+				break;
+			}
+			clear_bit(zone,sb->u.affs_sb.s_zonemap);
+		}
+
+		/* Skip to next possible zone */
+
+		pr_debug("  ++ Skipping to next zone\n");
+		if (++zone == start)
+			break;
+	}
+	unlock_super(sb);
+	return;
+}
+
+LONG
+affs_new_header(struct inode *inode)
+{
+	struct affs_zone	*zone;
+	LONG			 block;
+	struct super_block	*sb;
+	struct buffer_head	*bh;
+
+	sb   = inode->i_sb;
+	zone = &sb->u.affs_sb.s_zones[0];
+
+	/* We try up to 3 times to find a free block:
+	 * If there is no more room in the current header zone,
+	 * we try to get a new one and allocate the block there.
+	 * If there is no zone with at least AFFS_HDR_MIN_FREE
+	 * percent of free blocks, we try to find a zone with
+	 * at least one free block.
+	 */
+
+	if (!(block = affs_balloc(inode,0))) {
+		clear_bit(zone->z_zone_no,sb->u.affs_sb.s_zonemap);
+		affs_find_new_zone(sb,zone,AFFS_HDR_MIN_FREE,(sb->u.affs_sb.s_num_zones + 1) / 2);
+		if (!(block = affs_balloc(inode,0))) {
+			clear_bit(zone->z_zone_no,sb->u.affs_sb.s_zonemap);
+			affs_find_new_zone(sb,zone,0,(sb->u.affs_sb.s_num_zones + 1) / 2);
+			if (!(block = affs_balloc(inode,0)))
+				return 0;
+		}
+	}
+	if (!(bh = getblk(inode->i_dev,block,sb->s_blocksize))) {
+		printk("AFFS: balloc(): cannot read block %d\n",block);
+		return 0;
+	}
+	memset(bh->b_data,0,sb->s_blocksize);
+	mark_buffer_uptodate(bh,1);
+	mark_buffer_dirty(bh,1);
+	affs_brelse(bh);
+
+	return block;
+}
+
+LONG
+affs_new_data(struct inode *inode)
+{
+	int			 empty, old;
+	unsigned long		 oldest;
+	struct affs_zone	*zone;
+	struct super_block	*sb;
+	struct buffer_head	*bh;
+	int			 i = 0;
+	LONG			 block;
+
+	pr_debug("AFFS: new_data(ino=%lu)\n",inode->i_ino);
+
+	sb = inode->i_sb;
+	lock_super(sb);
+	if (inode->u.affs_i.i_pa_cnt) {
+		inode->u.affs_i.i_pa_cnt--;
+		unlock_super(sb);
+		block = inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++];
+		inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+		goto init_block;
+	}
+	unlock_super(sb);
+repeat:
+	oldest = jiffies;
+	old    = 0;
+	empty  = 0;
+	zone   = &sb->u.affs_sb.s_zones[inode->u.affs_i.i_zone];
+	if (zone->z_ino == inode->i_ino) {
+		i = inode->u.affs_i.i_zone;
+		goto found;
+	}
+	for (i = 1; i < MAX_ZONES; i++) {
+		zone = &sb->u.affs_sb.s_zones[i];
+		if (!empty && zone->z_bm && !zone->z_ino)
+			empty = i;
+		if (zone->z_bm && zone->z_lru_time < oldest) {
+			old    = i;
+			oldest = zone->z_lru_time;
+		}
+	}
+	if (empty)
+		i = empty;
+	else if (old)
+		i = old;
+	else
+		return affs_new_header(inode);
+
+	inode->u.affs_i.i_zone = i;
+	zone->z_ino            = inode->i_ino;
+
+found:
+	zone = &sb->u.affs_sb.s_zones[i];
+	if (!(block = affs_balloc(inode,i))) {				/* Zone is full */
+		clear_bit(zone->z_zone_no,sb->u.affs_sb.s_zonemap);
+		affs_find_new_zone(sb,zone,AFFS_DATA_MIN_FREE,sb->u.affs_sb.s_nextzone);
+		sb->u.affs_sb.s_nextzone = zone->z_zone_no + 1;
+		goto repeat;
+	}
+
+init_block:
+	if (!(bh = getblk(inode->i_dev,block,sb->s_blocksize))) {
+		printk("AFFS: balloc(): cannot read block %u\n",block);
+		return 0;
+	}
+	memset(bh->b_data,0,sb->s_blocksize);
+	mark_buffer_uptodate(bh,1);
+	mark_buffer_dirty(bh,1);
+	affs_brelse(bh);
+
+	return block;
+}
+
+void
+affs_make_zones(struct super_block *sb)
+{
+	int	 i, j;
+
+	pr_debug("AFFS: make_zones(): num_zones=%d\n",sb->u.affs_sb.s_num_zones);
+
+	j = (sb->u.affs_sb.s_num_zones + 1) / 2;
+
+	affs_find_new_zone(sb,&sb->u.affs_sb.s_zones[0],AFFS_HDR_MIN_FREE,j);
+	for (i = 1; i < MAX_ZONES; i++) {
+		affs_find_new_zone(sb,&sb->u.affs_sb.s_zones[i],AFFS_DATA_MIN_FREE,j);
+		j = sb->u.affs_sb.s_zones[i].z_zone_no + 1;
+	}
+}
diff -u --recursive --new-file v1.3.98/linux/fs/affs/dir.c linux/fs/affs/dir.c
--- v1.3.98/linux/fs/affs/dir.c	Tue Apr 23 13:57:11 1996
+++ linux/fs/affs/dir.c	Mon May  6 14:36:24 1996
@@ -1,6 +1,8 @@
 /*
  *  linux/fs/affs/dir.c
  *
+ *  (c) 1996  Hans-Joachim Widmaier - Modifications for larger blocks
+ *		and hard links.
  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
  *
  *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
@@ -8,25 +10,25 @@
  *  (C) 1991  Linus Torvalds - minix filesystem
  *
  *  affs directory handling functions
+ *
  */
 
-#include <linux/errno.h>
-
 #include <asm/segment.h>
-
+#include <linux/errno.h>
 #include <linux/fs.h>
-#include <linux/affs_fs.h>
 #include <linux/kernel.h>
 #include <linux/affs_fs.h>
 #include <linux/stat.h>
 #include <linux/string.h>
-#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/amigaffs.h>
 
 static int affs_readdir(struct inode *, struct file *, void *, filldir_t);
+static int affs_dir_read(struct inode * inode, struct file * filp, char * buf, int count);
 
-struct file_operations affs_dir_operations = {
+static struct file_operations affs_dir_operations = {
 	NULL,			/* lseek - default */
-	NULL,			/* read */
+	affs_dir_read,		/* read */
 	NULL,			/* write - bad */
 	affs_readdir,		/* readdir */
 	NULL,			/* select - default */
@@ -34,7 +36,7 @@
 	NULL,			/* mmap */
 	NULL,			/* no special open code */
 	NULL,			/* no special release code */
-	NULL			/* fsync */
+	file_fsync		/* default fsync */
 };
 
 /*
@@ -42,109 +44,153 @@
  */
 struct inode_operations affs_dir_inode_operations = {
 	&affs_dir_operations,	/* default directory file-ops */
-	NULL,			/* create */
+	affs_create,		/* create */
 	affs_lookup,		/* lookup */
-	NULL,			/* link */
-	NULL,       		/* unlink */
-	NULL,       		/* symlink */
-	NULL,       		/* mkdir */
-	NULL,			/* rmdir */
+	affs_link,		/* link */
+	affs_unlink,		/* unlink */
+	affs_symlink,		/* symlink */
+	affs_mkdir,		/* mkdir */
+	affs_rmdir,		/* rmdir */
 	NULL,			/* mknod */
-	NULL,			/* rename */
+	affs_rename,		/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
 	NULL,			/* bmap */
-	NULL,			/* truncate */
-	NULL			/* permission */
+	affs_dir_truncate,	/* truncate */
+	NULL			/* permissions */
 };
 
-/* This is used to speed up lookup.  Without this we would need to
-make a linear search of the directory to find the file that the
-directory read just returned.  This is a single element cache. */
-
-/* struct lookup_cache cache = {0,}; */
+static int
+affs_dir_read(struct inode * inode, struct file * filp, char * buf, int count)
+{
+	return -EISDIR;
+}
 
-static int affs_readdir(struct inode * inode, struct file * filp,
-	void * dirent, filldir_t filldir)
+static int
+affs_readdir(struct inode *inode, struct file *filp, void *dirent, filldir_t filldir)
 {
-	int i, j, chain_pos, hash_pos, reclen, ino;
+	int			 j, namelen;
+	LONG			 i;
+	ULONG			 hash_pos;
+	ULONG			 chain_pos;
+	unsigned long		 ino;
+	unsigned long			 old;
+	int stored;
 	char *name;
 	struct buffer_head *dir_bh;
 	struct buffer_head *fh_bh;
-	void *dir_data;
-	void *fh_data;
+	struct inode	   *dir;
 
-#ifdef DEBUG
-	printk ("AFFS: readdir: inode=%d f_pos=%d\n",
-		inode->i_ino, filp->f_pos);
-#endif
+	pr_debug("AFFS: readdir(ino=%ld,f_pos=%lu)\n",inode->i_ino,filp->f_pos);
 	
+
 	if (!inode || !S_ISDIR(inode->i_mode))
 		return -EBADF;
 
-	while ((unsigned long)filp->f_pos < 2) {
-		if (filp->f_pos == 0) {
-			if (filldir (dirent, ".", 1, filp->f_pos, inode->i_ino) < 0)
-				return 0;
-		} else {
-			i = affs_parent_ino (inode);
-			if (filldir (dirent, "..", 2, filp->f_pos, i) < 0)
-				return 0;
+	stored = 0;
+	dir_bh = NULL;
+	fh_bh  = NULL;
+	dir    = NULL;
+	old    = filp->f_pos & 0x80000000;
+	filp->f_pos &= 0x7FFFFFFF;
+
+	if (filp->f_pos == 0) {
+		filp->private_data = (void *)0;
+		if (filldir(dirent,".",1,filp->f_pos,inode->i_ino) < 0) {
+			return 0;
 		}
-		filp->f_pos++;
+		++filp->f_pos;
+		stored++;
 	}
-	/* No caching here.  I've got 16 megs why should I care?  :-) */
-	chain_pos = (filp->f_pos - 2) & 0xffff;
-	if (chain_pos == 0xffff)
-		return 0;
-	hash_pos = (filp->f_pos - 2) >> 16;
-#ifdef DEBUG
-	printk ("AFFS: hash_pos=%d chain_pos=%d\n", hash_pos, chain_pos);
-#endif
-	if (!(dir_bh = affs_pread(inode, inode->i_ino, &dir_data)))
-		return 0;
-	/* HASH_POS should already be on a used entry unless it is
-	   the first read of the directory.  Will this break the
-	   dirtell thing somehow? */
-	i = affs_find_next_hash_entry (AFFS_I2BSIZE (inode), dir_data,
-				       &hash_pos);
-	j = chain_pos;
-	for (;;) {
-		if (i <= 0) {
-#ifdef DEBUG
-			printk ("AFFS: bad f_pos in readdir\n");
-#endif
-			brelse (dir_bh);
-			return 0;
+	if (filp->f_pos == 1) {
+		if (filldir(dirent,"..",2,filp->f_pos,affs_parent_ino(inode)) < 0) {
+			filp->f_pos |= 0x80000000;
+			return stored;
 		}
-		ino = i;
-		if (!(fh_bh = affs_pread (inode, i, &fh_data))) {
-			brelse (dir_bh);
-			return 0;
+		filp->f_pos = 2;
+		stored++;
+	}
+
+	/* Read original if this is a link */
+	ino = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+	if (!(dir = iget(inode->i_sb,ino)))
+		return stored;
+	
+	chain_pos = (filp->f_pos - 2) & 0xffff;
+	hash_pos  = (filp->f_pos - 2) >> 16;
+	if (chain_pos == 0xffff) {
+		printk("AFFS: more than 65535 entries in chain\n");
+		chain_pos = 0;
+		hash_pos++;
+		filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
+	}
+	if (!(dir_bh = affs_bread(inode->i_dev,ino,AFFS_I2BSIZE(inode))))
+		goto readdir_done;
+
+	while (!stored || !old) {
+		while (hash_pos < AFFS_I2HSIZE(inode) &&
+		     !((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos])
+			hash_pos++;
+		if (hash_pos >= AFFS_I2HSIZE(inode))
+			goto readdir_done;
+		
+		i = htonl(((struct dir_front *)dir_bh->b_data)->hashtable[hash_pos]);
+		j = chain_pos;
+		/* If the directory hasn't changed since the last call to readdir(),
+		 * we can jump directly to where we left off.
+		 */
+		if (filp->private_data && filp->f_version == dir->i_version) {
+			i = (ULONG)filp->private_data;
+			j = 0;
+			pd_debug("AFFS: readdir() left off=%lu\n",i);
 		}
-		i = affs_get_fh_hash_link (AFFS_I2BSIZE (inode), fh_data);
-		if (j == 0) {
-			j = 1;
-			if (i <= 0) {
-				hash_pos++;
-				i = affs_find_next_hash_entry (AFFS_I2BSIZE (inode),
-							       dir_data, &hash_pos);
-				if (i <= 0)
-					chain_pos = 0xffff;
-				else
-					chain_pos = 0;
-			} else
-				chain_pos++;
-			reclen = affs_get_file_name (AFFS_I2BSIZE (inode),
-						     fh_data, &name);
-			if (filldir (dirent, name, reclen, filp->f_pos, ino) < 0) {
-				brelse (fh_bh);
-				brelse (dir_bh);
-				return 0;
+		filp->f_version = dir->i_version;
+		pd_debug("AFFS: hash_pos=%lu chain_pos=%lu\n", hash_pos, chain_pos);
+		while (i) {
+			if (!(fh_bh = affs_bread(inode->i_dev,i,AFFS_I2BSIZE(inode)))) {
+				printk("AFFS: readdir: Can't get block %d\n",i);
+				goto readdir_done;
 			}
-			filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
+			ino = i;
+			i   = htonl(FILE_END(fh_bh->b_data,inode)->hash_chain);
+			if (j == 0)
+				break;
+			affs_brelse(fh_bh);
+			fh_bh = NULL;
+			j--;
+		}
+		if (fh_bh) {
+			namelen = affs_get_file_name(AFFS_I2BSIZE(inode),fh_bh->b_data,&name);
+			pr_debug("AFFS: readdir(): filldir(..,\"%.*s\",ino=%lu), i=%lu\n",namelen,name,ino,i);
+			filp->private_data = (void *)ino;
+			if (filldir(dirent,name,namelen,filp->f_pos,ino) < 0)
+				goto readdir_done;
+			filp->private_data = (void *)i;
+			affs_brelse(fh_bh);
+			fh_bh = NULL;
+			stored++;
 		}
-		brelse (fh_bh);
-		j--;
+		if (i == 0) {
+			hash_pos++;
+			chain_pos = 0;
+		} else
+			chain_pos++;
+		filp->f_pos = ((hash_pos << 16) | chain_pos) + 2;
 	}
+
+readdir_done:
+	filp->f_pos |= old;
+	affs_brelse(dir_bh);
+	affs_brelse(fh_bh);
+	iput(dir);
+	pr_debug("AFFS: readdir()=%d\n",stored);
+	return stored;
+}
+
+void
+affs_dir_truncate(struct inode *inode)
+{
+	printk("AFFS: dir_truncate()\n");
 }
diff -u --recursive --new-file v1.3.98/linux/fs/affs/file.c linux/fs/affs/file.c
--- v1.3.98/linux/fs/affs/file.c	Sat Apr 27 15:19:57 1996
+++ linux/fs/affs/file.c	Mon May  6 14:36:24 1996
@@ -1,6 +1,8 @@
 /*
  *  linux/fs/affs/file.c
  *
+ *  (c) 1996  Hans-Joachim Widmaier - Rewritten
+ *
  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
  *
  *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
@@ -12,7 +14,6 @@
 
 #include <asm/segment.h>
 #include <asm/system.h>
-
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
 #include <linux/fcntl.h>
@@ -20,36 +21,31 @@
 #include <linux/errno.h>
 #include <linux/stat.h>
 #include <linux/locks.h>
-
 #include <linux/dirent.h>
-
-#define	NBUF	16
+#include <linux/fs.h>
+#include <linux/amigaffs.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
 
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
-#include <linux/fs.h>
-#include <linux/affs_fs.h>
-
-#include "amigaffs.h"
-
-int affs_file_read(struct inode *, struct file *, char *, int);
+static int affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count);
+static int affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count);
+static int affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count);
+static void affs_release_file(struct inode *inode, struct file *filp);
 
-/*
- * We have mostly NULL's here: the current defaults are ok for
- * the affs filesystem.
- */
-struct file_operations affs_file_operations = {
+static struct file_operations affs_file_operations = {
 	NULL,			/* lseek - default */
-	affs_file_read,		/* read */
-	NULL,			/* write */
+	generic_file_read,	/* read */
+	affs_file_write,	/* write */
 	NULL,			/* readdir - bad */
 	NULL,			/* select - default */
 	NULL,			/* ioctl - default */
 	generic_file_mmap,	/* mmap */
 	NULL,			/* no special open is needed */
-	NULL,			/* release */
-	NULL			/* can't fsync */
+	affs_release_file,	/* release */
+	file_fsync		/* brute force, but works */
 };
 
 struct inode_operations affs_file_inode_operations = {
@@ -65,75 +61,229 @@
 	NULL,			/* rename */
 	NULL,			/* readlink */
 	NULL,			/* follow_link */
-	NULL /* affs_bmap */,		/* bmap */
-	NULL,			/* truncate */
-	NULL			/* permission */
+	generic_readpage,	/* readpage */
+	NULL,			/* writepage */
+	affs_bmap,		/* bmap */
+	affs_truncate,		/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
 };
 
-static int affs_smap(struct inode *inode, int block)
-{
-	struct buffer_head *bh;
-	int key;
-	void *fh_data;
+static struct file_operations affs_file_operations_ofs = {
+	NULL,			/* lseek - default */
+	affs_file_read_ofs,	/* read */
+	affs_file_write_ofs,	/* write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select - default */
+	NULL,			/* ioctl - default */
+	NULL,			/* mmap */
+	NULL,			/* no special open is needed */
+	NULL,			/* release */
+	file_fsync		/* brute force, but works */
+};
 
-/* FIXME */
-#define KEY_SLOTS_PER_BLOCK 72
+struct inode_operations affs_file_inode_operations_ofs = {
+	&affs_file_operations_ofs,	/* default file operations */
+	NULL,			/* create */
+	NULL,			/* lookup */
+	NULL,			/* link */
+	NULL,			/* unlink */
+	NULL,			/* symlink */
+	NULL,			/* mkdir */
+	NULL,			/* rmdir */
+	NULL,			/* mknod */
+	NULL,			/* rename */
+	NULL,			/* readlink */
+	NULL,			/* follow_link */
+	NULL,			/* readpage */
+	NULL,			/* writepage */
+	NULL,			/* bmap */
+	affs_truncate_ofs,	/* truncate */
+	NULL,			/* permission */
+	NULL			/* smap */
+};
+
+int
+affs_bmap(struct inode *inode, LONG block)
+{
+	struct buffer_head	*bh;
+	LONG			 ext, key;
+	LONG			 ptype, stype;
 
-#ifdef DEBUG
-	printk ("affs_smap: ino=%d block=%d\n", inode->i_ino, block);
-#endif
+	pr_debug("AFFS: bmap(%lu,%d)\n",inode->i_ino,block);
 
 	if (block < 0) {
-		printk("affs_smap: block < 0");
+		printk("affs_bmap: block < 0\n");
 		return 0;
 	}
 
-	key = inode->i_ino;
+	/* If this is a hard link, quietly exchange the inode with the original */
+
+	key = inode->u.affs_i.i_original ? inode->u.affs_i.i_original : inode->i_ino;
+
+	ext = block / AFFS_I2HSIZE(inode);
+	if (ext) {
+		if (ext > inode->u.affs_i.i_max_ext)
+			ext = inode->u.affs_i.i_max_ext;
+		if (ext)
+			key = inode->u.affs_i.i_ext[ext -  1];
+		block -= ext * AFFS_I2HSIZE(inode);
+	}
+
 	for (;;) {
-		bh = affs_pread (inode, key, &fh_data);
+		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
 		if (!bh)
 			return 0;
-		if (block < KEY_SLOTS_PER_BLOCK)
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) ||
+		    (ptype != T_SHORT && ptype != T_LIST) || stype != ST_FILE) {
+			affs_brelse(bh);
+			return 0;
+		}
+		if (block < AFFS_I2HSIZE(inode))
 			break;
-		block -= KEY_SLOTS_PER_BLOCK;
-		key = affs_get_extension (AFFS_I2BSIZE (inode), fh_data);
-#ifdef DEBUG
-		printk ("affs_smap: reading extension block %d\n", key);
-#endif
-		brelse (bh);
-	}
-	key = affs_get_key_entry (AFFS_I2BSIZE (inode), fh_data,
-				  (KEY_SLOTS_PER_BLOCK - 1) - block);
-	brelse (bh);
-
-#ifdef DEBUG
-	printk ("affs_smap: key=%d\n", key);
-#endif
+		block -= AFFS_I2HSIZE(inode);
+		key    = htonl(FILE_END(bh->b_data,inode)->extension);
+		affs_brelse(bh);
+		if (ext < EXT_CACHE_SIZE - 1) {
+			inode->u.affs_i.i_ext[ext] = key;
+			inode->u.affs_i.i_max_ext  = ++ext;
+		}
+	}
+	key = AFFS_GET_HASHENTRY(bh->b_data,(AFFS_I2HSIZE(inode) - 1) - block);
+	affs_brelse(bh);
 	return key;
 }
 
-/*
- * affs_file_read() is also needed by the directory read-routine,
- * so it's not static. NOTE! reading directories directly is a bad idea,
- * but has to be supported for now for compatibility reasons with older
- * versions.
+struct buffer_head *
+affs_getblock(struct inode *inode, LONG block)
+{
+	struct buffer_head	*bh;
+	struct buffer_head	*ebh;
+	LONG			 key;
+	LONG			 ext;
+	LONG			 cnt, j, pt;
+
+	pr_debug("AFFS: getblock(%lu,%d)\n",inode->i_ino,block);
+
+	if (block < 0)
+		return NULL;
+	key = inode->i_ino;
+	pt  = T_SHORT;
+
+	ext = block / AFFS_I2HSIZE(inode);
+	if (ext) {
+		if (ext > inode->u.affs_i.i_max_ext)
+			ext = inode->u.affs_i.i_max_ext;
+		if (ext) {
+			key    = inode->u.affs_i.i_ext[ext - 1];
+			block -= ext * AFFS_I2HSIZE(inode);
+			pt     = T_LIST;
+		}
+	}
+
+	for (;;) {
+		bh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+		if (!bh)
+			return NULL;
+		if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&cnt,&j) ||
+		    cnt != pt || j != ST_FILE) {
+		    	printk("AFFS: getblock(): inode %d is not a valid %s\n",key,
+			       pt == T_SHORT ? "file header" : "extension block");
+			affs_brelse(bh);
+			return NULL;
+		}
+		j = htonl(((struct file_front *)bh->b_data)->block_count);
+		while (j < AFFS_I2HSIZE(inode) && j <= block) {
+			key = affs_new_data(inode);
+			if (!key)
+				break;
+			lock_super(inode->i_sb);
+			if (AFFS_BLOCK(bh->b_data,inode,j)) {
+				unlock_super(inode->i_sb);
+				printk("AFFS: getblock(): block already allocated\n");
+				affs_free_block(inode->i_sb,key);
+				j++;
+				continue;
+			}
+			unlock_super(inode->i_sb);
+			AFFS_BLOCK(bh->b_data,inode,j) = ntohl(key);
+			j++;
+		}
+		if (pt == T_SHORT)
+			((struct file_front *)bh->b_data)->first_data =
+								AFFS_BLOCK(bh->b_data,inode,0);
+		((struct file_front *)bh->b_data)->block_count = ntohl(j);
+		affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+		mark_buffer_dirty(bh,1);
+
+		if (block < j)
+			break;
+		if (j < AFFS_I2HSIZE(inode)) {
+			affs_brelse(bh);
+			return NULL;
+		}
+
+		block -= AFFS_I2HSIZE(inode);
+		key    = htonl(FILE_END(bh->b_data,inode)->extension);
+		if (!key) {
+			key = affs_new_header(inode);
+			if (!key) {
+				affs_brelse(bh);
+				return NULL;
+			}
+			ebh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+			if (!ebh) {
+				affs_free_block(inode->i_sb,key);
+				return NULL;
+			}
+			((struct file_front *)ebh->b_data)->primary_type = ntohl(T_LIST);
+			((struct file_front *)ebh->b_data)->own_key      = ntohl(key);
+			FILE_END(ebh->b_data,inode)->secondary_type      = ntohl(ST_FILE);
+			FILE_END(ebh->b_data,inode)->parent              = ntohl(inode->i_ino);
+			affs_fix_checksum(AFFS_I2BSIZE(inode),ebh->b_data,5);
+			FILE_END(bh->b_data,inode)->extension = ntohl(key);
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+			mark_buffer_dirty(bh,1);
+			affs_brelse(bh);
+			bh = ebh;
+		}
+		affs_brelse(bh);
+		pt = T_LIST;
+		if (ext < EXT_CACHE_SIZE - 1) {
+			inode->u.affs_i.i_ext[ext] = key;
+			inode->u.affs_i.i_max_ext  = ++ext;
+		}
+	}
+	key = htonl(AFFS_BLOCK(bh->b_data,inode,block));
+	affs_brelse(bh);
+	if (!key)
+		return NULL;
+
+	return affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode));
+}
+
+/* This could be made static, regardless of what the former comment said.
+ * You cannot directly read affs directories.
  */
-int affs_file_read(struct inode * inode, struct file * filp,
-		   char * buf, int count)
+
+static int
+affs_file_read_ofs(struct inode *inode, struct file *filp, char *buf, int count)
 {
 	char *start;
 	int left, offset, size, sector;
+	int blocksize;
 	struct buffer_head *bh;
 	void *data;
 
+	pr_debug("AFFS: file_read_ofs(ino=%lu,pos=%lu,%d)\n",inode->i_ino,(long)filp->f_pos,count);
+
 	if (!inode) {
 		printk("affs_file_read: inode = NULL\n");
 		return -EINVAL;
 	}
+	blocksize = AFFS_I2BSIZE(inode) - 24;
 	if (!(S_ISREG(inode->i_mode))) {
-#ifdef DEBUG
-		printk("affs_file_read: mode = %07o\n",inode->i_mode);
-#endif
+		pr_debug("affs_file_read: mode = %07o\n",inode->i_mode);
 		return -EINVAL;
 	}
 	if (filp->f_pos >= inode->i_size || count <= 0)
@@ -141,34 +291,296 @@
 
 	start = buf;
 	for (;;) {
-		left = MIN (inode->i_size - filp->f_pos,
-			    count - (buf - start));
+		left = MIN (inode->i_size - filp->f_pos,count - (buf - start));
 		if (!left)
 			break;
-		sector = affs_smap (inode, filp->f_pos >> AFFS_BLOCK_BITS);
+		sector = affs_bmap(inode,(ULONG)filp->f_pos / blocksize);
 		if (!sector)
 			break;
-		offset = filp->f_pos & (AFFS_BLOCK_SIZE - 1);
-		bh = affs_pread (inode, sector, &data);
+		offset = (ULONG)filp->f_pos % blocksize;
+		bh = affs_bread(inode->i_dev,sector,AFFS_I2BSIZE(inode));
 		if (!bh)
 			break;
-		size = MIN (AFFS_BLOCK_SIZE - offset, left);
+		data = bh->b_data + 24;
+		size = MIN(blocksize - offset,left);
 		filp->f_pos += size;
-		memcpy_tofs (buf, data + offset, size);
+		memcpy_tofs(buf,data + offset,size);
 		buf += size;
-		brelse (bh);
+		affs_brelse(bh);
 	}
 	if (start == buf)
 		return -EIO;
 	return buf - start;
+}
 
-#if 0
-	if (filp->f_pos == 0 && count > 0) {
-		put_fs_byte ('X', buf++);
-		filp->f_pos++;
-		return 1;
+static int
+affs_file_write(struct inode *inode, struct file *filp, const char *buf, int count)
+{
+	off_t			 pos;
+	int			 written;
+	int			 c;
+	int			 blocksize;
+	struct buffer_head	*bh;
+	struct inode		*ino;
+	char			*p;
+
+	pr_debug("AFFS: file_write(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
+		(unsigned long)filp->f_pos,count);
+
+	ino = NULL;
+	if (!inode) {
+		printk("AFFS: file_write(): inode=NULL\n");
+		return -EINVAL;
+	}
+	if (inode->u.affs_i.i_original) {
+		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+		if (!ino) {
+			printk("AFFS: could not follow link from inode %lu to %d\n",
+			       inode->i_ino,inode->u.affs_i.i_original);
+			return -EINVAL;
+		}
+		inode = ino;
+	}
+	if (!S_ISREG(inode->i_mode)) {
+		printk("AFFS: file_write(): mode=%07o\n",inode->i_mode);
+		iput(inode);
+		return -EINVAL;
+	}
+	if (filp->f_flags & O_APPEND) {
+		pos = inode->i_size;
+	} else
+		pos = filp->f_pos;
+	written   = 0;
+	blocksize = AFFS_I2BSIZE(inode);
+
+	while (written < count) {
+		bh = affs_getblock(inode,pos / blocksize);
+		if (!bh) {
+			if (!written)
+				written = -ENOSPC;
+			break;
+		}
+		c = blocksize - (pos % blocksize);
+		if (c > count - written)
+			c = count - written;
+		if (c != blocksize && !buffer_uptodate(bh)) {
+			ll_rw_block(READ,1,&bh);
+			wait_on_buffer(bh);
+			if (!buffer_uptodate(bh)) {
+				affs_brelse(bh);
+				if (!written)
+					written = -EIO;
+				break;
+			}
+		}
+		p = (pos % blocksize) + bh->b_data;
+		memcpy_fromfs(p,buf,c);
+		update_vm_cache(inode,pos,p,c);
+		mark_buffer_uptodate(bh,1);
+		mark_buffer_dirty(bh,0);
+		affs_brelse(bh);
+		pos     += c;
+		written += c;
+		buf     += c;
+	}
+	if (pos > inode->i_size)
+		inode->i_size = pos;
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+	filp->f_pos    = pos;
+	inode->i_dirt  = 1;
+	iput(ino);
+	return written;
+}
+
+static int
+affs_file_write_ofs(struct inode *inode, struct file *filp, const char *buf, int count)
+{
+	pr_debug("AFFS: file_write_ofs(ino=%lu,pos=%lu,count=%d)\n",inode->i_ino,
+		(unsigned long)filp->f_pos,count);
+
+	return -ENOSPC;
+}
+
+void
+affs_truncate(struct inode *inode)
+{
+	struct buffer_head	*bh;
+	struct inode		*ino;
+	LONG	 first;
+	LONG	 block;
+	LONG	 key;
+	LONG	*keyp;
+	LONG	 ekey;
+	LONG	 ptype, stype;
+	int	 freethis;
+	int	 ext;
+
+	pr_debug("AFFS: file_truncate(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+
+	ino = NULL;
+	if (inode->u.affs_i.i_original) {
+		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+		if (!ino) {
+			printk("AFFS: truncate(): cannot follow link from %lu to %u\n",
+			       inode->i_ino,inode->u.affs_i.i_original);
+			return;
+		}
+		inode = ino;
+	}
+	first = (inode->i_size + AFFS_I2BSIZE(inode) - 1) / AFFS_I2BSIZE(inode);
+	ekey  = inode->i_ino;
+	ext   = 0;
+
+	while (ekey) {
+		if (!(bh = affs_bread(inode->i_dev,ekey,AFFS_I2BSIZE(inode)))) {
+			printk("AFFS: truncate(): Can't read block %d\n",ekey);
+			break;
+		}
+		ptype = htonl(((struct file_front *)bh->b_data)->primary_type);
+		stype = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+		if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
+		    LINK_END(bh->b_data,inode)->original == 0) {
+			pr_debug("AFFS: truncate(): dumping link\n");
+			affs_brelse(bh);
+			break;
+		}
+		if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
+			printk("AFFS: truncate(): bad block (ptype=%d, stype=%d)\n",
+			        ptype,stype);
+			affs_brelse(bh);
+			break;
+		}
+		/* Do not throw away file header */
+		freethis = first == 0 && ekey != inode->i_ino;
+		for ( block = first; block < AFFS_I2HSIZE(inode); block++) {
+			keyp = &AFFS_BLOCK(bh->b_data,inode,block);
+			key  = htonl(*keyp);
+			if (key) {
+				*keyp = 0;
+				affs_free_block(inode->i_sb,key);
+			} else {
+				block = AFFS_I2HSIZE(inode);
+				break;
+			}
+		}
+		keyp = &GET_END_PTR(struct file_end,bh->b_data,AFFS_I2BSIZE(inode))->extension;
+		key  = htonl(*keyp);
+		if (first <= AFFS_I2HSIZE(inode)) {
+			((struct file_front *)bh->b_data)->block_count = htonl(first);
+			first = 0;
+			*keyp = 0;
+		} else {
+			first -= AFFS_I2HSIZE(inode);
+		}
+		if (freethis) {		/* Don't bother fixing checksum */
+			affs_brelse(bh);
+			affs_free_block(inode->i_sb,ekey);
+		} else {
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+			mark_buffer_dirty(bh,1);
+			affs_brelse(bh);
+		}
+		ekey = key;
+	}
+	inode->u.affs_i.i_max_ext = 0;		/* invalidate cache */
+	iput(ino);
+}
+
+void
+affs_truncate_ofs(struct inode *inode)
+{
+	struct buffer_head	*bh;
+	struct inode		*ino;
+	LONG	 first;
+	LONG	 block;
+	LONG	 key;
+	LONG	*keyp;
+	LONG	 ekey;
+	LONG	 ptype, stype;
+	int	 freethis;
+	int	 blocksize;
+
+	pr_debug("AFFS: file_truncate_ofs(inode=%ld,size=%lu)\n",inode->i_ino,inode->i_size);
+
+	ino = NULL;
+	if (inode->u.affs_i.i_original) {
+		ino = iget(inode->i_sb,inode->u.affs_i.i_original);
+		if (!ino) {
+			printk("AFFS: truncate(): cannot follow link from %lu to %u\n",
+			       inode->i_ino,inode->u.affs_i.i_original);
+			return;
+		}
+		inode = ino;
+	}
+	blocksize = AFFS_I2BSIZE(inode) - 24;
+	first = (inode->i_size + blocksize - 1) / blocksize;
+	ekey  = inode->i_ino;
+
+	while (ekey) {
+		if (!(bh = affs_bread(inode->i_dev,ekey,AFFS_I2BSIZE(inode)))) {
+			printk("AFFS: truncate(): Can't read block %d\n",ekey);
+			break;
+		}
+		ptype = htonl(((struct file_front *)bh->b_data)->primary_type);
+		stype = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+		if (ekey == inode->i_ino && ptype == T_SHORT && stype == ST_LINKFILE &&
+		    LINK_END(bh->b_data,inode)->original == 0) {
+			pr_debug("AFFS: truncate(): dumping link\n");
+			affs_brelse(bh);
+			break;
+		}
+		if (stype != ST_FILE || (ptype != T_SHORT && ptype != T_LIST)) {
+			printk("AFFS: truncate(): bad block (ptype=%d, stype=%d)\n",
+			        ptype,stype);
+			affs_brelse(bh);
+			break;
+		}
+		/* Do not throw away file header */
+		freethis = first == 0 && ekey != inode->i_ino;
+		for ( block = first; block < AFFS_I2HSIZE(inode); block++) {
+			keyp  = &((struct file_front *)bh->b_data)->
+				 blocks[AFFS_I2HSIZE(inode) - 1 - block];
+			key   = htonl(*keyp);
+			if (key) {
+				*keyp = 0;
+				affs_free_block(inode->i_sb,key);
+			} else {
+				block = AFFS_I2HSIZE(inode);
+				break;
+			}
+		}
+		keyp = &GET_END_PTR(struct file_end,bh->b_data,AFFS_I2BSIZE(inode))->extension;
+		key  = htonl(*keyp);
+		if (first <= AFFS_I2HSIZE(inode)) {
+			((struct file_front *)bh->b_data)->block_count = htonl(first);
+			first = 0;
+			*keyp = 0;
+		} else {
+			first -= AFFS_I2HSIZE(inode);
+		}
+		if (freethis) {		/* Don't bother fixing checksum */
+			affs_brelse(bh);
+			affs_free_block(inode->i_sb,ekey);
+		} else {
+			affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+			mark_buffer_dirty(bh,1);
+			affs_brelse(bh);
+		}
+		ekey = key;
+	}
+	inode->u.affs_i.i_max_ext = 0;		/* invalidate cache */
+	iput(ino);
+}
+
+static void
+affs_release_file(struct inode *inode, struct file *filp)
+{
+	if (filp->f_mode & 2) {		/* Free preallocated blocks */
+		while (inode->u.affs_i.i_pa_cnt) {
+			affs_free_block(inode->i_sb,
+					inode->u.affs_i.i_data[inode->u.affs_i.i_pa_next++]);
+			inode->u.affs_i.i_pa_next &= MAX_PREALLOC - 1;
+			inode->u.affs_i.i_pa_cnt--;
+		}
 	}
-	else
-		return 0;
-#endif
 }
diff -u --recursive --new-file v1.3.98/linux/fs/affs/inode.c linux/fs/affs/inode.c
--- v1.3.98/linux/fs/affs/inode.c	Sat Apr 27 15:19:57 1996
+++ linux/fs/affs/inode.c	Mon May  6 14:36:24 1996
@@ -1,15 +1,19 @@
 /*
  *  linux/fs/affs/inode.c
  *
- *  (C) 1994  Geert Uytterhoeven - Modified for MultiUserFileSystem
+ *  (c) 1996  Hans-Joachim Widmaier - Modified for larger blocks.
  *
  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
- *
+ * 
  *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
  *
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
 #include <linux/stat.h>
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
@@ -17,50 +21,80 @@
 #include <linux/mm.h>
 #include <linux/string.h>
 #include <linux/locks.h>
-
-#include <asm/system.h>
-#include <asm/segment.h>
 #include <linux/errno.h>
-
 #include <linux/genhd.h>
+#include <linux/major.h>
+#include <linux/amigaffs.h>
+#include <asm/system.h>
+#include <asm/segment.h>
 
-#include "amigaffs.h"
-
-extern int check_cdrom_media_change(int, int);
-
-#ifdef LEAK_CHECK
-static int check_malloc = 0;
-static int check_bread = 0;
-#endif
+extern int *blk_size[];
+extern struct timezone sys_tz;
 
 void affs_put_super(struct super_block *sb)
 {
-	lock_super(sb);
+	int	 i;
+
+	pr_debug("affs_put_super()\n");
 
-#ifdef LEAK_CHECK
-	printk("Outstanding mallocs:%d, outstanding buffers: %d\n",
-	       check_malloc, check_bread);
-#endif
+	lock_super(sb);
+	if (!(sb->s_flags & MS_RDONLY)) {
+		for (i = 0; i < sb->u.affs_sb.s_bm_count; i++)
+			affs_brelse(sb->u.affs_sb.s_bitmap[i].bm_bh);
+		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(1);
+		secs_to_datestamp(CURRENT_TIME,
+				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
+		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
+		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
+	}
+	if (sb->u.affs_sb.s_flags & SF_PREFIX)
+		kfree(sb->u.affs_sb.s_prefix);
+	kfree(sb->u.affs_sb.s_bitmap);
+	affs_brelse(sb->u.affs_sb.s_root_bh);
+	set_blocksize(sb->s_dev,BLOCK_SIZE);
 	sb->s_dev = 0;
 	unlock_super(sb);
+	MOD_DEC_USE_COUNT;
 	return;
 }
 
-static struct super_operations affs_sops = {
+static void affs_write_super(struct super_block *sb)
+{
+	int	 i, clean = 2;
+
+	if (!(sb->s_flags & MS_RDONLY)) {
+		for (i = 0, clean = 1; i < sb->u.affs_sb.s_bm_count; i++) {
+			if (buffer_dirty(sb->u.affs_sb.s_bitmap[i].bm_bh)) {
+				clean = 0;
+				break;
+			}
+		}
+		ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->bm_flag = htonl(clean);
+		secs_to_datestamp(CURRENT_TIME,
+				  &ROOT_END_S(sb->u.affs_sb.s_root_bh->b_data,sb)->disk_altered);
+		affs_fix_checksum(sb->s_blocksize,sb->u.affs_sb.s_root_bh->b_data,5);
+		mark_buffer_dirty(sb->u.affs_sb.s_root_bh,1);
+		sb->s_dirt = !clean;	/* redo until bitmap synced */
+	} else
+		sb->s_dirt = 0;
+
+	pr_debug("AFFS: write_super() at %d, clean=%d\n",CURRENT_TIME,clean);
+}
+
+static struct super_operations affs_sops = { 
 	affs_read_inode,
-	NULL,			/* notify_change */
-	NULL,			/* write_inode */
-	NULL,			/* put_inode */
+	affs_notify_change,
+	affs_write_inode,
+	affs_put_inode,	
 	affs_put_super,
-	NULL,			/* write_super */
+	affs_write_super,
 	affs_statfs,
 	NULL			/* remount */
 };
 
 int affs_parent_ino(struct inode *dir)
 {
-	int root_ino = (dir->i_sb->u.affs_sb.s_root_block
-			- dir->i_sb->u.affs_sb.s_partition_offset);
+	int root_ino = (dir->i_sb->u.affs_sb.s_root_block);
 
 	if (!S_ISDIR (dir->i_mode)) {
 		printk ("affs_parent_ino: argument is not a directory\n");
@@ -71,361 +105,854 @@
 	return dir->u.affs_i.i_parent;
 }
 
-static int parse_options(char *options, struct affs_options *optp)
+
+static int
+parse_options(char *options, uid_t *uid, gid_t *gid, int *mode,
+	      int *reserved, int *root,	int *blocksize, char **prefix,
+	      char *volume, unsigned long *mount_opts)
 {
-	char *this_opt,*value,*end;
-	int n;
+	char	*this_char, *value;
+	int	 f;
 
-	optp->offset = 0;
-	optp->size = 0;
-	optp->root = 0;
-	optp->conv_links = 0;
+	/* Fill in defaults */
 
+	*uid        = 0;
+	*gid        = 0;
+	*reserved   = 2;
+	*root       = -1;
+	*blocksize  = -1;
+	*prefix     = "/";
+	volume[0]   = ':';
+	volume[1]   = 0;
+	*mount_opts = 0;
 	if (!options)
 		return 1;
-	for (this_opt = strtok(options,","); this_opt; this_opt = strtok(NULL,",")) {
-		if ((value = strchr(this_opt,'='))) *value++ = 0;
-
-		if (!strcmp(this_opt,"offset") && value) {
-			n = simple_strtoul (value, &end, 10);
-			if (end == value || *end != 0)
+	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
+		f = 0;
+		if ((value = strchr(this_char,'=')) != NULL)
+			*value++ = 0;
+		if (!strcmp(this_char,"protect")) {
+			if (value) {
+				printk("AFFS: option protect does not take an argument\n");
+				return 0;
+			}
+			*mount_opts |= SF_IMMUTABLE;
+		}
+		else if (!strcmp(this_char,"verbose")) {
+			if (value) {
+				printk("AFFS: option verbose does not take an argument\n");
+				return 0;
+			}
+			*mount_opts |= SF_VERBOSE;
+		}
+		else if ((f = !strcmp(this_char,"uid")) || !strcmp(this_char,"setuid")) {
+			if (!value)
+				*uid = current->uid;
+			else if (!*value) {
+				printk("AFFS: argument for uid option missing\n");
+				return 0;
+			} else {
+				*uid = simple_strtoul(value,&value,0);
+				if (*value)
+					return 0;
+				if (!f)
+					*mount_opts |= SF_SETUID;
+			}
+		}
+		else if ((f = !strcmp(this_char,"gid")) || !strcmp(this_char,"setgid")) {
+			if (!value)
+				*gid = current->gid;
+			else if (!*value) {
+				printk("AFFS: argument for gid option missing\n");
+				return 0;
+			} else {
+				*gid = simple_strtoul(value,&value,0);
+				if (*value)
+					return 0;
+				if (!f)
+					*mount_opts |= SF_SETGID;
+			}
+		}
+		else if (!strcmp(this_char,"prefix")) {
+			if (!value) {
+				printk("AFFS: The prefix option requires an argument\n");
+				return 0;
+			}
+			*prefix = kmalloc(strlen(value) + 1,GFP_KERNEL);
+			if (!*prefix)
+				return 0;
+			strcpy(*prefix,value);
+			*mount_opts |= SF_PREFIX;
+		}
+		else if (!strcmp(this_char,"volume")) {
+			if (!value) {
+				printk("AFFS: The volume option requires an argument\n");
+				return 0;
+			}
+			if (strlen(value) > 30)
+				value[30] = 0;
+			strcpy(volume,value);
+		}
+		else if (!strcmp(this_char,"mode")) {
+			if (!value || !*value) {
+				printk("AFFS: The mode option requires an argument\n");
+				return 0;
+			}
+			*mode = simple_strtoul(value,&value,8) & 0777;
+			if (*value)
+				return 0;
+			*mount_opts |= SF_SETMODE;
+		}
+		else if (!strcmp(this_char,"reserved")) {
+			if (!value || !*value) {
+				printk("AFFS: The reserved option requires an argument\n");
+				return 0;
+			}
+			*reserved = simple_strtoul(value,&value,0);
+			if (*value)
 				return 0;
-			optp->offset = n;
 		}
-		else if (!strcmp(this_opt,"size") && value) {
-			n = simple_strtoul (value, &end, 10);
-			if (end == value || *end != 0 || n <= 0)
+		else if (!strcmp(this_char,"root")) {
+			if (!value || !*value) {
+				printk("AFFS: The root option requires an argument\n");
+				return 0;
+			}
+			*root = simple_strtoul(value,&value,0);
+			if (*value)
 				return 0;
-			optp->size = n;
 		}
-		else if (!strcmp(this_opt,"root") && value) {
-			n = simple_strtoul (value, &end, 10);
-			if (end == value || *end != 0 || n <= 0)
+		else if (!strcmp(this_char,"bs")) {
+			if (!value || !*value) {
+				printk("AFFS: The bs option requires an argument\n");
+				return 0;
+			}
+			*blocksize = simple_strtoul(value,&value,0);
+			if (*value)
+				return 0;
+			if (*blocksize != 512 && *blocksize != 1024 && *blocksize != 2048
+			    && *blocksize != 4096) {
+				printk ("AFFS: Invalid blocksize (512, 1024, 2048, 4096 allowed).\n");
 				return 0;
-			optp->root = n;
+			}
 		}
-		else if (!strcmp(this_opt,"conv_symlinks")) {
-			optp->conv_links = 1;
+		/* Silently ignore the quota options */
+		else if (!strcmp (this_char, "grpquota")
+			 || !strcmp (this_char, "noquota")
+			 || !strcmp (this_char, "quota")
+			 || !strcmp (this_char, "usrquota"))
+			;
+		else {
+			printk("AFFS: Unrecognized mount option %s\n",
+			       this_char);
+			return 0;
 		}
-		else return 0;
 	}
 	return 1;
 }
 
-/* Is this The Right Way?  Should I be locking something? */
-
-static int get_device_size (dev_t dev)
+struct super_block *
+affs_read_super(struct super_block *s,void *data, int silent)
 {
-	struct gendisk *gd_p;
-	int dev_size = 0;
+	struct buffer_head	*bh = NULL;
+	struct buffer_head	*bb;
+	kdev_t			 dev = s->s_dev;
+	int			 root_block;
+	int			 size;
+	ULONG			 chksum;
+	ULONG			*bm;
+	LONG			 ptype, stype;
+	int			 mapidx = 0;
+	int			 num_bm;
+	int			 i;
+	int			 key;
+	int			 blocksize;
+	uid_t			 uid;
+	gid_t			 gid;
+	int			 mode, reserved;
+	int			 zm_size;
+	unsigned long		 mount_flags;
+	ULONG			 offset;
 
-	for (gd_p = gendisk_head ; gd_p ; gd_p=gd_p->next) {
-		if (gd_p->major != MAJOR(dev))
-			continue;
-		dev_size = gd_p->part[MINOR(dev)].nr_sects;
-		break;
-	}
-	return dev_size;
-}
+	pr_debug("affs_read_super(%s)\n",(const char *)data);
 
-struct super_block *affs_read_super(struct super_block *s,void *data,
-				    int silent)
-{
-	struct buffer_head *bh;
-	int dev = s->s_dev;
-	int root_block;
-	int ptype, stype;
-	void *root_data;
-	struct affs_options *optp;
+	MOD_INC_USE_COUNT;
 
-	optp = &s->u.affs_sb.s_options;
-
-	if (!parse_options((char *) data, optp)) {
+	if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block,
+	    &blocksize,&s->u.affs_sb.s_prefix,s->u.affs_sb.s_volume,&mount_flags)) {
 		s->s_dev = 0;
-		printk ("AFFS: bad mount options\n");
+		printk("AFFS: error parsing options.\n");
+		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
-
 	lock_super(s);
 
-	root_block = 0;
-	if (optp->size) {
-		s->u.affs_sb.s_partition_size = optp->size;
-	}
-	else {
-		int size = get_device_size (dev);
-		if (size == 0) {
-			s->s_dev = 0;
-			unlock_super(s);
-			printk ("affs_read_super: could not"
-				"determine device size\n");
-		}
-		s->u.affs_sb.s_partition_size = size;
-	}
-
-	s->u.affs_sb.s_partition_offset = optp->offset;
-	root_block = optp->root;
-
-	if (!root_block)
-		root_block = (s->u.affs_sb.s_partition_offset
-			      + s->u.affs_sb.s_partition_size / 2
-			      + (s->u.affs_sb.s_partition_size & 1));
-	s->u.affs_sb.s_root_block = root_block;
-
-	s->u.affs_sb.s_block_size = AFFS_BLOCK_SIZE;
-
-#if 0
-	printk ("affs_read_super: dev=0x%04x offset=%d "
-		"size=%d root=%d blocksize=%d\n",
-		dev,
-		s->u.affs_sb.s_partition_offset,
-		s->u.affs_sb.s_partition_size,
-		s->u.affs_sb.s_root_block,
-		s->u.affs_sb.s_block_size);
-#endif
+	/* Get the size of the device in 512-byte blocks.
+	 * If we later see that the partition uses bigger
+	 * blocks, we will have to change it.
+	 */
+
+	size                      = 2 * blk_size[MAJOR(dev)][MINOR(dev)];
+	s->u.affs_sb.s_bitmap     = NULL;
+	s->u.affs_sb.s_root_bh    = NULL;
+	s->u.affs_sb.s_flags      = mount_flags;
+	s->u.affs_sb.s_mode       = mode;
+	s->u.affs_sb.s_uid        = uid;
+	s->u.affs_sb.s_gid        = gid;
 
-	bh = affs_sread (dev, root_block, &root_data);
-	if (!bh) {
+	if (size == 0) {
 		s->s_dev = 0;
 		unlock_super(s);
-		printk ("AFFS: unable to read superblock\n");
-		return NULL;
+		printk("affs_read_super: could not determine device size\n");
+		goto out;
 	}
+	s->u.affs_sb.s_partition_size = size;
+	s->u.affs_sb.s_reserved       = reserved;
+
+	/* Try to find root block. It's location may depend on the block size. */
 
-	if (affs_checksum_block (AFFS_BLOCK_SIZE, root_data, &ptype, &stype)
-	    || ptype != T_SHORT || stype != ST_ROOT) {
-		printk ("AFFS: invalid root block %d on device 0x%04x\n",
-			root_block, dev);
+	s->u.affs_sb.s_hashsize = 0;
+	if (blocksize > 0) {
+		chksum = blocksize;
+		num_bm = blocksize;
+	} else {
+		chksum = 512;
+		num_bm = 4096;
+	}
+	for (blocksize = chksum; blocksize <= num_bm; blocksize <<= 1, size >>= 1) {
+		if (root_block < 0)
+		    if (MAJOR(dev) == FLOPPY_MAJOR)
+		        s->u.affs_sb.s_root_block = size/4;
+		    else
+			s->u.affs_sb.s_root_block = (reserved + size - 1) / 2;
+		else
+			s->u.affs_sb.s_root_block = root_block;
+		pr_debug("Trying bs=%d bytes, root at %d, size=%d blocks (%d reserved)\n",
+			blocksize,s->u.affs_sb.s_root_block,size,reserved);
+		set_blocksize(dev,blocksize);
+		bh = affs_bread(dev,s->u.affs_sb.s_root_block,blocksize);
+		if (!bh) {
+			printk("AFFS: unable to read root block\n");
+			goto out;
+		}
+		if (!affs_checksum_block(blocksize,bh->b_data,&ptype,&stype) &&
+		    ptype == T_SHORT && stype == ST_ROOT) {
+			s->s_blocksize          = blocksize;
+			s->u.affs_sb.s_hashsize = blocksize / 4 - 56;
+			break;
+		}
+		affs_brelse(bh);
+		bh = NULL;
+	}
+	if (!s->u.affs_sb.s_hashsize) {
+		affs_brelse(bh);
+		if (!silent)
+			printk("AFFS: Can't find a valid root block on device %s\n",kdevname(dev));
 		goto out;
 	}
+	root_block = s->u.affs_sb.s_root_block;
 
-#if 1
-{
-	char *name;
-	int len;
-	char buf[33];
-	len = affs_get_file_name (AFFS_BLOCK_SIZE, root_data, &name);
-	memcpy (buf,name,len);
-	buf[len] = 0;
-#if 0
-	printk ("affs_read_super: volume name \"%s\"\n", buf);
-#endif
-}
-#endif
+	s->u.affs_sb.s_partition_size   = size;
+	s->s_blocksize_bits             = blocksize == 512 ? 9 :
+					  blocksize == 1024 ? 10 :
+					  blocksize == 2048 ? 11 : 12;
+
+	/* Find out which kind of FS we have */
+	bb = affs_bread(dev,0,s->s_blocksize);
+	if (bb) {
+		chksum = htonl(*(ULONG *)bb->b_data);
+		switch (chksum) {
+			case MUFS_FS:
+			case MUFS_INTLFFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+				/* fall thru */
+			case FS_INTLFFS:
+				s->u.affs_sb.s_flags |= SF_INTL;
+				break;
+			case MUFS_FFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+				break;
+			case FS_FFS:
+				break;
+			case MUFS_OFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+				/* fall thru */
+			case FS_OFS:
+				s->u.affs_sb.s_flags |= SF_OFS;
+				break;
+			case MUFS_INTLOFS:
+				s->u.affs_sb.s_flags |= SF_MUFS;
+				/* fall thru */
+			case FS_INTLOFS:
+				s->u.affs_sb.s_flags |= SF_INTL | SF_OFS;
+				break;
+			case FS_DCOFS:
+			case FS_DCFFS:
+			case MUFS_DCOFS:
+			case MUFS_DCFFS:
+				if (!silent)
+					printk("AFFS: Unsupported filesystem on device %s: %08X\n",
+					        kdevname(dev),chksum);
+				if (0)
+			default:
+				printk("AFFS: Unknown filesystem on device %s: %08X\n",
+				       kdevname(dev),chksum);
+				affs_brelse(bb);
+				goto out;
+		}
+		affs_brelse(bb);
+	} else {
+		printk("AFFS: Can't get boot block.\n");
+		goto out;
+	}
+	if (mount_flags & SF_VERBOSE) {
+		chksum = ntohl(chksum);
+		printk("AFFS: Mounting volume \"%*s\": Type=%.3s\\%c, Blocksize=%d\n",
+		       GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[0],
+		       &GET_END_PTR(struct root_end,bh->b_data,blocksize)->disk_name[1],
+		       (char *)&chksum,((char *)&chksum)[3] + '0',blocksize);
+	}
 
 	s->s_magic = AFFS_SUPER_MAGIC;
+	s->s_flags = MS_NODEV | MS_NOSUID;
 
-	s->s_flags = MS_RDONLY | MS_NODEV | MS_NOSUID;
+	/* Keep super block in cache */
+	if (!(s->u.affs_sb.s_root_bh = affs_bread(dev,root_block,s->s_blocksize))) {
+		printk("AFFS: Can't read root block a second time\n");
+		goto out;
+	}
 
-	brelse(bh);
+	/* Allocate space for bitmap pointers and read the bitmap */
+
+	size    = s->u.affs_sb.s_partition_size - reserved;
+	num_bm  = (size + s->s_blocksize * 8 - 32 - 1) / (s->s_blocksize * 8 - 32);
+	zm_size = (num_bm *  (1 << (s->s_blocksize_bits - 8)) + 7) / 8;
+	ptype   = num_bm * sizeof(struct affs_bm_info) + zm_size +
+		  MAX_ZONES * sizeof(struct affs_zone);
+	if (!(s->u.affs_sb.s_bitmap = kmalloc(ptype,GFP_KERNEL))) {
+		printk("AFFS: Can't get memory for bitmap info.\n");
+		goto out;
+	}
+	memset(s->u.affs_sb.s_bitmap,0,ptype);
+
+	if( (ULONG)((UBYTE *)bh->b_data + s->s_blocksize - 200) == 0 ) {
+		if (!(s->s_flags & MS_RDONLY)) {
+			printk("AFFS: Bitmap invalid - mounting %s read only.\n",kdevname(dev));
+			s->s_flags |= MS_RDONLY;
+		}
+		affs_brelse(bh);
+		bh = NULL;
+		goto nobitmap;
+	}
+
+	pr_debug("AFFS: %d bitmap blocks\n",num_bm);
+
+	key    = root_block;
+	ptype  = s->s_blocksize / 4 - 49;
+	stype  = ptype + 25;
+	offset = s->u.affs_sb.s_reserved;
+	while (bh) {
+		bm = (ULONG *)bh->b_data;
+		for (i = ptype; i < stype && bm[i]; i++, mapidx++) {
+			if (mapidx >= num_bm) {
+				printk("AFFS: Not enough bitmap space!?\n");
+				goto out;
+			}
+			bb = affs_bread(s->s_dev,htonl(bm[i]),s->s_blocksize);
+			if (bb) {
+				if (affs_checksum_block(s->s_blocksize,bb->b_data,NULL,NULL) /*&&
+				    !(s->s_flags & MS_RDONLY)*/) {
+					printk("AFFS: Bitmap (%d,key=%lu) invalid - mounting %s read only.\n",
+					       mapidx, htonl(bm[i]),
+					       kdevname(dev));
+					s->s_flags |= MS_RDONLY;
+				}
+				if (size <= s->s_blocksize * 8 - 32) {	/* last bitmap */
+					ptype = size / 32 + 1;		/* word number */
+					key   = size & 0x1F;		/* used bits */
+					if (key) {
+						chksum = ntohl(0x7FFFFFFF >> (31 - key));
+						((ULONG *)bb->b_data)[ptype] &= chksum;
+						affs_fix_checksum(s->s_blocksize,bb->b_data,0);
+						/* no need to mark buffer as dirty */
+					}
+					ptype = (size + 31) & ~0x1F;
+					size  = 0;
+					if (!(s->s_flags & MS_RDONLY))
+						s->u.affs_sb.s_flags |= SF_BM_VALID;
+				} else {
+					ptype = s->s_blocksize * 8 - 32;
+					size -= ptype;
+				}
+				s->u.affs_sb.s_bitmap[mapidx].bm_firstblk = offset;
+				s->u.affs_sb.s_bitmap[mapidx].bm_size     = ptype;
+				s->u.affs_sb.s_bitmap[mapidx].bm_bh       = bb;
+				s->u.affs_sb.s_bitmap[mapidx].bm_free     =
+				  affs_count_free_bits(ptype/8,bb->b_data + 4);
+				offset += ptype;
+			} else {
+				printk("AFFS: Can't read bitmap.\n");
+				goto out;
+			}
+		}
+		key   = htonl(bm[stype]);   /* Next block of bitmap pointers */
+		ptype = 0;
+		stype = s->s_blocksize / 4 - 1;
+		affs_brelse(bh);
+		if (key) {
+			if (!(bh = affs_bread(s->s_dev,key,s->s_blocksize))) {
+				printk("AFFS: Can't read bitmap extension.\n");
+				goto out;
+			}
+		} else
+			bh = NULL;
+	}
+	if (mapidx != num_bm) {
+		printk("AFFS: Got only %d bitmap blocks, expected %d\n",
+		       mapidx, num_bm);
+		goto out;
+	}
+	s->u.affs_sb.s_num_zones = ((num_bm - 1) << (s->s_blocksize_bits - 8))+
+				   (s->u.affs_sb.s_bitmap[num_bm - 1].bm_size + 2047) / 2048;
+nobitmap:
+	s->u.affs_sb.s_bm_count  = mapidx;
+	s->u.affs_sb.s_zones     = (struct affs_zone *)&s->u.affs_sb.s_bitmap[num_bm];
+	s->u.affs_sb.s_zonemap   = (char *)&s->u.affs_sb.s_zones[MAX_ZONES];
 
 	/* set up enough so that it can read an inode */
-	s->s_dev = dev;
-	s->s_op = &affs_sops;
-	s->s_blocksize = AFFS_BUFFER_SIZE;
-	s->s_mounted = iget (s, root_block - s->u.affs_sb.s_partition_offset);
 
+	s->s_dev       = dev;
+	s->s_op        = &affs_sops;
+	s->s_mounted   = iget(s,root_block);
 	unlock_super(s);
 
 	if (!(s->s_mounted)) {
 		s->s_dev = 0;
 		printk("AFFS: get root inode failed\n");
+		MOD_DEC_USE_COUNT;
 		return NULL;
 	}
 
+	/* If the fs is mounted r/w, create data zones, else free bitmaps. */
+
+	if (!(s->s_flags & MS_RDONLY)) {
+		ROOT_END(s->u.affs_sb.s_root_bh->b_data,s->s_mounted)->bm_flag = 0;
+		secs_to_datestamp(CURRENT_TIME,&ROOT_END(s->u.affs_sb.s_root_bh->b_data,
+				  s->s_mounted)->disk_altered);
+		affs_fix_checksum(s->s_blocksize,s->u.affs_sb.s_root_bh->b_data,5);
+		mark_buffer_dirty(s->u.affs_sb.s_root_bh,1);
+		affs_make_zones(s);
+	} else {
+		for (i = 0; i < s->u.affs_sb.s_bm_count; i++) {
+			affs_brelse(s->u.affs_sb.s_bitmap[i].bm_bh);
+			s->u.affs_sb.s_bitmap[i].bm_bh = NULL;
+		}
+	}
+
+
+	pr_debug("AFFS: s_flags=%lX\n", s->s_flags);
 	return s;
 
  out: /* Kick out for various error conditions */
-	brelse (bh);
+	affs_brelse (bh);
+	affs_brelse(s->u.affs_sb.s_root_bh);
+	if (s->u.affs_sb.s_bitmap) {
+		for (i = 0; i < mapidx; i++)
+			affs_brelse(s->u.affs_sb.s_bitmap[i].bm_bh);
+		kfree(s->u.affs_sb.s_bitmap);
+	}
 	s->s_dev = 0;
 	unlock_super(s);
+	MOD_DEC_USE_COUNT;
 	return NULL;
 }
 
-void affs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz)
+void
+affs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
 {
-#ifdef DEBUG
-	printk ("AFFS: affs_statfs called\n");
-#endif
-	put_fs_long(AFFS_SUPER_MAGIC, &buf->f_type);
-	put_fs_long(sb->u.affs_sb.s_block_size, &buf->f_bsize);
-	put_fs_long(sb->u.affs_sb.s_partition_size, &buf->f_blocks);
-	put_fs_long(0, &buf->f_bfree);
-	put_fs_long(0, &buf->f_bavail);
-	put_fs_long(0, &buf->f_files);
-	put_fs_long(0, &buf->f_ffree);
-	/* Don't know what value to put in buf->f_fsid */
-}
-
-static int prot_table[9][2] = {
-	{PROT_OTR_EXECUTE, PROT_OTR_EXECUTE},	/* other: 1 = allowed */
-	{PROT_OTR_WRITE, PROT_OTR_WRITE},
-	{PROT_OTR_READ, PROT_OTR_READ},
-	{PROT_GRP_EXECUTE, PROT_GRP_EXECUTE},	/* group: 1 = allowed */
-	{PROT_GRP_WRITE, PROT_GRP_WRITE},
-	{PROT_GRP_READ, PROT_GRP_READ},
-	{PROT_EXECUTE, 0},			/* owner: 0 = allowed */
-	{PROT_WRITE, 0},
-	{PROT_READ, 0}
-};
+	ULONG		 free;
+	struct statfs	 tmp;
+
+	pr_debug("AFFS: statfs() partsize=%d, reserved=%d\n",
+		sb->u.affs_sb.s_partition_size, sb->u.affs_sb.s_reserved);
 
-void affs_read_inode(struct inode * inode)
+	free          = affs_count_free_blocks(sb);
+	tmp.f_type    = AFFS_SUPER_MAGIC;
+	tmp.f_bsize   = sb->s_blocksize;
+	tmp.f_blocks  = sb->u.affs_sb.s_partition_size - sb->u.affs_sb.s_reserved;
+	tmp.f_bfree   = free;
+	tmp.f_bavail  = free;
+	tmp.f_files   = 0;
+	tmp.f_ffree   = 0;
+	memcpy_tofs(buf,&tmp,bufsiz);
+}
+
+void
+affs_read_inode(struct inode *inode)
 {
-	struct buffer_head *bh;
-	int block;
-	void *fh_data;
-	struct file_front *file_front;
-	struct file_end *file_end;
-	int i;
-	struct hardlink_end *link_end;
-	int link;	
-
-#ifdef DEBUG
-	printk ("AFFS: entering affs_read_inode\n");
-#endif
-
-	inode->i_nlink = 1; /* at least */
-	do {
-		link = 0;
-		block = inode->i_ino;
-		if (!(bh=affs_pread (inode, block, &fh_data))) {
-			printk("AFFS: unable to read i-node block %d\n", block);
-			return;
-		}
-
-		file_front = (struct file_front *) fh_data;
-		file_end = GET_END_PTR (struct file_end, fh_data, /* coincidently the same as  dir_end */
-					AFFS_I2BSIZE (inode));
-
-		/* don't use bitmap data for mode, uid & gid of the rootblock */
-		if (block == inode->i_sb->u.affs_sb.s_root_block) {
-			inode->u.affs_i.i_protect = 0;
-			inode->u.affs_i.i_parent = block;
-
-			inode->i_mode = S_IRWXUGO | S_IFDIR | S_ISVTX ;  /* drwxrwxrwt */
-			inode->i_nlink = 2; /* at least ..... */
-
-			inode->i_size = 0;  /* some different idea ? */
-
-			inode->i_uid = 0;
-			inode->i_gid = 0;
-		}
-		else { 
-
-			inode->u.affs_i.i_protect = file_end->protect;
-			inode->u.affs_i.i_parent = swap_long (file_end->parent);
-
-			inode->i_mode = 0;
-			for (i = 0; i < 9; i++)
-				if ((prot_table[i][0] & inode->u.affs_i.i_protect) == prot_table[i][1])
-					inode->i_mode |= 1<<i;
-			switch(swap_long(file_end->secondary_type)) {
-				case ST_USERDIR:
-					inode->i_mode |= ((inode->i_mode & 0444)>>2) | S_IFDIR;
-
-					inode->i_nlink++; /* There are always at least 2.  It is
-							       hard to figure out what is correct*/
-					inode->i_size = 0;
-				break;
-				case ST_SOFTLINK:
-					inode->i_mode |= S_IFLNK;
-					inode->i_size  = 0;
-				break;
-				case ST_LINKFILE:   /* doing things very easy (not really correct) */
-				case ST_LINKDIR:    /* code is _very_ inefficient (see below) */
+	struct buffer_head	*bh, *lbh;
+	struct file_front	*file_front;
+	struct file_end		*file_end;
+	LONG			 block;
+	ULONG			 prot;
+	LONG			 ptype, stype;
+	unsigned short		 id;
+
+	pr_debug("AFFS: read_inode(%lu)\n",inode->i_ino);
+	lbh   = NULL;
+	block = inode->i_ino;
+	if (!(bh = affs_bread(inode->i_dev,block,AFFS_I2BSIZE(inode)))) {
+		printk("AFFS: unable to read i-node block %d\n",block);
+		return;
+	}
+	if (affs_checksum_block(AFFS_I2BSIZE(inode),bh->b_data,&ptype,&stype) || ptype != T_SHORT) {
+		printk("AFFS: read_inode(): checksum or type (ptype=%d) error on inode %d\n",
+		       ptype, block);
+		affs_brelse(bh);
+		return;
+	}
 
-				  /* Where is struct link_end defined?
-				     ... I don't know what is going on
-				     here, someone else should
-				     probably spend some time on this */
-					link_end = (struct hardlink_end *)file_end;
-					inode->i_ino = link_end->original;
-					inode->i_nlink += 2; /* It's hard to say what's correct */
-					brelse(bh);
-					link = 1;
-				break;
-				default:
-					printk("affs: unknown secondary type %ld; assuming file\n",
-						file_end->secondary_type);
-				case ST_FILE:
-					inode->i_mode |= S_IFREG;
-					inode->i_size = swap_long (file_end->byte_size);
-				break;
-				}
-			if (file_end->uid == 0xffff)
-				inode->i_uid = 0;	/* root uid */
-			else if (file_end->uid == 0x0000) {
-				umode_t mode;
-				inode->i_uid = -1;	/* unknown uid */
-
-				/*
-				 * change the mode of the inode to duplicate the
-				 * perms of the user in the group and other fields;
-				 * the assumption is that this isn't a MultiUser
-				 * filesystem/file, so the permissions should be
-				 * the same for all users
-				 */
-				mode = (inode->i_mode >> 6) & 7;
-				inode->i_mode |= (mode << 3) | (mode);
-			} else
-				inode->i_uid = file_end->uid;
-			if (file_end->gid == 0xffff)
-				inode->i_gid = 0;	/* root gid */
-			else if (file_end->gid == 0x0000)
-				inode->i_gid = -1;	/* unknown gid */
-			else
-				inode->i_gid = file_end->gid;
+	file_front = (struct file_front *)bh->b_data;
+	file_end   = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
+	prot       = (htonl(file_end->protect) & ~0x10) ^ FIBF_OWNER;
+
+	inode->u.affs_i.i_protect  = prot;
+	inode->u.affs_i.i_parent   = htonl(file_end->parent);
+	inode->u.affs_i.i_original = 0;	
+	inode->u.affs_i.i_zone     = 0;
+	inode->u.affs_i.i_hlink    = 0;
+	inode->u.affs_i.i_pa_cnt   = 0;
+	inode->u.affs_i.i_pa_next  = 0;
+	inode->u.affs_i.i_pa_last  = 0;
+	inode->u.affs_i.i_ext[0]   = 0;
+	inode->u.affs_i.i_max_ext  = 0;
+	inode->i_nlink             = 1;
+	inode->i_mode              = 0;
+
+	if (inode->i_sb->u.affs_sb.s_flags & SF_SETMODE)
+		inode->i_mode = inode->i_sb->u.affs_sb.s_mode;
+	else
+		inode->i_mode = prot_to_mode(prot);
+	
+	if (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)
+		inode->i_uid = inode->i_sb->u.affs_sb.s_uid;
+	else {
+		id = htons(file_end->owner_uid);
+		if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+			if (id == 0 || id == 0xFFFF)
+				id ^= ~0;
 		}
+		inode->i_uid = id;
+	}
+	if (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)
+		inode->i_gid = inode->i_sb->u.affs_sb.s_gid;
+	else {
+		id = htons(file_end->owner_gid);
+		if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+			if (id == 0 || id == 0xFFFF)
+				id ^= ~0;
+		}
+		inode->i_gid = id;
 	}
-	while (link);
-
-#ifdef DEBUG
-	printk ("AFFS: read inode %d: size=%d\n", block, inode->i_size);
-#endif
-	inode->i_mtime = inode->i_atime = inode->i_ctime
-		= (swap_long (file_end->created.ds_Days) * (24 * 60 * 60)
-		   + swap_long (file_end->created.ds_Minute) * 60
-		   + swap_long (file_end->created.ds_Tick) / 50
-		   + ((8 * 365 + 2) * 24 * 60 * 60));
 
-	brelse(bh);
+	switch (htonl(file_end->secondary_type)) {
+		case ST_ROOT:
+			inode->i_uid   = inode->i_sb->u.affs_sb.s_uid;
+			inode->i_gid   = inode->i_sb->u.affs_sb.s_gid;
+		case ST_USERDIR:
+			if (htonl(file_end->secondary_type) == ST_USERDIR ||
+			    inode->i_sb->u.affs_sb.s_flags & SF_SETMODE) {
+				if (inode->i_mode & S_IRUSR)
+					inode->i_mode |= S_IXUSR;
+				if (inode->i_mode & S_IRGRP)
+					inode->i_mode |= S_IXGRP;
+				if (inode->i_mode & S_IROTH)
+					inode->i_mode |= S_IXOTH;
+				inode->i_mode |= S_IFDIR;
+			} else
+				inode->i_mode = S_IRUGO | S_IXUGO | S_IWUSR | S_IFDIR;
+			inode->i_size  = 0;
+			break;
+		case ST_LINKDIR:
+			inode->u.affs_i.i_original = htonl(file_end->original);
+			inode->u.affs_i.i_hlink    = 1;
+			inode->i_mode             |= S_IFDIR;
+			inode->i_size              = 0;
+			break;
+		case ST_LINKFILE:
+			inode->u.affs_i.i_original = htonl(file_end->original);
+			inode->u.affs_i.i_hlink    = 1;
+			if (!(lbh = affs_bread(inode->i_dev,inode->u.affs_i.i_original,
+			                  AFFS_I2BSIZE(inode)))) {
+				affs_brelse(bh);
+				printk("AFFS: unable to read i-node block %ld\n",inode->i_ino);
+				return;
+			}
+			file_end = GET_END_PTR(struct file_end,lbh->b_data,AFFS_I2BSIZE(inode));
+		case ST_FILE:
+			inode->i_mode |= S_IFREG;
+			inode->i_size  = htonl(file_end->byte_size);
+			break;
+		case ST_SOFTLINK:
+			inode->i_mode |= S_IFLNK;
+			inode->i_size  = 0;
+			break;
+	}
 
+	inode->i_mtime = inode->i_atime = inode->i_ctime
+		       = (htonl(file_end->created.ds_Days) * (24 * 60 * 60) +
+		         htonl(file_end->created.ds_Minute) * 60 +
+			 htonl(file_end->created.ds_Tick) / 50 +
+			 ((8 * 365 + 2) * 24 * 60 * 60)) +
+			 sys_tz.tz_minuteswest * 60;
+	affs_brelse(bh);
+	affs_brelse(lbh);
+	
 	inode->i_op = NULL;
-	if (S_ISREG(inode->i_mode))
-		inode->i_op = &affs_file_inode_operations;
-	else if (S_ISDIR(inode->i_mode))
+	if (S_ISREG(inode->i_mode)) {
+		if (inode->i_sb->u.affs_sb.s_flags & SF_OFS)
+			inode->i_op = &affs_file_inode_operations_ofs;
+		else
+			inode->i_op = &affs_file_inode_operations;
+	} else if (S_ISDIR(inode->i_mode))
 		inode->i_op = &affs_dir_inode_operations;
 	else if (S_ISLNK(inode->i_mode))
 		inode->i_op = &affs_symlink_inode_operations;
 }
 
+void
+affs_write_inode(struct inode *inode)
+{
+	struct buffer_head *bh;
+	struct file_end	   *file_end;
+	short		    uid, gid;
 
-#ifdef LEAK_CHECK
-#undef malloc
-#undef free_s
-#undef bread
-#undef brelse
+	pr_debug("AFFS: write_inode(%lu)\n",inode->i_ino);
 
-void * leak_check_malloc(unsigned int size){
-  void * tmp;
-  check_malloc++;
-  tmp = kmalloc(size, GFP_ATOMIC);
-  return tmp;
+	inode->i_dirt = 0;
+	if (!inode->i_nlink)
+		return;
+	if (!(bh = bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode)))) {
+		printk("AFFS: Unable to read block of inode %ld on %s\n",
+		       inode->i_ino,kdevname(inode->i_dev));
+		return;
+	}
+	file_end = GET_END_PTR(struct file_end, bh->b_data,AFFS_I2BSIZE(inode));
+	if (file_end->secondary_type == htonl(ST_ROOT)) {
+		secs_to_datestamp(inode->i_mtime,&ROOT_END(bh->b_data,inode)->disk_altered);
+	} else {
+		file_end->protect   = ntohl(inode->u.affs_i.i_protect ^ FIBF_OWNER);
+		file_end->byte_size = ntohl(inode->i_size);
+		secs_to_datestamp(inode->i_mtime,&file_end->created);
+		if (!(inode->i_ino == inode->i_sb->u.affs_sb.s_root_block)) {
+			uid = inode->i_uid;
+			gid = inode->i_gid;
+			if (inode->i_sb->u.affs_sb.s_flags & SF_MUFS) {
+				if (inode->i_uid == 0 || inode->i_uid == 0xFFFF)
+					uid = inode->i_uid ^ ~0;
+				if (inode->i_gid == 0 || inode->i_gid == 0xFFFF)
+					gid = inode->i_gid ^ ~0;
+			}
+			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETUID))
+				file_end->owner_gid = ntohs(uid);
+			if (!(inode->i_sb->u.affs_sb.s_flags & SF_SETGID))
+				file_end->owner_gid = ntohs(gid);
+		}
+	}
+	affs_fix_checksum(AFFS_I2BSIZE(inode),bh->b_data,5);
+	mark_buffer_dirty(bh,1);
+	brelse(bh);
 }
 
-void leak_check_free_s(void * obj, int size){
-  check_malloc--;
-  return kfree_s(obj, size);
+int
+affs_notify_change(struct inode *inode, struct iattr *attr)
+{
+	int error;
+
+	pr_debug("AFFS: notify_change(%lu,0x%x)\n",
+		inode->i_ino,attr->ia_valid);
+
+	error = inode_change_ok(inode,attr);
+	if (error)
+		return error;
+	
+	if (((attr->ia_valid & ATTR_UID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETUID)) ||
+	    ((attr->ia_valid & ATTR_GID) && (inode->i_sb->u.affs_sb.s_flags & SF_SETGID)) ||
+	    ((attr->ia_valid & ATTR_MODE) &&
+	     (inode->i_sb->u.affs_sb.s_flags & (SF_SETMODE | SF_IMMUTABLE))))
+		error = -EPERM;
+	
+	if (error)
+		return (inode->i_sb->u.affs_sb.s_flags & SF_QUIET) ? 0 : error;
+	
+	if (attr->ia_valid & ATTR_MODE)
+		inode->u.affs_i.i_protect = mode_to_prot(attr->ia_mode);
+
+	inode_setattr(inode,attr);
+
+	return 0;
 }
 
-struct buffer_head * leak_check_bread(int dev, int block, int size){
-  check_bread++;
-  return bread(dev, block, size);
+void
+affs_put_inode(struct inode *inode)
+{
+	pr_debug("AFFS: put_inode(ino=%lu, nlink=%u)\n",
+		inode->i_ino,inode->i_nlink);
+	if (inode->i_nlink) {
+		return;
+	}
+	inode->i_size = 0;
+	if (S_ISREG(inode->i_mode) && !inode->u.affs_i.i_hlink)
+		affs_truncate(inode);
+	affs_free_block(inode->i_sb,inode->i_ino);
+	clear_inode(inode);
 }
 
-void leak_check_brelse(struct buffer_head * bh){
-  check_bread--;
-  return brelse(bh);
+struct inode *
+affs_new_inode(const struct inode *dir)
+{
+	struct inode		*inode;
+	struct super_block	*sb;
+	ULONG			 block;
+
+	if (!dir || !(inode = get_empty_inode()))
+		return NULL;
+	
+	sb = dir->i_sb;
+	inode->i_sb    = sb;
+	inode->i_flags = sb->s_flags;
+
+	if (!(block = affs_new_header((struct inode *)dir))) {
+		iput(inode);
+		return NULL;
+	}
+
+	inode->i_count   = 1;
+	inode->i_nlink   = 1;
+	inode->i_dev     = sb->s_dev;
+	inode->i_uid     = current->fsuid;
+	inode->i_gid     = current->fsgid;
+	inode->i_dirt    = 1;
+	inode->i_ino     = block;
+	inode->i_op      = NULL;
+	inode->i_blocks  = 0;
+	inode->i_size    = 0;
+	inode->i_mode    = 0;
+	inode->i_blksize = 0;
+	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+
+	inode->u.affs_i.i_original = 0;
+	inode->u.affs_i.i_parent   = dir->i_ino;
+	inode->u.affs_i.i_zone     = 0;
+	inode->u.affs_i.i_hlink    = 0;
+	inode->u.affs_i.i_pa_cnt   = 0;
+	inode->u.affs_i.i_pa_next  = 0;
+	inode->u.affs_i.i_pa_last  = 0;
+	inode->u.affs_i.i_ext[0]   = 0;
+	inode->u.affs_i.i_max_ext  = 0;
+
+	insert_inode_hash(inode);
+
+	return inode;
+}
+
+int
+affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
+	       const char *name, int len, LONG type)
+{
+	struct buffer_head	*dir_bh;
+	struct buffer_head	*inode_bh;
+	struct buffer_head	*link_bh;
+	ULONG			 hash;
+
+	pr_debug("AFFS: add_entry(dir=%lu,inode=%lu,\"%*s\",type=%ld\n",
+		dir->i_ino,inode->i_ino, len,name,type);
+
+	dir_bh     = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
+	inode_bh   = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	link_bh    = NULL;
+	if (!dir_bh || !inode_bh) {
+		affs_brelse(dir_bh);
+		affs_brelse(inode_bh);
+		return -ENOSPC;
+	}
+	if (link) {
+		link_bh = affs_bread(link->i_dev,link->i_ino,AFFS_I2BSIZE(link));
+		if (!link_bh) {
+			affs_brelse(dir_bh);
+			affs_brelse(inode_bh);
+			return -EINVAL;
+		}
+	}
+	((struct dir_front *)inode_bh->b_data)->primary_type = ntohl(T_SHORT);
+	((struct dir_front *)inode_bh->b_data)->own_key      = ntohl(inode->i_ino);
+
+	if (len > 30)		/* truncate name quietly */
+		len = 30;
+	DIR_END(inode_bh->b_data,inode)->dir_name[0] = len;
+	strncpy(DIR_END(inode_bh->b_data,inode)->dir_name + 1,name,len);
+	DIR_END(inode_bh->b_data,inode)->secondary_type = ntohl(type);
+	DIR_END(inode_bh->b_data,inode)->parent         = ntohl(dir->i_ino);
+	hash = affs_hash_name(name,len,AFFS_I2FSTYPE(dir),AFFS_I2HSIZE(dir));
+
+	lock_super(inode->i_sb);
+	DIR_END(inode_bh->b_data,inode)->hash_chain = 
+			 ((struct dir_front *)dir_bh->b_data)->hashtable[hash];
+	((struct dir_front *)dir_bh->b_data)->hashtable[hash] = ntohl(inode->i_ino);
+	if (link_bh) {
+		LINK_END(inode_bh->b_data,inode)->original   = ntohl(link->i_ino);
+		LINK_END(inode_bh->b_data,inode)->link_chain = 
+				    FILE_END(link_bh->b_data,link)->link_chain;
+		FILE_END(link_bh->b_data,link)->link_chain   = ntohl(inode->i_ino);
+		affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5);
+		link->i_version = ++event;
+		link->i_dirt    = 1;
+		mark_buffer_dirty(link_bh,1);
+	}
+	affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5);
+	affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5);
+	dir->i_version = ++event;
+	dir->i_mtime   = dir->i_atime = dir->i_ctime = CURRENT_TIME;
+	unlock_super(inode->i_sb);
+
+	dir->i_dirt    = 1;
+	inode->i_dirt  = 1;
+	mark_buffer_dirty(dir_bh,1);
+	mark_buffer_dirty(inode_bh,1);
+	affs_brelse(dir_bh);
+	affs_brelse(inode_bh);
+	affs_brelse(link_bh);
+
+	return 0;
+}
+
+static struct file_system_type affs_fs_type = {
+	affs_read_super,
+	"affs",
+	1,
+	NULL
+};
+
+int
+init_affs_fs(void)
+{
+	return register_filesystem(&affs_fs_type);
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+	int	 status;
+	if ((status = init_affs_fs()) == 0)
+		register_symtab(0);
+	return status;
+}
+
+void
+cleanup_module(void)
+{
+	unregister_filesystem(&affs_fs_type);
 }
 
 #endif
diff -u --recursive --new-file v1.3.98/linux/fs/affs/namei.c linux/fs/affs/namei.c
--- v1.3.98/linux/fs/affs/namei.c	Tue Apr 23 13:57:11 1996
+++ linux/fs/affs/namei.c	Mon May  6 14:36:05 1996
@@ -1,187 +1,736 @@
 /*
  *  linux/fs/affs/namei.c
  *
- *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
+ *  (c) 1996  Hans-Joachim Widmaier - Heavily hacked up.
  *
- *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
+ *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
  *
  *  (C) 1991  Linus Torvalds - minix filesystem
  */
 
 #include <linux/sched.h>
 #include <linux/affs_fs.h>
+#include <linux/amigaffs.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/fcntl.h>
+#include <linux/locks.h>
 #include <asm/segment.h>
 
 #include <linux/errno.h>
 
+/* Simple toupper() for DOS\1 */
 
-static inline int namecompare(int len, int maxlen,
-	const char * name, const char * buffer)
+static inline unsigned int
+affs_toupper(unsigned int ch)
 {
-	if (len >= maxlen || !buffer[len]) {
-		return strncmp (name, buffer, len) == 0;
-	}
-	return 0;
+	return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
+}
+
+/* International toupper() for DOS\3 */
+
+static inline unsigned int
+affs_intl_toupper(unsigned int ch)
+{
+	return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
+		&& ch <= 0xFE && ch != 0xF7) ?
+		ch - ('a' - 'A') : ch;
 }
 
 /*
- * ok, we cannot use strncmp, as the name is not in our data space.
- * Thus we'll have to use affs_match. No big problem. Match also makes
- * some sanity tests.
- *
  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
  */
-static int affs_match(int len,const char * name, char * compare, int dlen)
+
+static int
+affs_match(const char *name, int len, const char *compare, int dlen, int intl)
 {
-	if (!compare) return 0;
+	if (!compare)
+		return 0;
+
+	if (len > 30)
+		len = 30;
+	if (dlen > 30)
+		dlen = 30;
 
 	/* "" means "." ---> so paths like "/usr/lib//libc.a" work */
 	if (!len && dlen == 1 && compare[0] == '.')
 		return 1;
-	
-#if 0
-	if (len <= 2) printk("Match: %d %d %s %d %d \n",len,dlen,compare,de->name[0], dlen);
-#endif
-	
-	return namecompare(len,dlen,name,compare);
+	if (dlen != len)
+		return 0;
+	if (intl) {
+		while (dlen--) {
+			if (affs_intl_toupper(*name & 0xFF) != affs_intl_toupper(*compare & 0xFF))
+				return 0;
+			name++;
+			compare++;
+		}
+	} else {
+		while (dlen--) {
+			if (affs_toupper(*name & 0xFF) != affs_toupper(*compare & 0xFF))
+				return 0;
+			name++;
+			compare++;
+		}
+	}
+	return 1;
 }
 
-/* Avoid pulling in ctype stuff. */
-
-static int affs_toupper (int ch)
+int
+affs_hash_name(const char *name, int len, int intl, int hashsize)
 {
-	if (ch >= 'a' && ch <= 'z')
-		ch -= ('a' - 'A');
-	return ch;
-}
+	unsigned int i, x;
 
-static int affs_hash_name (const char *name, int len)
-{
-	int i, x;
+	if (len > 30)
+		len = 30;
 
 	x = len;
 	for (i = 0; i < len; i++)
-		x = (x * 13 + affs_toupper (name[i])) & 0x7ff;
-	return x % 72;  /* FIXME: Assumes 512 byte blocks. */
+		if (intl)
+			x = (x * 13 + affs_intl_toupper(name[i] & 0xFF)) & 0x7ff;
+		else
+			x = (x * 13 + affs_toupper(name[i] & 0xFF)) & 0x7ff;
+
+	return x % hashsize;
 }
 
-static struct buffer_head *affs_find_entry(struct inode *dir,
-	const char *name, int namelen, int *ino)
+static struct buffer_head *
+affs_find_entry(struct inode *dir, const char *name, int namelen,
+		unsigned long *ino)
 {
 	struct buffer_head *bh;
-	void *dir_data;
-	int key;
+	int	 intl;
+	ULONG	 key;
 
-	*ino = 0;
+	pr_debug("AFFS: find_entry(%.*s)=\n",namelen,name);
 
-	bh = affs_pread (dir, dir->i_ino, &dir_data);
+	intl = AFFS_I2FSTYPE(dir);
+	bh   = affs_bread(dir->i_dev,dir->i_ino,AFFS_I2BSIZE(dir));
 	if (!bh)
 		return NULL;
 
-	if (affs_match (namelen, name, ".", 1)) {
+	if (affs_match(name,namelen,".",1,intl)) {
 		*ino = dir->i_ino;
 		return bh;
 	}
-	if (affs_match (namelen, name, "..", 2)) {
-		*ino = affs_parent_ino (dir);
+	if (affs_match(name,namelen,"..",2,intl)) {
+		*ino = affs_parent_ino(dir);
 		return bh;
 	}
-	key = affs_get_key_entry (AFFS_I2BSIZE (dir), dir_data,
-				  affs_hash_name (name, namelen));
+
+	key = AFFS_GET_HASHENTRY(bh->b_data,affs_hash_name(name,namelen,intl,AFFS_I2HSIZE(dir)));
 
 	for (;;) {
 		char *cname;
 		int cnamelen;
 
-		brelse (bh);
-		if (key <= 0)
-			return NULL;
-		bh = affs_pread (dir, key, &dir_data);
+		affs_brelse(bh);
+		if (key == 0) {
+			bh = NULL;
+			break;
+		}
+		bh = affs_bread(dir->i_dev,key,AFFS_I2BSIZE(dir));
 		if (!bh)
-			return NULL;
-		cnamelen = affs_get_file_name (AFFS_I2BSIZE (dir),
-					       dir_data, &cname);
-		if (affs_match (namelen, name, cname, cnamelen))
 			break;
-		key = affs_get_fh_hash_link (AFFS_I2BSIZE (dir), dir_data);
+		cnamelen = affs_get_file_name(AFFS_I2BSIZE(dir),bh->b_data,&cname);
+		if (affs_match(name,namelen,cname,cnamelen,intl))
+			break;
+		key = htonl(FILE_END(bh->b_data,dir)->hash_chain);
 	}
-		
+	pr_debug("%lu\n",key);
 	*ino = key;
-
 	return bh;
 }
 
-int affs_lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result)
+int
+affs_lookup(struct inode *dir, const char *name, int len, struct inode **result)
 {
-	int ino;
+	int res;
+	unsigned long ino;
 	struct buffer_head *bh;
 
+	pr_debug("AFFS: lookup(%.*s)\n",len,name);
+
 	*result = NULL;
 	if (!dir)
 		return -ENOENT;
 
-#ifdef DEBUG
-	printk ("lookup: %d %d\n", dir->i_ino, len);
-#endif
+	res = -ENOENT;
+	if (S_ISDIR(dir->i_mode)) {
+		if ((bh = affs_find_entry(dir,name,len,&ino))) {
+			if (FILE_END(bh->b_data,dir)->original)
+				ino = htonl(FILE_END(bh->b_data,dir)->original);
+			affs_brelse(bh);
+			if ((*result = iget(dir->i_sb,ino))) 
+				res = 0;
+			else
+				res = -EACCES;
+		}
+	}
+	iput(dir);
+	return res;
+}
+
+int
+affs_unlink(struct inode *dir, const char *name, int len)
+{
+	int			 retval;
+	struct buffer_head	*bh;
+	unsigned long		 ino;
+	struct inode		*inode;
+
+	pr_debug("AFFS: unlink(dir=%ld,\"%.*s\")\n",dir->i_ino,len,name);
+
+	bh      = NULL;
+	inode   = NULL;
+	retval  = -ENOENT;
+	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+		goto unlink_done;
+	}
+	if (!(inode = iget(dir->i_sb,ino))) {
+		goto unlink_done;
+	}
+	if (S_ISDIR(inode->i_mode)) {
+		retval = -EPERM;
+		goto unlink_done;
+	}
+
+	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
+					 AFFS_I2HSIZE(dir)) + 6,ino,
+					 FILE_END(bh->b_data,dir)->hash_chain)))
+		goto unlink_done;
+
+	if ((retval = affs_fixup(bh,inode)))
+		goto unlink_done;
+
+	inode->i_nlink=0;
+	inode->i_dirt=1;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_version = ++event;
+	dir->i_dirt=1;
+unlink_done:
+	affs_brelse(bh);
+	iput(inode);
+	iput(dir);
+	return retval;
+}
+
+int
+affs_create(struct inode *dir, const char *name, int len, int mode, struct inode **result)
+{
+	struct inode	*inode;
+	int		 error;
+	
+	pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
+
 
-	if (!S_ISDIR(dir->i_mode)) {
+	*result = NULL;
+
+	if (!dir || !dir->i_sb) {
 		iput(dir);
-		return -ENOENT;
+		return -EINVAL;
 	}
-	if (!(bh = affs_find_entry(dir, name, len, &ino))) {
-	  iput(dir);
-	  return -ENOENT;
+	inode = affs_new_inode(dir);
+	if (!inode) {
+		iput (dir);
+		return -ENOSPC;
 	}
-	brelse(bh);
-	if (!(*result = iget(dir->i_sb, ino))) {
+	inode->i_op   = &affs_file_inode_operations;
+	inode->i_mode = mode;
+	error         = affs_add_entry(dir,NULL,inode,name,len,ST_FILE);
+	if (error) {
 		iput(dir);
-		return -EACCES;
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+		iput(inode);
+		return -ENOSPC;
 	}
-	iput (dir);
+	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+
+	iput(dir);
+	*result = inode;
+
 	return 0;
+}
 
-#if 0
-	ino = 0;
-	while(cache.lock);
-	cache.lock = 1;
-	if (dir->i_dev == cache.dev && 
-	    dir->i_ino == cache.dir &&
-	    len == cache.dlen && 
-	    affs_match(len, name, cache.filename, cache.dlen))
-	  {
-	    ino = cache.ino;
-	    ino_back = dir->i_ino;
-	    /* These two cases are special, but since they are at the start
-	       of the directory, we can just as easily search there */
-	    if (cache.dlen == 1 && cache.filename[0] == '.') ino = 0;
-	    if (cache.dlen == 2 && cache.filename[0] == '.' && 
-		cache.filename[1] == '.') ino = 0;
-	  };
-	cache.lock = 0;
-
-	if (!ino) {
-	  if (!(bh = affs_find_entry(dir,name,len, &ino, &ino_back))) {
-	    iput(dir);
-	    return -ENOENT;
-	  }
-	  brelse(bh);
-	};
+int
+affs_mkdir(struct inode *dir, const char *name, int len, int mode)
+{
+	struct inode		*inode;
+	struct buffer_head	*bh;
+	unsigned long		 i;
+	int			 error;
+	
+	pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,len,name,mode);
 
-	if (!(*result = iget(dir->i_sb,ino))) {
+	if (!dir || !dir->i_sb) {
+		iput(dir);
+		return -EINVAL;
+	}
+	bh = affs_find_entry(dir,name,len,&i);
+	if (bh) {
+		affs_brelse(bh);
 		iput(dir);
-		return -EACCES;
+		return -EEXIST;
 	}
+	inode = affs_new_inode(dir);
+	if (!inode) {
+		iput (dir);
+		return -ENOSPC;
+	}
+	inode->i_op = &affs_dir_inode_operations;
+	error       = affs_add_entry(dir,NULL,inode,name,len,ST_USERDIR);
+	if (error) {
+		iput(dir);
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+		iput(inode);
+		return error;
+	}
+	inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
+	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
 
-	/* We need this backlink for the .. entry */
-	
-	if (ino_back) (*result)->u.affs_i.i_backlink = ino_back; 
+	iput(dir);
+	iput(inode);
+
+	return 0;
+}
+
+static int
+empty_dir(struct buffer_head *bh, int hashsize)
+{
+	while (--hashsize >= 0) {
+		if (((struct dir_front *)bh->b_data)->hashtable[hashsize])
+			return 0;
+	}
+	return 1;
+}
+
+int
+affs_rmdir(struct inode *dir, const char *name, int len)
+{
+	int			 retval;
+	unsigned long		 ino;
+	struct inode		*inode;
+	struct buffer_head	*bh;
+
+	pr_debug("AFFS: rmdir(dir=%lu,\"%.*s\")\n",dir->i_ino,len,name);
+
+	inode  = NULL;
+	retval = -ENOENT;
+	if (!(bh = affs_find_entry(dir,name,len,&ino))) {
+		goto rmdir_done;
+	}
+	if (!(inode = iget(dir->i_sb,ino))) {
+		goto rmdir_done;
+	}
+	retval = -EPERM;
+        if (!fsuser() && current->fsuid != inode->i_uid &&
+            current->fsuid != dir->i_uid)
+		goto rmdir_done;
+	if (inode->i_dev != dir->i_dev)
+		goto rmdir_done;
+	if (inode == dir)	/* we may not delete ".", but "../dir" is ok */
+		goto rmdir_done;
+	if (!S_ISDIR(inode->i_mode)) {
+		retval = -ENOTDIR;
+		goto rmdir_done;
+	}
+	if (!empty_dir(bh,AFFS_I2HSIZE(inode))) {
+		retval = -ENOTEMPTY;
+		goto rmdir_done;
+	}
+	if (inode->i_count > 1) {
+		retval = -EBUSY;
+		goto rmdir_done;
+	}
+	if ((retval = affs_fix_hash_pred(dir,affs_hash_name(name,len,AFFS_I2FSTYPE(dir),
+					 AFFS_I2HSIZE(dir)) + 6,ino,
+					 FILE_END(bh->b_data,dir)->hash_chain)))
+		goto rmdir_done;
+
+	if ((retval = affs_fixup(bh,inode)))
+		goto rmdir_done;
+
+	inode->i_nlink=0;
+	inode->i_dirt=1;
+	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
+	dir->i_version = ++event;
+	dir->i_dirt=1;
+rmdir_done:
+	iput(dir);
+	iput(inode);
+	affs_brelse(bh);
+	return retval;
+}
+
+int
+affs_symlink(struct inode *dir, const char *name, int len, const char *symname)
+{
+	struct buffer_head	*bh;
+	struct inode		*inode;
+	char			*p;
+	unsigned long		 tmp;
+	int			 i, maxlen;
+	char			 c, lc;
+
+	pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
+	printk("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,len,name,symname);
 	
+	maxlen = 4 * AFFS_I2HSIZE(dir) - 1;
+	inode  = affs_new_inode(dir);
+	if (!inode) {
+		iput(dir);
+		return -ENOSPC;
+	}
+	inode->i_op   = &affs_symlink_inode_operations;
+	inode->i_mode = S_IFLNK | 0777;
+	inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
+	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	if (!bh) {
+		iput(dir);
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+		iput(inode);
+		return -EIO;
+	}
+	i  = 0;
+	p  = ((struct slink_front *)bh->b_data)->symname;
+	lc = '/';
+	if (*symname == '/') {
+		while (*symname == '/')
+			symname++;
+		while (inode->i_sb->u.affs_sb.s_volume[i])	/* Cannot overflow */
+			*p++ = inode->i_sb->u.affs_sb.s_volume[i++];
+	}
+	while (i < maxlen && (c = *symname++)) {
+		if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
+			*p++ = '/';
+			i++;
+			symname += 2;
+			lc = '/';
+		} else if (c == '.' && lc == '/' && *symname == '/') {
+			symname++;
+			lc = '/';
+		} else {
+			*p++ = c;
+			lc   = c;
+			i++;
+		}
+		if (lc == '/')
+			while (*symname == '/')
+				symname++;
+	}
+	*p = 0;
+	mark_buffer_dirty(bh,1);
+	affs_brelse(bh);
+	inode->i_dirt = 1;
+	bh = affs_find_entry(dir,name,len,&tmp);
+	if (bh) {
+		inode->i_nlink = 0;
+		iput(inode);
+		affs_brelse(bh);
+		iput(dir);
+		return -EEXIST;
+	}
+	i = affs_add_entry(dir,NULL,inode,name,len,ST_SOFTLINK);
+	if (i) {
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+		iput(inode);
+		affs_brelse(bh);
+		iput(dir);
+		return i;
+	}
 	iput(dir);
+	iput(inode);
+	
 	return 0;
-#endif
+}
+
+int
+affs_link(struct inode *oldinode, struct inode *dir, const char *name, int len)
+{
+	struct inode		*inode;
+	struct buffer_head	*bh;
+	unsigned long		 i;
+	int			 error;
+	
+	pr_debug("AFFS: link(%lu,%lu,\"%.*s\")\n",oldinode->i_ino,dir->i_ino,len,name);
+
+	bh = affs_find_entry(dir,name,len,&i);
+	if (bh) {
+		affs_brelse(bh);
+		iput(oldinode);
+		iput(dir);
+		return -EEXIST;
+	}
+	if (oldinode->u.affs_i.i_hlink) {
+		i = oldinode->u.affs_i.i_original;
+		iput(oldinode);
+		oldinode = iget(dir->i_sb,i);
+		if (!oldinode) {
+			printk("AFFS: link(): original does not exist.\n");
+			iput(dir);
+			return -ENOENT;
+		}
+	}
+	inode = affs_new_inode(dir);
+	if (!inode) {
+		iput(oldinode);
+		iput(dir);
+		return -ENOSPC;
+	}
+	inode->i_op                = oldinode->i_op;
+	inode->i_mode              = oldinode->i_mode;
+	inode->i_uid               = oldinode->i_uid;
+	inode->i_gid               = oldinode->i_gid;
+	inode->u.affs_i.i_protect  = mode_to_prot(inode->i_mode);
+	inode->u.affs_i.i_original = oldinode->i_ino;
+	inode->u.affs_i.i_hlink    = 1;
+
+	if (S_ISDIR(oldinode->i_mode))
+		error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKDIR);
+	else
+		error = affs_add_entry(dir,oldinode,inode,name,len,ST_LINKFILE);
+	if (error) {
+		inode->i_nlink = 0;
+		inode->i_dirt  = 1;
+	}
+	iput(dir);
+	iput(inode);
+	iput(oldinode);
+
+	return error;
+}
+
+static int
+subdir(struct inode * new_inode, struct inode * old_inode)
+{
+    int ino;
+    int result;
+
+    new_inode->i_count++;
+    result = 0;
+    for (;;) {
+        if (new_inode == old_inode) {
+	    result = 1;
+	    break;
+	}
+	if (new_inode->i_dev != old_inode->i_dev)
+	    break;
+	ino = new_inode->i_ino;
+	if (affs_lookup(new_inode,"..",2,&new_inode))
+	    break;
+	if (new_inode->i_ino == ino)
+	    break;
+    }
+    iput(new_inode);
+    return result;
+}
+
+/* I'm afraid this might not be race proof. Maybe next time. */
+
+int
+affs_rename(struct inode *old_dir, const char *old_name, int old_len,
+	    struct inode *new_dir, const char *new_name, int new_len)
+{
+	struct inode		*old_inode;
+	struct inode		*new_inode;
+	struct buffer_head	*old_bh;
+	struct buffer_head	*new_bh;
+	unsigned long		 old_ino;
+	unsigned long		 new_ino;
+	int			 retval;
+
+	pr_debug("AFFS: rename(old=%lu,\"%*s\" to new=%lu,\"%*s\")\n",old_dir->i_ino,old_len,old_name,
+		new_dir->i_ino,new_len,new_name);
+	
+	if (new_len > 30)
+		new_len = 30;
+	goto start_up;
+retry:
+	affs_brelse(old_bh);
+	affs_brelse(new_bh);
+	iput(new_inode);
+	iput(old_inode);
+	current->counter = 0;
+	schedule();
+start_up:
+	old_inode = new_inode = NULL;
+	old_bh    = new_bh = NULL;
+	retval    = -ENOENT;
+
+	old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino);
+	if (!old_bh)
+		goto end_rename;
+	old_inode = __iget(old_dir->i_sb,old_ino,0);
+	if (!old_inode)
+		goto end_rename;
+	new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino);
+	if (new_bh) {
+		new_inode = __iget(new_dir->i_sb,new_ino,0);
+		if (!new_inode) {		/* What does this mean? */
+			affs_brelse(new_bh);
+			new_bh = NULL;
+		}
+	}
+	if (new_inode == old_inode) {		/* Won't happen */
+		retval = 0;
+		goto end_rename;
+	}
+	if (new_inode && S_ISDIR(new_inode->i_mode)) {
+		retval = -EISDIR;
+		if (!S_ISDIR(old_inode->i_mode))
+			goto end_rename;
+		retval = -EINVAL;
+		if (subdir(new_dir,old_inode))
+			goto end_rename;
+		retval = -ENOTEMPTY;
+		if (!empty_dir(new_bh,AFFS_I2HSIZE(new_inode)))
+			goto end_rename;
+		retval = -EBUSY;
+		if (new_inode->i_count > 1)
+			goto end_rename;
+	}
+	if (S_ISDIR(old_inode->i_mode)) {
+		retval = -ENOTDIR;
+		if (new_inode && !S_ISDIR(new_inode->i_mode))
+			goto end_rename;
+		retval = -EINVAL;
+		if (subdir(new_dir,old_inode))
+			goto end_rename;
+		if (affs_parent_ino(old_inode) != old_dir->i_ino)
+			goto end_rename;
+	}
+	/* Unlink destination if existant */
+	if (new_inode) {
+		if ((retval = affs_fix_hash_pred(new_dir,affs_hash_name(new_name,new_len,
+		                                 AFFS_I2FSTYPE(new_dir),AFFS_I2HSIZE(new_dir)) + 6,
+						 new_ino,
+						 FILE_END(new_bh->b_data,new_dir)->hash_chain)))
+			goto retry;
+		if ((retval = affs_fixup(new_bh,new_inode)))
+			goto retry;
+		mark_buffer_dirty(new_bh,1);
+		new_dir->i_version = ++event;
+		new_dir->i_dirt    = 1;
+		new_inode->i_nlink = 0;
+		new_inode->i_dirt  = 1;
+	}
+	retval = affs_fix_hash_pred(old_dir,affs_hash_name(old_name,old_len,AFFS_I2FSTYPE(old_dir),
+				    AFFS_I2HSIZE(old_dir)) + 6,old_ino,
+				    FILE_END(old_bh->b_data,old_dir)->hash_chain);
+	if (retval)
+		goto retry;
+
+	retval = affs_add_entry(new_dir,NULL,old_inode,new_name,new_len,
+				htonl(FILE_END(old_bh->b_data,old_dir)->secondary_type));
+
+	new_dir->i_ctime   = new_dir->i_mtime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	new_dir->i_version = ++event;
+	old_dir->i_version = ++event;
+	new_dir->i_dirt    = 1;
+	old_dir->i_dirt    = 1;
+	mark_buffer_dirty(old_bh,1);
+	
+end_rename:
+	affs_brelse(old_bh);
+	affs_brelse(new_bh);
+	iput(new_inode);
+	iput(old_inode);
+	iput(old_dir);
+	iput(new_dir);
+
+	return retval;
+}
+
+int
+affs_fixup(struct buffer_head *bh, struct inode *inode)
+{
+	ULONG			 key, link_key;
+	LONG			 type;
+	struct buffer_head	*nbh;
+	struct inode		*ofinode;
+
+	type = htonl(FILE_END(bh->b_data,inode)->secondary_type);
+	if (type == ST_LINKFILE || type == ST_LINKDIR) {
+		key = htonl(LINK_END(bh->b_data,inode)->original);
+		LINK_END(bh->b_data,inode)->original = 0;
+		if (!key) {
+			printk("AFFS: fixup(): hard link without original: ino=%lu\n",inode->i_ino);
+			return -ENOENT;
+		}
+		if (!(ofinode = iget(inode->i_sb,key)))
+			return -ENOENT;
+		type = affs_fix_link_pred(ofinode,inode->i_ino,
+					  FILE_END(bh->b_data,inode)->link_chain);
+		iput(ofinode);
+		return type;
+	} else if (type == ST_FILE || type == ST_USERDIR) {
+		if ((key = htonl(FILE_END(bh->b_data,inode)->link_chain))) {
+			/* Get first link, turn it to a file */
+			if (!(ofinode = iget(inode->i_sb,key))) {
+				printk("AFFS: fixup(): cannot read inode %u\n",key);
+				return -ENOENT;
+			}
+			if (!ofinode->u.affs_i.i_hlink) {
+				printk("AFFS: fixup(): first link to %lu (%u) is not a link?\n",
+					inode->i_ino,key);
+				iput(ofinode);
+				return -ENOENT;
+			}
+			if (!(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode)))) {
+				printk("AFFS: fixup(): cannot read block %u\n",key);
+				iput(ofinode);
+				return -ENOENT;
+			}
+			lock_super(inode->i_sb);
+			memcpy(nbh->b_data + 8,bh->b_data + 8,AFFS_I2BSIZE(inode) - 208);
+			FILE_END(nbh->b_data,inode)->byte_size = FILE_END(bh->b_data,inode)->
+									  byte_size;
+			FILE_END(nbh->b_data,inode)->extension = FILE_END(bh->b_data,inode)->
+									  extension;
+			FILE_END(nbh->b_data,inode)->secondary_type = FILE_END(bh->b_data,inode)->
+									  secondary_type;
+			FILE_END(nbh->b_data,inode)->original = 0;
+
+			ofinode->u.affs_i.i_original = 0;
+			ofinode->u.affs_i.i_hlink    = 0;
+			ofinode->i_size              = inode->i_size;
+			ofinode->i_uid               = inode->i_uid;
+			ofinode->i_gid               = inode->i_gid;
+			ofinode->i_dirt              = 1;
+			link_key                     = ofinode->i_ino;
+
+			/* Let all remaining links point to the new file */
+			while (1) {
+				affs_fix_checksum(AFFS_I2BSIZE(inode),nbh->b_data,5);
+				mark_buffer_dirty(nbh,1);
+				key = htonl(FILE_END(nbh->b_data,inode)->link_chain);
+				affs_brelse(nbh);
+				iput(ofinode);
+				if (!key || !(nbh = affs_bread(inode->i_dev,key,AFFS_I2BSIZE(inode))))
+					break;
+				if ((ofinode = iget(inode->i_sb,key))) {
+					if (!ofinode->u.affs_i.i_hlink)
+						printk("AFFS: fixup() inode %u in link chain is "
+						       "not a link\n",key);
+					ofinode->u.affs_i.i_original = link_key;
+					ofinode->i_dirt              = 1;
+					FILE_END(nbh->b_data,inode)->original = htonl(link_key);
+				} else
+					printk("AFFS: fixup(): cannot get inode %u\n",key);
+			}
+			/* Turn old inode to a link */
+			inode->u.affs_i.i_hlink = 1;
+			unlock_super(inode->i_sb);
+		}
+		return 0;
+	} else if (type == ST_SOFTLINK) {
+		return 0;
+	} else {
+		printk("AFFS: fixup(): secondary type=%d\n",type);
+		return -EBADF;
+	}
 }
diff -u --recursive --new-file v1.3.98/linux/fs/affs/symlink.c linux/fs/affs/symlink.c
--- v1.3.98/linux/fs/affs/symlink.c	Tue Apr 23 13:57:11 1996
+++ linux/fs/affs/symlink.c	Mon May  6 14:36:05 1996
@@ -1,34 +1,28 @@
 /*
  *  linux/fs/affs/symlink.c
  *
- *  (C) 1995  Joerg Dorchain Modified for Amiga FFS filesystem
- *            based on:
- *
- *  (C) 1992  Eric Youngdale Modified for ISO9660 filesystem.
+ *  1995  Hans-Joachim Widmaier - modified for AFFS.
  *
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *
- *  isofs symlink handling code.  This is only used with the Rock Ridge
- *  extensions to iso9660
+ *  affs symlink handling code
  */
 
-#include <asm/segment.h>
-
 #include <linux/errno.h>
 #include <linux/sched.h>
+#include <linux/malloc.h>
 #include <linux/fs.h>
 #include <linux/stat.h>
-#include <linux/malloc.h>
 #include <linux/affs_fs.h>
+#include <linux/amigaffs.h>
+
+#include <asm/segment.h>
 
-#include "amigaffs.h"
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
 
 static int affs_readlink(struct inode *, char *, int);
 static int affs_follow_link(struct inode *, struct inode *, int, int, struct inode **);
 
-/*
- * symlinks can't do much...
- */
 struct inode_operations affs_symlink_inode_operations = {
 	NULL,			/* no file-operations */
 	NULL,			/* create */
@@ -47,21 +41,27 @@
 	NULL			/* permission */
 };
 
-static int affs_follow_link(struct inode * dir, struct inode * inode,
-	int flag, int mode, struct inode ** res_inode)
+static int
+affs_follow_link(struct inode *dir, struct inode *inode, int flag, int mode,
+		 struct inode **res_inode)
 {
-	int error;
-	char * pnt;
-	struct buffer_head *bh;
-	struct symlink_front *sy_data;
+	struct buffer_head	*bh;
+	struct slink_front	*lf;
+	char			*buffer;
+	int			 error;
+	int			 i, j;
+	char			 c;
+	char			 lc;
 
+	pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino);
+
+	*res_inode = NULL;
 	if (!dir) {
 		dir = current->fs->root;
 		dir->i_count++;
 	}
 	if (!inode) {
 		iput(dir);
-		*res_inode = NULL;
 		return -ENOENT;
 	}
 	if (!S_ISLNK(inode->i_mode)) {
@@ -70,110 +70,108 @@
 		return 0;
 	}
 	if (current->link_count > 5) {
-		iput(dir);
 		iput(inode);
-		*res_inode = NULL;
+		iput(dir);
 		return -ELOOP;
 	}
-	if (!(bh = affs_pread(inode,inode->i_ino,(void **)&sy_data))) {
-		printk("affs: unable to read block %ld",inode->i_ino);
-		return 0;
+	if (!(buffer = kmalloc(1024,GFP_KERNEL))) {
+		iput(inode);
+		iput(dir);
+		return -ENOSPC;
 	}
-
-	pnt = sy_data->symname;
+	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	i  = 0;
+	j  = 0;
+	if (!bh) {
+		printk("AFFS: unable to read i-node block %lu\n",inode->i_ino);
+		kfree(buffer);
+		iput(inode);
+		iput(dir);
+		return -EIO;
+	}
+	lf = (struct slink_front *)bh->b_data;
+	lc = 0;
+	if (strchr(lf->symname,':')) {		/* Handle assign or volume name */
+		while (i < 1023 && (c = inode->i_sb->u.affs_sb.s_prefix[i]))
+			buffer[i++] = c;
+		while (i < 1023 && lf->symname[j] != ':')
+			buffer[i++] = lf->symname[j++];
+		if (i < 1023)
+			 buffer[i++] = '/';
+		j++;
+		lc = '/';
+	}
+	while (i < 1023 && (c = lf->symname[j])) {
+		if (c == '/' && lc == '/' && i < 1020) {	/* parent dir */
+			buffer[i++] = '.';
+			buffer[i++] = '.';
+		}
+		buffer[i++] = c;
+		lc = c;
+		j++;
+	}
+	buffer[i] = '\0';
+	affs_brelse(bh);
 	iput(inode);
 	current->link_count++;
-	error = open_namei(pnt,flag,mode,res_inode,dir);
+	error = open_namei(buffer,flag,mode,res_inode,dir);
 	current->link_count--;
-	brelse(bh);
+	kfree(buffer);
 	return error;
 }
 
-static char *affs_conv_path(char *affs_path)
+static int
+affs_readlink(struct inode *inode, char *buffer, int buflen)
 {
-static char unix_path[1024]="/";
-int up,ap;
-char dp,slash;
-
-
-dp=1;
-slash=1;
-ap=0;
-up=1;
-if (affs_path[0] == 0)
-  unix_path[up++]='.';
-while ((up < 1020) && (affs_path[ap]!=0))
- {
-  switch (affs_path[ap]) {
-    case ':':
-      if (dp == 0) {
-        slash=0;
-        unix_path[up++]=':';
-      }
-      else {
-        dp=0;
-        slash=1;
-        unix_path[up++]='/';
-      }
-      break;
-    case '/':
-      if (slash==0) {
-        slash=1;
-        unix_path[up++]='/';
-      }
-      else {
-        unix_path[up++]='.';
-        unix_path[up++]='.';
-        unix_path[up++]='/';
-      }
-      break;
-    default:
-      slash=0;
-      unix_path[up++]=affs_path[ap];
-      break;
-  }
-  ap++;
- }
-unix_path[up]=0;
-return unix_path+dp;
-}
-
+	struct buffer_head	*bh;
+	struct slink_front	*lf;
+	int			 i, j;
+	char			 c;
+	char			 lc;
 
-static int affs_readlink(struct inode * inode, char * buffer, int buflen)
-{
-        char * pnt;
-	int i;
-	char c;
-	struct buffer_head *bh;
-	struct symlink_front *sy_data;
+	pr_debug("AFFS: readlink(ino=%lu,buflen=%d)\n",inode->i_ino,buflen);
 
 	if (!S_ISLNK(inode->i_mode)) {
 		iput(inode);
 		return -EINVAL;
 	}
-
-	if (buflen > 1023)
-		buflen = 1023;
-	
-	if (!(bh = affs_pread(inode,inode->i_ino,(void **)&sy_data))) {
-		printk("affs: unable to read block %ld\n",inode->i_ino);
-		return -ENOENT;
+	bh = affs_bread(inode->i_dev,inode->i_ino,AFFS_I2BSIZE(inode));
+	i  = 0;
+	j  = 0;
+	if (!bh) {
+		printk("AFFS: unable to read i-node block %lu\n",inode->i_ino);
+		goto symlink_end;
 	}
+	lf = (struct slink_front *)bh->b_data;
+	lc = 0;
 	
-	iput(inode);
-
-	pnt = sy_data->symname;
-	if (inode->i_sb->u.affs_sb.s_options.conv_links != 0)
-	  pnt = affs_conv_path(pnt);
-
-	i = 0;
-
-	while (i<buflen && (c = pnt[i])) {
-		i++;
-		put_fs_byte(c,buffer++);
+	if (strchr(lf->symname,':')) {		/* Handle assign or volume name */
+		while (i < buflen && (c = inode->i_sb->u.affs_sb.s_prefix[i])) {
+			put_user(c,buffer++);
+			i++;
+		}
+		while (i < buflen && (c = lf->symname[j]) != ':') {
+			put_user(c,buffer++);
+			i++, j++;
+		}
+		if (i < buflen) {
+			put_user('/',buffer++);
+			i++, j++;
+		}
+		lc = '/';
+	}
+	while (i < buflen && (c = lf->symname[j])) {
+		if (c == '/' && lc == '/' && (i + 3 < buflen)) {	/* parent dir */
+			put_user('.',buffer++);
+			put_user('.',buffer++);
+			i += 2;
+		}
+		put_user(c,buffer++);
+		lc = c;
+		i++, j++;
 	}
-	
-	brelse(bh);
-
+symlink_end:
+	iput(inode);
+	affs_brelse(bh);
 	return i;
 }
diff -u --recursive --new-file v1.3.98/linux/fs/block_dev.c linux/fs/block_dev.c
--- v1.3.98/linux/fs/block_dev.c	Mon Mar  4 11:25:48 1996
+++ linux/fs/block_dev.c	Mon May  6 12:26:13 1996
@@ -225,9 +225,9 @@
 	if (block + blocks > size)
 		blocks = size - block;
 
-	/* We do this in a two stage process.  We first try and request
+	/* We do this in a two stage process.  We first try to request
 	   as many blocks as we can, then we wait for the first one to
-	   complete, and then we try and wrap up as many as are actually
+	   complete, and then we try to wrap up as many as are actually
 	   done.  This routine is rather generic, in that it can be used
 	   in a filesystem by substituting the appropriate function in
 	   for getblk.
diff -u --recursive --new-file v1.3.98/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.98/linux/fs/buffer.c	Sun May  5 08:52:02 1996
+++ linux/fs/buffer.c	Mon May  6 12:26:13 1996
@@ -86,7 +86,7 @@
 				activate bdflush */
 		int ndirty;  /* Maximum number of dirty blocks to write out per
 				wake-cycle */
-		int nrefill; /* Number of clean buffers to try and obtain
+		int nrefill; /* Number of clean buffers to try to obtain
 				each time we call refill */
 		int nref_dirt; /* Dirty buffer threshold for activating bdflush
 				  when trying to refill buffers. */
@@ -572,7 +572,7 @@
 	   now so as to ensure that there are still clean buffers available
 	   for user processes to use (and dirty) */
 	
-	/* We are going to try and locate this much memory */
+	/* We are going to try to locate this much memory */
 	needed =bdf_prm.b_un.nrefill * size;  
 
 	while (nr_free_pages > min_free_pages*2 && needed > 0 &&
@@ -592,7 +592,7 @@
 		 if(needed <= 0) return;
 	 };
 
-	/* OK, we cannot grow the buffer cache, now try and get some
+	/* OK, we cannot grow the buffer cache, now try to get some
 	   from the lru list */
 
 	/* First set the candidate pointers to usable buffers.  This
@@ -885,8 +885,7 @@
 	struct buffer_head * bh;
 
 	if (!(bh = getblk(dev, block, size))) {
-		printk("VFS: bread: READ error on device %s\n",
-			kdevname(dev));
+		printk("VFS: bread: impossible error\n");
 		return NULL;
 	}
 	if (buffer_uptodate(bh))
@@ -1867,7 +1866,7 @@
 
 
 /* 
- * Here we attempt to write back old buffers.  We also try and flush inodes 
+ * Here we attempt to write back old buffers.  We also try to flush inodes 
  * and supers as well, since this function is essentially "update", and 
  * otherwise there would be no way of ensuring that these quantities ever 
  * get written back.  Ideally, we would have a timestamp on the inodes
diff -u --recursive --new-file v1.3.98/linux/fs/ext/file.c linux/fs/ext/file.c
--- v1.3.98/linux/fs/ext/file.c	Sun Feb 25 11:17:58 1996
+++ linux/fs/ext/file.c	Mon May  6 12:26:13 1996
@@ -113,9 +113,9 @@
 			blocks = size - block;
 	}
 
-	/* We do this in a two stage process.  We first try and request
+	/* We do this in a two stage process.  We first try to request
 	   as many blocks as we can, then we wait for the first one to
-	   complete, and then we try and wrap up as many as are actually
+	   complete, and then we try to wrap up as many as are actually
 	   done.  This routine is rather generic, in that it can be used
 	   in a filesystem by substituting the appropriate function in
 	   for getblk.
diff -u --recursive --new-file v1.3.98/linux/fs/fat/dir.c linux/fs/fat/dir.c
--- v1.3.98/linux/fs/fat/dir.c	Mon Mar 11 11:25:57 1996
+++ linux/fs/fat/dir.c	Mon May  6 11:43:19 1996
@@ -380,6 +380,7 @@
 int fat_dir_ioctl(struct inode * inode, struct file * filp,
 		  unsigned int cmd, unsigned long arg)
 {
+	int err;
 	/*
 	 * We want to provide an interface for Samba to be able
 	 * to get the short filename for a given long filename.
@@ -389,6 +390,9 @@
 	switch (cmd) {
 	case VFAT_IOCTL_READDIR_BOTH: {
 		struct dirent *d1 = (struct dirent *)arg;
+		err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+		if (err)
+			return err;
 		put_user(0, &d1->d_reclen);
 		return fat_readdirx(inode,filp,(void *)arg,
 				    vfat_ioctl_fill, NULL, 0, 1, 1);
@@ -396,6 +400,9 @@
 	case VFAT_IOCTL_READDIR_SHORT: {
 		struct dirent *d1 = (struct dirent *)arg;
 		put_user(0, &d1->d_reclen);
+		err = verify_area(VERIFY_WRITE, d1, sizeof (*d1));
+		if (err)
+			return err;
 		return fat_readdirx(inode,filp,(void *)arg,
 				    vfat_ioctl_fill, NULL, 1, 0, 1);
 	}
diff -u --recursive --new-file v1.3.98/linux/fs/fat/msbuffer.h linux/fs/fat/msbuffer.h
--- v1.3.98/linux/fs/fat/msbuffer.h	Sat Apr 27 15:19:58 1996
+++ linux/fs/fat/msbuffer.h	Mon May  6 07:28:52 1996
@@ -17,8 +17,8 @@
 /* These macros exist to avoid modifying all the code */
 /* They should be removed one day I guess */
 
-/* The versioning mechanism of the modules system define those macros */
-/* This remove some warnings */
+/* The versioning mechanism of the modules system defines those macros */
+/* This removes some warnings */
 #ifdef brelse
 	#undef brelse
 #endif
diff -u --recursive --new-file v1.3.98/linux/fs/isofs/inode.c linux/fs/isofs/inode.c
--- v1.3.98/linux/fs/isofs/inode.c	Sat Apr 27 15:19:58 1996
+++ linux/fs/isofs/inode.c	Mon May  6 07:28:52 1996
@@ -88,15 +88,15 @@
 	        if (strncmp(this_char,"norock",6) == 0) {
 		  popt->rock = 'n';
 		  continue;
-		};
+		}
 	        if (strncmp(this_char,"unhide",6) == 0) {
 		  popt->unhide = 'y';
 		  continue;
-		};
+		}
 	        if (strncmp(this_char,"cruft",5) == 0) {
 		  popt->cruft = 'y';
 		  continue;
-		};
+		}
 		if ((value = strchr(this_char,'=')) != NULL)
 			*value++ = 0;
 		if (!strcmp(this_char,"map") && value) {
@@ -134,7 +134,7 @@
 		    if(*vpnt <  '0' || *vpnt > '9') break;
 		    ivalue = ivalue * 10 + (*vpnt - '0');
 		    vpnt++;
-		  };
+		  }
 		  if (*vpnt) return 0;
 		  switch(*this_char) {
 		  case 'b':
@@ -241,8 +241,8 @@
 	  while (i != 1){
 	    blocksize_bits++;
 	    i >>=1;
-	  };
-	};
+	  }
+	}
 	set_blocksize(dev, opt.blocksize);
 
 	lock_super(s);
@@ -251,15 +251,15 @@
 
 	vol_desc_start = isofs_get_last_session(dev);
 	
-	for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) {
-#if 0
-	        printk("isofs.inode: iso_blknum=%d\n", iso_blknum);
-#endif 0
-		if (!(bh = bread(dev, iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits), opt.blocksize))) {
+	for (iso_blknum = vol_desc_start+16;
+             iso_blknum < vol_desc_start+100; iso_blknum++) {
+                int b = iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits);
+
+		if (!(bh = bread(dev,b,opt.blocksize))) {
 			s->s_dev = 0;
 			printk("isofs_read_super: bread failed, dev "
-			       "%s iso_blknum %d\n",
-			       kdevname(dev), iso_blknum);
+			       "%s iso_blknum %d block %d\n",
+			       kdevname(dev), iso_blknum, b);
 			unlock_super(s);
 			MOD_DEC_USE_COUNT;
 			return NULL;
@@ -280,7 +280,7 @@
 		        opt.rock = 'n';
 		        h_pri = (struct hs_primary_descriptor *)vdp;
 			break;
-		};
+		}
 		
 		if (strncmp (vdp->id, ISO_STANDARD_ID, sizeof vdp->id) == 0) {
 		  if (isonum_711 (vdp->type) != ISO_VD_PRIMARY)
@@ -290,7 +290,7 @@
 		
 		        pri = (struct iso_primary_descriptor *)vdp;
 			break;
-	        };
+	        }
 
 		brelse(bh);
 	      }
@@ -301,8 +301,7 @@
 		unlock_super(s);
 		MOD_DEC_USE_COUNT;
 		return NULL;
-	};
-	
+	}
 	
 	if(high_sierra){
 	  rootp = (struct iso_directory_record *) h_pri->root_directory_record;
@@ -311,7 +310,7 @@
 	    printk("Multi-volume disks not (yet) supported.\n");
 	    goto out;
 #endif
-	  };
+	  }
 	  s->u.isofs_sb.s_nzones = isonum_733 (h_pri->volume_space_size);
 	  s->u.isofs_sb.s_log_zone_size = isonum_723 (h_pri->logical_block_size);
 	  s->u.isofs_sb.s_max_size = isonum_733(h_pri->volume_space_size);
@@ -322,7 +321,7 @@
 	    printk("Multi-volume disks not (yet) supported.\n");
 	    goto out;
 #endif
-	  };
+	  }
 	  s->u.isofs_sb.s_nzones = isonum_733 (pri->volume_space_size);
 	  s->u.isofs_sb.s_log_zone_size = isonum_723 (pri->logical_block_size);
 	  s->u.isofs_sb.s_max_size = isonum_733(pri->volume_space_size);
@@ -793,7 +792,7 @@
 			if(!(bh = bread(parent->i_dev,block,bufsize))) {
  			        kfree(cpnt);
 				return -1;
-			};
+			}
  			memcpy((char *)cpnt+frag1, bh->b_data, offset);
 		}
 		
diff -u --recursive --new-file v1.3.98/linux/fs/isofs/rock.c linux/fs/isofs/rock.c
--- v1.3.98/linux/fs/isofs/rock.c	Fri Apr 19 10:08:01 1996
+++ linux/fs/isofs/rock.c	Mon May  6 12:26:14 1996
@@ -319,7 +319,7 @@
 	break;
       case SIG('T','F'):
 	/* Some RRIP writers incorrectly place ctime in the TF_CREATE field.
-	   Try and handle this correctly for either case. */
+	   Try to handle this correctly for either case. */
 	cnt = 0; /* Rock ridge never appears on a High Sierra disk */
 	if(rr->u.TF.flags & TF_CREATE) 
 	  inode->i_ctime = iso_date(rr->u.TF.times[cnt++].time, 0);
diff -u --recursive --new-file v1.3.98/linux/fs/locks.c linux/fs/locks.c
--- v1.3.98/linux/fs/locks.c	Sun May  5 08:52:03 1996
+++ linux/fs/locks.c	Sun May  5 08:51:12 1996
@@ -639,7 +639,7 @@
 				locks_free_lock(new_fl);
 				return (-ERESTARTSYS);
 			}
-			/* Try to avoid deadlocks due to pathalogical programs that
+			/* Try to avoid deadlocks due to pathological programs that
 			 * mix calls to flock() and fcntl(). Return EAGAIN, because
 			 * EDEADLOCK isn't a documented return value for flock().
 			 */
diff -u --recursive --new-file v1.3.98/linux/fs/nfs/nfsroot.c linux/fs/nfs/nfsroot.c
--- v1.3.98/linux/fs/nfs/nfsroot.c	Sun May  5 08:52:03 1996
+++ linux/fs/nfs/nfsroot.c	Mon May  6 12:26:14 1996
@@ -292,7 +292,7 @@
 	unsigned long sip, tip;
 	unsigned char *sha, *tha;		/* s for "source", t for "target" */
 
-	/* If this test doesn't pass, its not IP, or we should ignore it anyway */
+	/* If this test doesn't pass, it's not IP, or we should ignore it anyway */
 	if (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd)) {
 		kfree_skb(skb, FREE_READ);
 		return 0;
@@ -763,9 +763,9 @@
  */
 static void root_do_bootp_ext(u8 *ext)
 {
+#ifdef NFSROOT_BOOTP_DEBUG
 	u8 *c;
 
-#ifdef NFSROOT_BOOTP_DEBUG
 	printk("BOOTP: Got extension %02x",*ext);
 	for(c=ext+2; c<ext+2+ext[1]; c++)
 		printk(" %02x", *c);
diff -u --recursive --new-file v1.3.98/linux/fs/super.c linux/fs/super.c
--- v1.3.98/linux/fs/super.c	Mon Apr 29 18:05:19 1996
+++ linux/fs/super.c	Mon May  6 07:28:52 1996
@@ -92,9 +92,9 @@
 	struct vfsmount *lptr;
 	char *tmp;
 
-	if ((lptr = (struct vfsmount *)
-	     kmalloc(sizeof(struct vfsmount), GFP_KERNEL)) == (struct vfsmount *)NULL)
-		return ((struct vfsmount *)NULL);
+	lptr = (struct vfsmount *)kmalloc(sizeof(struct vfsmount), GFP_KERNEL);
+        if (!lptr)
+		return NULL;
 	memset(lptr, 0, sizeof(struct vfsmount));
 
 	lptr->mnt_dev = dev;
diff -u --recursive --new-file v1.3.98/linux/fs/sysv/file.c linux/fs/sysv/file.c
--- v1.3.98/linux/fs/sysv/file.c	Sun Feb 25 11:17:58 1996
+++ linux/fs/sysv/file.c	Mon May  6 12:26:15 1996
@@ -114,9 +114,9 @@
 			blocks = size - block;
 	}
 
-	/* We do this in a two stage process.  We first try and request
+	/* We do this in a two stage process.  We first try to request
 	   as many blocks as we can, then we wait for the first one to
-	   complete, and then we try and wrap up as many as are actually
+	   complete, and then we try to wrap up as many as are actually
 	   done.  This routine is rather generic, in that it can be used
 	   in a filesystem by substituting the appropriate function in
 	   for getblk.
diff -u --recursive --new-file v1.3.98/linux/fs/ufs/ufs_super.c linux/fs/ufs/ufs_super.c
--- v1.3.98/linux/fs/ufs/ufs_super.c	Mon Apr 29 18:05:19 1996
+++ linux/fs/ufs/ufs_super.c	Sun May  5 12:05:48 1996
@@ -13,6 +13,9 @@
 /*
  * Kernel module support added on 96/04/26 by
  * Stefan Reinauer <stepan@home.culture.mipt.ru>
+ *
+ * Module usage counts added on 96/04/29 by
+ * Gertjan van Wingerde <gertjan@cs.vu.nl>
  */
 
 #include <linux/module.h>
diff -u --recursive --new-file v1.3.98/linux/fs/umsdos/namei.c linux/fs/umsdos/namei.c
--- v1.3.98/linux/fs/umsdos/namei.c	Wed Nov  8 12:33:39 1995
+++ linux/fs/umsdos/namei.c	Mon May  6 12:26:15 1996
@@ -433,7 +433,7 @@
 		in unused entry of the EMD file. The other is to have a separate
 		file dedicated to hold all symbolic links data.
 
-		Lets go for simplicity...
+		Let's go for simplicity...
 	*/
 	struct inode *inode;
 	int ret;
diff -u --recursive --new-file v1.3.98/linux/fs/vfat/Makefile linux/fs/vfat/Makefile
--- v1.3.98/linux/fs/vfat/Makefile	Wed Feb  7 09:39:29 1996
+++ linux/fs/vfat/Makefile	Mon May  6 11:43:19 1996
@@ -8,8 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := vfat.o
-O_OBJS   := namei.o
-OX_OBJS  := 
+O_OBJS   := 
+OX_OBJS  := namei.o
 M_OBJS   := $(O_TARGET)
 
 include $(TOPDIR)/Rules.make
diff -u --recursive --new-file v1.3.98/linux/fs/vfat/namei.c linux/fs/vfat/namei.c
--- v1.3.98/linux/fs/vfat/namei.c	Sun Apr 21 19:22:11 1996
+++ linux/fs/vfat/namei.c	Mon May  6 11:43:19 1996
@@ -1569,12 +1569,26 @@
 	vfat_read_super, "vfat", 1, NULL
 };
 
+static struct symbol_table vfat_syms = {
+#include <linux/symtab_begin.h>
+	X(vfat_create),
+	X(vfat_unlink),
+	X(vfat_mkdir),
+	X(vfat_rmdir),
+	X(vfat_rename),
+	X(vfat_put_super),
+	X(vfat_read_super),
+	X(vfat_read_inode),
+	X(vfat_lookup),
+#include <linux/symtab_end.h>
+};                                           
+
 int init_vfat_fs(void)
 {
 	int status;
 
 	if ((status = register_filesystem(&vfat_fs_type)) == 0)
-		status = register_symtab(0);
+		status = register_symtab(&vfat_syms);
 	return status;
 }
 
diff -u --recursive --new-file v1.3.98/linux/fs/xiafs/file.c linux/fs/xiafs/file.c
--- v1.3.98/linux/fs/xiafs/file.c	Sun Feb 25 11:17:58 1996
+++ linux/fs/xiafs/file.c	Mon May  6 12:26:15 1996
@@ -106,9 +106,9 @@
 	    zones = f_zones - zone_nr;
     }
 
-    /* We do this in a two stage process.  We first try and request
+    /* We do this in a two stage process.  We first try to request
        as many blocks as we can, then we wait for the first one to
-       complete, and then we try and wrap up as many as are actually
+       complete, and then we try to wrap up as many as are actually
        done.  This routine is rather generic, in that it can be used
        in a filesystem by substituting the appropriate function in
        for getblk.
diff -u --recursive --new-file v1.3.98/linux/include/asm-i386/locks.h linux/include/asm-i386/locks.h
--- v1.3.98/linux/include/asm-i386/locks.h	Fri Apr 12 15:52:05 1996
+++ linux/include/asm-i386/locks.h	Mon May  6 12:26:15 1996
@@ -21,7 +21,7 @@
 	while(lock_set_bit(0,&sp->lock))
 	{
 		/*
-		 *	Failed, but thats cos we own it!
+		 *	Failed, but that's cos we own it!
 		 */
 		 
 		if(sp->cpu==processor)
@@ -45,7 +45,7 @@
 		}
 		/*
 		 *	Someone wrote the line, we go 'I' and get
-		 *	the cache entry. Now try and regrab
+		 *	the cache entry. Now try to regrab
 		 */
 	}
 	sp->users++;sp->cpu=processor;
@@ -117,7 +117,7 @@
 extern __inline__ void spintestlock(struct spinlock *sp)
 {
 	/*
-	 *	We do no sanity checks, its legal to optimistically
+	 *	We do no sanity checks, it's legal to optimistically
 	 *	get a lower lock.
 	 */
 	prim_spin_lock_nb(sp);
diff -u --recursive --new-file v1.3.98/linux/include/asm-m68k/atomic.h linux/include/asm-m68k/atomic.h
--- v1.3.98/linux/include/asm-m68k/atomic.h	Sat Apr 27 15:19:59 1996
+++ linux/include/asm-m68k/atomic.h	Mon May  6 12:44:32 1996
@@ -10,54 +10,32 @@
  * We do not have SMP m68k systems, so we don't have to deal with that.
  */
 
-/*
- * Make sure gcc doesn't try to be clever and move things around
- * on us. We need to use _exactly_ the address the user gave us,
- * not some alias that contains the same information.
- */
-#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x)
-
 typedef int atomic_t;
 
 static __inline__ void atomic_add(atomic_t i, atomic_t *v)
 {
-	__asm__ __volatile__(
-		"addl %1,%0"
-		:"=m" (__atomic_fool_gcc(v))
-		:"ir" (i), "0" (__atomic_fool_gcc(v)));
+	__asm__ __volatile__("addl %1,%0" : : "m" (*v), "id" (i));
 }
 
 static __inline__ void atomic_sub(atomic_t i, atomic_t *v)
 {
-	__asm__ __volatile__(
-		"subl  %1,%0"
-		:"=m" (__atomic_fool_gcc(v))
-		:"ir" (i), "0" (__atomic_fool_gcc(v)));
+	__asm__ __volatile__("subl %1,%0" : : "m" (*v), "id" (i));
 }
 
 static __inline__ void atomic_inc(atomic_t *v)
 {
-	__asm__ __volatile__(
-		"addql #1,%0"
-		:"=m" (__atomic_fool_gcc(v))
-		:"0" (__atomic_fool_gcc(v)));
+	__asm__ __volatile__("addql #1,%0" : : "m" (*v));
 }
 
 static __inline__ void atomic_dec(atomic_t *v)
 {
-	__asm__ __volatile__(
-		"subql #1,%0"
-		:"=m" (__atomic_fool_gcc(v))
-		:"0" (__atomic_fool_gcc(v)));
+	__asm__ __volatile__("subql #1,%0" : : "m" (*v));
 }
 
 static __inline__ int atomic_dec_and_test(atomic_t *v)
 {
 	char c;
-	__asm__ __volatile__(
-		"subql #1,%0; seq %1"
-		:"=m" (__atomic_fool_gcc(v)), "=d" (c)
-		:"0" (__atomic_fool_gcc(v)));
+	__asm__ __volatile__("subql #1,%1; seq %0" : "=d" (c) : "m" (*v));
 	return c != 0;
 }
 
diff -u --recursive --new-file v1.3.98/linux/include/asm-m68k/bitops.h linux/include/asm-m68k/bitops.h
--- v1.3.98/linux/include/asm-m68k/bitops.h	Sat Apr 27 15:19:59 1996
+++ linux/include/asm-m68k/bitops.h	Mon May  6 12:44:32 1996
@@ -111,6 +111,54 @@
 	return res ^ 31;
 }
 
+extern __inline__ int find_first_one_bit(void * vaddr, unsigned size)
+{
+	unsigned long *p = vaddr, *addr = vaddr;
+	int res;
+	unsigned long num;
+
+	if (!size)
+		return 0;
+
+	while (!*p++)
+	{
+		if (size <= 32)
+			return (p - addr) << 5;
+		size -= 32;
+	}
+
+	num = *--p;
+	__asm__ __volatile__ ("bfffo %1{#0,#0},%0"
+			      : "=d" (res) : "d" (num & -num));
+	return ((p - addr) << 5) + (res ^ 31);
+}
+
+extern __inline__ int find_next_one_bit (void *vaddr, int size,
+				      int offset)
+{
+	unsigned long *addr = vaddr;
+	unsigned long *p = addr + (offset >> 5);
+	int set = 0, bit = offset & 31UL, res;
+
+	if (offset >= size)
+		return size;
+
+	if (bit) {
+		unsigned long num = *p & (~0UL << bit);
+
+		/* Look for one in first longword */
+		__asm__ __volatile__ ("bfffo %1{#0,#0},%0"
+				      : "=d" (res) : "d" (num & -num));
+		if (res < 32)
+			return (offset & ~31UL) + (res ^ 31);
+                set = 32 - bit;
+		p++;
+	}
+	/* No one yet, search remaining full bytes for a one */
+	res = find_first_one_bit (p, size - 32 * (p - addr));
+	return (offset + set + res);
+}
+
 /* Bitmap functions for the minix filesystem */
 
 extern __inline__ int
diff -u --recursive --new-file v1.3.98/linux/include/asm-m68k/checksum.h linux/include/asm-m68k/checksum.h
--- v1.3.98/linux/include/asm-m68k/checksum.h	Tue Apr 23 13:57:12 1996
+++ linux/include/asm-m68k/checksum.h	Mon May  6 12:44:32 1996
@@ -108,7 +108,6 @@
  * in icmp.c
  */
 
-#if 1
 static inline unsigned short
 ip_compute_csum(unsigned char * buff, int len)
 {
@@ -124,52 +123,5 @@
 		: "0" (csum_partial(buff, len, 0)));
 	return ~sum;
 }
-#else
-static inline unsigned short
-ip_compute_csum(unsigned char * buff, int len)
-{
-	unsigned long sum = 0;
-
-  /* Do the first multiple of 4 bytes and convert to 16 bits. */
-  if (len > 3)
-    {
-      int dummy;
-      __asm__ ("subql #1,%2\n\t"
-	       "1:\t"
-	       "movel %1@+,%/d0\n\t"
-	       "addxl %/d0,%0\n\t"
-	       "dbra %2,1b\n\t"
-	       "movel %0,%/d0\n\t"
-	       "swap %/d0\n\t"
-	       "addxw %/d0,%0\n\t"
-	       "clrw %/d0\n\t"
-	       "addxw %/d0,%0"
-	       : "=d" (sum), "=a" (buff), "=d" (dummy)
-	       : "0" (sum), "1" (buff), "2" (len >> 2)
-	       : "d0");
-    }
-  if (len & 2)
-    {
-      __asm__ ("addw %1@+,%0\n\t"
-	       "addxw %2,%0"
-	       : "=d" (sum), "=a" (buff)
-	       : "d" (0), "0" (sum), "1" (buff));
-    }
-  if (len & 1)
-    {
-      __asm__ ("movew %1@,%/d0\n\t"
-	       "clrb %/d0\n\t"
-	       "addw %/d0,%0\n\t"
-	       "clrw %/d0\n\t"
-	       "addxw %/d0,%0"
-	       : "=d" (sum)
-	       : "a" (buff), "0" (sum)
-	       : "d0");
-    }
-
-	sum =~sum;
-	return(sum & 0xffff);
-}
-#endif
 
 #endif /* _M68K_CHECKSUM_H */
diff -u --recursive --new-file v1.3.98/linux/include/asm-m68k/elf.h linux/include/asm-m68k/elf.h
--- v1.3.98/linux/include/asm-m68k/elf.h	Tue Apr 23 13:57:12 1996
+++ linux/include/asm-m68k/elf.h	Mon May  6 12:44:32 1996
@@ -12,11 +12,6 @@
 #define ELF_NGREG 20 /* d1-d7/a0-a6/d0/usp/orig_d0/sr/pc/fmtvec */
 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 
-/* XXX temporary */
-/*
-typedef unsigned long elf_fpregset_t;
-*/
-
 typedef struct user_m68kfp_struct elf_fpregset_t;
 
 #endif
diff -u --recursive --new-file v1.3.98/linux/include/asm-m68k/pgtable.h linux/include/asm-m68k/pgtable.h
--- v1.3.98/linux/include/asm-m68k/pgtable.h	Sat Apr 27 15:19:59 1996
+++ linux/include/asm-m68k/pgtable.h	Mon May  6 12:44:32 1996
@@ -14,7 +14,6 @@
 		__asm__ __volatile__("pflusha\n"::); \
 } while (0)
 
-#if 1
 static inline void __flush_tlb_one(unsigned long addr)
 {
 	if (m68k_is040or060) {
@@ -24,9 +23,6 @@
 	} else
 		__asm__ __volatile__("pflush #0,#0,(%0)" : : "a" (addr));
 }
-#else
-#define __flush_tlb_one(addr) __flush_tlb()
-#endif
 
 #define flush_tlb() __flush_tlb()
 #define flush_tlb_all() flush_tlb()
@@ -573,15 +569,14 @@
 
 /*
  * I don't know what is going on here, but since these were changed,
- * swapping haven't been working on the 68040.
+ * swapping hasn't been working on the 68040.
  */
 
-#if 0
 #define SWP_TYPE(entry)  (((entry) >> 2) & 0x7f)
+#if 0
 #define SWP_OFFSET(entry) ((entry) >> 9)
 #define SWP_ENTRY(type,offset) (((type) << 2) | ((offset) << 9))
 #else
-#define SWP_TYPE(entry)  (((entry) & 0x1fc) >> 2)
 #define SWP_OFFSET(entry) ((entry) >> PAGE_SHIFT)
 #define SWP_ENTRY(type,offset) (((type) << 2) | ((offset) << PAGE_SHIFT))
 #endif
diff -u --recursive --new-file v1.3.98/linux/include/asm-m68k/serial.h linux/include/asm-m68k/serial.h
--- v1.3.98/linux/include/asm-m68k/serial.h	Thu Jan  1 02:00:00 1970
+++ linux/include/asm-m68k/serial.h	Mon May  6 12:44:32 1996
@@ -0,0 +1,380 @@
+/*
+ * include/linux/serial.h
+ *
+ * Copyright (C) 1992 by Theodore Ts'o.
+ * 
+ * Redistribution of this file is permitted under the terms of the GNU 
+ * Public License (GPL)
+ */
+
+#ifndef _M68K_SERIAL_H
+#define _M68K_SERIAL_H
+
+
+/* m68k serial port types are numbered from 100 to avoid interference
+ * with the PC types (1..4)
+ */
+#define PORT_UNKNOWN	0
+#define PORT_8250	1
+#define PORT_16450	2
+#define PORT_16550	3
+#define PORT_16550A	4
+#define PORT_CIRRUS     5
+#define SER_SCC_NORM	100	/* standard SCC channel */
+#define	SER_SCC_DMA	101	/* SCC channel with DMA support */
+#define	SER_MFP_CTRL	102	/* standard MFP port with modem control signals */
+#define	SER_MFP_BARE	103	/* MFP port without modem controls */
+#define	SER_MIDI	104	/* Atari MIDI */
+#define	SER_AMIGA	105	/* Amiga built-in serial port */
+#define SER_IOEXT	106	/* Amiga GVP IO-Extender (16c552) */
+#define SER_MFC_III	107	/* Amiga BSC Multiface Card III (MC68681) */
+
+
+struct serial_struct {
+	int	type;
+	int	line;
+	int	port;
+	int	irq;
+	int	flags;
+	int	xmit_fifo_size;
+	int	custom_divisor;
+	int	baud_base;
+	unsigned short	close_delay;
+	char	reserved_char[2];
+	int	hub6;
+	unsigned short	closing_wait; /* time to wait before closing */
+	unsigned short	closing_wait2; /* no longer used... */
+	int	reserved[4];
+};
+
+/*
+ * For the close wait times, 0 means wait forever for serial port to
+ * flush its output.  65535 means don't wait at all.
+ */
+#define ASYNC_CLOSING_WAIT_INF	0
+#define ASYNC_CLOSING_WAIT_NONE	65535
+
+/* This function tables does the abstraction from the underlying
+ * hardware:
+ *
+ *   init(): Initialize the port as necessary, set RTS and DTR and
+ *      enable interrupts. It does not need to set the speed and other
+ *      parameters, because change_speed() is called, too.
+ *   deinit(): Stop and shutdown the port (e.g. disable interrupts, ...)
+ *   enab_tx_int(): Enable or disable the Tx Buffer Empty interrupt
+ *      independently from other interrupt sources. If the int is
+ *      enabled, the transmitter should also be restarted, i.e. if there
+ *      are any chars to be sent, they should be put into the Tx
+ *      register. The real en/disabling of the interrupt may be a no-op
+ *      if there is no way to do this or it is too complex. This Tx ints
+ *      are just disabled to save some interrupts if the transmitter is
+ *      stopped anyway. But the restarting must be implemented!
+ *   check_custom_divisor(): Check the given custom divisor for legality
+ *      and return 0 if OK, non-zero otherwise.
+ *   change_speed(): Set port speed, character size, number of stop
+ *      bits and parity from the termios structure. If the user wants
+ *      to set the speed with a custom divisor, he is required to
+ *      check the baud_base first!
+ *   throttle(): Set or clear the RTS line according to 'status'.
+ *   set_break(): Set or clear the 'Send a Break' flag.
+ *   get_serial_info(): Fill in the baud_base and custom_divisor
+ *      fields of a serial_struct. It may also modify other fields, if
+ *      needed.
+ *   get_modem_info(): Return the status of RTS, DTR, DCD, RI, DSR and CTS.
+ *   set_modem_info(): Set the status of RTS and DTR according to
+ *      'new_dtr' and 'new_rts', resp. 0 = clear, 1 = set, -1 = don't change
+ *   ioctl(): Process any port-specific ioctl's. This pointer may be
+ *      NULL, if the port has no own ioctl's.
+ *   stop_receive(): Turn off the Rx part of the port, so no more characters
+ *      will be received. This is called before shutting the port down.
+ *   trans_empty(): Return !=0 if there are no more characters still to be
+ *      sent out (Tx buffer register and FIFOs empty)
+ *   check_open(): Is called before the port is opened. The driver can check
+ *      if that's ok and return an error code, or keep track of the opening
+ *      even before init() is called. Use deinit() for matching closing of the
+ *      port.
+ *
+ */
+
+struct async_struct;
+
+typedef struct {
+	void (*init)( struct async_struct *info );
+	void (*deinit)( struct async_struct *info, int leave_dtr );
+	void (*enab_tx_int)( struct async_struct *info, int enab_flag );
+	int  (*check_custom_divisor)( struct async_struct *info, int baud_base,
+				     int divisor );
+	void (*change_speed)( struct async_struct *info );
+	void (*throttle)( struct async_struct *info, int status );
+	void (*set_break)( struct async_struct *info, int break_flag );
+	void (*get_serial_info)( struct async_struct *info,
+				struct serial_struct *retinfo );
+	unsigned int (*get_modem_info)( struct async_struct *info );
+	int  (*set_modem_info)( struct async_struct *info, int new_dtr,
+			       int new_rts );
+	int  (*ioctl)( struct tty_struct *tty, struct file *file,
+		      struct async_struct *info, unsigned int cmd,
+		      unsigned long arg );
+	void (*stop_receive)( struct async_struct *info );
+	int  (*trans_empty)( struct async_struct *info );
+	int  (*check_open)( struct async_struct *info, struct tty_struct *tty,
+			   struct file *file );
+} SERIALSWITCH;
+
+/*
+ * Definitions for async_struct (and serial_struct) flags field
+ */
+#define ASYNC_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes 
+				   on the callout port */
+#define ASYNC_FOURPORT  0x0002	/* Set OU1, OUT2 per AST Fourport settings */
+#define ASYNC_SAK	0x0004	/* Secure Attention Key (Orange book) */
+#define ASYNC_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define ASYNC_SPD_MASK	0x0030
+#define ASYNC_SPD_HI	0x0010	/* Use 56000 instead of 38400 bps */
+
+#define ASYNC_SPD_VHI	0x0020  /* Use 115200 instead of 38400 bps */
+#define ASYNC_SPD_CUST	0x0030  /* Use user-specified divisor */
+
+#define ASYNC_SKIP_TEST	0x0040 /* Skip UART test during autoconfiguration */
+#define ASYNC_AUTO_IRQ  0x0080 /* Do automatic IRQ during autoconfiguration */
+#define ASYNC_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define ASYNC_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
+#define ASYNC_CALLOUT_NOHUP   0x0400 /* Don't do hangups for cua device */
+
+#define ASYNC_FLAGS	0x0FFF	/* Possible legal async flags */
+#define ASYNC_USR_MASK 0x0430	/* Legal flags that non-privileged
+				 * users can set or reset */
+
+/* Internal flags used only by drivers/char/m68kserial.c */
+#define ASYNC_INITIALIZED	0x80000000 /* Serial port was initialized */
+#define ASYNC_CALLOUT_ACTIVE	0x40000000 /* Call out device is active */
+#define ASYNC_NORMAL_ACTIVE	0x20000000 /* Normal device is active */
+#define ASYNC_BOOT_AUTOCONF	0x10000000 /* Autoconfigure port on bootup */
+#define ASYNC_CLOSING		0x08000000 /* Serial port is closing */
+#define ASYNC_CTS_FLOW		0x04000000 /* Do CTS flow control */
+#define ASYNC_CHECK_CD		0x02000000 /* i.e., CLOCAL */
+
+/*
+ * Serial input interrupt line counters -- external structure
+ * Four lines can interrupt: CTS, DSR, RI, DCD
+ */
+struct serial_icounter_struct {
+	int cts, dsr, rng, dcd;
+	int reserved[16];
+};
+
+
+#ifdef __KERNEL__
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+
+/*
+ * Counters of the input lines (CTS, DSR, RI, CD) interrupts
+ */
+struct async_icount {
+	__u32	cts, dsr, rng, dcd;	
+};
+
+struct async_struct {
+	int			magic;
+	int			baud_base;
+	int			port;
+	int			irq;
+	int			flags; 		/* defined in tty.h */
+	int			hub6;		/* HUB6 plus one */
+	int			type;
+	struct tty_struct 	*tty;
+	int			read_status_mask;
+	int			ignore_status_mask;
+	int			timeout;
+	int			xmit_fifo_size;
+	int			custom_divisor;
+	int			x_char;	/* xon/xoff character */
+	int			close_delay;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
+	int			IER; 	/* Interrupt Enable Register */
+	int			MCR; 	/* Modem control register */
+	int			MCR_noint; /* MCR with interrupts off */
+	unsigned long		event;
+	unsigned long		last_active;
+	int			line;
+	int			count;	    /* # of fd on device */
+	int			blocked_open; /* # of blocked opens */
+	long			session; /* Session of opening process */
+	long			pgrp; /* pgrp of opening process */
+	unsigned char 		*xmit_buf;
+	int			xmit_head;
+	int			xmit_tail;
+	int			xmit_cnt;
+	struct tq_struct	tqueue;
+	struct tq_struct	tqueue_hangup;
+	struct termios		normal_termios;
+	struct termios		callout_termios;
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
+	struct wait_queue	*delta_msr_wait;
+	struct async_icount	icount;	/* kernel counters for the 4 input interrupts */
+	struct async_struct	*next_port; /* For the linked list */
+	struct async_struct	*prev_port;
+	void			*board_base; /* board-base address for use with
+						boards carrying several UART's,
+						like some Amiga boards. */
+	unsigned short		nr_uarts;    /* UART-counter, that indicates
+						how manu UART's there are on
+						the board.  If the board has a
+						IRQ-register, this can be used
+						to check if any of the uarts,
+						on the board has requested an
+						interrupt, instead of checking
+						IRQ-registers on all UART's */
+	SERIALSWITCH		*sw;		/* functions to manage this port */
+};
+
+#define SERIAL_MAGIC 0x5301
+
+/*
+ * The size of the serial xmit buffer is 1 page, or 4096 bytes
+ */
+#define SERIAL_XMIT_SIZE 4096
+
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at rs interrupt time.
+ */
+#define RS_EVENT_WRITE_WAKEUP	0
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+/* Export to allow PCMCIA to use this - Dave Hinds */
+extern int register_serial(struct serial_struct *req);
+extern void unregister_serial(int line);
+extern struct async_struct rs_table[];
+extern task_queue tq_serial;
+
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static __inline__ void rs_sched_event(struct async_struct *info, int event)
+{
+	info->event |= 1 << event;
+	queue_task_irq(&info->tqueue, &tq_serial);
+	mark_bh(SERIAL_BH);
+}
+
+static __inline__ void rs_receive_char( struct async_struct *info,
+					    int ch, int err )
+{
+	struct tty_struct *tty = info->tty;
+	
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+		return;
+	tty->flip.count++;
+	if (err == TTY_BREAK) {
+		if (info->flags & ASYNC_SAK)
+			do_SAK(tty);
+	}
+	*tty->flip.flag_buf_ptr++ = err;
+	*tty->flip.char_buf_ptr++ = ch;
+	queue_task_irq(&tty->flip.tqueue, &tq_timer);
+}
+
+static __inline__ int rs_get_tx_char( struct async_struct *info )
+{
+	unsigned char ch;
+	
+	if (info->x_char) {
+		ch = info->x_char;
+		info->x_char = 0;
+		return( ch );
+	}
+
+	if (info->xmit_cnt <= 0 || info->tty->stopped || info->tty->hw_stopped)
+		return( -1 );
+
+	ch = info->xmit_buf[info->xmit_tail++];
+	info->xmit_tail &= SERIAL_XMIT_SIZE - 1;
+	if (--info->xmit_cnt < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+	return( ch );
+}
+
+static __inline__ int rs_no_more_tx( struct async_struct *info )
+{
+	return( info->xmit_cnt <= 0 ||
+			info->tty->stopped ||
+			info->tty->hw_stopped );
+}
+
+static __inline__ void rs_dcd_changed( struct async_struct *info, int dcd )
+
+{
+	/* update input line counter */
+	info->icount.dcd++;
+	wake_up_interruptible(&info->delta_msr_wait);
+
+	if (info->flags & ASYNC_CHECK_CD) {
+#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
+		printk("ttyS%d CD now %s...", info->line,
+		       dcd ? "on" : "off");
+#endif		
+		if (dcd) {
+			wake_up_interruptible(&info->open_wait);
+		} else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+			     (info->flags & ASYNC_CALLOUT_NOHUP))) {
+#ifdef SERIAL_DEBUG_OPEN
+			printk("scheduling hangup...");
+#endif
+			queue_task_irq(&info->tqueue_hangup,
+				       &tq_scheduler);
+		}
+	}
+}
+
+
+void rs_stop( struct tty_struct *tty );
+void rs_start( struct tty_struct *tty );
+
+static __inline__ void rs_check_cts( struct async_struct *info, int cts )
+{
+	/* update input line counter */
+	info->icount.cts++;
+	wake_up_interruptible(&info->delta_msr_wait);
+	
+	if ((info->flags & ASYNC_CTS_FLOW) && info->tty)
+		if (info->tty->hw_stopped) {
+			if (cts) {
+#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
+				printk("CTS tx start...");
+#endif
+				info->tty->hw_stopped = 0;
+				rs_start( info->tty );
+				rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+				return;
+			}
+		} else {
+			if (!cts) {
+				info->tty->hw_stopped = 1;
+				rs_stop( info->tty );
+			}
+		}
+		
+}
+
+
+#endif /* __KERNEL__ */
+
+#endif /* _M68K_SERIAL_H */
diff -u --recursive --new-file v1.3.98/linux/include/asm-m68k/system.h linux/include/asm-m68k/system.h
--- v1.3.98/linux/include/asm-m68k/system.h	Sat Apr 27 15:19:59 1996
+++ linux/include/asm-m68k/system.h	Mon May  6 12:44:32 1996
@@ -64,6 +64,7 @@
 #endif /* machine compilation types */ 
 #define cli() __asm__ __volatile__ ("oriw  #0x0700,%/sr": : : "memory")
 #define nop() __asm__ __volatile__ ("nop"::)
+#define mb()  __asm__ __volatile__ (""   : : :"memory")
 
 #define save_flags(x) \
 __asm__ __volatile__("movew %/sr,%0":"=d" (x) : /* no input */ :"memory")
diff -u --recursive --new-file v1.3.98/linux/include/asm-mips/shmparam.h linux/include/asm-mips/shmparam.h
--- v1.3.98/linux/include/asm-mips/shmparam.h	Wed Dec 13 12:39:46 1995
+++ linux/include/asm-mips/shmparam.h	Mon May  6 12:26:15 1996
@@ -39,7 +39,7 @@
 #define SHMALL				/* max shm system wide (pages) */ \
 	(1<<(_SHM_IDX_BITS+_SHM_ID_BITS))
 /*
- * This constant is very large but the ABI in it's wisdom says ...
+ * This constant is very large but the ABI in its wisdom says ...
  */
 #define	SHMLBA 0x40000			/* attach addr a multiple of this */
 #define SHMSEG SHMMNI			/* max shared segs per process */
diff -u --recursive --new-file v1.3.98/linux/include/asm-sparc/floppy.h linux/include/asm-sparc/floppy.h
--- v1.3.98/linux/include/asm-sparc/floppy.h	Mon Apr 29 18:05:19 1996
+++ linux/include/asm-sparc/floppy.h	Mon May  6 12:26:16 1996
@@ -290,7 +290,7 @@
 		/* We certainly don't have a floppy controller. */
 		goto no_sun_fdc;
 	}
-	/* Well, try and find one. */
+	/* Well, try to find one. */
 	tnode = prom_getchild(prom_root_node);
 	fd_node = prom_searchsiblings(tnode, "obio");
 	if(fd_node != 0) {
diff -u --recursive --new-file v1.3.98/linux/include/asm-sparc/head.h linux/include/asm-sparc/head.h
--- v1.3.98/linux/include/asm-sparc/head.h	Sat Apr 27 15:20:04 1996
+++ linux/include/asm-sparc/head.h	Mon May  6 12:26:16 1996
@@ -90,7 +90,7 @@
 #define NMI_TRAP \
         rd %wim, %l3; b linux_trap_nmi_sun4c; mov %psr, %l0; nop;
 
-/* Window overflows/underflows are special and we need to try and be as
+/* Window overflows/underflows are special and we need to try to be as
  * efficient as possible here....
  */
 #define WINDOW_SPILL \
diff -u --recursive --new-file v1.3.98/linux/include/asm-sparc/kdebug.h linux/include/asm-sparc/kdebug.h
--- v1.3.98/linux/include/asm-sparc/kdebug.h	Mon Mar  4 08:50:00 1996
+++ linux/include/asm-sparc/kdebug.h	Mon May  6 12:26:16 1996
@@ -26,7 +26,7 @@
 
 #ifndef __ASSEMBLY__
 /* The debug vector is passed in %o1 at boot time.  It is a pointer to
- * a structure in the debuggers address space.  Here is it's format.
+ * a structure in the debuggers address space.  Here is its format.
  */
 
 typedef unsigned int (*debugger_funct)(void);
diff -u --recursive --new-file v1.3.98/linux/include/asm-sparc/smp.h linux/include/asm-sparc/smp.h
--- v1.3.98/linux/include/asm-sparc/smp.h	Wed Apr 24 17:00:42 1996
+++ linux/include/asm-sparc/smp.h	Mon May  6 12:26:16 1996
@@ -26,7 +26,7 @@
 /* Per processor Sparc parameters we need. */
 
 struct cpuinfo_sparc {
-	unsigned long udelay_val; /* thats it */
+	unsigned long udelay_val; /* that's it */
 };
 
 extern struct cpuinfo_sparc cpu_data[NR_CPUS];
diff -u --recursive --new-file v1.3.98/linux/include/asm-sparc/system.h linux/include/asm-sparc/system.h
--- v1.3.98/linux/include/asm-sparc/system.h	Sun Apr 21 19:22:13 1996
+++ linux/include/asm-sparc/system.h	Mon May  6 12:26:16 1996
@@ -40,7 +40,7 @@
 #define halt() romvec->pv_halt()
 
 /* When a context switch happens we must flush all user windows so that
- * the windows of the current process are flushed onto it's stack. This
+ * the windows of the current process are flushed onto its stack. This
  * way the windows are all clean for the next process and the stack
  * frames are up to date.
  */
diff -u --recursive --new-file v1.3.98/linux/include/linux/a.out.h linux/include/linux/a.out.h
--- v1.3.98/linux/include/linux/a.out.h	Sun Apr 21 19:22:15 1996
+++ linux/include/linux/a.out.h	Mon May  6 12:44:32 1996
@@ -129,7 +129,7 @@
 
 #ifdef linux
 #include <asm/page.h>
-#ifdef __i386__
+#if defined(__i386__) || defined(__mc68000__)
 #define SEGMENT_SIZE	1024
 #else
 #ifndef SEGMENT_SIZE
diff -u --recursive --new-file v1.3.98/linux/include/linux/affs_fs.h linux/include/linux/affs_fs.h
--- v1.3.98/linux/include/linux/affs_fs.h	Tue Apr 23 13:57:13 1996
+++ linux/include/linux/affs_fs.h	Mon May  6 14:58:39 1996
@@ -1,101 +1,104 @@
-
 #ifndef _AFFS_FS_H
 #define _AFFS_FS_H
-
-#include <linux/types.h>
 /*
  * The affs filesystem constants/structures
  */
 
-#define AFFS_BLOCK_BITS 9
-#define AFFS_BLOCK_SIZE 512
-
-#define AFFS_BUFFER_BITS 9
-#define AFFS_BUFFER_SIZE 512
-
-#define AFFS_BLOCK_NUMBER(X) (X<<1)
+#include <linux/types.h>
+#include <linux/amigaffs.h>
 
 #define AFFS_SUPER_MAGIC 0xadff
 
 /* Get the filesystem block size given an inode. */
-#define AFFS_I2BSIZE(inode) ((inode)->i_sb->u.affs_sb.s_block_size)
+#define AFFS_I2BSIZE(inode) ((inode)->i_sb->s_blocksize)
 
-/* Read the device block that contains filesystem block ("sector"). */
+/* Get the filesystem hash table size given an inode. */
+#define AFFS_I2HSIZE(inode) ((inode)->i_sb->u.affs_sb.s_hashsize)
 
-static inline struct buffer_head *affs_sread(int dev,int sector,void **start)
-{
- 	struct buffer_head *bh;
-	int mask;
-
-	bh = bread (dev, sector >> (BLOCK_SIZE_BITS - AFFS_BLOCK_BITS), 1024);
-	if (!bh)
-		return NULL;
-	mask = (1 << (BLOCK_SIZE_BITS - AFFS_BLOCK_BITS)) - 1;
-    	*start = bh->b_data + ((sector & mask) << AFFS_BLOCK_BITS);
-	return bh;
-}
-
-/* Use affs_sread() to read a "sector", but take the filesystems partition
-   offset into account. */
-
-static inline struct buffer_head *affs_pread(struct inode *inode,
-					     int sector, void **start)
-{
-	int offset = inode->i_sb->u.affs_sb.s_partition_offset;
-	return affs_sread (inode->i_dev, sector + offset, start);
-}
-
-/* amigaffs.c prototypes */
-
-extern int affs_get_key_entry (int bsize, void *data, int entry_pos);
-extern int affs_find_next_hash_entry (int bsize, void *dir_data, int *hash_pos);
-extern int affs_get_fh_hash_link (int bsize, void *fh_data);
-extern int affs_get_file_name (int bsize, void *fh_data, char **name);
-extern int affs_get_extension (int bsize, void *fh_data);
-extern int affs_checksum_block (int bsize, void *data, int *ptype, int *stype);
-
-/* The stuff that follows may be totally unneeded. I have not checked to see 
- which prototypes we are still using.  */
-
-extern int affs_open(struct inode * inode, struct file * filp);
-extern void affs_release(struct inode * inode, struct file * filp);
-extern int affs_lookup(struct inode * dir,const char * name, int len,
-	struct inode ** result);
-extern unsigned long affs_count_free_inodes(struct super_block *sb);
-extern int affs_new_block(int dev);
-extern int affs_free_block(int dev, int block);
-extern int affs_bmap(struct inode *,int);
-
-extern void affs_put_super(struct super_block *);
-extern struct super_block *affs_read_super(struct super_block *,void *,int);
-extern void affs_read_inode(struct inode *);
-extern void affs_put_inode(struct inode *);
-extern void affs_statfs(struct super_block *, struct statfs *, int);
-extern int affs_parent_ino(struct inode *dir);
-extern int affs_lseek(struct inode *, struct file *, off_t, int);
-extern int affs_read(struct inode *, struct file *, char *, int);
-extern int affs_file_read(struct inode *, struct file *, char *, int);
-extern int init_affs_fs(void);
+/* Get the block number bits given an inode */
+#define AFFS_I2BITS(inode) ((inode)->i_sb->s_blocksize_bits)
 
-extern struct inode_operations affs_file_inode_operations;
-extern struct inode_operations affs_dir_inode_operations;
-extern struct inode_operations affs_symlink_inode_operations;
-extern struct inode_operations affs_chrdev_inode_operations;
-extern struct inode_operations affs_blkdev_inode_operations;
-
-extern struct file_operations affs_file_operations;
-extern struct file_operations affs_dir_operations;
-
-/* The following macros are used to check for memory leaks. */
-#ifdef LEAK_CHECK
-#define free_s leak_check_free_s
-#define malloc leak_check_malloc
-#define bread leak_check_bread
-#define brelse leak_check_brelse
-extern void * leak_check_malloc(unsigned int size);
-extern void leak_check_free_s(void * obj, int size);
-extern struct buffer_head * leak_check_bread(int dev, int block, int size);
-extern void leak_check_brelse(struct buffer_head * bh);
-#endif /* LEAK_CHECK */
+/* Get the fs type given an inode */
+#define AFFS_I2FSTYPE(inode) ((inode)->i_sb->u.affs_sb.s_flags & SF_INTL)
+
+/* --- Prototypes -----------------------------------------------------------------------------	*/
+
+/* amigaffs.c */
+
+extern int		   affs_get_key_entry(int bsize, void *data, int entry_pos);
+extern int		   affs_find_next_hash_entry(int bsize, void *dir_data, ULONG *hash_pos);
+extern int		   affs_get_file_name(int bsize, void *fh_data, char **name);
+extern ULONG		   affs_checksum_block(int bsize, void *data, LONG *ptype, LONG *stype);
+extern void		   affs_fix_checksum(int bsize, void *data, int cspos);
+extern void		   secs_to_datestamp(int secs, struct DateStamp *ds);
+extern int		   prot_to_mode(ULONG prot);
+extern ULONG		   mode_to_prot(int mode);
+extern int		   affs_fix_hash_pred(struct inode *startino, int startoffset,
+		 			      LONG key, LONG newkey);
+extern int		   affs_fix_link_pred(struct inode *startino, LONG key, LONG newkey);
+
+/* bitmap. c */
+
+extern int		   affs_count_free_blocks(struct super_block *s);
+extern int		   affs_count_free_bits(int blocksize, const UBYTE *data);
+extern void		   affs_free_block(struct super_block *sb, LONG block);
+extern LONG		   affs_new_header(struct inode *inode);
+extern LONG		   affs_new_data(struct inode *inode);
+extern void		   affs_make_zones(struct super_block *sb);
+
+/* namei.c */
+
+extern int		   affs_hash_name(const char *name, int len, int intl, int hashsize);
+extern int		   affs_lookup(struct inode *dir,const char *name, int len,
+				       struct inode **result);
+extern int		   affs_unlink(struct inode *dir, const char *name, int len);
+extern int		   affs_create(struct inode *dir, const char *name, int len, int mode,
+				       struct inode **result);
+extern int		   affs_mkdir(struct inode *dir, const char *name, int len, int mode);
+extern int		   affs_rmdir(struct inode *dir, const char *name, int len);
+extern int		   affs_link(struct inode *oldinode, struct inode *dir,
+				     const char *name, int len);
+extern int		   affs_symlink(struct inode *dir, const char *name, int len,
+				        const char *symname);
+extern int		   affs_fixup(struct buffer_head *bh, struct inode *inode);
+extern int		   affs_rename(struct inode *old_dir, const char *old_name, int old_len,
+				       struct inode *new_dir, const char *new_name, int new_len);
+
+/* inode.c */
+
+extern struct buffer_head *affs_bread(kdev_t dev, int block, int size);
+extern void		   affs_brelse(struct buffer_head *buf);
+extern void		   affs_put_super(struct super_block *);
+extern int		   affs_parent_ino(struct inode *dir);
+extern struct super_block *affs_read_super(struct super_block *,void *, int);
+extern void		   affs_statfs(struct super_block *, struct statfs *, int bufsiz);
+extern void		   affs_read_inode(struct inode *);
+extern void		   affs_write_inode(struct inode *);
+extern int		   affs_notify_change(struct inode *inode, struct iattr *attr);
+extern void		   affs_put_inode(struct inode *);
+extern struct inode	  *affs_new_inode(const struct inode *dir);
+extern int		   affs_add_entry(struct inode *dir, struct inode *link, struct inode *inode,
+					  const char *name, int len, LONG type);
+
+/* file.c */
+
+extern int		   affs_bmap(struct inode *inode, int block);
+extern struct buffer_head *affs_getblock(struct inode *inode, int block);
+extern void		   affs_truncate(struct inode *);
+extern void		   affs_truncate_ofs(struct inode *);
+
+/* dir.c */
+
+extern void		   affs_dir_truncate(struct inode *);
+
+/* jump tables */
+
+extern struct inode_operations	 affs_file_inode_operations;
+extern struct inode_operations	 affs_file_inode_operations_ofs;
+extern struct inode_operations	 affs_dir_inode_operations;
+extern struct inode_operations	 affs_symlink_inode_operations;
+extern struct inode_operations	 affs_chrdev_inode_operations;
+extern struct inode_operations	 affs_blkdev_inode_operations;
 
+extern int init_affs_fs(void);
 #endif
diff -u --recursive --new-file v1.3.98/linux/include/linux/affs_fs_i.h linux/include/linux/affs_fs_i.h
--- v1.3.98/linux/include/linux/affs_fs_i.h	Tue Apr 23 13:57:13 1996
+++ linux/include/linux/affs_fs_i.h	Mon May  6 12:44:32 1996
@@ -1,12 +1,25 @@
 #ifndef _AFFS_FS_I
 #define _AFFS_FS_I
 
+#define EXT_CACHE_SIZE	16
+#define MAX_PREALLOC	8	/* MUST be a power of 2 */
+
 /*
  * affs fs inode data in memory
  */
 struct affs_inode_info {
 	int i_protect;	/* unused attribute bits */
 	int i_parent;   /* parent ino */
+	int i_original;	/* if != 0, this is the key of the original */
+	__u32 i_ext[EXT_CACHE_SIZE]; /* extension block numbers */
+	__u32 i_data[MAX_PREALLOC]; /* preallocated blocks */
+	short i_max_ext; /* last known extension block */
+	short i_pa_cnt;	/* number of preallocated blocks */
+	short i_pa_next;  /* Index of next block in i_data[] */
+	short i_pa_last;  /* Index of next free slot in i_data[] */
+	short i_zone;	/* write zone */
+	unsigned char i_hlink;	/* This is a fake */
+	unsigned char i_pad;
 };
 
 #endif
diff -u --recursive --new-file v1.3.98/linux/include/linux/affs_fs_sb.h linux/include/linux/affs_fs_sb.h
--- v1.3.98/linux/include/linux/affs_fs_sb.h	Tue Apr 23 13:57:13 1996
+++ linux/include/linux/affs_fs_sb.h	Mon May  6 14:03:07 1996
@@ -4,34 +4,63 @@
 /*
  * super-block data in memory
  *
- * Block numbers are for FFS-sized (normally 512 bytes) blocks.
+ * Block numbers are adjusted for their actual size
  *
  */
 
-/* Mount options */
+#include <linux/amigaffs.h>
 
-struct affs_options {
-	int offset;
-	int size;
-	int root;
-	int nocase:1;			/* Ignore case in filenames. */
-	int conv_links:1;		/* convert pathnames symlinks point to */
+#define MAX_ZONES		8
+#define AFFS_DATA_MIN_FREE	30	/* Percentage of free blocks needed for a data zone */
+#define AFFS_HDR_MIN_FREE	10	/* Same for header blocks */
+
+struct affs_bm_info {
+	struct buffer_head *bm_bh;	/* Buffer for bitmap. */
+	int bm_free;			/* Free blocks. */
+	int bm_size;			/* Size in bits, rounded to multiple of 32. */
+	int bm_firstblk;		/* Block number of first bit in this map */
+};
+
+struct affs_zone {
+	unsigned long z_ino;		/* Associated inode number */
+	struct affs_bm_info *z_bm;	/* Zone lies in this bitmap */
+	int z_start;			/* Index of first word in bitmap */
+	int z_zone_no;			/* Zone number */
+	unsigned long z_lru_time;	/* Time of last usage */
 };
 
 struct affs_sb_info {
-	int s_partition_offset;         /* Offset to start in blocks. */
 	int s_partition_size;		/* Partition size in blocks. */
-	int s_root_block;		/* Absolute FFS root block number. */
-	int s_block_size;		/* Block size in bytes. */
-	char s_volume_name[42];
-	struct affs_options s_options;
+	int s_root_block;		/* FFS root block number. */
+	int s_hashsize;			/* Size of hash table. */
+	unsigned long s_flags;		/* See below. */
+	short s_uid;			/* uid to override */
+	short s_gid;			/* gid to override */
+	umode_t s_mode;			/* mode to override */
+	int s_reserved;			/* Number of reserved blocks. */
+	struct buffer_head *s_root_bh;	/* Cached root block. */
+	struct affs_bm_info *s_bitmap;	/* Bitmap infos. */
+	int s_bm_count;			/* Number of bitmap blocks. */
+	int s_nextzone;			/* Next zone to look for free blocks. */
+	int s_num_zones;		/* Total number of zones. */
+	struct affs_zone *s_zones;	/* The zones themselfes. */
+	char *s_zonemap;		/* Bitmap for zones. */
+	char *s_prefix;			/* Prefix for volumes and assignes. */
+	int s_prefix_len;		/* Length of prefix. */
+	char s_volume[32];		/* Volume prefix for absolute symlinks. */
 };
 
-#endif
-
-
-
-
-
-
+#define SF_INTL		0x0001		/* International filesystem. */
+#define SF_BM_VALID	0x0002		/* Bitmap is valid. */
+#define SF_IMMUTABLE	0x0004		/* Protection bits cannot be changed */
+#define SF_QUIET	0x0008		/* chmod errors will be not reported */
+#define SF_SETUID	0x0010		/* Ignore Amiga uid */
+#define SF_SETGID	0x0020		/* Ignore Amiga gid */
+#define SF_SETMODE	0x0040		/* Ignore Amiga protection bits */
+#define SF_USE_MP	0x0080		/* Use uid and gid from mount point */
+#define SF_MUFS		0x0100		/* Use MUFS uid/gid mapping */
+#define SF_OFS		0x0200		/* Old filesystem */
+#define SF_PREFIX	0x0400		/* Buffer for prefix is allocated */
+#define SF_VERBOSE	0x0800		/* Talk about fs when mounting */
 
+#endif
diff -u --recursive --new-file v1.3.98/linux/include/linux/affs_hardblocks.h linux/include/linux/affs_hardblocks.h
--- v1.3.98/linux/include/linux/affs_hardblocks.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/affs_hardblocks.h	Mon May  6 12:44:32 1996
@@ -0,0 +1,70 @@
+#ifndef	AFFS_HARDBLOCKS_H
+#define	AFFS_HARDBLOCKS_H
+
+/* Just the needed definitions for the RDB of an Amiga HD. */
+
+#ifndef AMIGAFFS_H
+#include <linux/amigaffs.h>
+#endif
+
+struct RigidDiskBlock {
+	ULONG	rdb_ID;
+	ULONG	rdb_SummedLongs;
+	LONG	rdb_ChkSum;
+	ULONG	rdb_HostID;
+	ULONG	rdb_BlockBytes;
+	ULONG	rdb_Flags;
+	ULONG	rdb_BadBlockList;
+	ULONG	rdb_PartitionList;
+	ULONG	rdb_FileSysHeaderList;
+	ULONG	rdb_DriveInit;
+	ULONG	rdb_Reserved1[6];
+	ULONG	rdb_Cylinders;
+	ULONG	rdb_Sectors;
+	ULONG	rdb_Heads;
+	ULONG	rdb_Interleave;
+	ULONG	rdb_Park;
+	ULONG	rdb_Reserved2[3];
+	ULONG	rdb_WritePreComp;
+	ULONG	rdb_ReducedWrite;
+	ULONG	rdb_StepRate;
+	ULONG	rdb_Reserved3[5];
+	ULONG	rdb_RDBBlocksLo;
+	ULONG	rdb_RDBBlocksHi;
+	ULONG	rdb_LoCylinder;
+	ULONG	rdb_HiCylinder;
+	ULONG	rdb_CylBlocks;
+	ULONG	rdb_AutoParkSeconds;
+	ULONG	rdb_HighRDSKBlock;
+	ULONG	rdb_Reserved4;
+	char	rdb_DiskVendor[8];
+	char	rdb_DiskProduct[16];
+	char	rdb_DiskRevision[4];
+	char	rdb_ControllerVendor[8];
+	char	rdb_ControllerProduct[16];
+	char	rdb_ControllerRevision[4];
+	ULONG	rdb_Reserved5[10];
+};
+
+#define	IDNAME_RIGIDDISK	0x5244534B	/* "RDSK" */
+
+struct PartitionBlock {
+	ULONG	pb_ID;
+	ULONG	pb_SummedLongs;
+	LONG	pb_ChkSum;
+	ULONG	pb_HostID;
+	ULONG	pb_Next;
+	ULONG	pb_Flags;
+	ULONG	pb_Reserved1[2];
+	ULONG	pb_DevFlags;
+	UBYTE	pb_DriveName[32];
+	ULONG	pb_Reserved2[15];
+	ULONG	pb_Environment[17];
+	ULONG	pb_EReserved[15];
+};
+
+#define	IDNAME_PARTITION	0x50415254	/* "PART" */
+
+#define RDB_ALLOCATION_LIMIT	16
+
+#endif	/* AFFS_HARDBLOCKS_H */
diff -u --recursive --new-file v1.3.98/linux/include/linux/amigaffs.h linux/include/linux/amigaffs.h
--- v1.3.98/linux/include/linux/amigaffs.h	Thu Jan  1 02:00:00 1970
+++ linux/include/linux/amigaffs.h	Mon May  6 14:02:33 1996
@@ -0,0 +1,224 @@
+#ifndef AMIGAFFS_H
+#define AMIGAFFS_H
+
+#include <asm/byteorder.h>
+#include <linux/types.h>
+
+/* Ugly macros make the code more pretty. */
+
+#define GET_END_PTR(st,p,sz)		 ((st *)((char *)(p)+((sz)-sizeof(st))))
+#define AFFS_GET_HASHENTRY(data,hashkey) htonl(((struct dir_front *)data)->hashtable[hashkey])
+#define AFFS_BLOCK(data,ino,blk)	 ((struct file_front *)data)->blocks[AFFS_I2HSIZE(ino)-1-blk]
+
+#define FILE_END(p,i)	GET_END_PTR(struct file_end,p,AFFS_I2BSIZE(i))
+#define ROOT_END(p,i)	GET_END_PTR(struct root_end,p,AFFS_I2BSIZE(i))
+#define DIR_END(p,i)	GET_END_PTR(struct dir_end,p,AFFS_I2BSIZE(i))
+#define LINK_END(p,i)	GET_END_PTR(struct hlink_end,p,AFFS_I2BSIZE(i))
+#define ROOT_END_S(p,s)	GET_END_PTR(struct root_end,p,(s)->s_blocksize)
+
+/* Only for easier debugging if need be */
+#define affs_bread	bread
+#define affs_brelse	brelse
+
+#ifdef __LITTLE_ENDIAN
+#define BO_EXBITS	0x18UL
+#elif defined(__BIG_ENDIAN)
+#define BO_EXBITS	0x00UL
+#else
+#error Endianess must be known for affs to work.
+#endif
+
+/* The following constants will be checked against the values read native */
+
+#define FS_OFS		0x444F5300
+#define FS_FFS		0x444F5301
+#define FS_INTLOFS	0x444F5302
+#define FS_INTLFFS	0x444F5303
+#define FS_DCOFS	0x444F5304
+#define FS_DCFFS	0x444F5305
+#define MUFS_FS		0x6d754653   /* 'muFS' */
+#define MUFS_OFS	0x6d754600   /* 'muF\0' */
+#define MUFS_FFS	0x6d754601   /* 'muF\1' */
+#define MUFS_INTLOFS	0x6d754602   /* 'muF\2' */
+#define MUFS_INTLFFS	0x6d754603   /* 'muF\3' */
+#define MUFS_DCOFS	0x6d754604   /* 'muF\4' */
+#define MUFS_DCFFS	0x6d754605   /* 'muF\5' */
+
+typedef __u32	ULONG;
+typedef __u16	UWORD;
+typedef __u8	UBYTE;
+
+typedef __s32	LONG;
+typedef __s16	WORD;
+typedef __s8	BYTE;
+
+struct DateStamp
+{
+  ULONG ds_Days;
+  ULONG ds_Minute;
+  ULONG ds_Tick;
+};
+
+#define T_SHORT		2
+#define T_LIST		16
+#define T_DATA		8
+
+#define ST_LINKFILE	-4
+#define ST_FILE		-3
+#define ST_ROOT		1
+#define ST_USERDIR	2
+#define ST_SOFTLINK	3
+#define ST_LINKDIR	4
+
+struct root_front
+{
+  LONG primary_type;
+  ULONG spare1[2];
+  ULONG hash_size;
+  ULONG spare2;
+  ULONG checksum;
+  ULONG hashtable[0];
+};
+
+struct root_end
+{
+  LONG bm_flag;
+  ULONG bm_keys[25];
+  ULONG bm_extend;
+  struct DateStamp dir_altered;
+  UBYTE disk_name[40];
+  struct DateStamp disk_altered;
+  struct DateStamp disk_made;
+  ULONG spare1[3];
+  LONG secondary_type;
+};
+
+struct dir_front
+{
+  LONG primary_type;
+  ULONG own_key;
+  ULONG spare1[3];
+  ULONG checksum;
+  ULONG hashtable[0];
+};
+
+struct dir_end
+{
+  ULONG spare1;
+  UWORD owner_uid;
+  UWORD owner_gid;
+  ULONG protect;
+  ULONG spare2;
+  UBYTE comment[92];
+  struct DateStamp created;
+  UBYTE dir_name[32];
+  ULONG spare3[2];
+  ULONG link_chain;
+  ULONG spare4[5];
+  ULONG hash_chain;
+  ULONG parent;
+  ULONG spare5;
+  LONG secondary_type;
+};
+
+struct file_front
+{
+  LONG primary_type;
+  ULONG own_key;
+  ULONG block_count;
+  ULONG unknown1;
+  ULONG first_data;
+  ULONG checksum;
+  ULONG blocks[0];
+};
+
+struct file_end
+{
+  ULONG spare1;
+  UWORD owner_uid;
+  UWORD owner_gid;
+  ULONG protect;
+  ULONG byte_size;
+  UBYTE comment[92];
+  struct DateStamp created;
+  UBYTE file_name[32];
+  ULONG spare2;
+  ULONG original;	/* not really in file_end */
+  ULONG link_chain;
+  ULONG spare3[5];
+  ULONG hash_chain;
+  ULONG parent;
+  ULONG extension;
+  LONG secondary_type;
+};
+
+struct hlink_front
+{
+  LONG primary_type;
+  ULONG own_key;
+  ULONG spare1[3];
+  ULONG checksum;
+};
+
+struct hlink_end
+{
+  ULONG spare1;
+  UWORD owner_uid;
+  UWORD owner_gid;
+  ULONG protect;
+  UBYTE comment[92];
+  struct DateStamp created;
+  UBYTE link_name[32];
+  ULONG spare2;
+  ULONG original;
+  ULONG link_chain;
+  ULONG spare3[5];
+  ULONG hash_chain;
+  ULONG parent;
+  ULONG spare4;
+  LONG secondary_type;
+};
+
+struct slink_front
+{
+  LONG primary_type;
+  ULONG own_key;
+  ULONG spare1[3];
+  ULONG checksum;
+  UBYTE	symname[288];	/* depends on block size */
+};
+
+/* Permission bits */
+
+#define FIBF_OTR_READ		0x8000
+#define FIBF_OTR_WRITE		0x4000
+#define FIBF_OTR_EXECUTE	0x2000
+#define FIBF_OTR_DELETE		0x1000
+#define FIBF_GRP_READ		0x0800
+#define FIBF_GRP_WRITE		0x0400
+#define FIBF_GRP_EXECUTE	0x0200
+#define FIBF_GRP_DELETE		0x0100
+
+#define FIBF_SCRIPT		0x0040
+#define FIBF_PURE		0x0020		/* no use under linux */
+#define FIBF_ARCHIVE		0x0010		/* never set, always cleared on write */
+#define FIBF_READ		0x0008		/* 0 means allowed */
+#define FIBF_WRITE		0x0004		/* 0 means allowed */
+#define FIBF_EXECUTE		0x0002		/* 0 means allowed, ignored under linux */
+#define FIBF_DELETE		0x0001		/* 0 means allowed */
+
+#define FIBF_OWNER		0x000F		/* Bits pertaining to owner */
+
+#define AFFS_UMAYWRITE(prot)	(((prot) & (FIBF_WRITE|FIBF_DELETE)) == (FIBF_WRITE|FIBF_DELETE))
+#define AFFS_UMAYREAD(prot)	((prot) & FIBF_READ)
+#define AFFS_UMAYEXECUTE(prot)	(((prot) & (FIBF_SCRIPT|FIBF_READ)) == (FIBF_SCRIPT|FIBF_READ))
+#define AFFS_GMAYWRITE(prot)	(((prot)&(FIBF_GRP_WRITE|FIBF_GRP_DELETE))==\
+							(FIBF_GRP_WRITE|FIBF_GRP_DELETE))
+#define AFFS_GMAYREAD(prot)	((prot) & FIBF_GRP_READ)
+#define AFFS_GMAYEXECUTE(prot)	(((prot)&(FIBF_SCRIPT|FIBF_GRP_READ))==(FIBF_SCRIPT|FIBF_GRP_READ))
+#define AFFS_OMAYWRITE(prot)	(((prot)&(FIBF_OTR_WRITE|FIBF_OTR_DELETE))==\
+							(FIBF_OTR_WRITE|FIBF_OTR_DELETE))
+#define AFFS_OMAYREAD(prot)	((prot) & FIBF_OTR_READ)
+#define AFFS_OMAYEXECUTE(prot)	(((prot)&(FIBF_SCRIPT|FIBF_OTR_READ))==(FIBF_SCRIPT|FIBF_OTR_READ))
+
+#endif
diff -u --recursive --new-file v1.3.98/linux/include/linux/genhd.h linux/include/linux/genhd.h
--- v1.3.98/linux/include/linux/genhd.h	Sat Apr 27 15:20:06 1996
+++ linux/include/linux/genhd.h	Tue May  7 14:12:47 1996
@@ -22,7 +22,7 @@
 #endif
 
 /* These two have identical behaviour; use the second one if DOS fdisk gets
-   confused about extended/logical partitions starting past cylinder 1023. */	
+   confused about extended/logical partitions starting past cylinder 1023. */
 #define DOS_EXTENDED_PARTITION 5
 #define LINUX_EXTENDED_PARTITION 0x85
 
diff -u --recursive --new-file v1.3.98/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v1.3.98/linux/include/linux/hdreg.h	Fri Apr 12 15:52:07 1996
+++ linux/include/linux/hdreg.h	Tue May  7 14:12:47 1996
@@ -8,7 +8,7 @@
 
 #define HD_IRQ 14		/* the standard disk interrupt */
 
-/* ide.c has it's own port definitions in "ide.h" */
+/* ide.c has its own port definitions in "ide.h" */
 
 /* Hd controller regs. Ref: IBM AT Bios-listing */
 #define HD_DATA		0x1f0	/* _CTL when writing */
diff -u --recursive --new-file v1.3.98/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v1.3.98/linux/include/linux/if_arp.h	Sat Apr 27 15:20:06 1996
+++ linux/include/linux/if_arp.h	Tue May  7 12:06:50 1996
@@ -111,18 +111,16 @@
 
 #define ARPD_UPDATE	0x01
 #define ARPD_LOOKUP	0x02
+#define ARPD_FLUSH	0x03
 
 struct arpd_request
 {
 	unsigned short	req;			/* request type */
 	__u32		ip;			/* ip address of entry */
-	__u32		mask;			/* netmask - used for proxy */
+	unsigned long	dev;			/* Device entry is tied to */
+	unsigned long	stamp;
+	unsigned long	updated;
 	unsigned char	ha[MAX_ADDR_LEN];	/* Hardware address */
-	unsigned long	last_used;		/* For expiry */
-	unsigned long	last_updated;		/* For expiry */
-	unsigned int	flags;			/* Control status */
-	struct device	*dev;			/* Device entry is tied to */
-	int		loc;			/* Debugging call location */
 };
 
 #endif	/* _LINUX_IF_ARP_H */
diff -u --recursive --new-file v1.3.98/linux/include/linux/if_frad.h linux/include/linux/if_frad.h
--- v1.3.98/linux/include/linux/if_frad.h	Mon Apr 15 12:20:21 1996
+++ linux/include/linux/if_frad.h	Mon May  6 12:26:16 1996
@@ -3,7 +3,7 @@
  *		created for each DLCI associated with a FRAD.  The FRAD driver
  *		is not truly a network device, but the lower level device
  *		handler.  This allows other FRAD manufacturers to use the DLCI
- *		code, including it's RFC1490 encapsulation along side the current
+ *		code, including its RFC1490 encapsulation alongside the current
  *		implementation for the Sangoma cards.
  *
  * Version:	@(#)if_ifrad.h	0.15	31 Mar 96
diff -u --recursive --new-file v1.3.98/linux/include/linux/in.h linux/include/linux/in.h
--- v1.3.98/linux/include/linux/in.h	Mon Mar 25 10:22:43 1996
+++ linux/include/linux/in.h	Mon May  6 12:26:16 1996
@@ -129,7 +129,7 @@
 
 /*
  *	IPv6 definitions as we start to include them. This is just
- *	a beginning dont get excited 8)
+ *	a beginning -- don't get excited 8)
  */
  
 struct in_addr6
diff -u --recursive --new-file v1.3.98/linux/include/linux/isdn.h linux/include/linux/isdn.h
--- v1.3.98/linux/include/linux/isdn.h	Sun Apr 21 19:22:16 1996
+++ linux/include/linux/isdn.h	Mon May  6 07:28:53 1996
@@ -184,7 +184,7 @@
 #ifdef CONFIG_ISDN_PPP
 
 #ifdef CONFIG_ISDN_PPP_VJ
-#  include "/usr/src/linux/drivers/net/slhc.h"
+#  include <net/slhc_vj.h>
 #endif
 
 #include <linux/ppp_defs.h>
diff -u --recursive --new-file v1.3.98/linux/include/linux/isdnif.h linux/include/linux/isdnif.h
--- v1.3.98/linux/include/linux/isdnif.h	Sun Apr 21 19:22:16 1996
+++ linux/include/linux/isdnif.h	Mon May  6 12:26:16 1996
@@ -2,7 +2,7 @@
  *
  * Linux ISDN subsystem
  *
- * Definition of the interface between the subsystem and it's lowlevel-drivers.
+ * Definition of the interface between the subsystem and its low-level drivers.
  *
  * Copyright 1994,95,96 by Fritz Elfert (fritz@wuemaus.franken.de)
  * Copyright 1995,96    Thinking Objects Software GmbH Wuerzburg
@@ -136,7 +136,7 @@
  * The interface-struct itself (initialized at load-time of lowlevel-driver)
  *
  * See Documentation/isdn/INTERFACE for a description, how the communication
- * between the ISDN subsystem and it's drivers is done.
+ * between the ISDN subsystem and its drivers is done.
  *
  */
 typedef struct {
diff -u --recursive --new-file v1.3.98/linux/include/linux/kernel.h linux/include/linux/kernel.h
--- v1.3.98/linux/include/linux/kernel.h	Sun May  5 08:52:04 1996
+++ linux/include/linux/kernel.h	Mon May  6 14:50:10 1996
@@ -57,8 +57,13 @@
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
 
-#define pr_debug(show,fmt,arg...) \
-	do { if (show) printk(KERN_DEBUG fmt,##arg); } while (0)
+#if DEBUG
+#define pr_debug(fmt,arg...) \
+	printk(KERN_DEBUG fmt,##arg)
+#else
+#define pr_debug(fmt,arg...) \
+	do { } while (0)
+#endif
 
 #define pr_info(fmt,arg...) \
 	printk(KERN_INFO fmt,##arg)
diff -u --recursive --new-file v1.3.98/linux/include/linux/math_emu.h linux/include/linux/math_emu.h
--- v1.3.98/linux/include/linux/math_emu.h	Wed Feb 16 13:07:57 1994
+++ linux/include/linux/math_emu.h	Mon May  6 16:31:18 1996
@@ -1,6 +1,10 @@
 #ifndef _LINUX_MATH_EMU_H
 #define _LINUX_MATH_EMU_H
 
+
+void restore_i387_soft(struct _fpstate *buf);
+struct _fpstate * save_i387_soft(struct _fpstate * buf);
+
 struct fpu_reg {
 	char sign;
 	char tag;
diff -u --recursive --new-file v1.3.98/linux/include/linux/mc146818rtc.h linux/include/linux/mc146818rtc.h
--- v1.3.98/linux/include/linux/mc146818rtc.h	Mon Apr 15 12:20:21 1996
+++ linux/include/linux/mc146818rtc.h	Tue May  7 08:18:50 1996
@@ -124,4 +124,22 @@
 #define RTC_IRQP_READ	0x0b		/* Read periodic IRQ rate (Hz)	*/
 #define RTC_IRQP_SET	0x0c		/* Set periodic IRQ rate (Hz)	*/
 
+/*
+ * The struct used to pass data via the above ioctl. Similar to the
+ * struct tm in <time.h>, but it needs to be here so that the kernel 
+ * source is self contained, allowing cross-compiles, etc. etc.
+ */
+
+struct rtc_time {
+	int tm_sec;
+	int tm_min;
+	int tm_hour;
+	int tm_mday;
+	int tm_mon;
+	int tm_year;
+	int tm_wday;
+	int tm_yday;
+	int tm_isdst;
+};
+
 #endif /* _MC146818RTC_H */
diff -u --recursive --new-file v1.3.98/linux/include/linux/mroute.h linux/include/linux/mroute.h
--- v1.3.98/linux/include/linux/mroute.h	Mon Mar 25 10:27:41 1996
+++ linux/include/linux/mroute.h	Mon May  6 12:26:16 1996
@@ -112,7 +112,7 @@
 };
 
 /*
- *	Thats all usermode folks
+ *	That's all usermode folks
  */
 
 #ifdef __KERNEL__
diff -u --recursive --new-file v1.3.98/linux/include/linux/msdos_fs.h linux/include/linux/msdos_fs.h
--- v1.3.98/linux/include/linux/msdos_fs.h	Mon Mar 25 10:25:11 1996
+++ linux/include/linux/msdos_fs.h	Tue May  7 14:16:16 1996
@@ -256,6 +256,21 @@
 /* fatfs_syms.c */
 extern int init_fat_fs(void);
 
+/* vfat/namei.c - these are for dmsdos */
+extern int vfat_create(struct inode *dir,const char *name,int len,int mode,
+		       struct inode **result);
+extern int vfat_unlink(struct inode *dir,const char *name,int len);
+extern int vfat_mkdir(struct inode *dir,const char *name,int len,int mode);
+extern int vfat_rmdir(struct inode *dir,const char *name,int len);
+extern int vfat_rename(struct inode *old_dir,const char *old_name,int old_len,
+		       struct inode *new_dir,const char *new_name,int new_len);
+extern void vfat_put_super(struct super_block *sb);
+extern struct super_block *vfat_read_super(struct super_block *sb,void *data,
+					   int silent);
+extern void vfat_read_inode(struct inode *inode);
+extern int vfat_lookup(struct inode *dir,const char *name,int len,
+		       struct inode **result);
+
 #endif /* __KERNEL__ */
 
 #endif
diff -u --recursive --new-file v1.3.98/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.3.98/linux/include/linux/netdevice.h	Sun Apr 21 12:39:03 1996
+++ linux/include/linux/netdevice.h	Tue May  7 14:14:53 1996
@@ -71,7 +71,7 @@
 					 * any address resolution module,
 					 * not only ARP.
 					 */
-	unsigned int	hh_refcnt;	/* number of users */
+	int		hh_refcnt;	/* number of users */
 	unsigned short  hh_type;	/* protocol identifier, f.e ETH_P_IP */
 	char		hh_uptodate;	/* hh_data is valid */
 	char		hh_data[16];    /* cached hardware header */
@@ -218,7 +218,7 @@
 
 extern int		ip_addr_match(unsigned long addr1, unsigned long addr2);
 extern int		ip_chk_addr(unsigned long addr);
-extern struct device	*ip_dev_check(unsigned long daddr);
+extern struct device	*ip_dev_bynet(unsigned long daddr, unsigned long mask);
 extern unsigned long	ip_my_addr(void);
 extern unsigned long	ip_get_mask(unsigned long addr);
 extern struct device 	*ip_dev_find(unsigned long addr);
@@ -246,7 +246,7 @@
 extern int		dev_lockct;
 
 /*
- *	These two dont currently need to be interrupt safe
+ *	These two don't currently need to be interrupt-safe
  *	but they may do soon. Do it properly anyway.
  */
 
diff -u --recursive --new-file v1.3.98/linux/include/linux/skbuff.h linux/include/linux/skbuff.h
--- v1.3.98/linux/include/linux/skbuff.h	Mon Apr 29 18:05:20 1996
+++ linux/include/linux/skbuff.h	Tue May  7 14:12:57 1996
@@ -392,7 +392,11 @@
 	skb->tail+=len;
 	skb->len+=len;
 	if(skb->tail>skb->end)
-		panic("skput:over: %p:%d", __builtin_return_address(0),len);
+	{
+		__label__ here;
+		panic("skput:over: %p:%d", &&here,len);
+here:
+	}
 	return tmp;
 }
 
@@ -401,7 +405,11 @@
 	skb->data-=len;
 	skb->len+=len;
 	if(skb->data<skb->head)
-		panic("skpush:under: %p:%d", __builtin_return_address(0),len);
+	{
+		__label__ here;
+		panic("skpush:under: %p:%d", &&here,len);
+here:
+	}
 	return skb->data;
 }
 
diff -u --recursive --new-file v1.3.98/linux/include/linux/string.h linux/include/linux/string.h
--- v1.3.98/linux/include/linux/string.h	Mon Mar 25 10:22:43 1996
+++ linux/include/linux/string.h	Sun May  5 15:00:23 1996
@@ -17,6 +17,7 @@
 extern char * strcat(char *, const char *);
 extern char * strncat(char *, const char *, size_t);
 extern char * strchr(const char *,int);
+extern char * strrchr(const char *,int);
 extern char * strpbrk(const char *,const char *);
 extern char * strtok(char *,const char *);
 extern char * strstr(const char *,const char *);
diff -u --recursive --new-file v1.3.98/linux/include/net/netlink.h linux/include/net/netlink.h
--- v1.3.98/linux/include/net/netlink.h	Mon Mar 25 10:12:54 1996
+++ linux/include/net/netlink.h	Tue May  7 14:18:35 1996
@@ -2,7 +2,7 @@
 #define __NET_NETLINK_H
 
 #define NET_MAJOR 36		/* Major 18 is reserved for networking 						*/
-#define MAX_LINKS 8		/* 18,0 for route updates, 18,1 for SKIP, 18,2 debug tap 18,3 PPP reserved 	*/
+#define MAX_LINKS 9		/* 18,0 for route updates, 18,1 for SKIP, 18,2 debug tap 18,3 PPP reserved 	*/
 				/* 4-7 are psi0-psi3 */
 #define MAX_QBYTES 32768	/* Maximum bytes in the queue 							*/
 
@@ -19,6 +19,7 @@
 #define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
 #define NETLINK_FIREWALL	3	/* Firewalling hook				*/
 #define NETLINK_PSI		4	/* PSI devices - 4 to 7 */
+#define NETLINK_ARPD		8
 
 #ifdef CONFIG_RTNETLINK
 extern void ip_netlink_msg(unsigned long, __u32, __u32, __u32, short, short, char *);
diff -u --recursive --new-file v1.3.98/linux/include/net/route.h linux/include/net/route.h
--- v1.3.98/linux/include/net/route.h	Sun May  5 08:52:05 1996
+++ linux/include/net/route.h	Tue May  7 15:24:06 1996
@@ -62,120 +62,16 @@
 #define RTF_LOCAL 0x8000
 #endif
 
-/*
- * Semaphores.
- */
-#if defined(__alpha__)
-
-static __inline__ void ATOMIC_INCR(unsigned int * addr)
-{
-	unsigned tmp;
-
-	__asm__ __volatile__(
-		"1:\n\
-		 ldl_l %1,%2\n\
-		 addl  %1,1,%1\n\
-		 stl_c %1,%0\n\
-		 beq   %1,1b\n"
-		: "m=" (*addr), "r=&" (tmp)
-		: "m"(*addr));
-}
-
-static __inline__ void ATOMIC_DECR(unsigned int * addr)
-{
-	unsigned tmp;
-
-	__asm__ __volatile__(
-		"1:\n\
-		 ldl_l %1,%2\n\
-		 subl  %1,1,%1\n\
-		 stl_c %1,%0\n\
-		 beq   %1,1b\n"
-		: "m=" (*addr), "r=&" (tmp)
-		: "m"(*addr));
-}
-
-static __inline__ int ATOMIC_DECR_AND_CHECK (unsigned int * addr)
-{
-	unsigned tmp;
-	int result;
-
-	__asm__ __volatile__(
-		"1:\n\
-		 ldl_l %1,%3\n\
-		 subl  %1,1,%1\n\
-		 mov   %1,%2\n\
-		 stl_c %1,%0\n\
-		 beq   %1,1b\n"
-		: "m=" (*addr), "r=&" (tmp), "r=&"(result)
-		: "m"(*addr));
-	return result;
-}
-
-#elif defined(__i386__)
-#include <asm/bitops.h>
-
-extern __inline__ void ATOMIC_INCR(void * addr)
-{
-	__asm__ __volatile__(
-		"incl %0"
-		:"=m" (ADDR));
-}
-
-extern __inline__ void ATOMIC_DECR(void * addr)
-{
-	__asm__ __volatile__(
-		"decl %0"
-		:"=m" (ADDR));
-}
-
-/*
- * It is DECR that is ATOMIC, not CHECK!
- * If you want to do atomic checks, use cli()/sti(). --ANK
- */
-
-extern __inline__ unsigned long ATOMIC_DECR_AND_CHECK(void * addr)
-{
-	unsigned long retval;
-	__asm__ __volatile__(
-		"decl %0\nmovl %0,%1"
-		: "=m" (ADDR), "=r"(retval));
-	return retval;
-}
-
-
-#else
-
-static __inline__ void ATOMIC_INCR(unsigned int * addr)
-{
-	(*(__volatile__ unsigned int*)addr)++;
-}
-
-static __inline__ void ATOMIC_DECR(unsigned int * addr)
-{
-	(*(__volatile__ unsigned int*)addr)--;
-}
-
-static __inline__ int ATOMIC_DECR_AND_CHECK (unsigned int * addr)
-{
-	ATOMIC_DECR(addr);
-	return *(volatile unsigned int*)addr;
-}
-
-#endif
-
-
-
 struct rtable 
 {
 	struct rtable		*rt_next;
 	__u32			rt_dst;
 	__u32			rt_src;
 	__u32			rt_gateway;
-	unsigned		rt_refcnt;
-	unsigned		rt_use;
+	atomic_t		rt_refcnt;
+	atomic_t		rt_use;
 	unsigned long		rt_window;
-	unsigned long		rt_lastuse;
+	atomic_t		rt_lastuse;
 	struct hh_cache		*rt_hh;
 	struct device		*rt_dev;
 	unsigned short		rt_flags;
@@ -185,6 +81,7 @@
 };
 
 extern void		ip_rt_flush(struct device *dev);
+extern void		ip_rt_update(int event, struct device *dev);
 extern void		ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev);
 extern struct rtable	*ip_rt_slow_route(__u32 daddr, int local);
 extern int		rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy);
@@ -196,23 +93,23 @@
 extern void		ip_rt_advice(struct rtable **rp, int advice);
 
 extern void		ip_rt_run_bh(void);
-extern int	    	ip_rt_lock;
+extern atomic_t	    	ip_rt_lock;
 extern unsigned		ip_rt_bh_mask;
 extern struct rtable 	*ip_rt_hash_table[RT_HASH_DIVISOR];
 
 extern __inline__ void ip_rt_fast_lock(void)
 {
-	ATOMIC_INCR(&ip_rt_lock);
+	atomic_inc(&ip_rt_lock);
 }
 
 extern __inline__ void ip_rt_fast_unlock(void)
 {
-	ATOMIC_DECR(&ip_rt_lock);
+	atomic_dec(&ip_rt_lock);
 }
 
 extern __inline__ void ip_rt_unlock(void)
 {
-	if (!ATOMIC_DECR_AND_CHECK(&ip_rt_lock) && ip_rt_bh_mask)
+	if (atomic_dec_and_test(&ip_rt_lock) && ip_rt_bh_mask)
 		ip_rt_run_bh();
 }
 
@@ -227,7 +124,7 @@
 #ifndef MODULE
 {
 	if (rt)
-		ATOMIC_DECR(&rt->rt_refcnt);
+		atomic_dec(&rt->rt_refcnt);
 }
 #else
 ;
@@ -248,8 +145,8 @@
 		if (rth->rt_dst == daddr)
 		{
 			rth->rt_lastuse = jiffies;
-			ATOMIC_INCR(&rth->rt_use);
-			ATOMIC_INCR(&rth->rt_refcnt);
+			atomic_inc(&rth->rt_use);
+			atomic_inc(&rth->rt_refcnt);
 			ip_rt_unlock();
 			return rth;
 		}
diff -u --recursive --new-file v1.3.98/linux/include/net/slhc_vj.h linux/include/net/slhc_vj.h
--- v1.3.98/linux/include/net/slhc_vj.h	Thu Jan  1 02:00:00 1970
+++ linux/include/net/slhc_vj.h	Mon May  6 07:28:54 1996
@@ -0,0 +1,187 @@
+#ifndef _SLHC_H
+#define _SLHC_H
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *	Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ *	- Initial distribution.
+ *
+ *
+ * modified for KA9Q Internet Software Package by
+ * Katie Stevens (dkstevens@ucdavis.edu)
+ * University of California, Davis
+ * Computing Services
+ *	- 01-31-90	initial adaptation
+ *
+ *	- Feb 1991	Bill_Simpson@um.cc.umich.edu
+ *			variable number of conversation slots
+ *			allow zero or one slots
+ *			separate routines
+ *			status display
+ */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits).  The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet.  The next two octets are the TCP checksum
+ * from the original datagram.  The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowledgment, sequence number and IP ID.  (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.)  Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0.  (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type.  There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows.  Top
+ * three bits are actual packet type.  For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* SLIP compression masks for len/vers byte */
+#define SL_TYPE_IP 0x40
+#define SL_TYPE_UNCOMPRESSED_TCP 0x70
+#define SL_TYPE_COMPRESSED_TCP 0x80
+#define SL_TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C	0x40	/* flag bits for what changed in a packet */
+#define NEW_I	0x20
+#define NEW_S	0x08
+#define NEW_A	0x04
+#define NEW_W	0x02
+#define NEW_U	0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U)		/* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U)	/* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+/*
+ * data type and sizes conversion assumptions:
+ *
+ *	VJ code		KA9Q style	generic
+ *	u_char		byte_t		unsigned char	 8 bits
+ *	u_short		int16		unsigned short	16 bits
+ *	u_int		int16		unsigned short	16 bits
+ *	u_long		unsigned long	unsigned long	32 bits
+ *	int		int32		long		32 bits
+ */
+
+typedef unsigned char byte_t;
+typedef unsigned long int32;
+
+/*
+ * "state" data for each active tcp conversation on the wire.  This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+	byte_t	cs_this;	/* connection id number (xmit) */
+	struct cstate *next;	/* next in ring (xmit) */
+	struct iphdr cs_ip;	/* ip/tcp hdr from most recent packet */
+	struct tcphdr cs_tcp;
+	unsigned char cs_ipopt[64];
+	unsigned char cs_tcpopt[64];
+	int cs_hsize;
+};
+#define NULLSLSTATE	(struct cstate *)0
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct slcompress {
+	struct cstate *tstate;	/* transmit connection states (array)*/
+	struct cstate *rstate;	/* receive connection states (array)*/
+
+	byte_t tslot_limit;	/* highest transmit slot id (0-l)*/
+	byte_t rslot_limit;	/* highest receive slot id (0-l)*/
+
+	byte_t xmit_oldest;	/* oldest xmit in ring */
+	byte_t xmit_current;	/* most recent xmit id */
+	byte_t recv_current;	/* most recent rcvd id */
+
+	byte_t flags;
+#define SLF_TOSS	0x01	/* tossing rcvd frames until id received */
+
+	int32 sls_o_nontcp;	/* outbound non-TCP packets */
+	int32 sls_o_tcp;	/* outbound TCP packets */
+	int32 sls_o_uncompressed;	/* outbound uncompressed packets */
+	int32 sls_o_compressed;	/* outbound compressed packets */
+	int32 sls_o_searches;	/* searches for connection state */
+	int32 sls_o_misses;	/* times couldn't find conn. state */
+
+	int32 sls_i_uncompressed;	/* inbound uncompressed packets */
+	int32 sls_i_compressed;	/* inbound compressed packets */
+	int32 sls_i_error;	/* inbound error packets */
+	int32 sls_i_tossed;	/* inbound packets tossed because of error */
+
+	int32 sls_i_runt;
+	int32 sls_i_badcheck;
+};
+#define NULLSLCOMPR	(struct slcompress *)0
+
+#define __ARGS(x) x
+
+/* In slhc.c: */
+struct slcompress *slhc_init __ARGS((int rslots, int tslots));
+void slhc_free __ARGS((struct slcompress *comp));
+
+int slhc_compress __ARGS((struct slcompress *comp, unsigned char *icp,
+			  int isize, unsigned char *ocp, unsigned char **cpp,
+			  int compress_cid));
+int slhc_uncompress __ARGS((struct slcompress *comp, unsigned char *icp,
+			    int isize));
+int slhc_remember __ARGS((struct slcompress *comp, unsigned char *icp,
+			  int isize));
+int slhc_toss __ARGS((struct slcompress *comp));
+
+void slhc_i_status __ARGS((struct slcompress *comp));
+void slhc_o_status __ARGS((struct slcompress *comp));
+
+#endif	/* _SLHC_H */
diff -u --recursive --new-file v1.3.98/linux/include/net/sock.h linux/include/net/sock.h
--- v1.3.98/linux/include/net/sock.h	Fri Apr 19 10:08:02 1996
+++ linux/include/net/sock.h	Tue May  7 14:14:58 1996
@@ -488,7 +488,7 @@
  *	protocols can't normally use this as they need to fit buffers in
  *	and play with them.
  *
- * 	Inlined as its very short and called for pretty much every
+ * 	Inlined as it's very short and called for pretty much every
  *	packet ever received.
  */
 
diff -u --recursive --new-file v1.3.98/linux/include/net/tcp.h linux/include/net/tcp.h
--- v1.3.98/linux/include/net/tcp.h	Sat Apr 27 15:20:07 1996
+++ linux/include/net/tcp.h	Tue May  7 15:24:31 1996
@@ -192,50 +192,7 @@
 	return sk->window - (sk->acked_seq - sk->lastwin_seq);
 }
 
-static __inline__ int tcp_new_window(struct sock * sk)
-{
-	int window = sock_rspace(sk);
-
-	if (window > 1024)
-		window &= ~0x3FF;	/* make free space a multiple of 1024 */
-
-	if (sk->window_clamp && sk->window_clamp < window)
-		window = sk->window_clamp;
-
-	/*
-	 * RFC 1122 says:
-	 *
-	 * "the suggested [SWS] avoidance algorithm for the receiver is to keep
-	 *  RECV.NEXT + RCV.WIN fixed until:
-	 *  RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
-	 *
-	 * Experiments against BSD and Solaris machines show that following
-	 * these rules results in the BSD and Solaris machines making very
-	 * bad guesses about how much data they can have in flight.
-	 *
-	 * Instead we follow the BSD lead and offer a window that gives
-	 * the size of the current free space, truncated to a multiple
-	 * of 1024 bytes. If the window is smaller than
-	 * 	min(sk->mss, MAX_WINDOW/2)
-	 * then we advertise the window as having size 0, unless this
-	 * would shrink the window we offered last time.
-	 * This results in as much as double the throughput as the original
-	 * implementation.
-	 */
-
-	if (sk->mss == 0)
-		sk->mss = sk->mtu;
-
-	/* BSD style SWS avoidance
-	 * Note that RFC1122 only says we must do silly window avoidance,
-	 * it does not require that we use the suggested algorithm.
-	 */
-
-	if (window < min(sk->mss, MAX_WINDOW/2))
-		window = 0;
-
-	return window;
-}
+extern int tcp_new_window(struct sock *);
 
 /*
  * Return true if we should raise the window when we
@@ -247,7 +204,8 @@
  */
 static __inline__ int tcp_raise_window(struct sock * sk)
 {
-	return tcp_new_window(sk) >= 2*tcp_old_window(sk);
+	int new = tcp_new_window(sk);
+	return new && (new >= 2*tcp_old_window(sk));
 }
 
 static __inline__ unsigned short tcp_select_window(struct sock *sk)
diff -u --recursive --new-file v1.3.98/linux/ipc/msg.c linux/ipc/msg.c
--- v1.3.98/linux/ipc/msg.c	Fri Apr 12 15:52:09 1996
+++ linux/ipc/msg.c	Mon May  6 12:09:01 1996
@@ -159,7 +159,8 @@
  * Take care of missing kerneld, especially in case of multiple daemons
  */
 #define KERNELD_TIMEOUT 1 * (HZ)
-#define DROP_TIMER if ((msgflg & IPC_KERNELD) && kd_timer.next && kd_timer.prev) del_timer(&kd_timer)
+#define DROP_TIMER del_timer(&kd_timer)
+/*#define DROP_TIMER if ((msgflg & IPC_KERNELD) && kd_timer.next && kd_timer.prev) del_timer(&kd_timer)*/
 
 static void kd_timeout(unsigned long msgid)
 {
@@ -185,7 +186,7 @@
 
 static int real_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg)
 {
-	struct timer_list kd_timer;
+	struct timer_list kd_timer = { NULL, NULL, 0, 0, 0};
 	struct msqid_ds *msq;
 	struct ipc_perm *ipcp;
 	struct msg *tmsg, *leastp = NULL;
@@ -229,16 +230,20 @@
 	 *  msgtyp < 0 => get message with least type must be < abs(msgtype).  
 	 */
 	while (!nmsg) {
-		if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI)
+		if (msq->msg_perm.seq != (unsigned int) msqid / MSGMNI) {
+			DROP_TIMER;
 			return -EIDRM;
+		}
 		if ((msgflg & IPC_KERNELD) == 0) {
 			/*
 			 * Non-root processes may receive from kerneld! 
 			 * i.e. no permission check if called from the kernel
 			 * otoh we don't want user level non-root snoopers...
 			 */
-			if (ipcperms (ipcp, S_IRUGO))
+			if (ipcperms (ipcp, S_IRUGO)) {
+				DROP_TIMER;
 				return -EACCES;
+			}
 		}
 		if (msgtyp == 0) 
 			nmsg = msq->msg_first;
@@ -267,8 +272,10 @@
 		
 		if (nmsg) { /* done finding a message */
 			DROP_TIMER;
-			if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR))
+			if ((msgsz < nmsg->msg_ts) && !(msgflg & MSG_NOERROR)) {
+				DROP_TIMER;
 				return -E2BIG;
+			}
 			msgsz = (msgsz > nmsg->msg_ts)? nmsg->msg_ts : msgsz;
 			if (nmsg ==  msq->msg_first)
 				msq->msg_first = nmsg->msg_next;
@@ -315,6 +322,7 @@
 				memcpy_tofs (msgp->mtext, nmsg->msg_spot, msgsz);
 			}
 			kfree(nmsg);
+			DROP_TIMER;
 			return msgsz;
 		} else {  /* did not find a message */
 			if (msgflg & IPC_NOWAIT) {
diff -u --recursive --new-file v1.3.98/linux/kernel/module.c linux/kernel/module.c
--- v1.3.98/linux/kernel/module.c	Sun Apr 21 19:22:16 1996
+++ linux/kernel/module.c	Mon May  6 14:37:42 1996
@@ -46,12 +46,6 @@
 
 #ifdef CONFIG_MODULES		/* a *big* #ifdef block... */
 
-#ifdef DEBUG_MODULE
-#define PRINTK(a) printk a
-#else
-#define PRINTK(a) /* */
-#endif
-
 static struct module kernel_module;
 static struct module *module_list = &kernel_module;
 
@@ -127,8 +121,8 @@
 	* (long *) addr = 0;	/* set use count to zero */
 	module_list = mp;	/* link it in */
 
-	PRINTK(("module `%s' (%lu pages @ 0x%08lx) created\n",
-		mp->name, (unsigned long) mp->size, (unsigned long) mp->addr));
+	pr_debug("module `%s' (%lu pages @ 0x%08lx) created\n",
+		mp->name, (unsigned long) mp->size, (unsigned long) mp->addr);
 	return (unsigned long) addr;
 }
 
@@ -167,8 +161,8 @@
 
 	if ((error = get_mod_name(module_name, name)) != 0)
 		return error;
-	PRINTK(("initializing module `%s', %d (0x%x) bytes\n",
-		name, codesize, codesize));
+	pr_debug("initializing module `%s', %d (0x%x) bytes\n",
+		name, codesize, codesize);
 	memcpy_fromfs(&rt, routines, sizeof rt);
 	if ((mp = find_module(name)) == NULL)
 		return -ENOENT;
@@ -185,8 +179,8 @@
 	memcpy_fromfs((char *)mp->addr + sizeof (long), code, codesize);
 	memset((char *)mp->addr + sizeof (long) + codesize, 0,
 		mp->size * PAGE_SIZE - (codesize + sizeof (long)));
-	PRINTK(( "module init entry = 0x%08lx, cleanup entry = 0x%08lx\n",
-		(unsigned long) rt.init, (unsigned long) rt.cleanup));
+	pr_debug("module init entry = 0x%08lx, cleanup entry = 0x%08lx\n",
+		(unsigned long) rt.init, (unsigned long) rt.cleanup);
 	mp->cleanup = rt.cleanup;
 
 	/* update kernel symbol table */
diff -u --recursive --new-file v1.3.98/linux/kernel/sched.c linux/kernel/sched.c
--- v1.3.98/linux/kernel/sched.c	Sat Apr 27 15:20:09 1996
+++ linux/kernel/sched.c	Tue May  7 12:06:51 1996
@@ -121,8 +121,7 @@
 	init_task.prev_run = p;
 #ifdef __SMP__
 	/* this is safe only if called with cli()*/
-	while(set_bit(31,&smp_process_available));
-#if 0	
+	while(set_bit(31,&smp_process_available))
 	{
 		while(test_bit(31,&smp_process_available))
 		{
@@ -133,7 +132,6 @@
 			}
 		}
 	}
-#endif	
 	smp_process_available++;
 	clear_bit(31,&smp_process_available);
 	if ((0!=p->pid) && smp_threads_ready)
@@ -242,6 +240,11 @@
 	/* We are not permitted to run a task someone else is running */
 	if (p->processor != NO_PROC_ID)
 		return -1000;
+#ifdef PAST_2_0		
+	/* This process is locked to a processor group */
+	if (p->processor_mask && !(p->processor_mask & (1<<this_cpu))
+		return -1000;
+#endif		
 #endif
 
 	/*
@@ -340,7 +343,7 @@
 	 *	This is safe as we do not permit re-entry of schedule()
 	 */
 	prev->processor = NO_PROC_ID;
-#define idle_task (task[this_cpu])
+#define idle_task (task[cpu_number_map[this_cpu]])
 #else
 #define idle_task (&init_task)
 #endif	
diff -u --recursive --new-file v1.3.98/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.98/linux/mm/filemap.c	Sun May  5 08:52:05 1996
+++ linux/mm/filemap.c	Mon May  6 12:44:32 1996
@@ -779,7 +779,8 @@
 		free_page(page);
 		return new_page;
 	}
-	flush_page_to_ram(page);
+	if (page)
+		flush_page_to_ram(page);
 	return page;
 }
 
diff -u --recursive --new-file v1.3.98/linux/mm/swapfile.c linux/mm/swapfile.c
--- v1.3.98/linux/mm/swapfile.c	Sat Mar 16 11:55:40 1996
+++ linux/mm/swapfile.c	Mon May  6 12:42:08 1996
@@ -187,6 +187,7 @@
 		return 1;
 	}
 	set_pte(dir, pte_mkwrite(pte_mkdirty(mk_pte(page, vma->vm_page_prot))));
+	flush_tlb_page(vma, address);
 	++vma->vm_mm->rss;
 	swap_free(pte_val(pte));
 	return 1;
diff -u --recursive --new-file v1.3.98/linux/net/Changes linux/net/Changes
--- v1.3.98/linux/net/Changes	Fri Apr 12 15:52:10 1996
+++ linux/net/Changes	Mon May  6 12:26:16 1996
@@ -376,7 +376,7 @@
 	load.						[TRYING THINGS]
 o	AX.25/NetROM needs more locking.
 o	NFS flow control is needed with the new multirequest NFS support.
-o	Need to be able to turn off the intelligent arp refreshing as its not so
+o	Need to be able to turn off the intelligent arp refreshing as it's not so
 	hot over AX.25 and upsets some people with very dumb ISDN bridges.
 o	Matti Arnio's TCP problem.
 o	Should unix domain connect never block ?
@@ -435,7 +435,7 @@
 problem. [See the LiS project]
 
 10.	Frame Relay/WAN/ISDN drivers [I'm working on the sonix EuroISDN board
-driver but thats for an internal project and its general release is still
+driver but that's for an internal project and its general release is still
 a maybe (so is finishing it ;))][Jim Freeman is working on Frame Relay as is
 Mike McLagan][Friz Elfert is doing the isdn4linux kit].
  
diff -u --recursive --new-file v1.3.98/linux/net/TUNABLE linux/net/TUNABLE
--- v1.3.98/linux/net/TUNABLE	Fri Apr 12 15:52:10 1996
+++ linux/net/TUNABLE	Mon May  6 12:26:16 1996
@@ -35,7 +35,7 @@
 MAX_HEADER		Largest physical header (tunable)
 MAX_ADDR_LEN		Largest physical address (tunable)
 SOCK_ARRAY_SIZE		IP socket array hash size (tunable)
-ARP_RES_TIME		Time we try and resolve (tunable)
+ARP_RES_TIME		Time we try to resolve (tunable)
 ARP_DEAD_RES_TIME	Time the entry stays dead (tunable)
 ARP_MAX_TRIES		Maximum tries (tunable)
 ARP_TIMEOUT		Timeout on an ARP (tunable)
diff -u --recursive --new-file v1.3.98/linux/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- v1.3.98/linux/net/ax25/af_ax25.c	Sat Apr 27 15:20:10 1996
+++ linux/net/ax25/af_ax25.c	Mon May  6 12:26:17 1996
@@ -13,7 +13,7 @@
  *		2 of the License, or (at your option) any later version.
  *
  *	History
- *	AX.25 006	Alan(GW4PTS)		Nearly died of shock - its working 8-)
+ *	AX.25 006	Alan(GW4PTS)		Nearly died of shock - it's working 8-)
  *	AX.25 007	Alan(GW4PTS)		Removed the silliest bugs
  *	AX.25 008	Alan(GW4PTS)		Cleaned up, fixed a few state machine problems, added callbacks
  *	AX.25 009	Alan(GW4PTS)		Emergency patch kit to fix memory corruption
@@ -25,7 +25,7 @@
  *						Correct receive on SOCK_DGRAM.
  *	AX.25 013	Alan(GW4PTS)		Send DM to all unknown frames, missing initialiser fixed
  *						Leave spare SSID bits set (DAMA etc) - thanks for bug report,
- *						removed device registration (its not used or needed). Clean up for
+ *						removed device registration (it's not used or needed). Clean up for
  *						gcc 2.5.8. PID to AX25_P_
  *	AX.25 014	Alan(GW4PTS)		Cleanup and NET3 merge
  *	AX.25 015	Alan(GW4PTS)		Internal test version.
@@ -409,7 +409,7 @@
  *	Once it is removed from the queue no interrupt or bottom half will
  *	touch it and we are (fairly 8-) ) safe.
  */
-void ax25_destroy_socket(ax25_cb *ax25)	/* Not static as its used by the timer */
+void ax25_destroy_socket(ax25_cb *ax25)	/* Not static as it's used by the timer */
 {
 	struct sk_buff *skb;
 	unsigned long flags;
@@ -1893,7 +1893,7 @@
  */
 static int kiss_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *ptype)
 {
-	skb->sk = NULL;		/* Initially we don't know who its for */
+	skb->sk = NULL;		/* Initially we don't know who it's for */
 
 	if ((*skb->data & 0x0F) != 0) {
 		kfree_skb(skb, FREE_READ);	/* Not a KISS data frame */
@@ -1914,7 +1914,7 @@
 	ax25_address *port_call;
 	int len;
 
-	skb->sk = NULL;		/* Initially we don't know who its for */
+	skb->sk = NULL;		/* Initially we don't know who it's for */
 
 	if ((port_call = ax25_bpq_get_addr(dev)) == NULL) {
 		kfree_skb(skb, FREE_READ);	/* We have no port callsign */
@@ -2546,7 +2546,7 @@
 		if (mode == 'V' || mode == 'v' || (mode == ' ' && ax25_dev_get_value(dev, AX25_VALUES_IPDEFMODE) == 'V')) 
 		{
 			/*
-			 *	This is a workaround to try and keep the device locking
+			 *	This is a workaround to try to keep the device locking
 			 *	straight until skb->free=0 is abolished post 1.4.
 			 *
 			 *	We clone the buffer and release the original thereby
diff -u --recursive --new-file v1.3.98/linux/net/ax25/ax25_out.c linux/net/ax25/ax25_out.c
--- v1.3.98/linux/net/ax25/ax25_out.c	Sun May  5 08:52:05 1996
+++ linux/net/ax25/ax25_out.c	Mon May  6 12:26:17 1996
@@ -389,7 +389,7 @@
 
 	/* The FLEXNET DAMA master implementation refuses to send us ANY */
 	/* I frame for this connection if we send a REJ here, probably   */
-	/* due to it's frame collector scheme? A simple RR or  RNR will  */
+	/* due to its frame collector scheme? A simple RR or  RNR will   */
 	/* invoke the retransmission, and in fact REJs are superfluous   */
 	/* in DAMA mode anyway...					 */
 	
diff -u --recursive --new-file v1.3.98/linux/net/ax25/ax25_subr.c linux/net/ax25/ax25_subr.c
--- v1.3.98/linux/net/ax25/ax25_subr.c	Fri Apr 19 10:08:02 1996
+++ linux/net/ax25/ax25_subr.c	Mon May  6 12:26:17 1996
@@ -248,7 +248,7 @@
 /*
  *	Send a 'DM' to an unknown connection attempt, or an invalid caller.
  *
- *	Note: src here is the sender, thus its the target of the DM
+ *	Note: src here is the sender, thus it's the target of the DM
  */
 void ax25_return_dm(struct device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi)
 {
diff -u --recursive --new-file v1.3.98/linux/net/core/dev.c linux/net/core/dev.c
--- v1.3.98/linux/net/core/dev.c	Sun May  5 08:52:06 1996
+++ linux/net/core/dev.c	Tue May  7 12:06:51 1996
@@ -291,13 +291,7 @@
 	 *	Flush the multicast chain
 	 */
 	dev_mc_discard(dev);
-	/*
-	 *	Blank the IP addresses
-	 */
-	dev->pa_addr = 0;
-	dev->pa_dstaddr = 0;
-	dev->pa_brdaddr = 0;
-	dev->pa_mask = 0;
+
 	/*
 	 *	Purge any queued packets when we down the link 
 	 */
@@ -1082,6 +1076,17 @@
 			}
 			else
 			{
+				u32 new_pa_addr = (*(struct sockaddr_in *)
+					 &ifr.ifr_addr).sin_addr.s_addr;
+				u16 new_family = ifr.ifr_addr.sa_family;
+
+				if (new_family == dev->family &&
+				    new_pa_addr == dev->pa_addr) {
+					ret =0;
+					break;
+				}
+				if (dev->flags & IFF_UP)
+					notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
 
 				/*
 				 *	if dev is an alias, must rehash to update
@@ -1092,16 +1097,19 @@
 			  	if (net_alias_is(dev))
 			    	net_alias_dev_rehash(dev ,&ifr.ifr_addr);
 #endif
-				dev->pa_addr = (*(struct sockaddr_in *)
-					 &ifr.ifr_addr).sin_addr.s_addr;
-				dev->family = ifr.ifr_addr.sa_family;
+				dev->pa_addr = new_pa_addr;
+				dev->family = new_family;
 			
 #ifdef CONFIG_INET	
 				/* This is naughty. When net-032e comes out It wants moving into the net032
 				   code not the kernel. Till then it can sit here (SIGH) */		
-				dev->pa_mask = ip_get_mask(dev->pa_addr);
+				if (!dev->pa_mask)
+					dev->pa_mask = ip_get_mask(dev->pa_addr);
 #endif			
-				dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
+				if (!dev->pa_brdaddr)
+					dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
+				if (dev->flags & IFF_UP)
+					notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
 				ret = 0;
 			}
 			break;
diff -u --recursive --new-file v1.3.98/linux/net/core/net_alias.c linux/net/core/net_alias.c
--- v1.3.98/linux/net/core/net_alias.c	Fri Apr 12 15:52:11 1996
+++ linux/net/core/net_alias.c	Mon May  6 12:26:17 1996
@@ -1122,7 +1122,7 @@
   if (main_dev == NULL) return NULL;
 
   /*
-   * if not aliased, dont bother any more
+   * if not aliased, don't bother any more
    */
 
   if ((alias_info = main_dev->alias_info) == NULL)
@@ -1194,7 +1194,7 @@
   if (main_dev == NULL) return NULL;
 
   /*
-   * if not aliased, dont bother any more
+   * if not aliased, don't bother any more
    */
 
   if ((alias_info = main_dev->alias_info) == NULL)
diff -u --recursive --new-file v1.3.98/linux/net/ethernet/eth.c linux/net/ethernet/eth.c
--- v1.3.98/linux/net/ethernet/eth.c	Fri Apr 12 15:52:11 1996
+++ linux/net/ethernet/eth.c	Mon May  6 12:26:17 1996
@@ -154,7 +154,7 @@
 	}
 
 	/*
-	 *	Try and get ARP to resolve the header.
+	 *	Try to get ARP to resolve the header.
 	 */
 #ifdef CONFIG_INET	 
 	return arp_find(eth->h_dest, dst, dev, dev->pa_addr, skb)? 1 : 0;
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/Config.in linux/net/ipv4/Config.in
--- v1.3.98/linux/net/ipv4/Config.in	Sun May  5 08:52:06 1996
+++ linux/net/ipv4/Config.in	Tue May  7 12:06:52 1996
@@ -22,7 +22,7 @@
 	tristate 'IP: aliasing support' CONFIG_IP_ALIAS
 fi
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-  if [ "$CONFIG_KERNELD" = "y" ]; then
+  if [ "$CONFIG_NETLINK" = "y" ]; then
     bool 'IP: ARP daemon support (EXPERIMENTAL)' CONFIG_ARPD
   fi
 fi
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v1.3.98/linux/net/ipv4/arp.c	Fri Apr 12 15:52:11 1996
+++ linux/net/ipv4/arp.c	Tue May  7 12:06:52 1996
@@ -103,8 +103,8 @@
 #include <linux/net_alias.h>
 #endif
 #ifdef CONFIG_ARPD
-#include <linux/kerneld.h>
-#endif /* CONFIG_ARPD */
+#include <net/netlink.h>
+#endif
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -112,97 +112,157 @@
 #include <stdarg.h>
 
 /*
- *	This structure defines the ARP mapping cache. As long as we make changes
- *	in this structure, we keep interrupts off. But normally we can copy the
- *	hardware address and the device pointer in a local variable and then 
- *	make any "long calls" to send a packet out.
+ *	Configurable Parameters
  */
 
-struct arp_table
-{
-	struct arp_table		*next;			/* Linked entry list 		*/
-	unsigned long			last_used;		/* For expiry 			*/
-	unsigned long			last_updated;		/* For expiry 			*/
-	unsigned int			flags;			/* Control status 		*/
-	u32				ip;			/* ip address of entry 		*/
-	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
-	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
-	struct device			*dev;			/* Device the entry is tied to 	*/
+/*
+ *	After that time, an unused entry is deleted from the arp table.
+ *	RFC1122 recommends set it to 60*HZ, if your site uses proxy arp
+ *	and dynamic routing.
+ */
 
-	/*
-	 *	The following entries are only used for unresolved hw addresses.
-	 */
-	
-	struct timer_list		timer;			/* expire timer 		*/
-	int				retries;		/* remaining retries	 	*/
-	struct sk_buff_head		skb;			/* list of queued packets 	*/
-	struct hh_cache			*hh;
-};
+#ifndef CONFIG_ARPD
+#define ARP_TIMEOUT		(600*HZ)
+#else
+#define ARP_TIMEOUT		(60*HZ)
+#define ARPD_TIMEOUT		(600*HZ)
+#endif
+
+/*
+ *	How often is ARP cache checked for expire.
+ *	It is useless to set ARP_CHECK_INTERVAL > ARP_TIMEOUT
+ */
 
+#define ARP_CHECK_INTERVAL	(60*HZ)
 
 /*
- *	Configurable Parameters (don't touch unless you know what you are doing
+ *	Soft limit on ARP cache size.
+ *	Note that this number should be greater, than
+ *	number of simultaneously opened sockets, else
+ *	hardware header cache will be not efficient.
  */
 
+#if RT_CACHE_DEBUG >= 2
+#define ARP_MAXSIZE	4
+#else
+#ifdef CONFIG_ARPD
+#define ARP_MAXSIZE	64
+#else
+#define ARP_MAXSIZE	256
+#endif /* CONFIG_ARPD */
+#endif
+
 /*
  *	If an arp request is send, ARP_RES_TIME is the timeout value until the
  *	next request is send.
  * 	RFC1122: OK.  Throttles ARPing, as per 2.3.2.1. (MUST)
  *	The recommended minimum timeout is 1 second per destination.
- *	This timeout is prolongated to ARP_DEAD_RES_TIME, if
- *	destination does not respond.
+ *
  */
 
 #define ARP_RES_TIME		(5*HZ)
-#define ARP_DEAD_RES_TIME	(60*HZ)
 
 /*
- *	The number of times an arp request is send, until the host is
- *	considered temporarily unreachable.
+ *	The number of times an broadcast arp request is send, until
+ *	the host is considered temporarily unreachable.
  */
 
 #define ARP_MAX_TRIES		3
 
 /*
- *	After that time, an unused entry is deleted from the arp table.
+ *	The entry is reconfirmed by sending point-to-point ARP
+ *	request after ARP_CONFIRM_INTERVAL.
+ *	RFC1122 recommends 60*HZ.
+ *
+ *	Warning: there exist nodes, that answer only broadcast
+ *	ARP requests (Cisco-4000 in hot standby mode?)
+ *	Now arp code should work with such nodes, but
+ *	it still will generate redundant broadcast requests, so that
+ *	this interval should be enough long.
  */
 
-#define ARP_TIMEOUT		(600*HZ)
+#define ARP_CONFIRM_INTERVAL	(300*HZ)
 
 /*
- *	How often is the function 'arp_check_retries' called.
- *	An unused entry is invalidated in the time between ARP_TIMEOUT and
- *	(ARP_TIMEOUT+ARP_CHECK_INTERVAL).
+ *	We wait for answer to unicast request for ARP_CONFIRM_TIMEOUT.
  */
 
-#define ARP_CHECK_INTERVAL	(60*HZ)
+#define ARP_CONFIRM_TIMEOUT	ARP_RES_TIME
 
 /*
- *	The entry is reconfirmed by sending point-to-point ARP
- *	request after ARP_CONFIRM_INTERVAL. If destinations does not respond
- *	for ARP_CONFIRM_TIMEOUT, normal broadcast resolution scheme is started.
+ *	The number of times an unicast arp request is retried, until
+ *	the cache entry is considered suspicious.
+ *	Value 0 means that no unicast pings will be sent.
+ *	RFC1122 recommends 2.
+ */
+
+#define ARP_MAX_PINGS		1
+
+/*
+ *	When a host is dead, but someone tries to connect it,
+ *	we do not remove corresponding cache entry (it would
+ *	be useless, it will be created again immediately)
+ *	Instead we prolongate interval between broadcasts
+ *	to ARP_DEAD_RES_TIME.
+ *	This interval should be not very long.
+ *	(When the host will be up again, we will notice it only
+ *	when ARP_DEAD_RES_TIME expires, or when the host will arp us.
  */
 
-#define ARP_CONFIRM_INTERVAL	(300*HZ)
-#define ARP_CONFIRM_TIMEOUT	ARP_RES_TIME
+#define ARP_DEAD_RES_TIME	(60*HZ)
+
+/*
+ *	This structure defines the ARP mapping cache.
+ */
+
+struct arp_table
+{
+	struct arp_table		*next;			/* Linked entry list 		*/
+	unsigned long			last_used;		/* For expiry 			*/
+	unsigned long			last_updated;		/* For expiry 			*/
+	unsigned int			flags;			/* Control status 		*/
+	u32				ip;			/* ip address of entry 		*/
+	u32				mask;			/* netmask - used for generalised proxy arps (tridge) 		*/
+	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
+	struct device			*dev;			/* Device the entry is tied to 	*/
+	struct hh_cache			*hh;			/* Hardware headers chain	*/
+
+	/*
+	 *	The following entries are only used for unresolved hw addresses.
+	 */
+	
+	struct timer_list		timer;			/* expire timer 		*/
+	int				retries;		/* remaining retries	 	*/
+	struct sk_buff_head		skb;			/* list of queued packets 	*/
+};
+
+
+static atomic_t arp_size = 0;
+
+#ifdef CONFIG_ARPD
+static int arpd_not_running;
+static int arpd_stamp;
+#endif
 
-static unsigned int arp_lock;
 static unsigned int arp_bh_mask;
 
 #define ARP_BH_BACKLOG	1
 
+/*
+ *	Backlog for ARP updates.
+ */
 static struct arp_table *arp_backlog;
 
-/* If we have arpd configured, assume that we will be running arpd and keep
-   the internal cache small */
-#ifdef CONFIG_ARPD
-#define ARP_MAXSIZE	256
-#endif /* CONFIG_ARPD */
+/*
+ *	Backlog for incomplete entries.
+ */
+static struct arp_table *arp_req_backlog;
 
-static unsigned int arp_size = 0;
 
 static void arp_run_bh(void);
 static void arp_check_expire (unsigned long);  
+static int  arp_update (u32 sip, char *sha, struct device * dev,
+	    struct arp_table *ientry, int grat);
 
 static struct timer_list arp_timer =
 	{ NULL, NULL, ARP_CHECK_INTERVAL, 0L, &arp_check_expire };
@@ -216,12 +276,10 @@
 
 /*
  * 	The size of the hash table. Must be a power of two.
- * 	Maybe we should remove hashing in the future for arp and concentrate
- * 	on Patrick Schaaf's Host-Cache-Lookup...
  */
 
-#define ARP_TABLE_SIZE  16
-#define FULL_ARP_TABLE_SIZE (ARP_TABLE_SIZE+1)
+#define ARP_TABLE_SIZE		16
+#define FULL_ARP_TABLE_SIZE	(ARP_TABLE_SIZE+1)
 
 struct arp_table *arp_tables[FULL_ARP_TABLE_SIZE] =
 {
@@ -238,22 +296,47 @@
 #define HASH(paddr) 		(htonl(paddr) & (ARP_TABLE_SIZE - 1))
 
 /*
- * Lock/unlock arp_table chains.
+ *	ARP cache semaphore.
+ *
+ *	Every time when someone wants to traverse arp table,
+ *	he MUST call arp_fast_lock.
+ *	It will guarantee that arp cache list will not change
+ *	by interrupts and the entry that you found will not
+ *	disappear unexpectedly.
+ *	
+ *	If you want to modify arp cache lists, you MUST
+ *	call arp_fast_lock, and check that you are the only
+ *	owner of semaphore (arp_lock == 1). If it is not the case
+ *	you can defer your operation or forgot it,
+ *	but DO NOT TOUCH lists.
+ *
+ *	However, you are allowed to change arp entry contents.
+ *
+ *	Assumptions:
+ *	     -- interrupt code MUST have lock/unlock balanced,
+ *		you cannot lock cache on interrupt and defer unlocking
+ *		to callback.
+ *		In particular, it means that lock/unlock are allowed
+ *		to be non-atomic. They are made atomic, but it was not
+ *		necessary.
+ *	     -- nobody is allowed to sleep while
+ *		it keeps arp locked. (route cache has similar locking
+ *		scheme, but allows sleeping)
+ *		
  */
 
-static __inline__ void arp_fast_lock(void)
-{
-	ATOMIC_INCR(&arp_lock);
-}
+static atomic_t arp_lock;
+
+#define ARP_LOCKED() (arp_lock != 1)
 
-static __inline__ void arp_fast_unlock(void)
+static __inline__ void arp_fast_lock(void)
 {
-	ATOMIC_DECR(&arp_lock);
+	atomic_inc(&arp_lock);
 }
 
 static __inline__ void arp_unlock(void)
 {
-	if (!ATOMIC_DECR_AND_CHECK(&arp_lock) && arp_bh_mask)
+	if (atomic_dec_and_test(&arp_lock) && arp_bh_mask)
 		arp_run_bh();
 }
 
@@ -306,7 +389,7 @@
  * Purge all linked skb's of the entry.
  */
 
-static void arp_release_entry(struct arp_table *entry)
+static void arp_purge_send_q(struct arp_table *entry)
 {
 	struct sk_buff *skb;
 	unsigned long flags;
@@ -328,6 +411,7 @@
 /*
  * 	Release the entry and all resources linked to it: skb's, hh's, timer
  * 	and certainly memory.
+ *	The entry should be already removed from lists.
  */
 
 static void arp_free_entry(struct arp_table *entry)
@@ -336,280 +420,416 @@
 	struct hh_cache *hh, *next;
 
 	del_timer(&entry->timer);
+	arp_purge_send_q(entry);
 
 	save_flags(flags);
 	cli();
-	arp_release_entry(entry);
+	hh = entry->hh;
+	entry->hh = NULL;
+	restore_flags(flags);
 
-	for (hh = entry->hh; hh; hh = next)
+	for ( ; hh; hh = next)
 	{
 		next = hh->hh_next;
-		hh->hh_arp = NULL;
 		hh->hh_uptodate = 0;
-		if (!--hh->hh_refcnt)
+		hh->hh_next = NULL;
+		hh->hh_arp = NULL;
+		if (atomic_dec_and_test(&hh->hh_refcnt))
 			kfree_s(hh, sizeof(struct(struct hh_cache)));
 	}
-	restore_flags(flags);
 
 	kfree_s(entry, sizeof(struct arp_table));
-	--arp_size;
+	atomic_dec(&arp_size);
 	return;
 }
 
 /*
- * How many users has this entry?
+ *	Hardware header cache.
+ *
+ *	BEWARE! Hardware header cache has no locking, so that
+ *	it requires especially careful handling.
+ *	It is the only part of arp+route, where a list
+ *	should be traversed with masked interrupts.
+ *	Luckily, this list contains one element 8), as rule.
+ */
+
+/*
+ *	How many users has this entry?
+ *	The answer is reliable only when interrupts are masked.
  */
 
 static __inline__ int arp_count_hhs(struct arp_table * entry)
 {
-	struct hh_cache *hh, **hhp;
+	struct hh_cache *hh;
 	int count = 0;
 
-	hhp = &entry->hh;
-	while ((hh=*hhp) != NULL)
-	{
-		if (hh->hh_refcnt == 1)
-		{
-			*hhp = hh->hh_next;
-			kfree_s(hh, sizeof(struct hh_cache));
-			continue;
-		}
+	for (hh = entry->hh; hh; hh = hh->hh_next)
 		count += hh->hh_refcnt-1;
-		hhp = &hh->hh_next;
-	}
 
 	return count;
 }
 
-
 /*
- *	Force the expiry of an entry in the internal cache so the memory
- *	can be used for a new request or for loading a query from arpd.
- *	I'm not really sure what the best algorithm should be, so I just
- *	search for the oldest.  NOTE:  make sure the cache is locked before
- *      jumping into this function!  If someone wants to do something
- *	other than searching the whole cache, by all means do so!
+ * Signal to device layer, that hardware address may be changed.
  */
 
-#ifdef CONFIG_ARPD
-static int arp_force_expire(void)
+static __inline__ void arp_update_hhs(struct arp_table * entry)
 {
-	int i;
-	struct arp_table *entry = NULL;
-	struct arp_table **pentry = NULL;
-	struct arp_table **oldest_entry = NULL, **last_resort = NULL;
-	unsigned long oldest_used = ~0;
-
-#if RT_CACHE_DEBUG >= 2
-	printk("Looking for something to force expire.\n");
-#endif
-	for (i = 0; i < ARP_TABLE_SIZE; i++)
-	{
-		pentry = &arp_tables[i];
+	struct hh_cache *hh;
 
-		while ((entry = *pentry) != NULL)
-		{
-			if (entry->last_used < oldest_used)
-			{
-				if (arp_count_hhs(entry) == 0)
-				{
-					oldest_entry = pentry;
-				}
-				last_resort = pentry;
-				oldest_used = entry->last_used;
-			}
-			pentry = &entry->next;	/* go to next entry */
-		}
-	}
-	if (oldest_entry == NULL)
-	{
-		if (last_resort == NULL)
-			return -1;
-		oldest_entry = last_resort;
-	}
-		
-	entry = *oldest_entry;
-	*oldest_entry = (*oldest_entry)->next;
-#if RT_CACHE_DEBUG >= 2
-	printk("Force expiring %08x\n", entry->ip);
-#endif
-	arp_free_entry(entry);
-	return 0;
+	for (hh=entry->hh; hh; hh=hh->hh_next)
+		entry->dev->header_cache_update(hh, entry->dev, entry->ha);
 }
-#endif /* CONFIG_ARPD */
 
+/*
+ *	Invalidate all hh's, so that higher level will not try to use it.
+ */
 
-static void arpd_update(struct arp_table * entry, int loc)
+static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
 {
-#ifdef CONFIG_ARPD
-	static struct arpd_request arpreq;
-
-	arpreq.req = ARPD_UPDATE;
-	arpreq.ip = entry->ip;
-	arpreq.mask = entry->mask;
-	memcpy (arpreq.ha, entry->ha, MAX_ADDR_LEN);
-	arpreq.loc = loc;
-	arpreq.last_used = entry->last_used;
-	arpreq.last_updated = entry->last_updated;
-	arpreq.flags = entry->flags;
-	arpreq.dev = entry->dev;
+	struct hh_cache *hh;
 
-	kerneld_send(KERNELD_ARP, 0, sizeof(arpreq),
-				(char *) &arpreq, NULL);
-#endif /* CONFIG_ARPD */
+	for (hh=entry->hh; hh; hh=hh->hh_next)
+		hh->hh_uptodate = 0;
 }
 
-/* 
- * Allocate memory for a new entry.  If we are at the maximum limit
- * of the internal ARP cache, arp_force_expire() an entry.  NOTE:  
- * arp_force_expire() needs the cache to be locked, so therefore
- * arp_add_entry() should only be called with the cache locked too!
+/*
+ *	Atomic attaching new hh entry.
+ *	Return 1, if entry has been freed, rather than attached.
  */
 
-static struct arp_table * arp_add_entry(void)
+static int arp_set_hh(struct hh_cache **hhp, struct hh_cache *hh)
 {
-	struct arp_table * entry;
+	unsigned long flags;
+	struct hh_cache *hh1;
+	struct arp_table *entry;
 
-#ifdef CONFIG_ARPD
-	if (arp_size >= ARP_MAXSIZE)
+	atomic_inc(&hh->hh_refcnt);
+
+	save_flags(flags);
+	cli();
+	if ((hh1 = *hhp) == NULL)
 	{
-		if (arp_force_expire() < 0)
-			return NULL;
+		*hhp = hh;
+		restore_flags(flags);
+		return 0;
 	}
-#endif /* CONFIG_ARPD */
 
-	entry = (struct arp_table *)
-		kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+	entry = (struct arp_table*)hh->hh_arp;
 
-	if (entry != NULL)
-		++arp_size;
-	return entry;
+	/*
+	 *	An hh1 entry is already attached to this point.
+	 *	Is it not linked to arp entry? Link it!
+	 */
+	if (!hh1->hh_arp && entry)
+	{
+		atomic_inc(&hh1->hh_refcnt);
+		hh1->hh_next = entry->hh;
+		entry->hh = hh1;
+		hh1->hh_arp = (void*)entry;
+		restore_flags(flags);
+
+		if (entry->flags & ATF_COM)
+			entry->dev->header_cache_update(hh1, entry->dev, entry->ha);
+#if RT_CACHE_DEBUG >= 1
+		printk("arp_set_hh: %08x is reattached. Good!\n", entry->ip);
+#endif
+	}
+#if RT_CACHE_DEBUG >= 1
+	else if (entry)
+		printk("arp_set_hh: %08x rr1 ok!\n", entry->ip);
+#endif
+	restore_flags(flags);
+	if (atomic_dec_and_test(&hh->hh_refcnt))
+		kfree_s(hh, sizeof(struct hh_cache));
+	return 1;
 }
 
+static __inline__ struct hh_cache * arp_alloc_hh(int htype)
+{
+	struct hh_cache *hh;
+	hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+	if (hh)
+	{
+		memset(hh, 0, sizeof(struct hh_cache));
+		hh->hh_type = htype;
+	}
+	return hh;
+}
 
 /*
- * Lookup ARP entry by (addr, dev) pair in the arpd.
+ * Test if a hardware address is all zero
  */
 
-static struct arp_table * arpd_lookup(u32 addr, unsigned short flags, 
-						struct device * dev,
-						int loc)
+static __inline__ int empty(unsigned char * addr, int len)
 {
-#ifdef CONFIG_ARPD
-	static struct arpd_request arpreq, retreq;
-	struct arp_table * entry;
-	int rv, i;
+	while (len > 0)
+	{
+		if (*addr)
+			return 0;
+		len--;
+		addr++;
+	}
+	return 1;
+}
 
-	arpreq.req = ARPD_LOOKUP;
-	arpreq.ip = addr;
-	arpreq.loc = loc;
-
-	rv = kerneld_send(KERNELD_ARP, 
-			sizeof(retreq) | KERNELD_WAIT,
-			sizeof(arpreq),
-			(char *) &arpreq,
-			(char *) &retreq);
-	/* don't worry about rv != 0 too much, it's probably
-	   because arpd isn't running or an entry couldn't
-	   be found */
-
-	if (rv != 0)
-		return NULL;
-	if (dev != retreq.dev)
-		return NULL;
-	if (! memcmp (retreq.ha, "\0\0\0\0\0\0", 6))
-		return NULL;
 
-	arp_fast_lock();
-	entry = arp_add_entry();
-	arp_unlock();
+#ifdef CONFIG_ARPD
 
-	if (entry == NULL)
-		return NULL;
+/*
+ *	Send ARPD message.
+ */
+static void arpd_send(int req, u32 addr, struct device * dev, char *ha,
+		      unsigned long updated)
+{
+	int retval;
+	struct sk_buff *skb;
+	struct arpd_request *arpreq;
 
-	entry->next = NULL;
-	entry->last_used = retreq.last_used;
-	entry->last_updated = retreq.last_updated;
-	entry->flags = retreq.flags;
-	entry->ip = retreq.ip;
-	entry->mask = retreq.mask;
-	memcpy (entry->ha, retreq.ha, MAX_ADDR_LEN);
-	arpreq.dev = entry->dev;
+	if (arpd_not_running)
+		return;
 
-	skb_queue_head_init(&entry->skb);
-	entry->hh = NULL;
-	entry->retries = 0;
+	skb = alloc_skb(sizeof(struct arpd_request), GFP_ATOMIC);
+	if (skb == NULL)
+		return;
 
-#if RT_CACHE_DEBUG >= 2
-	printk("Inserting arpd entry %08x\n in local cache.", entry->ip);
-#endif
-	i = HASH(entry->ip);
-	arp_fast_lock();
-	entry->next = arp_tables[i]->next;
-	arp_tables[i]->next = entry;
-	arp_unlock();
-	return entry;
-#endif /* CONFIG_ARPD */
-	return NULL;
-}
+	skb->free=1;
+	arpreq=(struct arpd_request *)skb_put(skb, sizeof(struct arpd_request));
+	arpreq->req = req;
+	arpreq->ip  = addr;
+	arpreq->dev = (unsigned long)dev;
+	arpreq->stamp = arpd_stamp;
+	arpreq->updated = updated;
+	if (ha)
+		memcpy(arpreq->ha, ha, sizeof(arpreq->ha));
 
+	retval = netlink_post(NETLINK_ARPD, skb);
+	if (retval)
+	{
+		kfree_skb(skb, FREE_WRITE);
+		if (retval == -EUNATCH)
+			arpd_not_running = 1;
+	}
+}
 
 /*
- * Invalidate all hh's, so that higher level will not try to use it.
+ *	Send ARPD update message.
  */
 
-static __inline__ void arp_invalidate_hhs(struct arp_table * entry)
+static __inline__ void arpd_update(struct arp_table * entry)
 {
-	struct hh_cache *hh;
-
-	for (hh=entry->hh; hh; hh=hh->hh_next)
-		hh->hh_uptodate = 0;
+	if (arpd_not_running)
+		return;
+	arpd_send(ARPD_UPDATE, entry->ip, entry->dev, entry->ha,
+		  entry->last_updated);
 }
 
 /*
- * Signal to device layer, that hardware address may be changed.
+ *	Send ARPD lookup request.
  */
 
-static __inline__ void arp_update_hhs(struct arp_table * entry)
+static __inline__ void arpd_lookup(u32 addr, struct device * dev)
 {
-	struct hh_cache *hh;
-
-	for (hh=entry->hh; hh; hh=hh->hh_next)
-		entry->dev->header_cache_update(hh, entry->dev, entry->ha);
+	if (arpd_not_running)
+		return;
+	arpd_send(ARPD_LOOKUP, addr, dev, NULL, 0);
 }
 
 /*
- *	Check if there are too old entries and remove them. If the ATF_PERM
- *	flag is set, they are always left in the arp cache (permanent entry).
- *      If an entry was not be confirmed  for ARP_CONFIRM_INTERVAL,
- *	declare it invalid and send point-to-point ARP request.
- *	If it will not be confirmed for ARP_CONFIRM_TIMEOUT,
- *	give it to shred by arp_expire_entry.
+ *	Send ARPD flush message.
  */
 
-static void arp_check_expire(unsigned long dummy)
+static __inline__ void arpd_flush(struct device * dev)
 {
-	int i;
-	unsigned long now = jiffies;
+	if (arpd_not_running)
+		return;
+	arpd_send(ARPD_FLUSH, 0, dev, NULL, 0);
+}
 
-	del_timer(&arp_timer);
 
-	if (!arp_lock)
-	{
-		arp_fast_lock();
+static int arpd_callback(struct sk_buff *skb)
+{
+	struct device * dev;
+	struct arpd_request *retreq;
 
-		for (i = 0; i < ARP_TABLE_SIZE; i++)
-		{
-			struct arp_table *entry;
-			struct arp_table **pentry;
-		
-			pentry = &arp_tables[i];
+	arpd_not_running = 0;
+
+	if (skb->len != sizeof(struct arpd_request))
+	{
+		kfree_skb(skb, FREE_READ);
+		return -EINVAL;
+	}
+
+	retreq = (struct arpd_request *)skb->data;
+	dev = (struct device*)retreq->dev;
+
+	if (retreq->stamp != arpd_stamp || !dev)
+	{
+		kfree_skb(skb, FREE_READ);
+		return -EINVAL;
+	}
+
+	if (!retreq->updated || empty(retreq->ha, sizeof(retreq->ha)))
+	{
+/*
+ *	Invalid mapping: drop it and send ARP broadcast.
+ */
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, retreq->ip, dev, dev->pa_addr, NULL, 
+			 dev->dev_addr, NULL);
+	}
+	else
+	{
+		arp_fast_lock();
+		arp_update(retreq->ip, retreq->ha, dev, NULL, 0);
+		arp_unlock();
+
+/*
+ *	Old mapping: we cannot trust it, send ARP broadcast to confirm it.
+ *	If it will answer, the entry will be updated,
+ *	if not ... we are lost. We will use it for ARP_CONFIRM_INTERVAL.
+ */
+		if (jiffies - retreq->updated < ARPD_TIMEOUT)
+			arp_send(ARPOP_REQUEST, ETH_P_ARP, retreq->ip, dev, dev->pa_addr, NULL, 
+				 dev->dev_addr, NULL);
+	}
+
+	kfree_skb(skb, FREE_READ);
+	return sizeof(struct arpd_request);
+}
+
+#else
+
+static __inline__ void arpd_update(struct arp_table * entry)
+{
+	return;
+}
+
+#endif /* CONFIG_ARPD */
+
+
+
+
+/*
+ *	ARP expiration routines.
+ */
+
+/*
+ *	Force the expiry of an entry in the internal cache so the memory
+ *	can be used for a new request.
+ */
+
+static int arp_force_expire(void)
+{
+	int i;
+	struct arp_table *entry, **pentry;
+	struct arp_table **oldest_entry = NULL;
+	unsigned long oldest_used = ~0;
+	unsigned long flags;
+	unsigned long now = jiffies;
+	int result = 0;
+
+	static last_index;
+
+	if (ARP_LOCKED())
+		return 0;
+
+	save_flags(flags);
+
+	if (last_index >= ARP_TABLE_SIZE)
+		last_index = 0;
+
+	for (i = 0; i < ARP_TABLE_SIZE; i++, last_index++)
+	{
+		pentry = &arp_tables[last_index & (ARP_TABLE_SIZE-1)];
+
+		while ((entry = *pentry) != NULL)
+		{
+			if (!(entry->flags & ATF_PERM))
+			{
+				int users;
+				cli();
+				users = arp_count_hhs(entry);
+
+				if (!users && now - entry->last_used > ARP_TIMEOUT)
+				{
+					*pentry = entry->next;
+					restore_flags(flags);
+#if RT_CACHE_DEBUG >= 2
+					printk("arp_force_expire: %08x expired\n", entry->ip);
+#endif
+					arp_free_entry(entry);
+					result++;
+					if (arp_size < ARP_MAXSIZE)
+						goto done;
+					continue;
+				}
+				restore_flags(flags);
+				if (!users && entry->last_used < oldest_used)
+				{
+					oldest_entry = pentry;
+					oldest_used = entry->last_used;
+				}
+			}
+			pentry = &entry->next;
+		}
+	}
+
+done:
+	if (result || !oldest_entry)
+		return result;
+
+	entry = *oldest_entry;
+	*oldest_entry = entry->next;
+#if RT_CACHE_DEBUG >= 2
+	printk("arp_force_expire: expiring %08x\n", entry->ip);
+#endif
+	arp_free_entry(entry);
+	return 1;
+}
+
+/*
+ *	Check if there are too old entries and remove them. If the ATF_PERM
+ *	flag is set, they are always left in the arp cache (permanent entry).
+ *      If an entry was not be confirmed  for ARP_CONFIRM_INTERVAL,
+ *	send point-to-point ARP request.
+ *	If it will not be confirmed for ARP_CONFIRM_TIMEOUT,
+ *	give it to shred by arp_expire_entry.
+ */
+
+static void arp_check_expire(unsigned long dummy)
+{
+	int i;
+	unsigned long now = jiffies;
+
+	del_timer(&arp_timer);
+
+#ifdef CONFIG_ARPD
+	arpd_not_running = 0;
+#endif
+
+	ip_rt_check_expire();
+
+	arp_fast_lock();
+
+	if (!ARP_LOCKED())
+	{
+
+		for (i = 0; i < ARP_TABLE_SIZE; i++)
+		{
+			struct arp_table *entry, **pentry;
+		
+			pentry = &arp_tables[i];
+
+			while ((entry = *pentry) != NULL)
+			{
+				if (entry->flags & ATF_PERM)
+				{
+					pentry = &entry->next;
+					continue;
+				}
 
-			while ((entry = *pentry) != NULL)
-			{
 				cli();
 				if (now - entry->last_used > ARP_TIMEOUT
-				    && !(entry->flags & ATF_PERM)
 				    && !arp_count_hhs(entry))
 				{
 					*pentry = entry->next;
@@ -618,17 +838,15 @@
 					printk("arp_expire: %08x expired\n", entry->ip);
 #endif
 					arp_free_entry(entry);
+					continue;
 				}
-				else if (entry->last_updated
-					 && now - entry->last_updated > ARP_CONFIRM_INTERVAL
-					 && !(entry->flags & ATF_PERM))
+				sti();
+				if (entry->last_updated
+				    && now - entry->last_updated > ARP_CONFIRM_INTERVAL
+				    && !(entry->flags & ATF_PERM))
 				{
 					struct device * dev = entry->dev;
-					pentry = &entry->next;
-					entry->flags &= ~ATF_COM;
-					arp_invalidate_hhs(entry);
-					sti();
-					entry->retries = ARP_MAX_TRIES+1;
+					entry->retries = ARP_MAX_TRIES+ARP_MAX_PINGS;
 					del_timer(&entry->timer);
 					entry->timer.expires = jiffies + ARP_CONFIRM_TIMEOUT;
 					add_timer(&entry->timer);
@@ -639,14 +857,12 @@
 					printk("arp_expire: %08x requires confirmation\n", entry->ip);
 #endif
 				}
-				else
-					pentry = &entry->next;	/* go to next entry */
+				pentry = &entry->next;	/* go to next entry */
 			}
 		}
-		arp_unlock();
 	}
 
-	ip_rt_check_expire();
+	arp_unlock();
 
 	/*
 	 *	Set the timer again.
@@ -669,34 +885,46 @@
 	unsigned long hash;
 	unsigned long flags;
 
+	arp_fast_lock();
+
 	save_flags(flags);
 	cli();
+	del_timer(&entry->timer);
 
 	/*
-	 *	Since all timeouts are handled with interrupts enabled, there is a
-	 *	small chance, that this entry has just been resolved by an incoming
-	 *	packet. This is the only race condition, but it is handled...
+	 *	If arp table is locked, defer expire processing.
 	 */
-	
-	if (entry->flags & ATF_COM)
+	if (ARP_LOCKED())
 	{
+#if RT_CACHE_DEBUG >= 1
+		printk(KERN_DEBUG "arp_expire_request: %08x deferred\n", entry->ip);
+#endif
+		entry->timer.expires = jiffies + HZ/10;
+		add_timer(&entry->timer);
 		restore_flags(flags);
+		arp_unlock();
 		return;
 	}
 
-	if (arp_lock)
+	/*
+	 *	Since all timeouts are handled with interrupts enabled, there is a
+	 *	small chance, that this entry has just been resolved by an incoming
+	 *	packet. This is the only race condition, but it is handled...
+	 *
+	 *	One exception: if entry is COMPLETE but old,
+	 *	it means that point-to-point ARP ping has been failed
+	 *	(It really occurs with Cisco 4000 routers)
+	 *	We should reconfirm it.
+	 */
+	
+	if ((entry->flags & ATF_COM) && entry->last_updated
+	    && jiffies - entry->last_updated <= ARP_CONFIRM_INTERVAL)
 	{
-#if RT_CACHE_DEBUG >= 1
-		printk("arp_expire_request: %08x postponed\n", entry->ip);
-#endif
-		del_timer(&entry->timer);
-		entry->timer.expires = jiffies + HZ/10;
-		add_timer(&entry->timer);
 		restore_flags(flags);
+		arp_unlock();
 		return;
 	}
 
-	arp_fast_lock();
 	restore_flags(flags);
 
 	if (entry->last_updated && --entry->retries > 0)
@@ -707,29 +935,47 @@
 		printk("arp_expire_request: %08x timed out\n", entry->ip);
 #endif
 		/* Set new timer. */
-		del_timer(&entry->timer);
 		entry->timer.expires = jiffies + ARP_RES_TIME;
 		add_timer(&entry->timer);
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
-			 NULL, dev->dev_addr, NULL);
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr,
+			 entry->retries > ARP_MAX_TRIES ? entry->ha : NULL,
+			 dev->dev_addr, NULL);
 		arp_unlock();
 		return;
 	}
 
-	arp_release_entry(entry);
+	/*
+	 *	The host is really dead.
+	 */
+
+	arp_purge_send_q(entry);
 
 	cli();
 	if (arp_count_hhs(entry))
 	{
+		/*
+		 *	The host is dead, but someone refers to it.
+		 *	It is useless to drop this entry just now,
+		 *	it will be born again, so that
+		 *	we keep it, but slow down retransmitting
+		 *	to ARP_DEAD_RES_TIME.
+		 */
+
 		struct device *dev = entry->dev;
 #if RT_CACHE_DEBUG >= 2
 		printk("arp_expire_request: %08x is dead\n", entry->ip);
 #endif
-		arp_release_entry(entry);
 		entry->retries = ARP_MAX_TRIES;
+		entry->flags &= ~ATF_COM;
+		arp_invalidate_hhs(entry);
 		restore_flags(flags);
+
+		/*
+		 *	Declare the entry dead.
+		 */
 		entry->last_updated = 0;
-		del_timer(&entry->timer);
+		arpd_update(entry);
+
 		entry->timer.expires = jiffies + ARP_DEAD_RES_TIME;
 		add_timer(&entry->timer);
 		arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, 
@@ -739,30 +985,65 @@
 	}
 	restore_flags(flags);
 
+	entry->last_updated = 0;
+	arpd_update(entry);
+
 	hash = HASH(entry->ip);
 
 	pentry = &arp_tables[hash];
 
 	while (*pentry != NULL)
 	{
-		if (*pentry == entry)
+		if (*pentry != entry)
 		{
-			cli();
-			*pentry = entry->next;
-			restore_flags(flags);
+			pentry = &(*pentry)->next;
+			continue;
+		}
+		*pentry = entry->next;
 #if RT_CACHE_DEBUG >= 2
-			printk("arp_expire_request: %08x is killed\n", entry->ip);
+		printk("arp_expire_request: %08x is killed\n", entry->ip);
 #endif
-			arp_free_entry(entry);
-			arp_unlock();
-			return;
-		}
-		pentry = &(*pentry)->next;
+		arp_free_entry(entry);
 	}
-	printk("arp_expire_request: bug: ARP entry is lost!\n");
 	arp_unlock();
 }
 
+
+/* 
+ * Allocate memory for a new entry.  If we are at the maximum limit
+ * of the internal ARP cache, arp_force_expire() an entry.  NOTE:  
+ * arp_force_expire() needs the cache to be locked, so therefore
+ * arp_alloc_entry() should only be called with the cache locked too!
+ */
+
+static struct arp_table * arp_alloc_entry(void)
+{
+	struct arp_table * entry;
+
+
+	if (arp_size >= ARP_MAXSIZE)
+		arp_force_expire();
+
+	entry = (struct arp_table *)
+		kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+
+	if (entry != NULL)
+	{
+		atomic_inc(&arp_size);
+		memset(entry, 0, sizeof(struct arp_table));
+
+                entry->mask = DEF_ARP_NETMASK;
+		init_timer(&entry->timer);
+		entry->timer.function = arp_expire_request;
+		entry->timer.data = (unsigned long)entry;
+		entry->last_updated = entry->last_used = jiffies;
+		skb_queue_head_init(&entry->skb);
+	}
+	return entry;
+}
+
+
+
 /*
  *	Purge a device from the ARP queue
  */
@@ -774,15 +1055,17 @@
 	
 	if (event != NETDEV_DOWN)
 		return NOTIFY_DONE;
-	/*
-	 *	This is a bit OTT - maybe we need some arp semaphores instead.
-	 */
 
-#if RT_CACHE_DEBUG >= 1	 
-	if (arp_lock)
-		printk("arp_device_event: bug\n");
+#ifdef  CONFIG_ARPD
+	arpd_flush(dev);
+	arpd_stamp++;
 #endif
+
 	arp_fast_lock();
+#if RT_CACHE_DEBUG >= 1	 
+	if (ARP_LOCKED())
+		printk("arp_device_event: impossible\n");
+#endif
 
 	for (i = 0; i < FULL_ARP_TABLE_SIZE; i++)
 	{
@@ -805,104 +1088,29 @@
 }
 
 
+
 /*
- *	Create and send an arp packet. If (dest_hw == NULL), we create a broadcast
- *	message.
+ *	This will try to retransmit everything on the queue.
  */
 
-void arp_send(int type, int ptype, u32 dest_ip, 
-	      struct device *dev, u32 src_ip, 
-	      unsigned char *dest_hw, unsigned char *src_hw,
-	      unsigned char *target_hw)
+static void arp_send_q(struct arp_table *entry)
 {
 	struct sk_buff *skb;
-	struct arphdr *arp;
-	unsigned char *arp_ptr;
 
-	/*
-	 *	No arp on this interface.
-	 */
-	
-	if (dev->flags&IFF_NOARP)
-		return;
+	unsigned long flags;
 
 	/*
-	 *	Allocate a buffer
+	 *	Empty the entire queue, building its data up ready to send
 	 */
 	
-	skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
-				+ dev->hard_header_len, GFP_ATOMIC);
-	if (skb == NULL)
+	if(!(entry->flags&ATF_COM))
 	{
-		printk("ARP: no memory to send an arp packet\n");
-		return;
-	}
-	skb_reserve(skb, dev->hard_header_len);
-	arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
-	skb->arp = 1;
-	skb->dev = dev;
-	skb->free = 1;
-	skb->protocol = htons (ETH_P_IP);
-
-	/*
-	 *	Fill the device header for the ARP frame
-	 */
-
-	dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len);
-
-	/* Fill out the arp protocol part. */
-	arp->ar_hrd = htons(dev->type);
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
-	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
-#else
-	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
-#endif
-#else
-	arp->ar_pro = htons(ETH_P_IP);
-#endif
-	arp->ar_hln = dev->addr_len;
-	arp->ar_pln = 4;
-	arp->ar_op = htons(type);
-
-	arp_ptr=(unsigned char *)(arp+1);
-
-	memcpy(arp_ptr, src_hw, dev->addr_len);
-	arp_ptr+=dev->addr_len;
-	memcpy(arp_ptr, &src_ip,4);
-	arp_ptr+=4;
-	if (target_hw != NULL)
-		memcpy(arp_ptr, target_hw, dev->addr_len);
-	else
-		memset(arp_ptr, 0, dev->addr_len);
-	arp_ptr+=dev->addr_len;
-	memcpy(arp_ptr, &dest_ip, 4);
-
-	dev_queue_xmit(skb, dev, 0);
-}
-
-/*
- *	This will try to retransmit everything on the queue.
- */
-
-static void arp_send_q(struct arp_table *entry)
-{
-	struct sk_buff *skb;
-
-	unsigned long flags;
-
-	/*
-	 *	Empty the entire queue, building its data up ready to send
-	 */
-	
-	if(!(entry->flags&ATF_COM))
-	{
-		printk("arp_send_q: incomplete entry for %s\n",
-				in_ntoa(entry->ip));
-		/* Can't flush the skb, because RFC1122 says to hang on to */
-		/* at least one from any unresolved entry.  --MS */
-		/* What's happened is that someone has 'unresolved' the entry
-		   as we got to use it - this 'can't happen' -- AC */
+		printk("arp_send_q: incomplete entry for %s\n",
+				in_ntoa(entry->ip));
+		/* Can't flush the skb, because RFC1122 says to hang on to */
+		/* at least one from any unresolved entry.  --MS */
+		/* What's happened is that someone has 'unresolved' the entry
+		   as we got to use it - this 'can't happen' -- AC */
 		return;
 	}
 
@@ -927,435 +1135,137 @@
 }
 
 
-/*
- *	Delete an ARP mapping entry in the cache.
- */
-
-static void arp_destroy(struct arp_table * entry)
+static int
+arp_update (u32 sip, char *sha, struct device * dev,
+	    struct arp_table *ientry, int grat)
 {
-	struct arp_table *entry1;
-	struct arp_table **pentry;
+	struct arp_table * entry;
+	unsigned long hash;
 
-	if (entry->flags & ATF_PUBL)
-		pentry = &arp_proxy_list;
-	else
-		pentry = &arp_tables[HASH(entry->ip)];
+	hash = HASH(sip);
 
-	while ((entry1 = *pentry) != NULL)
+	for (entry=arp_tables[hash]; entry; entry = entry->next)
+		if (entry->ip == sip && entry->dev == dev)
+			break;
+
+	if (entry)
 	{
-		if (entry1 == entry)
+/*
+ *	Entry found; update it only if it is not a permanent entry.
+ */
+		if (!(entry->flags & ATF_PERM)) 
 		{
-			*pentry = entry1->next;
 			del_timer(&entry->timer);
-			arp_free_entry(entry);
-			return;
+			entry->last_updated = jiffies;
+			if (memcmp(entry->ha, sha, dev->addr_len)!=0)
+			{
+				memcpy(entry->ha, sha, dev->addr_len);
+				if (entry->flags & ATF_COM)
+					arp_update_hhs(entry);
+			}
+			arpd_update(entry);
 		}
-		pentry = &entry1->next;
-	}
-}
 
+		if (!(entry->flags & ATF_COM))
+		{
 /*
- *	Receive an arp request by the device layer. Maybe I rewrite it, to
- *	use the incoming packet for the reply. The time for the current
- *	"overhead" isn't that high...
+ *	This entry was incomplete.  Delete the retransmit timer
+ *	and switch to complete status.
  */
-
-int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
-{
-/*
- *	We shouldn't use this type conversion. Check later.
+			entry->flags |= ATF_COM;
+			arp_update_hhs(entry);
+/* 
+ *	Send out waiting packets. We might have problems, if someone is 
+ *	manually removing entries right now -- entry might become invalid 
+ *	underneath us.
  */
-	
-	struct arphdr *arp = (struct arphdr *)skb->h.raw;
-	unsigned char *arp_ptr= (unsigned char *)(arp+1);
-	struct arp_table *entry;
-	struct arp_table *proxy_entry;
-	unsigned long hash, grat=0;
-	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
-	unsigned char *sha,*tha;
-	u32 sip,tip;
-	
-/*
- *	The hardware length of the packet should match the hardware length
- *	of the device.  Similarly, the hardware types should match.  The
- *	device should be ARP-able.  Also, if pln is not 4, then the lookup
- *	is not from an IP number.  We can't currently handle this, so toss
- *	it. 
- */  
-	if (arp->ar_hln != dev->addr_len    || 
-     		dev->type != ntohs(arp->ar_hrd) || 
-		dev->flags & IFF_NOARP          ||
-		arp->ar_pln != 4)
-	{
-		kfree_skb(skb, FREE_READ);
-		return 0;
-		/* Should this be an error/printk?  Seems like something */
-		/* you'd want to know about. Unless it's just !IFF_NOARP. -- MS */
+			arp_send_q(entry);
+		}
+		return 1;
 	}
 
 /*
- *	Another test.
- *	The logic here is that the protocol being looked up by arp should 
- *	match the protocol the device speaks.  If it doesn't, there is a
- *	problem, so toss the packet.
+ * 	No entry found.  Need to add a new entry to the arp table.
  */
-/* Again, should this be an error/printk? -- MS */
-
-  	switch (dev->type)
-  	{
-#ifdef CONFIG_AX25
-		case ARPHRD_AX25:
-			if(arp->ar_pro != htons(AX25_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
-#endif
-#ifdef CONFIG_NETROM
-		case ARPHRD_NETROM:
-			if(arp->ar_pro != htons(AX25_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
-#endif
-		case ARPHRD_ETHER:
-		case ARPHRD_ARCNET:
-		case ARPHRD_METRICOM:
-			if(arp->ar_pro != htons(ETH_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
+	entry = ientry;
 
-		case ARPHRD_IEEE802:
-			if(arp->ar_pro != htons(ETH_P_IP))
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			break;
+	if (grat && !entry)
+		return 0;
 
-		default:
-			printk("ARP: dev->type mangled!\n");
-			kfree_skb(skb, FREE_READ);
+	if (!entry)
+	{
+		entry = arp_alloc_entry();
+		if (!entry)
 			return 0;
+
+		entry->ip = sip;
+		entry->flags = ATF_COM;
+		memcpy(entry->ha, sha, dev->addr_len);
+		entry->dev = dev;
 	}
 
-/*
- *	Extract fields
- */
+	entry->last_updated = entry->last_used = jiffies;
+	arpd_update(entry);
 
-	sha=arp_ptr;
-	arp_ptr += dev->addr_len;
-	memcpy(&sip, arp_ptr, 4);
-	arp_ptr += 4;
-	tha=arp_ptr;
-	arp_ptr += dev->addr_len;
-	memcpy(&tip, arp_ptr, 4);
-  
-/* 
- *	Check for bad requests for 127.x.x.x and requests for multicast
- *	addresses.  If this is one such, delete it.
- */
-	if (LOOPBACK(tip) || MULTICAST(tip))
+	if (!ARP_LOCKED())
 	{
-		kfree_skb(skb, FREE_READ);
+		entry->next = arp_tables[hash];
+		arp_tables[hash] = entry;
 		return 0;
 	}
+#if RT_CACHE_DEBUG >= 2
+	printk("arp_update: %08x backlogged\n", entry->ip);
+#endif
+	arp_enqueue(&arp_backlog, entry);
+	arp_bh_mask |= ARP_BH_BACKLOG;
+	return 0;
+}
 
-/*
- *  Process entry.  The idea here is we want to send a reply if it is a
- *  request for us or if it is a request for someone else that we hold
- *  a proxy for.  We want to add an entry to our cache if it is a reply
- *  to us or if it is a request for our address.  
- *  (The assumption for this last is that if someone is requesting our 
- *  address, they are probably intending to talk to us, so it saves time 
- *  if we cache their address.  Their address is also probably not in 
- *  our cache, since ours is not in their cache.)
- * 
- *  Putting this another way, we only care about replies if they are to
- *  us, in which case we add them to the cache.  For requests, we care
- *  about those for us and those for our proxies.  We reply to both,
- *  and in the case of requests for us we add the requester to the arp 
- *  cache.
- */
+
+
+static __inline__ struct arp_table *arp_lookup(u32 paddr, struct device * dev)
+{
+	struct arp_table *entry;
+
+	for (entry = arp_tables[HASH(paddr)]; entry != NULL; entry = entry->next)
+		if (entry->ip == paddr && (!dev || entry->dev == dev))
+			return entry;
+	return NULL;
+}
 
 /*
- *	try to switch to alias device whose addr is tip or closest to sip.
+ *	Find an arp mapping in the cache. If not found, return false.
  */
 
-#ifdef CONFIG_NET_ALIAS
-	if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
-	{
-		/*
-		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
-		 */
-		dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
+{
+	struct arp_table *entry;
 
-		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
+	arp_fast_lock();
+
+	entry = arp_lookup(paddr, dev);
+
+	if (entry != NULL)
+	{
+		entry->last_used = jiffies;
+		if (entry->flags & ATF_COM)
 		{
-			kfree_skb(skb, FREE_READ);
-			return 0;
+			memcpy(haddr, entry->ha, dev->addr_len);
+			arp_unlock();
+			return 1;
 		}
 	}
-#endif
+	arp_unlock();
+	return 0;
+}
 
-	if (arp->ar_op == htons(ARPOP_REQUEST))
-	{ 
-/*
- * Only reply for the real device address or when it's in our proxy tables
- */
-		if (tip != dev->pa_addr)
-		{
-/*
- * 	To get in here, it is a request for someone else.  We need to
- * 	check if that someone else is one of our proxies.  If it isn't,
- * 	we can toss it.
- */
-			arp_fast_lock();
-
-			for (proxy_entry=arp_proxy_list;
-			     proxy_entry;
-			     proxy_entry = proxy_entry->next)
-			{
-				/* we will respond to a proxy arp request
-				   if the masked arp table ip matches the masked
-				   tip. This allows a single proxy arp table
-				   entry to be used on a gateway machine to handle
-				   all requests for a whole network, rather than
-				   having to use a huge number of proxy arp entries
-				   and having to keep them uptodate.
-				   */
-				if (proxy_entry->dev == dev &&
-				    !((proxy_entry->ip^tip)&proxy_entry->mask))
-					break;
-
-			}
-			if (proxy_entry)
-			{
-				memcpy(ha, proxy_entry->ha, dev->addr_len);
-				arp_unlock();
-				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha, sha);
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
-			else
-			{
-				arp_unlock();
-			}
-		}
-		else
-		{
-/*
- * 	To get here, it must be an arp request for us.  We need to reply.
- */
-			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
-		}
-		grat = 1;
-		goto gratuitous;
-	}
-/*
- *	It is now an arp reply.
- */
-	if(ip_chk_addr(tip)!=IS_MYADDR)
-	{
-/*
- *	Replies to other machines get tossed.
- */
- 		kfree_skb(skb, FREE_READ);
- 		return 0;
- 	}
-/*
- * Now all replies are handled.  Next, anything that falls through to here
- * needs to be added to the arp cache, or have its entry updated if it is 
- * there.
- */
- 
-gratuitous:
-
-	arp_fast_lock();
-
-
-	hash = HASH(sip);
-	for (entry=arp_tables[hash]; entry; entry=entry->next)
-		if (entry->ip == sip && entry->dev == dev)
-			break;
-
-	if (entry)
-	{
-/*
- *	Entry found; update it only if it is not a permanent entry.
- */
-		if (!(entry->flags & ATF_PERM)) 
-		{
-			if(memcmp(entry->ha, sha,dev->addr_len)!=0)
-			{
-				memcpy(entry->ha, sha, dev->addr_len);
-				if(entry->flags & ATF_COM)
-					arp_update_hhs(entry);
-			}
-			entry->last_updated = jiffies;
-			arpd_update(entry, __LINE__);
-		}
-		if (!(entry->flags & ATF_COM))
-		{
-/*
- *	This entry was incomplete.  Delete the retransmit timer
- *	and switch to complete status.
- */
-			del_timer(&entry->timer);
-			entry->flags |= ATF_COM;
-			arp_update_hhs(entry);
-/* 
- *	Send out waiting packets. We might have problems, if someone is 
- *	manually removing entries right now -- entry might become invalid 
- *	underneath us.
- */
-			arp_send_q(entry);
-		}
-	}
-	else
-	{
-/*
- * 	No entry found.  Need to add a new entry to the arp table.
- */
-
-		if (grat) 
-			goto end;
-
-                entry = arp_add_entry();
-		if(entry == NULL)
-		{
-			arp_unlock();
-#if RT_CACHE_DEBUG >= 2
-			printk("ARP: no memory for new arp entry\n");
-#endif
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-
-                entry->mask = DEF_ARP_NETMASK;
-		entry->ip = sip;
-		entry->flags = ATF_COM;
-		entry->hh    = NULL;
-		init_timer(&entry->timer);
-		entry->timer.function = arp_expire_request;
-		entry->timer.data = (unsigned long)entry;
-		memcpy(entry->ha, sha, dev->addr_len);
-		entry->last_updated = entry->last_used = jiffies;
-		arpd_update(entry, __LINE__);
-/*
- *	make entry point to	'correct' device
- */
-
-#ifdef CONFIG_NET_ALIAS
-		entry->dev = dev;
-#else
-		entry->dev = skb->dev;
-#endif
-		skb_queue_head_init(&entry->skb);
-		if (arp_lock == 1)
-		{
-			entry->next = arp_tables[hash];
-			arp_tables[hash] = entry;
-		}
-		else
-		{
-#if RT_CACHE_DEBUG >= 2
-			printk("arp_rcv: %08x backlogged\n", entry->ip);
-#endif
-			arp_enqueue(&arp_backlog, entry);
-			arp_bh_mask |= ARP_BH_BACKLOG;
-		}
-	}
-
-/*
- *	Replies have been sent, and entries have been added.  All done.
- */
-
-end:
-	kfree_skb(skb, FREE_READ);
-	arp_unlock();
-	return 0;
-}
-
-/*
- * Lookup ARP entry by (addr, dev) pair.
- * Flags: ATF_PUBL - search for proxy entries
- *	  ATF_NETMASK - search for proxy network entry.
- * NOTE:  should be called with locked ARP tables.
- */
 
-static struct arp_table *arp_lookup(u32 paddr, unsigned short flags, struct device * dev)
-{
-	struct arp_table *entry;
-
-	if (!(flags & ATF_PUBL))
-	{
-		for (entry = arp_tables[HASH(paddr)];
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == paddr && (!dev || entry->dev == dev))
-				break;
-		return entry;
-	}
-
-	if (!(flags & ATF_NETMASK))
-	{
-		for (entry = arp_proxy_list;
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == paddr && (!dev || entry->dev == dev))
-				break;
-		return entry;
-	}
-
-	for (entry=arp_proxy_list; entry != NULL; entry = entry->next)
-		if (!((entry->ip^paddr)&entry->mask) && 
-		                                  (!dev || entry->dev == dev))
-			break;
-	return entry;
-}
-
-/*
- *	Find an arp mapping in the cache. If not found, return false.
- */
-
-int arp_query(unsigned char *haddr, u32 paddr, struct device * dev)
-{
-	struct arp_table *entry;
-
-	arp_fast_lock();
-
-	entry = arp_lookup(paddr, 0, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(paddr, 0, dev, __LINE__);
-
-	if (entry != NULL)
-	{
-		entry->last_used = jiffies;
-		if (entry->flags & ATF_COM)
-		{
-			memcpy(haddr, entry->ha, dev->addr_len);
-			arpd_update(entry, __LINE__);
-			arp_unlock();
-			return 1;
-		}
-	}
-	arpd_update(entry, __LINE__);
-	arp_unlock();
-	return 0;
-}
-
-
-static int arp_set_predefined(int addr_hint, unsigned char * haddr, __u32 paddr, struct device * dev)
+static int arp_set_predefined(int addr_hint, unsigned char * haddr, u32 paddr, struct device * dev)
 {
 	switch (addr_hint)
 	{
 		case IS_MYADDR:
-			printk("ARP: arp called for own IP address\n");
+			printk(KERN_DEBUG "ARP: arp called for own IP address\n");
 			memcpy(haddr, dev->dev_addr, dev->addr_len);
 			return 1;
 #ifdef CONFIG_IP_MULTICAST
@@ -1387,325 +1297,221 @@
 }
 
 /*
- *	Find an arp mapping in the cache. If not found, post a request.
+ *	Create a new unresolved entry.
  */
 
-int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
-	     u32 saddr, struct sk_buff *skb)
+struct arp_table * arp_new_entry(u32 paddr, struct device *dev, struct hh_cache *hh, struct sk_buff *skb)
 {
 	struct arp_table *entry;
-	unsigned long hash;
-
-	if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
-	{
-		if (skb)
-			skb->arp = 1;
-		return 0;
-	}
-
-	hash = HASH(paddr);
-	arp_fast_lock();
-
-	/*
-	 *	Find an entry
-	 */
-	entry = arp_lookup(paddr, 0, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(paddr, 0, dev, __LINE__);
-
-	if (entry != NULL) 	/* It exists */
-	{
-		if (!(entry->flags & ATF_COM))
-		{
-			/*
-			 *	A request was already send, but no reply yet. Thus
-			 *	queue the packet with the previous attempt
-			 */
-			
-			if (skb != NULL)
-			{
-				if (entry->last_updated)
-				{
-					skb_queue_tail(&entry->skb, skb);
-					skb_device_unlock(skb);
-				}
-				/*
-				 * If last_updated==0 host is dead, so
-				 * drop skb's and set socket error.
-				 */
-				else
-				{
-#if 0				
-					/*
-					 * FIXME: ICMP HOST UNREACHABLE should be
-					 *	  sent in this situation. --ANK
-					 */
-					if (skb->sk)
-					{
-						skb->sk->err = EHOSTDOWN;
-						skb->sk->error_report(skb->sk);
-					}
-#else
-					icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
-#endif										
-					dev_kfree_skb(skb, FREE_WRITE);
-				}
-			}
-			arp_unlock();
-			return 1;
-		}
 
-		/*
-		 *	Update the record
-		 */
-		
-		entry->last_used = jiffies;
-		memcpy(haddr, entry->ha, dev->addr_len);
-		arpd_update(entry, __LINE__);
-		if (skb)
-			skb->arp = 1;
-		arp_unlock();
-		return 0;
-	}
+	entry = arp_alloc_entry();
 
-	/*
-	 *	Create a new unresolved entry.
-	 */
-	
-	entry = arp_add_entry();
 	if (entry != NULL)
 	{
-		entry->last_updated = entry->last_used = jiffies;
-		entry->flags = 0;
 		entry->ip = paddr;
-		entry->mask = DEF_ARP_NETMASK;
-		memset(entry->ha, 0, dev->addr_len);
 		entry->dev = dev;
-		entry->hh    = NULL;
-		arpd_update(entry, __LINE__);
-		init_timer(&entry->timer);
-		entry->timer.function = arp_expire_request;
-		entry->timer.data = (unsigned long)entry;
+		if (hh)
+		{
+			entry->hh = hh;
+			atomic_inc(&hh->hh_refcnt);
+			hh->hh_arp = (void*)entry;
+		}
 		entry->timer.expires = jiffies + ARP_RES_TIME;
-		skb_queue_head_init(&entry->skb);
+
 		if (skb != NULL)
 		{
 			skb_queue_tail(&entry->skb, skb);
 			skb_device_unlock(skb);
 		}
-		if (arp_lock == 1)
+
+		if (!ARP_LOCKED())
 		{
+			unsigned long hash = HASH(paddr);
 			entry->next = arp_tables[hash];
 			arp_tables[hash] = entry;
 			add_timer(&entry->timer);
 			entry->retries = ARP_MAX_TRIES;
+#ifdef CONFIG_ARPD
+			if (!arpd_not_running)
+				arpd_lookup(paddr, dev);
+			else
+#endif
+				arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, 
+					 dev->dev_addr, NULL);
 		}
 		else
 		{
 #if RT_CACHE_DEBUG >= 2
-			printk("arp_find: %08x backlogged\n", entry->ip);
+			printk("arp_new_entry: %08x backlogged\n", entry->ip);
 #endif
-			arp_enqueue(&arp_backlog, entry);
+			arp_enqueue(&arp_req_backlog, entry);
 			arp_bh_mask |= ARP_BH_BACKLOG;
 		}
 	}
-	else if (skb != NULL)
-		dev_kfree_skb(skb, FREE_WRITE);
-	arp_unlock();
-
-	/*
-	 *	If we didn't find an entry, we will try to send an ARP packet.
-	 */
-	
-	arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, 
-		 dev->dev_addr, NULL);
-
-	return 1;
+	return entry;
 }
 
 
 /*
- *	Write the contents of the ARP cache to a PROCfs file.
+ *	Find an arp mapping in the cache. If not found, post a request.
  */
 
-#define HBUFFERLEN 30
-
-int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
+	     u32 saddr, struct sk_buff *skb)
 {
-	int len=0;
-	off_t pos=0;
-	int size;
 	struct arp_table *entry;
-	char hbuffer[HBUFFERLEN];
-	int i,j,k;
-	const char hexbuf[] =  "0123456789ABCDEF";
-
-	size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
+	unsigned long hash;
 
-	pos+=size;
-	len+=size;
+	if (arp_set_predefined(ip_chk_addr(paddr), haddr, paddr, dev))
+	{
+		if (skb)
+			skb->arp = 1;
+		return 0;
+	}
 
+	hash = HASH(paddr);
 	arp_fast_lock();
 
-	for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
+	/*
+	 *	Find an entry
+	 */
+	entry = arp_lookup(paddr, dev);
+
+	if (entry != NULL) 	/* It exists */
 	{
-		for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
+		if (entry->flags & ATF_COM)
 		{
-/*
- *	Convert hardware address to XX:XX:XX:XX ... form.
- */
-#ifdef CONFIG_AX25
-#ifdef CONFIG_NETROM
-			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
-			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
-			else {
-#else
-			if(entry->dev->type==ARPHRD_AX25)
-			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
-			else {
-#endif
-#endif
+			entry->last_used = jiffies;
+			memcpy(haddr, entry->ha, dev->addr_len);
+			if (skb)
+				skb->arp = 1;
+			arp_unlock();
+			return 0;
+		}
 
-			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
+		/*
+		 *	A request was already send, but no reply yet. Thus
+		 *	queue the packet with the previous attempt
+		 */
+			
+		if (skb != NULL)
+		{
+			if (entry->last_updated)
 			{
-				hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
-				hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
-				hbuffer[k++]=':';
+				skb_queue_tail(&entry->skb, skb);
+				skb_device_unlock(skb);
 			}
-			hbuffer[--k]=0;
-	
-#ifdef CONFIG_AX25
+			/*
+			 * If last_updated==0 host is dead, so
+			 * drop skb's and set socket error.
+			 */
+			else
+			{
+				icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
+				dev_kfree_skb(skb, FREE_WRITE);
 			}
-#endif
-			size = sprintf(buffer+len,
-				"%-17s0x%-10x0x%-10x%s",
-				in_ntoa(entry->ip),
-				(unsigned int)entry->dev->type,
-				entry->flags,
-				hbuffer);
-#if RT_CACHE_DEBUG < 2
-			size += sprintf(buffer+len+size,
-				 "     %-17s %s\n",
-				 entry->mask==DEF_ARP_NETMASK ?
-				 "*" : in_ntoa(entry->mask), entry->dev->name);
-#else
-			size += sprintf(buffer+len+size,
-				 "     %-17s %s\t%ld\t%1d\n",
-				 entry->mask==DEF_ARP_NETMASK ?
-				 "*" : in_ntoa(entry->mask), entry->dev->name, 
-				 entry->hh ? entry->hh->hh_refcnt : -1,
-				 entry->hh ? entry->hh->hh_uptodate : 0);
-#endif
-	
-			len += size;
-			pos += size;
-		  
-			if (pos <= offset)
-				len=0;
-			if (pos >= offset+length)
-				goto done;
 		}
+		arp_unlock();
+		return 1;
 	}
-done:
+
+	entry = arp_new_entry(paddr, dev, NULL, skb);
+
+	if (skb != NULL && !entry)
+		dev_kfree_skb(skb, FREE_WRITE);
+
 	arp_unlock();
-  
-	*start = buffer+len-(pos-offset);	/* Start of wanted data */
-	len = pos-offset;			/* Start slop */
-	if (len>length)
-		len = length;			/* Ending slop */
-	return len;
+	return 1;
 }
 
-
+/*
+ *	Binding hardware header cache entry.
+ *	It is the only really complicated part of arp code.
+ *	We have no locking for hh records, so that
+ *	all possible race conditions should be resolved by
+ *	cli()/sti() pairs.
+ *
+ *	Important note: hhs never disapear from lists, if ARP_LOCKED,
+ *	this fact allows to scan hh lists with enabled interrupts,
+ *	but results in generating duplicate hh entries.
+ *	It is harmless. (and I've never seen such event)
+ *
+ *	Returns 0, if hh has been just created, so that
+ *	caller should fill it.
+ */
 
 int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short htype, u32 paddr)
 {
 	struct arp_table *entry;
-	struct hh_cache *hh = *hhp;
+	struct hh_cache *hh;
 	int addr_hint;
 	unsigned long flags;
 
-	if (hh)
-		return 1;
+	save_flags(flags);
 
 	if ((addr_hint = ip_chk_addr(paddr)) != 0)
 	{
 		unsigned char haddr[MAX_ADDR_LEN];
-		if (hh)
+		if (*hhp)
 			return 1;
-		hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+		hh = arp_alloc_hh(htype);
 		if (!hh)
 			return 1;
 		arp_set_predefined(addr_hint, haddr, paddr, dev);
-		hh->hh_uptodate = 0;
-		hh->hh_refcnt = 1;
-		hh->hh_arp = NULL;
-		hh->hh_next = NULL;
-		hh->hh_type = htype;
-		*hhp = hh;
 		dev->header_cache_update(hh, dev, haddr);
-		return 0;
+		return arp_set_hh(hhp, hh);
 	}
 
-	save_flags(flags);
-
 	arp_fast_lock();
 
-	entry = arp_lookup(paddr, 0, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(paddr, 0, dev, __LINE__);
+	entry = arp_lookup(paddr, dev);
 
 	if (entry)
 	{
-		cli();
 		for (hh = entry->hh; hh; hh=hh->hh_next)
 			if (hh->hh_type == htype)
 				break;
+
 		if (hh)
 		{
-			hh->hh_refcnt++;
-			*hhp = hh;
-			restore_flags(flags);
+			arp_set_hh(hhp, hh);
 			arp_unlock();
 			return 1;
 		}
-		restore_flags(flags);
 	}
 
-	hh = kmalloc(sizeof(struct hh_cache), GFP_ATOMIC);
+	hh = arp_alloc_hh(htype);
 	if (!hh)
 	{
 		arp_unlock();
 		return 1;
 	}
 
-	hh->hh_uptodate = 0;
-	hh->hh_refcnt = 1;
-	hh->hh_arp = NULL;
-	hh->hh_next = NULL;
-	hh->hh_type = htype;
-
 	if (entry)
 	{
-		dev->header_cache_update(hh, dev, entry->ha);
-		*hhp = hh;
+
 		cli();
 		hh->hh_arp = (void*)entry;
+		hh->hh_next = entry->hh;
 		entry->hh = hh;
-		hh->hh_refcnt++;
+		atomic_inc(&hh->hh_refcnt);
 		restore_flags(flags);
+
+		if (entry->flags & ATF_COM)
+			dev->header_cache_update(hh, dev, entry->ha);
+
+		if (arp_set_hh(hhp, hh))
+		{
+			arp_unlock();
+			return 0;
+		}
+
 		entry->last_used = jiffies;
-		arpd_update(entry, __LINE__);
 		arp_unlock();
 		return 0;
 	}
 
-
-	/*
-	 *	Create a new unresolved entry.
-	 */
-	
-	entry = arp_add_entry();
+	entry = arp_new_entry(paddr, dev, hh, NULL);
 	if (entry == NULL)
 	{
 		kfree_s(hh, sizeof(struct hh_cache));
@@ -1713,91 +1519,74 @@
 		return 1;
 	}
 
-	entry->last_updated = entry->last_used = jiffies;
-	entry->flags = 0;
-	entry->ip = paddr;
-	entry->mask = DEF_ARP_NETMASK;
-	memset(entry->ha, 0, dev->addr_len);
-	entry->dev = dev;
-	entry->hh = hh;
-	arpd_update(entry, __LINE__);
-	ATOMIC_INCR(&hh->hh_refcnt);
-	init_timer(&entry->timer);
-	entry->timer.function = arp_expire_request;
-	entry->timer.data = (unsigned long)entry;
-	entry->timer.expires = jiffies + ARP_RES_TIME;
-	skb_queue_head_init(&entry->skb);
-
-	if (arp_lock == 1)
+	if (!arp_set_hh(hhp, hh))
 	{
-		unsigned long hash = HASH(paddr);
-		cli();
-		entry->next = arp_tables[hash];
-		arp_tables[hash] = entry;
-		hh->hh_arp = (void*)entry;
-		entry->retries = ARP_MAX_TRIES;
-		restore_flags(flags);
-
-		add_timer(&entry->timer);
-		arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
-	}
-	else
-	{
-#if RT_CACHE_DEBUG >= 1
-		printk("arp_cache_bind: %08x backlogged\n", entry->ip);
-#endif
-		arp_enqueue(&arp_backlog, entry);
-		arp_bh_mask |= ARP_BH_BACKLOG;
+		arp_unlock();
+		return 0;
 	}
-	*hhp = hh;
 	arp_unlock();
-	return 0;
+	return 1;
 }
 
 static void arp_run_bh()
 {
 	unsigned long flags;
 	struct arp_table *entry, *entry1;
+	struct device  * dev;
+	unsigned long hash;
 	struct hh_cache *hh;
-	__u32 sip;
+	u32 sip;
 
 	save_flags(flags);
 	cli();
-	if (!arp_lock)
+	arp_fast_lock();
+
+	while (arp_bh_mask)
 	{
-		arp_fast_lock();
+		arp_bh_mask  &= ~ARP_BH_BACKLOG;
 
 		while ((entry = arp_dequeue(&arp_backlog)) != NULL)
 		{
-			unsigned long hash;
-			sti();
+			restore_flags(flags);
+			if (arp_update(entry->ip, entry->ha, entry->dev, entry, 0))
+				arp_free_entry(entry);
+			cli();
+		}
+
+		cli();
+		while ((entry = arp_dequeue(&arp_req_backlog)) != NULL)
+		{
+			restore_flags(flags);
+
+			dev = entry->dev;
 			sip = entry->ip;
 			hash = HASH(sip);
 
-			/* It's possible, that an entry with the same pair 
-			 * (addr,type) was already created. Our entry is older,
-			 * so it should be discarded.
-			 */
-			for (entry1=arp_tables[hash]; entry1; entry1=entry1->next)
-				if (entry1->ip==sip && entry1->dev == entry->dev)
+			for (entry1 = arp_tables[hash]; entry1; entry1 = entry1->next)
+				if (entry1->ip == sip && entry1->dev == dev)
 					break;
 
 			if (!entry1)
 			{
-				struct device  * dev = entry->dev;
 				cli();
 				entry->next = arp_tables[hash];
 				arp_tables[hash] = entry;
-				for (hh=entry->hh; hh; hh=hh->hh_next)
-					hh->hh_arp = (void*)entry;
-				sti();
-				del_timer(&entry->timer);
+				restore_flags(flags);
 				entry->timer.expires = jiffies + ARP_RES_TIME;
-				add_timer(&entry->timer);
 				entry->retries = ARP_MAX_TRIES;
-				arp_send(ARPOP_REQUEST, ETH_P_ARP, entry->ip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+				entry->last_used = jiffies;
+				if (!(entry->flags & ATF_COM))
+				{
+					add_timer(&entry->timer);
+#ifdef CONFIG_ARPD
+					if (!arpd_not_running)
+						arpd_lookup(sip, dev);
+					else
+#endif
+						arp_send(ARPOP_REQUEST, ETH_P_ARP, sip, dev, dev->pa_addr, NULL, dev->dev_addr, NULL);
+				}
 #if RT_CACHE_DEBUG >= 1
-				printk("arp_run_bh: %08x reinstalled\n", sip);
+				printk(KERN_DEBUG "arp_run_bh: %08x reinstalled\n", sip);
 #endif
 			}
 			else
@@ -1824,59 +1613,353 @@
 				while ((skb = skb_dequeue(&entry->skb)) != NULL)
 				{
 					skb_device_lock(skb);
-					sti();
+					restore_flags(flags);
 					skb_queue_tail(&entry1->skb, skb);
 					skb_device_unlock(skb);
 					cli();
 				}
-				sti();
+				restore_flags(flags);
 				
-#if RT_CACHE_DEBUG >= 1
-				printk("arp_run_bh: entry %08x was born dead\n", entry->ip);
-#endif
 				arp_free_entry(entry);
 
-				if (entry1->flags & ATF_COM)
-				{
-					arp_update_hhs(entry1);
-					arp_send_q(entry1);
-				}
+				if (entry1->flags & ATF_COM)
+				{
+					arp_update_hhs(entry1);
+					arp_send_q(entry1);
+				}
+			}
+			cli();
+		}
+		cli();
+	}
+	arp_unlock();
+	restore_flags(flags);
+}
+
+
+/*
+ *	Interface to link layer: send routine and receive handler.
+ */
+
+/*
+ *	Create and send an arp packet. If (dest_hw == NULL), we create a broadcast
+ *	message.
+ */
+
+void arp_send(int type, int ptype, u32 dest_ip, 
+	      struct device *dev, u32 src_ip, 
+	      unsigned char *dest_hw, unsigned char *src_hw,
+	      unsigned char *target_hw)
+{
+	struct sk_buff *skb;
+	struct arphdr *arp;
+	unsigned char *arp_ptr;
+
+	/*
+	 *	No arp on this interface.
+	 */
+	
+	if (dev->flags&IFF_NOARP)
+		return;
+
+	/*
+	 *	Allocate a buffer
+	 */
+	
+	skb = alloc_skb(sizeof(struct arphdr)+ 2*(dev->addr_len+4)
+				+ dev->hard_header_len, GFP_ATOMIC);
+	if (skb == NULL)
+	{
+		printk("ARP: no memory to send an arp packet\n");
+		return;
+	}
+	skb_reserve(skb, dev->hard_header_len);
+	arp = (struct arphdr *) skb_put(skb,sizeof(struct arphdr) + 2*(dev->addr_len+4));
+	skb->arp = 1;
+	skb->dev = dev;
+	skb->free = 1;
+	skb->protocol = htons (ETH_P_IP);
+
+	/*
+	 *	Fill the device header for the ARP frame
+	 */
+
+	dev->hard_header(skb,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len);
+
+	/* Fill out the arp protocol part. */
+	arp->ar_hrd = htons(dev->type);
+#ifdef CONFIG_AX25
+#ifdef CONFIG_NETROM
+	arp->ar_pro = (dev->type == ARPHRD_AX25 || dev->type == ARPHRD_NETROM) ? htons(AX25_P_IP) : htons(ETH_P_IP);
+#else
+	arp->ar_pro = (dev->type != ARPHRD_AX25) ? htons(ETH_P_IP) : htons(AX25_P_IP);
+#endif
+#else
+	arp->ar_pro = htons(ETH_P_IP);
+#endif
+	arp->ar_hln = dev->addr_len;
+	arp->ar_pln = 4;
+	arp->ar_op = htons(type);
+
+	arp_ptr=(unsigned char *)(arp+1);
+
+	memcpy(arp_ptr, src_hw, dev->addr_len);
+	arp_ptr+=dev->addr_len;
+	memcpy(arp_ptr, &src_ip,4);
+	arp_ptr+=4;
+	if (target_hw != NULL)
+		memcpy(arp_ptr, target_hw, dev->addr_len);
+	else
+		memset(arp_ptr, 0, dev->addr_len);
+	arp_ptr+=dev->addr_len;
+	memcpy(arp_ptr, &dest_ip, 4);
+
+	dev_queue_xmit(skb, dev, 0);
+}
+
+
+/*
+ *	Receive an arp request by the device layer.
+ */
+
+int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+/*
+ *	We shouldn't use this type conversion. Check later.
+ */
+	
+	struct arphdr *arp = (struct arphdr *)skb->h.raw;
+	unsigned char *arp_ptr= (unsigned char *)(arp+1);
+	unsigned char *sha,*tha;
+	u32 sip,tip;
+	
+/*
+ *	The hardware length of the packet should match the hardware length
+ *	of the device.  Similarly, the hardware types should match.  The
+ *	device should be ARP-able.  Also, if pln is not 4, then the lookup
+ *	is not from an IP number.  We can't currently handle this, so toss
+ *	it. 
+ */  
+	if (arp->ar_hln != dev->addr_len    || 
+     		dev->type != ntohs(arp->ar_hrd) || 
+		dev->flags & IFF_NOARP          ||
+		arp->ar_pln != 4)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+		/* Should this be an error/printk?  Seems like something */
+		/* you'd want to know about. Unless it's just !IFF_NOARP. -- MS */
+	}
+
+/*
+ *	Another test.
+ *	The logic here is that the protocol being looked up by arp should 
+ *	match the protocol the device speaks.  If it doesn't, there is a
+ *	problem, so toss the packet.
+ */
+/* Again, should this be an error/printk? -- MS */
+
+  	switch (dev->type)
+  	{
+#ifdef CONFIG_AX25
+		case ARPHRD_AX25:
+			if(arp->ar_pro != htons(AX25_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+#endif
+#ifdef CONFIG_NETROM
+		case ARPHRD_NETROM:
+			if(arp->ar_pro != htons(AX25_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+#endif
+		case ARPHRD_ETHER:
+		case ARPHRD_ARCNET:
+		case ARPHRD_METRICOM:
+			if(arp->ar_pro != htons(ETH_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+
+		case ARPHRD_IEEE802:
+			if(arp->ar_pro != htons(ETH_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
+
+		default:
+			printk("ARP: dev->type mangled!\n");
+			kfree_skb(skb, FREE_READ);
+			return 0;
+	}
+
+/*
+ *	Extract fields
+ */
+
+	sha=arp_ptr;
+	arp_ptr += dev->addr_len;
+	memcpy(&sip, arp_ptr, 4);
+	arp_ptr += 4;
+	tha=arp_ptr;
+	arp_ptr += dev->addr_len;
+	memcpy(&tip, arp_ptr, 4);
+  
+/* 
+ *	Check for bad requests for 127.x.x.x and requests for multicast
+ *	addresses.  If this is one such, delete it.
+ */
+	if (LOOPBACK(tip) || MULTICAST(tip))
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+/*
+ *  Process entry.  The idea here is we want to send a reply if it is a
+ *  request for us or if it is a request for someone else that we hold
+ *  a proxy for.  We want to add an entry to our cache if it is a reply
+ *  to us or if it is a request for our address.  
+ *  (The assumption for this last is that if someone is requesting our 
+ *  address, they are probably intending to talk to us, so it saves time 
+ *  if we cache their address.  Their address is also probably not in 
+ *  our cache, since ours is not in their cache.)
+ * 
+ *  Putting this another way, we only care about replies if they are to
+ *  us, in which case we add them to the cache.  For requests, we care
+ *  about those for us and those for our proxies.  We reply to both,
+ *  and in the case of requests for us we add the requester to the arp 
+ *  cache.
+ */
+
+/*
+ *	try to switch to alias device whose addr is tip or closest to sip.
+ */
+
+#ifdef CONFIG_NET_ALIAS
+	if (tip != dev->pa_addr && net_alias_has(skb->dev)) 
+	{
+		/*
+		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
+		 */
+		dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+
+		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
+		}
+	}
+#endif
+
+	if (arp->ar_op == htons(ARPOP_REQUEST))
+	{ 
+
+/*
+ * Only reply for the real device address or when it's in our proxy tables
+ */
+		if (tip != dev->pa_addr)
+		{
+			struct arp_table *proxy_entry;
+
+/*
+ * 	To get in here, it is a request for someone else.  We need to
+ * 	check if that someone else is one of our proxies.  If it isn't,
+ * 	we can toss it.
+ *
+ *	Make "longest match" lookup, a la routing.
+ */
+
+			arp_fast_lock();
+
+			for (proxy_entry = arp_proxy_list; proxy_entry;
+			     proxy_entry = proxy_entry->next)
+			{
+				if (proxy_entry->dev == dev &&
+				    !((proxy_entry->ip^tip)&proxy_entry->mask))
+					break;
+			}
+
+			if (proxy_entry && (proxy_entry->mask || ((dev->pa_addr^tip)&dev->pa_mask)))
+			{
+				char ha[MAX_ADDR_LEN];
+				struct rtable * rt;
+
+				/* Unlock arp tables to make life for
+				 * ip_rt_route easy. Note, that we are obliged
+				 * to make local copy of hardware address.
+				 */
+
+				memcpy(ha, proxy_entry->ha, dev->addr_len);
+				arp_unlock();
+
+				rt = ip_rt_route(tip, 0);
+				if (rt  && rt->rt_dev != dev)
+					arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha,sha);
+				ip_rt_put(rt);
+
 			}
-			cli();
+			else
+				arp_unlock();
 		}
-		arp_bh_mask  &= ~ARP_BH_BACKLOG;
+		else
+			arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
+
+/*
+ *	Handle gratuitous arp.
+ */
+		arp_fast_lock();
+		arp_update(sip, sha, dev, NULL, 1);
 		arp_unlock();
+		kfree_skb(skb, FREE_READ);
+		return 0;
 	}
-	restore_flags(flags);
+
+	arp_fast_lock();
+	arp_update(sip, sha, dev, NULL, ip_chk_addr(tip) != IS_MYADDR);
+	arp_unlock();
+	kfree_skb(skb, FREE_READ);
+	return 0;
 }
 
+
+
 /*
- * Test if a hardware address is all zero
+ *	User level interface (ioctl, /proc)
  */
 
-static inline int empty(unsigned char * addr, int len)
-{
-	while (len > 0) {
-		if (*addr)
-			return 0;
-		len--;
-		addr++;
-	}
-	return 1;
-}
-
 /*
  *	Set (create) an ARP cache entry.
  */
 
 static int arp_req_set(struct arpreq *r, struct device * dev)
 {
-	struct arp_table *entry;
+	struct arp_table *entry, **entryp;
 	struct sockaddr_in *si;
-	struct rtable *rt;
-	struct device *dev1;
 	unsigned char *ha;
 	u32 ip;
+	u32 mask = DEF_ARP_NETMASK;
+	unsigned long flags;
+
+	/*
+	 *	Extract netmask (if supplied).
+	 */
+
+	if (r->arp_flags&ATF_NETMASK)
+	{
+		si = (struct sockaddr_in *) &r->arp_netmask;
+		mask = si->sin_addr.s_addr;
+	}
 
 	/*
 	 *	Extract destination.
@@ -1885,112 +1968,89 @@
 	si = (struct sockaddr_in *) &r->arp_pa;
 	ip = si->sin_addr.s_addr;
 
-	/*
-	 *	Is it reachable ?
-	 */
 
-	if (ip_chk_addr(ip) == IS_MYADDR)
-		dev1 = dev_get("lo");
-	else {
-		rt = ip_rt_route(ip, 0);
-		if (!rt)
-			return -ENETUNREACH;
-		dev1 = rt->rt_dev;
-		ip_rt_put(rt);
+	if (r->arp_flags&ATF_PUBL)
+	{
+		if (!mask && ip)
+			return -EINVAL;
+		if (!dev)
+			dev = dev_getbytype(r->arp_ha.sa_family);
 	}
-
-	/* good guess about the device if it isn't a ATF_PUBL entry */
-	if (!dev) {
-		if (dev1->flags&(IFF_LOOPBACK|IFF_NOARP))
-			return -ENODEV;
-		dev = dev1;
+	else
+	{
+		if (ip_chk_addr(ip))
+			return -EINVAL;
+		if (!dev)
+		{
+			struct rtable * rt;
+			rt = ip_rt_route(ip, 0);
+			if (!rt)
+				return -ENETUNREACH;
+			dev = rt->rt_dev;
+			ip_rt_put(rt);
+		}
 	}
+	if (!dev || (dev->flags&(IFF_LOOPBACK|IFF_NOARP)))
+		return -ENODEV;
 
-	/* this needs to be checked only for dev=dev1 but it doesn't hurt */	
 	if (r->arp_ha.sa_family != dev->type)	
 		return -EINVAL;
 		
-	if (((r->arp_flags & ATF_PUBL) && dev == dev1) ||
-	    (!(r->arp_flags & ATF_PUBL) && dev != dev1))
-		return -EINVAL;
-
+	arp_fast_lock();
 #if RT_CACHE_DEBUG >= 1
-	if (arp_lock)
+	if (ARP_LOCKED())
 		printk("arp_req_set: bug\n");
 #endif
-	arp_fast_lock();
-
-	/*
-	 *	Is there an existing entry for this address?
-	 */
 
-	/*
-	 *	Find the entry
-	 */
-	
-	entry = arp_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(ip, r->arp_flags & ~ATF_NETMASK, dev, __LINE__);
+	if (!(r->arp_flags & ATF_PUBL))
+		entryp = &arp_tables[HASH(ip)];
+	else
+		entryp = &arp_proxy_list;
 
-	if (entry)
+	while ((entry = *entryp) != NULL)
 	{
-		arp_destroy(entry);
-		entry = NULL;
+		if (entry->ip == ip && entry->mask == mask && entry->dev == dev)
+			break;
+		if ((entry->mask & mask) != mask)
+		{
+			entry = NULL;
+			break;
+		}
+		entryp = &entry->next;
 	}
 
 	/*
-	 *	Do we need to create a new entry
+	 *	Do we need to create a new entry?
 	 */
 	
 	if (entry == NULL)
 	{
-		entry = arp_add_entry();
+		entry = arp_alloc_entry();
 		if (entry == NULL)
 		{
 			arp_unlock();
 			return -ENOMEM;
 		}
 		entry->ip = ip;
-		entry->hh = NULL;
-		init_timer(&entry->timer);
-		entry->timer.function = arp_expire_request;
-		entry->timer.data = (unsigned long)entry;
+		entry->dev = dev;
+		entry->mask = mask;
+		entry->flags = r->arp_flags;
 
-		if (r->arp_flags & ATF_PUBL)
-		{
-			cli();
-			entry->next = arp_proxy_list;
-			arp_proxy_list = entry;
-			sti();
-		}
-		else
-		{
-			unsigned long hash = HASH(ip);
-			cli();
-			entry->next = arp_tables[hash];
-			arp_tables[hash] = entry;
-			sti();
-		}
-		skb_queue_head_init(&entry->skb);
+		entry->next = (*entryp)->next;
+		*entryp = entry;
 	}
-	/*
-	 *	We now have a pointer to an ARP entry.  Update it!
-	 */
+
 	ha = r->arp_ha.sa_data;
-	if ((r->arp_flags & ATF_COM) && empty(ha, dev->addr_len))
+	if (empty(ha, dev->addr_len))
 		ha = dev->dev_addr;
+
+	save_flags(flags);
+	cli();
 	memcpy(entry->ha, ha, dev->addr_len);
 	entry->last_updated = entry->last_used = jiffies;
-	arpd_update(entry, __LINE__);
-	entry->flags = r->arp_flags | ATF_COM;
-	if ((entry->flags & ATF_PUBL) && (entry->flags & ATF_NETMASK))
-	{
-		si = (struct sockaddr_in *) &r->arp_netmask;
-		entry->mask = si->sin_addr.s_addr;
-	}
-	else
-		entry->mask = DEF_ARP_NETMASK;
-	entry->dev = dev;
+	entry->flags |= ATF_COM;
+	restore_flags(flags);
+	arpd_update(entry);
 	arp_update_hhs(entry);
 	arp_unlock();
 	return 0;
@@ -2006,77 +2066,88 @@
 {
 	struct arp_table *entry;
 	struct sockaddr_in *si;
+	u32 mask = DEF_ARP_NETMASK;
+
+	if (r->arp_flags&ATF_NETMASK)
+	{
+		si = (struct sockaddr_in *) &r->arp_netmask;
+		mask = si->sin_addr.s_addr;
+	}
 
 	si = (struct sockaddr_in *) &r->arp_pa;
 
+	arp_fast_lock();
 #if RT_CACHE_DEBUG >= 1
-	if (arp_lock)
-		printk("arp_req_set: bug\n");
+	if (ARP_LOCKED())
+		printk("arp_req_set: impossible\n");
 #endif
-	arp_fast_lock();
 
-	entry = arp_lookup(si->sin_addr.s_addr, r->arp_flags|ATF_NETMASK, dev);
-	if (entry == NULL)
-		entry = arpd_lookup(si->sin_addr.s_addr, 
-					r->arp_flags|ATF_NETMASK, dev, __LINE__);
+	if (!(r->arp_flags & ATF_PUBL))
+		entry = arp_tables[HASH(si->sin_addr.s_addr)];
+	else
+		entry = arp_proxy_list;
 
-	if (entry == NULL)
+	for ( ; entry ;entry = entry->next)
 	{
-		arp_unlock();
-		return -ENXIO;
+		if (entry->ip == si->sin_addr.s_addr 
+		    && (!dev || entry->dev == dev)
+		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
+		{
+			memcpy(r->arp_ha.sa_data, entry->ha, entry->dev->addr_len);
+			r->arp_ha.sa_family = entry->dev->type;
+			r->arp_flags = entry->flags;
+			strncpy(r->arp_dev, entry->dev->name, sizeof(r->arp_dev));
+			arp_unlock();
+			return 0;
+		}
 	}
 
-	/*
-	 *	We found it; copy into structure.
-	 */
-	
-	memcpy(r->arp_ha.sa_data, &entry->ha, entry->dev->addr_len);
-	r->arp_ha.sa_family = entry->dev->type;
-	r->arp_flags = entry->flags;
-	strncpy(r->arp_dev, entry->dev->name, 16);
 	arp_unlock();
-	return 0;
+	return -ENXIO;
 }
 
 static int arp_req_delete(struct arpreq *r, struct device * dev)
 {
-	struct arp_table *entry;
-	struct sockaddr_in *si;
+	struct sockaddr_in	*si;
+	struct arp_table	*entry, **entryp;
+	int	retval = -ENXIO;
+	u32	mask = DEF_ARP_NETMASK;
+
+	if (r->arp_flags&ATF_NETMASK)
+	{
+		si = (struct sockaddr_in *) &r->arp_netmask;
+		mask = si->sin_addr.s_addr;
+	}
 
 	si = (struct sockaddr_in *) &r->arp_pa;
+
+	arp_fast_lock();
 #if RT_CACHE_DEBUG >= 1
-	if (arp_lock)
-		printk("arp_req_delete: bug\n");
+	if (ARP_LOCKED())
+		printk("arp_req_delete: impossible\n");
 #endif
-	arp_fast_lock();
 
 	if (!(r->arp_flags & ATF_PUBL))
-	{
-		for (entry = arp_tables[HASH(si->sin_addr.s_addr)];
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == si->sin_addr.s_addr 
-			    && (!dev || entry->dev == dev))
-			{
-				arp_destroy(entry);
-				arp_unlock();
-				return 0;
-			}
-	}
+		entryp = &arp_tables[HASH(si->sin_addr.s_addr)];
 	else
+		entryp = &arp_proxy_list;
+
+	while ((entry = *entryp) != NULL)
 	{
-		for (entry = arp_proxy_list;
-		     entry != NULL; entry = entry->next)
-			if (entry->ip == si->sin_addr.s_addr 
-			    && (!dev || entry->dev == dev)) 
-			{
-				arp_destroy(entry);
-				arp_unlock();
-				return 0;
-			}
+		if (entry->ip == si->sin_addr.s_addr 
+		    && (!dev || entry->dev == dev)
+		    && (!(r->arp_flags&ATF_NETMASK) || entry->mask == mask))
+		{
+			*entryp = entry->next;
+			arp_free_entry(entry);
+			retval = 0;
+			continue;
+		}
+		entryp = &entry->next;
 	}
 
 	arp_unlock();
-	return -ENXIO;
+	return retval;
 }
 
 /*
@@ -2119,8 +2190,11 @@
 
 	if (r.arp_pa.sa_family != AF_INET)
 		return -EPFNOSUPPORT;
-	if (((struct sockaddr_in *)&r.arp_pa)->sin_addr.s_addr == 0)
-		return -EINVAL;
+
+	if (!(r.arp_flags & ATF_PUBL))
+		r.arp_flags &= ~ATF_NETMASK;
+	if (!(r.arp_flags & ATF_NETMASK))
+		((struct sockaddr_in *)&r.arp_netmask)->sin_addr.s_addr=DEF_ARP_NETMASK;
 
 	if (r.arp_dev[0])
 	{
@@ -2132,14 +2206,6 @@
 		else if (r.arp_ha.sa_family != dev->type)
 			return -EINVAL;
 	}
-	else
-	{
-		if ((r.arp_flags & ATF_PUBL) &&
-		    ((cmd == SIOCSARP) || (cmd == OLD_SIOCSARP))) {
-			if ((dev = dev_getbytype(r.arp_ha.sa_family)) == NULL)
-				return -ENODEV;
-		}
-	}		 
 
 	switch(cmd)
 	{
@@ -2199,6 +2265,99 @@
 	return 0;
 }
 
+/*
+ *	Write the contents of the ARP cache to a PROCfs file.
+ */
+
+#define HBUFFERLEN 30
+
+int arp_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len=0;
+	off_t pos=0;
+	int size;
+	struct arp_table *entry;
+	char hbuffer[HBUFFERLEN];
+	int i,j,k;
+	const char hexbuf[] =  "0123456789ABCDEF";
+
+	size = sprintf(buffer,"IP address       HW type     Flags       HW address            Mask     Device\n");
+
+	pos+=size;
+	len+=size;
+
+	arp_fast_lock();
+
+	for(i=0; i<FULL_ARP_TABLE_SIZE; i++)
+	{
+		for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
+		{
+/*
+ *	Convert hardware address to XX:XX:XX:XX ... form.
+ */
+#ifdef CONFIG_AX25
+#ifdef CONFIG_NETROM
+			if (entry->dev->type == ARPHRD_AX25 || entry->dev->type == ARPHRD_NETROM)
+			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+			else {
+#else
+			if(entry->dev->type==ARPHRD_AX25)
+			     strcpy(hbuffer,ax2asc((ax25_address *)entry->ha));
+			else {
+#endif
+#endif
+
+			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->dev->addr_len;j++)
+			{
+				hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
+				hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
+				hbuffer[k++]=':';
+			}
+			hbuffer[--k]=0;
+	
+#ifdef CONFIG_AX25
+			}
+#endif
+			size = sprintf(buffer+len,
+				"%-17s0x%-10x0x%-10x%s",
+				in_ntoa(entry->ip),
+				(unsigned int)entry->dev->type,
+				entry->flags,
+				hbuffer);
+#if RT_CACHE_DEBUG < 2
+			size += sprintf(buffer+len+size,
+				 "     %-17s %s\n",
+				 entry->mask==DEF_ARP_NETMASK ?
+				 "*" : in_ntoa(entry->mask), entry->dev->name);
+#else
+			size += sprintf(buffer+len+size,
+				 "     %-17s %s\t%d\t%1d\n",
+				 entry->mask==DEF_ARP_NETMASK ?
+				 "*" : in_ntoa(entry->mask), entry->dev->name, 
+				 entry->hh ? entry->hh->hh_refcnt : -1,
+				 entry->hh ? entry->hh->hh_uptodate : 0);
+#endif
+	
+			len += size;
+			pos += size;
+		  
+			if (pos <= offset)
+				len=0;
+			if (pos >= offset+length)
+				goto done;
+		}
+	}
+done:
+	arp_unlock();
+  
+	*start = buffer+len-(pos-offset);	/* Start of wanted data */
+	len = pos-offset;			/* Start slop */
+	if (len>length)
+		len = length;			/* Ending slop */
+	return len;
+}
+
+
 
 /*
  *	Called once on startup.
@@ -2237,5 +2396,8 @@
 		arp_get_info
 	});
 #endif
-}
 
+#ifdef CONFIG_ARPD
+	netlink_attach(NETLINK_ARPD, arpd_callback);
+#endif
+}
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c
--- v1.3.98/linux/net/ipv4/devinet.c	Tue Nov 14 16:03:57 1995
+++ linux/net/ipv4/devinet.c	Tue May  7 15:22:30 1996
@@ -14,6 +14,8 @@
  *	Additional Authors:
  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
  */
+
+#include <linux/config.h>	/* For CONFIG_IP_CLASSLESS */
  
 #include <asm/segment.h>
 #include <asm/system.h>
@@ -76,7 +78,9 @@
 int ip_chk_addr(unsigned long addr)
 {
 	struct device *dev;
+#ifndef CONFIG_IP_CLASSLESS
 	unsigned long mask;
+#endif
 
 	/* 
 	 *	Accept both `all ones' and `all zeros' as BROADCAST. 
@@ -90,6 +94,7 @@
 	    addr == htonl(0x7FFFFFFFL))
 		return IS_BROADCAST;
 
+#ifndef  CONFIG_IP_CLASSLESS
 	mask = ip_get_mask(addr);
 
 	/*
@@ -98,6 +103,10 @@
 	 
 	if ((addr & mask) == htonl(0x7F000000L))
 		return IS_MYADDR;
+#else
+	if ((addr & htonl(0x7F000000L)) == htonl(0x7F000000L))
+		return IS_MYADDR;
+#endif
 
 	/*
 	 *	OK, now check the interface addresses. We could
@@ -139,6 +148,7 @@
 				return IS_BROADCAST;
 		}
 		
+#ifndef CONFIG_IP_CLASSLESS
 		/*
 	 	 *	Nope. Check for Network broadcast. 
 	 	 */
@@ -150,6 +160,7 @@
 			if ((addr & ~mask) == ~mask)
 				return IS_BROADCAST;
 		}
+#endif
 	}
 	if(IN_MULTICAST(ntohl(addr)))
 		return IS_MULTICAST;
@@ -181,36 +192,34 @@
 
 /*
  *	Find an interface that can handle addresses for a certain address. 
- *
- *	This needs optimising, since it's relatively trivial to collapse
- *	the two loops into one.
  */
- 
-struct device * ip_dev_check(unsigned long addr)
+
+struct device * ip_dev_bynet(unsigned long addr, unsigned long mask)
 {
 	struct device *dev;
+	struct device *best_dev = NULL;
+	__u32  best_mask = mask;
 
 	for (dev = dev_base; dev; dev = dev->next) 
 	{
 		if (!(dev->flags & IFF_UP))
 			continue;
-		if (!(dev->flags & IFF_POINTOPOINT))
-			continue;
-		if (addr != dev->pa_dstaddr)
-			continue;
-		return dev;
-	}
-	for (dev = dev_base; dev; dev = dev->next) 
-	{
-		if (!(dev->flags & IFF_UP))
-			continue;
 		if (dev->flags & IFF_POINTOPOINT)
+		{
+			if (addr == dev->pa_dstaddr)
+				return dev;
 			continue;
+		}
 		if (dev->pa_mask & (addr ^ dev->pa_addr))
 			continue;
-		return dev;
+		if (mask == dev->pa_mask)
+			return dev;
+		if (best_dev && (best_mask & dev->pa_mask) != best_mask)
+			continue;
+		best_dev = dev;
+		best_mask = dev->pa_mask;
 	}
-	return NULL;
+	return best_dev;
 }
 
 /*
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c
--- v1.3.98/linux/net/ipv4/igmp.c	Fri Apr 12 15:52:12 1996
+++ linux/net/ipv4/igmp.c	Mon May  6 12:26:17 1996
@@ -215,7 +215,7 @@
 }
 
 /*
- *	Inlined as its only called once.
+ *	Inlined as it's only called once.
  */
 
 static void igmp_start_timer(struct ip_mc_list *im,unsigned char max_resp_time)
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/ip_forward.c linux/net/ipv4/ip_forward.c
--- v1.3.98/linux/net/ipv4/ip_forward.c	Fri Apr 12 15:52:12 1996
+++ linux/net/ipv4/ip_forward.c	Mon May  6 12:26:17 1996
@@ -216,7 +216,7 @@
 #ifndef CONFIG_IP_NO_ICMP_REDIRECT
 		if (dev == dev2 && 
 			!((iph->saddr^dev->pa_addr)&dev->pa_mask) &&
-			/* The daddr!=raddr test isn't obvious - what its doing
+			/* The daddr!=raddr test isn't obvious - what it's doing
 			   is avoiding sending a frame the receiver will not 
 			   believe anyway.. */
 			iph->daddr != raddr/*ANK*/ && !opt->srr)
@@ -247,7 +247,7 @@
 #ifdef CONFIG_IP_MASQUERADE
 		/*
 		 * If this fragment needs masquerading, make it so...
-		 * (Dont masquerade de-masqueraded fragments)
+		 * (Don't masquerade de-masqueraded fragments)
 		 */
 		if (!(is_frag&IPFWD_MASQUERADED) && fw_res==FW_MASQUERADE)
 			ip_fw_masquerade(&skb, dev2);
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v1.3.98/linux/net/ipv4/ip_fragment.c	Sat Apr 27 15:20:10 1996
+++ linux/net/ipv4/ip_fragment.c	Mon May  6 12:26:17 1996
@@ -754,7 +754,7 @@
 			ip_options_fragment(skb);
 
 		/*
-		 *	Added AC : If we are fragmenting a fragment thats not the
+		 *	Added AC : If we are fragmenting a fragment that's not the
 		 *		   last fragment then keep MF on each bit
 		 */
 		if (left > 0 || (is_frag & 1))
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/ip_masq.c linux/net/ipv4/ip_masq.c
--- v1.3.98/linux/net/ipv4/ip_masq.c	Tue Apr 23 13:57:14 1996
+++ linux/net/ipv4/ip_masq.c	Mon May  6 12:26:17 1996
@@ -539,7 +539,7 @@
 
  /*
   *	Check if it's an masqueraded port, look it up,
-  *	and send it on it's way...
+  *	and send it on its way...
   *
   *	Better not have many hosts using the designated portrange
   *	as 'normal' ports, or you'll be spending many time in
@@ -639,7 +639,6 @@
                  */
                 if (iph->protocol==IPPROTO_UDP)
 		{
-			unsigned long 	timeout;
                         recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size);
 			ip_masq_set_expire(ms, 0);
 			ip_masq_set_expire(ms, ip_masq_expire->udp_timeout);
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c
--- v1.3.98/linux/net/ipv4/ip_output.c	Wed Apr 24 17:00:43 1996
+++ linux/net/ipv4/ip_output.c	Tue May  7 12:06:53 1996
@@ -224,7 +224,7 @@
 		 * release route, so that...
 		 */
 		if (rt)
-			ATOMIC_INCR(&rt->rt_refcnt);
+			atomic_inc(&rt->rt_refcnt);
 	}
 	else
 		rt = ip_rt_route(daddr, skb->localroute);
@@ -1059,7 +1059,8 @@
 	nrt->rtmsg_flags=flags;
 	nrt->rtmsg_metric=metric;
 	strcpy(nrt->rtmsg_device,name);
-	netlink_post(NETLINK_ROUTE, skb);
+	if (netlink_post(NETLINK_ROUTE, skb))
+		kfree_skb(skb, FREE_WRITE);
 }	
 
 #endif
@@ -1085,6 +1086,7 @@
 		ip_mc_allhost(dev);
 #endif		
 		ip_netlink_msg(RTMSG_NEWDEVICE, 0,0,0,0,0,dev->name);
+		ip_rt_update(NETDEV_UP, dev);
 	}
 	return NOTIFY_DONE;
 }
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v1.3.98/linux/net/ipv4/ip_sockglue.c	Sun May  5 08:52:06 1996
+++ linux/net/ipv4/ip_sockglue.c	Tue May  7 12:06:53 1996
@@ -181,7 +181,7 @@
 		case IP_TOS:		/* This sets both TOS and Precedence */
 			if (val<0 || val>63)	/* Reject setting of unused bits */
 				return -EINVAL;
-			if ((val&3) > 4 && !suser())	/* Only root can set Prec>4 */
+			if ((val&7) > 4 && !suser())	/* Only root can set Prec>4 */
 				return -EPERM;
 			sk->ip_tos=val;
 			switch (val & 0x38) {
@@ -297,7 +297,7 @@
 				{
 					dev=rt->rt_dev;
 					route_src = rt->rt_src;
-					ATOMIC_DECR(&rt->rt_use);
+					atomic_dec(&rt->rt_use);
 					ip_rt_put(rt);
 				}
 			}
@@ -350,7 +350,7 @@
 				if((rt=ip_rt_route(mreq.imr_multiaddr.s_addr,0))!=NULL)
 			        {
 					dev=rt->rt_dev;
-					ATOMIC_DECR(&rt->rt_use);
+					atomic_dec(&rt->rt_use);
 					route_src = rt->rt_src;
 					ip_rt_put(rt);
 				}
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v1.3.98/linux/net/ipv4/ipip.c	Tue Apr 23 13:57:14 1996
+++ linux/net/ipv4/ipip.c	Tue May  7 12:06:53 1996
@@ -74,39 +74,15 @@
 	skb->h.iph=(struct iphdr *)skb->data;
 	skb->ip_hdr=(struct iphdr *)skb->data;
 	memset(skb->proto_priv, 0, sizeof(struct options));
-	if (skb->ip_hdr->ihl > 5) 
-	{
-		if (ip_options_compile(NULL, skb))
-			return 0;
-	}
-	
-#ifdef CONFIG_FIREWALL
-	/*
-	 *	Check the firewall [well spotted Olaf]
-	 */
-	 
-	if((err=call_in_firewall(PF_INET, skb->dev, skb->ip_hdr))<FW_ACCEPT)
-	{
-		if(err==FW_REJECT)
-			icmp_send(skb,ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0 , dev);
-		kfree_skb(skb, FREE_READ);
-		return 0;
-	}	
-#endif
 
 	/*
 	 *	If you want to add LZ compressed IP or things like that here,
 	 *	and in drivers/net/tunnel.c are the places to add.
 	 */
 	
-	/* skb=lzw_uncompress(skb); */
-	
-	/*
-	 *	Feed to IP forward.
-	 */
-	 
-	if(ip_forward(skb, dev, 0, daddr))
-		kfree_skb(skb, FREE_READ);
+	skb->protocol = htons(ETH_P_IP);
+	skb->ip_summed = 0;
+	netif_rx(skb);
 	MOD_DEC_USE_COUNT;
 	return(0);
 }
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c
--- v1.3.98/linux/net/ipv4/ipmr.c	Fri Apr 12 15:52:12 1996
+++ linux/net/ipv4/ipmr.c	Mon May  6 12:26:17 1996
@@ -397,7 +397,7 @@
 		return 0;
 	}
 	/*
-	 *	Unsolicited update - thats ok add anyway.
+	 *	Unsolicited update - that's ok, add anyway.
 	 */
 	 
 	
@@ -420,7 +420,7 @@
 /*
  *	Socket options and virtual interface manipulation. The whole
  *	virtual interface system is a complete heap, but unfortunately
- *	thats how BSD mrouted happens to think. Maybe one day with a proper
+ *	that's how BSD mrouted happens to think. Maybe one day with a proper
  *	MOSPF/PIM router set up we can clean this up.
  */
  
@@ -487,7 +487,7 @@
 				{
 					if(dev->flags&IFF_MULTICAST)
 					{
-						/* Most ethernet cards dont know
+						/* Most ethernet cards don't know
 						   how to do this yet.. */
 						dev->flags|=IFF_ALLMULTI;
 						dev_mc_upload(dev);
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v1.3.98/linux/net/ipv4/packet.c	Fri Apr 12 15:52:12 1996
+++ linux/net/ipv4/packet.c	Mon May  6 12:26:17 1996
@@ -5,7 +5,7 @@
  *
  *		PACKET - implements raw packet sockets.
  *
- *		Doesn't belong in IP but its currently too hooked into ip
+ *		Doesn't belong in IP but it's currently too hooked into ip
  *		to separate.
  *
  * Version:	@(#)packet.c	1.0.6	05/25/93
@@ -176,7 +176,7 @@
 
 	/*
 	 *	If the write buffer is full, then tough. At this level the user gets to
-	 *	deal with the problem - do your own algorithmic backoffs. Thats far
+	 *	deal with the problem - do your own algorithmic backoffs. That's far
 	 *	more flexible.
 	 */
 	 
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v1.3.98/linux/net/ipv4/proc.c	Mon Mar 25 08:58:25 1996
+++ linux/net/ipv4/proc.c	Tue May  7 12:06:53 1996
@@ -68,11 +68,15 @@
 	unsigned short destp, srcp;
 	int len=0;
 	off_t pos=0;
-	off_t begin=0;
+	off_t begin;
+	char tmpbuf[129];
   
 	s_array = pro->sock_array;
-	len += sprintf(buffer, "sl  local_address rem_address   st tx_queue "
-	    "rx_queue tr tm->when uid inode\n");
+	if (offset < 128) 
+		len += sprintf(buffer, "%-127s\n",
+			       "  sl  local_address rem_address   st tx_queue "
+			       "rx_queue tr tm->when retrnsmt   uid  timeout inode");
+	pos = 128;
 /*
  *	This was very pretty but didn't work when a socket is destroyed
  *	at the wrong moment (eg a syn recv socket getting a reset), or
@@ -85,6 +89,12 @@
 		sp = s_array[i];
 		while(sp != NULL) 
 		{
+			pos += 128;
+			if (pos < offset)
+			{
+				sp = sp->next;
+				continue;
+			}
 			dest  = sp->daddr;
 			src   = sp->saddr;
 			destp = sp->dummy_th.dest;
@@ -109,8 +119,8 @@
 			    timer_active=timer_active2;
 			    timer_expires=sp->timer.expires;
 			}
-			len += sprintf(buffer+len, "%2d: %08lX:%04X %08lX:%04X"
-			    " %02X %08X:%08X %02X:%08lX %08X %d %d %ld\n",
+			sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+				" %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
 				i, src, srcp, dest, destp, sp->state, 
 				format==0?sp->write_seq-sp->rcv_ack_seq:sp->wmem_alloc, 
 				format==0?sp->acked_seq-sp->copied_seq:sp->rmem_alloc,
@@ -121,32 +131,28 @@
 				SOCK_INODE(sp->socket)->i_ino : 0);
 			if (timer_active1) add_timer(&sp->retransmit_timer);
 			if (timer_active2) add_timer(&sp->timer);
+			len += sprintf(buffer+len, "%-127s\n", tmpbuf);
 			/*
 			 * All sockets with (port mod SOCK_ARRAY_SIZE) = i
 			 * are kept in sock_array[i], so we must follow the
 			 * 'next' link to get them all.
 			 */
-			sp = sp->next;
-			pos=begin+len;
-			if(pos<offset)
-			{
-				len=0;
-				begin=pos;
-			}
-			if(pos>offset+length)
+			if(len >= length)
 				break;
+			sp = sp->next;
 		}
 		sti();	/* We only turn interrupts back on for a moment,
 			   but because the interrupt queues anything built
 			   up before this will clear before we jump back
 			   and cli(), so it's not as bad as it looks */
-		if(pos>offset+length)
+		if(len>= length)
 			break;
 	}
-	*start=buffer+(offset-begin);
-	len-=(offset-begin);
+	begin = len - (pos - offset);
+	*start = buffer + begin;
+	len -= begin;
 	if(len>length)
-	  	len=length;
+		len = length;
 	return len;
 } 
 
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v1.3.98/linux/net/ipv4/raw.c	Fri Apr 19 10:08:03 1996
+++ linux/net/ipv4/raw.c	Mon May  6 12:26:17 1996
@@ -198,7 +198,7 @@
 		if(!iph->saddr)
 			iph->saddr=saddr;
 		iph->check=0;
-		iph->tot_len=htons(fraglen);	/* This is right as you cant frag
+		iph->tot_len=htons(fraglen);	/* This is right as you can't frag
 					   RAW packets */
 		/*
 	 	 *	Deliberate breach of modularity to keep 
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v1.3.98/linux/net/ipv4/route.c	Fri Apr 12 15:52:12 1996
+++ linux/net/ipv4/route.c	Tue May  7 12:06:54 1996
@@ -321,42 +321,6 @@
 }
 
 /*
- *	Used by 'rt_add()' when we can't get the netmask any other way..
- *
- *	If the lower byte or two are zero, we guess the mask based on the
- *	number of zero 8-bit net numbers, otherwise we use the "default"
- *	masks judging by the destination address and our device netmask.
- */
- 
-static __u32 unsigned long default_mask(__u32 dst)
-{
-	dst = ntohl(dst);
-	if (IN_CLASSA(dst))
-		return htonl(IN_CLASSA_NET);
-	if (IN_CLASSB(dst))
-		return htonl(IN_CLASSB_NET);
-	return htonl(IN_CLASSC_NET);
-}
-
-
-/*
- *	If no mask is specified then generate a default entry.
- */
-
-static __u32 guess_mask(__u32 dst, struct device * dev)
-{
-	__u32 mask;
-
-	if (!dst)
-		return 0;
-	mask = default_mask(dst);
-	if ((dst ^ dev->pa_addr) & mask)
-		return mask;
-	return dev->pa_mask;
-}
-
-
-/*
  *	Check if a mask is acceptable.
  */
  
@@ -527,50 +491,6 @@
 	struct fib_info * fi;
 	int logmask;
 
-	if (flags & RTF_HOST) 
-		mask = 0xffffffff;
-	/*
-	 * If mask is not specified, try to guess it.
-	 */
-	else if (!mask)
-	{
-		if (!((dst ^ dev->pa_addr) & dev->pa_mask)) 
-		{
-			mask = dev->pa_mask;
-			flags &= ~RTF_GATEWAY;
-			if (flags & RTF_DYNAMIC) 
-			{
-				printk("Dynamic route to my own net rejected\n");
-				return;
-			}
-		} 
-		else
-			mask = guess_mask(dst, dev);
-		dst &= mask;
-	}
-	
-	/*
-	 *	A gateway must be reachable and not a local address
-	 */
-	 
-	if (gw == dev->pa_addr)
-		flags &= ~RTF_GATEWAY;
-		
-	if (flags & RTF_GATEWAY) 
-	{
-		/*
-		 *	Don't try to add a gateway we can't reach.. 
-		 *	Tunnel devices are exempt from this rule.
-		 */
-		 
-		if ((dev != get_gw_dev(gw)) && dev->type!=ARPHRD_TUNNEL)
-			return;
-			
-		flags |= RTF_GATEWAY;
-	} 
-	else
-		gw = 0;
-		
 	/*
 	 *	Allocate an entry and fill it in.
 	 */
@@ -632,7 +552,7 @@
 	if (fz->fz_nent >= RTZ_HASHING_LIMIT && !fz->fz_hash_table && logmask<32)
 	{
 		struct fib_node ** ht;
-#if RT_CACHE_DEBUG
+#if RT_CACHE_DEBUG >= 2
 		printk("fib_add_1: hashing for zone %d started\n", logmask);
 #endif
 		ht = kmalloc(RTZ_HASH_DIVISOR*sizeof(struct rtable*), GFP_KERNEL);
@@ -747,7 +667,12 @@
 	struct fib_node *f;
 
 	while ((f = *fp) != NULL) {
-		if (f->fib_info->fib_dev != dev) {
+/*
+ *	"Magic" device route is allowed to point to loopback,
+ *	discard it too.
+ */
+		if (f->fib_info->fib_dev != dev &&
+		    (dev != &loopback_dev || f->fib_dst != dev->pa_addr)) {
 			fp = &f->fib_next;
 			continue;
 		}
@@ -797,7 +722,7 @@
  *
  *	We preserve the old format but pad the buffers out. This means that
  *	we can spin over the other entries as we read them. Remember the
- *	gated BGP4 code could need to read 60,000+ routes on occasion (thats
+ *	gated BGP4 code could need to read 60,000+ routes on occasion (that's
  *	about 7Mb of data). To do that ok we will need to also cache the
  *	last route we got to (reads will generally be following on from
  *	one another without gaps).
@@ -961,12 +886,9 @@
 	{
 		struct hh_cache * hh = rt->rt_hh;
 		rt->rt_hh = NULL;
-		if (hh && !--hh->hh_refcnt)
-		{
-			restore_flags(flags);
-			kfree_s(hh, sizeof(struct hh_cache));
-		}
 		restore_flags(flags);
+		if (hh && atomic_dec_and_test(&hh->hh_refcnt))
+			kfree_s(hh, sizeof(struct hh_cache));
 		kfree_s(rt, sizeof(struct rt_table));
 		return;
 	}
@@ -1000,12 +922,9 @@
 #endif
 			*rtp = rt->rt_next;
 			rt->rt_hh = NULL;
-			if (hh && !--hh->hh_refcnt)
-			{
-				sti();
-				kfree_s(hh, sizeof(struct hh_cache));
-			}
 			sti();
+			if (hh && atomic_dec_and_test(&hh->hh_refcnt))
+				kfree_s(hh, sizeof(struct hh_cache));
 			kfree_s(rt, sizeof(struct rt_table));
 #if RT_CACHE_DEBUG >= 2
 			printk("rt_kick_free_queue: %08x is free\n", daddr);
@@ -1017,7 +936,8 @@
 	}
 }
 
-void ip_rt_run_bh() {
+void ip_rt_run_bh()
+{
 	unsigned long flags;
 	save_flags(flags);
 	cli();
@@ -1409,7 +1329,7 @@
 			else
 			{
 				if (rtg->rt_hh)
-					ATOMIC_INCR(&rtg->rt_hh->hh_refcnt);
+					atomic_inc(&rtg->rt_hh->hh_refcnt);
 				rth->rt_hh = rtg->rt_hh;
 				ip_rt_put(rtg);
 			}
@@ -1570,7 +1490,7 @@
 	{
 		rt_free(rth);
 #if RT_CACHE_DEBUG >= 1
-		printk("rt_cache: route to %08x was born dead\n", daddr);
+		printk(KERN_DEBUG "rt_cache: route to %08x was born dead\n", daddr);
 #endif
 	}
 
@@ -1581,7 +1501,7 @@
 void ip_rt_put(struct rtable * rt)
 {
 	if (rt)
-		ATOMIC_DECR(&rt->rt_refcnt);
+		atomic_dec(&rt->rt_refcnt);
 }
 
 struct rtable * ip_rt_route(__u32 daddr, int local)
@@ -1595,8 +1515,8 @@
 		if (rth->rt_dst == daddr)
 		{
 			rth->rt_lastuse = jiffies;
-			ATOMIC_INCR(&rth->rt_use);
-			ATOMIC_INCR(&rth->rt_refcnt);
+			atomic_inc(&rth->rt_use);
+			atomic_inc(&rth->rt_refcnt);
 			ip_rt_unlock();
 			return rth;
 		}
@@ -1671,42 +1591,49 @@
 		}
 	}
 
-	/*
-	 *	Ignore faulty masks
-	 */
-	 
-	if (bad_mask(mask, daddr))
-		mask=0;
-
-	/*
-	 *	Set the mask to nothing for host routes.
-	 */
-	 
-	if (flags & RTF_HOST)
+	if (flags & RTF_HOST) 
 		mask = 0xffffffff;
 	else if (mask && r->rt_genmask.sa_family != AF_INET)
 		return -EAFNOSUPPORT;
 
-	/*
-	 *	You can only gateway IP via IP..
-	 */
-	 
 	if (flags & RTF_GATEWAY) 
 	{
 		if (r->rt_gateway.sa_family != AF_INET)
 			return -EAFNOSUPPORT;
+
+		/*
+		 *	Don't try to add a gateway we can't reach.. 
+		 *	Tunnel devices are exempt from this rule.
+		 */
+
 		if (!dev)
 			dev = get_gw_dev(gw);
+		else if (dev != get_gw_dev(gw) && dev->type != ARPHRD_TUNNEL)
+			return -EINVAL;
+		if (!dev)
+			return -ENETUNREACH;
 	} 
-	else if (!dev)
-		dev = ip_dev_check(daddr);
+	else
+	{
+		gw = 0;
+		if (!dev)
+			dev = ip_dev_bynet(daddr, mask);
+		if (!dev)
+			return -ENETUNREACH;
+		if (!mask)
+		{
+			if (((daddr ^ dev->pa_addr) & dev->pa_mask) == 0)
+				mask = dev->pa_mask;
+		}
+	}
 
-	/*
-	 *	Unknown device.
-	 */
-	 
-	if (dev == NULL)
-		return -ENETUNREACH;
+#ifndef CONFIG_IP_CLASSLESS
+	if (!mask)
+		mask = ip_get_mask(daddr);
+#endif
+	
+	if (bad_mask(mask, daddr))
+		return -EINVAL;
 
 	/*
 	 *	Add the route
@@ -1783,3 +1710,10 @@
 	return;
 }
 
+void ip_rt_update(int event, struct device *dev)
+{
+	if (event == NETDEV_UP)
+		rt_add(RTF_HOST|RTF_UP, dev->pa_addr, ~0, 0, dev, 0, 0, 0, 0);
+	else if (event == NETDEV_DOWN)
+		rt_del(dev->pa_addr, ~0, dev, 0, RTF_HOST|RTF_UP, 0);
+}
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v1.3.98/linux/net/ipv4/tcp.c	Sat Apr 27 15:20:10 1996
+++ linux/net/ipv4/tcp.c	Mon May  6 12:26:17 1996
@@ -172,7 +172,7 @@
  *					ack if stat is TCP_CLOSED.
  *		Alan Cox	:	Look up device on a retransmit - routes may
  *					change. Doesn't yet cope with MSS shrink right
- *					but its a start!
+ *					but it's a start!
  *		Marc Tamsky	:	Closing in closing fixes.
  *		Mike Shaver	:	RFC1122 verifications.
  *		Alan Cox	:	rcv_saddr errors.
@@ -1962,7 +1962,7 @@
 	/*
 	 *	SKIP devices set their MTU to 65535. This is so they can take packets
 	 *	unfragmented to security process then fragment. They could lie to the
-	 *	TCP layer about a suitable MTU, but its easier to let skip sort it out
+	 *	TCP layer about a suitable MTU, but it's easier to let skip sort it out
 	 *	simply because the final package we want unfragmented is going to be
 	 *
 	 *	[IPHDR][IPSP][Security data][Modified TCP data][Security data]
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v1.3.98/linux/net/ipv4/tcp_input.c	Fri Apr 19 10:08:03 1996
+++ linux/net/ipv4/tcp_input.c	Mon May  6 12:26:17 1996
@@ -27,7 +27,7 @@
 #include <net/tcp.h>
 
 /*
- *	Policy code extracted so its now separate
+ *	Policy code extracted so it's now separate
  */
 
 /*
@@ -344,7 +344,7 @@
 	 *	flurry of syns from eating up all our memory.
 	 *
 	 *	BSD does some funnies here and allows 3/2 times the
-	 *	set backlog as a fudge factor. Thats just too gross.
+	 *	set backlog as a fudge factor. That's just too gross.
 	 */
 
 	if (sk->ack_backlog >= sk->max_ack_backlog) 
@@ -502,7 +502,7 @@
 	/*
 	 *	SKIP devices set their MTU to 65535. This is so they can take packets
 	 *	unfragmented to security process then fragment. They could lie to the
-	 *	TCP layer about a suitable MTU, but its easier to let skip sort it out
+	 *	TCP layer about a suitable MTU, but it's easier to let skip sort it out
 	 *	simply because the final package we want unfragmented is going to be
 	 *
 	 *	[IPHDR][IPSP][Security data][Modified TCP data][Security data]
@@ -607,7 +607,7 @@
 	 */
 
 	if(sk->zapped)
-		return(1);	/* Dead, cant ack any more so why bother */
+		return(1);	/* Dead, can't ack any more so why bother */
 
 	/*
 	 *	We have dropped back to keepalive timeouts. Thus we have
@@ -1736,7 +1736,7 @@
 	
 		/* 
 		 *	Retransmitted SYN for our socket. This is uninteresting. If sk->state==TCP_LISTEN
-		 *	then its a new connection
+		 *	then it's a new connection
 		 */
 		 
 		if (sk->state == TCP_SYN_RECV && th->syn && skb->seq+1 == sk->acked_seq)
@@ -1759,7 +1759,7 @@
 				/* We got an ack, but it's not a good ack */
 				if(!tcp_ack(sk,th,skb->ack_seq,len))
 				{
-					/* Reset the ack - its an ack from a 
+					/* Reset the ack - it's an ack from a 
 					   different connection  [ th->rst is checked in tcp_send_reset()] */
 					tcp_statistics.TcpAttemptFails++;
 					tcp_send_reset(daddr, saddr, th,
diff -u --recursive --new-file v1.3.98/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v1.3.98/linux/net/ipv4/tcp_output.c	Sun Apr 21 12:39:03 1996
+++ linux/net/ipv4/tcp_output.c	Mon May  6 16:25:53 1996
@@ -26,6 +26,68 @@
 #include <linux/interrupt.h>
 
 /*
+ * RFC 1122 says:
+ *
+ * "the suggested [SWS] avoidance algorithm for the receiver is to keep
+ *  RECV.NEXT + RCV.WIN fixed until:
+ *  RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
+ *
+ * Experiments against BSD and Solaris machines show that following
+ * these rules results in the BSD and Solaris machines making very
+ * bad guesses about how much data they can have in flight.
+ *
+ * Instead we follow the BSD lead and offer a window that gives
+ * the size of the current free space, truncated to a multiple
+ * of 1024 bytes. If the window is smaller than
+ * 	min(sk->mss, MAX_WINDOW/2)
+ * then we advertise the window as having size 0, unless this
+ * would shrink the window we offered last time.
+ * This results in as much as double the throughput as the original
+ * implementation.
+ *
+ * We do BSD style SWS avoidance -- note that RFC1122 only says we
+ * must do silly window avoidance, it does not require that we use
+ * the suggested algorithm.
+ *
+ * The "rcvbuf" and "rmem_alloc" values are shifted by 1, because
+ * they also contain buffer handling overhead etc, so the window
+ * we actually use is essentially based on only half those values.
+ */
+int tcp_new_window(struct sock * sk)
+{
+	unsigned long window;
+	unsigned long minwin, maxwin;
+
+	/* Get minimum and maximum window values.. */
+	minwin = sk->mss;
+	if (!minwin)
+		minwin = sk->mtu;
+	maxwin = sk->window_clamp;
+	if (!maxwin)
+		maxwin = MAX_WINDOW;
+	if (minwin > maxwin/2)
+		minwin = maxwin/2;
+
+	/* Get current rcvbuf size.. */
+	window = sk->rcvbuf/2;
+	if (window < minwin) {
+		sk->rcvbuf = minwin*2;
+		window = minwin;
+	}
+
+	/* Check rcvbuf against used and minimum window */
+	window -= sk->rmem_alloc/2;
+	if ((long)(window - minwin) < 0)		/* SWS avoidance */
+		window = 0;
+
+	if (window > 1023)
+		window &= ~1023;
+	if (window > maxwin)
+		window = maxwin;
+	return window;
+}
+
+/*
  *	Get rid of any delayed acks, we sent one already..
  */
 static __inline__ void clear_delayed_acks(struct sock * sk)
diff -u --recursive --new-file v1.3.98/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v1.3.98/linux/net/ipx/af_ipx.c	Fri Apr 12 15:52:13 1996
+++ linux/net/ipx/af_ipx.c	Tue May  7 12:06:54 1996
@@ -44,6 +44,14 @@
  *	Revision 0.34:	Module support. <Jim Freeman>
  *	Revision 0.35:  Checksum support. <Neil Turton>, hooked in by <Alan Cox>
  *
+ *	Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
+ *	pair. Also, now usage count is managed this way
+ *	-Count one if the auto_interface mode is on
+ *      -Count one per configured interface
+ *
+ *	Jacques Gelinas (jacques@solucorp.qc.ca)
+ *
+ *
  * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
  *	Neither Greg Page nor Caldera, Inc. admit liability nor provide 
  *	warranty for any of this software. This material is provided 
@@ -105,7 +113,14 @@
 static int
 ipxcfg_set_auto_create(char val)
 {
-	ipxcfg_auto_create_interfaces = val;
+	if (ipxcfg_auto_create_interfaces != val){
+		if (val){
+			MOD_INC_USE_COUNT;
+		}else{
+			MOD_DEC_USE_COUNT;
+		}
+		ipxcfg_auto_create_interfaces = val;
+	}
 	return 0;
 }
 		
@@ -336,6 +351,7 @@
 	/* sockets still dangling
 	 * - must be closed from user space
 	 */
+	MOD_DEC_USE_COUNT;
 	return;
 }
 
@@ -803,6 +819,7 @@
 
 	if (ipxcfg_auto_select_primary && (ipx_primary_net == NULL))
 		ipx_primary_net = intrfc;
+	MOD_INC_USE_COUNT;
 	return;
 }
 
@@ -1015,7 +1032,7 @@
 }
 
 static int 
-ipxitf_ioctl(unsigned int cmd, void *arg)
+ipxitf_ioctl_real(unsigned int cmd, void *arg)
 {
 	int err;
 	switch(cmd)
@@ -1080,6 +1097,15 @@
 	}
 }
 
+static int 
+ipxitf_ioctl(unsigned int cmd, void *arg)
+{
+	int ret;
+	MOD_INC_USE_COUNT;
+	ret = ipxitf_ioctl_real (cmd,arg);
+	MOD_DEC_USE_COUNT;
+	return ret;
+}
 /*******************************************************************************************************************\
 *													            *
 *	            			Routing tables for the IPX socket layer				            *
@@ -1312,7 +1338,7 @@
 	 *	Apply checksum. Not allowed on 802.3 links.
 	 */
 	 
-	if(sk->no_check || intrfc->if_dlink_type!=IPX_FRAME_8023)
+	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));
diff -u --recursive --new-file v1.3.98/linux/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- v1.3.98/linux/net/netrom/af_netrom.c	Fri Apr 19 10:08:03 1996
+++ linux/net/netrom/af_netrom.c	Mon May  6 12:26:17 1996
@@ -235,7 +235,7 @@
  *	Once it is removed from the queue no interrupt or bottom half will
  *	touch it and we are (fairly 8-) ) safe.
  */
-void nr_destroy_socket(struct sock *sk)	/* Not static as its used by the timer */
+void nr_destroy_socket(struct sock *sk)	/* Not static as it's used by the timer */
 {
 	struct sk_buff *skb;
 	unsigned long flags;
@@ -954,7 +954,7 @@
 	unsigned short frametype, window, timeout;
 	
 
-	skb->sk = NULL;		/* Initially we don't know who its for */
+	skb->sk = NULL;		/* Initially we don't know who it's for */
 
 	/*
 	 *	skb->data points to the netrom frame start
@@ -980,7 +980,7 @@
 #endif
 
 	/*
-	 * Find an existing socket connection, based on circuit ID, if its
+	 * Find an existing socket connection, based on circuit ID, if it's
 	 * a Connect Request base it on their circuit ID.
 	 */
 	if (((frametype & 0x0F) != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) ||
diff -u --recursive --new-file v1.3.98/linux/net/socket.c linux/net/socket.c
--- v1.3.98/linux/net/socket.c	Sat Apr 27 15:20:10 1996
+++ linux/net/socket.c	Mon May  6 12:26:18 1996
@@ -395,7 +395,7 @@
 
 /*
  *	With an ioctl arg may well be a user mode pointer, but we don't know what to do
- *	with it - thats up to the protocol still.
+ *	with it - that's up to the protocol still.
  */
 
 int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
diff -u --recursive --new-file v1.3.98/linux/net/unix/af_unix.c linux/net/unix/af_unix.c
--- v1.3.98/linux/net/unix/af_unix.c	Fri Apr 12 15:52:13 1996
+++ linux/net/unix/af_unix.c	Tue May  7 12:06:54 1996
@@ -27,6 +27,7 @@
  *		Marty Leisner	:	Fixes to fd passing
  *		Nick Nevin	:	recvmsg bugfix.
  *		Alan Cox	:	Started proper garbage collector
+ *		Heiko EiBfeldt	:	Missing verify_area check
  *
  * Known differences from reference BSD that was tested:
  *
@@ -352,7 +353,7 @@
 	if(skpair!=NULL)
 		skpair->protinfo.af_unix.locks--;	/* It may now die */
 	sk->protinfo.af_unix.other=NULL;		/* No pair */
-	unix_destroy_socket(sk);			/* Try and flush out this socket. Throw our buffers at least */
+	unix_destroy_socket(sk);			/* Try to flush out this socket. Throw out buffers at least */
 	
 	/*
 	 *	FIXME: BSD difference: In BSD all sockets connected to use get ECONNRESET and we die on the spot. In
@@ -1222,6 +1223,8 @@
 			if((skb=skb_peek(&sk->receive_queue))!=NULL)
 				amount=skb->len;
 			err=verify_area(VERIFY_WRITE,(void *)arg,sizeof(unsigned long));
+			if(err)
+				return err;
 			put_fs_long(amount,(unsigned long *)arg);
 			return 0;
 		}
diff -u --recursive --new-file v1.3.98/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v1.3.98/linux/scripts/Menuconfig	Sun May  5 08:52:07 1996
+++ linux/scripts/Menuconfig	Mon May  6 12:26:18 1996
@@ -1110,7 +1110,7 @@
 #
 # Check kernel version of previous menuconfig build.
 # If it's different then we should tell the sound driver
-# to rebuild it's Config.in file.
+# to rebuild its Config.in file.
 #
 rebuildsound=TRUE
 if [ -e .menuconfig ]
diff -u --recursive --new-file v1.3.98/linux/scripts/tkcond.c linux/scripts/tkcond.c
--- v1.3.98/linux/scripts/tkcond.c	Fri Apr 12 15:52:13 1996
+++ linux/scripts/tkcond.c	Mon May  6 12:26:18 1996
@@ -449,7 +449,7 @@
 	{
 	  /*
 	   * Now search the condition list for a known configuration variable
-	   * that has conditions of it's own.
+	   * that has conditions of its own.
 	   */
 	  if(cnd->op != op_kvariable) continue;
 	  if(cnd->variable.cfg->cond == NULL) continue;
diff -u --recursive --new-file v1.3.98/linux/scripts/tkparse.c linux/scripts/tkparse.c
--- v1.3.98/linux/scripts/tkparse.c	Wed Apr 10 17:02:29 1996
+++ linux/scripts/tkparse.c	Mon May  6 12:26:18 1996
@@ -662,7 +662,7 @@
    * Input file is now parsed.  Next we need to go through and attach
    * the correct conditions to each of the actual menu items and kill
    * the if/else/endif tokens from the list.  We also flag the menu items
-   * that have other things that depend upon it's setting.
+   * that have other things that depend upon its setting.
    */
   fix_conditionals(config);
 
