]> git.neil.brown.name Git - history.git/commitdiff
Import 1.1.12 1.1.12
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:29 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:29 +0000 (15:09 -0500)
30 files changed:
CREDITS
Makefile
config.in
drivers/net/slip.c
drivers/scsi/README.st [new file with mode: 0644]
drivers/scsi/st.c
drivers/scsi/st.h
drivers/scsi/wd7000.c
drivers/scsi/wd7000.h
fs/buffer.c
fs/proc/net.c
include/linux/if_arp.h
include/linux/if_slip.h [new file with mode: 0644]
include/linux/ip.h
include/linux/mtio.h
include/linux/netdevice.h
include/linux/sockios.h
include/linux/tcp.h
init/main.c
kernel/sys.c
kernel/vm86.c
net/inet/arp.c
net/inet/arp.h
net/inet/ip.c
net/inet/rarp.c [new file with mode: 0644]
net/inet/rarp.h [new file with mode: 0644]
net/inet/sock.c
net/inet/tcp.c
net/inet/timer.c
net/inet/udp.c

diff --git a/CREDITS b/CREDITS
index 68119eba2a5199ba7e6d267bdbc4263c5991e6e1..53126bfccf04f0a32cae08c7dd8b11725a771ace 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -34,7 +34,7 @@ S: 76297 Stutensee
 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: Bellevue WA 98007
 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: Dragonvagen 1 A 13
 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
@@ -451,6 +460,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
 D: Mitsumi CD-ROM driver patches March version
index da0fb25c0bb2dd5aea40ee378fa6e0305d942af0..c9e5ca42c251417777789ce2c86ef0e3c5bbc9de 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 11
+SUBLEVEL = 12
 
 all:   Version zImage
 
index 890a96b3a040b1d902bedc92b44a9f10cba01a3d..f167060804989d687130e0555067afc3d980a88a 100644 (file)
--- a/config.in
+++ b/config.in
@@ -13,6 +13,19 @@ bool 'Limit memory to low 16MB' CONFIG_MAX_16M n
 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
index 7c555f93620c93ac185ad19903dcd34c389b78e7..699f781499422c7d9046da64c1325bd7c14882bf 100644 (file)
@@ -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 @@ sl_initialize(struct slip *sl, struct device *dev)
   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 @@ slip_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
                {
                        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 --git a/drivers/scsi/README.st b/drivers/scsi/README.st
new file mode 100644 (file)
index 0000000..c750000
--- /dev/null
@@ -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
index 580b51b7ae20b2e1c9e2a036230a4f191253e21b..faf4cdd96c36d31e6e5d732064e2a1b8c9d69d3d 100644 (file)
@@ -1,42 +1,17 @@
 /*
-  SCSI Tape Driver for Linux
-
-  Version 0.02 for Linux 0.98.4 and Eric Youngdale's new scsi driver
+  SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
+  file README.st for more information.
 
   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.
-
-  Copyright 1992, 1993 Kai Makisara
+  Contribution and ideas from several people including (in alphabetical
+  order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and
+  Eric Youngdale.
+
+  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>
 #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 */
+#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
 
-/* 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_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
 #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 @@ st_chk_result(Scsi_Cmnd * SCpnt)
   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 @@ st_chk_result(Scsi_Cmnd * SCpnt)
 #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 @@ st_sleep_done (Scsi_Cmnd * SCpnt)
       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,11 +202,44 @@ write_behind_check(int dev)
           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). */
@@ -239,20 +252,19 @@ flush_write_buffer(int dev)
   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 @@ flush_write_buffer(int dev)
     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 @@ flush_write_buffer(int dev)
       }
       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 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next)
   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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
     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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
     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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
       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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
     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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
     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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
       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 @@ scsi_tape_open(struct inode * inode, struct file * filp)
 
     }
     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 @@ scsi_tape_close(struct inode * inode, struct file * filp)
       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 @@ scsi_tape_close(struct inode * inode, struct file * filp)
        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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
     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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
       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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
        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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
     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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
                    (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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
          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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
       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 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
       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_write(struct inode * inode, struct file * filp, char * buf, int count)
                   st_sleep_done, ST_TIMEOUT, MAX_RETRIES);
     }
     else
-#endif
       SCpnt->request.dev = -1;  /* Mark as not busy */
 
     return( total);
@@ -838,9 +883,13 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
     }
 #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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
     }
 
 #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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
        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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
 
        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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
                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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
              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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
                  (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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
        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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
       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 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
     return total;
 }
 
+
+\f
+/* 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;
+}
+
 \f
 /* Internal ioctl function */
        static int
@@ -1025,11 +1139,15 @@ st_int_ioctl(struct inode * inode,struct file * file,
    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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
 #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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        (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 @@ st_int_ioctl(struct inode * inode,struct file * file,
    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 @@ st_int_ioctl(struct inode * inode,struct file * file,
    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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_int_ioctl(struct inode * inode,struct file * file,
        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 @@ st_ioctl(struct inode * inode,struct file * file,
    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 @@ st_ioctl(struct inode * inode,struct file * file,
      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 @@ st_ioctl(struct inode * inode,struct file * file,
      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 @@ st_ioctl(struct inode * inode,struct file * file,
      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 @@ st_ioctl(struct inode * inode,struct file * file,
      return scsi_ioctl(STp->device, cmd_in, (void *) arg);
 }
 
-
 \f
+/* 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 @@ unsigned long st_init1(unsigned long mem_start, unsigned long mem_end){
 /* 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 @@ unsigned long st_init(unsigned long mem_start, unsigned long mem_end)
   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;
index 7b530faa68af19029de87357611fa425b105b920..9df6d7b8f7f0f53090721d4d044b17210bc6c7be 100644 (file)
@@ -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 @@ typedef struct {
   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;
 
index 0bc67ae3bdf52ee07315265dd1181e75007dbd6e..107ddc3a0b08a06b8473a56b2742cb1f4e7b8b64 100644 (file)
@@ -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>
 #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>
 #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:
   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).
 
-static int wd7000_host = 0;
-static unchar controlstat = 0;
+   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 unchar rev_1 = 0, rev_2 = 0;  /* filled in by wd7000_revision */
 
-#define wd7000_intr_ack()  outb(0,INTR_ACK)
+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 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();
-
-    if (scbfree == NULL)  {
-        panic("wd7000: can't allocate free SCB.\n");
-       restore_flags(flags);
-       return NULL;
+    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 */
+
+    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 Scb *alloc_scb(void)
 
 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 @@ static inline void init_scbs(void)
     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 @@ int make_code(unsigned hosterr, unsigned scsierr)
 
 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();
-       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 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(" ICMB %d posted for SCB/ICB %06x, status %02x, vue %02x",
-          icmb, scb, icmb_status, scb->vue );
+    printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host);
 #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;
+    flag = inb(host->iobase+ASC_INTR_STAT);
+#ifdef DEBUG
+    printk("wd7000_intr_handle: intr stat = %02x\n",flag);
+#endif
 
-           if (SCpnt->host_scribble != NULL)
-               scsi_free(SCpnt->host_scribble,WD7000_SCRIBBLE);
-           free_scb(scb);
+    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; 
+    }
 
-           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;
-
-    cdb = (unchar *) SCpnt->cmnd;
-    cdblen = COMMAND_SIZE(*cdb);
+    register Scb *scb;
+    register Sgb *sgb;
+    register unchar *cdb = (unchar *) SCpnt->cmnd;
+    register unchar idlun;
+    register short cdblen;
+    Adapter *host = (Adapter *) SCpnt->host->hostdata;
+
+    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_command(Scsi_Cmnd *SCpnt)
 }
 
 
-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;
-
-    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");
-           }   
-       }
-    }
-    if (base_address == NULL) return 0;
+    int i,j, present = 0;
+    const Config *cfg;
+    const Signature *sig;
+    Adapter *host = NULL;
+    struct Scsi_Host *sh;
 
-    snarf_region(IO_BASE, 4); /* Register our ports */
-    /* Store our host number */
-    wd7000_host = hostnum;
+    /* Set up SCB free list, which is shared by all adapters */
+    init_scbs();
 
-    wd7000_init();    
-    wd7000_revision();  /* will set scatter/gather by rev level */
+    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 */
+    }
 
-    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)
 /*
- *  This is borrowed directly from aha1542.c, but my disks are organized
- *   this way, so I think it will work OK.
+ *  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 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 @@ int wd7000_biosparam(int size, int dev, int* ip)
 /*  if (ip[2] >= 1024) ip[2] = 1024; */
   return 0;
 }
-
index ab8b2a586ad758d4a5193a9d416890b582ee78ad..162d3bfa764dfc15e7f390b2f613ce4366866861 100644 (file)
  *
  * Header file for the WD-7000 driver for Linux
  *
- * $Log: $
- * Revision 1.1  1992/07/24  06:27:38  root
- * Initial revision
- *
- * 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 ***
+ * John Boyd <boyd@cis.ohio-state.edu>  Jan 1994:
+ * This file has been reduced to only the definitions needed for the
+ * WD7000 host structure.
  *
  */
 
 #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_reset(Scsi_Cmnd *);
 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.
+ *  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.
  *
- *  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.
+ *  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
index 9745f1ead3d0bb05b9db31949d092a18b73f6ced..6cbf5ce0a97e071c9f05b587c6618d16d60becef 100644 (file)
@@ -49,7 +49,7 @@ extern int check_mcd_media_change(int, int);
 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);
index fcb95268366ad97d94c8c33fc62a8fd0c2635fd9..831d5600e80535e8f3670c9bb92ef20175ef4a06 100644 (file)
@@ -44,6 +44,7 @@ extern int tcp_get_info(char *, char **, off_t, int);
 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 @@ static struct proc_dir_entry net_dir[] = {
        { 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 @@ static int proc_readnet(struct inode * inode, struct file * file,
                        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 */
index be5d904d2846c51dc9ee95a89af1034985e2c7af..9824c89f3bb7c9f5dfdcdbd35d27e643396becbb 100644 (file)
 #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 --git a/include/linux/if_slip.h b/include/linux/if_slip.h
new file mode 100644 (file)
index 0000000..0cb7f71
--- /dev/null
@@ -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
index 26d4a5907576ef7d6e44f2a8ec121cb2ae2e5495..32c3dd1d80d1a727d8d3462a5a32345557f23959 100644 (file)
@@ -32,8 +32,17 @@ struct timestamp {
   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 options {
 
 
 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;
index 446667e388d9eaf56b859d83ed0dfe26a2aa1727..50065b059c2a31f87717e4e50fd55fa743a3633a 100644 (file)
@@ -88,6 +88,7 @@ struct        mtget {
 #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 @@ struct mt_tape_info {
        {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} \
 }
 
@@ -152,5 +154,22 @@ struct     mtpos {
  * 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 */
 
index d84456b281d18f1f8f152d50aecd502b42a8ddfe..bf479a8c5ec9692c893c55e12f9a28fd1ebc69a4 100644 (file)
@@ -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 @@ struct device
    */
   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 @@ struct device
                           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 @@ struct device
                                         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);
 };
 
 
index c678cdb227a875fc1b46a7648fd63ea77bc09300..76367caa103a727fe64ecbe8199008ae92c5a350 100644 (file)
 #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 */
index e666f510699846fb20676e89830f97ada0c32ec6..ad92080560e35afa4ae828419d9f740328f6dfbf 100644 (file)
@@ -26,6 +26,7 @@ struct tcphdr {
   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 @@ struct tcphdr {
                        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 @@ enum {
   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 */
index dcc3e53a93b988e81287de815a098284b869d06c..1619ab722947f980545fd8bc9ddee6b9c82d5e75 100644 (file)
@@ -84,6 +84,7 @@ extern void bmouse_setup(char *str, int *ints);
 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);
@@ -176,6 +177,9 @@ struct {
 #ifdef CONFIG_BLK_DEV_HD
        { "hd=", hd_setup },
 #endif
+#ifdef CONFIG_CHR_DEV_ST
+       { "st=", st_setup },
+#endif
 #ifdef CONFIG_BUSMOUSE
        { "bmouse=", bmouse_setup },
 #endif
index 0fdd140d0682a0063b1736a4f30a479d8c6c31a1..abbd3659213e79fe8eef3b87eebe2f4bad7c8735 100644 (file)
@@ -227,7 +227,7 @@ asmlinkage int sys_setregid(gid_t rgid, gid_t egid)
                }
        }
        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 @@ asmlinkage int sys_setreuid(uid_t ruid, uid_t euid)
                }
        }
        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;
 }
index e742c02519257a488ab307b891194dbefbda8384..3b3ecb0b5d3c76e7342edf96b9d3c51d049e9ae3 100644 (file)
@@ -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 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned
        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 @@ static void do_int(struct vm86_regs *regs, int i, unsigned char * ssp, unsigned
 
 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)
index 826a122ef273d7d29443c7f22b1d4794467aef53..18bb863c7d1af307ef6d3ed6fa0b47dda2f01bf6 100644 (file)
@@ -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 @@ struct arp_table
        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 @@ static void arp_release_entry(struct arp_table *entry)
  *     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 @@ static void arp_send(int type, unsigned long dest_ip, struct device *dev,
         *      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 @@ static void arp_expire_request (unsigned long arg)
                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 @@ void arp_destroy(unsigned long ip_addr, int force)
 
 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 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
         *      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 @@ int arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
 
 /*
  *     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;
 }
 
index 2b1f239b9ec206d880b3bfed109047ee4fe22899..7f59ca3520aa369e9ea1d8b430ce646274ad0b6f 100644 (file)
@@ -10,5 +10,8 @@ extern int    arp_find(unsigned char *haddr, unsigned long paddr,
                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 */
index 4d8a2cd4004a338c5372f98920c8fbf42d1eb2ec..fd5b5f570bb39d830c73ce684ad5d13aa9971b79 100644 (file)
@@ -1273,7 +1273,7 @@ static struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct
                 *      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 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
        ip_statistics.IpInReceives++;
        
        DPRINTF((DBG_IP, "<<\n"));
-
+       
        /*
         *      Tag the ip header of this packet so we can find it
         */
diff --git a/net/inet/rarp.c b/net/inet/rarp.c
new file mode 100644 (file)
index 0000000..5badcbe
--- /dev/null
@@ -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 --git a/net/inet/rarp.h b/net/inet/rarp.h
new file mode 100644 (file)
index 0000000..02ee778
--- /dev/null
@@ -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 */
+
index de345ac8fe6ab359aa4fca5a3d1d44eb96ad756e..233337f0a33b4ec36fb2fe40a7217aafc29f8545 100644 (file)
@@ -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 @@ inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
        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 @@ inet_fioctl(struct inode *inode, struct file *file,
 
   /* 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 @@ void inet_proto_init(struct ddi_proto *pro)
        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)
         */
index 18c097cbda650aa98c4e463933a5b0c6d53993ab..c3037a2a15dd1869a3381b53f309d2f63eeba238 100644 (file)
@@ -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:
  *             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>
 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 @@ static int tcp_read(struct sock *sk, unsigned char *to,
 
  
 /*
- * 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 @@ void tcp_shutdown(struct sock *sk, int how)
         * 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 @@ void tcp_shutdown(struct sock *sk, int how)
        {
                /*
                 *      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 @@ void tcp_shutdown(struct sock *sk, int how)
 
        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 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
   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 @@ static void tcp_close(struct sock *sk, int timeout)
        {
                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 @@ static void tcp_close(struct sock *sk, int timeout)
                         * 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 @@ static void tcp_close(struct sock *sk, int timeout)
                        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 @@ static void tcp_close(struct sock *sk, int timeout)
                                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 @@ sort_send(struct sock *sk)
 }
   
 
-/* 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_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
        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 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
        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 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
        sk->state = TCP_LAST_ACK;
        if (!sk->dead) sk->state_change(sk);
   }
+#endif
 
   return(0);
 }
@@ -2920,15 +3049,27 @@ static inline int tcp_urg(struct sock *sk, struct tcphdr *th,
 
 
 /*
- *     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 @@ static int tcp_fin(struct sock *sk, struct tcphdr *th,
                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 @@ static int tcp_fin(struct sock *sk, struct tcphdr *th,
                        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 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
 
        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 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
                }
 
                /* 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 (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
                        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 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
                        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 @@ static void tcp_write_wakeup(struct sock *sk)
        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) 
index 09c3ac84d99b8f4633756dcfaa41ff26acef4d2e..8f34745a42ef4f49323a8cd1182c8b8b6c141695 100644 (file)
@@ -195,7 +195,7 @@ net_timer (unsigned long data)
            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 {
index 2d63a4b1db2721087b880ce704d08b0b0a01e073..ce17a40dfa24302063f633d51cbcc9f1835aba7c 100644 (file)
@@ -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 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
 {
        struct sock *sk;
        struct udphdr *uh;
-
+       unsigned short ulen;
+               
        /*
         *      Get the header.
         */
@@ -661,7 +663,22 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
        
        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) 
        {