patch-1.3.96 linux/drivers/scsi/wd33c93.c

Next file: linux/drivers/scsi/wd33c93.h
Previous file: linux/drivers/scsi/scsi.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v1.3.95/linux/drivers/scsi/wd33c93.c linux/drivers/scsi/wd33c93.c
@@ -28,18 +28,23 @@
  *
  *    -  Target Disconnection/Reconnection  is now supported. Any
  *          system with more than one device active on the SCSI bus
- *          will benefit from this.
+ *          will benefit from this. The driver defaults to what I'm
+ *          'adaptive disconnect' - meaning that each command is
+ *          evaluated individually as to whether or not it should
+ *          be run with the option to disconnect/reslect (if the
+ *          device chooses), or as a "SCSI-bus-hog".
  *
- *    -  Synchronous data transfers are now supported. The driver
- *          automatically uses this faster protocol with any device
- *          able to handle it.
+ *    -  Synchronous data transfers are now supported. Because of
+ *          a few devices that choke after telling the driver that
+ *          they can do sync transfers, we don't automatically use
+ *          this faster protocol - it can be enabled via the command-
+ *          line on a device-by-device basis.
  *
  *    -  Runtime operating parameters can now be specified through
- *       either the 'amiboot' or the LILO command line. Something
- *       like:
- *          "wd33c93=0x0000"
- *       The value 0x0000 results in the defaults being used; bits
- *       are defined in wd33c93.h.
+ *       the 'amiboot' or the 'insmod' command line. For amiboot do:
+ *          "amiboot [usual stuff] wd33c93=blah,blah,blah"
+ *       The defaults should be good for most people. See the comment
+ *       for 'setup_strings' below for more details.
  *
  *    -  The old driver relied exclusively on what the Western Digital
  *          docs call "Combination Level 2 Commands", which are a great
@@ -52,7 +57,7 @@
  *
  *
  * TODO:
- *       more speed. tagged queuing.
+ *       more speed. linked commands.
  *
  *
  * People with bug reports, wish-lists, complaints, comments,
@@ -67,18 +72,33 @@
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/delay.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x010300
 #include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+
 #include "scsi.h"
 #include "hosts.h"
 #include "wd33c93.h"
 
+#ifdef MODULE
+#include <linux/module.h>
+#endif
 
-#define SYNC_DEBUG
+/* Leave this undefined for now - need to make some changes in the
+ * a3000/a2019/gvp11 files to get it working right
+ */
+/*#define PROC_INTERFACE*/     /* add code for /proc/scsi/wd33c93/xxx interface */
 
-#define DEBUGGING_ON
+#define SYNC_DEBUG         /* extra info on sync negotiation printed */
+#define DEBUGGING_ON       /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0   /* default debugging bitmask */
 
-#define WD33C93_VERSION    "1.17"
-#define WD33C93_DATE       "06/Feb/1996"
+#define WD33C93_VERSION    "1.21"
+#define WD33C93_DATE       "20/Apr/1996"
 
 #ifdef DEBUGGING_ON
 #define DB(f,a) if (hostdata->args & (f)) a;
@@ -92,15 +112,59 @@
 
 
 /*
- * setup_default is a bunch of bits that define some of the operating
- * parameters and settings for this driver. It is used unless a LILO
- * or insmod command line has been specified, in which case setup_default
- * is _completely_ ignored. Take a look at the "defines for hostdata->args"
- * section in wd33c93.h - that stuff is what you'd use here if you want
- * to change the defaults.
+ * setup_strings is an array of strings that define some of the operating
+ * parameters and settings for this driver. It is used unless an amiboot
+ * or insmod command line has been specified, in which case those settings
+ * are combined with the ones here. The driver recognizes the following
+ * keywords (lower case required) and arguments:
+ *
+ * -  nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with
+ *                    the 7 possible SCSI devices. Set a bit to prevent sync
+ *                    negotiation on that device. To maintain backwards
+ *                    compatability, a command-line such as "wd33c93=255" will
+ *                    be automatically translated to "wd33c93=nosync:0xff".
+ * -  period:ns      -ns is the minimum # of nanoseconds in a SCSI data transfer
+ *                    period. Default is 500; acceptable values are 250 - 1000.
+ * -  disconnect:x   -x = 0 to never allow disconnects, 2 to always allow them.
+ *                    x = 1 does 'adaptive' disconnects, which is the default
+ *                    and generally the best choice.
+ * -  debug:x        -If 'DEBUGGING_ON' is defined, x is a bit mask that causes
+ *                    various types of debug output to printed - see the DB_xxx
+ *                    defines in wd33c93.h
+ * -  clock:x        -x = clock input in MHz for WD33c93 chip. Normal values
+ *                    would be from 8 through 20. Default is 8.
+ * -  next           -No argument. Used to separate blocks of keywords when
+ *                    there's more than one host adapter in the system.
+ *
+ * Syntax Notes:
+ * -  Numeric arguments can be decimal or the '0x' form of hex notation. There
+ *    _must_ be a colon between a keyword and its numeric argument, with no
+ *    spaces.
+ * -  Keywords are separated by commas, no spaces, in the standard kernel
+ *    command-line manner, except in the case of 'setup_strings[]' (see
+ *    below), which is simply a C array of pointers to char. Each element
+ *    in the array is a string comprising one keyword & argument.
+ * -  A keyword in the 'nth' comma-separated command-line member will overwrite
+ *    the 'nth' element of setup_strings[]. A blank command-line member (in
+ *    other words, a comma with no preceding keyword) will _not_ overwrite
+ *    the corresponding setup_strings[] element.
+ * -  If a keyword is used more than once, the first one applies to the first
+ *    SCSI host found, the second to the second card, etc, unless the 'next'
+ *    keyword is used to change the order.
+ *
+ * Some amiboot examples (for insmod, use 'setup_strings' instead of 'wd33c93'):
+ * -  wd33c93=nosync:255
+ * -  wd33c93=disconnect:2,nosync:0x08,period:250
+ * -  wd33c93=debug:0x1c
  */
 
-static unsigned int setup_default = 0;
+static char *setup_strings[] =
+      {"","","","","","","","","","","",""};
+
+#ifdef PROC_INTERFACE
+unsigned long disc_allowed_total;
+unsigned long disc_taken_total;
+#endif
 
 
 inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num)
@@ -176,27 +240,32 @@
    {1000,0x00},
    {0,   0} };
 
-uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+int round_period(unsigned int period)
 {
-uchar result;
 int x;
 
-   period *= 4;   /* convert SDTR code to ns */
-   result = 0x00;
    for (x=1; sx_table[x].period_ns; x++) {
       if ((period <= sx_table[x-0].period_ns) &&
           (period >  sx_table[x-1].period_ns)) {
-         result = sx_table[x].reg_value;
-         break;
+         return x;
          }
       }
+   return 7;
+}
+
+uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+{
+uchar result;
+
+   period *= 4;   /* convert SDTR code to ns */
+   result = sx_table[round_period(period)].reg_value;
    result |= (offset < OPTIMUM_SX_OFF)?offset:OPTIMUM_SX_OFF;
    return result;
 }
 
 
 
-static void wd33c93_execute(struct Scsi_Host *instance);
+void wd33c93_execute(struct Scsi_Host *instance);
 
 int wd33c93_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
 {
@@ -204,13 +273,14 @@
 Scsi_Cmnd *tmp;
 unsigned long flags;
 
+
+   save_flags(flags);
+   cli();
    hostdata = (struct WD33C93_hostdata *)cmd->host->hostdata;
 
 DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld( ",cmd->target,cmd->cmnd[0],cmd->pid))
 
-
-/*
- * Set up a few fields in the Scsi_Cmnd structure for our own use:
+/* Set up a few fields in the Scsi_Cmnd structure for our own use:
  *  - host_scribble is the pointer to the next cmd in the input queue
  *  - scsi_done points to the routine we call when a cmd is finished
  *  - result is what you'd expect
@@ -230,6 +300,9 @@
  *  - SCp.this_residual is the size of that buffer
  *  - SCp.buffer points to the current scatter-gather buffer
  *  - SCp.buffers_residual tells us how many S.G. buffers there are
+ *  - SCp.have_data_in is not used
+ *  - SCp.sent_command is not used
+ *  - SCp.phase records this command's SRCID_ER bit setting
  */
 
    if (cmd->use_sg) {
@@ -255,19 +328,16 @@
     * sense data is not lost before REQUEST_SENSE executes.
     */
 
-   save_flags(flags);
-   cli();
    if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
       cmd->host_scribble = (uchar *)hostdata->input_Q;
       hostdata->input_Q = cmd;
       }
-   else {
+   else {   /* find the end of the queue */
       for (tmp=(Scsi_Cmnd *)hostdata->input_Q; tmp->host_scribble;
             tmp=(Scsi_Cmnd *)tmp->host_scribble)
          ;
       tmp->host_scribble = (uchar *)cmd;
       }
-   restore_flags(flags);
 
 /* We know that there's at least one command in 'input_Q' now.
  * Go see if any of them are runnable!
@@ -275,7 +345,9 @@
 
    wd33c93_execute(cmd->host);
 
-DB(DB_QUEUE_COMMAND,printk(")Q-%d-%02x-%ld ",cmd->target,cmd->cmnd[0],cmd->pid))
+DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid))
+
+   restore_flags(flags);
    return 0;
 }
 
@@ -287,7 +359,7 @@
  * the input_Q, using the first command we find that's intended
  * for a currently non-busy target/lun.
  */
-static void wd33c93_execute (struct Scsi_Host *instance)
+void wd33c93_execute (struct Scsi_Host *instance)
 {
 struct WD33C93_hostdata *hostdata;
 wd33c93_regs *regp;
@@ -301,10 +373,12 @@
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
    regp = hostdata->regp;
 
-DB(DB_EXECUTE,printk("EX( "))
+DB(DB_EXECUTE,printk("EX("))
 
    if (hostdata->selecting || hostdata->connected) {
+
 DB(DB_EXECUTE,printk(")EX-0 "))
+
       restore_flags(flags);
       return;
       }
@@ -326,18 +400,19 @@
    /* quit if queue empty or all possible targets are busy */
 
    if (!cmd) {
+
 DB(DB_EXECUTE,printk(")EX-1 "))
+
       restore_flags(flags);
       return;
       }
 
-   /*  remove command from queue, put it in selecting */
+   /*  remove command from queue */
    
    if (prev)
       prev->host_scribble = cmd->host_scribble;
    else
       hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
-   hostdata->selecting = cmd;
 
    /*
     * Start the selection process
@@ -348,22 +423,78 @@
    else
       write_wd33c93(regp, WD_DESTINATION_ID, cmd->target | DSTID_DPD);
 
+/* Now we need to figure out whether or not this command is a good
+ * candidate for disconnect/reselect. We guess to the best of our
+ * ability, based on a set of hierarchical rules. When several
+ * devices are operating simultaneously, disconnects are usually
+ * an advantage. In a single device system, or if only 1 device
+ * is being accessed, transfers usually go faster if disconnects
+ * are not allowed:
+ *
+ * + Commands should NEVER disconnect if hostdata->disconnect =
+ *   DIS_NEVER (this holds for tape drives also), and ALWAYS
+ *   disconnect if hostdata->disconnect = DIS_ALWAYS.
+ * + Tape drive commands should always be allowed to disconnect.
+ * + Disconnect should be allowed if disconnected_Q isn't empty.
+ * + Commands should NOT disconnect if input_Q is empty.
+ * + Disconnect should be allowed if there are commands in input_Q
+ *   for a different target/lun. In this case, the other commands
+ *   should be made disconnect-able, if not already.
+ *
+ * I know, I know - this code would flunk me out of any
+ * "C Programming 101" class ever offered. But it's easy
+ * to change around and experiment with for now.
+ */
+
+   cmd->SCp.phase = 0;  /* assume no disconnect */
+   if (hostdata->disconnect == DIS_NEVER)
+      goto no;
+   if (hostdata->disconnect == DIS_ALWAYS)
+      goto yes;
+   if (cmd->device->type == 1)   /* tape drive? */
+      goto yes;
+   if (hostdata->disconnected_Q) /* other commands disconnected? */
+      goto yes;
+   if (!(hostdata->input_Q))     /* input_Q empty? */
+      goto no;
+   for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+         prev=(Scsi_Cmnd *)prev->host_scribble) {
+      if ((prev->target != cmd->target) || (prev->lun != cmd->lun)) {
+         for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+               prev=(Scsi_Cmnd *)prev->host_scribble)
+            prev->SCp.phase = 1;
+         goto yes;
+         }
+      }
+   goto no;
+
+yes:
+   cmd->SCp.phase = 1;
+
+#ifdef PROC_INTERFACE
+   disc_allowed_total++;
+#endif
+
+no:
+   write_wd33c93(regp, WD_SOURCE_ID, ((cmd->SCp.phase)?SRCID_ER:0));
+
    write_wd33c93(regp, WD_TARGET_LUN, cmd->lun);
-   write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
-   write_wd33c93_count(regp, 0); /* this guarantees a DATA_PHASE interrupt */
+   write_wd33c93(regp,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
    hostdata->busy[cmd->target] |= (1 << cmd->lun);
 
    if ((hostdata->level2 == L2_NONE) ||
        (hostdata->sync_stat[cmd->target] == SS_UNSET)) {
 
          /*
-          * Now do a 'Select-With-ATN' command. This will end with
+          * Do a 'Select-With-ATN' command. This will end with
           * one of the following interrupts:
           *    CSR_RESEL_AM:  failure - can try again later.
           *    CSR_TIMEOUT:   failure - give up.
           *    CSR_SELECT:    success - proceed.
           */
 
+      hostdata->selecting = cmd;
+
 /* Every target has its own synchronous transfer setting, kept in the
  * sync_xfer array, and a corresponding status byte in sync_stat[].
  * Each target's sync_stat[] entry is initialized to SX_UNSET, and its
@@ -376,19 +507,20 @@
  * make the defaults final.
  */
       if (hostdata->sync_stat[cmd->target] == SS_UNSET) {
-         if (hostdata->args & (1 << cmd->target))
+         if (hostdata->no_sync & (1 << cmd->target))
             hostdata->sync_stat[cmd->target] = SS_SET;
          else
             hostdata->sync_stat[cmd->target] = SS_FIRST;
          }
       hostdata->state = S_SELECTING;
+      write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */
       write_wd33c93_cmd(regp, WD_CMD_SEL_ATN);
       }
 
    else {
 
          /*
-          * Now do a 'Select-With-ATN-Xfer' command. This will end with
+          * Do a 'Select-With-ATN-Xfer' command. This will end with
           * one of the following interrupts:
           *    CSR_RESEL_AM:  failure - can try again later.
           *    CSR_TIMEOUT:   failure - give up.
@@ -396,13 +528,15 @@
           */
 
       hostdata->connected = cmd;
-      hostdata->selecting = NULL;
       write_wd33c93(regp, WD_COMMAND_PHASE, 0);
 
-   /* copy command_descriptor_block into WD chip */
+   /* copy command_descriptor_block into WD chip
+    * (take advantage of auto-incrementing)
+    */
 
+      regp->SASR = WD_CDB_1;
       for (i=0; i<cmd->cmd_len; i++)
-         write_wd33c93(regp, WD_CDB_1+i, cmd->cmnd[i]);
+         regp->SCMD = cmd->cmnd[i];
 
    /* The wd33c93 only knows about Group 0, 1, and 5 commands when
     * it's doing a 'select-and-transfer'. To be safe, we write the
@@ -412,6 +546,23 @@
 
       write_wd33c93(regp, WD_OWN_ID, cmd->cmd_len);
 
+   /* When doing a non-disconnect command, we can save ourselves a DATA
+    * phase interrupt later by setting everything up now.
+    */
+
+      if (cmd->SCp.phase == 0) {
+         if (hostdata->dma_setup(cmd,
+                     (IS_DIR_OUT(cmd))?DATA_OUT_DIR:DATA_IN_DIR))
+            write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */
+         else {
+            write_wd33c93_count(regp, cmd->SCp.this_residual);
+            write_wd33c93(regp,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);
+            hostdata->dma = D_DMA_RUNNING;
+            }
+         }
+      else
+         write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */
+
       hostdata->state = S_RUNNING_LEVEL2;
       write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
       }
@@ -423,47 +574,38 @@
     * to search the input_Q again...
     */
       
-DB(DB_EXECUTE,printk(")EX-2 "))
+DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))
+
    restore_flags(flags);
 }
 
 
 
-void transfer_pio(wd33c93_regs *regp, uchar **buf, int *cnt,
+void transfer_pio(wd33c93_regs *regp, uchar *buf, int cnt,
                   int data_in_dir, struct WD33C93_hostdata *hostdata)
 {
-uchar *b, asr;
-int c;
+uchar asr;
+
+DB(DB_TRANSFER,printk("(%p,%d,%s)",buf,cnt,data_in_dir?"in":"out"))
 
    write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
-   b = *buf;
-   c = *cnt;
-DB(DB_TRANSFER_DATA,printk("[[%p/%d]]",b,c))
-   write_wd33c93_count(regp,c);
+   write_wd33c93_count(regp,cnt);
    write_wd33c93_cmd(regp, WD_CMD_TRANS_INFO);
    if (data_in_dir) {
       do {
          asr = READ_AUX_STAT();
          if (asr & ASR_DBR)
-            *b++ = read_wd33c93(regp, WD_DATA);
+            *buf++ = read_wd33c93(regp, WD_DATA);
          } while (!(asr & ASR_INT));
       }
    else {
       do {
          asr = READ_AUX_STAT();
          if (asr & ASR_DBR)
-            write_wd33c93(regp, WD_DATA, *b++);
+            write_wd33c93(regp, WD_DATA, *buf++);
          } while (!(asr & ASR_INT));
       }
 
-/* update original buffer pointer and original count */
-
-   *cnt = read_wd33c93_count(regp);
-   if (data_in_dir)
-      *buf = b;
-   else
-      *buf += (c - *cnt);
-
    /* Note: we are returning with the interrupt UN-cleared.
    * Since (presumably) an entire I/O operation has
    * completed, the bus phase is probably different, and
@@ -501,7 +643,7 @@
 /* 'dma_setup()' will return TRUE if we can't do DMA. */
 
    if (hostdata->dma_setup(cmd, data_in_dir)) {
-      transfer_pio(regp, (uchar **)&cmd->SCp.ptr, &cmd->SCp.this_residual,
+      transfer_pio(regp, (uchar *)&cmd->SCp.ptr, cmd->SCp.this_residual,
                          data_in_dir, hostdata);
       }
 
@@ -516,11 +658,10 @@
 
    else {
       write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);
-DB(DB_TRANSFER_DATA,printk("[%p/%d]",cmd->SCp.ptr,cmd->SCp.this_residual))
       write_wd33c93_count(regp,cmd->SCp.this_residual);
 
-      if (hostdata->level2 >= L2_DATA) {
-         write_wd33c93(regp, WD_COMMAND_PHASE, 0x41);
+      if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+         write_wd33c93(regp, WD_COMMAND_PHASE, 0x45);
          write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
          hostdata->state = S_RUNNING_LEVEL2;
          }
@@ -538,9 +679,9 @@
 struct WD33C93_hostdata *hostdata;
 Scsi_Cmnd *patch, *cmd;
 wd33c93_regs *regp;
+unsigned long flags;
 uchar asr, sr, phs, id, lun, *ucp, msg;
 unsigned long length;
-int i;
 
 
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
@@ -550,6 +691,11 @@
    if (!(asr & ASR_INT) || (asr & ASR_BSY))
       return;
 
+/* OK - it should be safe to re-enable system interrupts */
+
+   save_flags(flags);
+   sti();
+
    cmd = (Scsi_Cmnd *)hostdata->connected;   /* assume we're connected */
    sr = read_wd33c93(regp, WD_SCSI_STATUS);  /* clear the interrupt */
    phs = read_wd33c93(regp, WD_COMMAND_PHASE);
@@ -572,13 +718,13 @@
  */
 
    if (hostdata->dma == D_DMA_RUNNING) {
-DB(DB_TRANSFER_DATA,printk("[%p/%d:",cmd->SCp.ptr,cmd->SCp.this_residual))
+DB(DB_TRANSFER,printk("[%p/%d:",cmd->SCp.ptr,cmd->SCp.this_residual))
       hostdata->dma_stop(cmd->host, cmd, 1);
       hostdata->dma = D_DMA_OFF;
       length = cmd->SCp.this_residual;
       cmd->SCp.this_residual = read_wd33c93_count(regp);
       cmd->SCp.ptr += (length - cmd->SCp.this_residual);
-DB(DB_TRANSFER_DATA,printk("%p/%d]",cmd->SCp.ptr,cmd->SCp.this_residual))
+DB(DB_TRANSFER,printk("%p/%d]",cmd->SCp.ptr,cmd->SCp.this_residual))
       }
 
 /* Respond to the specific WD3393 interrupt - there are quite a few! */
@@ -586,26 +732,26 @@
    switch (sr) {
 
       case CSR_TIMEOUT:
-         cli();
 DB(DB_INTR,printk("TIMEOUT"))
-         if (hostdata->state == S_RUNNING_LEVEL2) {
+
+         cli();
+         if (hostdata->state == S_RUNNING_LEVEL2)
             hostdata->connected = NULL;
-            hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
-            }
          else {
             cmd = (Scsi_Cmnd *)hostdata->selecting;   /* get a valid cmd */
             hostdata->selecting = NULL;
             }
 
          cmd->result = DID_NO_CONNECT << 16;
-         hostdata->selecting = NULL;
+         hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
          hostdata->state = S_UNCONNECTED;
-         cmd->scsi_done(cmd);    /* I think scsi_done() enables ints */
+         cmd->scsi_done(cmd);
 
 /* We are not connected to a target - check to see if there
  * are commands waiting to be executed.
  */
 
+         sti();
          wd33c93_execute(instance);
          break;
 
@@ -620,10 +766,9 @@
 
       /* construct an IDENTIFY message with correct disconnect bit */
 
-         if (hostdata->args & A_NO_DISCONNECT)
-            hostdata->outgoing_msg[0] = (0x80 | cmd->lun);
-         else
-            hostdata->outgoing_msg[0] = (0x80 | 0x40 | cmd->lun);
+         hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->lun);
+         if (cmd->SCp.phase)
+            hostdata->outgoing_msg[0] |= 0x40;
 
          if (hostdata->sync_stat[cmd->target] == SS_FIRST) {
 #ifdef SYNC_DEBUG
@@ -671,11 +816,10 @@
 /* Note: this interrupt should not occur in a LEVEL2 command */
 
       case CSR_XFER_DONE|PHS_COMMAND:
+      case CSR_UNEXP    |PHS_COMMAND:
       case CSR_SRV_REQ  |PHS_COMMAND:
 DB(DB_INTR,printk("CMND-%02x,%ld",cmd->cmnd[0],cmd->pid))
-         ucp = cmd->cmnd;
-         i = cmd->cmd_len;
-         transfer_pio(regp, &ucp, &i, DATA_OUT_DIR, hostdata);
+         transfer_pio(regp, cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata);
          hostdata->state = S_CONNECTED;
          break;
 
@@ -704,6 +848,7 @@
       case CSR_SRV_REQ  |PHS_MESS_IN:
 DB(DB_INTR,printk("MSG_IN="))
 
+         cli();
          msg = read_1_byte(regp);
          sr = read_wd33c93(regp, WD_SCSI_STATUS);  /* clear interrupt */
 
@@ -730,7 +875,6 @@
 
             case RESTORE_POINTERS:
 DB(DB_INTR,printk("RDP"))
-
                if (hostdata->level2 >= L2_BASIC) {
                   write_wd33c93(regp, WD_COMMAND_PHASE, 0x45);
                   write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
@@ -777,24 +921,50 @@
                      case EXTENDED_SDTR:
                         id = calc_sync_xfer(ucp[3],ucp[4]);
                         if (hostdata->sync_stat[cmd->target] != SS_WAITING) {
-                           printk("Rejecting target's SDTR message ");
+
+/* A device has sent an unsolicited SDTR message; rather than go
+ * through the effort of decoding it and then figuring out what
+ * our reply should be, we're just gonna say that we have a
+ * synchronous fifo depth of 0. This will result in asynchronous
+ * transfers - not ideal but so much easier.
+ * Actually, this is OK because it assures us that if we don't
+ * specifically ask for sync transfers, we won't do any.
+ */
+
                            write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
-                           hostdata->outgoing_msg[0] = MESSAGE_REJECT;
-                           hostdata->outgoing_len = 1;
+                           hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+                           hostdata->outgoing_msg[1] = 3;
+                           hostdata->outgoing_msg[2] = EXTENDED_SDTR;
+                           hostdata->outgoing_msg[3] = hostdata->default_sx_per/4;
+                           hostdata->outgoing_msg[4] = 0;
+                           hostdata->outgoing_len = 5;
+                           hostdata->sync_xfer[cmd->target] =
+                                       calc_sync_xfer(hostdata->default_sx_per/4,0);
                            }
                         else {
                            hostdata->sync_xfer[cmd->target] = id;
-                           hostdata->sync_stat[cmd->target] = SS_SET;
                            }
 #ifdef SYNC_DEBUG
-printk("sync_xfer=%02x",id);
+printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]);
 #endif
+                        hostdata->sync_stat[cmd->target] = SS_SET;
+                        write_wd33c93_cmd(regp,WD_CMD_NEGATE_ACK);
+                        hostdata->state = S_CONNECTED;
+                        break;
+                     case EXTENDED_WDTR:
+                        write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+                        printk("sending WDTR ");
+                        hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+                        hostdata->outgoing_msg[1] = 2;
+                        hostdata->outgoing_msg[2] = EXTENDED_WDTR;
+                        hostdata->outgoing_msg[3] = 0;   /* 8 bit transfer width */
+                        hostdata->outgoing_len = 4;
                         write_wd33c93_cmd(regp,WD_CMD_NEGATE_ACK);
                         hostdata->state = S_CONNECTED;
                         break;
                      default:
-                        printk("Rejecting Unknown Extended Message(%02x). ",ucp[2]);
                         write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+                        printk("Rejecting Unknown Extended Message(%02x). ",ucp[2]);
                         hostdata->outgoing_msg[0] = MESSAGE_REJECT;
                         hostdata->outgoing_len = 1;
                         write_wd33c93_cmd(regp,WD_CMD_NEGATE_ACK);
@@ -814,7 +984,7 @@
                break;
 
             default:
-               printk("Rejecting Unknown Message(%02x) ",ucp[0]);
+               printk("Rejecting Unknown Message(%02x) ",msg);
                write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
                hostdata->outgoing_msg[0] = MESSAGE_REJECT;
                hostdata->outgoing_len = 1;
@@ -828,6 +998,12 @@
 
       case CSR_SEL_XFER_DONE:
          cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+         write_wd33c93(regp,WD_SOURCE_ID, SRCID_ER);
          if (phs == 0x60) {
 DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid))
             cmd->SCp.Message = COMMAND_COMPLETE;
@@ -841,7 +1017,7 @@
                cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
             hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
             hostdata->state = S_UNCONNECTED;
-            cmd->scsi_done(cmd);    /* I think scsi_done() enables ints */
+            cmd->scsi_done(cmd);
 
 /* We are no longer  connected to a target - check to see if
  * there are commands waiting to be executed.
@@ -877,8 +1053,8 @@
  * it - like when our SDTR message is rejected by a target. Some
  * targets send the REJECT before receiving all of the extended
  * message, and then seem to go back to MESSAGE_OUT for a byte
- * or two. Not sure why, ot if I'm doing something wrong to
- * casue this to happen. Regardless, it seems that sending
+ * or two. Not sure why, or if I'm doing something wrong to
+ * cause this to happen. Regardless, it seems that sending
  * NOP messages in these situations results in no harm and
  * makes everyone happy.
  */
@@ -887,22 +1063,67 @@
             hostdata->outgoing_len = 1;
             hostdata->outgoing_msg[0] = NOP;
             }
-         ucp = hostdata->outgoing_msg;
-         i = hostdata->outgoing_len;
-         transfer_pio(regp, &ucp, &i, DATA_OUT_DIR, hostdata);
+         transfer_pio(regp, hostdata->outgoing_msg, hostdata->outgoing_len,
+                            DATA_OUT_DIR, hostdata);
 DB(DB_INTR,printk("%02x",hostdata->outgoing_msg[0]))
          hostdata->outgoing_len = 0;
          hostdata->state = S_CONNECTED;
          break;
 
 
-      case CSR_DISC:
-DB(DB_INTR,printk("DISC"))
+      case CSR_UNEXP_DISC:
+
+/* I think I've seen this after a request-sense that was in response
+ * to an error condition, but not sure. We certainly need to do
+ * something when we get this interrupt - the question is 'what?'.
+ * Let's think positively, and assume some command has finished
+ * in a legal manner (like a command that provokes a request-sense),
+ * so we treat it as a normal command-complete-disconnect.
+ */
+
+         cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+         write_wd33c93(regp,WD_SOURCE_ID, SRCID_ER);
          if (cmd == NULL) {
             printk(" - Already disconnected! ");
             hostdata->state = S_UNCONNECTED;
             return;
             }
+DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
+         hostdata->connected = NULL;
+         hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+         hostdata->state = S_UNCONNECTED;
+         if (cmd->cmnd[0] != REQUEST_SENSE)
+            cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+         else if (cmd->SCp.Status != GOOD)
+            cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+         cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+         wd33c93_execute(instance);
+         break;
+
+
+      case CSR_DISC:
+         cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+         write_wd33c93(regp,WD_SOURCE_ID, SRCID_ER);
+DB(DB_INTR,printk("DISC-%ld",cmd->pid))
+         if (cmd == NULL) {
+            printk(" - Already disconnected! ");
+            hostdata->state = S_UNCONNECTED;
+            }
          switch (hostdata->state) {
             case S_PRE_CMP_DISC:
                hostdata->connected = NULL;
@@ -912,7 +1133,7 @@
                   cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
                else if (cmd->SCp.Status != GOOD)
                   cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
-               cmd->scsi_done(cmd);    /* I think scsi_done() enables ints */
+               cmd->scsi_done(cmd);
                break;
             case S_PRE_TMP_DISC:
             case S_RUNNING_LEVEL2:
@@ -920,6 +1141,11 @@
                hostdata->disconnected_Q = cmd;
                hostdata->connected = NULL;
                hostdata->state = S_UNCONNECTED;
+
+#ifdef PROC_INTERFACE
+               disc_taken_total++;
+#endif
+
                break;
             default:
                printk("*** Unexpected DISCONNECT interrupt! ***");
@@ -943,7 +1169,7 @@
    /* happen during Arbitration/Selection of some other device. */
    /* If yes, put losing command back on top of input_Q. */
 
-         if (hostdata->level2 == L2_NONE) {
+         if (hostdata->level2 <= L2_NONE) {
 
             if (hostdata->selecting) {
                cmd = (Scsi_Cmnd *)hostdata->selecting;
@@ -971,7 +1197,7 @@
 
             }
 
-   /* OK - find out which device reslected us. */
+   /* OK - find out which device reselected us. */
 
          id = read_wd33c93(regp, WD_SOURCE_ID);
          id &= SRCID_MASK;
@@ -1034,9 +1260,11 @@
          break;
          
       default:
-         printk("\n---UNKNOWN INTERRUPT:%02x:%02x:%02x!!---",asr,sr,phs);
+         printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--",asr,sr,phs);
       }
 
+   restore_flags(flags);
+
 DB(DB_INTR,printk("} "))
 
 }
@@ -1056,7 +1284,7 @@
                  instance->this_id | hostdata->clock_freq);
    write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
    write_wd33c93(regp, WD_SYNCHRONOUS_TRANSFER,
-                 calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF));
+                 calc_sync_xfer(hostdata->default_sx_per/4,DEFAULT_SX_OFF));
    write_wd33c93(regp, WD_COMMAND, WD_CMD_RESET);
 
    while (!(READ_AUX_STAT() & ASR_INT))
@@ -1080,26 +1308,44 @@
       hostdata->chip = C_UNKNOWN_CHIP;
 
    write_wd33c93(regp, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
-   if (hostdata->args & A_NO_DISCONNECT)
-      write_wd33c93(regp, WD_SOURCE_ID, 0);
-   else
-      write_wd33c93(regp, WD_SOURCE_ID, SRCID_ER);
    write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
 }
 
 
 
+#if LINUX_VERSION_CODE >= 0x010300
+int wd33c93_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+#else
 int wd33c93_reset(Scsi_Cmnd *SCpnt)
+#endif
 {
 unsigned long flags;
 struct Scsi_Host *instance;
+struct WD33C93_hostdata *hostdata;
+int i;
 
    instance = SCpnt->host;
+   hostdata = (struct WD33C93_hostdata *)instance->hostdata;
 
    printk("scsi%d: reset. ", instance->host_no);
    save_flags(flags);
    cli();
+
    ((struct WD33C93_hostdata *)instance->hostdata)->dma_stop(instance,NULL,0);
+   for (i = 0; i < 8; i++) {
+      hostdata->busy[i] = 0;
+      hostdata->sync_xfer[i] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
+      hostdata->sync_stat[i] = SS_UNSET;  /* using default sync values */
+      }
+   hostdata->input_Q = NULL;
+   hostdata->selecting = NULL;
+   hostdata->connected = NULL;
+   hostdata->disconnected_Q = NULL;
+   hostdata->state = S_UNCONNECTED;
+   hostdata->dma = D_DMA_OFF;
+   hostdata->incoming_ptr = 0;
+   hostdata->outgoing_len = 0;
+
    reset_wd33c93(instance);
    SCpnt->result = DID_RESET << 16;
    restore_flags(flags);
@@ -1113,38 +1359,41 @@
 struct Scsi_Host *instance;
 struct WD33C93_hostdata *hostdata;
 wd33c93_regs *regp;
-Scsi_Cmnd *tmp, **prev;
+Scsi_Cmnd *tmp, *prev;
 unsigned long flags;
 
+   save_flags (flags);
+   cli();
+
    instance = cmd->host;
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
    regp = hostdata->regp;
 
-   printk ("scsi%d: abort. ", instance->host_no);
-
-   save_flags (flags);
-   cli();
-
 /*
  * Case 1 : If the command hasn't been issued yet, we simply remove it
- *     from the issue queue.
+ *     from the input_Q.
  */
-   for (prev=(Scsi_Cmnd **)&(hostdata->input_Q),tmp=(Scsi_Cmnd *)hostdata->input_Q;
-         tmp;
-         prev=(Scsi_Cmnd **)&(tmp->host_scribble),tmp=(Scsi_Cmnd *)tmp->host_scribble)
-      if (cmd == tmp) {
-         (*prev) = (Scsi_Cmnd *)tmp->host_scribble;
-         tmp->host_scribble = NULL;
-         tmp->result = DID_ABORT << 16;
+
+   tmp = (Scsi_Cmnd *)hostdata->input_Q;
+   prev = 0;
+   while (tmp) {
+      if (tmp == cmd) {
+         if (prev)
+            prev->host_scribble = cmd->host_scribble;
+         cmd->host_scribble = NULL;
+         cmd->result = DID_ABORT << 16;
+         printk("scsi%d: Abort - removing command %ld from input_Q. ",
+           instance->host_no, cmd->pid);
+         cmd->scsi_done(cmd);
          restore_flags(flags);
-         printk("scsi%d : abort removed command from issue queue. ",
-           instance->host_no);
-         tmp->scsi_done(tmp);
          return SCSI_ABORT_SUCCESS;
          }
+      prev = tmp;
+      tmp = (Scsi_Cmnd *)tmp->host_scribble;
+      }
 
 /*
- * Case 2 : If any commands are connected, we're going to fail the abort
+ * Case 2 : If the command is connected, we're going to fail the abort
  *     and let the high level SCSI driver retry at a later time or
  *     issue a reset.
  *
@@ -1158,20 +1407,22 @@
       uchar sr, asr;
       unsigned long timeout;
 
-      printk("scsi%d : aborting connected command. ", instance->host_no);
+      printk("scsi%d: Aborting connected command %ld - ",
+              instance->host_no, cmd->pid);
 
+      printk("stopping DMA - ");
       if (hostdata->dma == D_DMA_RUNNING) {
          hostdata->dma_stop(instance, cmd, 0);
          hostdata->dma = D_DMA_OFF;
          }
 
-         printk("scsi%d : wd33c93 asr is %x. ", instance->host_no, READ_AUX_STAT());
-
+      printk("sending wd33c93 ABORT command - ");
       write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
       write_wd33c93_cmd(regp, WD_CMD_ABORT);
 
 /* Now we have to attempt to flush out the FIFO... */
 
+      printk("flushing fifo - ");
       timeout = 1000000;
       do {
          asr = READ_AUX_STAT();
@@ -1179,46 +1430,34 @@
             read_wd33c93(regp, WD_DATA);
          } while (!(asr & ASR_INT) && timeout-- > 0);
       sr = read_wd33c93(regp, WD_SCSI_STATUS);
-      printk("scsi%d : wd33c93 sr is %x. ", instance->host_no,
-         read_wd33c93(regp, WD_SCSI_STATUS));
+      printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ",
+             asr, sr, read_wd33c93_count(regp), timeout);
 
-      if (sr >= (CSR_ABORT|PHS_DATA_OUT) && sr <= (CSR_ABORT|PHS_MESS_IN)) {
    /*
     * Abort command processed.
     * Still connected.
     * We must disconnect.
     */
-         printk("scsi%d : count was %ld. ", instance->host_no,
-            read_wd33c93_count(regp));
 
-         timeout = 1000000;
-         while ((asr & ASR_CIP) && timeout-- > 0)
-            asr = READ_AUX_STAT();
-         write_wd33c93_cmd(regp, WD_CMD_DISCONNECT);
-         asr = READ_AUX_STAT();
-         if (asr & ASR_LCI)
-            printk ("scsi%d: disconnect command ignored. ",
-               instance->host_no);
-         timeout = 1000000;
-         while ((asr & ASR_CIP) && timeout-- > 0)
-            asr = READ_AUX_STAT();
-         }
-      asr = READ_AUX_STAT();
-      sr = read_wd33c93(regp, WD_SCSI_STATUS);
-      printk("scsi%d : asr is %x, sr is %x. ",instance->host_no,asr,sr);
+      printk("sending wd33c93 DISCONNECT command - ");
       write_wd33c93_cmd(regp, WD_CMD_DISCONNECT);
+
       timeout = 1000000;
+      asr = READ_AUX_STAT();
       while ((asr & ASR_CIP) && timeout-- > 0)
          asr = READ_AUX_STAT();
       sr = read_wd33c93(regp, WD_SCSI_STATUS);
-      printk("scsi%d : asr is %x, sr is %x. ",instance->host_no,asr,sr);
-      reset_wd33c93(instance);
-      cmd->result = DID_ABORT << 16;
-      cmd->scsi_done(cmd);
+      printk("asr=%02x, sr=%02x.",asr,sr);
+
       hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
       hostdata->connected = NULL;
       hostdata->state = S_UNCONNECTED;
+      cmd->result = DID_ABORT << 16;
+      cmd->scsi_done(cmd);
+
+/*      sti();*/
       wd33c93_execute (instance);
+
       restore_flags(flags);
       return SCSI_ABORT_SUCCESS;
       }
@@ -1229,12 +1468,17 @@
  * an ABORT_SNOOZE and hope for the best...
  */
 
-   for (tmp=(Scsi_Cmnd *)hostdata->disconnected_Q; tmp;
-         tmp=(Scsi_Cmnd *)tmp->host_scribble)
-      if (cmd == tmp) {
+   tmp = (Scsi_Cmnd *)hostdata->disconnected_Q;
+   while (tmp) {
+      if (tmp == cmd) {
+         printk("scsi%d: Abort - command %ld found on disconnected_Q - ",
+                 instance->host_no, cmd->pid);
+         printk("returning ABORT_SNOOZE. ");
          restore_flags(flags);
          return SCSI_ABORT_SNOOZE;
          }
+      tmp = (Scsi_Cmnd *)tmp->host_scribble;
+      }
 
 /*
  * Case 4 : If we reached this point, the command was not found in any of
@@ -1242,29 +1486,101 @@
  *
  * We probably reached this point because of an unlikely race condition
  * between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case somethign really
+ * so we won't panic, but we will notify the user in case something really
  * broke.
  */
 
+/*   sti();*/
+   wd33c93_execute (instance);
+
    restore_flags(flags);
-   printk("scsi%d : warning : SCSI command probably completed successfully\n"
+   printk("scsi%d: warning : SCSI command probably completed successfully"
       "         before abortion. ", instance->host_no);
    return SCSI_ABORT_NOT_RUNNING;
 }
 
 
 
-#define MAX_WD33C93_HOSTS 8
-static unsigned int setup_args_array[MAX_WD33C93_HOSTS];
-static int setup_args_array_x = 0;
+#define MAX_WD33C93_HOSTS 4
+#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define SETUP_BUFFER_SIZE 200
+static char setup_buffer[SETUP_BUFFER_SIZE];
+static char setup_used[MAX_SETUP_STRINGS];
 
 void wd33c93_setup (char *str, int *ints)
 {
-int i;
+int i,x;
+char *p1,*p2;
+
+   /* The kernel does some processing of the command-line before calling
+    * this function: If it begins with any decimal or hex number arguments,
+    * ints[0] = how many numbers found and ints[1] through [n] are the values
+    * themselves. str points to where the non-numeric arguments (if any)
+    * start: We do our own parsing of those. We construct synthetic 'nosync'
+    * keywords out of numeric args (to maintain compatibility with older
+    * versions) and then add the rest of the arguments.
+    */
+
+   p1 = setup_buffer;
+   *p1 = '\0';
+   if (ints[0]) {
+      for (i=0; i<ints[0]; i++) {
+         x = vsprintf(p1,"nosync:0x%02x,",&(ints[i+1]));
+         p1 += x;
+         }
+      }
+   if (str)
+      strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer));
+   setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
+   p1 = setup_buffer;
+   i = 0;
+   while (*p1 && (i < MAX_SETUP_STRINGS)) {
+      p2 = strchr(p1, ',');
+      if (p2) {
+         *p2 = '\0';
+         if (p1 != p2)
+            setup_strings[i] = p1;
+         p1 = p2 + 1;
+         i++;
+         }
+      else {
+         setup_strings[i] = p1;
+         break;
+         }
+      }
+   for (i=0; i<MAX_SETUP_STRINGS; i++)
+      setup_used[i] = 0;
+}
+
+
+/* check_setup_strings() returns index if key found, 0 if not
+ */
+
+int check_setup_strings(char *key, int *flags, int *val, char *buf)
+{
+int x;
+char *cp;
 
-   for (i=0; i<ints[0]; i++) {
-      setup_args_array[i] = ints[i+1];
+   for  (x=0; x<MAX_SETUP_STRINGS; x++) {
+      if (setup_used[x])
+         continue;
+      if (!strncmp(setup_strings[x], key, strlen(key)))
+         break;
+      if (!strncmp(setup_strings[x], "next", strlen("next")))
+         return 0;
+      }
+   if (x == MAX_SETUP_STRINGS)
+      return 0;
+   setup_used[x] = 1;
+   cp = setup_strings[x] + strlen(key);
+   *val = -1;
+   if (*cp != ':')
+      return ++x;
+   cp++;
+   if ((*cp >= '0') && (*cp <= '9')) {
+      *val = simple_strtoul(cp,NULL,0);
       }
+   return ++x;
 }
 
 
@@ -1274,6 +1590,9 @@
 {
 struct WD33C93_hostdata *hostdata;
 int i;
+int flags;
+int val;
+char buf[32];
 
    hostdata = (struct WD33C93_hostdata *)instance->hostdata;
 
@@ -1294,46 +1613,226 @@
    hostdata->disconnected_Q = NULL;
    hostdata->state = S_UNCONNECTED;
    hostdata->dma = D_DMA_OFF;
+   hostdata->level2 = L2_BASIC;
+   hostdata->disconnect = DIS_ADAPTIVE;
+   hostdata->args = DEBUG_DEFAULTS;
    hostdata->incoming_ptr = 0;
    hostdata->outgoing_len = 0;
+   hostdata->default_sx_per = DEFAULT_SX_PER;
+   hostdata->no_sync = 0xff;     /* sync defaults to off */
+
+#ifdef PROC_INTERFACE
+   hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS|
+                    PR_CONNECTED|PR_INPUTQ|PR_DISCQ|
+                    PR_STOP;
+
+   disc_allowed_total = 0;
+   disc_taken_total = 0;
+#endif
+
+
+   if (check_setup_strings("nosync",&flags,&val,buf))
+      hostdata->no_sync = val;
+
+   if (check_setup_strings("period",&flags,&val,buf))
+      hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns;
+
+   if (check_setup_strings("disconnect",&flags,&val,buf)) {
+      if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
+         hostdata->disconnect = val;
+      else
+         hostdata->disconnect = DIS_ADAPTIVE;
+      }
+
+   if (check_setup_strings("debug",&flags,&val,buf))
+      hostdata->args = val & DB_MASK;
+
+   if (check_setup_strings("clock",&flags,&val,buf)) {
+      if (val>7 && val<11)
+         val = WD33C93_FS_8_10;
+      else if (val>11 && val<16)
+         val = WD33C93_FS_12_15;
+      else if (val>15 && val<21)
+         val = WD33C93_FS_16_20;
+      else
+         val = WD33C93_FS_8_10;
+      hostdata->clock_freq = val;
+      }
+
+   if ((i = check_setup_strings("next",&flags,&val,buf))) {
+      while (i)
+         setup_used[--i] = 1;
+      }
+
+#ifdef PROC_INTERFACE
+   if (check_setup_strings("proc",&flags,&val,buf))
+      hostdata->proc = val;
+#endif
 
-   hostdata->args = setup_default;
-   if ((setup_args_array_x < MAX_WD33C93_HOSTS) &&
-       (setup_args_array[setup_args_array_x]))
-      hostdata->args = setup_args_array[setup_args_array_x];
-   setup_args_array_x++;
-
-   i = hostdata->args & (A_LEVEL2_0 | A_LEVEL2_1 | A_LEVEL2_2);
-   i >>= 8;
-   if (i == 0)
-      i = L2_DEFAULT;
-   hostdata->level2 = i;
 
    cli();
    reset_wd33c93(instance);
    sti();
 
-   printk("wd33c93-%d: ",instance->host_no);
-   switch (hostdata->chip) {
-      case C_WD33C93:
-         printk("Found WD33c93 chip! This driver probably needs at least the 'A' version!\n");
-         break;
-      case C_WD33C93A:
-         printk("Found WD33c93A chip: microcode=%02x\n",hostdata->microcode);
-         break;
-      case C_WD33C93B:
-         printk("Found WD33c93B chip: microcode=%02x\n",hostdata->microcode);
-         break;
-      default:
-         printk("Unknown 3393 chip!\n");
-      }
-   printk("wd33c93-%d: LEVEL2 commands %s (%d)\n",instance->host_no,
-            (hostdata->level2 == L2_NONE)?"disabled":"enabled",
-            hostdata->level2);
+   printk("wd33c93-%d: chip=%s microcode=%02x\n",instance->host_no,
+         (hostdata->chip==C_WD33C93)?"WD33c93":
+         (hostdata->chip==C_WD33C93A)?"WD33c93A":
+         (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown",
+         hostdata->microcode);
+
 #ifdef DEBUGGING_ON
+   printk("wd33c93-%d: setup_strings=",instance->host_no);
+   for (i=0; i<MAX_SETUP_STRINGS; i++)
+      printk("%s,",setup_strings[i]);
+   printk("\n");
    printk("wd33c93-%d: debug_flags = %04x\n",instance->host_no,hostdata->args);
 #endif
    printk("wd33c93-%d: driver version %s - %s\n",instance->host_no,
                      WD33C93_VERSION,WD33C93_DATE);
+   printk("wd33c93-%d: compiled on %s at %s\n",instance->host_no,
+                     __DATE__,__TIME__);
+}
+
+
+int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int in)
+{
+
+#ifdef PROC_INTERFACE
+
+char *bp;
+char tbuf[128];
+unsigned long flags;
+struct Scsi_Host *instance;
+struct WD33C93_hostdata *hd;
+Scsi_Cmnd *cmd;
+int x,i;
+static int stop = 0;
+
+   for (instance=instance_list; instance; instance=instance->next) {
+      if (instance->host_no == hn)
+         break;
+      }
+   if (!instance) {
+      printk("*** Hmm... Can't find host #%d!\n",hn);
+      return (-ESRCH);
+      }
+   hd = (struct WD33C93_hostdata *)instance->hostdata;
+
+/* If 'in' is TRUE we need to _read_ the proc file. We accept the following
+ * keywords (same format as command-line, but only ONE per read):
+ *    debug
+ *    disconnect
+ *    period
+ *    resync
+ *    proc
+ */
+
+   if (in) {
+      buf[len] = '\0';
+      bp = buf;
+      if (!strncmp(bp,"debug:",6)) {
+         bp += 6;
+         hd->args = simple_strtoul(bp,NULL,0) & DB_MASK;
+         }
+      else if (!strncmp(bp,"disconnect:",11)) {
+         bp += 11;
+         x = simple_strtoul(bp,NULL,0);
+         if (x < DIS_NEVER || x > DIS_ALWAYS)
+            x = DIS_ADAPTIVE;
+         hd->disconnect = x;
+         }
+      else if (!strncmp(bp,"period:",7)) {
+         bp += 7;
+         x = simple_strtoul(bp,NULL,0);
+         hd->default_sx_per = sx_table[round_period((unsigned int)x)].period_ns;
+         }
+      else if (!strncmp(bp,"resync:",7)) {
+         bp += 7;
+         x = simple_strtoul(bp,NULL,0);
+         for (i=0; i<7; i++)
+            if (x & (1<<i))
+               hd->sync_stat[i] = SS_UNSET;
+         }
+      else if (!strncmp(bp,"proc:",5)) {
+         bp += 5;
+         hd->proc = simple_strtoul(bp,NULL,0);
+         }
+      return len;
+      }
+
+   save_flags(flags);
+   cli();
+   bp = buf;
+   *bp = '\0';
+   if (hd->proc & PR_VERSION) {
+      sprintf(tbuf,"\nVersion %s - %s. Compiled %s %s",
+            WD33C93_VERSION,WD33C93_DATE,__DATE__,__TIME__);
+      strcat(bp,tbuf);
+      }
+   if (hd->proc & PR_INFO) {
+      ;
+      }
+   if (hd->proc & PR_TOTALS) {
+      sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken",
+            disc_allowed_total,disc_taken_total);
+      strcat(bp,tbuf);
+      }
+   if (hd->proc & PR_CONNECTED) {
+      strcat(bp,"\nconnected:     ");
+      if (hd->connected) {
+         cmd = (Scsi_Cmnd *)hd->connected;
+         sprintf(tbuf," %ld-%d:%d(%02x)",
+               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+         strcat(bp,tbuf);
+         }
+      }
+   if (hd->proc & PR_INPUTQ) {
+      strcat(bp,"\ninput_Q:       ");
+      cmd = (Scsi_Cmnd *)hd->input_Q;
+      while (cmd) {
+         sprintf(tbuf," %ld-%d:%d(%02x)",
+               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+         strcat(bp,tbuf);
+         cmd = (Scsi_Cmnd *)cmd->host_scribble;
+         }
+      }
+   if (hd->proc & PR_DISCQ) {
+      strcat(bp,"\ndisconnected_Q:");
+      cmd = (Scsi_Cmnd *)hd->disconnected_Q;
+      while (cmd) {
+         sprintf(tbuf," %ld-%d:%d(%02x)",
+               cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+         strcat(bp,tbuf);
+         cmd = (Scsi_Cmnd *)cmd->host_scribble;
+         }
+      }
+   strcat(bp,"\n");
+   restore_flags(flags);
+   *start = buf;
+   if (stop) {
+      stop = 0;
+      return 0;
+      }
+   if (off > 0x40000)   /* ALWAYS stop after 256k bytes have been read */
+      stop = 1;;
+   if (hd->proc & PR_STOP)    /* stop every other time */
+      stop = 1;
+   return strlen(bp);
+
+#else    /* PROC_INTERFACE */
+
+   return 0;
+
+#endif   /* PROC_INTERFACE */
+
 }
+
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = WD33C93;
+
+#include "scsi_module.c"
+
+#endif
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov with Sam's (original) version
of this