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

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

diff -u --recursive --new-file v1.3.35/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.0 1995/08/02 05:28:42 deang Exp $
+ *  $Id: aic7xxx.c,v 2.5 1995/09/20 05:18:18 deang Exp $
  *-M*************************************************************************/
 
 #ifdef MODULE
@@ -59,7 +59,7 @@
 #include <linux/sched.h>
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
 #include "sd.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -71,10 +71,11 @@
     S_IFDIR | S_IRUGO | S_IXUGO, 2
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 2.0 $"
+#define AIC7XXX_C_VERSION  "$Revision: 2.5 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        ((a < b) ? a : b)
+#define ALL_TARGETS -1
 #ifndef TRUE
 #  define TRUE 1
 #endif
@@ -84,7 +85,8 @@
 
 /*
  * Defines for PCI bus support, testing twin bus support, DMAing of
- * SCBs, and tagged queueing.
+ * SCBs, tagged queueing, commands (SCBs) per lun, and SCSI bus reset
+ * delay time.
  *
  *   o PCI bus support - this has been implemented and working since
  *     the December 1, 1994 release of this driver. If you don't have
@@ -95,7 +97,9 @@
  *
  *   o Twin bus support - this has been tested and does work.
  *
- *   o DMAing of SCBs - thanks to Kai Makisara, this now works
+ *   o DMAing of SCBs - thanks to Kai Makisara, this now works.
+ *     This define is now taken out and DMAing of SCBs is always
+ *     performed (8/12/95 - DE).
  *
  *   o Tagged queueing - this driver is capable of tagged queueing
  *     but I am unsure as to how well the higher level driver implements
@@ -109,29 +113,55 @@
  *     PCI code and interrupt handling needs to be modified to
  *     support this.
  *
+ *   o Commands per lun - If tagged queueing is enabled, then you
+ *     may want to try increasing AIC7XXX_CMDS_PER_LUN to more
+ *     than 2.  By default, we limit the SCBs per lun to 2 with
+ *     or without tagged queueing enabled.  If tagged queueing is
+ *     disabled, the sequencer will keep the 2nd SCB in the input
+ *     queue until the first one completes - so it is OK to to have
+ *     more than 1 SCB queued.  If tagged queueing is enabled, then
+ *     the sequencer will attempt to send the 2nd SCB to the device
+ *     while the first SCB is executing and the device is disconnected.
+ *     For adapters limited to 4 SCBs, you may want to actually
+ *     decrease the commands per lun to 1, if you often have more
+ *     than 2 devices active at the same time.  This will allocate
+ *     1 SCB for each device and ensure that there will always be
+ *     a free SCB for up to 4 devices active at the same time.
+ *
  *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 03/11/95
  */
 
 /* Uncomment this for testing twin bus support. */
 #define AIC7XXX_TWIN_SUPPORT
 
-/* Uncomment this for DMAing of SCBs. */
-#define AIC7XXX_USE_DMA
-
 /* Uncomment this for tagged queueing. */
 /* #define AIC7XXX_TAGGED_QUEUEING */
 
 /* Uncomment this for allowing sharing of IRQs. */
 #define AIC7XXX_SHARE_IRQS
 
+/*
+ * You can try raising me if tagged queueing is enabled, or lowering
+ * me if you only have 4 SCBs.
+ */
+#define AIC7XXX_CMDS_PER_LUN 2
+
 /* Set this to the delay in seconds after SCSI bus reset. */
 #define AIC7XXX_RESET_DELAY 15
 
 /*
- * Uncomment this to always use scatter/gather lists.
- * *NOTE: The sequencer must be changed also!
+ * Uncomment the following define for collection of SCSI transfer statistics
+ * for the /proc filesystem.
+ *
+ * NOTE: This does affect performance since it has to maintain statistics.
+ */
+/* #define AIC7XXX_PROC_STATS */
+
+/*
+ * Define this to use polling rather than using kernel support for waking
+ * up a waiting process.
  */
-#define AIC7XXX_USE_SG
+#undef AIC7XXX_POLL
 
 /*
  * Controller type and options
@@ -157,6 +187,12 @@
   AIC_DISABLED
 } aha_status_type;
 
+typedef enum {
+  LIST_HEAD,
+  LIST_SECOND,
+  LIST_TAIL
+} insert_type;
+
 /*
  * There should be a specific return value for this in scsi.h, but
  * it seems that most drivers ignore it.
@@ -256,6 +292,9 @@
  * SCSI Rate
  */
 #define SCSIRATE(x)		((x) + 0xC04ul)
+#define WIDEXFER		0x80		/* Wide transfer control */
+#define SXFR			0x70		/* Sync transfer rate */
+#define SOFS			0x0F		/* Sync offset */
 
 /*
  * SCSI ID (p. 3-18).
@@ -267,6 +306,15 @@
 #define		OID		0x0F		/* Our ID mask */
 
 /*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transfered
+ * across the SCSI bus.  The counter is decremented only once
+ * the data has been safely transfered.  SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */
+#define STCNT(x)		((x) + 0xC08ul)
+
+/*
  * SCSI Status 0 (p. 3-21)
  * Contains one set of SCSI Interrupt codes
  * These are most likely of interest to the sequencer
@@ -325,6 +373,16 @@
 #define		ENREQINIT	0x01
 
 /*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transfered on the SCSI bus.  They are counted up in the same
+ * manner as STCNT is counted down.  SHADDR should always be used
+ * to determine the address of the last byte transfered since HADDR
+ * can be squewed by write ahead.
+ */
+#define	SHADDR(x)		((x) + 0xC14ul)
+
+/*
  * Selection/Reselection ID (p. 3-31)
  * Upper four bits are the device id. The ONEBIT is set when the re/selecting
  * device did not set its own ID.
@@ -404,6 +462,7 @@
 #define SEQADDR1(x)		((x) + 0xC63ul)
 
 #define ACCUM(x)		((x) + 0xC64ul)		/* accumulator */
+#define SINDEX(x)		((x) + 0xC65ul)
 
 /*
  * Board Control (p. 3-43)
@@ -448,6 +507,13 @@
 #define		UNPAUSE_294X	IRQMS | INTEN
 
 /*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transfered across the host bus.
+ */
+#define HADDR(x)		((x) + 0xC88ul)
+
+/*
  * SCB Pointer (p. 3-49)
  * Gate one of the four SCBs into the SCBARRAY window.
  */
@@ -469,7 +535,8 @@
 #define			BAD_STATUS	0x70
 #define			RESIDUAL	0x80
 #define			ABORT_TAG	0x90
-#define			AWAITING_MSG	0xa0
+#define			AWAITING_MSG	0xA0
+#define			IMMEDDONE	0xB0
 #define 	BRKADRINT 0x08
 #define		SCSIINT	  0x04
 #define		CMDCMPLT  0x02
@@ -562,7 +629,7 @@
 /*
  * Bit vector of targets that have disconnection disabled.
  */
-#define	HA_DISC_DSB		((x) + 0xc32ul)
+#define	HA_DISC_DSB(x)		((x) + 0xC32ul)
 
 /*
  * Length of pending message
@@ -586,6 +653,8 @@
 #define		SEND_WDTR 	0x80
 #define		SEND_REJ	0x40
 
+#define	SG_COUNT(x)		((x) + 0xC4Dul)
+#define	SG_NEXT(x)		((x) + 0xC4Eul)
 #define HA_SIGSTATE(x)		((x) + 0xC4Bul)	/* value in SCSISIGO */
 #define HA_SCBCOUNT(x)		((x) + 0xC52ul)	/* number of hardware SCBs */
 
@@ -607,8 +676,12 @@
 #define HA_INTDEF(x)		((x) + 0xC5Cul)	/* interrupt def'n register */
 #define HA_HOSTCONF(x)		((x) + 0xC5Dul)	/* host config def'n register */
 
+#define HA_274_BIOSCTRL(x)	((x) + 0xC5Ful) /* BIOS enabled for 274x */
+#define BIOSMODE		0x30
+#define BIOSDISABLED		0x30
+
 #define MSG_ABORT		0x06
-#define	MSG_BUS_DEVICE_RESET	0x0c
+#define	MSG_BUS_DEVICE_RESET	0x0C
 #define BUS_8_BIT		0x00
 #define BUS_16_BIT		0x01
 #define BUS_32_BIT		0x02
@@ -734,8 +807,8 @@
  * kernel structure hasn't changed.
  */
 #define SG_STRUCT_CHECK(sg) \
-  ((char *)&(sg).address - (char *)&(sg) != 0 ||  \
-   (char *)&(sg).length  - (char *)&(sg) != 8 ||  \
+  ((char *) &(sg).address - (char *) &(sg) != 0 ||  \
+   (char *) &(sg).length  - (char *) &(sg) != 8 ||  \
    sizeof((sg).address) != 4 ||                   \
    sizeof((sg).length)  != 4 ||                   \
    sizeof(sg)           != 12)
@@ -786,36 +859,24 @@
 /*
  * The driver keeps up to four scb structures per card in memory. Only the
  * first 26 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookeeping. The driver is further optimized
- * so that we only have to download the first 19 bytes since as long
- * as we always use S/G, the last fields should be zero anyway.
+ * for driver level bookeeping.
  */
-#ifdef AIC7XXX_USE_SG
-#define SCB_DOWNLOAD_SIZE	19	/* amount to actually download */
-#else
-#define SCB_DOWNLOAD_SIZE	26
-#endif
-
-#define SCB_UPLOAD_SIZE		19	/* amount to actually upload */
+#define SCB_DOWNLOAD_SIZE	26	/* amount to actually download */
+#define SCB_UPLOAD_SIZE		26	/* amount to actually upload */
 
 struct aic7xxx_scb {
 /* ------------    Begin hardware supported fields    ---------------- */
 /*1 */  unsigned char control;
-#define SCB_NEEDWDTR 0x80                       /* Initiate Wide Negotiation */
-#define SCB_NEEDSDTR 0x40                       /* Initiate Sync Negotiation */
-#define SCB_NEEDDMA  0x08                       /* SCB needs to be DMA'd from
-						 * from host memory
-						 */
-#define SCB_REJ_MDP      0x80                   /* Reject MDP message */
-#define SCB_DISEN        0x40                   /* SCB Disconnect enable */
-#define SCB_TE           0x20                   /* Tag enable */
-/*      RESERVED         0x10 */
-#define SCB_WAITING      0x08                   /* Waiting */
-#define SCB_DIS          0x04                   /* Disconnected */
-#define SCB_TAG_TYPE     0x03
-#define         SIMPLE_QUEUE 0x00               /* Simple Queue */
-#define         HEAD_QUEUE   0x01               /* Head of Queue */
-#define         ORD_QUEUE    0x02               /* Ordered Queue */
+#define	SCB_NEEDWDTR 0x80			/* Initiate Wide Negotiation */
+#define SCB_DISCENB  0x40			/* Disconnection Enable */
+#define	SCB_TE	     0x20			/* Tag enable */
+#define SCB_NEEDSDTR 0x10			/* Initiate Sync Negotiation */
+#define	SCB_NEEDDMA  0x08			/* Refresh SCB from host ram */
+#define	SCB_DIS	     0x04
+#define	SCB_TAG_TYPE 0x03
+#define		SIMPLE_QUEUE	0x00
+#define		HEAD_QUEUE	0x01
+#define		OR_QUEUE	0x02
 /*              ILLEGAL      0x03 */
 /*2 */  unsigned char target_channel_lun;       /* 4/1/3 bits */
 /*3 */  unsigned char SG_segment_count;
@@ -830,7 +891,7 @@
 /*26*/  unsigned char data_count[3];
 /*30*/  unsigned char host_scb[4] __attribute__ ((packed));
 /*31*/  u_char next_waiting;            /* Used to thread SCBs awaiting selection. */
-#define SCB_LIST_NULL 0x10              /* SCB list equivelent to NULL */
+#define SCB_LIST_NULL 0xFF              /* SCB list equivelent to NULL */
 #if 0
 	/*
 	 *  No real point in transferring this to the
@@ -850,13 +911,22 @@
 #define SCB_IMMED              0x08
 #define SCB_SENSE              0x10
 	unsigned int        position;       /* Position in scb array */
-#ifdef AIC7XXX_USE_SG
 	struct scatterlist  sg;
 	struct scatterlist  sense_sg;
-#endif
 	unsigned char       sense_cmd[6];   /* Allocate 6 characters for sense command */
+#define TIMER_ENABLED		0x01
+#define TIMER_EXPIRED		0x02
+#define TIMED_CMD_DONE		0x04
+        volatile unsigned char timer_status;
+#ifndef AIC7XXX_POLL
+	struct wait_queue   *waiting;       /* wait queue for device reset command */
+        struct wait_queue   waitq;          /* waiting points to this */
+	struct timer_list   timer;          /* timeout for device reset command */
+#endif
 };
 
+typedef void (*timeout_fn)(unsigned long);
+
 static struct {
   unsigned char errno;
   const char *errmesg;
@@ -898,11 +968,34 @@
   volatile unsigned short  needwdtr_copy;    /* default config */
   volatile unsigned short  needwdtr;
   volatile unsigned short  wdtr_pending;
+  volatile unsigned short  discenable;       /* Targets allowed to disconnect */
   struct seeprom_config    seeprom;
   int                      have_seeprom;
   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 */
+#ifdef AIC7XXX_PROC_STATS
+  /*
+   * Statistics Kept:
+   *
+   * Total Xfers (count for each command that has a data xfer),
+   * broken down further by reads && writes.
+   *
+   * Binned sizes, writes && reads:
+   *    < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
+   *
+   * Total amounts read/written above 512 bytes (amts under ignored)
+   */
+  struct aic7xxx_xferstats {
+    long xfers;                              /* total xfer count */
+    long w_total;                            /* total writes */
+    long w_total512;                         /* 512 byte blocks written */
+    long w_bins[10];                         /* binned write */
+    long r_total;                            /* total reads */
+    long r_total512;                         /* 512 byte blocks read */
+    long r_bins[10];                         /* binned reads */
+  } stats[2][16][8];                         /* channel, target, lun */
+#endif /* AIC7XXX_PROC_STATS */
 };
 
 struct aic7xxx_host_config {
@@ -982,11 +1075,11 @@
    */
   if ((p->type == AIC_274x) || (p->type == AIC_284x))
   {
-    dfthresh = host_conf >> 6;
+    dfthresh = (host_conf >> 6);
   }
   else
   {
-    dfthresh = scsi_conf >> 6;
+    dfthresh = (scsi_conf >> 6);
   }
 
   brelease = p->busrtime;
@@ -1037,7 +1130,10 @@
 	 (scsi_conf & 0x40) ? "en" : "dis");
 
   if (((p->type == AIC_274x) || (p->type == AIC_284x)) && p->parity == AIC_UNKNOWN)
-  { /* Set the parity for 7770 based cards. */
+  {
+    /*
+     * Set the parity for 7770 based cards.
+     */
     p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
   }
   if (p->parity != AIC_UNKNOWN)
@@ -1061,9 +1157,35 @@
 	  (p->high_term == AIC_ENABLED) ? "en" : "dis");
   }
 }
+
+static void
+debug_scb(struct aic7xxx_scb *scb)
+{
+  printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
+         scb->control, scb->target_channel_lun, scb->SG_segment_count,
+         (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
+         (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
+         (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
+         (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
+         scb->SCSI_cmd_length);
+  printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
+         (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
+         scb->residual_SG_segment_count, (scb->residual_data_count[2] << 16) |
+         (scb->residual_data_count[1] << 8) | scb->residual_data_count[0]);
+  printk("data ptr 0x%x, data count %d, host scb 0x%x, next waiting %d\n",
+         (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
+         (scb->data_pointer[1] << 8) | scb->data_pointer[0],
+         (scb->data_count[2] << 16) | (scb->data_count[1] << 8) | scb->data_count[0],
+         (unsigned int) scb->host_scb, scb->next_waiting);
+  printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
+         (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
+         scb->position);
+}
+
 #else
 #  define debug(fmt, args...)
 #  define debug_config(x)
+#  define debug_scb(x)
 #endif AIC7XXX_DEBUG
 
 /*
@@ -1189,6 +1311,109 @@
   }
 }
 
+#ifdef AIC7XXX_POLL
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_poll_scb
+ *
+ * Description:
+ *   Function to poll for command completion when in aborting an SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_poll_scb(struct aic7xxx_host *p,
+                 struct aic7xxx_scb  *scb,
+                 unsigned long       timeout_ticks)
+{
+  unsigned long timer_expiration = jiffies + timeout_ticks;
+
+  while ((jiffies < timer_expiration) && !(scb->timer_status & TIMED_CMD_DONE))
+  {
+    udelay(1000);  /* delay for 1 msec. */
+  }
+}
+
+#else
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scb_timeout
+ *
+ * Description:
+ *   Called when a SCB reset command times out.  The input is actually
+ *   a pointer to the SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_scb_timeout(unsigned long data)
+{
+  struct aic7xxx_scb *scb = (struct aic7xxx_scb *) data;
+
+  scb->timer_status |= TIMER_EXPIRED;
+  wake_up(&(scb->waiting));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scb_untimeout
+ *
+ * Description:
+ *   This function clears the timeout and wakes up a waiting SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_scb_untimeout(struct aic7xxx_scb *scb)
+{
+  if (scb->timer_status & TIMER_ENABLED)
+  {
+    scb->timer_status = TIMED_CMD_DONE;
+    wake_up(&(scb->waiting));
+  }
+}
+#endif
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scb_tsleep
+ *
+ * Description:
+ *   Emulates a BSD tsleep where a process can sleep for a specified
+ *   amount of time, but may be awakened before that.  Linux provides
+ *   a sleep_on, wake_up, add_timer, and del_timer which can be used to
+ *   emulate tsleep, but there's not enough information available on
+ *   how to use them.  For now, we'll just poll for SCB completion.
+ *
+ *   The parameter ticks is the number of clock ticks
+ *   to wait before a timeout.  A 0 is returned if the scb does not
+ *   timeout, 1 is returned for a timeout.
+ *-F*************************************************************************/
+static int
+aic7xxx_scb_tsleep(struct aic7xxx_host *p,
+                   struct aic7xxx_scb  *scb,
+                   unsigned long        ticks)
+{
+  scb->timer_status = TIMER_ENABLED;
+#ifdef AIC7XXX_POLL
+  UNPAUSE_SEQUENCER(p);
+  aic7xxx_poll_scb(p, scb, ticks);
+#else
+  scb->waiting = &(scb->waitq);
+  scb->timer.expires = jiffies + ticks;
+  scb->timer.data = (unsigned long) scb;
+  scb->timer.function = (timeout_fn) aic7xxx_scb_timeout;
+  add_timer(&scb->timer);
+  UNPAUSE_SEQUENCER(p);
+  sleep_on(&(scb->waiting));
+  del_timer(&scb->timer);
+#endif
+  if (!(scb->timer_status & TIMED_CMD_DONE))
+  {
+    scb->timer_status = 0x0;
+    return (1);
+  }
+  else
+  {
+    scb->timer_status = 0x0;
+    return (0);
+  }
+}
+
 /*+F*************************************************************************
  * Function:
  *   rcs_version
@@ -1265,6 +1490,73 @@
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_length
+ *
+ * Description:
+ *   How much data should be transferred for this SCSI command? Stop
+ *   at segment sg_last if it's a scatter-gather command so we can
+ *   compute underflow easily.
+ *-F*************************************************************************/
+static unsigned
+aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+{
+  int i, segments;
+  unsigned length;
+  struct scatterlist *sg;
+
+  segments = cmd->use_sg - sg_last;
+  sg = (struct scatterlist *) cmd->buffer;
+
+  if (cmd->use_sg)
+  {
+    for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+    {
+      length += sg[i].length;
+    }
+  }
+  else
+  {
+    length = cmd->request_bufflen;
+  }
+
+  return (length);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scsirate
+ *
+ * Description:
+ *   Look up the valid period to SCSIRATE conversion in our table
+ *-F*************************************************************************/
+static void
+aic7xxx_scsirate(unsigned char *scsirate, unsigned char period,
+		 unsigned char offset, int target, char channel)
+{
+  int i;
+
+  for (i = 0; i < num_aic7xxx_syncrates; i++)
+  {
+    if ((aic7xxx_syncrates[i].period - period) >= 0)
+    {
+      *scsirate = (aic7xxx_syncrates[i].rate << 4) | (offset & 0x0F);
+      printk("aic7xxx: target %d, channel %c, now synchronous at %sMb/s, "
+             "offset = 0x%x\n",
+	     target, channel, aic7xxx_syncrates[i].english, offset);
+      return;
+    }
+  }
+
+  /*
+   * Default to asyncronous transfer
+   */
+  *scsirate = 0;
+  printk("aic7xxx: target %d, channel %c, using asynchronous transfers\n",
+         target, channel);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_putscb
  *
  * Description:
@@ -1273,42 +1565,23 @@
 static void
 aic7xxx_putscb(int base, struct aic7xxx_scb *scb)
 {
-#ifdef AIC7XXX_USE_DMA
   /*
    * All we need to do, is to output the position
    * of the SCB in the SCBARRAY to the QINFIFO
    * of the host adapter.
    */
   outb(scb->position, QINFIFO(base));
-#else
-  /*
-   * By turning on the SCB auto increment, any reference
-   * to the SCB I/O space postincrements the SCB address
-   * we're looking at. So turn this on and dump the relevant
-   * portion of the SCB to the card.
-   */
-  outb(SCBAUTO, SCBCNT(base));
-
-  asm volatile("cld\n\t"
-	       "rep\n\t"
-	       "outsb"
-	       : /* no output */
-	       :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
-	       :"si", "cx", "dx");
-
-  outb(0, SCBCNT(base));
-#endif
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_putdmascb
+ *   aic7xxx_putscb_dma
  *
  * Description:
  *   DMA a SCB to the controller.
  *-F*************************************************************************/
 static void
-aic7xxx_putdmascb(int base, struct aic7xxx_scb *scb)
+aic7xxx_putscb_dma(int base, struct aic7xxx_scb *scb)
 {
   /*
    * By turning on the SCB auto increment, any reference
@@ -1355,67 +1628,467 @@
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_length
+ *   aic7xxx_match_scb
  *
  * Description:
- *   How much data should be transferred for this SCSI command? Stop
- *   at segment sg_last if it's a scatter-gather command so we can
- *   compute underflow easily.
+ *   Checks to see if an scb matches the target/channel as specified.
+ *   If target is ALL_TARGETS (-1), then we're looking for any device
+ *   on the specified channel; this happens when a channel is going
+ *   to be reset and all devices on that channel must be aborted.
  *-F*************************************************************************/
-static unsigned
-aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+static int
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
 {
-  int i, segments;
-  unsigned length;
-  struct scatterlist *sg;
+  int targ = (scb->target_channel_lun >> 4) & 0x0F;
+  char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
 
-  segments = cmd->use_sg - sg_last;
-  sg = (struct scatterlist *) cmd->buffer;
+  if (target == ALL_TARGETS)
+  {
+    return (chan == channel);
+  }
+  else
+  {
+    return ((chan == channel) && (targ == target));
+  }
+}
 
-  if (cmd->use_sg)
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_unbusy_target
+ *
+ * Description:
+ *   Set the specified target inactive.
+ *-F*************************************************************************/
+static void
+aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+{
+  unsigned char active;
+  unsigned long active_port = HA_ACTIVE0(base);
+
+  if ((target > 0x07) || (channel == 'B'))
+  {
+    /*
+     * targets on the Second channel or above id 7 store info in byte two
+     * of HA_ACTIVE
+     */
+    active_port++;
+  }
+  active = inb(active_port);
+  active &= ~(0x01 << (target & 0x07));
+  outb(active_port, active);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_done
+ *
+ * Description:
+ *   Calls the higher level scsi done function and frees the scb.
+ *-F*************************************************************************/
+static void
+aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  long flags;
+  Scsi_Cmnd *cmd = scb->cmd;
+
+  if (scb->timer_status & TIMER_ENABLED)
+  {
+#ifdef AIC7XXX_POLL
+    scb->timer_status |= TIMED_CMD_DONE;
+#else
+    aic7xxx_scb_untimeout(scb);
+#endif
+  }
+  else
+  {
+    /*
+     * This is a critical section, since we don't want the
+     * queue routine mucking with the host data.
+     */
+    save_flags(flags);
+    cli();
+
+    /*
+     * 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 = &(p->scb_array[scb->position]);
+    scb->cmd = NULL;
+
+    restore_flags(flags);
+
+    cmd->scsi_done(cmd);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_add_waiting_scb
+ *
+ * Description:
+ *   Add this SCB to the "waiting for selection" list.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_waiting_scb(u_long              base,
+                        struct aic7xxx_scb *scb,
+                        insert_type         where)
+{
+  unsigned char head, tail;
+  unsigned char curscb;
+
+  curscb = inb(SCBPTR(base));
+  head = inb(WAITING_SCBH(base));
+  tail = inb(WAITING_SCBT(base));
+  if (head == SCB_LIST_NULL)
+  {
+    /*
+     * List was empty
+     */
+    head = scb->position;
+    tail = SCB_LIST_NULL;
+  }
+  else
   {
-    for (i = length = 0; i < cmd->use_sg && i < segments; i++)
+    if (where == LIST_HEAD)
     {
-      length += sg[i].length;
+      outb(scb->position, SCBPTR(base));
+      outb(head, SCBARRAY(base) + 30);
+      head = scb->position;
     }
+    else
+    {
+      if (tail == SCB_LIST_NULL)
+      {
+        /*
+         * List had one element
+         */
+        tail = scb->position;
+        outb(head, SCBPTR(base));
+        outb(tail, SCBARRAY(base) + 30);
+      }
+      else
+      {
+        if (where == LIST_SECOND)
+        {
+          unsigned char third_scb;
+
+          outb(head, SCBPTR(base));
+          third_scb = inb(SCBARRAY(base) + 30);
+          outb(scb->position, SCBARRAY(base) + 30);
+          outb(scb->position, SCBPTR(base));
+          outb(third_scb, SCBARRAY(base) + 30);
+        }
+        else
+        {
+          outb(tail, SCBPTR(base));
+          tail = scb->position;
+          outb(tail, SCBARRAY(base) + 30);
+        }
+      }
+    }
+  }
+  outb(head, WAITING_SCBH(base));
+  outb(tail, WAITING_SCBT(base));
+  outb(curscb, SCBPTR(base));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_abort_waiting_scb
+ *
+ * Description:
+ *   Manipulate the waiting for selection list and return the
+ *   scb that follows the one that we remove.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+                          unsigned char prev, unsigned char timedout_scb)
+{
+  unsigned char curscb, next;
+  int target = (scb->target_channel_lun >> 4) & 0x0F;
+  char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int base = p->base;
+
+  /*
+   * Select the SCB we want to abort and
+   * pull the next pointer out of it.
+   */
+  curscb = inb(SCBPTR(base));
+  outb(scb->position, SCBPTR(base));
+  next = inb(SCBARRAY(base) + 30);
+
+  /*
+   * Clear the necessary fields
+   */
+  outb(SCB_NEEDDMA, SCBARRAY(base));
+  outb(SCB_LIST_NULL, SCBARRAY(base) + 30);
+  aic7xxx_unbusy_target(target, channel, base);
+
+  /*
+   * Update the waiting list
+   */
+  if (prev == SCB_LIST_NULL)
+  {
+    /*
+     * First in the list
+     */
+    outb(next, WAITING_SCBH(base));
   }
   else
   {
-    length = cmd->request_bufflen;
+    /*
+     * Select the scb that pointed to us and update its next pointer.
+     */
+    outb(prev, SCBPTR(base));
+    outb(next, SCBARRAY(base) + 30);
+  }
+  /*
+   * Update the tale pointer
+   */
+  if (inb(WAITING_SCBT(base)) == scb->position)
+  {
+    outb(prev, WAITING_SCBT(base));
   }
 
-  return (length);
+  /*
+   * 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->cmd->result = (DID_RESET << 16);
+  aic7xxx_done(p, scb);
+
+  return (next);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_scsirate
+ *   aic7xxx_reset_device
  *
  * Description:
- *   Look up the valid period to SCSIRATE conversion in our table
+ *   The device at the given target/channel has been reset.  Abort
+ *   all active and queued scbs for that target/channel.
+ *-F*************************************************************************/
+static int
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+                     unsigned char timedout_scb)
+{
+  int base = p->base;
+  struct aic7xxx_scb *scb;
+  unsigned char active_scb;
+  int i = 0;
+  int found = 0;
+
+  /*
+   * Restore this when we're done
+   */
+  active_scb = inb(SCBPTR(base));
+
+  /*
+   * Search the QINFIFO.
+   */
+  {
+    int saved_queue[AIC7XXX_MAXSCB];
+    int queued = inb(QINCNT(base));
+
+    for (i = 0; i < (queued - found); i++)
+    {
+      saved_queue[i] = inb(QINFIFO(base));
+      scb = &(p->scb_array[saved_queue[i]]);
+      if (aic7xxx_match_scb(scb, target, channel))
+      {
+        /*
+         * We found an scb that needs to be aborted.
+         */
+        scb->state |= SCB_ABORTED;
+        scb->cmd->result = (DID_RESET << 16);
+        aic7xxx_done(p, scb);
+        outb(scb->position, SCBPTR(base));
+        outb(SCB_NEEDDMA, SCBARRAY(base));
+        i--;
+        found++;
+      }
+    }
+    /*
+     * Now put the saved scbs back.
+     */
+    for (queued = 0; queued < i; queued++)
+    {
+      outb(saved_queue[queued], QINFIFO(base));
+    }
+  }
+
+  /*
+   * Search waiting for selection list.
+   */
+  {
+    unsigned char next, prev;
+
+    next = inb(WAITING_SCBH(base));  /* Start at head of list. */
+    prev = SCB_LIST_NULL;
+
+    while (next != SCB_LIST_NULL)
+    {
+      scb = &(p->scb_array[next]);
+      /*
+       * Select the SCB.
+       */
+      if (aic7xxx_match_scb(scb, target, channel))
+      {
+        next = aic7xxx_abort_waiting_scb(p, scb, prev, timedout_scb);
+        found++;
+      }
+      else
+      {
+        outb(scb->position, SCBPTR(base));
+        prev = next;
+        next = inb(SCBARRAY(base) + 30);
+      }
+    }
+  }
+
+  /*
+   * 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 occured.
+   */
+  for(i = 0; i < p->numscb; i++)
+  {
+    scb = &(p->scb_array[i]);
+    if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+    {
+      /*
+       * Ensure the target is "free"
+       */
+      aic7xxx_unbusy_target(target, channel, base);
+      outb(scb->position, SCBPTR(base));
+      outb(SCB_NEEDDMA, SCBARRAY(base));
+      scb->state |= SCB_ABORTED;
+      scb->cmd->result = (DID_RESET << 16);
+      aic7xxx_done(p, scb);
+      found++;
+    }
+  }
+
+  outb(active_scb, SCBPTR(base));
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset_current_bus
+ *
+ * Description:
+ *   Reset the current SCSI bus.
  *-F*************************************************************************/
 static void
-aic7xxx_scsirate(unsigned char *scsirate, unsigned char period,
-		 unsigned char offset, int target)
+aic7xxx_reset_current_bus(int base)
 {
-  int i;
+  outb(SCSIRSTO, SCSISEQ(base));
+  udelay(1000);
+  outb(0, SCSISEQ(base));
+}
 
-  for (i = 0; i < num_aic7xxx_syncrates; i++)
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset_channel
+ *
+ * Description:
+ *   Reset the channel.
+ *-F*************************************************************************/
+static int
+aic7xxx_reset_channel(struct aic7xxx_host *p, char channel,
+                     unsigned char timedout_scb)
+{
+  int base = p->base;
+  unsigned char sblkctl;
+  char cur_channel;
+  unsigned long offset, offset_max;
+  int found;
+
+  /*
+   * Clean up all the state information for the
+   * pending transactions on this bus.
+   */
+  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, timedout_scb);
+
+  if (channel == 'B')
   {
-    if ((aic7xxx_syncrates[i].period - period) >= 0)
+    p->needsdtr |= (p->needsdtr_copy & 0xFF00);
+    p->sdtr_pending &= 0x00FF;
+    outb(0, HA_ACTIVE1(base));
+    offset = HA_TARG_SCRATCH(base) + 8;
+    offset_max = HA_TARG_SCRATCH(base) + 16;
+  }
+  else
+  {
+    if (p->bus_type == AIC_WIDE)
     {
-      *scsirate = (aic7xxx_syncrates[i].rate << 4) | (offset & 0x0F);
-      printk("aic7xxx: target %d now synchronous at %sMb/s, offset = 0x%x\n",
-	     target, aic7xxx_syncrates[i].english, offset);
-      return;
+      p->needsdtr = p->needsdtr_copy;
+      p->needwdtr = p->needwdtr_copy;
+      p->sdtr_pending = 0;
+      p->wdtr_pending = 0;
+      outb(0, HA_ACTIVE0(base));
+      outb(0, HA_ACTIVE1(base));
+      offset = HA_TARG_SCRATCH(base);
+      offset_max = HA_TARG_SCRATCH(base) + 16;
     }
+    else
+    {
+      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
+      p->sdtr_pending &= 0xFF00;
+      outb(0, HA_ACTIVE0(base));
+      offset = HA_TARG_SCRATCH(base);
+      offset_max = HA_TARG_SCRATCH(base) + 8;
+    }
+  }
+  while (offset < offset_max)
+  {
+    /*
+     * Revert to async/narrow transfers
+     * until we renegotiate.
+     */
+    u_char targ_scratch;
+    targ_scratch = inb(offset);
+    targ_scratch &= SXFR;
+    outb(targ_scratch, offset);
+    offset++;
   }
 
   /*
-   * Default to asyncronous transfer
+   * Reset the bus and unpause/restart the controller
    */
-  *scsirate = 0;
-  printk("aic7xxx: target %d using asynchronous transfers\n", target);
+
+  /*
+   * Case 1: Command for another bus is active
+   */
+  sblkctl = inb(SBLKCTL(base));
+  cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
+  if (cur_channel != channel)
+  {
+    /*
+     * Stealthily reset the other bus without upsetting the current bus
+     */
+    outb(sblkctl ^ SELBUSB, SBLKCTL(base));
+    aic7xxx_reset_current_bus(base);
+    outb(sblkctl, SBLKCTL(base));
+    UNPAUSE_SEQUENCER(p);
+  }
+  /*
+   * Case 2: A command from this bus is active or we're idle
+   */
+  else
+  {
+    aic7xxx_reset_current_bus(base);
+    RESTART_SEQUENCER(p);
+  }
+
+  return found;
 }
 
 /*+F*************************************************************************
@@ -1434,17 +2107,15 @@
   int base, intstat;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
-  unsigned char active, ha_flags, transfer;
+  unsigned char ha_flags, transfer;
   unsigned char scsi_id, bus_width;
-  unsigned char offset, rate, scratch;
+  unsigned char offset, rate, scratch, scratch_offset;
   unsigned char max_offset, rej_byte;
-  unsigned char head, tail;
-  unsigned short target_mask;
-  long flags;
+  unsigned short target_mask, active;
+  char channel;
   void *addr;
   int actual;
-  int target, tcl;
-  int scbptr;
+  int scb_index;
   Scsi_Cmnd *cmd;
 
   p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
@@ -1483,7 +2154,10 @@
     }
   }
 
-  p->isr_count++; /* Keep track of interrupts for /proc/scsi */
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
 
   if ((p->a_scanned == 0) && (p->isr_count == 1))
   {
@@ -1519,20 +2193,28 @@
     }
 
     panic("aic7xxx_isr: brkadrint, error = 0x%x, seqaddr = 0x%x\n",
-	  inb(ERROR(base)),
-	  inb(SEQADDR1(base)) << 8 | inb(SEQADDR0(base)));
+	  inb(ERROR(base)), (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base)));
   }
 
   if (intstat & SEQINT)
   {
     /*
      * Although the sequencer is paused immediately on
-     * a SEQINT, an interrupt for a SCSIINT or a CMDCMPLT
-     * condition will have unpaused the sequencer before
-     * this point.
+     * a SEQINT, an interrupt for a SCSIINT condition will
+     * unpaused the sequencer before this point.
      */
     PAUSE_SEQUENCER(p);
 
+    scsi_id = (inb(SCSIID(base)) >> 4) & 0x0F;
+    scratch_offset = scsi_id;
+    channel = 'A';
+    if (inb(SBLKCTL(base)) & SELBUSB)
+    {
+      channel = 'B';
+      scratch_offset += 8;
+    }
+    target_mask = (0x01 << scratch_offset);
+
     switch (intstat & SEQINT_MASK)
     {
       case BAD_PHASE:
@@ -1541,9 +2223,6 @@
 
       case SEND_REJECT:
         rej_byte = inb(HA_REJBYTE(base));
-        scsi_id = inb(SCSIID(base)) >> 0x04;
-        scbptr = inb(SCBPTR(base));
-        scb = &(p->scb_array[scbptr]);
         if (rej_byte != 0x20)
         {
           debug("aic7xxx_isr warning: issuing message reject, 1st byte 0x%x\n",
@@ -1551,53 +2230,28 @@
         }
         else
         {
+          scb_index = inb(SCBPTR(base));
+          scb = &(p->scb_array[scb_index]);
           printk("aic7xxx_isr warning: Tagged message rejected for target %d,"
-                 " channel %c.\n",
-                 scsi_id, (inb(SBLKCTL(base)) & SELBUSB ? 'B': 'A'));
+                 " channel %c.\n", scsi_id, channel);
           scb->cmd->device->tagged_supported = 0;
           scb->cmd->device->tagged_queue = 0;
         }
 	break;
 
       case NO_IDENT:
-	panic("aic7xxx_isr: reconnecting target %d at seqaddr 0x%x "
-	      "didn't issue IDENTIFY message\n",
-	      (inb(SELID(base)) >> 4) & 0x0F,
-	      (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base)));
+	panic("aic7xxx_isr: Target %d, channel %c, did not send an IDENTIFY "
+	      "message.  SAVED_TCL = 0x%x\n",
+              scsi_id, channel, inb(SAVED_TCL(base)));
 	break;
 
       case NO_MATCH:
-	tcl = inb(SCBARRAY(base) + 1);
-	target = (tcl >> 4) & 0x0F;
-	/* Purposefully mask off the top bit of targets 8-15. */
-	target_mask = 0x01 << (target & 0x07);
-
-	debug("aic7xxx_isr: sequencer couldn't find match "
-	      "for reconnecting target %d, channel %d, lun %d - "
-	      "issuing ABORT\n", target, (tcl & 0x08) >> 3, tcl & 0x07);
-	if (tcl & 0x88)
-	{
-	  /* Second channel stores its info in byte
-	   * two of HA_ACTIVE
-	   */
-	  active = inb(HA_ACTIVE1(base));
-	  active = active & ~(target_mask);
-	  outb(active, HA_ACTIVE1(base));
-	}
-	else
-	{
-	  active = inb(HA_ACTIVE0(base));
-	  active = active & ~(target_mask);
-	  outb(active, HA_ACTIVE0(base));
-	}
-#ifdef AIC7XXX_USE_DMA
-	outb(SCB_NEEDDMA, SCBARRAY(base));
-#endif
+	printk("aic7xxx_isr: No active SCB for reconnecting target %d, "
+	      "channel %c - issuing ABORT\n", scsi_id, channel);
+        printk("SAVED_TCL = 0x%x\n", inb(SAVED_TCL(base)));
+        aic7xxx_unbusy_target(scsi_id, channel, base);
+        outb(SCB_NEEDDMA, SCBARRAY(base));
 
-	/*
-	 * Check out why this use to be outb(0x80, CLRINT(base))
-	 * clear the timeout
-	 */
 	outb(CLRSELTIMEO, CLRSINT1(base));
 	RESTART_SEQUENCER(p);
 	break;
@@ -1611,33 +2265,27 @@
 	 */
 	transfer = (inb(HA_ARG_1(base)) << 2);
 	offset = inb(ACCUM(base));
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	if (inb(SBLKCTL(base)) & 0x08)
-	{
-	  scsi_id = scsi_id + 8;  /* B channel */
-	}
-	target_mask = (0x01 << scsi_id);
-	scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
+	scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
 	/*
 	 * The maximum offset for a wide device is 0x08; for a
-	 * 8-bit bus device the maximum offset is 0x0f.
+	 * 8-bit bus device the maximum offset is 0x0F.
 	 */
-	if (scratch & 0x80)
+	if (scratch & WIDEXFER)
 	{
 	  max_offset = 0x08;
 	}
 	else
 	{
-	  max_offset = 0x0f;
+	  max_offset = 0x0F;
 	}
-	aic7xxx_scsirate(&rate, transfer, MIN(offset, max_offset), scsi_id);
+	aic7xxx_scsirate(&rate, transfer, MIN(offset, max_offset), scsi_id, channel);
 	/*
 	 * Preserve the wide transfer flag.
 	 */
-	rate = rate | (scratch & 0x80);
-	outb(rate, HA_TARG_SCRATCH(base) + scsi_id);
-	outb(rate, SCSIRATE(base));
-	if ((rate & 0xf) == 0)
+	scratch = rate | (scratch & WIDEXFER);
+	outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	outb(scratch, SCSIRATE(base));
+	if ((scratch & 0x0F) == 0)
 	{ /*
 	   * The requested rate was so low that asynchronous transfers
 	   * are faster (not to mention the controller won't support
@@ -1670,23 +2318,23 @@
 	/*
 	 * Clear the flags.
 	 */
-	p->needsdtr = p->needsdtr & ~target_mask;
-	p->sdtr_pending = p->sdtr_pending & ~target_mask;
+	p->needsdtr &= ~target_mask;
+	p->sdtr_pending &= ~target_mask;
+#if 0
+  scb_index = inb(SCBPTR(base));
+  scb = &(p->scb_array[scb_index]);
+  debug_scb(scb);
+#endif
+
 	break;
 
       case MSG_WDTR:
       {
 	bus_width = inb(ACCUM(base));
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	if (inb(SBLKCTL(base)) & 0x08)
-	{
-	  scsi_id = scsi_id + 8;  /* B channel */
-	}
-	printk("aic7xxx_isr: Received MSG_WDTR, scsi_id = %d, "
-	       "needwdtr = 0x%x\n", scsi_id, p->needwdtr);
-	scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
+	printk("aic7xxx_isr: Received MSG_WDTR, scsi_id %d, channel %c "
+	       "needwdtr = 0x%x\n", scsi_id, channel, p->needwdtr);
+	scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
 
-	target_mask = (0x01 << scsi_id);
 	if (p->wdtr_pending & target_mask)
 	{
 	  /*
@@ -1696,13 +2344,13 @@
 	  switch (bus_width)
 	  {
 	    case BUS_8_BIT:
-	      scratch = scratch & 0x7F;
+	      scratch &= 0x7F;
 	      break;
 
 	    case BUS_16_BIT:
-	      printk("aic7xxx_isr: target %d using 16 bit transfers\n",
-		     scsi_id);
-	      scratch = scratch | 0x80;
+	      printk("aic7xxx_isr: target %d, channel %c, using 16 bit transfers\n",
+		     scsi_id, channel);
+	      scratch |= 0x80;
 	      break;
 	  }
 	}
@@ -1715,25 +2363,27 @@
 	  switch (bus_width)
 	  {
 	    case BUS_8_BIT:
-	      scratch = scratch & 0x7F;
+	      scratch &= 0x7F;
 	      break;
 
 	    case BUS_32_BIT:
-	      /* Negotiate 16 bits. */
+	      /*
+               * Negotiate 16 bits.
+               */
 	      bus_width = BUS_16_BIT;
-	      /* Yes, we mean to fall thru here */
+	      /* Yes, we mean to fall thru here. */
 
 	    case BUS_16_BIT:
-	      printk("aic7xxx_isr: target %d using 16 bit transfers\n",
-		     scsi_id);
-	      scratch = scratch | 0x80;
+	      printk("aic7xxx_isr: target %d, channel %c, using 16 bit transfers\n",
+		     scsi_id, channel);
+	      scratch |= 0x80;
 	      break;
 	  }
 	  outb(bus_width | SEND_WDTR, HA_RETURN_1(base));
 	}
-	p->needwdtr = p->needwdtr & ~target_mask;
-	p->wdtr_pending = p->wdtr_pending & ~target_mask;
-	outb(scratch, HA_TARG_SCRATCH(base) + scsi_id);
+	p->needwdtr &= ~target_mask;
+	p->wdtr_pending &= ~target_mask;
+	outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
 	outb(scratch, SCSIRATE(base));
 	break;
       }
@@ -1747,64 +2397,52 @@
 	 * the target is refusing negotiation.
 	 */
 
-	unsigned char targ_scratch, scsi_id;
-	unsigned short mask;
+	scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
 
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	if (inb(SBLKCTL(base)) & 0x08)
-	{
-	  scsi_id = scsi_id + 8;
-	}
-
-	mask = (0x01 << scsi_id);
-
-	targ_scratch = inb(HA_TARG_SCRATCH(base) + scsi_id);
-
-	if (p->wdtr_pending & mask)
+	if (p->wdtr_pending & target_mask)
 	{
 	  /*
 	   * note 8bit xfers and clear flag
 	   */
-	  targ_scratch = targ_scratch & 0x7F;
-	  p->needwdtr = p->needwdtr & ~mask;
-	  p->wdtr_pending = p->wdtr_pending & ~mask;
-	  outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
-	  printk("aic7xxx: target %d refusing WIDE negotiation. Using "
-		 "8 bit transfers\n", scsi_id);
+	  scratch &= 0x7F;
+	  p->needwdtr &= ~target_mask;
+	  p->wdtr_pending &= ~target_mask;
+	  outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	  printk("aic7xxx: target %d, channel %c, refusing WIDE negotiation. "
+                 "Using 8 bit transfers\n", scsi_id, channel);
 	}
 	else
 	{
-	  if (p->sdtr_pending & mask)
+	  if (p->sdtr_pending & target_mask)
 	  {
 	    /*
 	     * note asynch xfers and clear flag
 	     */
-	    targ_scratch = targ_scratch & 0xF0;
-	    p->needsdtr = p->needsdtr & ~mask;
-	    p->sdtr_pending = p->sdtr_pending & ~mask;
-	    outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
-	    printk("aic7xxx: target %d refusing syncronous negotiation. Using "
-		   "asyncronous transfers\n", scsi_id);
+	    scratch &= 0xF0;
+	    p->needsdtr &= ~target_mask;
+	    p->sdtr_pending &= ~target_mask;
+	    outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	    printk("aic7xxx: target %d, channel %c, refusing syncronous negotiation. "
+                   "Using asyncronous transfers\n", scsi_id, channel);
 	  }
 	  /*
 	   * Otherwise, we ignore it.
 	   */
 	}
-	outb(targ_scratch, HA_TARG_SCRATCH(base) + scsi_id);
-	outb(targ_scratch, SCSIRATE(base));
+	outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
+	outb(scratch, SCSIRATE(base));
 	break;
       }
 
       case BAD_STATUS:
-	scsi_id = inb(SCSIID(base)) >> 0x04;
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	outb(0, HA_RETURN_1(base));   /* CHECK_CONDITION may change this */
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -1812,62 +2450,51 @@
 	  aic7xxx_getscb(base, scb);
 	  aic7xxx_status(cmd) = scb->target_status;
 
-	  cmd->result = cmd->result | scb->target_status;
+	  cmd->result |= scb->target_status;
 
-	  /*
-	   * This test is just here for debugging purposes.
-	   * It will go away when the timeout problem is resolved.
-	   */
 	  switch (status_byte(scb->target_status))
 	  {
 	    case GOOD:
+              printk("aic7xxx_isr: Interrupted for status of 0???\n");
 	      break;
 
 	    case CHECK_CONDITION:
 	      if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
 	      {
+                unsigned char tcl;
+                unsigned char control;
 		void         *req_buf;
-#ifndef AIC7XXX_USE_SG
-		unsigned int  req_buflen;
-#endif
-
-		/* Update the timeout for the SCSI command. */
-/*                update_timeout(cmd, SENSE_TIMEOUT); */
 
-		/* Send a sense command to the requesting target. */
-		cmd->flags = cmd->flags | WAS_SENSE;
+                tcl = scb->target_channel_lun;
+		/*
+                 * Send a sense command to the requesting target.
+                 */
+		cmd->flags |= WAS_SENSE;
 		memcpy((void *) scb->sense_cmd, (void *) generic_sense,
 		       sizeof(generic_sense));
 
-		scb->sense_cmd[1] = cmd->lun << 5;
+		scb->sense_cmd[1] = (cmd->lun << 5);
 		scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
 
-#ifdef AIC7XXX_USE_SG
 		scb->sense_sg.address = (char *) &cmd->sense_buffer;
 		scb->sense_sg.length = sizeof(cmd->sense_buffer);
 		req_buf = &scb->sense_sg;
-#else
-		req_buf = &cmd->sense_buffer;
-		req_buflen = sizeof(cmd->sense_buffer);
-#endif
 		cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+                control = scb->control;
 		memset(scb, 0, SCB_DOWNLOAD_SIZE);
-		scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
-		    ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+                scb->control = control & SCB_DISCENB;
+		scb->target_channel_lun = tcl;
 		addr = scb->sense_cmd;
 		scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
 		memcpy(scb->SCSI_cmd_pointer, &addr,
 		       sizeof(scb->SCSI_cmd_pointer));
-#ifdef AIC7XXX_USE_SG
 		scb->SG_segment_count = 1;
 		memcpy(scb->SG_list_pointer, &req_buf,
 			sizeof(scb->SG_list_pointer));
-#else
-		scb->SG_segment_count = 0;
-		memcpy(scb->data_pointer, &req_buf,
-			sizeof(scb->data_pointer));
-		memcpy(scb->data_count, &req_buflen, 3);
-#endif
+                scb->data_count[0] = scb->sense_sg.length & 0xFF;
+                scb->data_count[1] = (scb->sense_sg.length >> 8) & 0xFF;
+                scb->data_count[2] = (scb->sense_sg.length >> 16) & 0xFF;
+		memcpy(scb->data_pointer, &(scb->sense_sg.address), 4);
 
 		outb(SCBAUTO, SCBCNT(base));
 		asm volatile("cld\n\t"
@@ -1878,34 +2505,16 @@
 			     :"si", "cx", "dx");
 		outb(0, SCBCNT(base));
 		outb(SCB_LIST_NULL, (SCBARRAY(base) + 30));
+                /*
+                 * Ensure that the target is "BUSY" so we don't get overlapping
+                 * commands if we happen to be doing tagged I/O.
+                 */
+                active = inb(HA_ACTIVE0(base)) | (inb(HA_ACTIVE1(base)) << 8);
+                active |= target_mask;
+                outb(active & 0xFF, HA_ACTIVE0(base));
+                outb((active >> 8) & 0xFF, HA_ACTIVE1(base));
 
-		/*
-		 * Add this SCB to the "waiting for selection" list.
-		 */
-		head = inb(WAITING_SCBH(base));
-		tail = inb(WAITING_SCBT(base));
-		if (head & SCB_LIST_NULL)
-		{ /* list is empty */
-		  head = scb->position;
-		  tail = SCB_LIST_NULL;
-		}
-		else
-		{
-		  if (tail & SCB_LIST_NULL)
-		  { /* list has one element */
-		    tail = scb->position;
-		    outb(head, SCBPTR(base));
-		    outb(tail, (SCBARRAY(base) + 30));
-		  }
-		  else
-		  { /* list has more than one element */
-		    outb(tail, SCBPTR(base));
-		    tail = scb->position;
-		    outb(tail, (SCBARRAY(base) + 30));
-		  }
-		}
-		outb(head, WAITING_SCBH(base));
-		outb(tail, WAITING_SCBT(base));
+                aic7xxx_add_waiting_scb(base, scb, LIST_HEAD);
 		outb(SEND_SENSE, HA_RETURN_1(base));
 	      }  /* first time sense, no errors */
 	      else
@@ -1915,7 +2524,7 @@
 		 * a normal command complete, and have the scsi driver handle
 		 * this condition.
 		 */
-		cmd->flags = cmd->flags | ASKED_FOR_SENSE;
+		cmd->flags |= ASKED_FOR_SENSE;
 	      }
 	      break;
 
@@ -1948,13 +2557,13 @@
 	break;
 
       case RESIDUAL:
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -1980,9 +2589,8 @@
 	    if (actual < cmd->underflow)
 	    {
 	      printk("aic7xxx: target %d underflow - "
-		     "wanted (at least) %u, got %u\n",
-		     cmd->target, cmd->underflow, actual);
-
+		     "wanted (at least) %u, got %u, count=%d\n",
+		     cmd->target, cmd->underflow, actual, inb(SCBARRAY(base + 18)));
 	      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
 	      aic7xxx_status(cmd) = scb->target_status;
 	    }
@@ -1991,13 +2599,13 @@
 	break;
 
       case ABORT_TAG:
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -2008,38 +2616,21 @@
 	   */
 	  printk("aic7xxx_isr: invalid tag recieved on channel %c "
 		 "target %d, lun %d -- sending ABORT_TAG\n",
-		  (cmd->channel & 0x01) ? 'B':'A',
-		  cmd->target, cmd->lun & 0x07);
-	  /*
-	   *  This is a critical section, since we don't want the
-	   *  queue routine mucking with the host data.
-	   */
-	  save_flags(flags);
-	  cli();
-
-	  /*
-	   *  Process the command after marking the scb as free
-	   *  and adding it to the free list.
-	   */
-	  scb->state = SCB_FREE;
-	  scb->cmd = NULL;
-	  scb->next = p->free_scb;      /* preserve next pointer */
-	  p->free_scb = scb;            /* add at head of list */
+		  channel, scsi_id, cmd->lun & 0x07);
 
-	  restore_flags(flags);
 	  cmd->result = (DID_RETRY_COMMAND << 16);
-	  cmd->scsi_done(cmd);
+          aic7xxx_done(p, scb);
 	}
 	break;
 
       case AWAITING_MSG:
-	scbptr = inb(SCBPTR(base));
-	scb = &(p->scb_array[scbptr]);
+	scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
 	if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
 	{
 	  printk("aic7xxx_isr: referenced scb not valid "
 		 "during seqint 0x%x scb(%d) state(%x), cmd(%x)\n",
-		 intstat, scbptr, scb->state, (unsigned int) scb->cmd);
+		 intstat, scb_index, scb->state, (unsigned int) scb->cmd);
 	}
 	else
 	{
@@ -2061,6 +2652,32 @@
 	}
 	break;
 
+      case IMMEDDONE:
+        scb_index = inb(SCBPTR(base));
+	scb = &(p->scb_array[scb_index]);
+        if (scb->state & SCB_DEVICE_RESET)
+        {
+          int found;
+
+          /*
+           * Go back to async/narrow transfers and renogiate.
+           */
+          aic7xxx_unbusy_target(scsi_id, channel, base);
+          p->needsdtr |= (p->needsdtr_copy & target_mask);
+          p->needwdtr |= (p->needwdtr_copy & target_mask);
+          p->sdtr_pending &= ~target_mask;
+          p->wdtr_pending &= ~target_mask;
+          scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
+          scratch &= SXFR;
+          outb(scratch, HA_TARG_SCRATCH(base));
+          found = aic7xxx_reset_device(p, (int) scsi_id, channel, SCB_LIST_NULL);
+        }
+        else
+        {
+          panic("aic7xxx_isr: Immediate complete for unknown operation.\n");
+        }
+        break;
+
       default:               /* unknown */
 	debug("aic7xxx_isr: seqint, intstat = 0x%x, scsisigi = 0x%x\n",
 	      intstat, inb(SCSISIGI(base)));
@@ -2074,8 +2691,8 @@
   {
     int status = inb(SSTAT1(base));
 
-    scbptr = inb(SCBPTR(base));
-    scb = &p->scb_array[scbptr];
+    scb_index = inb(SCBPTR(base));
+    scb = &(p->scb_array[scb_index]);
     if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
     {
       printk("aic7xxx_isr: no command for scb (scsiint)\n");
@@ -2128,13 +2745,11 @@
 	else
 	{
 	  active = inb(HA_ACTIVE0(base));
-	  active = active & ~(target_mask);
+	  active &= ~(target_mask);
 	  outb(active, HA_ACTIVE0(base));
 	}
 
-#ifdef AIC7XXX_USE_DMA
 	outb(SCB_NEEDDMA, SCBARRAY(base));
-#endif
 
 	/*
 	 * Shut off the offending interrupt sources, reset
@@ -2155,32 +2770,16 @@
 
 	outb(CLRSCSIINT, CLRINT(base));
 
-	/* Shift the waiting for selection queue forward */
+	/*
+         * Shift the waiting for selection queue forward
+         */
 	waiting = inb(WAITING_SCBH(base));
 	outb(waiting, SCBPTR(base));
 	waiting = inb(SCBARRAY(base) + 30);
 	outb(waiting, WAITING_SCBH(base));
 
 	RESTART_SEQUENCER(p);
-	/*
-	 * This is a critical section, since we don't want the
-	 * queue routine mucking with the host data.
-	 */
-	save_flags(flags);
-	cli();
-
-	/*
-	 * Process the command after marking the scb as free
-	 * and adding it to the free list.
-	 */
-	scb->state = SCB_FREE;
-	scb->cmd = NULL;
-	scb->next = p->free_scb;        /* preserve next pointer */
-	p->free_scb = scb;              /* add at head of list */
-
-	restore_flags(flags);
-
-	cmd->scsi_done(cmd);
+        aic7xxx_done(p, scb);
 #if 0
   printk("aic7xxx_isr: SELTO scb(%d) state(%x), cmd(%x)\n",
 	 scb->position, scb->state, (unsigned int) scb->cmd);
@@ -2246,9 +2845,9 @@
       {
 	printk("aic7xxx warning: "
 	       "no command for scb %d (cmdcmplt)\n"
-	       "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x\n",
+	       "QOUTCNT = %d, SCB state = 0x%x, CMD = 0x%x, pos = %d\n",
 	       complete, inb(QOUTFIFO(base)),
-	       scb->state, (unsigned int) scb->cmd);
+	       scb->state, (unsigned int) scb->cmd, scb->position);
 	outb(CLRCMDINT, CLRINT(base));
 	continue;
       }
@@ -2256,26 +2855,28 @@
 
       cmd->result = (aic7xxx_error(cmd) << 16) | aic7xxx_status(cmd);
       if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
-      { /* Got sense information. */
-	cmd->flags = cmd->flags & ASKED_FOR_SENSE;
+      {
+        /*
+         * Got sense information.
+         */
+	cmd->flags &= ASKED_FOR_SENSE;
       }
 #if 0
       printk("aic7xxx_intr: (complete) state = %d, cmd = 0x%x, free = 0x%x\n",
 	     scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
 #endif
+
       /*
-       * This is a critical section, since we don't want the
-       * queue routine mucking with the host data.
+       * Clear interrupt status before checking
+       * the output queue again. This eliminates
+       * a race condition whereby a command could
+       * complete between the queue poll and the
+       * interrupt clearing, so notification of the
+       * command being complete never made it back
+       * up to the kernel.
        */
-      save_flags(flags);
-      cli();
-
-      scb->state = SCB_FREE;
-      scb->next = p->free_scb;
-      scb->cmd = NULL;
-      p->free_scb = &(p->scb_array[scb->position]);
-
-      restore_flags(flags);
+      outb(CLRCMDINT, CLRINT(base));
+      aic7xxx_done(p, scb);
 #if 0
   if (scb != &p->scb_array[scb->position])
   {
@@ -2285,18 +2886,48 @@
 	 scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
 #endif
 
-      cmd->scsi_done(cmd);
-
+#ifdef AIC7XXX_PROC_STATS
       /*
-       * Clear interrupt status before checking
-       * the output queue again. This eliminates
-       * a race condition whereby a command could
-       * complete between the queue poll and the
-       * interrupt clearing, so notification of the
-       * command being complete never made it back
-       * up to the kernel.
+       * XXX: we should actually know how much actually transferred
+       * XXX: for each command, but apparently that's too difficult.
        */
-      outb(CLRCMDINT, CLRINT(base));
+      actual = aic7xxx_length(cmd, 0);
+      if (((cmd->flags & WAS_SENSE) == 0) && (actual > 0))
+      {
+        struct aic7xxx_xferstats *sp;
+        long *ptr;
+        int x;
+
+        sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+        sp->xfers++;
+
+        if (cmd->request.cmd == WRITE)
+        {
+          sp->w_total++;
+          sp->w_total512 += (actual >> 9);
+          ptr = sp->w_bins;
+        }
+        else
+        {
+          sp->r_total++;
+          sp->r_total512 += (actual >> 9);
+          ptr = sp->r_bins;
+        }
+        for (x = 9; x <= 17; x++)
+        {
+          if (actual < (1 << x))
+          {
+            ptr[x - 9]++;
+            break;
+          }
+        }
+        if (x > 17)
+        {
+          ptr[x - 9]++;
+        }
+      }
+#endif /* AIC7XXX_PROC_STATS */
+
     } while (inb(QOUTCNT(base)));
   }
 }
@@ -2449,7 +3080,7 @@
   timeout = jiffies + 100;  /* 1 second timeout */
   while ((jiffies < timeout) && ((inb(SEECTL(base)) & SEERDY) == 0))
   {
-    ;  /* Do nothing!  Wait for access to be granted. */
+    ; /* Do nothing!  Wait for access to be granted.  */
   }
   if ((inb(SEECTL(base)) & SEERDY) == 0)
   {
@@ -2465,7 +3096,9 @@
    */
   for (k = 0; k < (sizeof(*sc) / 2); k++)
   {
-    /* Send chip select for one clock cycle. */
+    /*
+     * Send chip select for one clock cycle.
+     */
     outb(SEEMS | SEECK | SEECS, SEECTL(base));
     CLOCK_PULSE(base);
 
@@ -2482,7 +3115,9 @@
       outb(temp, SEECTL(base));
       CLOCK_PULSE(base);
     }
-    /* Send the 6 bit address (MSB first, LSB last). */
+    /*
+     * Send the 6 bit address (MSB first, LSB last).
+     */
     for (i = 5; i >= 0; i--)
     {
       temp = k;
@@ -2523,7 +3158,9 @@
       checksum = checksum + seeprom[k];
     }
 
-    /* Reset the chip select for the next command cycle. */
+    /*
+     * Reset the chip select for the next command cycle.
+     */
     outb(SEEMS, SEECTL(base));
     CLOCK_PULSE(base);
     outb(SEEMS | SEECK, SEECTL(base));
@@ -2532,11 +3169,10 @@
     CLOCK_PULSE(base);
   }
 
-  if (checksum != sc->checksum)
-  {
-    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
-    return (0);
-  }
+  /*
+   * Release access to the memory port and the serial EEPROM.
+   */
+  outb(0, SEECTL(base));
 
 #if 0
   printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
@@ -2552,8 +3188,12 @@
   printk("\n");
 #endif
 
-  /* Release access to the memory port and the serial EEPROM. */
-  outb(0, SEECTL(base));
+  if (checksum != sc->checksum)
+  {
+    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+    return (0);
+  }
+
   return (1);
 }
 
@@ -2586,7 +3226,10 @@
       sblkctl_reg = inb(SBLKCTL(base)) ^ AUTOFLUSHDIS;
       outb(sblkctl_reg, SBLKCTL(base));
       if (inb(SBLKCTL(base)) == sblkctl_reg)
-      {  /* We detected a Rev E board. */
+      {
+        /*
+         * We detected a Rev E board.
+         */
 	printk("aic7770: Rev E and subsequent; using 4 SCB's\n");
 	outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL(base));
 	maxscb = 4;
@@ -2641,6 +3284,7 @@
   unsigned char sblkctl;
   int max_targets;
   int found = 1;
+  int bios_disabled = 0;
   unsigned char target_settings;
   unsigned char scsi_conf, host_conf;
   int have_seeprom = 0;
@@ -2713,6 +3357,10 @@
        * since there was some issue about reseting the board.
        */
       config.irq = inb(HA_INTDEF(config.base)) & 0x0F;
+      if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED)
+      {
+        bios_disabled = 1;
+      }
       host_conf = inb(HA_HOSTCONF(config.base));
       config.busrtime = host_conf & 0x3C;
       /* XXX Is this valid for motherboard based controllers? */
@@ -2736,6 +3384,10 @@
       config.pause = REQ_PAUSE; /* DWG would like to be like the rest */
       config.extended = aic7xxx_extended;
       config.irq = inb(HA_INTDEF(config.base)) & 0x0F;
+      if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED)
+      {
+        bios_disabled = 1;
+      }
       host_conf = inb(HA_HOSTCONF(config.base));
       config.busrtime = host_conf & 0x3C;
       /* XXX Is this valid for motherboard based controllers? */
@@ -2772,7 +3424,7 @@
       else
       {
 	printk("done\n");
-	config.extended = (sc.bios_control & CFEXTEND) >> 7;
+	config.extended = ((sc.bios_control & CFEXTEND) >> 7);
 	config.scsi_id = (sc.brtime_id & CFSCSIID);
 	config.parity = (sc.adapter_control & CFSPARITY) ?
 			 AIC_ENABLED : AIC_DISABLED;
@@ -2780,7 +3432,7 @@
 			      AIC_ENABLED : AIC_DISABLED;
 	config.high_term = (sc.adapter_control & CFWSTERM) ?
 			      AIC_ENABLED : AIC_DISABLED;
-	config.busrtime = (sc.brtime_id & CFBRTIME) >> 8;
+	config.busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
       }
 
       /*
@@ -2827,13 +3479,13 @@
   sblkctl = inb(SBLKCTL(base)) & 0x0F;  /* mask out upper two bits */
   switch (sblkctl)
   {
-    case 0:     /* narrow/normal bus */
+    case SELSINGLE:     /* narrow/normal bus */
       config.scsi_id = inb(HA_SCSICONF(base)) & 0x07;
       config.bus_type = AIC_SINGLE;
-      outb(0, HA_FLAGS(base));
+      outb(SINGLE_BUS, HA_FLAGS(base));
       break;
 
-    case 2:     /* Wide bus */
+    case SELWIDE:     /* Wide bus */
       config.scsi_id = inb(HA_SCSICONF(base) + 1) & 0x0F;
       config.bus_type = AIC_WIDE;
       printk("aic7xxx: Enabling wide channel of %s-Wide\n",
@@ -2841,7 +3493,7 @@
       outb(WIDE_BUS, HA_FLAGS(base));
       break;
 
-    case 8:     /* Twin bus */
+    case SELBUSB:     /* Twin bus */
       config.scsi_id = inb(HA_SCSICONF(base)) & 0x07;
 #ifdef AIC7XXX_TWIN_SUPPORT
       config.scsi_id_b = inb(HA_SCSICONF(base) + 1) & 0x07;
@@ -2940,11 +3592,7 @@
    */
   host = scsi_register(template, sizeof(struct aic7xxx_host));
   host->can_queue = config.maxscb;
-#ifdef AIC7XXX_TAGGED_QUEUEING
-  host->cmd_per_lun = 2;
-#else
-  host->cmd_per_lun = 1;
-#endif
+  host->cmd_per_lun = AIC7XXX_CMDS_PER_LUN;
   host->this_id = config.scsi_id;
   host->irq = config.irq;
   if (config.bus_type == AIC_WIDE)
@@ -2958,7 +3606,9 @@
 
   p = (struct aic7xxx_host *) host->hostdata;
 
-  /* Initialize the scb array by setting the state to free. */
+  /*
+   * Initialize the scb array by setting the state to free.
+   */
   for (i = 0; i < AIC7XXX_MAXSCB; i++)
   {
     p->scb_array[i].state = SCB_FREE;
@@ -3025,10 +3675,12 @@
   printk("aic7xxx: Downloading sequencer code..");
   aic7xxx_loadseq(base);
 
-  /* Set Fast Mode and Enable the board */
+  /*
+   * Set Fast Mode and Enable the board
+   */
   outb(FASTMODE, SEQCTL(base));
 
-  if ((p->type == AIC_274x || p->type == AIC_284x))
+  if ((p->type == AIC_274x) || (p->type == AIC_284x))
   {
     outb(ENABLE, BCTL(base));
   }
@@ -3046,18 +3698,20 @@
      */
     outb(config.scsi_id_b, SCSIID(base));
     scsi_conf = inb(HA_SCSICONF(base) + 1) & (ENSPCHK | STIMESEL);
-    scsi_conf = scsi_conf | ENSTIMER | ACTNEGEN | STPWEN;
-    outb(scsi_conf, SXFRCTL1(base));
+    outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base));
     outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
-    /* Select Channel A */
-    outb(0, SBLKCTL(base));
+    /*
+     * Select Channel A
+     */
+    outb(SELSINGLE, SBLKCTL(base));
   }
   outb(config.scsi_id, SCSIID(base));
   scsi_conf = inb(HA_SCSICONF(base)) & (ENSPCHK | STIMESEL);
   outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base));
   outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
 
-  /* Look at the information that board initialization or the board
+  /*
+   * Look at the information that board initialization or the board
    * BIOS has left us. In the lower four bits of each target's
    * scratch space any value other than 0 indicates that we should
    * initiate synchronous transfers. If it's zero, the user or the
@@ -3076,19 +3730,43 @@
   {
     max_targets = 16;
   }
+  /*
+   * Grab the disconnection disable table and invert it for our needs
+   */
+  if (have_seeprom)
+  {
+    p->discenable = 0;
+  }
+  else
+  {
+    if (bios_disabled)
+    {
+      printk("aic7xxx : Host Adapter Bios disabled.  Using default SCSI "
+             "device parameters\n");
+      p->discenable = 0xFFFF;
+    }
+    else
+    {
+      p->discenable = ~(inw(HA_DISC_DSB(base)));
+    }
+  }
 
   for (i = 0; i < max_targets; i++)
   {
     if (have_seeprom)
     {
-      target_settings = (sc.device_flags[i] & CFXFER) << 4;
+      target_settings = ((sc.device_flags[i] & CFXFER) << 4);
       if (sc.device_flags[i] & CFSYNCH)
       {
-	p->needsdtr_copy = p->needsdtr_copy | (0x01 << i);
+	p->needsdtr_copy |= (0x01 << i);
       }
       if ((sc.device_flags[i] & CFWIDEB) && (p->bus_type == AIC_WIDE))
       {
-	p->needwdtr_copy = p->needwdtr_copy | (0x01 << i);
+	p->needwdtr_copy |= (0x01 << i);
+      }
+      if (sc.device_flags[i] & CFDISC)
+      {
+        p->discenable |= (0x01 << i);
       }
     }
     else
@@ -3096,11 +3774,11 @@
       target_settings = inb(HA_TARG_SCRATCH(base) + i);
       if (target_settings & 0x0F)
       {
-	p->needsdtr_copy = p->needsdtr_copy | (0x01 << i);
+	p->needsdtr_copy |= (0x01 << i);
 	/*
 	 * Default to asynchronous transfers (0 offset)
 	 */
-	target_settings = target_settings & 0xF0;
+	target_settings &= 0xF0;
       }
       /*
        * If we are not wide, forget WDTR. This makes the driver
@@ -3109,8 +3787,8 @@
        */
       if ((target_settings & 0x80) && (p->bus_type == AIC_WIDE))
       {
-	p->needwdtr_copy = p->needwdtr_copy | (0x01 << i);
-	target_settings = target_settings & 0x7F;
+	p->needwdtr_copy |= (0x01 << i);
+	target_settings &= 0x7F;
       }
     }
     outb(target_settings, (HA_TARG_SCRATCH(base) + i));
@@ -3121,7 +3799,7 @@
 #if 0
   printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
   printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
-#endif 0
+#endif
 
   /*
    * Clear the control byte for every SCB so that the sequencer
@@ -3145,9 +3823,11 @@
   outb(0, HA_ACTIVE0(base));
   outb(0, HA_ACTIVE1(base));
 
-  /* We don't have any waiting selections */
-  outb (SCB_LIST_NULL, WAITING_SCBH(base));
-  outb (SCB_LIST_NULL, WAITING_SCBT(base));
+  /*
+   * We don't have any waiting selections
+   */
+  outb(SCB_LIST_NULL, WAITING_SCBH(base));
+  outb(SCB_LIST_NULL, WAITING_SCBT(base));
 
   /*
    * Reset the SCSI bus. Is this necessary?
@@ -3170,14 +3850,14 @@
       /*
        * Select channel B.
        */
-      outb(2, SBLKCTL(base));
+      outb(SELBUSB, SBLKCTL(base));
       outb(SCSIRSTO, SCSISEQ(base));
       udelay(1000);
       outb(0, SCSISEQ(base));
       /*
        * Select channel A.
        */
-      outb(0, SBLKCTL(base));
+      outb(SELSINGLE, SBLKCTL(base));
     }
 
     outb(SCSIRSTO, SCSISEQ(base));
@@ -3390,7 +4070,7 @@
 	 */
 	aic7xxx_spurious_count = 0;
 
-	index += 1;
+	index++;
       }
     }
   }
@@ -3414,8 +4094,8 @@
 		 struct aic7xxx_scb *scb)
 {
   void *addr;
-  unsigned length;
   unsigned short mask;
+  struct scatterlist *sg;
 
   /*
    * Setup the control byte if we need negotiation and have not
@@ -3432,15 +4112,19 @@
       cmd->device->current_tag = 1;  /* enable tagging */
     }
     cmd->tag = cmd->device->current_tag;
-    cmd->device->current_tag = cmd->device->current_tag + 1;
-    scb->control = scb->control | SCB_TE;
+    cmd->device->current_tag++;
+    scb->control |= SCB_TE;
   }
 #endif
-  mask = (0x01 << cmd->target);
+  mask = (0x01 << (cmd->target | (cmd->channel << 3)));
+  if (p->discenable & mask)
+  {
+    scb->control |= SCB_DISCENB;
+  }
   if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
   {
-    p->wdtr_pending = p->wdtr_pending | mask;
-    scb->control = scb->control | SCB_NEEDWDTR;
+    p->wdtr_pending |= mask;
+    scb->control |= SCB_NEEDWDTR;
 #if 0
     printk("Sending WDTR request to target %d.\n", cmd->target);
 #endif
@@ -3449,8 +4133,8 @@
   {
     if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
     {
-      p->sdtr_pending = p->sdtr_pending | mask;
-      scb->control = scb->control | SCB_NEEDSDTR;
+      p->sdtr_pending |= mask;
+      scb->control |= SCB_NEEDSDTR;
 #if 0
       printk("Sending SDTR request to target %d.\n", cmd->target);
 #endif
@@ -3469,17 +4153,7 @@
    * changes depending on whether or not use_sg is zero; a
    * non-zero use_sg indicates the number of elements in the
    * scatter-gather array.
-   *
-   * The AIC-7770 can't support transfers of any sort larger
-   * than 2^24 (three-byte count) without backflips. For what
-   * the kernel is doing, this shouldn't occur. I hope.
    */
-  length = aic7xxx_length(cmd, 0);
-
-  if (length > 0xFFFFFF)
-  {
-    panic("aic7xxx_buildscb: can't transfer > 2^24 - 1 bytes\n");
-  }
 
   /*
    * XXX - this relies on the host data being stored in a
@@ -3493,29 +4167,47 @@
   {
 #if 0
     debug("aic7xxx_buildscb: SG used, %d segments, length %u\n",
-	  cmd->use_sg, length);
+           cmd->use_sg, length);
 #endif
     scb->SG_segment_count = cmd->use_sg;
     memcpy(scb->SG_list_pointer, &cmd->request_buffer,
 	   sizeof(scb->SG_list_pointer));
+    memcpy(&sg, &cmd->request_buffer, sizeof(sg));
+    memcpy(scb->data_pointer, &(sg[0].address), sizeof(scb->data_pointer));
+    scb->data_count[0] = sg[0].length & 0xFF;
+    scb->data_count[1] = (sg[0].length >> 8) & 0xFF;
+    scb->data_count[2] = (sg[0].length >> 16) & 0xFF;
   }
   else
   {
 #if 0
-    debug("aic7xxx_buildscb: Creating scatterlist, addr=0x%lx, length=%d.\n",
-	  (unsigned long) cmd->request_buffer, cmd->request_bufflen);
-#endif
-#ifdef AIC7XXX_USE_SG
-    scb->SG_segment_count = 1;
-    scb->sg.address = (char *) cmd->request_buffer;
-    scb->sg.length = cmd->request_bufflen;
-    addr = &scb->sg;
-    memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-#else
-    scb->SG_segment_count = 0;
-    memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer));
-    memcpy(scb->data_count, &cmd->request_bufflen, 3);
+  debug("aic7xxx_buildscb: Creating scatterlist, addr=0x%lx, length=%d.\n",
+	(unsigned long) cmd->request_buffer, cmd->request_bufflen);
 #endif
+    if (cmd->request_bufflen == 0)
+    {
+      /*
+       * In case the higher level SCSI code ever tries to send a zero
+       * length command, ensure the SCB indicates no data.  The driver
+       * will interpret a zero length command as a Bus Device Reset.
+       */
+      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));
+      memset(scb->data_count, 0, sizeof(scb->data_count));
+    }
+    else
+    {
+      scb->SG_segment_count = 1;
+      scb->sg.address = (char *) cmd->request_buffer;
+      scb->sg.length = cmd->request_bufflen;
+      addr = &scb->sg;
+      memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
+      scb->data_count[0] = scb->sg.length & 0xFF;
+      scb->data_count[1] = (scb->sg.length >> 8) & 0xFF;
+      scb->data_count[2] = (scb->sg.length >> 16) & 0xFF;
+      memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer));
+    }
   }
 }
 
@@ -3530,17 +4222,16 @@
 aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
 {
   long flags;
-#ifndef AIC7XXX_USE_DMA
-  int old_scbptr;
-#endif
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
   unsigned char curscb;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
 
-  /* Check to see if channel was scanned. */
- if (!p->a_scanned && (cmd->channel == 0))
+  /*
+   * Check to see if channel was scanned.
+   */
+  if (!p->a_scanned && (cmd->channel == 0))
   {
     printk("aic7xxx: Scanning channel A for devices.\n");
     p->a_scanned = 1;
@@ -3615,13 +4306,11 @@
       scb->state = SCB_ACTIVE;
       scb->next_waiting = SCB_LIST_NULL;
       memcpy(scb->host_scb, &scb, sizeof(scb));
-#ifdef AIC7XXX_USE_DMA
       scb->control = SCB_NEEDDMA;
-#endif
       PAUSE_SEQUENCER(p);
       curscb = inb(SCBPTR(p->base));
       outb(scb->position, SCBPTR(p->base));
-      aic7xxx_putdmascb(p->base, scb);
+      aic7xxx_putscb_dma(p->base, scb);
       outb(curscb, SCBPTR(p->base));
       UNPAUSE_SEQUENCER(p);
       scb->control = 0;
@@ -3630,6 +4319,9 @@
 
   scb->cmd = cmd;
   aic7xxx_position(cmd) = scb->position;
+#if 0
+  debug_scb(scb);
+#endif;
 
   /*
    * Construct the SCB beforehand, so the sequencer is
@@ -3660,17 +4352,8 @@
    * the SCB, then write its pointer into the queue in FIFO
    * and restore the saved SCB pointer.
    */
-#ifdef AIC7XXX_USE_DMA
-  aic7xxx_putscb(p->base, scb);
-#else
-  old_scbptr = inb(SCBPTR(p->base));
-  outb(scb->position, SCBPTR(p->base));
-
   aic7xxx_putscb(p->base, scb);
 
-  outb(scb->position, QINFIFO(p->base));
-  outb(old_scbptr, SCBPTR(p->base));
-#endif
   /*
    * Make sure the Scsi_Cmnd pointer is saved, the struct it
    * points to is set up properly, and the parity error flag
@@ -3680,205 +4363,149 @@
   cmd->scsi_done = fn;
   aic7xxx_error(cmd) = DID_OK;
   aic7xxx_status(cmd) = 0;
-
+  scb->timer_status = 0x0;
   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",
+         (long) cmd, (long) scb->cmd, scb->position);
+#endif;
   restore_flags(flags);
   return (0);
 }
 
-/* return values from aic7xxx_kill */
-typedef enum {
-  k_ok,             /* scb found and message sent */
-  k_busy,           /* message already present */
-  k_absent,         /* couldn't locate scb */
-  k_disconnect,     /* scb found, but disconnected */
-} k_state;
-
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_kill
+ *   aic7xxx_abort_scb
  *
  * Description:
- *   This must be called with interrupts disabled - it's going to
- *   be messing around with the host data, and an interrupt being
- *   fielded in the middle could get ugly.
- *
- *   Since so much of the abort and reset code is shared, this
- *   function performs more magic than it really should. If the
- *   command completes ok, then it will call scsi_done with the
- *   result code passed in. The unpause parameter controls whether
- *   or not the sequencer gets unpaused - the reset function, for
- *   instance, may want to do something more aggressive.
- *
- *   Note that the command is checked for in our SCB_array first
- *   before the sequencer is paused, so if k_absent is returned,
- *   then the sequencer is NOT paused.
+ *   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.
  *-F*************************************************************************/
-static k_state
-aic7xxx_kill(Scsi_Cmnd *cmd, unsigned char message,
-	     unsigned int result, int unpause)
+static void
+aic7xxx_abort_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_host *p;
-  struct aic7xxx_scb *scb;
-  int i, active_scb, found, queued;
-  unsigned char scbsave[AIC7XXX_MAXSCB];
-  unsigned char flags;
-  int scb_control;
-  k_state status;
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = &p->scb_array[aic7xxx_position(cmd)];
-
-#if 0
-  printk("aic7xxx_kill: In the kill function...\n");
-#endif
-  PAUSE_SEQUENCER(p);
+  int base = p->base;
+  int found = 0;
+  char channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
 
   /*
-   * Case 1: In the QINFIFO
-   *
-   * This is the best case, really. Check to see if the
-   * command is still in the sequencer's input queue. If
-   * so, simply remove it. Reload the queue afterward.
+   * Ensure that the card doesn't do anything
+   * behind our back.
    */
-  queued = inb(QINCNT(p->base));
-
-  for (i = found = 0; i < (queued - found); i++)
-  {
-    scbsave[i] = inb(QINFIFO(p->base));
-
-    if (scbsave[i] == scb->position)
-    {
-      found = 1;
-      i--;
-    }
-  }
-
-  for (queued = 0; queued < i; queued++)
-  {
-    outb(scbsave[queued], QINFIFO(p->base));
-  }
-
-  if (found)
-  {
-    status = k_ok;
-    goto complete;
-  }
+  PAUSE_SEQUENCER(p);
 
-  active_scb = inb(SCBPTR(p->base));
   /*
-   * Case 2: Not the active command
-   *
-   * Check the current SCB bank. If it's not the one belonging
-   * to the command we want to kill, select the scb we want to
-   * abort and turn off the disconnected bit. The driver will
-   * then abort the command and notify us of the abort.
+   * 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 (active_scb != scb->position)
+  if (scb->state & SCB_ABORTED)
   {
-    outb(scb->position, SCBPTR(p->base));
-    scb_control = inb(SCBARRAY(p->base));
-    scb_control = scb_control & ~SCB_DIS;
-    outb(scb_control, SCBARRAY(p->base));
-    outb(active_scb, SCBPTR(p->base));
-    status = k_disconnect;
-    goto complete;
+    /*
+     * Been down this road before.  Do a full bus reset.
+     */
+    found = aic7xxx_reset_channel(p, channel, scb->position);
   }
-
-  scb_control = inb(SCBARRAY(p->base));
-  if (scb_control & SCB_DIS)
+  else
   {
-    scb_control = scb_control & ~SCB_DIS;
-    outb(scb_control, SCBARRAY(p->base));
-    status = k_disconnect;
-    goto complete;
-  }
+    unsigned char active_scb, control;
+    struct aic7xxx_scb *active_scbp;
 
-  /*
-   * Presumably at this point our target command is active. Check
-   * to see if there's a message already in effect. If not, place
-   * our message in and assert ATN so the target goes into MESSAGE
-   * OUT phase.
-   */
-  flags = inb(HA_FLAGS(p->base));
-  if (flags & ACTIVE_MSG)
-  {
     /*
-     * If there is a message in progress, reset the bus
-     * and have all devices renegotiate.
+     * 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 (cmd->channel & 0x01)
+    active_scb = inb(SCBPTR(base));
+    active_scbp = &(p->scb_array[active_scb]);
+    control = inb(SCBARRAY(base));
+
+    /*
+     * Test to see if scbp is disconnected
+     */
+    outb(scb->position, SCBPTR(base));
+    if (inb(SCBARRAY(base)) & SCB_DIS)
     {
-      p->needsdtr = p->needsdtr_copy & 0xFF00;
-      p->sdtr_pending = p->sdtr_pending & 0x00FF;
-      outb(0, HA_ACTIVE1(p->base));
+      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));
+      memset(scb->data_count, 0, sizeof(scb->data_count));
+      outb(SCBAUTO, SCBCNT(base));
+      asm volatile("cld\n\t"
+                   "rep\n\t"
+                   "outsb"
+                   : /* no output */
+                   :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
+                   :"si", "cx", "dx");
+      outb(0, SCBCNT(base));
+      aic7xxx_add_waiting_scb(base, scb, LIST_SECOND);
+      aic7xxx_scb_tsleep(p, scb, 2 * HZ);  /* unpauses the sequencer */
     }
     else
     {
-      if (p->bus_type == AIC_WIDE)
+      /*
+       * Is the active SCB really active?
+       */
+      if ((active_scbp->state & SCB_ACTIVE) && (control & SCB_NEEDDMA))
       {
-	p->needsdtr = p->needsdtr_copy;
-	p->needwdtr = p->needwdtr_copy;
-	p->sdtr_pending = 0;
-	p->wdtr_pending = 0;
-	outb(0, HA_ACTIVE0(p->base));
-	outb(0, HA_ACTIVE1(p->base));
+        unsigned char flags = inb(HA_FLAGS(base));
+        if (flags & ACTIVE_MSG)
+        {
+          /*
+           * 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
+        {
+          /*
+           * Load the message buffer and assert attention.
+           */
+          active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
+          outb(flags | ACTIVE_MSG, HA_FLAGS(base));
+          outb(1, HA_MSG_LEN(base));
+          outb(MSG_BUS_DEVICE_RESET, HA_MSG_START(base));
+          if (active_scbp->target_channel_lun != scb->target_channel_lun)
+          {
+            /*
+             * XXX - We would like to increment the timeout on scb, but
+             *       access to that routine is denied because it is hidden
+             *       in scsi.c.  If we were able to do this, it would give
+             *       scb a new lease on life.
+             */
+            ;
+          }
+          aic7xxx_scb_tsleep(p, active_scbp, 2 * HZ);
+        }
       }
       else
       {
-	p->needsdtr = p->needsdtr_copy & 0x00FF;
-	p->sdtr_pending = p->sdtr_pending & 0xFF00;
-	outb(0, HA_ACTIVE0(p->base));
+        /*
+         * No active command to single out, so reset
+         * the bus for the timed out target.
+         */
+        aic7xxx_reset_channel(p, channel, scb->position);
       }
     }
-    /* Reset the bus. */
-    outb(SCSIRSTO, SCSISEQ(p->base));
-    udelay(1000);
-    outb(0, SCSISEQ(p->base));
-    aic7xxx_delay(AIC7XXX_RESET_DELAY);
-
-    status = k_busy;
-    goto complete;
-  }
-
-  outb(flags | ACTIVE_MSG, HA_FLAGS(p->base));    /* active message */
-  outb(1, HA_MSG_LEN(p->base));                   /* length = 1 */
-  outb(message, HA_MSG_START(p->base));           /* message body */
-
-  /*
-   * Assert ATN. Use the value of SCSISIGO saved by the
-   * sequencer code so we don't alter its contents radically
-   * in the middle of something critical.
-   */
-  outb(inb(HA_SIGSTATE(p->base)) | 0x10, SCSISIGO(p->base));
-
-  status = k_ok;
-
-  /*
-   * The command has been killed. Do the bookkeeping, unpause
-   * the sequencer, and notify the higher-level SCSI code.
-   */
-complete:
-  if (unpause)
-  {
-    UNPAUSE_SEQUENCER(p);
   }
-
-  /*
-   * Mark the scb as free and clear the scbs command pointer.
-   * Add the scb to the head of the free list being careful
-   * to preserve the next pointers.
-   */
-  scb->state = SCB_FREE;          /* mark the scb as free */
-  scb->cmd = NULL;                /* clear the command pointer */
-  scb->next = p->free_scb;        /* preserve next pointer */
-  p->free_scb = scb;              /* add at head of free list */
-  cmd->result = cmd->result << 16;
-  cmd->scsi_done(cmd);
-  return (status);
 }
 
 /*+F*************************************************************************
@@ -3891,23 +4518,20 @@
 int
 aic7xxx_abort(Scsi_Cmnd *cmd)
 {
-  int rv;
+  struct aic7xxx_scb  *scb;
+  struct aic7xxx_host *p;
   long flags;
 
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  scb = &(p->scb_array[aic7xxx_position(cmd)]);
+
   save_flags(flags);
   cli();
 
-  switch (aic7xxx_kill(cmd, ABORT, DID_ABORT, !0))
-  {
-    case k_ok:          rv = SCSI_ABORT_SUCCESS;        break;
-    case k_busy:        rv = SCSI_ABORT_BUSY;           break;
-    case k_absent:      rv = SCSI_ABORT_NOT_RUNNING;    break;
-    case k_disconnect:  rv = SCSI_ABORT_SNOOZE;         break;
-    default:            panic("aic7xxx_abort: internal error\n");
-  }
+  aic7xxx_abort_scb(p, scb);
 
   restore_flags(flags);
-  return (rv);
+  return (0);
 }
 
 /*+F*************************************************************************
@@ -3923,76 +4547,7 @@
 int
 aic7xxx_reset(Scsi_Cmnd *cmd)
 {
-  long flags;
-  struct aic7xxx_host *p;
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  save_flags(flags);
-  cli();
-
-  switch (aic7xxx_kill(cmd, BUS_DEVICE_RESET, DID_RESET, 0))
-  {
-    case k_ok:
-      /*
-       * The RESET message was sent to the target
-       * with no problems. Flag that target as
-       * needing a SDTR negotiation on the next
-       * connection and restart the sequencer.
-       */
-      p->needsdtr = p->needsdtr & (1 << cmd->target);
-      UNPAUSE_SEQUENCER(p);
-      break;
-
-    case k_absent:
-      /*
-       * The sequencer will not be paused if aic7xxx_kill()
-       * couldn't find the command.
-       */
-      PAUSE_SEQUENCER(p);
-      /* falls through */
-
-    case k_busy:
-      cmd->result = DID_RESET << 16;  /* return reset code */
-      cmd->scsi_done(cmd);
-      break;
-
-    case k_disconnect:
-      /*
-       * Do a hard reset of the SCSI bus. According to the
-       * SCSI-2 draft specification, reset has to be asserted
-       * for at least 25us. I'm invoking the kernel delay
-       * function for 30us since I'm not totally trusting of
-       * the busy loop timing.
-       *
-       * XXX - I'm not convinced this works. I tried resetting
-       *       the bus before, trying to get the devices on the
-       *       bus to revert to asynchronous transfer, and it
-       *       never seemed to work.
-       */
-      debug("aic7xxx: attempting to reset scsi bus and card\n");
-
-      outb(SCSIRSTO, SCSISEQ(p->base));
-      udelay(1000);
-      outb(0, SCSISEQ(p->base));
-      aic7xxx_delay(AIC7XXX_RESET_DELAY);
-
-      UNPAUSE_SEQUENCER(p);
-
-      /*
-       * Locate the command and return a "reset" status
-       * for it. This is not completely correct and will
-       * probably return to haunt me later.
-       */
-      cmd->result = DID_RESET << 16;  /* return reset code */
-      cmd->scsi_done(cmd);
-      break;
-
-    default:
-      panic("aic7xxx_reset: internal error\n");
-  }
-
-  restore_flags(flags);
-  return (SCSI_RESET_SUCCESS);
+  return (aic7xxx_abort(cmd));
 }
 
 /*+F*************************************************************************
@@ -4032,6 +4587,8 @@
 
   return (0);
 }
+
+#include "aic7xxx_proc.c"
 
 #ifdef MODULE
 /* Eventually this will go into an include file, but this will be later */

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