patch-1.3.92 linux/drivers/scsi/aic7xxx.c

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

diff -u --recursive --new-file v1.3.91/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
@@ -41,7 +41,7 @@
  *
  *    -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 04/03/95
  *
- *  $Id: aic7xxx.c,v 2.22 1996/02/10 09:22:50 deang Exp deang $
+ *  $Id: aic7xxx.c,v 3.0 1996/04/16 09:11:53 deang Exp $
  *-M*************************************************************************/
 
 #ifdef MODULE
@@ -74,7 +74,7 @@
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 2.22 $"
+#define AIC7XXX_C_VERSION  "$Revision: 3.0 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        ((a < b) ? a : b)
@@ -186,8 +186,9 @@
   AIC_NONE,
   AIC_7770,	/* EISA aic7770 on motherboard */
   AIC_7771,	/* EISA aic7771 on 274x */
-  AIC_284x,	/* VLB  aic7770 on 284x */
+  AIC_284x,	/* VLB  aic7770 on 284x, BIOS disabled */
   AIC_7850,	/* PCI  aic7850 */
+  AIC_7855,	/* PCI  aic7855 */
   AIC_7870,	/* PCI  aic7870 on motherboard */
   AIC_7871,	/* PCI  aic7871 on 294x */
   AIC_7872,	/* PCI  aic7872 on 3940 */
@@ -240,6 +241,7 @@
   "AHA-2740",			/* AIC_7771 */
   "AHA-2840",			/* AIC_284x */
   "AIC-7850",			/* AIC_7850 */
+  "AIC-7855",			/* AIC_7855 */
   "AIC-7870",			/* AIC_7870 */
   "AHA-2940",			/* AIC_7871 */
   "AHA-3940",			/* AIC_7872 */
@@ -529,6 +531,7 @@
 #define SCB_DEVICE_RESET       0x04
 #define SCB_IMMED              0x08
 #define SCB_SENSE              0x10
+#define SCB_QUEUED_FOR_DONE    0x40
 	int                 state;          /* current state of scb */
 	unsigned int        position;       /* Position in scb array */
 	struct scatterlist  sg;
@@ -560,6 +563,7 @@
  * Define a structure used for each host adapter, only one per IRQ.
  */
 struct aic7xxx_host {
+  struct Scsi_Host        *host;             /* pointer to scsi host */
   int                      base;             /* card base address */
   int                      maxscb;           /* hardware SCBs */
   int                      numscb;           /* current number of scbs */
@@ -583,9 +587,11 @@
   volatile unsigned short  discenable;       /* Targets allowed to disconnect */
   struct seeprom_config    seeprom;
   int                      have_seeprom;
+  unsigned char            qcntmask;
   struct Scsi_Host        *next;             /* allow for multiple IRQs */
   struct aic7xxx_scb       scb_array[AIC7XXX_MAXSCB];  /* active commands */
   struct aic7xxx_scb      *free_scb;         /* list of free SCBs */
+  struct aic7xxx_scb      *aborted_scb;       /* list of aborted SCBs */
 #ifdef AIC7XXX_PROC_STATS
   /*
    * Statistics Kept:
@@ -619,13 +625,16 @@
   int              scsi_id;    /* host SCSI ID */
   int              scsi_id_b;  /* host SCSI ID B channel for twin cards */
   int              extended;   /* extended xlate? */
-  int              busrtime;   /* bus release time */
-  int              walk_scbs;  /* external SCB RAM detected; walk the scb array */
-  aha_type         type;       /* card type */
-  aha_chip_type    chip_type;  /* chip base type */
   int              ultra_enabled;    /* Ultra SCSI speed enabled */
   int              chan_num;   /* for 3940/3985, channel number */
+  int              use_defaults;   /* Use default wide/sync settings. */
+  unsigned char    busrtime;   /* bus release time */
+  unsigned char    bus_speed;  /* bus speed */
+  unsigned char    qcntmask;
+  aha_type         type;       /* card type */
+  aha_chip_type    chip_type;  /* chip base type */
   aha_bus_type     bus_type;   /* normal/twin/wide bus */
+  aha_status_type  bios;       /* BIOS is enabled/disabled */
   aha_status_type  parity;     /* bus parity enabled/disabled */
   aha_status_type  low_term;   /* bus termination low byte */
   aha_status_type  high_term;  /* bus termination high byte (wide cards only) */
@@ -680,7 +689,7 @@
 static void
 debug_config(struct aic7xxx_host_config *p)
 {
-  int host_conf, scsi_conf;
+  int scsi_conf;
   unsigned char brelease;
   unsigned char dfthresh;
 
@@ -688,26 +697,22 @@
   static int SST[] = { 256, 128, 64, 32 };
   static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
 
-  host_conf = inb(HOSTCONF + p->base);
   scsi_conf = inb(SCSICONF + p->base);
 
   /*
-   * The 7870 gets the bus release time and data FIFO threshold
-   * from the serial EEPROM (stored in the config structure) and
-   * scsi_conf register respectively.  The 7770 gets the bus
-   * release time and data FIFO threshold from the scsi_conf and
-   * host_conf registers respectively.
+   * Scale the Data FIFO Threshhold and the Bus Release Time; they are
+   * stored in formats compatible for writing to sequencer registers.
    */
+  dfthresh = p->bus_speed  >> 6;
+
   if (p->chip_type == AIC_777x)
   {
-    dfthresh = (host_conf >> 6);
+    brelease = p->busrtime >> 2;
   }
   else
   {
-    dfthresh = (scsi_conf >> 6);
+    brelease = p->busrtime;
   }
-
-  brelease = p->busrtime;
   if (brelease == 0)
   {
     brelease = 2;
@@ -727,6 +732,7 @@
       break;
 
     case AIC_7850:
+    case AIC_7855:
     case AIC_7870:
     case AIC_7871:
     case AIC_7872:
@@ -1081,7 +1087,7 @@
         if (!(aic7xxx_syncrates[i].rate & ULTRA_SXFR))
         {
           printk ("aic7xxx: Target %d, channel %c, requests %sMHz transfers, "
-                  "but adapter in Ultra mode can only sync at 10MHz or "
+                  "but adapter in Ultra mode can only sync at 7.2MHz or "
                   "above.\n", target, channel, aic7xxx_syncrates[i].english);
           break;  /* Use asynchronous transfers. */
         }
@@ -1296,55 +1302,84 @@
   scb->cmd = NULL;
 
   restore_flags(flags);
-
   cmd->scsi_done(cmd);
 }
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_done_aborted_scbs
+ *
+ * Description:
+ *   Calls the scsi_done() for the Scsi_Cmnd of each scb in the
+ *   aborted list, and adds each scb to the free list.
+ *-F*************************************************************************/
+static void
+aic7xxx_done_aborted_scbs (struct aic7xxx_host *p)
+{
+  Scsi_Cmnd *cmd;
+  struct aic7xxx_scb *scb;
+  int i;
+
+#ifdef AIC7XXX_DEBUG_ABORT
+  int scb_aborted = 0;
+  printk("aic7xxx: (done_aborted_scbs) calling scsi_done() for aborted scbs\n");
+#endif
+
+  for (i = 0; i < p->numscb; i++)
+  {
+    scb = &(p->scb_array[i]);
+    if (scb->state & SCB_QUEUED_FOR_DONE)
+    {
+#ifdef AIC7XXX_DEBUG_ABORT
+      if (scb_aborted == 0)
+      {
+        printk("aic7xxx: (done_aborted_scbs) Aborting scb %d", scb->position);
+        scb_aborted++;
+      }
+      else
+      {
+        printk(", %d", scb->position);
+      }
+#endif
+      /*
+       * Process the command after marking the scb as free
+       * and adding it to the free list.
+       */
+      scb->state = SCB_FREE;
+      scb->next = p->free_scb;
+      p->free_scb = scb;
+      cmd = scb->cmd;
+      scb->cmd = NULL;
+      cmd->scsi_done(cmd);  /* call the done function */
+    }
+  }
+#ifdef AIC7XXX_DEBUG_ABORT
+  if (scb_aborted != 0)
+    printk("\n");
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_add_waiting_scb
  *
  * Description:
- *   Add this SCB to the "waiting for selection" list.
+ *   Add this SCB to the head of the "waiting for selection" list.
  *-F*************************************************************************/
 static void
 aic7xxx_add_waiting_scb(u_long              base,
-                        struct aic7xxx_scb *scb,
-                        insert_type         where)
+                        struct aic7xxx_scb *scb)
 {
-  unsigned char head;
+  unsigned char next;
   unsigned char curscb;
 
   curscb = inb(SCBPTR + base);
-  head = inb(WAITING_SCBH + base);
-  if (head == SCB_LIST_NULL)
-  {
-    /*
-     * List was empty
-     */
-    head = scb->position;
-  }
-  else
-  {
-    if (where == LIST_HEAD)
-    {
-      outb(scb->position, SCBPTR + base);
-      outb(head, SCB_NEXT_WAITING + base);
-      head = scb->position;
-    }
-    else
-    {
-      /* where == LIST_SECOND */
-      unsigned char third_scb;
+  next = inb(WAITING_SCBH + base);
+
+  outb(scb->position, SCBPTR + base);
+  outb(next, SCB_NEXT_WAITING + base);
+  outb(scb->position, WAITING_SCBH + base);
 
-      outb(head, SCBPTR + base);
-      third_scb = inb(SCB_NEXT_WAITING + base);
-      outb(scb->position, SCB_NEXT_WAITING + base);
-      outb(scb->position, SCBPTR + base);
-      outb(third_scb, SCB_NEXT_WAITING + base);
-    }
-  }
-  outb(head, WAITING_SCBH + base);
   outb(curscb, SCBPTR + base);
 }
 
@@ -1366,8 +1401,7 @@
   int base = p->base;
 
   /*
-   * Select the SCB we want to abort and
-   * pull the next pointer out of it.
+   * Select the SCB we want to abort and pull the next pointer out of it.
    */
   curscb = inb(SCBPTR + base);
   outb(scb->position, SCBPTR + base);
@@ -1376,7 +1410,7 @@
   /*
    * Clear the necessary fields
    */
-  outb(0, SCBARRAY + base);
+  outb(0, SCB_CONTROL + base);
   outb(SCB_LIST_NULL, SCB_NEXT_WAITING + base);
   aic7xxx_unbusy_target(target, channel, base);
 
@@ -1399,22 +1433,12 @@
     outb(next, SCB_NEXT_WAITING + base);
   }
   /*
-   * Update the tail pointer
-   */
-  if (inb(WAITING_SCBT + base) == scb->position)
-  {
-    outb(prev, WAITING_SCBT + base);
-  }
-
-  /*
-   * Point us back at the original scb position
-   * and inform the SCSI system that the command
-   * has been aborted.
+   * Point us back at the original scb position and inform the SCSI
+   * system that the command has been aborted.
    */
   outb(curscb, SCBPTR + base);
-  scb->state |= SCB_ABORTED;
+  scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
   scb->cmd->result = (DID_RESET << 16);
-  aic7xxx_done(p, scb);
 
 #ifdef AIC7XXX_DEBUG_ABORT
   printk ("aic7xxx: (abort_waiting_scb) target/channel %d/%c, prev %d, "
@@ -1457,6 +1481,10 @@
     int saved_queue[AIC7XXX_MAXSCB];
     int queued = inb(QINCNT + base);
 
+#ifdef AIC7XXX_DEBUG_ABORT
+    if (queued)
+      printk ("aic7xxx: (reset_device) found %d SCBs in queue\n", queued);
+#endif
     for (i = 0; i < (queued - found); i++)
     {
       saved_queue[i] = inb(QINFIFO + base);
@@ -1466,9 +1494,11 @@
         /*
          * We found an scb that needs to be aborted.
          */
-        scb->state |= SCB_ABORTED;
+#ifdef AIC7XXX_DEBUG_ABORT
+        printk ("aic7xxx: (reset_device) aborting SCB %d\n", saved_queue[i]);
+#endif
+        scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
         scb->cmd->result = (DID_RESET << 16);
-        aic7xxx_done(p, scb);
         outb(scb->position, SCBPTR + base);
         outb(0, SCBARRAY + base);
         i--;
@@ -1493,6 +1523,9 @@
     next = inb(WAITING_SCBH + base);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
 
+#ifdef AIC7XXX_DEBUG_ABORT
+  printk ("aic7xxx: (reset_device) Searching waiting SCBs, head %d\n", next);
+#endif
     while (next != SCB_LIST_NULL)
     {
       scb = &(p->scb_array[next]);
@@ -1514,10 +1547,9 @@
   }
 
   /*
-   * Go through the entire SCB array now and look for
-   * commands for this target that are active.  These
-   * are other (most likely tagged) commands that
-   * were disconnected when the reset occurred.
+   * Go through the entire SCB array now and look for commands for
+   * for this target that are active.  These are other (most likely
+   * tagged) commands that were disconnected when the reset occurred.
    */
   for (i = 0; i < p->numscb; i++)
   {
@@ -1527,12 +1559,14 @@
       /*
        * Ensure the target is "free"
        */
+#ifdef AIC7XXX_DEBUG_ABORT
+  printk ("aic7xxx: (reset_device) freeing disconnected SCB %d\n", i);
+#endif
       aic7xxx_unbusy_target(target, channel, base);
       outb(scb->position, SCBPTR + base);
       outb(0, SCBARRAY + base);
-      scb->state |= SCB_ABORTED;
+      scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
       scb->cmd->result = (DID_RESET << 16);
-      aic7xxx_done(p, scb);
       found++;
     }
   }
@@ -1568,7 +1602,7 @@
  *-F*************************************************************************/
 static int
 aic7xxx_reset_channel(struct aic7xxx_host *p, char channel,
-                     unsigned char timedout_scb)
+                     unsigned char timedout_scb, int initiate_reset)
 {
   int base = p->base;
   unsigned char sblkctl;
@@ -1648,7 +1682,10 @@
      * Stealthily reset the other bus without upsetting the current bus
      */
     outb(sblkctl ^ SELBUSB, SBLKCTL + base);
-    aic7xxx_reset_current_bus(base);
+    if (initiate_reset)
+    {
+      aic7xxx_reset_current_bus(base);
+    }
     outb(sblkctl, SBLKCTL + base);
 
     UNPAUSE_SEQUENCER(p);
@@ -1662,10 +1699,26 @@
     printk ("aic7xxx: (reset_channel) Resetting current channel %c\n",
             channel);
 #endif
-    aic7xxx_reset_current_bus(base);
+    if (initiate_reset)
+    {
+      aic7xxx_reset_current_bus(base);
+    }
     RESTART_SEQUENCER(p);
+#ifdef AIC7XXX_DEBUG_ABORT
+    printk ("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
+#endif
   }
 
+  /*
+   * Set the time of last reset.
+   */
+  p->host->last_reset = jiffies;
+
+  /*
+   * Now loop through all the SCBs that have been marked for abortion,
+   * and call the scsi_done routines.
+   */
+  aic7xxx_done_aborted_scbs(p);
   return found;
 }
 
@@ -1770,7 +1823,6 @@
 	printk("  %s\n", hard_error[i].errmesg);
       }
     }
-
     panic("aic7xxx: (aic7xxx_isr) BRKADRINT, error(0x%x) seqaddr(0x%x).\n",
 	  inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
   }
@@ -2083,14 +2135,16 @@
                  */
 		aic7xxx_busy_target(scsi_id, channel, base);
 
-                aic7xxx_add_waiting_scb(base, scb, LIST_HEAD);
+                aic7xxx_add_waiting_scb(base, scb);
 		outb(SEND_SENSE, RETURN_1 + base);
 	      }  /* first time sense, no errors */
-
-              cmd->flags &= ~ASKED_FOR_SENSE;
-	      if (aic7xxx_error(cmd) == 0)
+              else
               {
-		aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+                cmd->flags &= ~ASKED_FOR_SENSE;
+	        if (aic7xxx_error(cmd) == 0)
+                {
+		  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+                }
               }
 	      break;
 
@@ -2246,6 +2300,7 @@
           scratch &= SXFR;
           outb(scratch, TARG_SCRATCH + base + scratch_offset);
           found = aic7xxx_reset_device(p, (int) scsi_id, channel, SCB_LIST_NULL);
+          aic7xxx_done_aborted_scbs (p);
         }
         else
         {
@@ -2254,94 +2309,22 @@
         break;
 
 #if AIC7XXX_NOT_YET
-     /* XXX Fill these in later */
-     case MESG_BUFFER_BUSY:
-       break;
-     case MSGIN_PHASEMIS:
-       break;
+      /* XXX Fill these in later */
+      case MESG_BUFFER_BUSY:
+        break;
+      case MSGIN_PHASEMIS:
+        break;
 #endif
 
-      case PARITY_ERROR:
-      {
-	scb_index = inb(SCBPTR + base);
-	scb = &(p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-	{
-	  printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
-		 "scb(%d) state(0x%x) cmd(0x%x).\n",
-		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
-	}
-	else
-	{
-          char  *phase;
-          unsigned char mesg_out = MSG_NOP;
-          unsigned char lastphase = inb(LASTPHASE + base);
-
-	  cmd = scb->cmd;
-          switch (lastphase)
-          {
-            case P_DATAOUT:
-              phase = "Data-Out";
-              break;
-            case P_DATAIN:
-              phase = "Data-In";
-              mesg_out = MSG_INITIATOR_DET_ERROR;
-              break;
-            case P_COMMAND:
-              phase = "Command";
-              break;
-            case P_MESGOUT:
-              phase = "Message-Out";
-              break;
-            case P_STATUS:
-              phase = "Status";
-              mesg_out = MSG_INITIATOR_DET_ERROR;
-              break;
-            case P_MESGIN:
-              phase = "Message-In";
-              mesg_out = MSG_MSG_PARITY_ERROR;
-              break;
-            default:
-              phase = "unknown";
-              break;
-          }
-
-          /*
-           * A parity error has occurred during a data
-           * transfer phase. Flag it and continue.
-           */
-          printk("aic7xxx: Parity error during phase %s on target %d, "
-                 "channel %d, lun %d.\n", phase,
-                 cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
-
-          /*
-           * We've set the hardware to assert ATN if we get a parity
-           * error on "in" phases, so all we need to do is stuff the
-           * message buffer with the appropriate message. In phases
-           * have set mesg_out to something other than MSG_NOP.
-           */
-          if (mesg_out != MSG_NOP)
-          {
-             outb(mesg_out, MSG0 + base);
-             outb(1, MSG_LEN + base);
-             aic7xxx_error(cmd) = DID_PARITY;
-          }
-          else
-          {
-             /*
-              * Should we allow the target to make this decision for us?
-              */
-             aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-          }
-        }
-        break;
-      }
       default:               /* unknown */
 	debug("aic7xxx: SEQINT, INTSTAT(0x%x) SCSISIGI(0x%x).\n",
 	      intstat, inb(SCSISIGI + base));
 	break;
     }
 
+    /*
+     * Clear the sequencer interrupt and unpause the sequencer.
+     */
     outb(CLRSEQINT, CLRINT + base);
     UNPAUSE_SEQUENCER(p);
   }
@@ -2362,95 +2345,155 @@
     {
       printk("aic7xxx: No command for SCB (SCSIINT).\n");
       /*
-       * Turn off the interrupt and set status
-       * to zero, so that it falls through the
-       * reset of the SCSIINT code.
+       * Turn off the interrupt and set status to zero, so that it
+       * falls through the rest of the SCSIINT code.
        */
       outb(status, CLRSINT1 + base);
       UNPAUSE_SEQUENCER(p);
       outb(CLRSCSIINT, CLRINT + base);
       scb = NULL;
     }
-    else
+    else if (status & SCSIRSTI)
+    {
+      PAUSE_SEQUENCER(p);
+      printk ("aic7xxx: Someone reset channel %c (SCSIINT).\n", channel);
+      /*
+       * Go through and abort all commands for the channel, but do not
+       * reset the channel again.
+       */
+      aic7xxx_reset_channel (p, channel, SCB_LIST_NULL, FALSE);
+    }
+    else if (status & SCSIPERR)
     {
+      char  *phase;
+      unsigned char mesg_out = MSG_NOP;
+      unsigned char lastphase = inb(LASTPHASE + base);
+
       cmd = scb->cmd;
+      switch (lastphase)
+      {
+        case P_DATAOUT:
+          phase = "Data-Out";
+          break;
+        case P_DATAIN:
+          phase = "Data-In";
+          mesg_out = MSG_INITIATOR_DET_ERROR;
+          break;
+        case P_COMMAND:
+          phase = "Command";
+          break;
+        case P_MESGOUT:
+          phase = "Message-Out";
+          break;
+        case P_STATUS:
+          phase = "Status";
+          mesg_out = MSG_INITIATOR_DET_ERROR;
+          break;
+        case P_MESGIN:
+          phase = "Message-In";
+          mesg_out = MSG_MSG_PARITY_ERROR;
+          break;
+        default:
+          phase = "unknown";
+          break;
+      }
+
+      /*
+       * A parity error has occurred during a data
+       * transfer phase. Flag it and continue.
+       */
+      printk("aic7xxx: Parity error during phase %s on target %d, "
+             "channel %d, lun %d.\n", phase,
+             cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
 
       /*
-       * Only the SCSI Status 1 register has information
-       * about exceptional conditions that we'd have a
-       * SCSIINT about; anything in SSTAT0 will be handled
-       * by the sequencer. Note that there can be multiple
-       * bits set.
+       * We've set the hardware to assert ATN if we get a parity
+       * error on "in" phases, so all we need to do is stuff the
+       * message buffer with the appropriate message. In phases
+       * have set mesg_out to something other than MSG_NOP.
        */
-      if (status & SELTO)
+      if (mesg_out != MSG_NOP)
       {
-	unsigned char waiting;
+         outb(mesg_out, MSG0 + base);
+         outb(1, MSG_LEN + base);
+         cmd->result = DID_PARITY << 16;
+      }
+      else
+      {
+         /*
+          * Should we allow the target to make this decision for us?
+          */
+         cmd->result = DID_RETRY_COMMAND << 16;
+      }
+      aic7xxx_done(p, scb);
+    }
+    else if (status & SELTO)
+    {
+      unsigned char waiting;
 
-	/*
-	 * Hardware selection timer has expired. Turn
-	 * off SCSI selection sequence.
-	 */
-	outb(ENRSELI, SCSISEQ + base);
-	cmd->result = (DID_TIME_OUT << 16);
-	/*
-	 * Clear an pending messages for the timed out
-	 * target and mark the target as free.
-	 */
-	ha_flags = inb(FLAGS + base);
-	outb(0, MSG_LEN + base);
-        aic7xxx_unbusy_target(scsi_id, channel, base);
+      cmd = scb->cmd;
 
-	outb(0, SCBARRAY + base);
+     /*
+       * Hardware selection timer has expired. Turn
+       * off SCSI selection sequence.
+       */
+      outb(ENRSELI, SCSISEQ + base);
+      cmd->result = (DID_TIME_OUT << 16);
+      /*
+       * Clear an pending messages for the timed out
+       * target and mark the target as free.
+       */
+      ha_flags = inb(FLAGS + base);
+      outb(0, MSG_LEN + base);
+      aic7xxx_unbusy_target(scsi_id, channel, base);
 
-	/*
-	 * Shut off the offending interrupt sources, reset
-	 * the sequencer address to zero and unpause it,
-	 * then call the high-level SCSI completion routine.
-	 *
-	 * WARNING!  This is a magic sequence!  After many
-	 * hours of guesswork, turning off the SCSI interrupts
-	 * in CLRSINT? does NOT clear the SCSIINT bit in
-	 * INTSTAT. By writing to the (undocumented, unused
-	 * according to the AIC-7770 manual) third bit of
-	 * CLRINT, you can clear INTSTAT. But, if you do it
-	 * while the sequencer is paused, you get a BRKADRINT
-	 * with an Illegal Host Address status, so the
-	 * sequencer has to be restarted first.
-	 */
-	outb(CLRSELTIMEO, CLRSINT1 + base);
+      outb(0, SCBARRAY + base);
 
-	outb(CLRSCSIINT, CLRINT + base);
+      /*
+       * Shut off the offending interrupt sources, reset
+       * the sequencer address to zero and unpause it,
+       * then call the high-level SCSI completion routine.
+       *
+       * WARNING!  This is a magic sequence!  After many
+       * hours of guesswork, turning off the SCSI interrupts
+       * in CLRSINT? does NOT clear the SCSIINT bit in
+       * INTSTAT. By writing to the (undocumented, unused
+       * according to the AIC-7770 manual) third bit of
+       * CLRINT, you can clear INTSTAT. But, if you do it
+       * while the sequencer is paused, you get a BRKADRINT
+       * with an Illegal Host Address status, so the
+       * sequencer has to be restarted first.
+       */
+      outb(CLRSELTIMEO, CLRSINT1 + base);
 
-	/*
-         * Shift the waiting for selection queue forward
-         */
-	waiting = inb(WAITING_SCBH + base);
-	outb(waiting, SCBPTR + base);
-	waiting = inb(SCB_NEXT_WAITING + base);
-	outb(waiting, WAITING_SCBH + base);
+      outb(CLRSCSIINT, CLRINT + base);
 
-	RESTART_SEQUENCER(p);
-        aic7xxx_done(p, scb);
+      /*
+       * Shift the waiting for selection queue forward
+       */
+      waiting = inb(WAITING_SCBH + base);
+      outb(waiting, SCBPTR + base);
+      waiting = inb(SCB_NEXT_WAITING + base);
+      outb(waiting, WAITING_SCBH + base);
+
+      RESTART_SEQUENCER(p);
+      aic7xxx_done(p, scb);
 #if 0
-  printk("aic7xxx: SELTO SCB(%d) state(0x%x) cmd(0x%x).\n",
-	 scb->position, scb->state, (unsigned int) scb->cmd);
+printk("aic7xxx: SELTO SCB(%d) state(0x%x) cmd(0x%x).\n",
+       scb->position, scb->state, (unsigned int) scb->cmd);
 #endif
-      }
-      else
-      {
-        if (!(status & BUSFREE))
-        {
-           /*
-            * We don't know what's going on. Turn off the
-            * interrupt source and try to continue.
-            */
-           printk("aic7xxx: SSTAT1(0x%x).\n", status);
-           outb(status, CLRSINT1 + base);
-           UNPAUSE_SEQUENCER(p);
-           outb(CLRSCSIINT, CLRINT + base);
-        }
-      }
-    }  /* else */
+    }
+    else if (!(status & BUSFREE))
+    {
+      /*
+       * We don't know what's going on. Turn off the
+       * interrupt source and try to continue.
+       */
+      printk("aic7xxx: SSTAT1(0x%x).\n", status);
+      outb(status, CLRSINT1 + base);
+      UNPAUSE_SEQUENCER(p);
+      outb(CLRSCSIINT, CLRINT + base);
+    }
   }
 
   if (intstat & CMDCMPLT)
@@ -2469,14 +2512,15 @@
       if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
       {
 	printk("aic7xxx: Warning - No command for SCB %d (CMDCMPLT).\n"
-	       "         QOUTCNT(%d) SCB state(0x%x) cmd(0x%x) pos(%d).\n",
-	       complete, inb(QOUTFIFO + base),
+	       "         QOUTCNT(%d) QINCNT(%d) SCB state(0x%x) cmd(0x%x) "
+               "pos(%d).\n",
+	       complete, inb(QOUTCNT + base), inb(QINCNT + base),
 	       scb->state, (unsigned int) scb->cmd, scb->position);
 	outb(CLRCMDINT, CLRINT + base);
 	continue;
       }
       cmd = scb->cmd;
-
+      cmd->result |= (aic7xxx_error(cmd) << 16);
       if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
       {
         /*
@@ -2551,7 +2595,7 @@
       }
 #endif /* AIC7XXX_PROC_STATS */
 
-    } while (inb(QOUTCNT + base));
+    } while (inb(QOUTCNT + base) & p->qcntmask);
   }
 }
 
@@ -2578,7 +2622,7 @@
  *   flag (rest of the bits are reserved?).
  *-F*************************************************************************/
 static aha_type
-aic7xxx_probe(int slot, int base)
+aic7xxx_probe(int slot, int base, aha_status_type *bios)
 {
   int i;
   unsigned char buf[4];
@@ -2587,11 +2631,12 @@
     int n;
     unsigned char signature[sizeof(buf)];
     aha_type type;
+    int bios_disabled;
   } AIC7xxx[] = {
-    { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771 },  /* host adapter 274x */
-    { 4, { 0x04, 0x90, 0x77, 0x70 }, AIC_7770 },  /* motherboard 7770  */
-    { 4, { 0x04, 0x90, 0x77, 0x56 }, AIC_284x },  /* 284x, BIOS enabled */
-    { 4, { 0x04, 0x90, 0x77, 0x57 }, AIC_284x }   /* 284x, BIOS disabled */
+    { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
+    { 4, { 0x04, 0x90, 0x77, 0x70 }, AIC_7770, FALSE }, /* motherboard 7770  */
+    { 4, { 0x04, 0x90, 0x77, 0x56 }, AIC_284x, FALSE }, /* 284x BIOS enabled */
+    { 4, { 0x04, 0x90, 0x77, 0x57 }, AIC_284x, TRUE }   /* 284x BIOS disabled */
   };
 
   /*
@@ -2613,6 +2658,14 @@
     {
       if (inb(base + 4) & 1)
       {
+        if (AIC7xxx[i].bios_disabled)
+        {
+          *bios = AIC_DISABLED;
+        }
+        else
+        {
+          *bios = AIC_ENABLED;
+        }
 	return (AIC7xxx[i].type);
       }
 
@@ -2959,7 +3012,6 @@
 
   if (checksum != sc->checksum)
   {
-    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
     return (0);
   }
 
@@ -2972,15 +3024,17 @@
  *   detect_maxscb
  *
  * Description:
- *   Return the maximum number of SCB's allowed for a given controller.
+ *   Detects the maximum number of SCBs for the controller and returns
+ *   the count and a mask in config (config->maxscbs, config->qcntmask).
  *-F*************************************************************************/
-static int
-detect_maxscb(aha_type type, int base, int walk_scbs)
+static void
+detect_maxscb(struct aic7xxx_host_config *config)
 {
-  unsigned char sblkctl_reg, scb_byte;
-  int maxscb = 0, i;
+  unsigned char sblkctl_reg;
+  int base, i;
 
-  switch (type)
+  base = config->base;
+  switch (config->type)
   {
     case AIC_7770:
     case AIC_7771:
@@ -3001,76 +3055,54 @@
         /*
          * We detected a Rev E board.
          */
-        printk("aic7xxx: %s Rev E and subsequent.\n", board_names[type]);
+        printk("aic7xxx: %s Rev E and subsequent.\n",
+               board_names[config->type]);
 	outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
-	maxscb = 4;
       }
       else
       {
-        printk("aic7xxx: %s Rev C and previous.\n", board_names[type]);
-	maxscb = 4;
+        printk("aic7xxx: %s Rev C and previous.\n", board_names[config->type]);
       }
       break;
 
-    case AIC_7850:
-      maxscb = 3;
+    default:
       break;
+  }
 
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7874:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7884:
-      maxscb = 16;
-      break;
+  /*
+   * Walk the SCBs to determine how many there are.
+   */
+  i = 1;
+  outb (0, SCBPTR + base);
+  outb (0, SCBARRAY + base);
 
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7882:
-    case AIC_7883:
-      /*
-       * Is suppose to have 255 SCBs, but we'll walk the SCBs
-       * looking for more if external RAM is detected.
-       */
-      maxscb = 16;
+  while (i < AIC7XXX_MAXSCB)
+  {
+    outb(i, SCBPTR + base);
+    outb(i, SCBARRAY + base);
+    if (inb(SCBARRAY + base) != i)
       break;
-
-    case AIC_NONE:
-      /*
-       * This should never happen... But just in case.
-       */
+    outb(0, SCBPTR + base);
+    if (inb(SCBARRAY + base) != 0)
       break;
-  }
 
-  if (walk_scbs)
-  {
-    /*
-     * This adapter has external SCB memory.
-     * Walk the SCBs to determine how many there are.
-     */
-    i = 0;
-    while (i < AIC7XXX_MAXSCB)
-    {
-      outb(i, SCBPTR + base);
-      scb_byte = ~(inb(SCBARRAY + base));  /* complement the byte */
-      outb(scb_byte, SCBARRAY + base);     /* write it back out */
-      if (inb(SCBARRAY + base) != scb_byte)
-      {
-        break;
-      }
-      i++;
-    }
-    maxscb = i;
+    outb(i, SCBPTR + base);      /* Clear the control byte. */
+    outb(0, SCBARRAY + base);
 
-    printk("aic7xxx: Using %d SCB's after checking for SCB memory.\n", maxscb);
-  }
-  else
-  {
-    printk("aic7xxx: Using %d SCB's; No SCB memory check.\n", maxscb);
+    config->qcntmask |= i;       /* Update the count mask. */
+    i++;
   }
+  outb(i, SCBPTR + base);   /* Ensure we clear the control bytes. */
+  outb(0, SCBARRAY + base);
+  outb(0, SCBPTR + base); 
+  outb(0, SCBARRAY + base);
+
+  config->maxscb = i;
+  config->qcntmask |= i;
+
+  printk("aic7xxx: Using %d SCB's after checking for SCB memory.\n",
+         config->maxscb);
 
-  return (maxscb);
 }
 
 /*+F*************************************************************************
@@ -3088,7 +3120,6 @@
   unsigned char sblkctl;
   int max_targets;
   int found = 1, base;
-  int bios_disabled = FALSE;
   unsigned char target_settings;
   unsigned char scsi_conf, host_conf;
   int have_seeprom = FALSE;
@@ -3134,16 +3165,24 @@
        * since there was some issue about resetting the board.
        */
       config->irq = inb(INTDEF + base) & 0x0F;
-      if ((inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
+      if ((config->type == AIC_7771) &&
+          (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
       {
-        bios_disabled = TRUE;
+        config->bios = AIC_DISABLED;
+        config->use_defaults = TRUE;
       }
-      host_conf = inb(HOSTCONF + base);
-      config->busrtime = host_conf & 0x3C;
-      /* XXX Is this valid for motherboard based controllers? */
-      /* Setup the FIFO threshold and the bus off time */
-      outb(host_conf & DFTHRSH, BUSSPD + base);
-      outb((host_conf << 2) & BOFF, BUSTIME + base);
+      else
+      {
+        host_conf = inb(HOSTCONF + base);
+        config->bus_speed = host_conf & DFTHRSH;
+        config->busrtime = (host_conf << 2) & BOFF;
+      }
+
+      /*
+       * Setup the FIFO threshold and the bus off time
+       */
+      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
+      outb(config->busrtime, BUSTIME + base);
 
       /*
        * A reminder until this can be detected automatically.
@@ -3159,12 +3198,9 @@
       aic7xxx_delay(1);
       outb(config->pause, HCNTRL + base);
 
+      config->parity = AIC_ENABLED;
       config->extended = aic7xxx_extended;
       config->irq = inb(INTDEF + base) & 0x0F;
-      if ((inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
-      {
-        bios_disabled = TRUE;
-      }
       host_conf = inb(HOSTCONF + base);
 
       printk("aic7xxx: Reading SEEPROM...");
@@ -3172,37 +3208,50 @@
       if (!have_seeprom)
       {
 	printk("aic7xxx: Unable to read SEEPROM.\n");
-        config->busrtime = host_conf & 0x3C;
       }
       else
       {
 	printk("done.\n");
 	config->extended = ((sc.bios_control & CF284XEXTEND) >> 5);
-	config->scsi_id = (sc.brtime_id & CFSCSIID);
-	config->parity = (sc.adapter_control & CFSPARITY) ?
-			 AIC_ENABLED : AIC_DISABLED;
-	config->low_term = (sc.adapter_control & CF284XSTERM) ?
-			      AIC_ENABLED : AIC_DISABLED;
-	/*
-	 * XXX - Adaptec *does* make 284x wide controllers, but the
-	 *       documents do not say where the high byte termination
-	 *       enable bit is located.  For now, we'll just assume
-	 *       that it's in the same place as for the 2940 card.
-	 */
-	config->high_term = (sc.adapter_control & CFWSTERM) ?
-			      AIC_ENABLED : AIC_DISABLED;
-	config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
+        if (!(sc.bios_control & CFBIOSEN))
+        {
+          /*
+           * The BIOS is disabled; the values left over in scratch
+           * RAM are still valid.  Do not use defaults as in the
+           * AIC-7770 case.
+           */
+          config->bios = AIC_DISABLED;
+        }
+        else
+        {
+	  config->parity = (sc.adapter_control & CFSPARITY) ?
+			   AIC_ENABLED : AIC_DISABLED;
+	  config->low_term = (sc.adapter_control & CF284XSTERM) ?
+			        AIC_ENABLED : AIC_DISABLED;
+	  /*
+	   * XXX - Adaptec *does* make 284x wide controllers, but the
+	   *       documents do not say where the high byte termination
+	   *       enable bit is located.
+           */
+        }
       }
-      /* XXX Is this valid for motherboard based controllers? */
-      /* Setup the FIFO threshold and the bus off time */
-      outb(host_conf & DFTHRSH, BUSSPD + base);
-      outb((host_conf << 2) & BOFF, BUSTIME + base);
+
+      host_conf = inb(HOSTCONF + base);
+      config->bus_speed = host_conf & DFTHRSH;
+      config->busrtime = (host_conf << 2) & BOFF;
+
+      /*
+       * Setup the FIFO threshold and the bus off time
+       */
+      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
+      outb(config->busrtime, BUSTIME + base);
 
       printk("aic7xxx: Extended translation %sabled.\n",
 	     config->extended ? "en" : "dis");
       break;
 
     case AIC_7850:
+    case AIC_7855:
     case AIC_7870:
     case AIC_7871:
     case AIC_7872:
@@ -3221,31 +3270,45 @@
 
       config->extended = aic7xxx_extended;
       config->scsi_id = 7;
+      config->parity = AIC_ENABLED;
 
       printk("aic7xxx: Reading SEEPROM...");
       have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), &sc);
       if (!have_seeprom)
       {
-	printk("aic7xxx: Unable to read SEEPROM.\n");
+	printk("\naic7xxx: Unable to read SEEPROM; "
+               "using leftover BIOS values.\n");
       }
       else
       {
 	printk("done.\n");
-	config->extended = ((sc.bios_control & CFEXTEND) >> 7);
-	config->scsi_id = (sc.brtime_id & CFSCSIID);
-	config->parity = (sc.adapter_control & CFSPARITY) ?
-			 AIC_ENABLED : AIC_DISABLED;
-	config->low_term = (sc.adapter_control & CFSTERM) ?
-			      AIC_ENABLED : AIC_DISABLED;
-	config->high_term = (sc.adapter_control & CFWSTERM) ?
-			      AIC_ENABLED : AIC_DISABLED;
-	config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
-        if (((config->type == AIC_7880) || (config->type == AIC_7882) ||
-             (config->type == AIC_7883) || (config->type == AIC_7884)) &&
-            (sc.adapter_control & CFULTRAEN))
+        if (!(sc.bios_control & CFBIOSEN))
         {
-          printk ("aic7xxx: Enabling support for Ultra SCSI speed.\n");
-          config->ultra_enabled = TRUE;
+          /*
+           * The BIOS is disabled; the values left over in scratch
+           * RAM are still valid.  Do not use defaults as in the
+           * AIC-7770 case.
+           */
+          config->bios = AIC_DISABLED;
+        }
+        else
+        {
+	  config->extended = ((sc.bios_control & CFEXTEND) >> 7);
+	  config->scsi_id = (sc.brtime_id & CFSCSIID);
+	  config->parity = (sc.adapter_control & CFSPARITY) ?
+			     AIC_ENABLED : AIC_DISABLED;
+	  config->low_term = (sc.adapter_control & CFSTERM) ?
+			       AIC_ENABLED : AIC_DISABLED;
+	  config->high_term = (sc.adapter_control & CFWSTERM) ?
+			        AIC_ENABLED : AIC_DISABLED;
+	  config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
+          if (((config->type == AIC_7880) || (config->type == AIC_7882) ||
+               (config->type == AIC_7883) || (config->type == AIC_7884)) &&
+              (sc.adapter_control & CFULTRAEN))
+          {
+            printk ("aic7xxx: Enabling support for Ultra SCSI speed.\n");
+            config->ultra_enabled = TRUE;
+          }
         }
       }
 
@@ -3256,13 +3319,20 @@
        * We don't know where this is set in the SEEPROM or by the BIOS,
        * so we default it to 100%.
        */
-      outb(config->scsi_id | DFTHRSH_100, SCSICONF + base);
-      outb(DFTHRSH_100, DSPCISTATUS + base);
+      config->bus_speed = DFTHRSH_100;
+      scsi_conf = config->scsi_id | config->bus_speed;
+      if (config->parity == AIC_ENABLED)
+      {
+        scsi_conf |= ENSPCHK;
+      }
+
+      outb(scsi_conf, SCSICONF + base);
+      outb(config->bus_speed, DSPCISTATUS + base);
 
       /*
-       * In case we are a wide card, place scsi ID in second conf byte.
+       * In case we are a wide card...
        */
-      outb(config->scsi_id, (SCSICONF + base + 1));
+      outb(scsi_conf, (SCSICONF + base + 1));
 
       printk("aic7xxx: Extended translation %sabled.\n",
 	     config->extended ? "en" : "dis");
@@ -3272,7 +3342,7 @@
       panic("aic7xxx: (aic7xxx_register) Internal error.\n");
   }
 
-  config->maxscb = detect_maxscb(config->type, base, config->walk_scbs);
+  detect_maxscb(config);
 
   if (config->chip_type == AIC_777x)
   {
@@ -3325,7 +3395,7 @@
 
     default:
       printk("aic7xxx: Unsupported type 0x%x, please "
-	     "mail deang@ims.com\n", inb(SBLKCTL + base));
+	     "mail deang@teleport.com\n", inb(SBLKCTL + base));
       outb(0, FLAGS + base);
       return (0);
   }
@@ -3418,21 +3488,13 @@
 
   p = (struct aic7xxx_host *) host->hostdata;
 
-  /*
-   * Initialize the scb array by setting the state to free.
-   */
-  for (i = 0; i < AIC7XXX_MAXSCB; i++)
-  {
-    p->scb_array[i].state = SCB_FREE;
-    p->scb_array[i].next = NULL;
-    p->scb_array[i].cmd = NULL;
-  }
-
+  p->host = host;
   p->isr_count = 0;
   p->a_scanned = FALSE;
   p->b_scanned = FALSE;
   p->base = base;
   p->maxscb = config->maxscb;
+  p->qcntmask = config->qcntmask;
   p->numscb = 0;
   p->extended = config->extended;
   p->type = config->type;
@@ -3443,6 +3505,7 @@
   p->have_seeprom = have_seeprom;
   p->seeprom = sc;
   p->free_scb = NULL;
+  p->aborted_scb = NULL;
   p->next = NULL;
 
   p->unpause = config->unpause;
@@ -3515,7 +3578,7 @@
     outb(config->scsi_id_b, SCSIID + base);
     scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
     outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-    outb(ENSELTIMO , SIMODE1 + base);
+    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
     if (p->ultra_enabled)
     {
       outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
@@ -3533,7 +3596,7 @@
   outb(config->scsi_id, SCSIID + base);
   scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
   outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-  outb(ENSELTIMO , SIMODE1 + base);
+  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
   if (p->ultra_enabled)
   {
     outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
@@ -3573,7 +3636,7 @@
   }
   else
   {
-    if (bios_disabled)
+    if (config->bios == AIC_DISABLED)
     {
       printk("aic7xxx : Host adapter BIOS disabled. Using default SCSI "
              "device parameters.\n");
@@ -3606,7 +3669,7 @@
     }
     else
     {
-      if (bios_disabled)
+      if (config->use_defaults)
       {
         target_settings = 0;  /* 10 MHz */
         p->needsdtr_copy |= (0x01 << i);
@@ -3640,7 +3703,7 @@
    */
   if (p->bus_type != AIC_WIDE)
   {
-    p->needwdtr = 0;
+    p->needwdtr_copy = 0;
   }
   p->needsdtr = p->needsdtr_copy;
   p->needwdtr = p->needwdtr_copy;
@@ -3650,16 +3713,6 @@
 #endif
 
   /*
-   * Clear the control byte for every SCB so that the sequencer
-   * doesn't get confused and think that one of them is valid
-   */
-  for (i = 0; i < config->maxscb; i++)
-  {
-    outb(i, SCBPTR + base);
-    outb(0, SCBARRAY + base);
-  }
-
-  /*
    * For reconnecting targets, the sequencer code needs to
    * know how many SCBs it has to search through.
    */
@@ -3672,6 +3725,12 @@
   outb(-i & 0xff, COMP_SCBCOUNT + base);
 
   /*
+   * Set the QCNT (queue count) mask to deal with broken aic7850s that
+   * sporatically get garbage in the upper bits of their QCNT registers.
+   */
+  outb(config->qcntmask, QCNTMASK + base);
+
+  /*
    * Clear the active flags - no targets are busy.
    */
   outb(0, ACTIVE_A + base);
@@ -3681,7 +3740,11 @@
    * We don't have any waiting selections
    */
   outb(SCB_LIST_NULL, WAITING_SCBH + base);
-  outb(SCB_LIST_NULL, WAITING_SCBT + base);
+
+  /*
+   * Message out buffer starts empty
+   */
+  outb(0, MSG_LEN + base);
 
   /*
    * Reset the SCSI bus. Is this necessary?
@@ -3785,7 +3848,7 @@
       continue;
     }
 
-    config.type = aic7xxx_probe(slot, HID0 + base);
+    config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
     if (config.type != AIC_NONE)
     {
       /*
@@ -3801,12 +3864,13 @@
       config.chip_type = AIC_777x;
       config.base = base;
       config.irq = irq;
-      config.parity = AIC_UNKNOWN;
+      config.parity = AIC_ENABLED;
       config.low_term = AIC_UNKNOWN;
       config.high_term = AIC_UNKNOWN;
-      config.busrtime = 0;
-      config.walk_scbs = FALSE;
       config.ultra_enabled = FALSE;
+      config.extended = aic7xxx_extended;
+      config.bus_speed = DFTHRSH_100;
+      config.busrtime = BOFF_60BCLKS;
       found += aic7xxx_register(template, &config);
 
       /*
@@ -3830,6 +3894,7 @@
       aha_chip_type  chip_type;
     } const aic7xxx_pci_devices[] = {
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
@@ -3868,12 +3933,20 @@
           config.type = aic7xxx_pci_devices[i].card_type;
           config.chip_type = aic7xxx_pci_devices[i].chip_type;
           config.chan_num = 0;
-          config.walk_scbs = FALSE;
+          config.bios = AIC_ENABLED;  /* Assume bios is enabled. */
+          config.use_defaults = FALSE;
+          config.busrtime = 40;
           switch (config.type)
           {
+            case AIC_7850:
+            case AIC_7855:
+              config.bios = AIC_DISABLED;
+              config.use_defaults = TRUE;
+              config.bus_speed = DFTHRSH_100;
+              break;
+
             case AIC_7872:  /* 3940 */
             case AIC_7882:  /* 3940-Ultra */
-              config.walk_scbs = TRUE;
               config.chan_num = number_of_39xxs & 0x1;  /* Has 2 controllers */
               number_of_39xxs++;
               if (number_of_39xxs == 2)
@@ -3912,13 +3985,17 @@
                                              CSIZE_LATTIME, &csize_lattime);
           if ((csize_lattime & CACHESIZE) == 0)
           {
-            /* Default to 8DWDs - what's the PCI define for this? */
+            /*
+             * Default to 8DWDs - what's the PCI define for this?
+             */
             csize_lattime |= 8;
           }
           if((csize_lattime & LATTIME) == 0)
           {
-            /* Default to 64 PCLKS (is this a good value?) */
-            /* This may also be available in the SEEPROM?? */
+            /*
+             * Default to 64 PCLKS (is this a good value?)
+             * This may also be available in the SEEPROM??
+             */
             csize_lattime |= (64 << 8);
           }
           pcibios_write_config_dword(pci_bus, pci_device_fn,
@@ -3953,22 +4030,22 @@
 
           /*
            * I don't think we need to bother with allowing
-           * spurious interrupts for the 787x/7850, but what
+           * spurious interrupts for the 787x/785x, but what
            * the hey.
            */
           aic7xxx_spurious_count = 1;
 
           config.base = base;
           config.irq = irq;
-          config.parity = AIC_UNKNOWN;
+          config.parity = AIC_ENABLED;
           config.low_term = AIC_UNKNOWN;
           config.high_term = AIC_UNKNOWN;
-          config.busrtime = 0;
+          config.extended = aic7xxx_extended;
           config.ultra_enabled = FALSE;
           if (devconfig & RAMPSM)
           {
             /*
-             * External SRAM present.  Have the probe walk the SCBs to see
+             * External SRAM present.  The probe will walk the SCBs to see
              * how much SRAM we have and set the number of SCBs accordingly.
              * We have to turn off SCBRAMSEL to access the external SCB
              * SRAM.
@@ -3987,7 +4064,6 @@
             devconfig &= ~(RAMPSM | SCBRAMSEL);
             pcibios_write_config_dword(pci_bus, pci_device_fn,
                                        DEVCONFIG, devconfig);
-            config.walk_scbs = TRUE;
           }
           found += aic7xxx_register(template, &config);
 
@@ -4252,6 +4328,18 @@
 	 scb->position, (unsigned int) scb->cmd,
 	 scb->state, (unsigned int) p->free_scb);
 #endif
+
+  /*
+   * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
+   * is set up properly, and the parity error flag is reset, then send
+   * the SCB to the sequencer and watch the fun begin.
+   */
+  cmd->scsi_done = fn;
+  aic7xxx_error(cmd) = DID_OK;
+  aic7xxx_status(cmd) = 0;
+  cmd->result = 0;
+  memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
   /*
    * Pause the sequencer so we can play with its registers -
    * wait for it to acknowledge the pause.
@@ -4269,18 +4357,6 @@
   aic7xxx_putscb(p, scb);
   outb(scb->position, QINFIFO + p->base);
 
-  /*
-   * Make sure the Scsi_Cmnd pointer is saved, the struct it
-   * points to is set up properly, and the parity error flag
-   * is reset, then unpause the sequencer and watch the fun
-   * begin.
-   */
-  cmd->scsi_done = fn;
-  aic7xxx_error(cmd) = DID_OK;
-  aic7xxx_status(cmd) = 0;
-  cmd->result = 0;
-  memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
-
   UNPAUSE_SEQUENCER(p);
 #if 0
   printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
@@ -4292,124 +4368,151 @@
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_abort_scb
+ *   aic7xxx_abort_reset
  *
  * Description:
- *   Abort an scb.  If the scb has not previously been aborted, then
- *   we attempt to send a BUS_DEVICE_RESET message to the target.  If
- *   the scb has previously been unsuccessfully aborted, then we will
- *   reset the channel and have all devices renegotiate.  Returns an
- *   enumerated type that indicates the status of the operation.
+ *   Abort or reset the current SCSI command(s).  If the scb has not
+ *   previously been aborted, then we attempt to send a BUS_DEVICE_RESET
+ *   message to the target.  If the scb has previously been unsuccessfully
+ *   aborted, then we will reset the channel and have all devices renegotiate.
+ *   Returns an enumerated type that indicates the status of the operation.
  *-F*************************************************************************/
 static aha_abort_reset_type
-aic7xxx_abort_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
-                  unsigned char errcode)
+aic7xxx_abort_reset(Scsi_Cmnd *cmd, unsigned char errcode)
 {
-  int base = p->base;
-  int found = FALSE;
+  struct aic7xxx_scb  *scb;
+  struct aic7xxx_host *p;
+  long flags;
+  unsigned char bus_state;
   aha_abort_reset_type scb_status = ABORT_RESET_SUCCESS;
-  char channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
+  int base, found;
+  char channel;
 
-  /*
-   * Ensure that the card doesn't do anything
-   * behind our back.
-   */
-  PAUSE_SEQUENCER(p);
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  scb = &(p->scb_array[aic7xxx_position(cmd)]);
+  base = p->base;
+  channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk ("aic7xxx: (abort_scb) scb %d, scb_aborted 0x%x\n",
-          scb->position, (scb->state & SCB_ABORTED));
-#endif
-  /*
-   * First, determine if we want to do a bus reset or simply a bus device
-   * reset.  If this is the first time that a transaction has timed out,
-   * just schedule a bus device reset.  Otherwise, we reset the bus and
-   * abort all pending I/Os on that bus.
-   */
-  if (scb->state & SCB_ABORTED)
+  save_flags(flags);
+  cli();
+
+  if (scb->state & SCB_ACTIVE)
   {
     /*
-     * Been down this road before.  Do a full bus reset.
+     * Ensure that the card doesn't do anything
+     * behind our back.
      */
-    found = aic7xxx_reset_channel(p, channel, scb->position);
-  }
-  else
-  {
-    unsigned char active_scb, control;
-    struct aic7xxx_scb *active_scbp;
+    PAUSE_SEQUENCER(p);
 
-    /*
-     * Send a Bus Device Reset Message:
-     * The target we select to send the message to may be entirely
-     * different than the target pointed to by the scb that timed
-     * out.  If the command is in the QINFIFO or the waiting for
-     * selection list, its not tying up the bus and isn't responsible
-     * for the delay so we pick off the active command which should
-     * be the SCB selected by SCBPTR.  If its disconnected or active,
-     * we device reset the target scbp points to.  Although it may
-     * be that this target is not responsible for the delay, it may
-     * may also be that we're timing out on a command that just takes
-     * too much time, so we try the bus device reset there first.
-     */
-    active_scb = inb(SCBPTR + base);
-    active_scbp = &(p->scb_array[active_scb]);
-    control = inb(SCBARRAY + base);
+    printk ("aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
+    bus_state = inb(LASTPHASE + p->base);
+
+    switch (bus_state)
+    {
+      case P_DATAOUT:
+        printk ("Data-Out phase, ");
+        break;
+      case P_DATAIN:
+        printk ("Data-In phase, ");
+        break;
+      case P_COMMAND:
+        printk ("Command phase, ");
+        break;
+      case P_MESGOUT:
+        printk ("Message-Out phase, ");
+        break;
+      case P_STATUS:
+        printk ("Status phase, ");
+        break;
+      case P_MESGIN:
+        printk ("Message-In phase, ");
+        break;
+      default:
+        printk ("while idle, LASTPHASE = 0x%x, ", bus_state);
+        /*
+         * We're not in a valid phase, so assume we're idle.
+         */
+        bus_state = 0;
+        break;
+    }
+    printk ("SCSISIGI = 0x%x\n", inb (p->base + SCSISIGI));
 
     /*
-     * Test to see if scbp is disconnected
+     * First, determine if we want to do a bus reset or simply a bus device
+     * reset.  If this is the first time that a transaction has timed out,
+     * just schedule a bus device reset.  Otherwise, we reset the bus and
+     * abort all pending I/Os on that bus.
      */
-    outb(scb->position, SCBPTR + base);
-    if (inb(SCBARRAY + base) & DISCONNECTED)
+    if (scb->state & SCB_ABORTED)
     {
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk ("aic7xxx: (abort_scb) scb %d is disconnected.\n", scb->position);
-#endif
-      scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-      scb->SG_segment_count = 0;
-      memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
-      memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
-      scb->data_count = 0;
-      aic7xxx_putscb(p, scb);
-      aic7xxx_error(scb->cmd) = errcode;
-      scb_status = ABORT_RESET_PENDING;
-      aic7xxx_add_waiting_scb(base, scb, LIST_SECOND);
-      UNPAUSE_SEQUENCER(p);
+      /*
+       * Been down this road before.  Do a full bus reset.
+       */
+      found = aic7xxx_reset_channel(p, channel, scb->position, TRUE);
     }
     else
     {
+      unsigned char active_scb, control;
+      struct aic7xxx_scb *active_scbp;
+
       /*
-       * Is the active SCB really active?
+       * Send a Bus Device Reset Message:
+       * The target we select to send the message to may be entirely
+       * different than the target pointed to by the scb that timed
+       * out.  If the command is in the QINFIFO or the waiting for
+       * selection list, its not tying up the bus and isn't responsible
+       * for the delay so we pick off the active command which should
+       * be the SCB selected by SCBPTR.  If its disconnected or active,
+       * we device reset the target scbp points to.  Although it may
+       * be that this target is not responsible for the delay, it may
+       * may also be that we're timing out on a command that just takes
+       * too much time, so we try the bus device reset there first.
        */
-      if (active_scbp->state & SCB_ACTIVE)
+      active_scb = inb(SCBPTR + base);
+      active_scbp = &(p->scb_array[active_scb]);
+      control = inb(SCB_CONTROL + base);
+
+      /*
+       * Test to see if scbp is disconnected
+       */
+      outb(scb->position, SCBPTR + base);
+      if (inb(SCB_CONTROL + base) & DISCONNECTED)
       {
-        unsigned char msg_len = inb(MSG_LEN + base);
-        if (msg_len != 0)
-        {
 #ifdef AIC7XXX_DEBUG_ABORT
-          printk ("aic7xxx: (abort_scb) scb is active, needs DMA, "
-                  "msg_len is non-zero.\n");
-#endif
-          /*
-           * If we're in a message phase, tacking on another message
-           * may confuse the target totally. The bus is probably wedged,
-           * so reset the channel.
-           */
-          channel = (active_scbp->target_channel_lun & SELBUSB) ? 'B': 'A';
-          aic7xxx_reset_channel(p, channel, scb->position);
-        }
-        else
-        {
-#ifdef AIC7XXX_DEBUG_ABORT
-          printk ("aic7xxx: (abort_scb) scb is active, needs DMA, "
-                  "msg_len is zero.\n");
+        printk ("aic7xxx: (abort_scb) scb %d is disconnected; "
+                "bus device reset message queued.\n", scb->position);
 #endif
+	scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
+	scb->SG_segment_count = 0;
+	memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
+	memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
+	scb->data_count = 0;
+	aic7xxx_putscb(p, scb);
+	aic7xxx_add_waiting_scb(base, scb);
+	aic7xxx_error(scb->cmd) = errcode;
+	scb_status = ABORT_RESET_PENDING;
+	outb(active_scb, SCBPTR + base);
+	UNPAUSE_SEQUENCER(p);
+      }
+      else
+      {
+	/*
+	 * Is the active SCB really active?
+	 */
+	if ((active_scbp->state & SCB_ACTIVE) && bus_state)
+	{
           /*
            * Load the message buffer and assert attention.
            */
           active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
           outb(1, MSG_LEN + base);
           outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-          if (active_scbp->target_channel_lun != scb->target_channel_lun)
+          outb(bus_state | ATNO, SCSISIGO + base);
+#ifdef AIC7XXX_DEBUG_ABORT
+          printk ("aic7xxx: (abort_scb) asserted ATN - "
+                  "bus device reset in message buffer.\n");
+#endif
+          if (active_scbp != scb)
           {
             /*
              * XXX - We would like to increment the timeout on scb, but
@@ -4421,70 +4524,27 @@
           }
           aic7xxx_error(scb->cmd) = errcode;
           scb_status = ABORT_RESET_PENDING;
+          /*
+           * Restore the active SCB and unpause the sequencer.
+           */
+          outb(active_scb, SCBPTR + base);
+
           UNPAUSE_SEQUENCER(p);
-        }
-      }
-      else
-      {
+	}
+	else
+	{
 #ifdef AIC7XXX_DEBUG_ABORT
-          printk ("aic7xxx: (abort_scb) no active command.\n");
+            printk ("aic7xxx: (abort_scb) no active command.\n");
 #endif
-        /*
-         * No active command to single out, so reset
-         * the bus for the timed out target.
-         */
-        aic7xxx_reset_channel(p, channel, scb->position);
+          /*
+           * No active command to single out, so reset
+           * the bus for the timed out target.
+           */
+          aic7xxx_reset_channel(p, channel, scb->position, TRUE);
+	}
       }
     }
   }
-  return (scb_status);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_abort_reset
- *
- * Description:
- *   Abort or reset the current SCSI command(s).  Returns an enumerated
- *   type that indicates the status of the operation.
- *-F*************************************************************************/
-static aha_abort_reset_type
-aic7xxx_abort_reset(Scsi_Cmnd *cmd, unsigned char errcode)
-{
-  struct aic7xxx_scb  *scb;
-  struct aic7xxx_host *p;
-  long flags;
-  aha_abort_reset_type scb_status = ABORT_RESET_SUCCESS;
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = &(p->scb_array[aic7xxx_position(cmd)]);
-
-  save_flags(flags);
-  cli();
-
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk ("aic7xxx: (abort_reset) scb state 0x%x\n", scb->state);
-#endif
-
-  if (scb->state & SCB_ACTIVE)
-  {
-    if (scb->state & SCB_IMMED)
-    {
-      /*
-       * Don't know how set the number of retries to 0.
-       */
-      /* cmd->retries = 0; */
-      aic7xxx_error(cmd) = errcode;
-      aic7xxx_done(p, scb);
-    }
-    else
-    {
-      /*
-       * Abort the operation.
-       */
-      scb_status = aic7xxx_abort_scb(p, scb, errcode);
-    }
-  }
   else
   {
     /*
@@ -4509,6 +4569,7 @@
     cmd->result = errcode << 16;
     cmd->scsi_done(cmd);
   }
+
   restore_flags(flags);
   return (scb_status);
 }
@@ -4565,8 +4626,10 @@
     case ABORT_RESET_PENDING:
       return (SCSI_RESET_PENDING);
       break;
-    case ABORT_RESET_INACTIVE:
     case ABORT_RESET_SUCCESS:
+      return (SCSI_RESET_BUS_RESET | SCSI_RESET_SUCCESS);
+      break;
+    case ABORT_RESET_INACTIVE:
     default:
       return (SCSI_RESET_SUCCESS);
       break;

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