diff -u --recursive --new-file v1.1.11/linux/CREDITS linux/CREDITS
--- v1.1.11/linux/CREDITS	Sat May  7 14:54:01 1994
+++ linux/CREDITS	Fri May  6 08:50:12 1994
@@ -34,7 +34,7 @@
 S: Germany
 
 N: Donald Becker
-E: becker@super.org
+E: becker@cesdis.gsfc.nasa.gov
 D: General low-level networking hacker
 D: Most of the ethercard drivers
 D: Original author of the NFS server
@@ -107,6 +107,7 @@
 S: USA
 
 N: Alan Cox
+E: A.Cox@swansea.ac.uk
 E: iiitac@pyr.swan.ac.uk
 E: gw4pts@gw4pts.ampr.org
 E: GW4PTS@GB7SWN (packet radio)
@@ -418,6 +419,14 @@
 S: FIN-00330 Helsingfors
 S: Finland
 
+N: David C. Niemi
+E: David.Niemi@oasis.gtegsc.com
+D: FSSTND, The XFree86 Project
+D: DMA memory support and future floppy driver
+S: 2364 Old Trail Drive
+S: Reston, VA 22091
+S: USA
+
 N: Kai Petzke
 E: wpp@marie.physik.tu-berlin.de
 D: Driver for Laser Magnetic Storage CD-ROM
@@ -450,6 +459,13 @@
 N: Robert Sanders
 E: gt8134b@prism.gatech.edu
 D: Dosemu
+
+N: Hannu Savolainen
+E: hannu@voxware.pp.fi
+D: Kernel sound drivers
+S: Pallaksentie 4 A 2
+S: 00970 Helsinki
+S: Finland
 
 N: Peter De Schrijver
 E: stud11@cc4.kuleuven.ac.be
diff -u --recursive --new-file v1.1.11/linux/Makefile linux/Makefile
--- v1.1.11/linux/Makefile	Sat May  7 14:54:01 1994
+++ linux/Makefile	Fri May  6 08:30:11 1994
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 11
+SUBLEVEL = 12
 
 all:	Version zImage
 
diff -u --recursive --new-file v1.1.11/linux/config.in linux/config.in
--- v1.1.11/linux/config.in	Tue Apr 19 10:53:40 1994
+++ linux/config.in	Sat May  7 14:10:07 1994
@@ -13,6 +13,19 @@
 bool 'System V IPC' CONFIG_SYSVIPC y
 bool 'Use -m486 flag for 486-specific optimizations' CONFIG_M486 y
 
+if [ "$CONFIG_INET" = "y" ]; then
+
+comment 'Networking options'
+comment '(it is safe to leave these untouched)'
+
+comment 'IP (required for now) y'
+bool 'Reverse ARP' CONFIG_INET_RARP y
+bool 'Assume subnets are local' CONFIG_INET_SNARL y
+bool 'Disable NAGLE algorithm (normally enabled)' CONFIG_TCP_NAGLE_OFF n
+#bool 'Novell IPX protocol' CONFIG_IPX n
+#bool 'Amateur Radio AX.25 Level 2' CONFIG_AX25 n
+fi
+
 comment 'Program binary formats'
 
 bool 'Elf executables' CONFIG_BINFMT_ELF y
diff -u --recursive --new-file v1.1.11/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v1.1.11/linux/drivers/net/slip.c	Tue Apr 19 22:20:34 1994
+++ linux/drivers/net/slip.c	Sat May  7 14:13:00 1994
@@ -21,6 +21,7 @@
  *		Michael Riepe	:	Automatic CSLIP recognition added
  *		Charles Hedrick :	CSLIP header length problem fix.
  *		Alan Cox	:	Corrected non-IP cases of the above.
+ *		Alan Cox	:	Now uses hardware type as per FvK.
  *
  *
  *	FIXME:	This driver still makes some IP'ish assumptions. It should build cleanly KISS TNC only without
@@ -56,6 +57,7 @@
 #include "tcp.h"
 #endif
 #include <linux/skbuff.h>
+#include <linux/if_arp.h>
 #include "sock.h"
 #include "slip.h"
 #ifdef CONFIG_INET
@@ -169,6 +171,7 @@
   dev->rmem_start	= (unsigned long) NULL;
   dev->mem_end		= (unsigned long) NULL;
   dev->mem_start	= (unsigned long) NULL;
+  dev->type		= ARPHRD_SLIP + sl->mode;
 }
 
 
@@ -1072,15 +1075,14 @@
 		{
 			sl->dev->addr_len=7;	/* sizeof an AX.25 addr */
 			sl->dev->hard_header_len=17;	/* We don't do digipeaters */
-			sl->dev->type=3;		/* AF_AX25 not an AF_INET device */
 		}
 		else
 		{
 			sl->dev->addr_len=0;	/* No mac addr in slip mode */
 			sl->dev->hard_header_len=0;
-			sl->dev->type=0;
 		}
 #endif		
+		sl->dev->type=ARPHRD_SLIP+sl->mode;
 		return(0);
 	case SIOCSIFHWADDR:
 #ifdef CONFIG_AX25	
diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/README.st linux/drivers/scsi/README.st
--- v1.1.11/linux/drivers/scsi/README.st
+++ linux/drivers/scsi/README.st	Fri May  6 08:28:58 1994
@@ -0,0 +1,141 @@
+This file contains brief information about the SCSI tape driver.
+Last modified: Thu May  5 00:13:24 1994 by root@kai.home
+
+BASICS
+
+The driver is generic. The state of a drive is not modified when the
+driver is initalized or a device is opened. The mode parameters of the
+drive can be modified with ioctls. The driver supports fixed and
+variable block size (within buffer limits). Both the auto-rewind
+(minor equals device number) and non-rewind devices (minor is 128 +
+device number) are implemented.
+
+
+BUFFERING
+
+The driver uses a buffer allocated at system initialization. The size
+of the buffer is selectable at compile and/or boot time. The buffer is
+used to store the data being transferred to/from the SCSI adapter. The
+following buffering options are selectable at compile time and/or at run
+time (via ioctl):
+
+Buffering of data to be written across write calls for fixed block
+mode (define ST_BUFFER_WRITES). This should be disabled if reliable
+detection of end of media (EOM) for fixed block mode is desired.
+
+Asynchronous writing. Writing the buffer contents to the tape is
+started and the write call returns immediately. The status is checked
+at the next tape operation. Should not used if reliable EOM detection
+is desired.
+
+Read ahead for fixed block mode (ST_READ_AHEAD). Filling the buffer is
+attempted even if the user does not want to get all of the data at
+this read command. Should be disabled for those drives that don't like
+a filemark to truncate a read request or that don't like backspacing.
+
+The buffer size is defined (in 1024 byte units) by ST_BUFFER_BLOCKS or
+at boot time. The maximum number of buffers allocated is defined by
+ST_MAX_BUFFERS. One buffer is allocated for each detected drive up to
+the maximum. The threshold for triggering asynchronous write is
+defined by ST_WRITE_THRESHOLD.
+
+
+BOOT TIME CONFIGURATION
+
+The buffer size, write threshold, and the maximum number of allocated buffers
+are configurable at boot time using, e.g., the LILO command line. The option
+syntax is the following:
+
+           st=aa[,bb[,cc]]
+
+where
+  aa is the buffer size in 1024 byte units
+  bb is the write threshold in 1024 byte units
+  cc is the maximum number of tape buffers to allocate (the number of
+        buffers is bounded also by the number of drives detected)
+
+
+IOCTLS
+
+The tape is positioned and the drive parameters are set with ioctls
+defined in mtio.h The tape control program 'mt' uses these ioctls. Try
+to find an mt that supports all of the Linux SCSI tape ioctls and
+opens the device for writing if the tape contents will be modified
+(look for a package mt-st* from the Linux ftp sites; the GNU mt does
+not open for writing for, e.g., erase).
+
+The supported ioctls are:
+
+The following use the structure mtop:
+
+MTFSF   Space forward over count filemarks. Tape positioned after filemark.
+MTFSFM  As above but tape positioned before filemark.
+MTBSF	Space backward over count filemarks. Tape positioned before
+        filemark.
+MTBSFM  As above but ape positioned after filemark.
+MTFSR   Space forward over count records.
+MTBSR   Space backward over count records.
+MTWEOF  Write count filemarks.
+MTREW   Rewind tape.
+MTOFFL  Set device off line (often rewind plus eject).
+MTNOP   Do nothing.
+MTRETEN Retension tape.
+MTEOM   Space to end of recorded data.
+MTERASE Erase tape.
+MTSEEK	Seek to tape block count. Uses Tandberg-compatible seek (QFA)
+        for SCSI-1 drives and SCSI-2 seek for SCSI-2 drives.
+MTSETBLK Set the drive block size. Setting to zero sets the drive into
+        variable block mode (if applicable).
+MTSETDENSITY Sets the drive density code to arg. See drive
+        documentation for available codes.
+MTSETDRVBUFFER
+	Is used for several things. The command is obtained from count
+        with mask MT_SET_OPTIONS, the low order bits are used as argument.
+	The subcommands are:
+	0
+           The drive buffer option is set to the argument. Zero means
+           no buffering.
+        MT_ST_BOOLEANS
+           Sets the buffering options. The bits are the new states
+           (enabled/disabled) of the write buffering (MT_ST_BUFFER_WRITES),
+           asynchronous writes (MT_ST_ASYNC_WRITES), read ahead
+           (MT_ST_READ_AHEAD) and debugging (MT_ST_DEBUGGING;
+           (debugging must be compiled into the driver).
+        MT_ST_WRITE_THRESHOLD
+           Sets the write threshold for this device to kilobytes
+           specified by the lowest bits.
+
+The following ioctl uses the structure mtpos:
+MTIOCPOS Reads the current position from the drive. Uses
+        Tandberg-compatible QFA for SCSI-1 drives and the SCSI-2
+        command for the SCSI-2 drives.
+
+The following ioctl uses the structure mtget to return the status:
+MTIOCGET Returns some status information.
+        The file number and block number within file are returned. The
+        block is -1 when it can't be determined (e.g., after MTBSF).
+        The drive type is either MTISSCSI1 or MTISSCSI2.
+        The number of recovered errors since the previous status call
+        is stored in the lower word of the field mt_erreg.
+        The current block size and the density code are stored in the field
+        mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and
+        MT_ST_DENSITY_SHIFT).
+        The other fields are empty.
+
+
+MISCELLANEOUS COMPILE OPTIONS
+
+The recovered write errors are considered fatal if ST_RECOVERED_WRITE_FATAL
+is defined.
+
+Immediate return from tape positioning SCSI commands can be enabled by
+defining ST_NOWAIT.
+
+When using read ahead or buffered writes the position within the file
+may not be correct after the file is closed (correct position may
+require backspacing over more than one record). The correct position
+within file can be obtained if ST_IN_FILE_POS is defined. (The
+driver always backs over a filemark crossed by read ahead if the user
+does not request data that far.)
+
+Kai M{kisara
diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v1.1.11/linux/drivers/scsi/st.c	Wed Mar 16 19:12:09 1994
+++ linux/drivers/scsi/st.c	Fri May  6 08:28:59 1994
@@ -1,42 +1,17 @@
 /*
-  SCSI Tape Driver for Linux
+  SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+  file README.st for more information.
 
-  Version 0.02 for Linux 0.98.4 and Eric Youngdale's new scsi driver
-
   History:
   Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
-  Contribution and ideas from several people including Eric Youngdale and
-  Wolfgang Denk.
-
-  Features:
-  - support for different block sizes and internal buffering
-  - support for fixed and variable block size (within buffer limit;
-    blocksize set to zero)
-  - *nix-style ioctl with codes from mtio.h from the QIC-02 driver by
-    Hennus Bergman (command MTSETBLK added)
-  - character device
-  - rewind and non-rewind devices
-  - capability to handle several tape drives simultaneously
-  - one buffer if one drive, two buffers if more than one drive (limits the
-    number of simultaneously open drives to two)
-  - write behind
-  - seek and tell (Tandberg compatible and SCSI-2)
-
-  Devices:
-  Autorewind devices have minor numbers equal to the tape numbers (0 > ).
-  Nonrewind device has the minor number equal to tape number + 128.
-
-  Problems:
-  The end of media detection works correctly in writing only if the drive
-  writes the buffer contents after the early-warning mark. If you want to
-  be sure that EOM is reported correctly, you should uncomment the line
-  defining ST_NO_DELAYED_WRITES. Note that when delayed writes are disabled
-  each write byte count must be an integral number of blocks.
+  Contribution and ideas from several people including (in alphabetical
+  order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and
+  Eric Youngdale.
 
-  Copyright 1992, 1993 Kai Makisara
+  Copyright 1992, 1993, 1994 Kai Makisara
 		 email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
 
-  Last modified: Thu Nov 25 21:49:02 1993 by root@kai.home
+  Last modified: Wed May  4 20:29:42 1994 by root@kai.home
 */
 
 #include <linux/fs.h>
@@ -57,44 +32,40 @@
 #include "st.h"
 #include "constants.h"
 
-/* Uncomment the following if you want the rewind, etc. commands return
-   before command completion. */
+/* #define DEBUG */
+
 /* #define ST_NOWAIT */
 
-/* Uncomment the following if you want the tape to be positioned correctly
-   within file after close (the tape is positioned correctly with respect
-   to the filemarks even wihout ST_IN_FILE_POS defined */
 /* #define ST_IN_FILE_POS */
 
-/* Uncomment the following if you want recovered write errors to be
-   fatal. */
 /* #define ST_RECOVERED_WRITE_FATAL */
 
-/* Uncomment the following if you want all data from a write command to
-   be written to tape before the command returns. Disables write-behind. */
-/* #define ST_NO_DELAYED_WRITES */
-
-/* Number of ST_BLOCK_SIZE blocks in the buffers */
-#define ST_BUFFER_BLOCKS 64
-/* Write-behind can be disabled by setting ST_WRITE_THRESHOLD_BLOCKS equal
-   to or larger than ST_BUFFER_BLOCKS */
-#define ST_WRITE_THRESHOLD_BLOCKS 60
-#define ST_BLOCK_SIZE 512
+#define ST_BUFFER_WRITES 1
+
+#define ST_ASYNC_WRITES 1
+
+#define ST_READ_AHEAD 1
+
+#define ST_BLOCK_SIZE 1024
+
+#define ST_MAX_BUFFERS 2
+
+#define ST_BUFFER_BLOCKS 32
+
+#define ST_WRITE_THRESHOLD_BLOCKS 30
+
 #define ST_BUFFER_SIZE (ST_BUFFER_BLOCKS * ST_BLOCK_SIZE)
 #define ST_WRITE_THRESHOLD (ST_WRITE_THRESHOLD_BLOCKS * ST_BLOCK_SIZE)
 
-#ifdef ST_NO_DELAYED_WRITES
-#undef ST_WRITE_THRESHOLD_BLOCKS
-#define ST_WRITE_THRESHOLD_BLOCKS ST_BUFFER_BLOCKS
-#endif
-
-/* The buffer size should fit into the 24 bits reserved for length in the
+/* The buffer size should fit into the 24 bits for length in the
    6-byte SCSI read and write commands. */
 #if ST_BUFFER_SIZE >= (2 << 24 - 1)
 #error "Buffer size should not exceed (2 << 24 - 1) bytes!"
 #endif
 
-/* #define DEBUG */
+#ifdef DEBUG
+static int debugging = 1;
+#endif
 
 #define MAX_RETRIES 0
 #define MAX_READY_RETRIES 5
@@ -104,7 +75,10 @@
 #define ST_LONG_TIMEOUT 200000
 
 static int st_nbr_buffers;
-static ST_buffer *st_buffers[2];
+static ST_buffer **st_buffers;
+static int st_buffer_size = ST_BUFFER_SIZE;
+static int st_write_threshold = ST_WRITE_THRESHOLD;
+static int st_max_buffers = ST_MAX_BUFFERS;
 
 static Scsi_Tape * scsi_tapes;
 int NR_ST=0;
@@ -128,8 +102,14 @@
   if (!result && SCpnt->sense_buffer[0] == 0)
     return 0;
 #ifdef DEBUG
-  printk("st%d: Error: %x\n", dev, result);
-  print_sense("st", SCpnt);
+  if (debugging) {
+    printk("st%d: Error: %x, cmd: %x %x %x %x %x %x Len: %d\n", dev, result,
+	   SCpnt->cmnd[0], SCpnt->cmnd[1], SCpnt->cmnd[2],
+	   SCpnt->cmnd[3], SCpnt->cmnd[4], SCpnt->cmnd[5],
+	   SCpnt->request_bufflen);
+    if (driver_byte(result) & DRIVER_SENSE)
+      print_sense("st", SCpnt);
+  }
 #endif
 /*  if ((sense[0] & 0x70) == 0x70 &&
        ((sense[2] & 0x80) ))
@@ -142,6 +122,7 @@
 #endif
       ) {
     scsi_tapes[dev].recover_count++;
+    scsi_tapes[dev].mt_status->mt_erreg += (1 << MT_ST_SOFTERR_SHIFT);
     if (SCpnt->cmnd[0] == READ_6)
       stp = "read";
     else if (SCpnt->cmnd[0] == WRITE_6)
@@ -192,13 +173,12 @@
       wake_up( &(STp->waiting) );
   }
 #ifdef DEBUG
-  else
+  else if (debugging)
     printk("st?: Illegal interrupt device %x\n", st_nbr);
 #endif
 }
 
 
-#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
 /* Handle the write-behind checking */
 	static void
 write_behind_check(int dev)
@@ -222,12 +202,45 @@
 	   STbuffer->b_data + STbuffer->writing,
 	   STbuffer->buffer_bytes - STbuffer->writing);
   STbuffer->buffer_bytes -= STbuffer->writing;
+  if (STp->drv_block >= 0) {
+    if (STp->block_size == 0)
+      STp->drv_block++;
+    else
+      STp->drv_block += STbuffer->writing / STp->block_size;
+  }
   STbuffer->writing = 0;
 
   return;
 }
-#endif
+
+
+/* Back over EOF if it has been inadvertently crossed (ioctl not used because
+   it messes up the block number). */
+	static int
+back_over_eof(int dev)
+{
+  Scsi_Cmnd *SCpnt;
+  Scsi_Tape *STp = &(scsi_tapes[dev]);
+  unsigned char cmd[10];
+
+  cmd[0] = SPACE;
+  cmd[1] = 0x01; /* Space FileMarks */
+  cmd[2] = cmd[3] = cmd[4] = 0xff;  /* -1 filemarks */
+  cmd[5] = 0;
+
+  SCpnt = allocate_device(NULL, (STp->device)->index, 1);
+  SCpnt->sense_buffer[0] = 0;
+  SCpnt->request.dev = dev;
+  scsi_do_cmd(SCpnt,
+	      (void *) cmd, (void *) (STp->buffer)->b_data, 0,
+	      st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+
+  if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+  SCpnt->request.dev = -1;
 
+  return (STp->buffer)->last_result_fatal;
+}
+
 
 /* Flush the write buffer (never need to write if variable blocksize). */
 	static int
@@ -239,20 +252,19 @@
   Scsi_Cmnd *SCpnt;
   Scsi_Tape *STp = &(scsi_tapes[dev]);
 
-#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
   if ((STp->buffer)->writing) {
     write_behind_check(dev);
     if ((STp->buffer)->last_result_fatal) {
 #ifdef DEBUG
-      printk("st%d: Async write error %x.\n", dev,
-	     (STp->buffer)->last_result);
+      if (debugging)
+	printk("st%d: Async write error (flush) %x.\n", dev,
+	       (STp->buffer)->last_result);
 #endif
       if ((STp->buffer)->last_result == INT_MAX)
 	return (-ENOSPC);
       return (-EIO);
     }
   }
-#endif
 
   result = 0;
   if (STp->dirty == 1) {
@@ -262,7 +274,8 @@
     transfer = ((offset + STp->block_size - 1) /
 		STp->block_size) * STp->block_size;
 #ifdef DEBUG
-    printk("st%d: Flushing %d bytes.\n", dev, transfer);
+    if (debugging)
+      printk("st%d: Flushing %d bytes.\n", dev, transfer);
 #endif
     memset((STp->buffer)->b_data + offset, 0, transfer - offset);
 
@@ -292,8 +305,11 @@
       }
       else
 	result = (-EIO);
+      STp->drv_block = (-1);
     }
     else {
+      if (STp->drv_block >= 0)
+	STp->drv_block += blks;
       STp->dirty = 0;
       (STp->buffer)->buffer_bytes = 0;
     }
@@ -332,7 +348,7 @@
   result = 0;
   if (!seek_next) {
     if ((STp->eof == ST_FM) && !STp->eof_hit) {
-      result = st_int_ioctl(inode, filp, MTBSF, 1); /* Back over the EOF hit */
+      result = back_over_eof(dev); /* Back over the EOF hit */
       if (!result) {
 	STp->eof = ST_NOEOF;
 	STp->eof_hit = 0;
@@ -400,31 +416,36 @@
     SCpnt->request.dev = dev;
     scsi_do_cmd(SCpnt,
                 (void *) cmd, (void *) (STp->buffer)->b_data,
-                ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT,
+                0, st_sleep_done, ST_LONG_TIMEOUT,
 		MAX_READY_RETRIES);
 
     if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
 
     if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
 	(SCpnt->sense_buffer[2] & 0x0f) == UNIT_ATTENTION) { /* New media? */
+      (STp->mt_status)->mt_fileno = 0 ;
       SCpnt->sense_buffer[0]=0;
       memset ((void *) &cmd[0], 0, 10);
       cmd[0] = TEST_UNIT_READY;
       SCpnt->request.dev = dev;
       scsi_do_cmd(SCpnt,
 		  (void *) cmd, (void *) (STp->buffer)->b_data,
-		  ST_BLOCK_SIZE, st_sleep_done, ST_LONG_TIMEOUT,
+		  0, st_sleep_done, ST_LONG_TIMEOUT,
 		  MAX_READY_RETRIES);
 
       if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
+      (STp->mt_status)->mt_fileno = STp->drv_block = 0;
     }
 
     if ((STp->buffer)->last_result_fatal != 0) {
       if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
-	  (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE)
+	  (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
+        (STp->mt_status)->mt_fileno = STp->drv_block = 0 ;
 	printk("st%d: No tape.\n", dev);
-      else
+      } else {
 	printk("st%d: Error %x.\n", dev, SCpnt->result);
+        (STp->mt_status)->mt_fileno = STp->drv_block = (-1);
+      }
       (STp->buffer)->in_use = 0;
       STp->in_use = 0;
       SCpnt->request.dev = -1;  /* Mark as not busy */
@@ -437,7 +458,7 @@
     SCpnt->request.dev = dev;
     scsi_do_cmd(SCpnt,
                 (void *) cmd, (void *) (STp->buffer)->b_data,
-                ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+		6, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
 
     if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
 
@@ -447,14 +468,16 @@
       STp->min_block = ((STp->buffer)->b_data[4] << 8) |
 	(STp->buffer)->b_data[5];
 #ifdef DEBUG
-      printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block,
-	     STp->max_block);
+      if (debugging)
+	printk("st%d: Block limits %d - %d bytes.\n", dev, STp->min_block,
+	       STp->max_block);
 #endif
     }
     else {
       STp->min_block = STp->max_block = (-1);
 #ifdef DEBUG
-      printk("st%d: Can't read block limits.\n", dev);
+      if (debugging)
+	printk("st%d: Can't read block limits.\n", dev);
 #endif
     }
 
@@ -465,13 +488,14 @@
     SCpnt->request.dev = dev;
     scsi_do_cmd(SCpnt,
                 (void *) cmd, (void *) (STp->buffer)->b_data,
-                ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+                12, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
 
     if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
 
     if ((STp->buffer)->last_result_fatal != 0) {
 #ifdef DEBUG
-      printk("st%d: No Mode Sense.\n", dev);
+      if (debugging)
+	printk("st%d: No Mode Sense.\n", dev);
 #endif
       (STp->buffer)->b_data[2] =
       (STp->buffer)->b_data[3] = 0;
@@ -479,9 +503,10 @@
     SCpnt->request.dev = -1;  /* Mark as not busy */
 
 #ifdef DEBUG
-    printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev,
-	   (STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
-	   (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]);
+    if (debugging)
+      printk("st%d: Mode sense. Length %d, medium %x, WBS %x, BLL %d\n", dev,
+	     (STp->buffer)->b_data[0], (STp->buffer)->b_data[1],
+	     (STp->buffer)->b_data[2], (STp->buffer)->b_data[3]);
 #endif
 
     if ((STp->buffer)->b_data[3] >= 8) {
@@ -490,13 +515,13 @@
       STp->block_size = (STp->buffer)->b_data[9] * 65536 +
 	(STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
 #ifdef DEBUG
-      printk(
-	"st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n",
-	     dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
-	     (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
-	     STp->block_size, STp->drv_buffer);
+      if (debugging)
+	printk("st%d: Density %x, tape length: %x, blocksize: %d, drv buffer: %d\n",
+	       dev, STp->density, (STp->buffer)->b_data[5] * 65536 +
+	       (STp->buffer)->b_data[6] * 256 + (STp->buffer)->b_data[7],
+	       STp->block_size, STp->drv_buffer);
 #endif
-      if (STp->block_size > ST_BUFFER_SIZE) {
+      if (STp->block_size > st_buffer_size) {
 	printk("st%d: Blocksize %d too large for buffer.\n", dev,
 	       STp->block_size);
 	(STp->buffer)->in_use = 0;
@@ -506,29 +531,31 @@
 
     }
     else
-      STp->block_size = ST_BLOCK_SIZE;
+      STp->block_size = 512;  /* "Educated Guess" (?) */
 
     if (STp->block_size > 0) {
-      (STp->buffer)->buffer_blocks = ST_BUFFER_SIZE / STp->block_size;
+      (STp->buffer)->buffer_blocks = st_buffer_size / STp->block_size;
       (STp->buffer)->buffer_size =
 	(STp->buffer)->buffer_blocks * STp->block_size;
     }
     else {
       (STp->buffer)->buffer_blocks = 1;
-      (STp->buffer)->buffer_size = ST_BUFFER_SIZE;
+      (STp->buffer)->buffer_size = st_buffer_size;
     }
     (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
 
 #ifdef DEBUG
-    printk("st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev,
-	   STp->block_size, (STp->buffer)->buffer_size,
-	   (STp->buffer)->buffer_blocks);
+    if (debugging)
+      printk("st%d: Block size: %d, buffer size: %d (%d blocks).\n", dev,
+	     STp->block_size, (STp->buffer)->buffer_size,
+	     (STp->buffer)->buffer_blocks);
 #endif
 
     if ((STp->buffer)->b_data[2] & 0x80) {
       STp->write_prot = 1;
 #ifdef DEBUG
-      printk( "st%d: Write protected\n", dev);
+      if (debugging)
+	printk( "st%d: Write protected\n", dev);
 #endif
     }
 
@@ -557,7 +584,8 @@
       result = flush_write_buffer(dev);
 
 #ifdef DEBUG
-      printk("st%d: File length %d bytes.\n", dev, filp->f_pos);
+      if (debugging)
+	printk("st%d: File length %ld bytes.\n", dev, filp->f_pos);
 #endif
 
       if (result == 0 || result == (-ENOSPC)) {
@@ -570,24 +598,29 @@
 	SCpnt->request.dev = dev;
 	scsi_do_cmd( SCpnt,
 		    (void *) cmd, (void *) (STp->buffer)->b_data,
-		    ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
+		    0, st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
 
 	if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
 
 	if ((STp->buffer)->last_result_fatal != 0)
 	  printk("st%d: Error on write filemark.\n", dev);
+	else {
+          (STp->mt_status)->mt_fileno++ ;
+	  STp->drv_block = 0;
+	}
 
 	SCpnt->request.dev = -1;  /* Mark as not busy */
       }
 
 #ifdef DEBUG
-      printk("st%d: Buffer flushed, EOF written\n", dev);
+      if (debugging)
+	printk("st%d: Buffer flushed, EOF written\n", dev);
 #endif
     }
     else if (!rewind) {
 #ifndef ST_IN_FILE_POS
       if ((STp->eof == ST_FM) && !STp->eof_hit)
-	st_int_ioctl(inode, filp, MTBSF, 1); /* Back over the EOF hit */
+	back_over_eof(dev);
 #else
       flush_buffer(inode, filp, 0);
 #endif
@@ -627,7 +660,7 @@
     if (STp->write_prot)
       return (-EACCES);
 
-    if (STp->block_size == 0 && count > ST_BUFFER_SIZE)
+    if (STp->block_size == 0 && count > st_buffer_size)
       return (-EOVERFLOW);
 
     if (STp->rw == ST_READING) {
@@ -637,13 +670,13 @@
       STp->rw = ST_WRITING;
     }
 
-#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
     if ((STp->buffer)->writing) {
       write_behind_check(dev);
       if ((STp->buffer)->last_result_fatal) {
 #ifdef DEBUG
-	printk("st%d: Async write error %x.\n", dev,
-	       (STp->buffer)->last_result);
+	if (debugging)
+	  printk("st%d: Async write error (write) %x.\n", dev,
+		 (STp->buffer)->last_result);
 #endif
 	if ((STp->buffer)->last_result == INT_MAX) {
 	  retval = (-ENOSPC);  /* All has been written */
@@ -654,20 +687,21 @@
 	return retval;
       }
     }
-#endif
 
     if (STp->eof == ST_EOM_OK)
       return (-ENOSPC);
     else if (STp->eof == ST_EOM_ERROR)
       return (-EIO);
 
-#ifdef ST_NO_DELAYED_WRITES
-    if (STp->block_size != 0 && (count % STp->block_size) != 0)
-      return (-EIO);   /* Write must be integral number of blocks */
-    write_threshold = 1;
-#else
-    write_threshold = (STp->buffer)->buffer_size;
-#endif
+    if (!STp->do_buffer_writes) {
+      if (STp->block_size != 0 && (count % STp->block_size) != 0)
+	return (-EIO);   /* Write must be integral number of blocks */
+      write_threshold = 1;
+    }
+    else
+      write_threshold = (STp->buffer)->buffer_size;
+    if (!STp->do_async_writes)
+      write_threshold--;
 
     SCpnt = allocate_device(NULL, (STp->device)->index, 1);
 
@@ -680,16 +714,9 @@
     STp->rw = ST_WRITING;
 
     b_point = buf;
-    while(
-#if ST_WRITE_THRESHOLD_BLOCKS  < ST_BUFFER_BLOCKS
-	  STp->block_size != 0 &&
-	  ((STp->buffer)->buffer_bytes + count) >
-	  write_threshold)
-#else
-	  (STp->block_size == 0 && count > 0) ||
-	  ((STp->buffer)->buffer_bytes + count) >=
-	  write_threshold)
-#endif
+    while((STp->block_size == 0 && !STp->do_async_writes && count > 0) ||
+	  (STp->block_size != 0 &&
+	   (STp->buffer)->buffer_bytes + count > write_threshold))
     {
       if (STp->block_size == 0)
 	do_count = count;
@@ -702,25 +729,27 @@
 		    (STp->buffer)->buffer_bytes, b_point, do_count);
 
       if (STp->block_size == 0)
-        blks = do_count;
-      else
+        blks = transfer = do_count;
+      else {
 	blks = ((STp->buffer)->buffer_bytes + do_count) /
 	  STp->block_size;
+	transfer = blks * STp->block_size;
+      }
       cmd[2] = blks >> 16;
       cmd[3] = blks >> 8;
       cmd[4] = blks;
       SCpnt->sense_buffer[0] = 0;
       SCpnt->request.dev = dev;
       scsi_do_cmd (SCpnt,
-		   (void *) cmd, (STp->buffer)->b_data,
-		   (STp->buffer)->buffer_size,
+		   (void *) cmd, (STp->buffer)->b_data, transfer,
 		   st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
 
       if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
 
       if ((STp->buffer)->last_result_fatal != 0) {
 #ifdef DEBUG
-	printk("st%d: Error on write:\n", dev);
+	if (debugging)
+	  printk("st%d: Error on write:\n", dev);
 #endif
 	if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
 	    (SCpnt->sense_buffer[2] & 0x40)) {
@@ -738,23 +767,34 @@
 	  if (transfer <= do_count) {
 	    filp->f_pos += do_count - transfer;
 	    count -= do_count - transfer;
+	    if (STp->drv_block >= 0) {
+	      if (STp->block_size == 0 && transfer < do_count)
+		STp->drv_block++;
+	      else if (STp->block_size != 0)
+		STp->drv_block += (do_count - transfer) / STp->block_size;
+	    }
 	    STp->eof = ST_EOM_OK;
 	    retval = (-ENOSPC); /* EOM within current request */
 #ifdef DEBUG
-	    printk("st%d: EOM with %d bytes unwritten.\n",
-		   dev, transfer);
+	    if (debugging)
+	      printk("st%d: EOM with %d bytes unwritten.\n",
+		     dev, transfer);
 #endif
 	  }
 	  else {
 	    STp->eof = ST_EOM_ERROR;
+	    STp->drv_block = (-1);    /* Too cautious? */
 	    retval = (-EIO); /* EOM for old data */
 #ifdef DEBUG
-	    printk("st%d: EOM with lost data.\n", dev);
+	    if (debugging)
+	      printk("st%d: EOM with lost data.\n", dev);
 #endif
 	  }
 	}
-	else
+	else {
+	  STp->drv_block = (-1);    /* Too cautious? */
 	  retval = (-EIO);
+	}
 
 	SCpnt->request.dev = -1;  /* Mark as not busy */
 	(STp->buffer)->buffer_bytes = 0;
@@ -767,6 +807,12 @@
       filp->f_pos += do_count;
       b_point += do_count;
       count -= do_count;
+      if (STp->drv_block >= 0) {
+	if (STp->block_size == 0)
+	  STp->drv_block++;
+	else
+	  STp->drv_block += blks;
+      }
       (STp->buffer)->buffer_bytes = 0;
       STp->dirty = 0;
     }
@@ -784,9 +830,9 @@
       return (STp->buffer)->last_result_fatal;
     }
 
-#if ST_WRITE_THRESHOLD_BLOCKS < ST_BUFFER_BLOCKS
-    if ((STp->buffer)->buffer_bytes >= ST_WRITE_THRESHOLD ||
-	STp->block_size == 0) {
+    if (STp->do_async_writes &&
+	((STp->buffer)->buffer_bytes >= STp->write_threshold ||
+	 STp->block_size == 0) ) {
       /* Schedule an asynchronous write */
       if (STp->block_size == 0)
 	(STp->buffer)->writing = (STp->buffer)->buffer_bytes;
@@ -811,7 +857,6 @@
 		   st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
     }
     else
-#endif
       SCpnt->request.dev = -1;  /* Mark as not busy */
 
     return( total);
@@ -838,9 +883,13 @@
     }
 #endif
 
-    if (STp->block_size == 0 && count > ST_BUFFER_SIZE)
+    if (STp->block_size == 0 && count > st_buffer_size)
       return (-EOVERFLOW);
 
+    if (!(STp->do_read_ahead) && STp->block_size != 0 &&
+	(count % STp->block_size) != 0)
+      return (-EIO);	/* Read must be integral number of blocks */
+
     if (STp->rw == ST_WRITING) {
       transfer = flush_buffer(inode, filp, 0);
       if (transfer)
@@ -849,7 +898,7 @@
     }
 
 #ifdef DEBUG
-    if (STp->eof != ST_NOEOF)
+    if (debugging && STp->eof != ST_NOEOF)
       printk("st%d: EOF flag up. Bytes %d\n", dev,
 	     (STp->buffer)->buffer_bytes);
 #endif
@@ -872,8 +921,17 @@
 	if (STp->block_size == 0)
 	  blks = bytes = count;
 	else {
-	  blks = (STp->buffer)->buffer_blocks;
-	  bytes = blks * STp->block_size;
+	  if (STp->do_read_ahead) {
+	    blks = (STp->buffer)->buffer_blocks;
+	    bytes = blks * STp->block_size;
+	  }
+	  else {
+	    bytes = count;
+	    if (bytes > st_buffer_size)
+	      bytes = st_buffer_size;
+	    blks = bytes / STp->block_size;
+	    bytes = blks * STp->block_size;
+	  }
 	}
 	cmd[2] = blks >> 16;
 	cmd[3] = blks >> 8;
@@ -893,11 +951,12 @@
 
 	if ((STp->buffer)->last_result_fatal) {
 #ifdef DEBUG
-	  printk("st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev,
-		 SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
-		 SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
-		 SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
-		 SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
+	  if (debugging)
+	    printk("st%d: Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n", dev,
+		   SCpnt->sense_buffer[0], SCpnt->sense_buffer[1],
+		   SCpnt->sense_buffer[2], SCpnt->sense_buffer[3],
+		   SCpnt->sense_buffer[4], SCpnt->sense_buffer[5],
+		   SCpnt->sense_buffer[6], SCpnt->sense_buffer[7]);
 #endif
 	  if ((SCpnt->sense_buffer[0] & 0x70) == 0x70) { /* extended sense */
 
@@ -917,7 +976,7 @@
 		if (STp->block_size == 0) {
 		  if (transfer <= 0)
 		    transfer = 0;
-		  (STp->buffer)->buffer_bytes = count - transfer;
+		  (STp->buffer)->buffer_bytes = bytes - transfer;
 		}
 		else {
 		  printk("st%d: Incorrect block size.\n", dev);
@@ -928,14 +987,14 @@
 	      else if (SCpnt->sense_buffer[2] & 0x40) {
 		STp->eof = ST_EOM_OK;
 		if (STp->block_size == 0)
-		  (STp->buffer)->buffer_bytes = count - transfer;
+		  (STp->buffer)->buffer_bytes = bytes - transfer;
 		else
 		  (STp->buffer)->buffer_bytes =
-		    ((STp->buffer)->buffer_blocks - transfer) *
-		      STp->block_size;
+		    bytes - transfer * STp->block_size;
 #ifdef DEBUG
-		printk("st%d: EOM detected (%d bytes read).\n", dev,
-		       (STp->buffer)->buffer_bytes);
+		if (debugging)
+		  printk("st%d: EOM detected (%d bytes read).\n", dev,
+			 (STp->buffer)->buffer_bytes);
 #endif
 	      }
 	      else if (SCpnt->sense_buffer[2] & 0x80) {
@@ -944,20 +1003,22 @@
 		  (STp->buffer)->buffer_bytes = 0;
 		else
 		  (STp->buffer)->buffer_bytes =
-		    ((STp->buffer)->buffer_blocks - transfer) *
-		      STp->block_size;
+		    bytes - transfer * STp->block_size;
 #ifdef DEBUG
-		printk(
-		 "st%d: EOF detected (%d bytes read, transferred %d bytes).\n",
-		       dev, (STp->buffer)->buffer_bytes, total);
+		if (debugging)
+		  printk(
+		    "st%d: EOF detected (%d bytes read, transferred %d bytes).\n",
+			 dev, (STp->buffer)->buffer_bytes, total);
 #endif
 	      } /* end of EOF, EOM, ILI test */
 	    }
 	    else { /* nonzero sense key */
 #ifdef DEBUG
-	      printk("st%d: Tape error while reading.\n", dev);
+	      if (debugging)
+		printk("st%d: Tape error while reading.\n", dev);
 #endif
 	      SCpnt->request.dev = -1;
+	      STp->drv_block = (-1);
 	      if (total)
 		return total;
 	      else
@@ -973,12 +1034,19 @@
 	else /* Read successful */
 	  (STp->buffer)->buffer_bytes = bytes;
 
+	if (STp->drv_block >= 0) {
+	  if (STp->block_size == 0)
+	    STp->drv_block++;
+	  else
+	    STp->drv_block += (STp->buffer)->buffer_bytes / STp->block_size;
+	}
+
       } /* if ((STp->buffer)->buffer_bytes == 0 &&
 	   STp->eof == ST_NOEOF) */
 
       if ((STp->buffer)->buffer_bytes > 0) {
 #ifdef DEBUG
-	if (STp->eof != ST_NOEOF)
+	if (debugging && STp->eof != ST_NOEOF)
 	  printk("st%d: EOF up. Left %d, needed %d.\n", dev,
 		 (STp->buffer)->buffer_bytes, count - total);
 #endif
@@ -995,8 +1063,11 @@
       else if (STp->eof != ST_NOEOF) {
 	STp->eof_hit = 1;
 	SCpnt->request.dev = -1;  /* Mark as not busy */
-	if (total == 0 && STp->eof == ST_FM)
+	if (total == 0 && STp->eof == ST_FM) {
 	  STp->eof = 0;
+	  STp->drv_block = 0;
+	  (STp->mt_status)->mt_fileno++;
+	}
 	if (total == 0 && STp->eof == ST_EOM_OK)
 	  return (-EIO);  /* ST_EOM_ERROR not used in read */
 	return total;
@@ -1012,6 +1083,49 @@
     return total;
 }
 
+
+
+/* Set the driver options */
+	static int
+st_set_options(struct inode * inode, long options)
+{
+  int dev, value;
+  Scsi_Tape *STp;
+
+  dev = MINOR(inode->i_rdev) & 127;
+  STp = &(scsi_tapes[dev]);
+  if ((options & MT_ST_OPTIONS) == MT_ST_BOOLEANS) {
+    STp->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
+    STp->do_async_writes  = (options & MT_ST_ASYNC_WRITES) != 0;
+    STp->do_read_ahead    = (options & MT_ST_READ_AHEAD) != 0;
+#ifdef DEBUG
+    debugging = (options & MT_ST_DEBUGGING) != 0;
+    printk(
+"st%d: options: buffer writes: %d, async writes: %d, read ahead: %d\n",
+	   dev, STp->do_buffer_writes, STp->do_async_writes,
+	   STp->do_read_ahead);
+    printk("              debugging: %d\n", debugging);
+#endif
+  }
+  else if ((options & MT_ST_OPTIONS) == MT_ST_WRITE_THRESHOLD) {
+    value = (options & ~MT_ST_OPTIONS) * ST_BLOCK_SIZE;
+    if (value < 1 || value > st_buffer_size) {
+      printk("st: Write threshold %d too small or too large.\n",
+	     value);
+      return (-EIO);
+    }
+    STp->write_threshold = value;
+#ifdef DEBUG
+    printk("st%d: Write threshold set to %d bytes.\n", dev,
+	   STp->write_threshold);
+#endif
+  }
+  else
+    return (-EIO);
+
+  return 0;
+}
+
 
 /* Internal ioctl function */
 	static int
@@ -1025,11 +1139,15 @@
    unsigned char cmd[10];
    Scsi_Cmnd * SCpnt;
    Scsi_Tape * STp;
+   int fileno, blkno, undone, datalen;
 
    dev = dev & 127;
    STp = &(scsi_tapes[dev]);
+   fileno = (STp->mt_status)->mt_fileno ;
+   blkno = STp->drv_block;
 
    memset(cmd, 0, 10);
+   datalen = 0;
    switch (cmd_in) {
      case MTFSF:
      case MTFSFM:
@@ -1039,9 +1157,12 @@
        cmd[3] = (arg >> 8);
        cmd[4] = arg;
 #ifdef DEBUG
-       printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
-	      cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+       if (debugging)
+	 printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
+		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
 #endif
+       fileno += arg;
+       blkno = 0;
        break; 
      case MTBSF:
      case MTBSFM:
@@ -1052,11 +1173,15 @@
        cmd[3] = (ltmp >> 8);
        cmd[4] = ltmp;
 #ifdef DEBUG
-       if (cmd[2] & 0x80)
-	 ltmp = 0xff000000;
-       ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
-       printk("st%d: Spacing tape backward over %d filemarks.\n", dev, (-ltmp));
+       if (debugging) {
+	 if (cmd[2] & 0x80)
+	   ltmp = 0xff000000;
+	 ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+	 printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp));
+       }
 #endif
+       fileno -= arg;
+       blkno = (-1);  /* We can't know the block number */
        break; 
       case MTFSR:
        cmd[0] = SPACE;
@@ -1065,9 +1190,12 @@
        cmd[3] = (arg >> 8);
        cmd[4] = arg;
 #ifdef DEBUG
-       printk("st%d: Spacing tape forward %d blocks.\n", dev,
-	      cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+       if (debugging)
+	 printk("st%d: Spacing tape forward %d blocks.\n", dev,
+		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
 #endif
+       if (blkno >= 0)
+	 blkno += arg;
        break; 
      case MTBSR:
        cmd[0] = SPACE;
@@ -1077,11 +1205,15 @@
        cmd[3] = (ltmp >> 8);
        cmd[4] = ltmp;
 #ifdef DEBUG
-       if (cmd[2] & 0x80)
-	 ltmp = 0xff000000;
-       ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
-       printk("st%d: Spacing tape backward %d blocks.\n", dev, (-ltmp));
+       if (debugging) {
+	 if (cmd[2] & 0x80)
+	   ltmp = 0xff000000;
+	 ltmp = ltmp | (cmd[2] << 16) | (cmd[3] << 8) | cmd[4];
+	 printk("st%d: Spacing tape backward %ld blocks.\n", dev, (-ltmp));
+       }
 #endif
+       if (blkno >= 0)
+	 blkno -= arg;
        break; 
      case MTWEOF:
        if (STp->write_prot)
@@ -1092,9 +1224,12 @@
        cmd[4] = arg;
        timeout = ST_TIMEOUT;
 #ifdef DEBUG
-       printk("st%d: Writing %d filemarks.\n", dev,
-	      cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
+       if (debugging)
+	 printk("st%d: Writing %d filemarks.\n", dev,
+		cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
 #endif
+       fileno += arg;
+       blkno = 0;
        break; 
      case MTREW:
        cmd[0] = REZERO_UNIT;
@@ -1103,8 +1238,10 @@
        timeout = ST_TIMEOUT;
 #endif
 #ifdef DEBUG
-       printk("st%d: Rewinding tape.\n", dev);
+       if (debugging)
+	 printk("st%d: Rewinding tape.\n", dev);
 #endif
+       fileno = blkno = 0 ;
        break; 
      case MTOFFL:
        cmd[0] = START_STOP;
@@ -1113,12 +1250,15 @@
        timeout = ST_TIMEOUT;
 #endif
 #ifdef DEBUG
-       printk("st%d: Unloading tape.\n", dev);
+       if (debugging)
+	 printk("st%d: Unloading tape.\n", dev);
 #endif
+       fileno = blkno = 0 ;
        break; 
      case MTNOP:
 #ifdef DEBUG
-       printk("st%d: No op on tape.\n", dev);
+       if (debugging)
+	 printk("st%d: No op on tape.\n", dev);
 #endif
        return 0;  /* Should do something ? */
        break;
@@ -1130,15 +1270,26 @@
 #endif
        cmd[4] = 3;
 #ifdef DEBUG
-       printk("st%d: Retensioning tape.\n", dev);
+       if (debugging)
+	 printk("st%d: Retensioning tape.\n", dev);
 #endif
+       fileno = blkno = 0 ;
        break; 
      case MTEOM:
+       /* space to the end of tape */
+       ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
+       fileno = (STp->mt_status)->mt_fileno ;
+       /* The next lines would hide the number of spaced FileMarks
+          That's why I inserted the previous lines. I had no luck
+	  with detecting EOM with FSF, so we go now to EOM.
+          Joerg Weule */
        cmd[0] = SPACE;
        cmd[1] = 3;
 #ifdef DEBUG
-       printk("st%d: Spacing to end of recorded medium.\n", dev);
+       if (debugging)
+	 printk("st%d: Spacing to end of recorded medium.\n", dev);
 #endif
+       blkno = (-1);
        break; 
      case MTERASE:
        if (STp->write_prot)
@@ -1146,8 +1297,10 @@
        cmd[0] = ERASE;
        cmd[1] = 1;  /* To the end of tape */
 #ifdef DEBUG
-       printk("st%d: Erasing tape.\n", dev);
+       if (debugging)
+	 printk("st%d: Erasing tape.\n", dev);
 #endif
+       fileno = blkno = 0 ;
        break;
      case MTSEEK:
        if ((STp->device)->scsi_level < SCSI_2) {
@@ -1170,8 +1323,10 @@
        timeout = ST_TIMEOUT;
 #endif
 #ifdef DEBUG
-       printk("st%d: Seeking tape to block %d.\n", dev, arg);
+       if (debugging)
+	 printk("st%d: Seeking tape to block %ld.\n", dev, arg);
 #endif
+       fileno = blkno = (-1);
        break;
      case MTSETBLK:  /* Set block length */
      case MTSETDENSITY: /* Set tape density */
@@ -1181,12 +1336,12 @@
        if (cmd_in == MTSETBLK &&
 	   arg != 0 &&
 	   (arg < STp->min_block || arg > STp->max_block ||
-	    arg > ST_BUFFER_SIZE)) {
+	    arg > st_buffer_size)) {
 	 printk("st%d: Illegal block size.\n", dev);
 	 return (-EINVAL);
        }
        cmd[0] = MODE_SELECT;
-       cmd[4] = 12;
+       cmd[4] = datalen = 12;
 
        memset((STp->buffer)->b_data, 0, 12);
        if (cmd_in == MTSETDRVBUFFER)
@@ -1208,17 +1363,19 @@
        (STp->buffer)->b_data[11] = ltmp;
        timeout = ST_TIMEOUT;
 #ifdef DEBUG
-       if (cmd_in == MTSETBLK)
-	 printk("st%d: Setting block size to %d bytes.\n", dev,
-		(STp->buffer)->b_data[9] * 65536 +
-		(STp->buffer)->b_data[10] * 256 +
-		(STp->buffer)->b_data[11]);
-       else if (cmd_in == MTSETDENSITY)
-	 printk("st%d: Setting density code to %x.\n", dev,
-		(STp->buffer)->b_data[4]);
-       else
-	 printk("st%d: Setting drive buffer code to %d.\n", dev,
-		((STp->buffer)->b_data[2] >> 4) & 7);
+       if (debugging) {
+	 if (cmd_in == MTSETBLK)
+	   printk("st%d: Setting block size to %d bytes.\n", dev,
+		  (STp->buffer)->b_data[9] * 65536 +
+		  (STp->buffer)->b_data[10] * 256 +
+		  (STp->buffer)->b_data[11]);
+	 else if (cmd_in == MTSETDENSITY)
+	   printk("st%d: Setting density code to %x.\n", dev,
+		  (STp->buffer)->b_data[4]);
+	 else
+	   printk("st%d: Setting drive buffer code to %d.\n", dev,
+		  ((STp->buffer)->b_data[2] >> 4) & 7);
+       }
 #endif
        break;
      default:
@@ -1230,7 +1387,7 @@
    SCpnt->sense_buffer[0] = 0;
    SCpnt->request.dev = dev;
    scsi_do_cmd(SCpnt,
-	       (void *) cmd, (void *) (STp->buffer)->b_data, ST_BLOCK_SIZE,
+	       (void *) cmd, (void *) (STp->buffer)->b_data, datalen,
 	       st_sleep_done, timeout, MAX_RETRIES);
 
    if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
@@ -1240,6 +1397,8 @@
    SCpnt->request.dev = -1;  /* Mark as not busy */
 
    if (!ioctl_result) {
+     STp->drv_block = blkno;
+     (STp->mt_status)->mt_fileno = fileno;
      if (cmd_in == MTBSFM)
        ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
      else if (cmd_in == MTFSFM)
@@ -1248,13 +1407,13 @@
        STp->block_size = arg;
        if (arg != 0) {
 	 (STp->buffer)->buffer_blocks =
-	   ST_BUFFER_SIZE / STp->block_size;
+	   st_buffer_size / STp->block_size;
 	 (STp->buffer)->buffer_size =
 	   (STp->buffer)->buffer_blocks * STp->block_size;
        }
        else {
 	 (STp->buffer)->buffer_blocks = 1;
-	 (STp->buffer)->buffer_size = ST_BUFFER_SIZE;
+	 (STp->buffer)->buffer_size = st_buffer_size;
        }
        (STp->buffer)->buffer_bytes =
 	 (STp->buffer)->read_pointer = 0;
@@ -1263,7 +1422,7 @@
        STp->drv_buffer = arg;
      else if (cmd_in == MTSETDENSITY)
        STp->density = arg;
-     if (cmd_in == MTEOM || cmd_in == MTWEOF) {
+     else if (cmd_in == MTEOM || cmd_in == MTWEOF) {
        STp->eof = ST_EOM_OK;
        STp->eof_hit = 0;
      }
@@ -1271,6 +1430,33 @@
        STp->eof = ST_NOEOF;
        STp->eof_hit = 0;
      }
+   } else {
+     if (SCpnt->sense_buffer[2] & 0x40) {
+       STp->eof = ST_EOM_OK;
+       STp->eof_hit = 0;
+       STp->drv_block = 0;
+     }
+     undone = (
+          (SCpnt->sense_buffer[3] << 24) +
+          (SCpnt->sense_buffer[4] << 16) +
+          (SCpnt->sense_buffer[5] << 8) +
+          SCpnt->sense_buffer[6] );
+     if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) )
+       (STp->mt_status)->mt_fileno = fileno - undone ;
+     else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) )
+       (STp->mt_status)->mt_fileno = fileno + undone ;
+     else if (cmd_in == MTFSR) {
+       if (blkno >= undone)
+	 STp->drv_block = blkno - undone;
+       else
+	 STp->drv_block = (-1);
+     }
+     else if (cmd_in == MTBSR && blkno >= 0) {
+       if (blkno >= 0)
+	 STp->drv_block = blkno + undone;
+       else
+	 STp->drv_block = (-1);
+     }
    }
 
    return ioctl_result ;
@@ -1294,7 +1480,7 @@
    dev = dev & 127;
    STp = &(scsi_tapes[dev]);
 #ifdef DEBUG
-   if (!STp->in_use) {
+   if (debugging && !STp->in_use) {
      printk("st%d: Incorrect device.\n", dev);
      return (-EIO);
    }
@@ -1318,7 +1504,11 @@
      if (i < 0)
        return i;
 
-     return st_int_ioctl(inode, file, mtc.mt_op, mtc.mt_count);
+     if (mtc.mt_op == MTSETDRVBUFFER &&
+	 (mtc.mt_count & MT_ST_OPTIONS) != 0)
+       return st_set_options(inode, mtc.mt_count);
+     else
+       return st_int_ioctl(inode, file, mtc.mt_op, mtc.mt_count);
    }
    else if (cmd == (MTIOCGET & IOCCMD_MASK)) {
 
@@ -1328,13 +1518,29 @@
      if (i)
        return i;
 
-     memcpy_tofs((char *)arg, (char *)(STp->buffer)->mt_status,
+     (STp->mt_status)->mt_dsreg =
+       ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
+       ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
+     (STp->mt_status)->mt_blkno = STp->drv_block;
+     if (STp->block_size != 0) {
+       if (STp->rw == ST_WRITING)
+	 (STp->mt_status)->mt_blkno +=
+	   (STp->buffer)->buffer_bytes / STp->block_size;
+       else if (STp->rw == ST_READING)
+	 (STp->mt_status)->mt_blkno -= ((STp->buffer)->buffer_bytes +
+	   STp->block_size - 1) / STp->block_size;
+     }
+
+     memcpy_tofs((char *)arg, (char *)(STp->mt_status),
 		 sizeof(struct mtget));
+
+     (STp->mt_status)->mt_erreg = 0;  /* Clear after read */
      return 0;
    }
    else if (cmd == (MTIOCPOS & IOCCMD_MASK)) {
 #ifdef DEBUG
-     printk("st%d: get tape position.\n", dev);
+     if (debugging)
+       printk("st%d: get tape position.\n", dev);
 #endif
      if (((cmd_in & IOCSIZE_MASK) >> IOCSIZE_SHIFT) != sizeof(struct mtpos))
        return (-EINVAL);
@@ -1363,14 +1569,15 @@
      SCpnt->sense_buffer[0] = 0;
      scsi_do_cmd(SCpnt,
 		 (void *) scmd, (void *) (STp->buffer)->b_data,
-		 ST_BLOCK_SIZE, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
+		 20, st_sleep_done, ST_TIMEOUT, MAX_READY_RETRIES);
 
      if (SCpnt->request.dev == dev) sleep_on( &(STp->waiting) );
      
      if ((STp->buffer)->last_result_fatal != 0) {
        mt_pos.mt_blkno = (-1);
 #ifdef DEBUG
-       printk("st%d: Can't read tape position.\n", dev);
+       if (debugging)
+	 printk("st%d: Can't read tape position.\n", dev);
 #endif
        result = (-EIO);
      }
@@ -1397,8 +1604,25 @@
      return scsi_ioctl(STp->device, cmd_in, (void *) arg);
 }
 
-
 
+/* Set the boot options. Syntax: st=xxx,yyy
+   where xxx is buffer size in 512 byte blocks and yyy is write threshold
+   in 512 byte blocks. */
+	void
+st_setup(char *str, int *ints)
+{
+  if (ints[0] > 0 && ints[1] > 0)
+    st_buffer_size = ints[1] * ST_BLOCK_SIZE;
+  if (ints[0] > 1 && ints[2] > 0) {
+    st_write_threshold = ints[2] * ST_BLOCK_SIZE;
+    if (st_write_threshold > st_buffer_size)
+      st_write_threshold = st_buffer_size;
+  }
+  if (ints[0] > 2 && ints[3] > 0)
+    st_max_buffers = ints[3];
+}
+
+
 static struct file_operations st_fops = {
    NULL,            /* lseek - default */
    st_read,         /* read - general block-dev read */
@@ -1426,7 +1650,8 @@
 /* Driver initialization */
 unsigned long st_init(unsigned long mem_start, unsigned long mem_end)
 {
-  int i;
+  int i, dev_nbr;
+  Scsi_Tape * STp;
 
   if (register_chrdev(MAJOR_NR,"st",&st_fops)) {
     printk("Unable to get major %d for SCSI tapes\n",MAJOR_NR);
@@ -1435,40 +1660,56 @@
   if (NR_ST == 0) return mem_start;
 
 #ifdef DEBUG
-  printk("st: Init tape.\n");
+  printk("st: Buffer size %d bytes, write threshold %d bytes.\n",
+	 st_buffer_size, st_write_threshold);
 #endif
 
-  for (i=0; i < NR_ST; ++i) {
-    scsi_tapes[i].capacity = 0xfffff;
-    scsi_tapes[i].dirty = 0;
-    scsi_tapes[i].rw = ST_IDLE;
-    scsi_tapes[i].eof = ST_NOEOF;
-    scsi_tapes[i].waiting = NULL;
-    scsi_tapes[i].in_use = 0;
-    scsi_tapes[i].drv_buffer = 1;  /* Try buffering if no mode sense */
-    scsi_tapes[i].density = 0;
+  for (i=0, dev_nbr=(-1); i < NR_ST; ++i) {
+    STp = &(scsi_tapes[i]);
+    STp->capacity = 0xfffff;
+    STp->dirty = 0;
+    STp->rw = ST_IDLE;
+    STp->eof = ST_NOEOF;
+    STp->waiting = NULL;
+    STp->in_use = 0;
+    STp->drv_buffer = 1;  /* Try buffering if no mode sense */
+    STp->density = 0;
+    STp->do_buffer_writes = ST_BUFFER_WRITES;
+    STp->do_async_writes = ST_ASYNC_WRITES;
+    STp->do_read_ahead = ST_READ_AHEAD;
+    STp->write_threshold = st_write_threshold;
+    STp->drv_block = 0;
+    STp->mt_status = (struct mtget *) mem_start;
+    mem_start += sizeof(struct mtget);
+    /* Initialize status */
+    memset((void *) scsi_tapes[i].mt_status, 0, sizeof(struct mtget));
+    for (dev_nbr++; dev_nbr < NR_SCSI_DEVICES; dev_nbr++)
+      if (scsi_devices[dev_nbr].type == TYPE_TAPE)
+	break;
+    if (dev_nbr >= NR_SCSI_DEVICES)
+      printk("st%d: ERROR: Not found in scsi chain.\n", i);
+    else {
+      if (scsi_devices[dev_nbr].scsi_level <= 2)
+	STp->mt_status->mt_type = MT_ISSCSI1;
+      else
+	STp->mt_status->mt_type = MT_ISSCSI2;
+    }
   }
 
-
   /* Allocate the buffers */
-  if (NR_ST == 1)
-    st_nbr_buffers = 1;
-  else
-    st_nbr_buffers = 2;
+  st_nbr_buffers = NR_ST;
+  if (st_nbr_buffers > st_max_buffers)
+    st_nbr_buffers = st_max_buffers;
+  st_buffers = (ST_buffer **)mem_start;
+  mem_start += st_nbr_buffers * sizeof(ST_buffer *);
   for (i=0; i < st_nbr_buffers; i++) {
     st_buffers[i] = (ST_buffer *) mem_start;
 #ifdef DEBUG
-    printk("st: Buffer address: %p\n", st_buffers[i]);
+/*    printk("st: Buffer address: %p\n", st_buffers[i]); */
 #endif
-    mem_start += sizeof(ST_buffer) - 1 + ST_BUFFER_BLOCKS * ST_BLOCK_SIZE;
-    st_buffers[i]->mt_status = (struct mtget *) mem_start;
-    mem_start += sizeof(struct mtget);
+    mem_start += sizeof(ST_buffer) - 1 + st_buffer_size;
     st_buffers[i]->in_use = 0;
     st_buffers[i]->writing = 0;
-
-    /* "generic" status */
-    memset((void *) st_buffers[i]->mt_status, 0, sizeof(struct mtget));
-    st_buffers[i]->mt_status->mt_type = MT_ISSCSI1;
   }
 
   return mem_start;
diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/st.h linux/drivers/scsi/st.h
--- v1.1.11/linux/drivers/scsi/st.h	Thu Dec 30 11:39:04 1993
+++ linux/drivers/scsi/st.h	Fri May  6 08:28:58 1994
@@ -11,7 +11,6 @@
 
 typedef struct {
   int in_use;
-  struct mtget * mt_status;
   int buffer_size;
   int buffer_blocks;
   int buffer_bytes;
@@ -33,12 +32,18 @@
   unsigned in_use:1;
   unsigned eof_hit:1;
   unsigned drv_buffer:3;
+  unsigned do_buffer_writes:1;
+  unsigned do_async_writes:1;
+  unsigned do_read_ahead:1;
   unsigned char density;
   ST_buffer * buffer;
   int block_size;
   int min_block;
   int max_block;
+  int write_threshold;
   int recover_count;
+  int drv_block;	/* The block where the drive head is */
+  struct mtget * mt_status;
   Scsi_Cmnd SCpnt;
 } Scsi_Tape;
 
diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/wd7000.c linux/drivers/scsi/wd7000.c
--- v1.1.11/linux/drivers/scsi/wd7000.c	Thu Mar 31 11:44:57 1994
+++ linux/drivers/scsi/wd7000.c	Wed Apr  6 21:49:04 1994
@@ -1,5 +1,5 @@
-/* $Id: wd7000.c,v 1.2 1994/01/15 06:02:32 drew Exp $
- *  linux/kernel/wd7000.c
+/* $Id: $
+ *  linux/drivers/scsi/wd7000.c
  *
  *  Copyright (C) 1992  Thomas Wuensche
  *	closely related to the aha1542 driver from Tommy Thorn
@@ -9,6 +9,95 @@
  *  accomodate Eric Youngdale's modifications to scsi.c.  Nov 1992.
  *
  *  Additional changes to support scatter/gather.  Dec. 1992.  tw/jb
+ *
+ *  No longer tries to reset SCSI bus at boot (it wasn't working anyway).
+ *  Rewritten to support multiple host adapters.
+ *  Miscellaneous cleanup.
+ *  So far, still doesn't do reset or abort correctly, since I have no idea
+ *  how to do them with this board (8^(.                      Jan 1994 jb
+ *
+ * This driver now supports both of the two standard configurations (per
+ * the 3.36 Owner's Manual, my latest reference) by the same method as
+ * before; namely, by looking for a BIOS signature.  Thus, the location of
+ * the BIOS signature determines the board configuration.  Until I have
+ * time to do something more flexible, users should stick to one of the
+ * following:
+ *
+ * Standard configuration for single-adapter systems:
+ *    - BIOS at CE00h
+ *    - I/O base address 350h
+ *    - IRQ level 15
+ *    - DMA channel 6
+ * Standard configuration for a second adapter in a system:
+ *    - BIOS at C800h
+ *    - I/O base address 330h
+ *    - IRQ level 11
+ *    - DMA channel 5
+ *
+ * Anyone who can recompile the kernel is welcome to add others as need
+ * arises, but unpredictable results may occur if there are conflicts.
+ * In any event, if there are multiple adapters in a system, they MUST
+ * use different I/O bases, IRQ levels, and DMA channels, since they will be
+ * indistinguishable (and in direct conflict) otherwise.
+ *
+ *   As a point of information, the NO_OP command toggles the CMD_RDY bit
+ * of the status port, and this fact could be used as a test for the I/O
+ * base address (or more generally, board detection).  There is an interrupt
+ * status port, so IRQ probing could also be done.  I suppose the full
+ * DMA diagnostic could be used to detect the DMA channel being used.  I
+ * haven't done any of this, though, because I think there's too much of
+ * a chance that such explorations could be destructive, if some other
+ * board's resources are used inadvertently.  So, call me a wimp, but I
+ * don't want to try it.  The only kind of exploration I trust is memory
+ * exploration, since it's more certain that reading memory won't be
+ * destructive.
+ *
+ * More to my liking would be a LILO boot command line specification, such
+ * as is used by the aha152x driver (and possibly others).  I'll look into
+ * it, as I have time...
+ *
+ *   I get mail occasionally from people who either are using or are
+ * considering using a WD7000 with Linux.  There is a variety of
+ * nomenclature describing WD7000's.  To the best of my knowledge, the
+ * following is a brief summary (from an old WD doc - I don't work for
+ * them or anything like that):
+ *
+ * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS
+ *        installed.  Last I heard, the BIOS was actually done by Columbia
+ *        Data Products.  The BIOS is only used by this driver (and thus
+ *        by Linux) to identify the board; none of it can be executed under
+ *        Linux.
+ *
+ * WD7000-ASC: This is the original adapter board, with or without BIOS.
+ *        The board uses a WD33C93 or WD33C93A SBIC, which in turn is
+ *        controlled by an onboard Z80 processor.  The board interface
+ *        visible to the host CPU is defined effectively by the Z80's
+ *        firmware, and it is this firmware's revision level that is
+ *        determined and reported by this driver.  (The version of the
+ *        on-board BIOS is of no interest whatsoever.)  The host CPU has
+ *        no access to the SBIC; hence the fact that it is a WD33C93 is
+ *        also of no interest to this driver.
+ *
+ * WD7000-AX:
+ * WD7000-MX:
+ * WD7000-EX: These are newer versions of the WD7000-ASC.  The -ASC is
+ *        largely built from discrete components; these boards use more
+ *        integration.  The -AX is an ISA bus board (like the -ASC),
+ *        the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an
+ *        EISA bus board.
+ *
+ *  At the time of my documentation, the -?X boards were "future" products,
+ *  and were not yet available.  However, I vaguely recall that Thomas
+ *  Wuensche had an -AX, so I believe at least it is supported by this
+ *  driver.  I have no personal knowledge of either -MX or -EX boards.
+ *
+ *  P.S. Just recently, I've discovered (directly from WD and Future
+ *  Domain) that all but the WD7000-EX have been out of production for
+ *  two years now.  FD has production rights to the 7000-EX, and are
+ *  producing it under a new name, and with a new BIOS.  If anyone has
+ *  one of the FD boards, it would be nice to come up with a signature
+ *  for it.
+ *                                                           J.B. Jan 1994.
  */
 
 #include <stdarg.h>
@@ -17,6 +106,7 @@
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/sched.h>
+#include <linux/malloc.h>
 #include <asm/system.h>
 #include <asm/dma.h>
 #include <asm/io.h>
@@ -26,17 +116,179 @@
 #include "scsi.h"
 #include "hosts.h"
 
-/* #define DEBUG  */
+#define ANY2SCSI_INLINE    /* undef this to use old macros */
+#undef DEBUG
 
 #include "wd7000.h"
 
 
-#ifdef DEBUG
-#define DEB(x) x
-#else
-#define DEB(x)
-#endif
+/*
+ *  Mailbox structure sizes.
+ *  I prefer to keep the number of ICMBs much larger than the number of
+ *  OGMBs.  OGMBs are used very quickly by the driver to start one or
+ *  more commands, while ICMBs are used by the host adapter per command.
+ */
+#define OGMB_CNT	16
+#define ICMB_CNT	32
+
+/*
+ *  Scb's are shared by all active adapters.  So, if they all become busy,
+ *  callers may be made to wait in alloc_scbs for them to free.  That can
+ *  be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q.  If you'd
+ *  rather conserve memory, use a smaller number (> 0, of course) - things
+ *  will should still work OK.
+ */
+#define MAX_SCBS        32
+
+/*
+ *  WD7000-specific mailbox structure
+ *
+ */
+typedef volatile struct mailbox{
+  unchar status;
+  unchar scbptr[3];             /* SCSI-style - MSB first (big endian) */
+} Mailbox;
+
+/*
+ *  This structure should contain all per-adapter global data.  I.e., any
+ *  new global per-adapter data should put in here.
+ *
+ */
+typedef struct adapter {
+  int num;                          /* Index into Scsi_hosts array */
+  struct Scsi_Host *sh;             /* Pointer to Scsi_Host structure */
+  int iobase;                       /* This adapter's I/O base address */
+  int irq;                          /* This adapter's IRQ level */
+  int dma;                          /* This adapter's DMA channel */
+  struct {                          /* This adapter's mailboxes */
+    Mailbox ogmb[OGMB_CNT];             /* Outgoing mailboxes */
+    Mailbox icmb[ICMB_CNT];             /* Incoming mailboxes */
+  } mb;
+  int next_ogmb;                    /* to reduce contention at mailboxes */
+  unchar control;                   /* shadows CONTROL port value */
+  unchar rev1, rev2;                /* filled in by wd7000_revision */
+} Adapter;
 
+/*
+ * The following is set up by wd7000_detect, and used thereafter by
+ * wd7000_intr_handle to map the irq level to the corresponding Adapter.
+ * Note that if request_irq instead of irqaction to allocate the IRQ,
+ * or if SA_INTERRUPT is not used, wd7000_intr_handle must be changed 
+ * to pick up the IRQ level correctly.
+ */
+Adapter *irq2host[16] = {NULL};  /* Possible IRQs are 0-15 */
+
+/*
+ *  Standard Adapter Configurations - used by wd7000_detect
+ */
+typedef struct {
+  const void *bios;             /* (linear) base address for ROM BIOS */
+  int iobase;                   /* I/O ports base address */
+  int irq;                      /* IRQ level */
+  int dma;                      /* DMA channel */
+} Config;
+
+static const Config configs[] = {
+  {(void *) 0xce000, 0x350, 15, 6},  /* defaults for single adapter */
+  {(void *) 0xc8000, 0x330, 11, 5},  /* defaults for second adapter */
+  {(void *) 0xd8000, 0x350, 15, 6},  /* Arghhh.... who added this ? */
+};
+#define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
+
+/*
+ *  The following list defines strings to look for in the BIOS that identify
+ *  it as the WD7000-FASST2 SST BIOS.  I suspect that something should be
+ *  added for the Future Domain version.
+ */
+typedef struct signature {
+    void    *sig;           /* String to look for */
+    unsigned ofs;           /* offset from BIOS base address */
+    unsigned len;           /* length of string */
+} Signature;
+
+static const Signature signatures[] = {
+  {"SSTBIOS",0x0000d,7}                  /* "SSTBIOS" @ offset 0x0000d */
+};
+#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
+
+
+/*
+ *  I/O Port Offsets and Bit Definitions
+ *  4 addresses are used.  Those not defined here are reserved.
+ */
+#define ASC_STAT        0       /* Status,  Read */
+#define ASC_COMMAND     0       /* Command, Write */
+#define ASC_INTR_STAT   1       /* Interrupt Status, Read */
+#define ASC_INTR_ACK    1       /* Acknowledge, Write */
+#define ASC_CONTROL     2       /* Control, Write */
+
+/* ASC Status Port
+ */
+#define INT_IM	        0x80		/* Interrupt Image Flag */
+#define CMD_RDY	        0x40		/* Command Port Ready */
+#define CMD_REJ	        0x20		/* Command Port Byte Rejected */
+#define ASC_INIT        0x10		/* ASC Initialized Flag */
+#define ASC_STATMASK    0xf0		/* The lower 4 Bytes are reserved */
+
+/* COMMAND opcodes
+ *
+ *  Unfortunately, I have no idea how to properly use some of these commands,
+ *  as the OEM manual does not make it clear.  I have not been able to use
+ *  enable/disable unsolicited interrupts or the reset commands with any
+ *  discernable effect whatsoever.  I think they may be related to certain
+ *  ICB commands, but again, the OEM manual doesn't make that clear.
+ */
+#define NO_OP             0     /* NO-OP toggles CMD_RDY bit in ASC_STAT */
+#define INITIALIZATION    1     /* initialization (10 bytes) */
+#define DISABLE_UNS_INTR  2     /* disable unsolicited interrupts */
+#define ENABLE_UNS_INTR   3     /* enable unsolicited interrupts */
+#define INTR_ON_FREE_OGMB 4     /* interrupt on free OGMB */
+#define SOFT_RESET        5     /* SCSI bus soft reset */
+#define HARD_RESET_ACK    6     /* SCSI bus hard reset acknowledge */
+#define START_OGMB        0x80  /* start command in OGMB (n) */
+#define SCAN_OGMBS        0xc0  /* start multiple commands, signature (n) */
+                                /*    where (n) = lower 6 bits */
+/* For INITIALIZATION:
+ */
+typedef struct initCmd {
+  unchar op;                   /* command opcode (= 1) */
+  unchar ID;                   /* Adapter's SCSI ID */
+  unchar bus_on;               /* Bus on time, x 125ns (see below) */
+  unchar bus_off;              /* Bus off time, ""         ""      */
+  unchar rsvd;                 /* Reserved */
+  unchar mailboxes[3];         /* Address of Mailboxes, MSB first  */
+  unchar ogmbs;                /* Number of outgoing MBs, max 64, 0,1 = 1 */
+  unchar icmbs;                /* Number of incoming MBs,   ""       ""   */
+} InitCmd;
+
+#define BUS_ON            64    /* x 125ns = 8000ns (BIOS default) */
+#define BUS_OFF           15    /* x 125ns = 1875ns (BIOS default) */
+ 
+/* Interrupt Status Port - also returns diagnostic codes at ASC reset
+ *
+ * if msb is zero, the lower bits are diagnostic status
+ * Diagnostics:
+ * 01	No diagnostic error occurred
+ * 02	RAM failure
+ * 03	FIFO R/W failed
+ * 04   SBIC register read/write failed
+ * 05   Initialization D-FF failed
+ * 06   Host IRQ D-FF failed
+ * 07   ROM checksum error
+ * Interrupt status (bitwise):
+ * 10NNNNNN   outgoing mailbox NNNNNN is free
+ * 11NNNNNN   incoming mailbox NNNNNN needs service
+ */
+#define MB_INTR	 0xC0		/* Mailbox Service possible/required */
+#define IMB_INTR 0x40		/* 1 Incoming / 0 Outgoing */
+#define MB_MASK  0x3f           /* mask for mailbox number */
+
+/* CONTROL port bits
+ */
+#define INT_EN	        0x08	/* Interrupt Enable	*/
+#define DMA_EN	        0x04	/* DMA Enable		*/
+#define SCSI_RES	0x02	/* SCSI Reset		*/
+#define ASC_RES	        0x01	/* ASC Reset		*/
 
 /*
    Driver data structures:
@@ -63,103 +315,327 @@
   indices need not be involved.
 */
 
-static struct {
-       struct wd_mailbox ogmb[OGMB_CNT]; 
-       struct wd_mailbox icmb[ICMB_CNT];
-} mb;
-static int next_ogmb = 0;   /* to reduce contention at mailboxes */
+/*
+ *  WD7000-specific scatter/gather element structure
+ */
+typedef struct sgb {
+    unchar len[3];
+    unchar ptr[3];              /* Also SCSI-style - MSB first */
+} Sgb;
+
+typedef struct scb {		/* Command Control Block 5.4.1 */
+  unchar op;			/* Command Control Block Operation Code */
+  unchar idlun;			/* op=0,2:Target Id, op=1:Initiator Id */
+				/* Outbound data transfer, length is checked*/
+				/* Inbound data transfer, length is checked */
+				/* Logical Unit Number */
+  unchar cdb[12];		/* SCSI Command Block */
+  volatile unchar status;       /* SCSI Return Status */
+  volatile unchar vue;		/* Vendor Unique Error Code */
+  unchar maxlen[3];		/* Maximum Data Transfer Length */
+  unchar dataptr[3];		/* SCSI Data Block Pointer */
+  unchar linkptr[3];		/* Next Command Link Pointer */
+  unchar direc;			/* Transfer Direction */
+  unchar reserved2[6];		/* SCSI Command Descriptor Block */
+                                /* end of hardware SCB */
+  Scsi_Cmnd *SCpnt;             /* Scsi_Cmnd using this SCB */
+  Sgb sgb[WD7000_SG];           /* Scatter/gather list for this SCB */
+  Adapter *host;                /* host adapter */
+  struct scb *next;             /* for lists of scbs */
+} Scb;
+
+/*
+ *  This driver is written to allow host-only commands to be executed.
+ *  These use a 16-byte block called an ICB.  The format is extended by the
+ *  driver to 18 bytes, to support the status returned in the ICMB and
+ *  an execution phase code.
+ *
+ *  There are other formats besides these; these are the ones I've tried
+ *  to use.  Formats for some of the defined ICB opcodes are not defined
+ *  (notably, get/set unsolicited interrupt status) in my copy of the OEM
+ *  manual, and others are ambiguous/hard to follow.
+ */
+#define ICB_OP_MASK             0x80  /* distinguishes scbs from icbs */
+#define ICB_OP_OPEN_RBUF        0x80  /* open receive buffer */
+#define ICB_OP_RECV_CMD         0x81  /* receive command from initiator */
+#define ICB_OP_RECV_DATA        0x82  /* receive data from initiator */
+#define ICB_OP_RECV_SDATA       0x83  /* receive data with status from init. */
+#define ICB_OP_SEND_DATA        0x84  /* send data with status to initiator */
+#define ICB_OP_SEND_STAT        0x86  /* send command status to initiator */
+                             /* 0x87 is reserved */
+#define ICB_OP_READ_INIT        0x88  /* read initialization bytes */
+#define ICB_OP_READ_ID          0x89  /* read adapter's SCSI ID */
+#define ICB_OP_SET_UMASK        0x8A  /* set unsolicited interrupt mask */
+#define ICB_OP_GET_UMASK        0x8B  /* read unsolicited interrupt mask */
+#define ICB_OP_GET_REVISION     0x8C  /* read firmware revision level */
+#define ICB_OP_DIAGNOSTICS      0x8D  /* execute diagnostics */
+#define ICB_OP_SET_EPARMS       0x8E  /* set execution parameters */
+#define ICB_OP_GET_EPARMS       0x8F  /* read execution parameters */
+
+typedef struct icbRecvCmd {
+  unchar op;
+  unchar IDlun;                 /* Initiator SCSI ID/lun */
+  unchar len[3];                /* command buffer length */
+  unchar ptr[3];                /* command buffer address */
+  unchar rsvd[7];               /* reserved */
+  volatile unchar vue;          /* vendor-unique error code */
+  volatile unchar status;       /* returned (icmb) status */
+  volatile unchar phase;        /* used by interrupt handler */
+} IcbRecvCmd;
+
+typedef struct icbSendStat {
+  unchar op;
+  unchar IDlun;                 /* Target SCSI ID/lun */
+  unchar stat;                  /* (outgoing) completion status byte 1 */
+  unchar rsvd[12];              /* reserved */
+  volatile unchar vue;          /* vendor-unique error code */
+  volatile unchar status;       /* returned (icmb) status */
+  volatile unchar phase;        /* used by interrupt handler */
+} IcbSendStat;
+
+typedef struct icbRevLvl {
+  unchar op;
+  volatile unchar primary;      /* primary revision level (returned) */
+  volatile unchar secondary;    /* secondary revision level (returned) */
+  unchar rsvd[12];              /* reserved */
+  volatile unchar vue;          /* vendor-unique error code */
+  volatile unchar status;       /* returned (icmb) status */
+  volatile unchar phase;        /* used by interrupt handler */
+} IcbRevLvl;
+
+typedef struct icbUnsMask {     /* I'm totally guessing here */
+  unchar op;
+  volatile unchar mask[14];     /* mask bits */
+#ifdef 0
+  unchar rsvd[12];              /* reserved */
+#endif
+  volatile unchar vue;          /* vendor-unique error code */
+  volatile unchar status;       /* returned (icmb) status */
+  volatile unchar phase;        /* used by interrupt handler */
+} IcbUnsMask;
+
+typedef struct icbDiag {
+  unchar op;
+  unchar type;                  /* diagnostics type code (0-3) */
+  unchar len[3];                /* buffer length */
+  unchar ptr[3];                /* buffer address */
+  unchar rsvd[7];               /* reserved */
+  volatile unchar vue;          /* vendor-unique error code */
+  volatile unchar status;       /* returned (icmb) status */
+  volatile unchar phase;        /* used by interrupt handler */
+} IcbDiag;
+
+#define ICB_DIAG_POWERUP        0     /* Power-up diags only */
+#define ICB_DIAG_WALKING        1     /* walking 1's pattern */
+#define ICB_DIAG_DMA            2     /* DMA - system memory diags */
+#define ICB_DIAG_FULL           3     /* do both 1 & 2 */
+
+typedef struct icbParms {
+  unchar op;
+  unchar rsvd1;                 /* reserved */
+  unchar len[3];                /* parms buffer length */
+  unchar ptr[3];                /* parms buffer address */
+  unchar idx[2];                /* index (MSB-LSB) */
+  unchar rsvd2[5];              /* reserved */
+  volatile unchar vue;          /* vendor-unique error code */
+  volatile unchar status;       /* returned (icmb) status */
+  volatile unchar phase;        /* used by interrupt handler */
+} IcbParms;
+
+typedef struct icbAny {
+  unchar op;
+  unchar data[14];              /* format-specific data */
+  volatile unchar vue;          /* vendor-unique error code */
+  volatile unchar status;       /* returned (icmb) status */
+  volatile unchar phase;        /* used by interrupt handler */
+} IcbAny;
+
+typedef union icb {
+  unchar op;                    /* ICB opcode */
+  IcbRecvCmd recv_cmd;          /* format for receive command */
+  IcbSendStat send_stat;        /* format for send status */
+  IcbRevLvl rev_lvl;            /* format for get revision level */
+  IcbDiag diag;                 /* format for execute diagnostics */
+  IcbParms eparms;              /* format for get/set exec parms */
+  IcbAny icb;                   /* generic format */
+  unchar data[18];
+} Icb;
+
 
+/*
+ *  Driver SCB structure pool.
+ *
+ *  The SCBs declared here are shared by all host adapters; hence, this
+ *  structure is not part of the Adapter structure.
+ */
 static Scb scbs[MAX_SCBS];
-static Scb *scbfree = NULL;
+static Scb *scbfree = NULL;      /* free list */
+static int freescbs = MAX_SCBS;  /* free list counter */
+
+/*
+ *  END of data/declarations - code follows.
+ */
+
+
+#ifdef ANY2SCSI_INLINE
+/*
+   Since they're used a lot, I've redone the following from the macros
+   formerly in wd7000.h, hopefully to speed them up by getting rid of
+   all the shifting (it may not matter; GCC might have done as well anyway).
+
+   xany2scsi and xscsi2int were not being used, and are no longer defined.
+   (They were simply 4-byte versions of these routines).
+*/
+
+typedef union {  /* let's cheat... */
+  int i;
+  unchar u[sizeof(int)];  /* the sizeof(int) makes it more portable */
+} i_u;
 
-static int wd7000_host = 0;
-static unchar controlstat = 0;
 
-static unchar rev_1 = 0, rev_2 = 0;  /* filled in by wd7000_revision */
+static inline void any2scsi( unchar *scsi, int any )
+{
+    *scsi++ = ((i_u) any).u[2];
+    *scsi++ = ((i_u) any).u[1];
+    *scsi++ = ((i_u) any).u[0];
+}
 
-#define wd7000_intr_ack()  outb(0,INTR_ACK)
 
-#define WAITnexttimeout 3000000
+static inline int scsi2int( unchar *scsi )
+{
+    i_u result;
 
+    result.i = 0;  /* clears unused bytes */
+    *(result.u+2) = *scsi++;
+    *(result.u+1) = *scsi++;
+      *(result.u) = *scsi++;
+    return result.i;
+}
+#else
+/*
+   These are the old ones - I've just moved them here...
+*/
+#undef any2scsi
+#define any2scsi(up, p)			\
+(up)[0] = (((unsigned long)(p)) >> 16);	        \
+(up)[1] = ((unsigned long)(p)) >> 8;		\
+(up)[2] = ((unsigned long)(p));
+
+#undef scsi2int
+#define scsi2int(up) ( (((unsigned long)*(up)) << 16) + \
+ (((unsigned long)(up)[1]) << 8) + ((unsigned long)(up)[2]) )
+#endif
 
-static inline void wd7000_enable_intr(void)
+    
+static inline void wd7000_enable_intr(Adapter *host)
 {
-    controlstat |= INT_EN;
-    outb(controlstat,CONTROL);
+    host->control |= INT_EN;
+    outb(host->control, host->iobase+ASC_CONTROL);
 }
 
 
-static inline void wd7000_enable_dma(void)
+static inline void wd7000_enable_dma(Adapter *host)
 {
-    controlstat |= DMA_EN;
-    outb(controlstat,CONTROL);
-    set_dma_mode(DMA_CH, DMA_MODE_CASCADE);
-    enable_dma(DMA_CH);
+    host->control |= DMA_EN;
+    outb(host->control,host->iobase+ASC_CONTROL);
+    set_dma_mode(host->dma, DMA_MODE_CASCADE);
+    enable_dma(host->dma);
 }
 
 
+#define WAITnexttimeout 200  /* 2 seconds */
+
 #define WAIT(port, mask, allof, noneof)					\
- { register WAITbits;							\
-   register WAITtimeout = WAITnexttimeout;				\
+ { register volatile unsigned WAITbits; 				\
+   register unsigned long WAITtimeout = jiffies + WAITnexttimeout;	\
    while (1) {								\
      WAITbits = inb(port) & (mask);					\
      if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
        break;                                                         	\
-     if (--WAITtimeout == 0) goto fail;					\
+     if (jiffies > WAITtimeout) goto fail;				\
    }									\
  }
 
 
 static inline void delay( unsigned how_long )
 {
-     unsigned long time = jiffies + how_long;
+     register unsigned long time = jiffies + how_long;
 
      while (jiffies < time);
 }
 
 
-static inline int command_out(unchar *cmdp, int len)
+static inline int command_out(Adapter *host, unchar *cmd, int len)
 {
-  if(len == 1) {
-    while(1==1){
-      WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
-      cli();
-      if(!(inb(ASC_STAT) & CMD_RDY)) {sti(); continue;}
-      outb(*cmdp, COMMAND);
-      sti();
-      return 1;
-    }
-  } else {
-    cli();
+    WAIT(host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0);
     while (len--)  {
-      WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
-      outb(*cmdp++, COMMAND);
+        do  {
+	    outb(*cmd, host->iobase+ASC_COMMAND);
+	    WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
+	}  while (inb(host->iobase+ASC_STAT) & CMD_REJ);
+	cmd++;
     }
-    sti();
-  }
     return 1;
 
 fail:
-    sti();
-    printk("wd7000_out WAIT failed(%d): ", len+1);
+    printk("wd7000 command_out: WAIT failed(%d)\n", len+1);
     return 0;
 }
 
-static inline Scb *alloc_scb(void)
+
+/*
+ *  This version of alloc_scbs is in preparation for supporting multiple
+ *  commands per lun and command chaining, by queueing pending commands.
+ *  We will need to allocate Scbs in blocks since they will wait to be
+ *  executed so there is the possibility of deadlock otherwise.
+ *  Also, to keep larger requests from being starved by smaller requests,
+ *  we limit access to this routine with an internal busy flag, so that
+ *  the satisfiability of a request is not dependent on the size of the
+ *  request.
+ */
+static inline Scb *alloc_scbs(int needed)
 {
-    Scb *scb;
-    unsigned long flags;
+    register Scb *scb, *p;
+    register unsigned long flags;
+    register unsigned long timeout = jiffies + WAITnexttimeout;
+    register unsigned long now;
+    static int busy = 0;
+    int i;
 
+    if (needed <= 0)  return NULL;  /* sanity check */
+
     save_flags(flags);
     cli();
+    while (busy)  { /* someone else is allocating */
+        sti();
+	now = jiffies;  while (jiffies == now)  /* wait a jiffy */;
+	cli();
+    }
+    busy = 1;          /* not busy now; it's our turn */
 
-    if (scbfree == NULL)  {
-        panic("wd7000: can't allocate free SCB.\n");
-	restore_flags(flags);
-	return NULL;
+    while (freescbs < needed)  {
+        timeout = jiffies + WAITnexttimeout;
+	do {
+	    sti();
+	    now = jiffies;   while (jiffies == now) /* wait a jiffy */;
+	    cli();
+	}  while (freescbs < needed && jiffies <= timeout);
+	/*
+	 *  If we get here with enough free Scbs, we can take them.
+	 *  Otherwise, we timed out and didn't get enough.
+	 */
+	if (freescbs < needed)  {
+	    busy = 0;
+	    panic("wd7000: can't get enough free SCBs.\n");
+	    restore_flags(flags);
+	    return NULL;
+	}
     }
-    scb = scbfree;  scbfree = scb->next;
-    memset(scb, 0, sizeof(Scb));  scb->next = NULL;
+    scb = scbfree;  freescbs -= needed;
+    for (i = 0; i < needed; i++)  { p = scbfree;  scbfree = p->next; }
+    p->next = NULL;
+    
+    busy = 0;   /* we're done */
 
     restore_flags(flags);
 
@@ -169,13 +645,14 @@
 
 static inline void free_scb( Scb *scb )
 {
-    unsigned long flags;
+    register unsigned long flags;
 
     save_flags(flags);
     cli();
 
     memset(scb, 0, sizeof(Scb));
     scb->next = scbfree;  scbfree = scb;
+    freescbs++;
 
     restore_flags(flags);
 }
@@ -190,62 +667,74 @@
     cli();
 
     scbfree = &(scbs[0]);
-    for (i = 0;  i < MAX_SCBS-1;  i++)  scbs[i].next = &(scbs[i+1]);
+    memset(scbs, 0, sizeof(scbs));
+    for (i = 0;  i < MAX_SCBS-1;  i++)  {
+      scbs[i].next = &(scbs[i+1]);  scbs[i].SCpnt = NULL;
+    }
     scbs[MAX_SCBS-1].next = NULL;
+    scbs[MAX_SCBS-1].SCpnt = NULL;
 
     restore_flags(flags);
 }    
     
 
-static int mail_out( Scb *scbptr )
+static int mail_out( Adapter *host, Scb *scbptr )
 /*
  *  Note: this can also be used for ICBs; just cast to the parm type.
  */
 {
-    int i, ogmb;
-    unsigned char start_cmd;
-    unsigned long flags;
-
-    DEB(printk("wd7000_scb_out: %06x");)
-
+    register int i, ogmb;
+    register unsigned long flags;
+    unchar start_ogmb;
+    Mailbox *ogmbs = host->mb.ogmb;
+    int *next_ogmb = &(host->next_ogmb);
+#ifdef DEBUG
+    printk("wd7000 mail_out: %06x",(unsigned int) scbptr);
+#endif
     /* We first look for a free outgoing mailbox */
     save_flags(flags);
     cli();
-    ogmb = next_ogmb;
+    ogmb = *next_ogmb;
     for (i = 0; i < OGMB_CNT; i++) {
-	if (mb.ogmb[ogmb].status == 0)  {
-	    DEB(printk(" using OGMB %x",ogmb));
-	    mb.ogmb[ogmb].status = 1;
-	    any2scsi(mb.ogmb[ogmb].scbptr, scbptr);
+	if (ogmbs[ogmb].status == 0)  {
+#ifdef DEBUG
+	    printk(" using OGMB %x",ogmb);
+#endif
+	    ogmbs[ogmb].status = 1;
+	    any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
 
-	    next_ogmb = (ogmb+1) % OGMB_CNT;
+	    *next_ogmb = (ogmb+1) % OGMB_CNT;
 	    break;
 	}  else
 	    ogmb = (++ogmb) % OGMB_CNT;
     }
     restore_flags(flags);
-    DEB(printk(", scb is %x",scbptr);)
-
+#ifdef DEBUG
+    printk(", scb is %x",(unsigned int) scbptr);
+#endif
     if (i >= OGMB_CNT) {
-        DEB(printk(", no free OGMBs.\n");)
-	/* Alternatively, issue "interrupt on free OGMB", and sleep... */
-        return 0;
+        /*
+	 *  Alternatively, we might issue the "interrupt on free OGMB",
+	 *  and sleep, but it must be ensured that it isn't the init
+	 *  task running.  Instead, this version assumes that the caller
+	 *  will be persistent, and try again.  Since it's the adapter
+	 *  that marks OGMB's free, waiting even with interrupts off
+	 *  should work, since they are freed very quickly in most cases.
+	 */
+        #ifdef DEBUG
+        printk(", no free OGMBs.\n");
+#endif
+	return 0;
     }
 
-    start_cmd = START_OGMB | ogmb;
-
-    wd7000_enable_intr(); 
-    do  {
-        command_out(&start_cmd, 1);
-	WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
-    }  while (inb(ASC_STAT) & CMD_REJ);
+    wd7000_enable_intr(host); 
 
-    DEB(printk(", awaiting interrupt.\n");)
+    start_ogmb = START_OGMB | ogmb;
+    command_out( host, &start_ogmb, 1 );
+#ifdef DEBUG
+    printk(", awaiting interrupt.\n");
+#endif
     return 1;
-
-fail:
-    DEB(printk(", WAIT timed out.\n");)
-    return 0;
 }
 
 
@@ -300,135 +789,159 @@
 
 static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
 {
-    DEB(printk("wd7000_scsi_done: %06x\n",SCpnt);)
+#ifdef DEBUG
+    printk("wd7000_scsi_done: %06x\n",(unsigned int) SCpnt);
+#endif
     SCpnt->SCp.phase = 0;
 }
 
 
+#define wd7000_intr_ack(host)  outb(0,host->iobase+ASC_INTR_ACK)
+
 void wd7000_intr_handle(int irq)
 {
-    int flag, icmb, errstatus, icmb_status;
-    int host_error, scsi_error;
-    Scb *scb;             /* for SCSI commands */
-    unchar *icb;          /* for host commands */
-    Scsi_Cmnd *SCpnt;
-
-    flag = inb(INTR_STAT);
-    DEB(printk("wd7000_intr_handle: intr stat = %02x",flag);)
-
-    if (!(inb(ASC_STAT)&0x80)){ 
-	DEB(printk("\nwd7000_intr_handle: phantom interrupt...\n");)
-	wd7000_intr_ack();
+#ifdef 0
+    /*
+     * Use irqp as the parm, and the following declaration, if request_irq
+     * is used or if SA_INTERRUPT is not used.
+     */
+    register int irq = *(((int *)irqp)-2);
+#endif
+    register int flag, icmb, errstatus, icmb_status;
+    register int host_error, scsi_error;
+    register Scb *scb;             /* for SCSI commands */
+    register IcbAny *icb;          /* for host commands */
+    register Scsi_Cmnd *SCpnt;
+    Adapter *host = irq2host[irq];  /* This MUST be set!!! */
+    Mailbox *icmbs = host->mb.icmb;
+
+#ifdef DEBUG
+    printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host);
+#endif
+
+    flag = inb(host->iobase+ASC_INTR_STAT);
+#ifdef DEBUG
+    printk("wd7000_intr_handle: intr stat = %02x\n",flag);
+#endif
+
+    if (!(inb(host->iobase+ASC_STAT) & INT_IM))  {
+        /* NB: these are _very_ possible if IRQ 15 is being used, since
+	   it's the "garbage collector" on the 2nd 8259 PIC.  Specifically,
+	   any interrupt signal into the 8259 which can't be identified
+	   comes out as 7 from the 8259, which is 15 to the host.  Thus, it
+	   is a good thing the WD7000 has an interrupt status port, so we
+	   can sort these out.  Otherwise, electrical noise and other such
+	   problems would be indistinguishable from valid interrupts...
+	*/
+#ifdef DEBUG 
+	printk("wd7000_intr_handle: phantom interrupt...\n");
+#endif
+	wd7000_intr_ack(host);
 	return; 
     }
 
-    /* check for an incoming mailbox */
-    if ((flag & 0x40) == 0) {
-        /*  for a free OGMB - need code for this case... */
-        DEB(printk("wd7000_intr_handle: free outgoing mailbox\n");)
-        wd7000_intr_ack();
-	return;
-    }
-    /* The interrupt is for an incoming mailbox */
-    icmb = flag & 0x3f;
-    scb = (struct scb *) scsi2int(mb.icmb[icmb].scbptr);
-    icmb_status = mb.icmb[icmb].status;
-    mb.icmb[icmb].status = 0;
-
-#ifdef DEBUG
-    printk(" ICMB %d posted for SCB/ICB %06x, status %02x, vue %02x",
- 	   icmb, scb, icmb_status, scb->vue );
-#endif
-
-    if (!(scb->op & 0x80))  {   /* an SCB is done */
-        SCpnt = scb->SCpnt;
-	if (--(SCpnt->SCp.phase) <= 0)  {  /* all scbs for SCpnt are done */
-	    host_error = scb->vue | (icmb_status << 8);
-	    scsi_error = scb->status;
-	    errstatus = make_code(host_error,scsi_error);    
-	    SCpnt->result = errstatus;
-
- 	    if (SCpnt->host_scribble != NULL)
- 	        scsi_free(SCpnt->host_scribble,WD7000_SCRIBBLE);
- 	    free_scb(scb);
-
-	    SCpnt->scsi_done(SCpnt);
-	}
-    }  else  {    /* an ICB is done */
-        icb = (unchar *) scb;
-        icb[ICB_STATUS] = icmb_status;
-	icb[ICB_PHASE] = 0;
+    if (flag & MB_INTR)  {
+        /* The interrupt is for a mailbox */
+	if (!(flag & IMB_INTR)) {
+#ifdef DEBUG
+	    printk("wd7000_intr_handle: free outgoing mailbox");
+#endif
+	    /*
+	     * If sleep_on() and the "interrupt on free OGMB" command are
+	     * used in mail_out(), wake_up() should correspondingly be called
+	     * here.  For now, we don't need to do anything special.
+	     */
+	    wd7000_intr_ack(host);
+	    return;
+	}  else  {
+	    /* The interrupt is for an incoming mailbox */
+	    icmb = flag & MB_MASK;
+	    icmb_status = icmbs[icmb].status;
+	    if (icmb_status & 0x80)  {  /* unsolicited - result in ICMB */
+#ifdef DEBUG
+ 	        printk("wd7000_intr_handle: unsolicited interrupt %02xh\n",
+		       icmb_status);
+#endif
+	        wd7000_intr_ack(host);
+	        return;
+	    }
+	    scb = (struct scb *) scsi2int((unchar *)icmbs[icmb].scbptr);
+	    icmbs[icmb].status = 0;
+	    if (!(scb->op & ICB_OP_MASK))  {   /* an SCB is done */
+	        SCpnt = scb->SCpnt;
+		if (--(SCpnt->SCp.phase) <= 0)  {  /* all scbs are done */
+		    host_error = scb->vue | (icmb_status << 8);
+		    scsi_error = scb->status;
+		    errstatus = make_code(host_error,scsi_error);    
+		    SCpnt->result = errstatus;
+
+		    free_scb(scb);
+
+		    SCpnt->scsi_done(SCpnt);
+		}
+	    }  else  {    /* an ICB is done */
+	        icb = (IcbAny *) scb;
+		icb->status = icmb_status;
+		icb->phase  = 0;
+	    }
+	}  /* incoming mailbox */
     }
 
-    wd7000_intr_ack();
-    DEB(printk(".\n");)
+    wd7000_intr_ack(host);
     return;
 }
 
 
 int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
 {
-    Scb *scb;
-    Sgb *sgb;
-    unchar *cdb;
-    unchar idlun;
-    short cdblen;
+    register Scb *scb;
+    register Sgb *sgb;
+    register unchar *cdb = (unchar *) SCpnt->cmnd;
+    register unchar idlun;
+    register short cdblen;
+    Adapter *host = (Adapter *) SCpnt->host->hostdata;
 
-    cdb = (unchar *) SCpnt->cmnd;
-    cdblen = COMMAND_SIZE(*cdb);
+    cdblen = COMMAND_SIZE(cdb[0]);
     idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
     SCpnt->scsi_done = done;
     SCpnt->SCp.phase = 1;
-    scb = alloc_scb();
+    scb = alloc_scbs(1);
     scb->idlun = idlun;
     memcpy(scb->cdb, cdb, cdblen);
     scb->direc = 0x40;		/* Disable direction check */
+
     scb->SCpnt = SCpnt;         /* so we can find stuff later */
-    SCpnt->host_scribble = NULL;
-    DEB(printk("request_bufflen is %x, bufflen is %x\n",\
-        SCpnt->request_bufflen, SCpnt->bufflen);)
+    SCpnt->host_scribble = (unchar *) scb;
+    scb->host = host;
 
     if (SCpnt->use_sg)  {
         struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
         unsigned i;
 
-        if (scsi_hosts[wd7000_host].sg_tablesize <= 0)  {
+        if (SCpnt->host->sg_tablesize == SG_NONE)  {
 	    panic("wd7000_queuecommand: scatter/gather not supported.\n");
 	}
 #ifdef DEBUG
  	printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg);
 #endif
-  	/*
- 	    Allocate memory for a scatter/gather-list in wd7000 format.
- 	    Save the pointer at host_scribble.
-  	*/
-#ifdef DEBUG
- 	if (SCpnt->use_sg > WD7000_SG)
- 	    panic("WD7000: requesting too many scatterblocks\n");
-#endif
- 	SCpnt->host_scribble = (unsigned char *) scsi_malloc(WD7000_SCRIBBLE);
-	sgb = (Sgb *) SCpnt->host_scribble;
- 	if (sgb == NULL)
-            panic("wd7000_queuecommand: scsi_malloc() failed.\n");
 
+	sgb = scb->sgb;
  	scb->op = 1;
- 	any2scsi(scb->dataptr, sgb);
+ 	any2scsi(scb->dataptr, (int) sgb);
  	any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) );
 
 	for (i = 0;  i < SCpnt->use_sg;  i++)  {
- 	    any2scsi(sgb->ptr, sg[i].address);
- 	    any2scsi(sgb->len, sg[i].length);
- 	    sgb++;
+ 	    any2scsi(sgb[i].ptr, (int) sg[i].address);
+ 	    any2scsi(sgb[i].len, sg[i].length);
         }
- 	DEB(printk("Using %d bytes for %d scatter/gather blocks\n",\
- 	    scsi2int(scb->maxlen), SCpnt->use_sg);)
     }  else  {
 	scb->op = 0;
-	any2scsi(scb->dataptr, SCpnt->request_buffer);
+	any2scsi(scb->dataptr, (int) SCpnt->request_buffer);
 	any2scsi(scb->maxlen, SCpnt->request_bufflen);
     }
+    while (!mail_out(host, scb)) /* keep trying */;
 
-    return mail_out(scb);
+    return 1;
 }
 
 
@@ -442,192 +955,292 @@
 }
 
 
-int wd7000_init(void)
-{   int i;
-    unchar init_block[] = {
-        INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT
+int wd7000_diagnostics( Adapter *host, int code )
+{
+    static IcbDiag icb = {ICB_OP_DIAGNOSTICS};
+    static unchar buf[256];
+    unsigned long timeout;
+
+    icb.type = code;
+    any2scsi(icb.len, sizeof(buf));
+    any2scsi(icb.ptr, (int) &buf);
+    icb.phase = 1;
+    /*
+     * This routine is only called at init, so there should be OGMBs
+     * available.  I'm assuming so here.  If this is going to
+     * fail, I can just let the timeout catch the failure.
+     */
+    mail_out(host, (struct scb *) &icb);
+    timeout = jiffies + WAITnexttimeout;  /* wait up to 2 seconds */
+    while (icb.phase && jiffies < timeout) /* wait for completion */;
+
+    if (icb.phase)  {
+        printk("wd7000_diagnostics: timed out.\n");
+	return 0;
+    }
+    if (make_code(icb.vue|(icb.status << 8),0))  {
+        printk("wd7000_diagnostics: failed (%02x,%02x)\n",
+	       icb.vue, icb.status);
+	return 0;
+    }
+
+    return 1;
+}
+
+
+int wd7000_init( Adapter *host )
+{
+    InitCmd init_cmd = {
+        INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0,0,0, OGMB_CNT, ICMB_CNT
     };
+    struct sigaction sa = {wd7000_intr_handle, 0, SA_INTERRUPT, NULL};
+    int diag;
 
-    /* Reset the adapter. */
-    outb(SCSI_RES|ASC_RES, CONTROL);
-    delay(1);  /* reset pulse: this is 10ms, only need 25us */
-    outb(0,CONTROL);  controlstat = 0;
     /*
-       Wait 2 seconds, then expect Command Port Ready.
-
-       I suspect something else needs to be done here, but I don't know
-       what.  The OEM doc says power-up diagnostics take 2 seconds, and
-       indeed, SCSI commands submitted before then will time out, but
-       none of what follows seems deterred by _not_ waiting 2 secs.
+       Reset the adapter - only.  The SCSI bus was initialized at power-up,
+       and we need to do this just so we control the mailboxes, etc.
     */
-    delay(200);
-
-    WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
-    DEB(printk("wd7000_init: Power-on Diagnostics finished\n");)
-    if (((i=inb(INTR_STAT)) != 1) && (i != 7)) {
-	panic("wd7000_init: Power-on Diagnostics error\n"); 
-	return 0;
+    outb(ASC_RES, host->iobase+ASC_CONTROL);
+    delay(1);  /* reset pulse: this is 10ms, only need 25us */
+    outb(0,host->iobase+ASC_CONTROL);
+    host->control = 0;   /* this must always shadow ASC_CONTROL */
+    WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
+
+    if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1)  {
+        printk("wd7000_init: ");
+        switch (diag)  {
+	case 2:
+	  printk("RAM failure.\n");
+	  break;
+	case 3:
+	  printk("FIFO R/W failed\n");
+	  break;
+	case 4:
+	  printk("SBIC register R/W failed\n");
+	  break;
+	case 5:
+	  printk("Initialization D-FF failed.\n");
+	  break;
+	case 6:
+	  printk("Host IRQ D-FF failed.\n");
+	  break;
+	case 7:
+	  printk("ROM checksum error.\n");
+	  break;
+	default:
+	  printk("diagnostic code %02Xh received.\n", diag);
+	  break;
+	}
+        return 0;
     }
     
     /* Clear mailboxes */
-    memset(&mb,0,sizeof (mb));
-    /* Set up SCB free list */
-    init_scbs();
+    memset(&(host->mb), 0, sizeof(host->mb));
 
-    /* Set up init block */
-    any2scsi(init_block+5,&mb);
     /* Execute init command */
-    if (!command_out(init_block,sizeof(init_block)))  {
-	panic("WD-7000 Initialization failed.\n"); 
+    any2scsi((unchar *) &(init_cmd.mailboxes), (int) &(host->mb));
+    if (!command_out(host, (unchar *) &init_cmd, sizeof(init_cmd)))  {
+	printk("wd7000_init: adapter initialization failed.\n"); 
 	return 0;
     }
-    
-    /* Wait until init finished */
-    WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
-    outb(DISABLE_UNS_INTR, COMMAND); 
-    WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
-
-    /* Enable Interrupt and DMA */
-    if (request_irq(IRQ_LVL, wd7000_intr_handle)) {
-      panic("Unable to allocate IRQ for WD-7000.\n");
-      return 0;
-    };
-    if(request_dma(DMA_CH)) {
-      panic("Unable to allocate DMA channel for WD-7000.\n");
-      free_irq(IRQ_LVL);
-      return 0;
-    };
-    wd7000_enable_dma();
-    wd7000_enable_intr();
+    WAIT(host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0);
+
+    if (irqaction(host->irq, &sa))  {
+        printk("wd7000_init: can't get IRQ %d.\n", host->irq);
+	return 0;
+    }
+    if (request_dma(host->dma))  {
+        printk("wd7000_init: can't get DMA channel %d.\n", host->dma);
+	free_irq(host->irq);
+	return 0;
+    }
+    wd7000_enable_dma(host);
+    wd7000_enable_intr(host);
+
+    if (!wd7000_diagnostics(host,ICB_DIAG_FULL))  {
+        free_dma(host->dma);
+        free_irq(host->irq);
+        return 0;
+    }
 
-    printk("WD-7000 initialized.\n");
     return 1;
+
   fail:
+    printk("wd7000_init: WAIT timed out.\n"); 
     return 0;					/* 0 = not ok */
 }
 
 
-void wd7000_revision(void)
+void wd7000_revision(Adapter *host)
 {
-    volatile unchar icb[ICB_LEN] = {0x8c};  /* read firmware revision level */
-
-    icb[ICB_PHASE] = 1;
-    mail_out( (struct scb *) icb );
-    while (icb[ICB_PHASE]) /* wait for completion */;
-    rev_1 = icb[1];
-    rev_2 = icb[2];
+    static IcbRevLvl icb = {ICB_OP_GET_REVISION};
 
+    icb.phase = 1;
     /*
-        For boards at rev 7.0 or later, enable scatter/gather.
-    */
-    if (rev_1 >= 7)  scsi_hosts[wd7000_host].sg_tablesize = WD7000_SG;
+     * Like diagnostics, this is only done at init time, in fact, from
+     * wd7000_detect, so there should be OGMBs available.  If it fails,
+     * the only damage will be that the revision will show up as 0.0,
+     * which in turn means that scatter/gather will be disabled.
+     */
+    mail_out(host, (struct scb *) &icb);
+    while (icb.phase) /* wait for completion */;
+    host->rev1 = icb.primary;
+    host->rev2 = icb.secondary;
 }
 
 
-static const char *wd_bases[] = {(char *)0xce000,(char *)0xd8000};
-
-typedef struct {
-    char * signature;
-    unsigned offset;
-    unsigned length;
-} Signature;
-
-static const Signature signatures[] = {{"SSTBIOS",0xd,0x7}};
-
-#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
-
-
 int wd7000_detect(int hostnum)
 /* 
- *  return non-zero on detection
+ *  Returns the number of adapters this driver is supporting.
+ *
+ *  The source for hosts.c says to wait to call scsi_register until 100%
+ *  sure about an adapter.  We need to do it a little sooner here; we
+ *  need the storage set up by scsi_register before wd7000_init, and
+ *  changing the location of an Adapter structure is more trouble than
+ *  calling scsi_unregister.
+ *
  */
 {
-    int i,j;
-    char const *base_address = NULL;
+    int i,j, present = 0;
+    const Config *cfg;
+    const Signature *sig;
+    Adapter *host = NULL;
+    struct Scsi_Host *sh;
+
+    /* Set up SCB free list, which is shared by all adapters */
+    init_scbs();
 
-    if(check_region(IO_BASE, 4)) return 0;  /* IO ports in use */
-    for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){
-	for(j=0;j<NUM_SIGNATURES;j++){
-	    if(!memcmp((void *)(wd_bases[i] + signatures[j].offset),
-		(void *) signatures[j].signature,signatures[j].length)){
-		    base_address=wd_bases[i];
-		    printk("WD-7000 detected.\n");
-	    }	
+    cfg = configs;
+    for (i = 0; i < NUM_CONFIGS; i++)  {
+        sig = signatures;
+	for (j = 0; j < NUM_SIGNATURES; j++)  {
+	    if (!memcmp(cfg->bios+sig->ofs, sig->sig, sig->len))  {
+	        /* matched this one */
+#ifdef DEBUG
+		printk("WD-7000 SST BIOS detected at %04X: checking...\n",
+		       (int) cfg->bios);
+#endif
+		/*
+		 *  We won't explicitly test the configuration (in this
+		 *  version); instead, we'll just see if it works to
+		 *  setup the adapter; if it does, we'll use it.
+		 */
+		if (check_region(cfg->iobase, 4))  {  /* ports in use */
+		    printk("IO %xh already in use.\n", host->iobase);
+		    continue;
+		}
+		/*
+		 *  We register here, to get a pointer to the extra space,
+		 *  which we'll use as the Adapter structure (host) for
+		 *  this adapter.  It is located just after the registered
+		 *  Scsi_Host structure (sh), and is located by the empty
+		 *  array hostdata.
+		 */
+		sh = scsi_register( hostnum, sizeof(Adapter) );
+		host = (Adapter *) sh->hostdata;
+#ifdef DEBUG
+		printk("wd7000_detect: adapter allocated at %06x\n",
+		       (int)host);
+#endif
+		memset( host, 0, sizeof(Adapter) );
+	        host->num = hostnum;  host->sh = sh;
+		host->irq = cfg->irq;
+		host->iobase = cfg->iobase;
+		host->dma = cfg->dma;
+		irq2host[host->irq] = host;
+
+		if (!wd7000_init(host))  {  /* Initialization failed */
+		    scsi_unregister( sh, sizeof(Adapter) );
+		    continue;
+		}
+
+		/*
+		 *  OK from here - we'll use this adapter/configuration.
+		 */
+		wd7000_revision(host);   /* important for scatter/gather */
+
+		printk("Western Digital WD-7000 (%d.%d) ",
+		       host->rev1, host->rev2);
+                printk("using IO %xh IRQ %d DMA %d.\n",
+		       host->iobase, host->irq, host->dma);
+
+		snarf_region(host->iobase, 4); /* Register our ports */
+		/*
+		 *  For boards before rev 6.0, scatter/gather isn't supported.
+		 */
+		if (host->rev1 < 6)  sh->sg_tablesize = SG_NONE;
+
+		present++;                      /* count it */
+		break;                          /* don't try any more sigs */
+	    }
+	    sig++;  /* try next signature with this configuration */
 	}
+	cfg++;      /* try next configuration */
     }
-    if (base_address == NULL) return 0;
 
-    snarf_region(IO_BASE, 4); /* Register our ports */
-    /* Store our host number */
-    wd7000_host = hostnum;
-
-    wd7000_init();    
-    wd7000_revision();  /* will set scatter/gather by rev level */
-
-    return 1;
+    return present;
 }
 
 
-
-static void wd7000_append_info( char *info, const char *fmt, ... )
 /*
- *  This is just so I can use vsprintf...
+ *  I have absolutely NO idea how to do an abort with the WD7000...
  */
-{
-    va_list args;
-    extern int vsprintf(char *buf, const char *fmt, va_list args);
-
-    va_start(args, fmt);
-    vsprintf(info, fmt, args);
-    va_end(args);
-
-    return;
-}
-
-
-const char *wd7000_info(void)
-{
-    static char info[80] = "Western Digital WD-7000, Firmware Revision ";
-
-    wd7000_revision();
-    wd7000_append_info( info+strlen(info), "%d.%d.\n", rev_1, rev_2 );
-
-    return info;
-}
-
 int wd7000_abort(Scsi_Cmnd * SCpnt, int i)
 {
 #ifdef DEBUG
-    printk("wd7000_abort: Scsi_Cmnd = 0x%08x, code = %d ", SCpnt, i);
+    printk("wd7000_abort: Scsi_Cmnd = 0x%06x, code = %d ", (int) SCpnt, i);
     printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun);
-    {  int j;  unchar *cdbj = (unchar *) SCpnt->cmnd;
-       for (j=0; j < COMMAND_SIZE(*cdbj);  j++)  printk(" %02x", *(cdbj++));
-       printk(" result %08x\n", SCpnt->result);
+    {
+        int j;  unchar *cdbj = (unchar *) SCpnt->cmnd;
+	for (j=0; j < COMMAND_SIZE(*cdbj);  j++)  printk(" %02x", *(cdbj++));
+	printk(" result %08x\n", SCpnt->result);
     }
 #endif
     return 0;
 }
 
 
-/* We do not implement a reset function here, but the upper level code assumes
-   that it will get some kind of response for the command in SCpnt.  We must
-   oblige, or the command will hang the scsi system */
-
+/*
+ *  I also have no idea how to do a reset...
+ */
 int wd7000_reset(Scsi_Cmnd * SCpnt)
 {
 #ifdef DEBUG
-    printk("wd7000_reset\n");
+    printk("wd7000_reset: Scsi_Cmnd = 0x%06x ", (int) SCpnt);
+    if (SCpnt)  {
+        printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun);
+	{
+	    int j;  unchar *cdbj = (unchar *) SCpnt->cmnd;
+	    for (j=0; j < COMMAND_SIZE(*cdbj);  j++)
+	        printk(" %02x", *(cdbj++));
+	    printk(" result %08x", SCpnt->result);
+	}
+    }
+    printk("\n");
 #endif
     if (SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
     return 0;
 }
 
 
-int wd7000_biosparam(int size, int dev, int* ip)
+/*
+ *  The info routine in the WD7000 structure isn't per-adapter, so it can't
+ *  really return any useful information about an adapter.  Because of this,
+ *  I'm no longer using it to return rev. level.
+ */
+const char *wd7000_info(void)
+{
+    static char info[] = "Western Digital WD-7000";
+    return info;
+}
+
+
 /*
- *  This is borrowed directly from aha1542.c, but my disks are organized
- *   this way, so I think it will work OK.
+ *  This was borrowed directly from aha1542.c, but my disks are organized
+ *  this way, so I think it will work OK.  Someone who is ambitious can
+ *  borrow a newer or more complete version from another driver.
  */
+int wd7000_biosparam(int size, int dev, int* ip)
 {
   ip[0] = 64;
   ip[1] = 32;
@@ -635,4 +1248,3 @@
 /*  if (ip[2] >= 1024) ip[2] = 1024; */
   return 0;
 }
-
diff -u --recursive --new-file v1.1.11/linux/drivers/scsi/wd7000.h linux/drivers/scsi/wd7000.h
--- v1.1.11/linux/drivers/scsi/wd7000.h	Wed Dec  1 14:44:16 1993
+++ linux/drivers/scsi/wd7000.h	Wed Apr  6 21:16:39 1994
@@ -4,169 +4,14 @@
  *
  * Header file for the WD-7000 driver for Linux
  *
- * $Log: $
- * Revision 1.1  1992/07/24  06:27:38  root
- * Initial revision
+ * John Boyd <boyd@cis.ohio-state.edu>  Jan 1994:
+ * This file has been reduced to only the definitions needed for the
+ * WD7000 host structure.
  *
- * Revision 1.1  1992/07/05  08:32:32  root
- * Initial revision
- *
- * Revision 1.1  1992/05/15  18:38:05  root
- * Initial revision
- *
- * Revision 1.1  1992/04/02  03:23:13  drew
- * Initial revision
- *
- * Revision 1.3  1992/01/27  14:46:29  tthorn
- * *** empty log message ***
- *
  */
 
 #include <linux/types.h>
 
-#undef STATMASK
-#undef CONTROL
-
-#define IO_BASE 	0x350
-#define IRQ_LVL 	15
-#define DMA_CH  	6
-#define OGMB_CNT	8
-#define ICMB_CNT	16
-
-/* I/O Port interface 4.2 */
-/* READ */
-#define ASC_STAT IO_BASE
-#define INT_IM	0x80		/* Interrupt Image Flag */
-#define CMD_RDY	0x40		/* Command Port Ready */
-#define CMD_REJ	0x20		/* Command Port Byte Rejected */
-#define ASC_INI	0x10		/* ASC Initialized Flag */
-#define STATMASK 0xf0		/* The lower 4 Bytes are reserved */
-
-/* This register serves two purposes
- * Diagnostics error code
- * Interrupt Status
- */
-#define INTR_STAT ASC_STAT+1
-#define ANYINTR	0x80		/* Mailbox Service possible/required */
-#define IMB	0x40		/* 1 Incoming / 0 Outgoing */
-#define MBMASK 0x3f
-/* if MSb is zero, the lower bits are diagnostic status *
- * Diagnostics:
- * 01	No diagnostic error occurred
- * 02	RAM failure
- * 03	FIFO R/W failed
- * 04   SBIC register read/write failed
- * 05   Initialization D-FF failed
- * 06   Host IRQ D-FF failed
- * 07   ROM checksum error
- * Interrupt status (bitwise):
- * 10NNNNNN   outgoing mailbox NNNNNN is free
- * 11NNNNNN   incoming mailbox NNNNNN needs service
- */
-
-/* WRITE */
-#define COMMAND ASC_STAT
-/*
- *  COMMAND opcodes
- */
-#define NO_OP             0
-#define INITIALIZATION    1     /* initialization after reset (10 bytes) */
-#define DISABLE_UNS_INTR  2     /* disable unsolicited interrupts */
-#define ENABLE_UNS_INTR   3     /* enable unsolicited interrupts */
-#define INTR_ON_FREE_OGMB 4     /* interrupt on free OGMB */
-#define SCSI_SOFT_RESET   5     /* SCSI soft reset */
-#define SCSI_HARD_RESET   6     /* SCSI hard reset acknowledge */
-#define START_OGMB        0x80  /* start command in OGMB (n) */
-#define SCAN_OGMBS        0xc0  /* start multiple commands, signature (n) */
-                                /*    where (n) = lower 6 bits */
-/*
- *  For INITIALIZATION:
- */
-#define BUS_ON            48    /* x 125ns, 48 = 6000ns, BIOS uses 8000ns */
-#define BUS_OFF           24    /* x 125ns, 24 = 3000ns, BIOS uses 1875ns */
- 
-#define INTR_ACK ASC_STAT+1
-
-
-#define CONTROL ASC_STAT+2
-#define INT_EN	0x08		/* Interrupt Enable	*/
-#define DMA_EN	0x04		/* DMA Enable		*/
-#define SCSI_RES	0x02	/* SCSI Reset		*/
-#define ASC_RES	0x01		/* ASC Reset		*/
-
-/* Mailbox Definition */
-
-struct wd_mailbox{
-	unchar status;
-	unchar scbptr[3];
-};
-
-
-/* These belong in scsi.h also */
-#undef any2scsi
-#define any2scsi(up, p)			\
-(up)[0] = (((long)(p)) >> 16);	\
-(up)[1] = ((long)(p)) >> 8;		\
-(up)[2] = ((long)(p));
-
-#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
-
-#define xany2scsi(up, p)	\
-(up)[0] = ((long)(p)) >> 24;	\
-(up)[1] = ((long)(p)) >> 16;	\
-(up)[2] = ((long)(p)) >> 8;	\
-(up)[3] = ((long)(p));
-
-#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
-		      + (((long)(up)[2]) <<  8) +  ((long)(up)[3]) )
-
-#define MAX_CDB 12
-#define MAX_SENSE 14
-
-typedef struct scb {		/* Command Control Block 5.4.1 */
-  unchar op;			/* Command Control Block Operation Code */
-  unchar idlun;			/* op=0,2:Target Id, op=1:Initiator Id */
-				/* Outbound data transfer, length is checked*/
-				/* Inbound data transfer, length is checked */
-				/* Logical Unit Number */
-  unchar cdb[12];		/* SCSI Command Block */
-  unchar status;		/* SCSI Return Status */
-  unchar vue;			/* Vendor Unique Error Code */
-  unchar maxlen[3];		/* Maximum Data Transfer Length */
-  unchar dataptr[3];		/* SCSI Data Block Pointer */
-  unchar linkptr[3];		/* Next Command Link Pointer */
-  unchar direc;			/* Transfer Direction */
-  unchar reserved2[6];		/* SCSI Command Descriptor Block */
-                                /* end of hardware SCB */
-  Scsi_Cmnd *SCpnt;             /* Scsi_Cmnd using this SCB */
-  struct scb *next;             /* for lists of scbs */
-} Scb;
-
-/*
- *  WD7000-specific scatter/gather element structure
- */
-typedef struct sgb {
-    unchar len[3];
-    unchar ptr[3];
-} Sgb;
-
-/*
- *  Note:  MAX_SCBS _must_ be defined large enough to keep ahead of the
- *  demand for SCBs, which will be at most WD7000_Q * WD7000_SG.  1 is
- *  added to each because they can be 0.
- */
-#define MAX_SCBS  ((WD7000_Q+1) * (WD7000_SG+1))
-
-/*
- *  The driver is written to allow host-only commands to be executed.  These
- *  use a 16-byte block called an ICB.
- *
- *  (Currently, only wd7000_info uses this, to get the firmware rev. level.)
- */
-#define ICB_STATUS  16          /* set to icmb status by wd7000_intr_handle */
-#define ICB_PHASE   17          /* set to 0 by wd7000_intr_handle */
-#define ICB_LEN     18          /* actually 16; this includes the above */
-
 int wd7000_detect(int);
 int wd7000_command(Scsi_Cmnd *);
 int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
@@ -176,30 +21,32 @@
 int wd7000_biosparam(int, int, int*);
 
 #ifndef NULL
-	#define NULL 0
+#define NULL 0L
 #endif
 
 /*
- *  Define WD7000_SG to be the number of Sgbs that will fit in a block of
- *  size WD7000_SCRIBBLE.  WD7000_SCRIBBLE must be 512, 1024, 2048, or 4096.
- *
- *  The sg_tablesize value will default to SG_NONE for older boards (before
- *  rev 7.0), but will be changed to WD7000_SG when a newer board is
- *  detected.
+ *  In this version, sg_tablesize now defaults to WD7000_SG, and will
+ *  be set to SG_NONE for older boards.  This is the reverse of the
+ *  previous default, and was changed so that the driver-level
+ *  Scsi_Host_Template would reflect the driver's support for scatter/
+ *  gather.
+ *
+ *  Also, it has been reported that boards at Revision 6 support scatter/
+ *  gather, so the new definition of an "older" board has been changed
+ *  accordingly.
  */
-#define WD7000_SCRIBBLE  512
-
-#define WD7000_Q    OGMB_CNT
-#define WD7000_SG   (WD7000_SCRIBBLE / sizeof(Sgb))
+#define WD7000_Q    16
+#define WD7000_SG   16
 
 #define WD7000 {\
 	"Western Digital WD-7000",      \
 	wd7000_detect,                  \
-	wd7000_info, wd7000_command,	\
+	wd7000_info,                    \
+	wd7000_command,	                \
 	wd7000_queuecommand,	        \
 	wd7000_abort,			\
 	wd7000_reset,			\
 	NULL,                           \
 	wd7000_biosparam,               \
-	WD7000_Q, 7, SG_NONE, 1, 0, 1}
+	WD7000_Q, 7, WD7000_SG, 1, 0, 1}
 #endif
diff -u --recursive --new-file v1.1.11/linux/fs/buffer.c linux/fs/buffer.c
--- v1.1.11/linux/fs/buffer.c	Sat May  7 14:54:04 1994
+++ linux/fs/buffer.c	Tue May  3 14:19:18 1994
@@ -49,7 +49,7 @@
 static char buffersize_index[9] = {-1,  0,  1, -1,  2, -1, -1, -1, 3};
 static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096};
 
-#define BUFSIZE_INDEX(X) (buffersize_index[(X)>>9])
+#define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])
 
 static int grow_buffers(int pri, int size);
 static int shrink_specific_buffers(unsigned int priority, int size);
diff -u --recursive --new-file v1.1.11/linux/fs/proc/net.c linux/fs/proc/net.c
--- v1.1.11/linux/fs/proc/net.c	Tue Apr 19 10:53:47 1994
+++ linux/fs/proc/net.c	Sat May  7 14:13:00 1994
@@ -44,6 +44,7 @@
 extern int udp_get_info(char *, char **, off_t, int);
 extern int raw_get_info(char *, char **, off_t, int);
 extern int arp_get_info(char *, char **, off_t, int);
+extern int rarp_get_info(char *, char **, off_t, int);
 extern int dev_get_info(char *, char **, off_t, int);
 extern int rt_get_info(char *, char **, off_t, int);
 #endif /* CONFIG_INET */
@@ -96,11 +97,14 @@
 	{ 131,3,"dev" },
 	{ 132,3,"raw" },
 	{ 133,3,"tcp" },
-	{ 134,3,"udp" }
+	{ 134,3,"udp" },
+#ifdef CONFIG_INET_RARP
+	{ 135,4,"rarp"}
+#endif
 #endif	/* CONFIG_INET */
 #ifdef CONFIG_IPX
-	,{ 135,9,"ipx_route" },
-	{ 136,3,"ipx" }
+	,{ 136,9,"ipx_route" },
+	{ 137,3,"ipx" }
 #endif /* CONFIG_IPX */
 };
 
@@ -212,12 +216,15 @@
 			case 134:
 				length = udp_get_info(page,&start,file->f_pos,thistime);
 				break;
+			case 135:
+				length = rarp_get_info(page,&start,file->f_pos,thistime);
+				break;
 #endif /* CONFIG_INET */
 #ifdef CONFIG_IPX
-			case 135:
+			case 136:
 				length = ipx_rt_get_info(page,&start,file->f_pos,thistime);
 				break;
-			case 136:
+			case 137:
 				length = ipx_get_info(page,&start,file->f_pos,thistime);
 				break;
 #endif /* CONFIG_IPX */
diff -u --recursive --new-file v1.1.11/linux/include/linux/if_arp.h linux/include/linux/if_arp.h
--- v1.1.11/linux/include/linux/if_arp.h	Tue Apr 19 10:53:49 1994
+++ linux/include/linux/if_arp.h	Sat May  7 14:12:56 1994
@@ -30,6 +30,13 @@
 #define	ARPHRD_IEEE802	6		/* IEEE 802.2 Ethernet- huh?	*/
 #define	ARPHRD_ARCNET	7		/* ARCnet			*/
 #define	ARPHRD_APPLETLK	8		/* APPLEtalk			*/
+/* Dummy types for non ARP hardware */
+#define ARPHRD_SLIP	256
+#define ARPHRD_CSLIP	257
+#define ARPHRD_SLIP6	258
+#define ARPHRD_CSLIP6	259
+#define ARPHRD_KISS	260
+#define ARPHRD_ADAPT	264
 
 /* ARP protocol opcodes. */
 #define	ARPOP_REQUEST	1		/* ARP request			*/
diff -u --recursive --new-file v1.1.11/linux/include/linux/if_slip.h linux/include/linux/if_slip.h
--- v1.1.11/linux/include/linux/if_slip.h
+++ linux/include/linux/if_slip.h	Sat May  7 14:12:56 1994
@@ -0,0 +1,19 @@
+/*
+ *	Swansea University Computer Society	NET3
+ *	
+ *	This file declares the constants of special use with the SLIP/CSLIP/
+ *	KISS TNC driver.
+ */
+ 
+#ifndef __LINUX_SLIP_H
+#define __LINUX_SLIP_H
+
+#define		SL_MODE_SLIP		0
+#define		SL_MODE_CSLIP		1
+#define 	SL_MODE_KISS		4
+
+#define		SL_OPT_SIXBIT		2
+#define		SL_OPT_ADAPTIVE		8
+
+
+#endif
diff -u --recursive --new-file v1.1.11/linux/include/linux/ip.h linux/include/linux/ip.h
--- v1.1.11/linux/include/linux/ip.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/ip.h	Sat May  7 14:12:56 1994
@@ -32,8 +32,17 @@
   unsigned char	len;
   unsigned char ptr;
   union {
+#if defined(__i386__)  
 	unsigned char	flags:4,
 			overflow:4;
+#else
+#if defined(__mc680x0__)
+	unsigned char	overflow:4,
+			flags:4;
+#else
+#error	"Adjust this structure to match your CPU"
+#endif
+#endif						
 	unsigned char	full_char;
   } x;
   unsigned long	data[9];
@@ -63,8 +72,17 @@
 
 
 struct iphdr {
+#if defined(__i386__)
   unsigned char		ihl:4,
 			version:4;
+#else
+#if defined (__mc680x0__)
+  unsigned char		version:4,
+  			ihl:4;
+#else
+#error "Adjust this structure to match your CPU"
+#endif			
+#endif
   unsigned char		tos;
   unsigned short	tot_len;
   unsigned short	id;
diff -u --recursive --new-file v1.1.11/linux/include/linux/mtio.h linux/include/linux/mtio.h
--- v1.1.11/linux/include/linux/mtio.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/mtio.h	Fri May  6 08:28:58 1994
@@ -88,6 +88,7 @@
 #define MT_ISDDS1		0x51	/* DDS device without partitions */
 #define MT_ISDDS2		0x52	/* DDS device with partitions */
 #define MT_ISSCSI1		0x71	/* Generic ANSI SCSI-1 tape unit */
+#define MT_ISSCSI2		0x72	/* Generic ANSI SCSI-2 tape unit */
 
 struct mt_tape_info {
 	long t_type;		/* device type id (mt_type) */
@@ -106,6 +107,7 @@
 	{MT_ISWT5099EEN24,	"Wangtek 5099-een24, 60MB"}, \
 	{MT_ISEVEREX_FT40A,	"Everex FT40A, QIC-40"}, \
 	{MT_ISSCSI1,		"Generic SCSI-1 tape"}, \
+	{MT_ISSCSI2,		"Generic SCSI-2 tape"}, \
 	{0, NULL} \
 }
 
@@ -151,6 +153,23 @@
  * files, rather than blocks. Not used. Not supported.
  * I think DDS drives are DAT drives.
  */
+
+/* SCSI-tape specific definitions */
+#define MT_ST_BLKSIZE_SHIFT	0
+#define MT_ST_BLKSIZE_MASK	0xffffff
+#define MT_ST_DENSITY_SHIFT	24
+#define MT_ST_DENSITY_MASK	0xff000000
+
+#define MT_ST_SOFTERR_SHIFT	0
+#define MT_ST_SOFTERR_MASK	0xffff
+
+#define MT_ST_OPTIONS		0xf0000000
+#define MT_ST_BOOLEANS		0x10000000
+#define MT_ST_WRITE_THRESHOLD	0x20000000
+#define MT_ST_BUFFER_WRITES	0x1
+#define MT_ST_ASYNC_WRITES	0x2
+#define MT_ST_READ_AHEAD	0x4
+#define MT_ST_DEBUGGING		0x8
 
 #endif /* _LINUX_MTIO_H */
 
diff -u --recursive --new-file v1.1.11/linux/include/linux/netdevice.h linux/include/linux/netdevice.h
--- v1.1.11/linux/include/linux/netdevice.h	Tue Apr 19 10:53:49 1994
+++ linux/include/linux/netdevice.h	Sat May  7 14:12:56 1994
@@ -11,6 +11,7 @@
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
  *		Corey Minyard <wf-rch!minyard@relay.EU.net>
  *		Donald J. Becker, <becker@super.org>
+ *		Alan Cox, <A.Cox@swansea.ac.uk>
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -34,7 +35,7 @@
 #define IS_MYADDR	1		/* address is (one of) our own	*/
 #define IS_LOOPBACK	2		/* address is for LOOPBACK	*/
 #define IS_BROADCAST	3		/* address is a valid broadcast	*/
-#define IS_INVBCAST	4		/* Wrong netmask bcast not for us */
+#define IS_INVBCAST	4		/* Wrong netmask bcast not for us (unused)*/
 
 /*
  * The DEVICE structure.
@@ -52,7 +53,7 @@
    */
   char			  *name;
 
-  /* I/O specific fields.  These will be moved to DDI soon. */
+  /* I/O specific fields.  */
   unsigned long		  rmem_end;		/* shmem "recv" end	*/
   unsigned long		  rmem_start;		/* shmem "recv" start	*/
   unsigned long		  mem_end;		/* sahared mem end	*/
@@ -65,12 +66,6 @@
                           tbusy,		/* transmitter busy	*/
                           interrupt;		/* interrupt arrived	*/
 
-  /*
-   * Another mistake.
-   * This points to the next device in the "dev" chain. It will
-   * be moved to the "invisible" part of the structure as soon as
-   * it has been cleaned up. -FvK
-   */
   struct device		  *next;
 
   /* The device initialization function. Called only once. */
@@ -135,6 +130,8 @@
   					 int num_addrs, void *addrs);
 #define HAVE_SET_MAC_ADDR  		 
   int			  (*set_mac_address)(struct device *dev, void *addr);
+#define HAVE_PRIVATE_IOCTL
+  int			  (*do_ioctl)(struct device *dev, struct ifreq *ifr);
 };
 
 
diff -u --recursive --new-file v1.1.11/linux/include/linux/sockios.h linux/include/linux/sockios.h
--- v1.1.11/linux/include/linux/sockios.h	Tue Apr 19 10:53:50 1994
+++ linux/include/linux/sockios.h	Sat May  7 14:12:57 1994
@@ -67,4 +67,21 @@
 #define SIOCGARP	0x8951		/* get ARP table entry		*/
 #define SIOCSARP	0x8952		/* set ARP table entry		*/
 
+/* RARP cache control calls. */
+#define SIOCDRARP	0x8960		/* delete RARP table entry	*/
+#define SIOCGRARP	0x8961		/* get RARP table entry		*/
+#define SIOCSRARP	0x8962		/* set RARP table entry		*/
+
+/* Device private ioctl calls */
+
+/*
+ *	These 16 ioctls are available to devices via the do_ioctl() device
+ *	vector. Each device should include this file and redefine these names
+ *	as their own. Because these are device dependant it is a good idea
+ *	_NOT_ to issue them to random objects and hope.
+ */
+ 
+#define SIOCDEVPRIVATE	0x89F0	/* to 89FF */
+
+
 #endif	/* _LINUX_SOCKIOS_H */
diff -u --recursive --new-file v1.1.11/linux/include/linux/tcp.h linux/include/linux/tcp.h
--- v1.1.11/linux/include/linux/tcp.h	Wed Dec  1 14:44:15 1993
+++ linux/include/linux/tcp.h	Sat May  7 14:12:57 1994
@@ -26,6 +26,7 @@
   unsigned short	dest;
   unsigned long		seq;
   unsigned long		ack_seq;
+#if defined(__i386__)
   unsigned short	res1:4,
 			doff:4,
 			fin:1,
@@ -35,6 +36,21 @@
 			ack:1,
 			urg:1,
 			res2:2;
+#else
+#if defined(__mc680x0__)
+  unsigned short	res2:2,
+  			urg:1,
+  			ack:1,
+  			psh:1,
+  			rst:1,
+  			syn:1,
+  			fin:1,
+  			doff:4,
+  			res1:4;
+#else
+#error	"Adjust this structure for your cpu alignment rules"
+#endif  		
+#endif	
   unsigned short	window;
   unsigned short	check;
   unsigned short	urg_ptr;
@@ -45,17 +61,14 @@
   TCP_ESTABLISHED = 1,
   TCP_SYN_SENT,
   TCP_SYN_RECV,
-#if 0
-  TCP_CLOSING, /* not a valid state, just a seperator so we can use
-		  < tcp_closing or > tcp_closing for checks. */
-#endif
   TCP_FIN_WAIT1,
   TCP_FIN_WAIT2,
   TCP_TIME_WAIT,
   TCP_CLOSE,
   TCP_CLOSE_WAIT,
   TCP_LAST_ACK,
-  TCP_LISTEN
+  TCP_LISTEN,
+  TCP_CLOSING	/* now a valid state */
 };
 
 #endif	/* _LINUX_TCP_H */
diff -u --recursive --new-file v1.1.11/linux/init/main.c linux/init/main.c
--- v1.1.11/linux/init/main.c	Mon Apr 25 10:04:36 1994
+++ linux/init/main.c	Fri May  6 08:28:58 1994
@@ -84,6 +84,7 @@
 extern void eth_setup(char *str, int *ints);
 extern void xd_setup(char *str, int *ints);
 extern void mcd_setup(char *str, int *ints);
+extern void st_setup(char *str, int *ints);
 extern void st0x_setup(char *str, int *ints);
 extern void tmc8xx_setup(char *str, int *ints);
 extern void t128_setup(char *str, int *ints);
@@ -175,6 +176,9 @@
 #endif
 #ifdef CONFIG_BLK_DEV_HD
 	{ "hd=", hd_setup },
+#endif
+#ifdef CONFIG_CHR_DEV_ST
+	{ "st=", st_setup },
 #endif
 #ifdef CONFIG_BUSMOUSE
 	{ "bmouse=", bmouse_setup },
diff -u --recursive --new-file v1.1.11/linux/kernel/sys.c linux/kernel/sys.c
--- v1.1.11/linux/kernel/sys.c	Sat May  7 14:54:16 1994
+++ linux/kernel/sys.c	Mon May  2 19:30:12 1994
@@ -227,7 +227,7 @@
 		}
 	}
 	if (rgid != (gid_t) -1 ||
-	    egid != (gid_t) -1 && egid != old_rgid)
+	    (egid != (gid_t) -1 && egid != old_rgid))
 		current->sgid = current->egid;
 	return 0;
 }
@@ -314,7 +314,7 @@
 		}
 	}
 	if (ruid != (uid_t) -1 ||
-	    euid != (uid_t) -1 && euid != old_ruid)
+	    (euid != (uid_t) -1 && euid != old_ruid))
 		current->suid = current->euid;
 	return 0;
 }
diff -u --recursive --new-file v1.1.11/linux/kernel/vm86.c linux/kernel/vm86.c
--- v1.1.11/linux/kernel/vm86.c	Sat May  7 14:54:16 1994
+++ linux/kernel/vm86.c	Thu May  5 10:59:35 1994
@@ -31,8 +31,8 @@
 /*
  * 8- and 16-bit register defines..
  */
-#define AL(regs)	(((unsigned char *) ((regs)->eax))[0])
-#define AH(regs)	(((unsigned char *) ((regs)->eax))[1])
+#define AL(regs)	(((unsigned char *)&((regs)->eax))[0])
+#define AH(regs)	(((unsigned char *)&((regs)->eax))[1])
 #define IP(regs)	(*(unsigned short *)&((regs)->eip))
 #define SP(regs)	(*(unsigned short *)&((regs)->esp))
 
@@ -289,9 +289,8 @@
 	if (seg == BIOSSEG || regs->cs == BIOSSEG ||
 	    is_revectored(i, &current->vm86_info->int_revectored))
 		return_to_32bit(regs, VM86_INTx + (i << 8));
-	if (i==0x21 && is_revectored(AH(regs),&current->vm86_info->int21_revectored)) {
+	if (i==0x21 && is_revectored(AH(regs),&current->vm86_info->int21_revectored))
 		return_to_32bit(regs, VM86_INTx + (i << 8));
-	}
 	pushw(ssp, sp, get_vflags(regs));
 	pushw(ssp, sp, regs->cs);
 	pushw(ssp, sp, IP(regs));
@@ -305,7 +304,15 @@
 
 void handle_vm86_debug(struct vm86_regs * regs, long error_code)
 {
-	do_int(regs, 3, (unsigned char *) (regs->ss << 4), SP(regs));
+#if 0
+	do_int(regs, 1, (unsigned char *) (regs->ss << 4), SP(regs));
+#else
+	if (current->flags & PF_PTRACED)
+		current->blocked &= ~(1 << (SIGTRAP-1));
+	send_sig(SIGTRAP, current, 1);
+	current->tss.trap_no = 1;
+	current->tss.error_code = error_code;
+#endif
 }
 
 void handle_vm86_fault(struct vm86_regs * regs, long error_code)
diff -u --recursive --new-file v1.1.11/linux/net/inet/arp.c linux/net/inet/arp.c
--- v1.1.11/linux/net/inet/arp.c	Tue Apr 19 10:54:17 1994
+++ linux/net/inet/arp.c	Sat May  7 14:12:57 1994
@@ -25,6 +25,7 @@
  *		Alan Cox	:	Allow >4K in /proc
  *		Alan Cox	:	Make ARP add its own protocol entry
  *
+ *              Ross Martin     :       Rewrote arp_rcv() and arp_get_info()
  */
 
 #include <linux/types.h>
@@ -69,7 +70,7 @@
 	unsigned long			ip;			/* ip address of entry 		*/
 	unsigned char			ha[MAX_ADDR_LEN];	/* Hardware address		*/
 	unsigned char			hlen;			/* Length of hardware address 	*/
-	unsigned char			htype;			/* Type of hardware in use	*/
+	unsigned short			htype;			/* Type of hardware in use	*/
 	struct device			*dev;			/* Device the entry is tied to 	*/
 
 	/*
@@ -246,8 +247,9 @@
  *	message.
  */
 
-static void arp_send(int type, unsigned long dest_ip, struct device *dev,
-	unsigned long src_ip, unsigned char *dest_hw, unsigned char *src_hw)
+void arp_send(int type, int ptype, unsigned long dest_ip, 
+	      struct device *dev, unsigned long src_ip, 
+	      unsigned char *dest_hw, unsigned char *src_hw)
 {
 	struct sk_buff *skb;
 	struct arphdr *arp;
@@ -280,7 +282,7 @@
 	 *	Fill the device header for the ARP frame
 	 */
 
-	dev->hard_header(skb->data,dev,ETH_P_ARP,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb);
+	dev->hard_header(skb->data,dev,ptype,dest_hw?dest_hw:dev->broadcast,src_hw?src_hw:NULL,skb->len,skb);
 
 	/* Fill out the arp protocol part. */
 	arp = (struct arphdr *) (skb->data + dev->hard_header_len);
@@ -348,8 +350,8 @@
 		entry->timer.expires = ARP_RES_TIME;
 		add_timer(&entry->timer);
 		restore_flags(flags);
-		arp_send(ARPOP_REQUEST, ip, dev, dev->pa_addr, NULL,
-				dev->dev_addr);
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, ip, dev, dev->pa_addr, 
+			 NULL, dev->dev_addr);
 		return;
 	}
 
@@ -463,166 +465,249 @@
 
 int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
 {
-	/*
-	 *	We shouldn't use this type conversion. Check later.
-	 */
+/*
+ *	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);
 	struct arp_table *entry;
 	struct arp_table *proxy_entry;
-	int addr_hint;
-	unsigned long hash;
+	int addr_hint,hlen,htype;
+	unsigned long hash,dest_hash;
 	unsigned char ha[MAX_ADDR_LEN];	/* So we can enable ints again. */
 	long sip,tip;
 	unsigned char *sha,*tha;
 
-	/*
-	 *	If this test doesn't pass, its not IP, or we should ignore it anyway
-	 */
-	
-	if (arp->ar_hln != dev->addr_len || dev->type != ntohs(arp->ar_hrd) || dev->flags&IFF_NOARP)
+/*
+ *	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;
 	}
 
-	/*
-	 *	For now we will only deal with IP addresses.
-	 */
-	if (
+/*
+ *	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.
+ */
+  	switch(dev->type)
+  	{
 #ifdef CONFIG_AX25
-		(arp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+		case ARPHRD_AX25:
+			if(arp->ar_pro != htons(AX25_P_IP))
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			break;
 #endif
-		(arp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
-		|| arp->ar_pln != 4)
-	{
-		/* This packet is not for us. Remove it. */
-		kfree_skb(skb, FREE_READ);
-		return 0;
+		case ARPHRD_ETHER:
+			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 variable width fields
-	 */
+/*
+ *	Extract fields
+ */
+
+	hlen  = dev->addr_len;
+	htype = dev->type;
 
 	sha=arp_ptr;
-	arp_ptr+=dev->addr_len;
+	arp_ptr+=hlen;
 	memcpy(&sip,arp_ptr,4);
 	arp_ptr+=4;
 	tha=arp_ptr;
-	arp_ptr+=dev->addr_len;
+	arp_ptr+=hlen;
 	memcpy(&tip,arp_ptr,4);
+  
+/* 
+ *	Check for bad requests for 127.0.0.1.  If this is one such, delete it.
+ */
+	if(tip == INADDR_LOOPBACK)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
 
-	/*
-	 *	Process entry
-	 */
+/*
+ *  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.
+ */
 
 	addr_hint = ip_chk_addr(tip);
 
-	hash = HASH(sip);
-	proxy_entry = NULL;
-	if (proxies != 0 && addr_hint != IS_MYADDR)
+	if(arp->ar_op == htons(ARPOP_REPLY))
 	{
-		unsigned long dest_hash = HASH(tip);
-		cli();
-		proxy_entry = arp_tables[dest_hash];
-		while (proxy_entry != NULL)
+		if(addr_hint!=IS_MYADDR)
 		{
-			if (proxy_entry->ip == tip && proxy_entry->htype==arp->ar_hrd)
-				break;
-			proxy_entry = proxy_entry->next;
+/* 
+ *	Replies to other machines get tossed. 
+ */
+			kfree_skb(skb, FREE_READ);
+			return 0;
 		}
-		if (proxy_entry && (proxy_entry->flags & ATF_PUBL))
-			memcpy(ha, proxy_entry->ha, dev->addr_len);
-		else
-			proxy_entry = NULL;
+/*
+ *	Fall through to code below that adds sender to cache. 
+ */
 	}
 	else
-		cli();
+	{ 
+/* 
+ * 	It is now an arp request 
+ */
+		if(addr_hint != IS_MYADDR)
+		{
+/*
+ * 	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.
+ */
+			if (proxies == 0)
+			{
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+	
+			dest_hash = HASH(tip);
+			cli();
+			for(proxy_entry=arp_tables[dest_hash];
+				proxy_entry;
+				proxy_entry = proxy_entry->next)
+			{
+				if(proxy_entry->ip == tip && proxy_entry->htype==htype)
+					break;
+			}
+			if (proxy_entry && (proxy_entry->flags & ATF_PUBL))
+			{
+				memcpy(ha, proxy_entry->ha, hlen);
+				sti();
+				arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,ha);
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+			else
+			{
+				sti();
+				kfree_skb(skb, FREE_READ);
+				return 0;
+			}
+		}
+		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);
+		}
+	}
 
-	for (entry = arp_tables[hash]; entry != NULL; entry = entry->next)
-		if (entry->ip == sip)
+
+/*
+ * 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.
+ */
+
+	hash = HASH(sip);
+	cli();
+	for(entry=arp_tables[hash];entry;entry=entry->next)
+		if(entry->ip==sip && entry->htype==htype)
 			break;
 
-	if (entry != NULL)
+	if(entry)
 	{
-		int old_flags = entry->flags;
-		memcpy(entry->ha, sha, arp->ar_hln);
-		entry->hlen = arp->ar_hln;
-		/* This seems sensible but not everyone gets it right ! */
-		entry->htype = ntohs(arp->ar_hrd);
-		if(entry->htype==0)
-			entry->htype = dev->type;	/* Not good but we have no choice */
+/*
+ *	Entry found; update it.
+ */
+		memcpy(entry->ha, sha, hlen);
+		entry->hlen = hlen;
 		entry->last_used = jiffies;
 		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;
-		}
-		sti();
-		if (!(old_flags & ATF_COM))
-		{
-			/* Send out waiting packets. We might have problems,
-			   if someone is manually removing entries right now.
-			   I will fix this one. */
+			sti();
+/* 
+ *	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, sha);
 		}
-		if (addr_hint != IS_MYADDR && proxy_entry == NULL)
+		else
 		{
-			kfree_skb(skb, FREE_READ);
-			return 0;
+			sti();
 		}
 	}
 	else
 	{
-		if (addr_hint != IS_MYADDR && proxy_entry == NULL)
-		{
-			/* We don't do "smart arp" and cache all possible
-			   entries. That just makes us more work. */
-			sti();
-			kfree_skb(skb, FREE_READ);
-			return 0;
-		}
-		entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),
-					GFP_ATOMIC);
-		if (entry == NULL)
+/*
+ * 	No entry found.  Need to add a new entry to the arp table.
+ */
+		entry = (struct arp_table *)kmalloc(sizeof(struct arp_table),GFP_ATOMIC);
+		if(entry == NULL)
 		{
 			sti();
-			kfree_skb(skb, FREE_READ);
 			printk("ARP: no memory for new arp entry\n");
+
+			kfree_skb(skb, FREE_READ);
 			return 0;
 		}
+
 		entry->ip = sip;
-		entry->hlen = arp->ar_hln;
-		entry->htype = arp->ar_hrd;
+		entry->hlen = hlen;
+		entry->htype = htype;
 		entry->flags = ATF_COM;
-		memcpy(entry->ha, sha, arp->ar_hln);
+		memcpy(entry->ha, sha, hlen);
 		entry->last_used = jiffies;
-		entry->next = arp_tables[hash];
-		arp_tables[hash] = entry;
 		entry->dev = skb->dev;
 		skb_queue_head_init(&entry->skb);
-		sti();
-	}
-
-	/* From here on, interrupts are enabled. Never touch entry->..
-	   any more. */
+		entry->next = arp_tables[hash];
+		arp_tables[hash] = entry;
 
-	if (arp->ar_op != htons(ARPOP_REQUEST)
-		|| tip == INADDR_LOOPBACK)
-	{
-		/* This wasn't a request, or some bad request for 127.0.0.1
-		   has made its way to the net, so delete it. */
-		kfree_skb(skb, FREE_READ);
-		return 0;
+		sti();
 	}
 
-	/* Either we respond with our own hw address, or we do proxy arp for
-	   another machine. */
-	arp_send(ARPOP_REPLY, sip, dev, tip, sha,
-		(addr_hint == IS_MYADDR)? dev->dev_addr : ha);
-
+/*
+ *	Replies have been sent, and entries have been added.  All done.
+ */
 	kfree_skb(skb, FREE_READ);
 	return 0;
 }
@@ -726,7 +811,8 @@
 	 *	If we didn't find an entry, we will try to send an ARP packet.
 	 */
 	
-	arp_send(ARPOP_REQUEST, paddr, dev, saddr, NULL, dev->dev_addr);
+	arp_send(ARPOP_REQUEST, ETH_P_ARP, paddr, dev, saddr, NULL, 
+		 dev->dev_addr);
 
 	return 1;
 }
@@ -734,51 +820,66 @@
 
 /*
  *	Write the contents of the ARP cache to a PROCfs file.
- *
- *	Will change soon to ASCII format
  */
 
+#define HBUFFERLEN 30
+
 int arp_get_info(char *buffer, char **start, off_t offset, int length)
 {
-	struct arp_table *entry;
-	struct arpreq *req = (struct arpreq *) buffer;
-	int i;
-	off_t pos=0;
-	off_t begin=0;
 	int len=0;
-
+	off_t begin=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\n");
+	pos+=size;
+	len+=size;
+	  
 	cli();
-	/* Loop over the ARP table and copy structures to the buffer. */
-	for (i = 0; i < ARP_TABLE_SIZE; i++)
+	for(i=0; i<ARP_TABLE_SIZE; i++)
 	{
-		for (entry = arp_tables[i]; entry; entry = entry->next)
+		for(entry=arp_tables[i]; entry!=NULL; entry=entry->next)
 		{
-			memset(req, 0, sizeof(struct arpreq));
-			req->arp_pa.sa_family = AF_INET;
-			memcpy(req->arp_pa.sa_data, &entry->ip, 4);
-			req->arp_ha.sa_family = entry->htype;
-			memcpy(req->arp_ha.sa_data, &entry->ha, MAX_ADDR_LEN);
-			req->arp_flags = entry->flags;
-			req++;
-			len+=sizeof(struct arpreq);
-			pos+=sizeof(struct arpreq);
+/*
+ *	Convert hardware address to XX:XX:XX:XX ... form.
+ */
+			for(k=0,j=0;k<HBUFFERLEN-3 && j<entry->hlen;j++)
+			{
+				hbuffer[k++]=hexbuf[ (entry->ha[j]>>4)&15 ];
+				hbuffer[k++]=hexbuf[  entry->ha[j]&15     ];
+				hbuffer[k++]=':';
+			}
+			hbuffer[--k]=0;
+	
+			size = sprintf(buffer+len,
+				"%-17s0x%-10x0x%-10x%s\n",
+				in_ntoa(entry->ip),
+				(unsigned int)entry->htype,
+				entry->flags,
+				hbuffer);
+	
+			len+=size;
+			pos=begin+len;
+		  
 			if(pos<offset)
 			{
 				len=0;
 				begin=pos;
-				req=(struct arpreq *) buffer;
 			}
 			if(pos>offset+length)
 				break;
 		}
-		if(pos>offset+length)
-			break;
 	}
 	sti();
-	*start=buffer+(offset-begin);
-	len-=(offset-begin);
+  
+	*start=buffer+(offset-begin);	/* Start of wanted data */
+	len-=(offset-begin);		/* Start slop */
 	if(len>length)
-		len=length;
+		len=length;		        /* Ending slop */
 	return len;
 }
 
diff -u --recursive --new-file v1.1.11/linux/net/inet/arp.h linux/net/inet/arp.h
--- v1.1.11/linux/net/inet/arp.h	Tue Apr 19 10:53:52 1994
+++ linux/net/inet/arp.h	Sat May  7 14:12:57 1994
@@ -10,5 +10,8 @@
 		struct device *dev, unsigned long saddr, struct sk_buff *skb);
 extern int	arp_get_info(char *buffer, char **start, off_t origin, int length);
 extern int	arp_ioctl(unsigned int cmd, void *arg);
+extern void     arp_send(int type, int ptype, unsigned long dest_ip, 
+			 struct device *dev, unsigned long src_ip, 
+			 unsigned char *dest_hw, unsigned char *src_hw);
 
 #endif	/* _ARP_H */
diff -u --recursive --new-file v1.1.11/linux/net/inet/ip.c linux/net/inet/ip.c
--- v1.1.11/linux/net/inet/ip.c	Tue Apr 19 22:20:35 1994
+++ linux/net/inet/ip.c	Sat May  7 14:12:58 1994
@@ -1273,7 +1273,7 @@
  		 *	Set up data on packet
  		 */
 
- 		skb2->arp = skb->arp;
+ 		skb2->arp = 0;/*skb->arp;*/
  		skb2->free = skb->free;
  		skb2->len = len + hlen;
  		skb2->h.raw=(char *) skb2->data;
@@ -1549,7 +1549,7 @@
 	ip_statistics.IpInReceives++;
 	
 	DPRINTF((DBG_IP, "<<\n"));
-
+	
 	/*
 	 *	Tag the ip header of this packet so we can find it
 	 */
diff -u --recursive --new-file v1.1.11/linux/net/inet/rarp.c linux/net/inet/rarp.c
--- v1.1.11/linux/net/inet/rarp.c
+++ linux/net/inet/rarp.c	Sat May  7 14:12:58 1994
@@ -0,0 +1,512 @@
+/* linux/net/inet/rarp.c
+ *
+ * Copyright (C) 1994 by Ross Martin
+ * Based on linux/net/inet/arp.c, Copyright (C) 1994 by Florian La Roche
+ *
+ * This module implements the Reverse Address Resolution Protocol 
+ * (RARP, RFC 903), which is used to convert low level addresses such
+ * as ethernet addresses into high level addresses such as IP addresses.
+ * The most common use of RARP is as a means for a diskless workstation 
+ * to discover its IP address during a network boot.
+ *
+ **
+ ***	WARNING:::::::::::::::::::::::::::::::::WARNING
+ ****
+ *****	SUN machines seem determined to boot solely from the person who
+ ****	answered their RARP query. NEVER add a SUN to your RARP table
+ ***	unless you have all the rest to boot the box from it. 
+ **
+ * 
+ * Currently, only ethernet address -> IP address is likely to work.
+ * (Is RARP ever used for anything else?)
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/config.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/in.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "ip.h"
+#include "route.h"
+#include "protocol.h"
+#include "tcp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+#include "arp.h"
+#include "rarp.h"
+#ifdef CONFIG_AX25
+#include "ax25.h"
+#endif
+
+#ifdef CONFIG_INET_RARP
+
+/*
+ *	This structure defines the RARP mapping cache. As long as we make 
+ *	changes in this structure, we keep interrupts off.
+ */
+
+struct rarp_table
+{
+	struct rarp_table  *next;             /* Linked entry list           */
+	unsigned long      ip;                /* ip address of entry         */
+	unsigned char      ha[MAX_ADDR_LEN];  /* Hardware address            */
+	unsigned char      hlen;              /* Length of hardware address  */
+	unsigned char      htype;             /* Type of hardware in use     */
+	struct device      *dev;              /* Device the entry is tied to */
+};
+
+struct rarp_table *rarp_tables = NULL;
+
+/*
+ * This structure defines an ethernet arp header, which is the same header
+ * that is used for rarp.
+ */
+
+struct arphdr
+{
+	unsigned short  ar_hrd;               /* format of hardware address  */
+	unsigned short  ar_pro;               /* format of protocol address  */
+	unsigned char   ar_hln;               /* length of hardware address  */
+	unsigned char   ar_pln;               /* length of protocol address  */
+	unsigned short  ar_op;                /* ARP opcode (command)        */
+#if 0
+  /*
+   *	Ethernet looks like this : This bit is variable sized however...
+   */
+	unsigned char   ar_sha[ETH_ALEN];     /* sender hardware address     */
+	unsigned char   ar_sip[4];            /* sender IP address           */
+	unsigned char   ar_tha[ETH_ALEN];     /* target hardware address     */
+	unsigned char   ar_tip[4];            /* target IP address           */
+#endif
+};
+
+static struct packet_type rarp_packet_type =
+{
+	0,  /* Should be: __constant_htons(ETH_P_RARP) - but this _doesn't_ come out constant! */
+	0,                /* copy */
+	rarp_rcv,
+	NULL,
+	NULL
+};
+
+static initflag = 1;
+
+/*
+ *	Called once when data first added to rarp cache with ioctl.
+ */
+
+static void rarp_init (void)
+{
+	/* Register the packet type */
+	rarp_packet_type.type=htons(ETH_P_RARP);
+	dev_add_pack(&rarp_packet_type);
+}
+
+/*
+ *	Release the memory for this entry.
+ */
+
+static inline void rarp_release_entry(struct rarp_table *entry)
+{
+	kfree_s(entry, sizeof(struct rarp_table));
+	return;
+}
+
+/*
+ *	Delete a RARP mapping entry in the cache.
+ */
+
+static void rarp_destroy(unsigned long ip_addr)
+{
+	struct rarp_table *entry;
+	struct rarp_table **pentry;
+  
+	cli();
+	pentry = &rarp_tables;
+	while ((entry = *pentry) != NULL)
+	{
+		if (entry->ip == ip_addr)
+		{
+			*pentry = entry->next;
+			sti();
+			rarp_release_entry(entry);
+			return;
+		}
+		pentry = &entry->next;
+	}
+	sti();
+}
+
+
+/*
+ *	Receive an arp request by the device layer.  Maybe it should be 
+ *	rewritten to use the incoming packet for the reply. The current 
+ *	"overhead" time isn't that high...
+ */
+
+int rarp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
+{
+/*
+ *	We shouldn't use this type conversion. Check later.
+ */
+	struct arphdr *rarp = (struct arphdr *)skb->h.raw;
+	unsigned char *rarp_ptr = (unsigned char *)(rarp+1);
+	struct rarp_table *entry;
+	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 (rarp->ar_hln != dev->addr_len || dev->type != ntohs(rarp->ar_hrd) 
+		|| dev->flags&IFF_NOARP)
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+/*
+ *	If it's not a RARP request, delete it.
+ */
+	if (rarp->ar_op != htons(ARPOP_RREQUEST))
+	{
+		kfree_skb(skb, FREE_READ);
+		return 0;
+	}
+
+/*
+ *	For now we will only deal with IP addresses.
+ */
+
+	if (
+#ifdef CONFIG_AX25
+		(rarp->ar_pro != htons(AX25_P_IP) && dev->type == ARPHRD_AX25) ||
+#endif
+		(rarp->ar_pro != htons(ETH_P_IP) && dev->type != ARPHRD_AX25)
+		|| rarp->ar_pln != 4)
+	{
+	/*
+	 *	This packet is not for us. Remove it. 
+	 */
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+  
+/*
+ *	Extract variable width fields
+ */
+
+	sha=rarp_ptr;
+	rarp_ptr+=dev->addr_len;
+	memcpy(&sip,rarp_ptr,4);
+	rarp_ptr+=4;
+	tha=rarp_ptr;
+	rarp_ptr+=dev->addr_len;
+	memcpy(&tip,rarp_ptr,4);
+
+/*
+ *	Process entry
+ */
+  
+	cli();
+	for (entry = rarp_tables; entry != NULL; entry = entry->next)
+	if (!memcmp(entry->ha, sha, rarp->ar_hln))
+		break;
+  
+	if (entry != NULL)
+	{
+		sip=entry->ip;
+		sti();
+
+		arp_send(ARPOP_RREPLY, ETH_P_RARP, sip, dev, dev->pa_addr, sha, 
+			dev->dev_addr);
+	}
+	else
+		sti();
+
+	kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
+
+/*
+ *	Set (create) a RARP cache entry.
+ */
+
+static int rarp_req_set(struct arpreq *req)
+{
+	struct arpreq r;
+	struct rarp_table *entry;
+	struct sockaddr_in *si;
+	int htype, hlen;
+	unsigned long ip;
+	struct rtable *rt;
+  
+	memcpy_fromfs(&r, req, sizeof(r));
+  
+	/*
+	 *	We only understand about IP addresses... 
+	 */
+
+	if (r.arp_pa.sa_family != AF_INET)
+		return -EPFNOSUPPORT;
+  
+	switch (r.arp_ha.sa_family) 
+	{
+		case ARPHRD_ETHER:
+			htype = ARPHRD_ETHER;
+			hlen = ETH_ALEN;
+			break;
+#ifdef CONFIG_AX25
+		case ARPHRD_AX25:
+			htype = ARPHRD_AX25;
+			hlen = 7;
+		break;
+#endif
+		default:
+			return -EPFNOSUPPORT;
+	}
+
+	si = (struct sockaddr_in *) &r.arp_pa;
+	ip = si->sin_addr.s_addr;
+	if (ip == 0)
+	{
+		printk("RARP: SETRARP: requested PA is 0.0.0.0 !\n");
+		return -EINVAL;
+	}
+  
+/*
+ *	Is it reachable directly ?
+ */
+  
+	rt = ip_rt_route(ip, NULL, NULL);
+	if (rt == NULL)
+		return -ENETUNREACH;
+
+/*
+ *	Is there an existing entry for this address?  Find out...
+ */
+  
+	cli();
+	for (entry = rarp_tables; entry != NULL; entry = entry->next)
+		if (entry->ip == ip)
+			break;
+  
+/*
+ *	If no entry was found, create a new one.
+ */
+
+	if (entry == NULL)
+	{
+		entry = (struct rarp_table *) kmalloc(sizeof(struct rarp_table),
+				    GFP_ATOMIC);
+		if (entry == NULL)
+		{
+			sti();
+			return -ENOMEM;
+		}
+		if(initflag)
+		{
+			rarp_init();
+			initflag=0;
+		}
+
+		entry->next = rarp_tables;
+		rarp_tables = entry;
+	}
+
+	entry->ip = ip;
+	entry->hlen = hlen;
+	entry->htype = htype;
+	memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
+	entry->dev = rt->rt_dev;
+
+	sti();  
+
+	return 0;
+}
+
+
+/*
+ *        Get a RARP cache entry.
+ */
+
+static int rarp_req_get(struct arpreq *req)
+{
+	struct arpreq r;
+	struct rarp_table *entry;
+	struct sockaddr_in *si;
+	unsigned long ip;
+
+/*
+ *	We only understand about IP addresses...
+ */
+        
+	memcpy_fromfs(&r, req, sizeof(r));
+  
+	if (r.arp_pa.sa_family != AF_INET)
+		return -EPFNOSUPPORT;
+  
+/*
+ *        Is there an existing entry for this address?
+ */
+
+	si = (struct sockaddr_in *) &r.arp_pa;
+	ip = si->sin_addr.s_addr;
+
+	cli();
+	for (entry = rarp_tables; entry != NULL; entry = entry->next)
+		if (entry->ip == ip)
+			break;
+
+	if (entry == NULL)
+	{
+		sti();
+		return -ENXIO;
+	}
+
+/*
+ *        We found it; copy into structure.
+ */
+        
+	memcpy(r.arp_ha.sa_data, &entry->ha, entry->hlen);
+	r.arp_ha.sa_family = entry->htype;
+	sti();
+  
+/*
+ *        Copy the information back
+ */
+  
+	memcpy_tofs(req, &r, sizeof(r));
+	return 0;
+}
+
+
+/*
+ *	Handle a RARP layer I/O control request.
+ */
+
+int rarp_ioctl(unsigned int cmd, void *arg)
+{
+	struct arpreq r;
+	struct sockaddr_in *si;
+	int err;
+
+	switch(cmd)
+	{
+		case SIOCDRARP:
+			if (!suser())
+				return -EPERM;
+			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+			if(err)
+				return err;
+			memcpy_fromfs(&r, arg, sizeof(r));
+			if (r.arp_pa.sa_family != AF_INET)
+				return -EPFNOSUPPORT;
+			si = (struct sockaddr_in *) &r.arp_pa;
+			rarp_destroy(si->sin_addr.s_addr);
+			return 0;
+
+		case SIOCGRARP:
+			err = verify_area(VERIFY_WRITE, arg, sizeof(struct arpreq));
+			if(err)
+				return err;
+			return rarp_req_get((struct arpreq *)arg);
+		case SIOCSRARP:
+			if (!suser())
+				return -EPERM;
+			err = verify_area(VERIFY_READ, arg, sizeof(struct arpreq));
+			if(err)
+				return err;
+			return rarp_req_set((struct arpreq *)arg);
+		default:
+			return -EINVAL;
+	}
+
+	/*NOTREACHED*/
+	return 0;
+}
+
+int rarp_get_info(char *buffer, char **start, off_t offset, int length)
+{
+	int len=0;
+	off_t begin=0;
+	off_t pos=0;
+	int size;
+	struct rarp_table *entry;
+	char ipbuffer[20];
+	unsigned long netip;
+	if(initflag)
+	{
+		size = sprintf(buffer,"RARP disabled until entries added to cache.\n");
+		pos+=size;
+		len+=size;
+	}   
+	else
+	{
+		size = sprintf(buffer,
+			"IP address       HW type             HW address\n");
+		pos+=size;
+		len+=size;
+      
+		cli();
+		for(entry=rarp_tables; entry!=NULL; entry=entry->next)
+		{
+			netip=htonl(entry->ip);          /* switch to network order */
+			sprintf(ipbuffer,"%d.%d.%d.%d",
+				(unsigned int)(netip>>24)&255,
+				(unsigned int)(netip>>16)&255,
+				(unsigned int)(netip>>8)&255,
+				(unsigned int)(netip)&255);
+
+			size = sprintf(buffer+len,
+				"%-17s%-20s%02x:%02x:%02x:%02x:%02x:%02x\n",
+				ipbuffer,
+				"10Mbps Ethernet",
+				(unsigned int)entry->ha[0],
+				(unsigned int)entry->ha[1],
+				(unsigned int)entry->ha[2],
+				(unsigned int)entry->ha[3],
+				(unsigned int)entry->ha[4],
+			 	(unsigned int)entry->ha[5]);
+	  
+			len+=size;
+			pos=begin+len;
+	  
+			if(pos<offset)
+			{
+				len=0;
+				begin=pos;
+			}
+			if(pos>offset+length)
+				break;
+		}
+		sti();
+	}      
+
+	*start=buffer+(offset-begin);	/* Start of wanted data */
+	len-=(offset-begin);		/* Start slop */
+	if(len>length)
+		len=length;		        /* Ending slop */
+	return len;
+}
+
+#endif
diff -u --recursive --new-file v1.1.11/linux/net/inet/rarp.h linux/net/inet/rarp.h
--- v1.1.11/linux/net/inet/rarp.h
+++ linux/net/inet/rarp.h	Sat May  7 14:12:58 1994
@@ -0,0 +1,14 @@
+/* linux/net/inet/rarp.h */
+#ifndef _RARP_H
+#define _RARP_H
+
+extern int rarp_ioctl(unsigned int cmd, void *arg);
+extern int rarp_rcv(struct sk_buff *skb, 
+		    struct device *dev, 
+		    struct packet_type *pt);
+extern int rarp_get_info(char *buffer, 
+			 char **start, 
+			 off_t offset, 
+			 int length);
+#endif	/* _RARP_H */
+
diff -u --recursive --new-file v1.1.11/linux/net/inet/sock.c linux/net/inet/sock.c
--- v1.1.11/linux/net/inet/sock.c	Thu Apr 21 09:46:36 1994
+++ linux/net/inet/sock.c	Sat May  7 14:12:58 1994
@@ -52,6 +52,7 @@
  *		Alan Cox	:	Split socket option code
  *		Alan Cox	:	Callbacks
  *		Alan Cox	:	Nagle flag for Charles & Johannes stuff
+ *		Alex		:	Removed restriction on inet fioctl
  *
  * To Fix:
  *
@@ -86,6 +87,7 @@
 #include "ip.h"
 #include "protocol.h"
 #include "arp.h"
+#include "rarp.h"
 #include "route.h"
 #include "tcp.h"
 #include "udp.h"
@@ -1482,6 +1484,11 @@
 	case SIOCSARP:
 		return(arp_ioctl(cmd,(void *) arg));
 
+	case SIOCDRARP:
+	case SIOCGRARP:
+	case SIOCSRARP:
+		return(rarp_ioctl(cmd,(void *) arg));
+
 	case SIOCGIFCONF:
 	case SIOCGIFFLAGS:
 	case SIOCSIFFLAGS:
@@ -1701,7 +1708,6 @@
 
   /* Extract the minor number on which we work. */
   minor = MINOR(inode->i_rdev);
-  if (minor != 0) return(-ENODEV);
 
   /* Now dispatch on the minor device. */
   switch(minor) {
@@ -1780,7 +1786,7 @@
 	struct inet_protocol *p;
 	int i;
 
-	printk("Swansea University Computer Society NET3.010\n");
+	printk("Swansea University Computer Society NET3.012\n");
 	/*
 	 *	Set up our UNIX VFS major device. (compatibility)
 	 */
diff -u --recursive --new-file v1.1.11/linux/net/inet/tcp.c linux/net/inet/tcp.c
--- v1.1.11/linux/net/inet/tcp.c	Tue Apr 19 22:20:37 1994
+++ linux/net/inet/tcp.c	Sat May  7 14:12:59 1994
@@ -15,6 +15,7 @@
  *		Charles Hedrick, <hedrick@klinzhai.rutgers.edu>
  *		Linus Torvalds, <torvalds@cs.helsinki.fi>
  *		Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *		Matthew Dillon, <dillon@apollo.west.oic.com>
  *
  * Fixes:	
  *		Alan Cox	:	Numerous verify_area() calls
@@ -67,6 +68,7 @@
  *		Linus		:	Rewrote tcp_read() and URG handling
  *					completely
  *		Gerhard Koerting:	Fixed some missing timer handling
+ *		Matthew Dillon  :	Reworked TCP machine states as per RFC
  *
  *
  * To Fix:
@@ -80,6 +82,40 @@
  *		modify it under the terms of the GNU General Public License
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or(at your option) any later version.
+ *
+ * Description of States:
+ *
+ *	TCP_SYN_SENT		sent a connection request, waiting for ack
+ *
+ *	TCP_SYN_RECV		received a connection request, sent ack,
+ *				waiting for final ack in three-way handshake.
+ *
+ *	TCP_ESTABLISHED		connection established
+ *
+ *	TCP_FIN_WAIT1		our side has shutdown, waiting to complete
+ *				transmission of remaining buffered data
+ *
+ *	TCP_FIN_WAIT2		all buffered data sent, waiting for remote
+ *				to shutdown
+ *
+ *	TCP_CLOSING		both sides have shutdown but we still have
+ *				data we have to finish sending
+ *
+ *	TCP_TIME_WAIT		timeout to catch resent junk before entering
+ *				closed, can only be entered from FIN_WAIT2
+ *				or CLOSING.  Required because the other end
+ *				may not have gotten our last ACK causing it
+ *				to retransmit the data packet (which we ignore)
+ *
+ *	TCP_CLOSE_WAIT		remote side has shutdown and is waiting for
+ *				us to finish writing our data and to shutdown
+ *				(we have to close() to move on to LAST_ACK)
+ *
+ *	TCP_LAST_ACK		out side has shutdown after remote has
+ *				shutdown.  There may still be data in our
+ *				buffer that we have to finish sending
+ *		
+ *	TCP_CLOSED		socket is finished
  */
 #include <linux/types.h>
 #include <linux/sched.h>
@@ -109,7 +145,6 @@
 unsigned long seq_offset;
 struct tcp_mib	tcp_statistics;
 
-#define SUBNETSARELOCAL
 
 static __inline__ int 
 min(unsigned int a, unsigned int b)
@@ -1498,8 +1533,7 @@
 
  
 /*
- * Send a FIN without closing the connection.
- * Not called at interrupt time.
+ * Shutdown the sending side of a connection.
  */
 
 void tcp_shutdown(struct sock *sk, int how)
@@ -1514,22 +1548,36 @@
 	 * We need to grab some memory, and put together a FIN,
 	 * and then put it into the queue to be sent.
 	 * FIXME:
+	 *
 	 *	Tim MacKenzie(tym@dibbler.cs.monash.edu.au) 4 Dec '92.
 	 *	Most of this is guesswork, so maybe it will work...
 	 */
+
+	if (!(how & SEND_SHUTDOWN)) 
+		return;
 	 
 	/*
 	 *	If we've already sent a FIN, return. 
 	 */
 	 
-	if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) 
-		return;
-	if (!(how & SEND_SHUTDOWN)) 
+	if (sk->state == TCP_FIN_WAIT1 ||
+	    sk->state == TCP_FIN_WAIT2 ||
+	    sk->state == TCP_CLOSING ||
+	    sk->state == TCP_LAST_ACK ||
+	    sk->state == TCP_TIME_WAIT
+	) {
 		return;
+	}
 	sk->inuse = 1;
 
 	/*
-	 *	Clear out any half completed packets. 
+	 * flag that the sender has shutdown
+	 */
+
+	sk->shutdown |= SEND_SHUTDOWN;
+
+	/*
+	 *  Clear out any half completed packets. 
 	 */
 
 	if (sk->partial)
@@ -1560,13 +1608,24 @@
 	{
   		/*
   		 *	Finish anyway, treat this as a send that got lost. 
+  		 *
+  		 *	Enter FIN_WAIT1 on normal shutdown, which waits for
+  		 *	written data to be completely acknowledged along
+  		 *	with an acknowledge to our FIN.
+  		 *
+  		 *	Enter FIN_WAIT2 on abnormal shutdown -- close before
+  		 *	connection established.
   		 */
 	  	buff->free=1;
 		prot->wfree(sk,buff->mem_addr, buff->mem_len);
-		if(sk->state==TCP_ESTABLISHED)
-			sk->state=TCP_FIN_WAIT1;
+
+		if (sk->state == TCP_ESTABLISHED)
+			sk->state = TCP_FIN_WAIT1;
+		else if(sk->state == TCP_CLOSE_WAIT)
+			sk->state = TCP_LAST_ACK;
 		else
-			sk->state=TCP_FIN_WAIT2;
+			sk->state = TCP_FIN_WAIT2;
+
 		release_sock(sk);
 		DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
 		return;
@@ -1610,7 +1669,9 @@
 
 	if (sk->state == TCP_ESTABLISHED) 
 		sk->state = TCP_FIN_WAIT1;
-	else 
+	else if (sk->state == TCP_CLOSE_WAIT)
+		sk->state = TCP_LAST_ACK;
+	else
 		sk->state = TCP_FIN_WAIT2;
 
 	release_sock(sk);
@@ -1934,7 +1995,7 @@
   if (sk->user_mss)
     newsk->mtu = sk->user_mss;
   else {
-#ifdef SUBNETSARELOCAL
+#ifdef CONFIG_INET_SNARL	/* Sub Nets ARe Local */
     if ((saddr ^ daddr) & default_mask(saddr))
 #else
     if ((saddr ^ daddr) & dev->pa_mask)
@@ -2081,7 +2142,18 @@
 	{
 		case TCP_FIN_WAIT1:
 		case TCP_FIN_WAIT2:
-		case TCP_LAST_ACK:
+		case TCP_CLOSING:
+			/*
+			 * These states occur when we have already closed out
+			 * our end.  If there is no timeout, we do not do
+			 * anything.  We may still be in the middle of sending
+			 * the remainder of our buffer, for example...
+			 * resetting the timer would be inappropriate.
+			 *
+			 * XXX if retransmit count reaches limit, is tcp_close()
+			 * called with timeout == 1 ? if not, we need to fix that.
+			 */
+#ifdef NOTDEF
 			/* 
 			 *	Start a timer.
 			 * original code was 4 * sk->rtt.  In converting to the
@@ -2089,11 +2161,16 @@
 			 * it seems to make most sense to  use the backed off value
 			 */
 			reset_timer(sk, TIME_CLOSE, 4 * sk->rto);
+#endif
 			if (timeout) 
 				tcp_time_wait(sk);
 			release_sock(sk);
 			return;	/* break causes a double release - messy */
 		case TCP_TIME_WAIT:
+		case TCP_LAST_ACK:
+			/*
+			 * A timeout from these states terminates the TCB.
+			 */
 			if (timeout) 
 			{
 		  		sk->state = TCP_CLOSE;
@@ -2140,6 +2217,12 @@
 			if (tmp < 0) 
 			{
 				kfree_skb(buff,FREE_WRITE);
+
+				/*
+				 * Enter FIN_WAIT1 to await completion of
+				 * written out data and ACK to our FIN.
+				 */
+
 				if(sk->state==TCP_ESTABLISHED)
 					sk->state=TCP_FIN_WAIT1;
 				else
@@ -2192,14 +2275,19 @@
 				skb_queue_tail(&sk->write_queue, buff);
 			}
 
-			if (sk->state == TCP_CLOSE_WAIT) 
-			{
-				sk->state = TCP_FIN_WAIT2;
-			} 
-			else 
-			{
-				sk->state = TCP_FIN_WAIT1;
-			}
+			/*
+			 * If established (normal close), enter FIN_WAIT1.
+			 * If in CLOSE_WAIT, enter LAST_ACK
+			 * If in CLOSING, remain in CLOSING
+			 * otherwise enter FIN_WAIT2
+			 */
+
+			if (sk->state == TCP_ESTABLISHED)
+			    sk->state = TCP_FIN_WAIT1;
+			else if (sk->state == TCP_CLOSE_WAIT)
+			    sk->state = TCP_LAST_ACK;
+			else if (sk->state != TCP_CLOSING)
+			    sk->state = TCP_FIN_WAIT2;
 	}
 	release_sock(sk);
 }
@@ -2276,7 +2364,10 @@
 }
   
 
-/* This routine deals with incoming acks, but not outgoing ones. */
+/*
+ * This routine deals with incoming acks, but not outgoing ones.
+ */
+
 static int
 tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
 {
@@ -2574,33 +2665,68 @@
 	tcp_send_partial(sk);
   }
 
-  /* See if we are done. */
-  if (sk->state == TCP_TIME_WAIT) {
+  /*
+   * In the LAST_ACK case, the other end FIN'd us.  We then FIN'd them, and
+   * we are now waiting for an acknowledge to our FIN.  The other end is
+   * already in TIME_WAIT.
+   *
+   * Move to TCP_CLOSE on success.
+   */
+
+  if (sk->state == TCP_LAST_ACK) {
 	if (!sk->dead)
 		sk->state_change(sk);
+	DPRINTF((DBG_TCP, "TCP_LAST_ACK-A: %d/%d %d/%d ack/sent %d %d\n",
+	    sk->rcv_ack_seq,
+	    sk->write_seq,
+	    sk->acked_seq,
+	    sk->fin_seq,
+	    ack,
+	    sk->sent_seq
+	));
 	if (sk->rcv_ack_seq == sk->write_seq && sk->acked_seq == sk->fin_seq) {
+		DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk));
 		flag |= 1;
 		sk->state = TCP_CLOSE;
 		sk->shutdown = SHUTDOWN_MASK;
 	}
   }
 
-  if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2) {
-	if (!sk->dead) sk->state_change(sk);
+  /*
+   * Incomming ACK to a FIN we sent in the case of our initiating the close.
+   *
+   * Move to FIN_WAIT2 to await a FIN from the other end.
+   */
+
+  if (sk->state == TCP_FIN_WAIT1) {
+	if (!sk->dead) 
+		sk->state_change(sk);
 	if (sk->rcv_ack_seq == sk->write_seq) {
 		flag |= 1;
 		if (sk->acked_seq != sk->fin_seq) {
 			tcp_time_wait(sk);
 		} else {
-			DPRINTF((DBG_TCP, "tcp_ack closing socket - %X\n", sk));
-			tcp_send_ack(sk->sent_seq, sk->acked_seq, sk,
-				     th, sk->daddr);
 			sk->shutdown = SHUTDOWN_MASK;
-			sk->state = TCP_CLOSE;
+			sk->state = TCP_FIN_WAIT2;
 		}
 	}
   }
 
+  /*
+   * Incomming ACK to a FIN we sent in the case of a simultanious close.
+   *
+   * Move to TIME_WAIT
+   */
+
+  if (sk->state == TCP_CLOSING) {
+	if (!sk->dead) 
+		sk->state_change(sk);
+	if (sk->rcv_ack_seq == sk->write_seq) {
+		flag |= 1;
+		tcp_time_wait(sk);
+	}
+  }
+
 /*
  * I make no guarantees about the first clause in the following
  * test, i.e. "(!flag) || (flag&4)".  I'm not entirely sure under
@@ -2851,6 +2977,8 @@
 	DPRINTF((DBG_TCP, "data received on dead socket.\n"));
   }
 
+#ifdef NOTDEF 	/* say what?  this is handled by tcp_ack() */
+
   if (sk->state == TCP_FIN_WAIT2 &&
       sk->acked_seq == sk->fin_seq && sk->rcv_ack_seq == sk->write_seq) {
 	DPRINTF((DBG_TCP, "tcp_data: entering last_ack state sk = %X\n", sk));
@@ -2860,6 +2988,7 @@
 	sk->state = TCP_LAST_ACK;
 	if (!sk->dead) sk->state_change(sk);
   }
+#endif
 
   return(0);
 }
@@ -2920,15 +3049,27 @@
 
 
 /*
- *	This deals with incoming fins. 'Linus at 9 O'clock' 8-) 
+ *  This deals with incoming fins. 'Linus at 9 O'clock' 8-) 
+ *
+ *  If we are ESTABLISHED, a received fin moves us to CLOSE-WAIT
+ *  (and thence onto LAST-ACK and finally, CLOSED, we never enter
+ *  TIME-WAIT)
+ *
+ *  If we are in FINWAIT-1, a received FIN indicates simultanious
+ *  close and we go into CLOSING (and later onto TIME-WAIT)
+ *
+ *  If we are in FINWAIT-2, a received FIN moves us to TIME-WAIT.
+ *
  */
  
-static int tcp_fin(struct sock *sk, struct tcphdr *th, 
+static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th, 
 	 unsigned long saddr, struct device *dev)
 {
 	DPRINTF((DBG_TCP, "tcp_fin(sk=%X, th=%X, saddr=%X, dev=%X)\n",
 					sk, th, saddr, dev));
   
+	sk->fin_seq = th->seq + skb->len + th->syn + th->fin;
+
 	if (!sk->dead) 
 	{
 		sk->state_change(sk);
@@ -2939,9 +3080,12 @@
 		case TCP_SYN_RECV:
 		case TCP_SYN_SENT:
 		case TCP_ESTABLISHED:
-			/* Contains the one that needs to be acked */
+			/*
+			 * move to CLOSE_WAIT, tcp_data() already handled
+			 * sending the ack.
+			 */
 			reset_timer(sk, TIME_CLOSE, TCP_TIMEOUT_LEN);
-			sk->fin_seq = th->seq+1;
+			/*sk->fin_seq = th->seq+1;*/
 			tcp_statistics.TcpCurrEstab--;
 			sk->state = TCP_CLOSE_WAIT;
 			if (th->rst)
@@ -2949,17 +3093,46 @@
 			break;
 
 		case TCP_CLOSE_WAIT:
-		case TCP_FIN_WAIT2:
-			break; /* we got a retransmit of the fin. */
-
-		case TCP_FIN_WAIT1:
-			/* Contains the one that needs to be acked */
-			sk->fin_seq = th->seq+1;
-			sk->state = TCP_FIN_WAIT2;
+		case TCP_CLOSING:
+			/*
+			 * received a retransmission of the FIN, do
+			 * nothing.
+			 */
 			break;
+		case TCP_TIME_WAIT:
+			/*
+			 * received a retransmission of the FIN,
+			 * restart the TIME_WAIT timer.
+			 */
+			reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+			return(0);
+		case TCP_FIN_WAIT1:
+			/*
+			 * This case occurs when a simultanious close
+			 * happens, we must ack the received FIN and
+			 * enter the CLOSING state.
+			 *
+			 * XXX timeout not set properly
+			 */
 
+			reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+			/*sk->fin_seq = th->seq+1;*/
+			sk->state = TCP_CLOSING;
+			break;
+		case TCP_FIN_WAIT2:
+			/*
+			 * received a FIN -- send ACK and enter TIME_WAIT
+			 */
+			reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+			/*sk->fin_seq = th->seq+1;*/
+			sk->state = TCP_TIME_WAIT;
+			break;
+		case TCP_CLOSE:
+			/*
+			 * already in CLOSE
+			 */
+			break;
 		default:
-		case TCP_TIME_WAIT:
 			sk->state = TCP_LAST_ACK;
 	
 			/* Start the timers. */
@@ -3364,6 +3537,7 @@
 
 	case TCP_ESTABLISHED:
 	case TCP_CLOSE_WAIT:
+	case TCP_CLOSING:
 	case TCP_FIN_WAIT1:
 	case TCP_FIN_WAIT2:
 	case TCP_TIME_WAIT:
@@ -3437,7 +3611,7 @@
 		}
 
 		/* Moved: you must do data then fin bit */
-		if (th->fin && tcp_fin(sk, th, saddr, dev)) {
+		if (th->fin && tcp_fin(skb, sk, th, saddr, dev)) {
 			kfree_skb(skb, FREE_READ);
 			release_sock(sk);
 			return(0);
@@ -3622,7 +3796,7 @@
 			if (tcp_data(skb, sk, saddr, len))
 						kfree_skb(skb, FREE_READ);
 
-			if (th->fin) tcp_fin(sk, th, saddr, dev);
+			if (th->fin) tcp_fin(skb, sk, th, saddr, dev);
 			release_sock(sk);
 			return(0);
 		}
@@ -3645,7 +3819,7 @@
 			release_sock(sk);
 			return(0);
 		}
-		tcp_fin(sk, th, saddr, dev);
+		tcp_fin(skb, sk, th, saddr, dev);
 		release_sock(sk);
 		return(0);
 	}
@@ -3667,9 +3841,19 @@
 	if (sk->zapped)
 		return;	/* Afer a valid reset we can send no more */
 
-	if (sk -> state != TCP_ESTABLISHED && sk->state != TCP_CLOSE_WAIT &&
-		sk -> state != TCP_FIN_WAIT1 && sk->state != TCP_FIN_WAIT2)
+	/*
+	 * Write data can still be transmitted/retransmitted in the
+	 * following states.  If any other state is encountered, return.
+	 */
+
+	if (sk->state != TCP_ESTABLISHED && 
+	    sk->state != TCP_CLOSE_WAIT &&
+	    sk->state != TCP_FIN_WAIT1 && 
+	    sk->state != TCP_LAST_ACK &&
+	    sk->state != TCP_CLOSING
+	) {
 		return;
+	}
 
 	buff = sk->prot->wmalloc(sk,MAX_ACK_SIZE,1, GFP_ATOMIC);
 	if (buff == NULL) 
diff -u --recursive --new-file v1.1.11/linux/net/inet/timer.c linux/net/inet/timer.c
--- v1.1.11/linux/net/inet/timer.c	Tue Apr 19 10:54:06 1994
+++ linux/net/inet/timer.c	Sat May  7 14:12:59 1994
@@ -195,7 +195,7 @@
 	    DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 2\n"));
 	    sk->err = ETIMEDOUT;
 	    if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2
-	      || sk->state == TCP_LAST_ACK) {
+	      || sk->state == TCP_CLOSING) {
 	      sk->state = TCP_TIME_WAIT;
 	      reset_timer (sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
 	    } else {
diff -u --recursive --new-file v1.1.11/linux/net/inet/udp.c linux/net/inet/udp.c
--- v1.1.11/linux/net/inet/udp.c	Thu Apr 21 09:46:36 1994
+++ linux/net/inet/udp.c	Sat May  7 14:13:00 1994
@@ -36,6 +36,7 @@
  *		Alan Cox	:	Use ip_tos and ip_ttl
  *		Alan Cox	:	SNMP Mibs
  *		Alan Cox	:	MSG_DONTROUTE, and 0.0.0.0 support.
+ *		Matt Dillon	:	UDP length checks.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -653,7 +654,8 @@
 {
   	struct sock *sk;
   	struct udphdr *uh;
-
+	unsigned short ulen;
+		
 	/*
 	 *	Get the header.
 	 */
@@ -661,7 +663,22 @@
   	
   	ip_statistics.IpInDelivers++;
 
-  	
+	/*
+	 *	Validate the packet and the UDP length.
+	 */
+	 
+	ulen = ntohs(uh->len);
+
+	if (ulen > len || len < sizeof(*uh) || ulen < sizeof(*uh)) 
+	{
+		printk("UDP: short packet: %d/%d\n", ulen, len);
+		DPRINTF((DBG_UDP, "UDP: short packet %d/%d\n", ulen, len));
+		udp_statistics.UdpInErrors++;
+		kfree_skb(skb, FREE_WRITE);
+		return(0);
+	}
+	len=ulen;
+
   	sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr);
 	if (sk == NULL) 
   	{
