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

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

diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
@@ -1,5 +1,3 @@
-#define EXPERIMENTAL_FLAGS 0
-
 /*+M*************************************************************************
  * Adaptec AIC7xxx device driver for Linux.
  *
@@ -29,21 +27,73 @@
  * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
  * ANSI SCSI-2 specification (draft 10c), ...
  *
- * ----------------------------------------------------------------
- *  Modified to include support for wide and twin bus adapters,
- *  DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * --------------------------------------------------------------------------
+ *
+ *  Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ *  Substantially modified to include support for wide and twin bus
+ *  adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
  *  SCB paging, and other rework of the code.
  *
- *  Parts of this driver are based on the FreeBSD driver by Justin
- *  T. Gibbs.
+ *  Parts of this driver were also based on the FreeBSD driver by
+ *  Justin T. Gibbs.  His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ *---------------------------------------------------------------------------
+ *
+ *  Thanks also go to (in alphabetical order) the following:
+ *
+ *    Rory Bolt     - Sequencer bug fixes
+ *    Jay Estabrook - Initial DEC Alpha support
+ *    Doug Ledford  - Much needed abort/reset bug fixes
+ *    Kai Makisara  - DMAing of SCBs
  *
  *  A Boot time option was also added for not resetting the scsi bus.
  *
- *    Form:  aic7xxx=extended,no_reset
+ *    Form:  aic7xxx=extended
+ *           aic7xxx=no_reset
+ *           aic7xxx=ultra
+ *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
+ *           aic7xxx=verbose
  *
- *    -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
  *
- *  $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
  *-M*************************************************************************/
 
 #ifdef MODULE
@@ -67,7 +117,11 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "aic7xxx.h"
+
+#include "aic7xxx/sequencer.h"
+#include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include "aic7xxx_seq.h"
 #include <linux/stat.h>
 #include <linux/malloc.h>	/* for kmalloc() */
 
@@ -81,14 +135,18 @@
 
 struct proc_dir_entry proc_scsi_aic7xxx = {
     PROC_SCSI_AIC7XXX, 7, "aic7xxx",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+    S_IFDIR | S_IRUGO | S_IXUGO, 2,
+    0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 4.0 $"
+#define AIC7XXX_C_VERSION  "$Revision: 4.1 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b)        ((a < b) ? a : b)
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
 #define ALL_TARGETS -1
+#define ALL_CHANNELS '\0'
+#define ALL_LUNS -1
 #ifndef TRUE
 #  define TRUE 1
 #endif
@@ -107,11 +165,8 @@
  *     support because all PCI dependent code is bracketed with
  *     "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
  *
- *   o Twin bus support - this has been tested and does work.
- *
- *   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 Twin bus support - this has been tested and does work.  It is
+ *     not an option anymore.
  *
  *   o Tagged queueing - this driver is capable of tagged queueing
  *     but I am unsure as to how well the higher level driver implements
@@ -140,16 +195,16 @@
  *     LUN using its own heuristic based on the number of available
  *     SCBs.
  *
- *   o 3985 support - The 3985 adapter is much like the 3940, but
- *     has three 7870 controllers as opposed to two for the 3940.
- *     It will get probed and recognized as three different adapters,
- *     but all three controllers can share the same external bank of
- *     255 SCBs.  If you enable AIC7XXX_SHARE_SCBS, then the driver
- *     will attempt to share the common bank of SCBs between the three
- *     controllers of the 3985.  This is experimental and hasn't
- *     been tested.  By default, we do not share the bank of SCBs,
- *     and force the controllers to use their own internal bank of
- *     16 SCBs.  Please let us know if sharing the SCB array works.
+ *   o 3985 support - The 3985 adapter is much like the 3940, but has
+ *     three 7870 controllers as opposed to two for the 3940.  It will
+ *     be probed and recognized as three different adapters, but all
+ *     three controllers can share the same external bank of 255 SCBs.
+ *     If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt
+ *     to use and share the common bank of SCBs between the three
+ *     controllers of the 3985.  This is experimental and hasn't been
+ *     been tested.  By default, we do not use external SCB RAM, and
+ *     force the controllers to use their own internal bank of 16 SCBs.
+ *     Please let us know if using the external SCB array works.
  *
  *   o SCB paging support - SCB paging is enabled by defining
  *     AIC7XXX_PAGE_ENABLE.  Support for this was taken from the
@@ -162,43 +217,54 @@
  *  Note that sharing of IRQs is not an option any longer.  Linux supports
  *  it so we support it.
  *
- *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96
  */
 
-/* Uncomment this for testing twin bus support. */
-#define AIC7XXX_TWIN_SUPPORT
-
 /* Uncomment this for tagged queueing. */
-/* #define AIC7XXX_TAGGED_QUEUEING */
+#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
+#define AIC7XXX_TAGGED_QUEUEING
+#endif
 
 /*
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-/* #define AIC7XXX_CMDS_PER_LUN 8 */
+#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
+#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
 #define AIC7XXX_RESET_DELAY 15
+#endif
 
 /*
- * Uncomment the following define for collection of SCSI transfer statistics
- * for the /proc filesystem.
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
  *
  * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
  * NOTE: This does affect performance since it has to maintain statistics.
  */
-/* #define AIC7XXX_PROC_STATS */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
 
 /*
- * Uncomment the following to enable SCB paging.
+ * Enable SCB paging.
  */
-/* #define AIC7XXX_PAGE_ENABLE */
+#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
+#define AIC7XXX_PAGE_ENABLE
+#endif
 
 /*
- * Uncomment the following to enable sharing of the external bank
- * of 255 SCBs for the 3985.
+ * Uncomment the following to enable use of the external bank
+ * of 255 SCBs.  For 3985 adapters, this will also enable sharing
+ * of the SCB array across all three controllers.
  */
-#define AIC7XXX_SHARE_SCBS
+#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
+#define AIC7XXX_USE_EXT_SCBRAM
+#endif
 
 /*
  * For debugging the abort/reset code.
@@ -211,6 +277,87 @@
 #define AIC7XXX_DEBUG
 
 /*
+ * Set this for defining the number of tagged commands on a device
+ * by device, and controller by controller basis.  The first set
+ * of tagged commands will be used for the first detected aic7xxx
+ * controller, the second set will be used for the second detected
+ * aic7xxx controller, and so on.  These values will *only* be used
+ * for targets that are tagged queueing capable; these values will
+ * be ignored in all other cases.  The tag_commands is an array of
+ * 16 to allow for wide and twin adapters.  Twin adapters will use
+ * indexes 0-7 for channel 0, and indexes 8-15 for channel 1.
+ *
+ * *** Determining commands per LUN ***
+ * 
+ * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN.  If SCB paging is
+ * enabled, the commands/LUN is 8.  When SCB paging is not enabled,
+ * then commands/LUN is 8 for adapters with 16 or more hardware SCBs
+ * and 4 commands/LUN for adapters with 3 or 4 SCBs.
+ *
+ */
+/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
+
+#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+typedef struct
+{
+  char tag_commands[16];   /* Allow for wide/twin channel adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver to use it's own algorithm
+ * for determining commands/LUN (see Determining commands per LUN
+ * above).
+ */
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+/*
+ * Modify this as you see fit for your system.  By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above).  When -1, the driver will
+ * not enable tagged queueing for that particular device.  When positive
+ * (> 0) the values in the array are used for the queue_depth.  Note
+ * that the maximum value for an entry is 127.
+ *
+ * In this example, the first line will enable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter and tells the driver
+ * to use it's own algorithm for determining commands/LUN.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}},
+  {DEFAULT_TAG_COMMANDS},
+  {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+#endif
+
+/*
+ * Don't define this unless you have problems with the driver
+ * interrupt handler.  The old method would register the drivers
+ * interrupt handler as a "fast" type interrupt handler that would
+ * lock out other interrupts.  Since this driver can spend a lot
+ * of time in the interrupt handler, this is _not_ a good idea.
+ * It also conflicts with some of the more common ethernet drivers
+ * that don't use fast interrupts.  Currently, Linux does not allow
+ * IRQ sharing unless both drivers can agree on the type of interrupt
+ * handler.
+ */
+/* #define AIC7XXX_OLD_ISR_TYPE */
+
+
+/*
  * Controller type and options
  */
 typedef enum {
@@ -232,14 +379,15 @@
   AIC_7882,	/* PCI  aic7882 on 3940 Ultra */
   AIC_7883,	/* PCI  aic7883 on 3985 Ultra */
   AIC_7884	/* PCI  aic7884 on 294x Ultra Differential */
-} aha_type;
+} aha_chip_type;
 
 typedef enum {
   AIC_777x,	/* AIC-7770 based */
-  AIC_785x,	/* AIC-7850 based */
+  AIC_785x,	/* AIC-7850 based (3 SCBs)*/
+  AIC_786x,	/* AIC-7860 based (7850 ultra) */
   AIC_787x,	/* AIC-7870 based */
-  AIC_788x	/* AIC-7880 based */
-} aha_chip_type;
+  AIC_788x	/* AIC-7880 based (ultra) */
+} aha_chip_class_type;
 
 typedef enum {
   AIC_SINGLE,  /* Single Channel */
@@ -269,24 +417,24 @@
  * Don't forget to change this when changing the types!
  */
 static const char *board_names[] = {
-  "<AIC-7xxx Unknown>",		/* AIC_NONE */
-  "AIC-7770",			/* AIC_7770 */
-  "AHA-2740",			/* AIC_7771 */
-  "AHA-2840",			/* AIC_284x */
-  "AIC-7850",			/* AIC_7850 */
-  "AIC-7855",			/* AIC_7855 */
-  "AIC-7850 Ultra",		/* AIC_7860 */
-  "AHA-2940A Ultra",		/* AIC_7861 */
-  "AIC-7870",			/* AIC_7870 */
-  "AHA-2940",			/* AIC_7871 */
-  "AHA-3940",			/* AIC_7872 */
-  "AHA-3985",			/* AIC_7873 */
-  "AHA-2940 Differential",	/* AIC_7874 */
-  "AIC-7880 Ultra",		/* AIC_7880 */
-  "AHA-2940 Ultra",		/* AIC_7881 */
-  "AHA-3940 Ultra",		/* AIC_7882 */
-  "AHA-3985 Ultra",		/* AIC_7883 */
-  "AHA-2940 Ultra Differential"	/* AIC_7884 */
+  "AIC-7xxx Unknown",		                        /* AIC_NONE */
+  "Adaptec AIC-7770 SCSI host adapter",			/* AIC_7770 */
+  "Adaptec AHA-274X SCSI host adapter",			/* AIC_7771 */
+  "Adaptec AHA-284X SCSI host adapter",			/* AIC_284x */
+  "Adaptec AIC-7850 SCSI host adapter",			/* AIC_7850 */
+  "Adaptec AIC-7855 SCSI host adapter",			/* AIC_7855 */
+  "Adaptec AIC-7860 Ultra SCSI host adapter",		/* AIC_7860 */
+  "Adaptec AHA-2940A Ultra SCSI host adapter",		/* AIC_7861 */
+  "Adaptec AIC-7870 SCSI host adapter",			/* AIC_7870 */
+  "Adaptec AHA-294X SCSI host adapter",			/* AIC_7871 */
+  "Adaptec AHA-394X SCSI host adapter",			/* AIC_7872 */
+  "Adaptec AHA-398X SCSI host adapter",			/* AIC_7873 */
+  "Adaptec AHA-2944 SCSI host adapter",	                /* AIC_7874 */
+  "Adaptec AIC-7880 Ultra SCSI host adapter",		/* AIC_7880 */
+  "Adaptec AHA-294X Ultra SCSI host adapter",		/* AIC_7881 */
+  "Adaptec AHA-394X Ultra SCSI host adapter",		/* AIC_7882 */
+  "Adaptec AHA-398X Ultra SCSI host adapter",		/* AIC_7883 */
+  "Adaptec AHA-2944 Ultra SCSI host adapter"	        /* AIC_7884 */
 };
 
 /*
@@ -310,12 +458,17 @@
  */
 #define DID_RETRY_COMMAND DID_ERROR
 
+#define HSCSIID        0x07
+#define HWSCSIID       0x0F
+#define SCSI_RESET     0x040
+
 /*
  * EISA/VL-bus stuff
  */
 #define MINSLOT		1
 #define MAXSLOT		15
 #define SLOTBASE(x)	((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
 
 /*
  * Standard EISA Host ID regs  (Offset from slot base)
@@ -334,14 +487,6 @@
 #define INTDEF		0x5C		/* Interrupt Definition Register */
 
 /*
- * Some defines for the HCNTRL register.
- */
-#define	REQ_PAUSE	IRQMS | INTEN | PAUSE
-#define	UNPAUSE_274X	IRQMS | INTEN
-#define	UNPAUSE_284X	INTEN
-#define	UNPAUSE_294X	IRQMS | INTEN
-
-/*
  * AIC-78X0 PCI registers
  */
 #define	CLASS_PROGIF_REVID	0x08
@@ -377,7 +522,7 @@
  * each word, while the C56 and C66 (4096 bits) use 8 bits to
  * address each word.
  */
-typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type;
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
 
 /*
  *
@@ -417,15 +562,15 @@
 /*
  * Host Adapter Control Bits
  */
-/* UNUSED               0x0001 */
+#define CFAUTOTERM      0x0001          /* Perform Auto termination */
 #define CFULTRAEN       0x0002          /* Ultra SCSI speed enable (Ultra cards) */
 #define CF284XSELTO     0x0003          /* Selection timeout (284x cards) */
 #define CF284XFIFO      0x000C          /* FIFO Threshold (284x cards) */
-#define CFSTERM         0x0004          /* SCSI low byte termination (non-wide cards) */
+#define CFSTERM         0x0004          /* SCSI low byte termination */
 #define CFWSTERM        0x0008          /* SCSI high byte termination (wide card) */
 #define CFSPARITY	0x0010		/* SCSI parity */
 #define CF284XSTERM	0x0020		/* SCSI low byte termination (284x cards) */
-#define CFRESETB	0x0040		/* reset SCSI bus at IC initialization */
+#define CFRESETB	0x0040		/* reset SCSI bus at boot */
 /* UNUSED		0xFF80 */
   unsigned short adapter_control;	/* word 17 */
 
@@ -448,35 +593,17 @@
   unsigned short checksum;		/* word 31 */
 };
 
-
-#define SCSI_RESET 0x040
-
-/*
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
- */
-#define PAUSE_SEQUENCER(p) \
-  outb(p->pause, HCNTRL + p->base);			\
-  while ((inb(HCNTRL + p->base) & PAUSE) == 0)		\
-    ;							\
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
-  outb(p->unpause, HCNTRL + p->base)
-
-/*
- * Restart the sequencer program from address zero
- */
-#define RESTART_SEQUENCER(p) \
-  do {							\
-    outb(SEQRESET | FASTMODE, SEQCTL + p->base);	\
-  } while (inb(SEQADDR0 + p->base) != 0 &&		\
-	   inb(SEQADDR1 + p->base) != 0);		\
-  UNPAUSE_SEQUENCER(p);
+#define SELBUS_MASK		0x0a
+#define 	SELNARROW	0x00
+#define 	SELBUSB		0x08
+#define SINGLE_BUS		0x00
+
+#define SCB_TARGET(scb)         \
+       (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb)            \
+       ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb)   \
+       (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
 
 /*
  * If an error occurs during a data transfer phase, run the command
@@ -540,12 +667,6 @@
 static int aic7xxx_spurious_count;
 
 /*
- * The driver keeps up to four scb structures per card in memory. Only the
- * first 25 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookkeeping.
- */
-
-/*
  * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
  * in the scatter-gather lists.  We need to convert the virtual
  * addresses to physical addresses.
@@ -558,20 +679,28 @@
 /*
  * Maximum number of SG segments these cards can support.
  */
-#define	MAX_SG 256
+#define	AIC7XXX_MAX_SG 27
 
-struct aic7xxx_scb {
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB	255
+
+
+struct aic7xxx_hwscb {
 /* ------------    Begin hardware supported fields    ---------------- */
 /* 0*/  unsigned char control;
 /* 1*/  unsigned char target_channel_lun;       /* 4/1/3 bits */
 /* 2*/  unsigned char target_status;
 /* 3*/  unsigned char SG_segment_count;
-/* 4*/  unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 4*/  unsigned int  SG_list_pointer;
 /* 8*/  unsigned char residual_SG_segment_count;
-/* 9*/  unsigned char residual_data_count[3] __attribute__ ((packed));
-/*12*/  unsigned char data_pointer[4] __attribute__ ((packed));
-/*16*/  unsigned int  data_count __attribute__ ((packed)); /* must be 32 bits */
-/*20*/  unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/* 9*/  unsigned char residual_data_count[3];
+/*12*/  unsigned int  data_pointer;
+/*16*/  unsigned int  data_count;
+/*20*/  unsigned int  SCSI_cmd_pointer;
 /*24*/  unsigned char SCSI_cmd_length;
 /*25*/	u_char tag;			/* Index into our kernel SCB array.
 					 * Also used as the tag for tagged I/O
@@ -579,29 +708,47 @@
 #define SCB_PIO_TRANSFER_SIZE	26 	/* amount we need to upload/download
 					 * via PIO to initialize a transaction.
 					 */
-/*26*/  u_char next;                    /* Used to thread SCBs awaiting selection
+/*26*/  unsigned char next;             /* Used to thread SCBs awaiting selection
                                          * or disconnected down in the sequencer.
                                          */
-	/*-----------------end of hardware supported fields----------------*/
-	Scsi_Cmnd          *cmd;	/* Scsi_Cmnd for this scb */
-        struct aic7xxx_scb *q_next;     /* next scb in queue */
-#define SCB_FREE               0x00
-#define SCB_ACTIVE             0x01
-#define SCB_ABORTED            0x02
-#define SCB_DEVICE_RESET       0x04
-#define SCB_IMMED              0x08
-#define SCB_SENSE              0x10
-#define SCB_QUEUED_FOR_DONE    0x40
-#define SCB_PAGED_OUT          0x80
-#define SCB_WAITINGQ           0x100
-#define SCB_ASSIGNEDQ          0x200
-#define SCB_SENTORDEREDTAG     0x400
-#define SCB_IN_PROGRESS        (SCB_ACTIVE | SCB_PAGED_OUT | \
-                                SCB_WAITINGQ | SCB_ASSIGNEDQ)
-	int                 state;          /* current state of scb */
-	unsigned int        position;       /* Position in scb array */
-	struct hw_scatterlist  sg_list[MAX_SG]; /* SG list in adapter format */
-	unsigned char       sense_cmd[6];   /* Allocate 6 characters for sense command */
+/*27*/  unsigned char prev;
+/*28*/  unsigned int pad;               /*
+                                         * Unused by the kernel, but we require
+                                         * the padding so that the array of
+                                         * hardware SCBs is alligned on 32 byte
+                                         * boundaries so the sequencer can index
+                                         */
+};
+
+typedef enum {
+	SCB_FREE		= 0x0000,
+	SCB_ACTIVE		= 0x0001,
+	SCB_ABORTED		= 0x0002,
+	SCB_DEVICE_RESET	= 0x0004,
+	SCB_SENSE		= 0x0008,
+	SCB_TIMEDOUT		= 0x0010,
+	SCB_QUEUED_FOR_DONE	= 0x0020,
+	SCB_RECOVERY_SCB	= 0x0040,
+	SCB_WAITINGQ		= 0x0080,
+	SCB_ASSIGNEDQ		= 0x0100,
+	SCB_SENTORDEREDTAG	= 0x0200,
+	SCB_MSGOUT_SDTR		= 0x0400,
+	SCB_MSGOUT_WDTR		= 0x0800,
+	SCB_ABORT		= 0x1000,
+	SCB_QUEUED_ABORT	= 0x2000
+} scb_flag_type;
+
+struct aic7xxx_scb {
+        struct aic7xxx_hwscb  *hscb;          /* corresponding hardware scb */
+	Scsi_Cmnd             *cmd;	      /* Scsi_Cmnd for this scb */
+        struct aic7xxx_scb    *q_next;        /* next scb in queue */
+	scb_flag_type          flags;         /* current state of scb */
+	struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        unsigned char          sg_count;
+	unsigned char          sense_cmd[6];  /*
+                                               * Allocate 6 characters for
+                                               * sense command.
+                                               */
 };
 
 /*
@@ -626,20 +773,17 @@
 generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
 
 typedef struct {
+  struct aic7xxx_hwscb *hscbs;
   scb_queue_type free_scbs;        /*
                                     * SCBs assigned to free slot on
                                     * card (no paging required)
                                     */
-  int            numscbs;          /* current number of scbs */
-  int            activescbs;       /* active scbs */
-} scb_usage_type;
-
-/*
- * The maximum number of SCBs we could have for ANY type
- * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
- * SEQUENCER CODE IF THIS IS MODIFIED!
- */
-#define AIC7XXX_MAXSCB	255
+  unsigned char  numscbs;          /* current number of scbs */
+  unsigned char  maxhscbs;         /* hardware scbs */
+  unsigned char  maxscbs;          /* max scbs including pageable scbs */
+  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  unsigned int   reserve[100];
+} scb_data_type;
 
 /*
  * Define a structure used for each host adapter, only one per IRQ.
@@ -647,17 +791,26 @@
 struct aic7xxx_host {
   struct Scsi_Host        *host;             /* pointer to scsi host */
   int                      host_no;          /* SCSI host number */
+  int                      instance;         /* aic7xxx instance number */
+  int                      scsi_id;          /* host adapter SCSI ID */
+  int                      scsi_id_b;        /*   channel B for twin adapters */
+  int                      irq;              /* IRQ for this adapter */
   int                      base;             /* card base address */
-  int                      maxhscbs;         /* hardware SCBs */
-  int                      maxscbs;          /* max SCBs (including pageable) */
-#define A_SCANNED              0x0001
-#define B_SCANNED              0x0002
-#define EXTENDED_TRANSLATION   0x0004
-#define HAVE_SEEPROM           0x0008
-#define ULTRA_ENABLED          0x0010
-#define PAGE_ENABLED           0x0020
-#define IN_ISR                 0x0040
-#define USE_DEFAULTS           0x0080
+  unsigned int             mbase;            /* I/O memory address */
+  volatile unsigned char  *maddr;            /* memory mapped address */
+#define A_SCANNED               0x0001
+#define B_SCANNED               0x0002
+#define EXTENDED_TRANSLATION    0x0004
+#define FLAGS_CHANNEL_B_PRIMARY 0x0008
+#define MULTI_CHANNEL           0x0010
+#define ULTRA_ENABLED           0x0020
+#define PAGE_ENABLED            0x0040
+#define USE_DEFAULTS            0x0080
+#define BIOS_ENABLED            0x0100
+#define IN_ISR                  0x0200
+#define IN_TIMEOUT              0x0400
+#define SHARED_SCBDATA          0x0800
+#define HAVE_SEEPROM            0x1000
   unsigned int             flags;
   unsigned int             isr_count;        /* Interrupt count */
   unsigned short           needsdtr_copy;    /* default config */
@@ -668,36 +821,23 @@
   unsigned short           wdtr_pending;
   unsigned short           orderedtag;
   unsigned short           discenable;	     /* Targets allowed to disconnect */
-  aha_type                 type;             /* card type */
-  aha_chip_type            chip_type;        /* chip base type */
+  aha_chip_type            chip_type;        /* card type */
+  aha_chip_class_type      chip_class;
   aha_bus_type             bus_type;         /* normal/twin/wide bus */
-  char *                   mbase;            /* I/O memory address */
-  unsigned char            chan_num;         /* for 3940/3985, channel number */
+  unsigned char            chan_num;         /* for 39xx, channel number */
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
   unsigned char            qcntmask;
-  struct seeprom_config    seeprom;
+  unsigned char            qfullcount;
+  unsigned char            cmdoutcnt;
+  unsigned char            curqincnt;
   struct Scsi_Host        *next;             /* allow for multiple IRQs */
-  struct aic7xxx_scb      *scb_array[AIC7XXX_MAXSCB];  /* active commands */
-  struct aic7xxx_scb      *pagedout_ntscbs[16];  /*
-                                                  * paged-out, non-tagged scbs
-                                                  * indexed by target.
-                                                  */
-  scb_queue_type           page_scbs;        /*
-                                              * SCBs that will require paging
-                                              * before use (no assigned slot)
-                                              */
+  unsigned char            activescbs;       /* active scbs */
   scb_queue_type           waiting_scbs;     /*
-                                              * SCBs waiting to be paged and
-                                              * started.
-                                              */
-  scb_queue_type           assigned_scbs;    /*
-                                              * SCBs that were waiting but have
-                                              * have now been assigned a slot
-                                              * by aic7xxx_free_scb
+                                              * SCBs waiting for space in
+                                              * the QINFIFO.
                                               */
-  scb_usage_type           scb_usage;
-  scb_usage_type          *scb_link;
+  scb_data_type           *scb_data;
 
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
@@ -709,6 +849,7 @@
 #define  BUS_DEVICE_RESET_PENDING       0x02
     int  flags;
     int  commands_sent;
+    int  active_cmds;
   } device_status[16];
 #ifdef AIC7XXX_PROC_STATS
   /*
@@ -730,38 +871,14 @@
     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 */
+  } stats[16][8];                            /* channel, target, lun */
 #endif /* AIC7XXX_PROC_STATS */
 };
 
-struct aic7xxx_host_config {
-  int              irq;        /* IRQ number */
-  int              mbase;      /* memory base address*/
-  int              base;       /* I/O base address*/
-  int              maxhscbs;   /* hardware SCBs */
-  int              maxscbs;    /* max SCBs (including pageable) */
-  int              unpause;    /* unpause value for HCNTRL */
-  int              pause;      /* pause value for HCNTRL */
-  int              scsi_id;    /* host SCSI ID */
-  int              scsi_id_b;  /* host SCSI ID B channel for twin cards */
-  unsigned int     flags;      /* used the same as struct aic7xxx_host flags */
-  int              chan_num;   /* for 3940/3985, channel number */
-  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) */
-};
-
 /*
  * Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns to the proper value to
- * stick in the scsiscfr reg to use that transfer rate.
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
  */
 static struct {
   short period;
@@ -770,17 +887,17 @@
   short rate;
   const char *english;
 } aic7xxx_syncrates[] = {
-  {  50,  0x100,  "20.0"  },
-  {  62,  0x110,  "16.0"  },
-  {  75,  0x120,  "13.4"  },
-  { 100,  0x000,  "10.0"  },
-  { 125,  0x010,   "8.0"  },
-  { 150,  0x020,   "6.67" },
-  { 175,  0x030,   "5.7"  },
-  { 200,  0x040,   "5.0"  },
-  { 225,  0x050,   "4.4"  },
-  { 250,  0x060,   "4.0"  },
-  { 275,  0x070,   "3.6"  }
+  { 12,  0x100,  "20.0"  },
+  { 15,  0x110,  "16.0"  },
+  { 18,  0x120,  "13.4"  },
+  { 25,  0x000,  "10.0"  },
+  { 31,  0x010,   "8.0"  },
+  { 37,  0x020,   "6.67" },
+  { 43,  0x030,   "5.7"  },
+  { 50,  0x040,   "5.0"  },
+  { 56,  0x050,   "4.4"  },
+  { 62,  0x060,   "4.0"  },
+  { 68,  0x070,   "3.6"  }
 };
 
 static int num_aic7xxx_syncrates =
@@ -789,166 +906,51 @@
 #ifdef CONFIG_PCI
 static int number_of_3940s = 0;
 static int number_of_3985s = 0;
-#ifdef AIC7XXX_SHARE_SCBS
-static scb_usage_type *shared_3985_scbs = NULL;
-#endif
-#endif CONFIG_PCI
+#endif /* CONFIG_PCI */
 
 #ifdef AIC7XXX_DEBUG
 
-static void
-debug_config(struct aic7xxx_host_config *p)
-{
-  int scsi_conf;
-  unsigned char brelease;
-  unsigned char dfthresh;
-
-  static int DFT[] = { 0, 50, 75, 100 };
-  static int SST[] = { 256, 128, 64, 32 };
-  static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
-
-  scsi_conf = inb(SCSICONF + p->base);
-
-  /*
-   * 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)
-  {
-    brelease = p->busrtime >> 2;
-  }
-  else
-  {
-    brelease = p->busrtime;
-  }
-  if (brelease == 0)
-  {
-    brelease = 2;
-  }
-
-  switch (p->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-      printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_284x:
-      printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type],
-             BUSW[p->bus_type], p->base, p->mbase);
-      break;
-
-    default:
-      panic("aic7xxx: (debug_config) internal error.\n");
-  }
-
-  printk("    irq %d\n"
-	 "    bus release time %d bclks\n"
-	 "    data fifo threshold %d%%\n",
-	 p->irq,
-	 brelease,
-	 DFT[dfthresh]);
-
-  printk("    SCSI CHANNEL A:\n"
-	 "        scsi id %d\n"
-	 "        scsi selection timeout %d ms\n"
-	 "        scsi bus reset at power-on %sabled\n",
-	 (p->bus_type & AIC_WIDE) ? (scsi_conf & 0x0f) : (scsi_conf & 0x07),
-	 SST[(scsi_conf >> 3) & 0x03],
-	 (scsi_conf & 0x40) ? "en" : "dis");
-
-  if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN))
-  {
-    /*
-     * Set the parity for 7770 based cards.
-     */
-    p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->parity != AIC_UNKNOWN)
-  {
-    printk("        scsi bus parity %sabled\n",
-	   (p->parity == AIC_ENABLED) ? "en" : "dis");
-  }
-
-  if ((p->type == AIC_7770) || (p->type == AIC_7771))
-  {
-    p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->low_term != AIC_UNKNOWN)
-  {
-    printk("        scsi bus termination (low byte) %sabled\n",
-	  (p->low_term == AIC_ENABLED) ? "en" : "dis");
-  }
-  if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
-  {
-    printk("        scsi bus termination (high byte) %sabled\n",
-	  (p->high_term == AIC_ENABLED) ? "en" : "dis");
-  }
-}
-
 #if 0
 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, 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, 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);
+  struct aic7xxx_hwscb *hscb = scb->hscb;
+
+  printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+    scb,
+    hscb->control,
+    hscb->target_channel_lun,
+    hscb->SCSI_cmd_length,
+    hscb->SCSI_cmd_pointer );
+  printk("        datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+    hscb->data_count,
+    hscb->data_pointer,
+    hscb->SG_segment_count,
+    hscb->SG_list_pointer);
+  printk("        sg_addr:%lx sg_len:%ld\n",
+    hscb->sg_list[0].address,
+    hscb->sg_list[0].length);
 }
 #endif
 
 #else
-#  define debug_config(x)
 #  define debug_scb(x)
 #endif AIC7XXX_DEBUG
 
-#define TCL_OF_SCB(x)  (((x)->target_channel_lun >> 4) & 0xf),  \
-                       (((x)->target_channel_lun >> 3) & 0x01), \
-                       ((x)->target_channel_lun & 0x07)
+#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                        (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+                        ((scb->hscb)->target_channel_lun & 0x07)
 
-#define TARGET_INDEX(x)  ((x)->target | ((x)->channel << 3))
+#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                       (((scb->hscb)->target_channel_lun >> 3) & 0x01)
+
+#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
+
+#define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
 
 /*
  * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *       cards in the system. This should be fixed, but then,
- *       does anyone really have more than one in a machine?
+ *       cards in the system.  This should be fixed.
  */
 static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
 static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
@@ -958,6 +960,60 @@
                                               *  1 use level triggered
                                               */
 static int aic7xxx_enable_ultra = 0;         /* enable ultra SCSI speeds */
+static int aic7xxx_verbose = 0;	             /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * These functions are not used yet, but when we do memory mapped
+ * IO, we'll use them then.
+ *
+ * For now we leave these commented out as the x86 inline assembly causes
+ * compiles to barf on DEC Alphas.  Besides, they aren't even used yet, so
+ * they constitute wasted .text space right now.
+ ***************************************************************************/
+
+/***************************************************************************
+
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+  if (p->maddr != NULL)
+    return (p->maddr[port]);
+  else
+    return (inb(p->base + port));
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+  if (p->maddr != NULL)
+    p->maddr[port] = val;
+  else
+    outb(val, p->base + port);
+}
+
+static inline void
+aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
+{
+  if (p->maddr != NULL)
+  {
+    __asm __volatile("
+      cld;
+    1:  lodsb;
+      movb %%al,(%0);
+      loop 1b"      :
+              :
+      "r" ((p)->maddr + (port)),
+      "S" ((valp)), "c" ((size))  :
+      "%esi", "%ecx", "%eax");
+  }
+  else
+  {
+    outsb(p->base + port, valp, size);
+  }
+}
+ ***************************************************************************/
 
 /*+F*************************************************************************
  * Function:
@@ -982,6 +1038,7 @@
     { "no_reset",    &aic7xxx_no_reset },
     { "irq_trigger", &aic7xxx_irq_trigger },
     { "ultra",       &aic7xxx_enable_ultra },
+    { "verbose",     &aic7xxx_verbose },
     { NULL,          NULL }
   };
 
@@ -1007,162 +1064,343 @@
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_loadseq
+ *   pause_sequencer
  *
  * Description:
- *   Load the sequencer code into the controller memory.
+ *   Pause the sequencer and wait for it to actually stop - this
+ *   is important since the sequencer can disable pausing for critical
+ *   sections.
  *-F*************************************************************************/
-static void
-aic7xxx_loadseq(int base)
+static inline void
+pause_sequencer(struct aic7xxx_host *p)
 {
-  static unsigned char seqprog[] = {
-    /*
-     * Each sequencer instruction is 29 bits
-     * long (fill in the excess with zeroes)
-     * and has to be loaded from least -> most
-     * significant byte, so this table has the
-     * byte ordering reversed.
-     */
-#   include "aic7xxx_seq.h"
-  };
-
-  /*
-   * When the AIC-7770 is paused (as on chip reset), the
-   * sequencer address can be altered and a sequencer
-   * program can be loaded by writing it, byte by byte, to
-   * the sequencer RAM port - the Adaptec documentation
-   * recommends using REP OUTSB to do this, hence the inline
-   * assembly. Since the address autoincrements as we load
-   * the program, reset it back to zero afterward. Disable
-   * sequencer RAM parity error detection while loading, and
-   * make sure the LOADRAM bit is enabled for loading.
-   */
-  outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
-
-  outsb(SEQRAM + base, seqprog, sizeof(seqprog));
-
-  /*
-   * WARNING!  This is a magic sequence!  After extensive
-   * experimentation, it seems that you MUST turn off the
-   * LOADRAM bit before you play with SEQADDR again, else
-   * you will end up with parity errors being flagged on
-   * your sequencer program. (You would also think that
-   * turning off LOADRAM and setting SEQRESET to reset the
-   * address to zero would work, but you need to do it twice
-   * for it to take effect on the address. Timing problem?)
-   */
-  do {
-    /*
-     * Actually, reset it until
-     * the address shows up as
-     * zero just to be safe..
-     */
-    outb(SEQRESET | FASTMODE, SEQCTL + base);
-  } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
+  outb(p->pause, p->base + HCNTRL);
+  while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+  {
+    ;
+  }
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_delay
+ *   unpause_sequencer
  *
  * Description:
- *   Delay for specified amount of time.
+ *   Unpause the sequencer. Unremarkable, yet done often enough to
+ *   warrant an easy way to do it.
  *-F*************************************************************************/
-static void
-aic7xxx_delay(int seconds)
+static inline void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
 {
-  unsigned long i;
-
-  i = jiffies + (seconds * HZ);  /* compute time to stop */
-
-  while (jiffies < i)
+  if (unpause_always ||
+      ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
   {
-    ;  /* Do nothing! */
+    outb(p->unpause, p->base + HCNTRL);
   }
 }
 
 /*+F*************************************************************************
  * Function:
- *   rcs_version
+ *   restart_sequencer
  *
  * Description:
- *   Return a string containing just the RCS version number from either
- *   an Id or Revision RCS clause.
+ *   Restart the sequencer program from address zero.  This assumes
+ *   that the sequencer is already paused.
  *-F*************************************************************************/
-const char *
-rcs_version(const char *version_info)
+static inline void
+restart_sequencer(struct aic7xxx_host *p)
 {
-  static char buf[10];
-  char *bp, *ep;
+  /* Set the sequencer address to 0. */
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
 
-  bp = NULL;
-  strcpy(buf, "????");
-  if (!strncmp(version_info, "$Id: ", 5))
-  {
-    if ((bp = strchr(version_info, ' ')) != NULL)
-    {
-      bp++;
-      if ((bp = strchr(bp, ' ')) != NULL)
-      {
-	bp++;
-      }
-    }
-  }
-  else
+  /*
+   * Reset and unpause the sequencer.  The reset is suppose to
+   * start the sequencer running, but we do an unpause to make
+   * sure.
+   */
+  outb(SEQRESET | FASTMODE, p->base + SEQCTL);
+
+  unpause_sequencer(p, /*unpause_always*/ TRUE);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_next_patch
+ *
+ * Description:
+ *   Find the next patch to download.
+ *-F*************************************************************************/
+static struct patch *
+aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+{
+  while (cur_patch != NULL)
   {
-    if (!strncmp(version_info, "$Revision: ", 11))
+    if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
+      || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
+      || (instrptr >= cur_patch->end))
     {
-      if ((bp = strchr(version_info, ' ')) != NULL)
+      /*
+       * Either we want to keep this section of code, or we have consumed
+       * this patch.  Skip to the next patch.
+       */
+      cur_patch++;
+      if (cur_patch->options == 0)
       {
-	bp++;
+        /* Out of patches. */
+        cur_patch = NULL;
       }
     }
-  }
-
-  if (bp != NULL)
-  {
-    if ((ep = strchr(bp, ' ')) != NULL)
+    else
     {
-      register int len = ep - bp;
-
-      strncpy(buf, bp, len);
-      buf[len] = '\0';
+      /* Found an OK patch. */
+      break;
     }
   }
-
-  return buf;
+  return (cur_patch);
 }
 
+
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_info
+ *   aic7xxx_download_instr
  *
  * Description:
- *   Return a string describing the driver.
+ *   Find the next patch to download.
  *-F*************************************************************************/
-const char *
-aic7xxx_info(struct Scsi_Host *notused)
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
 {
-  static char buffer[128];
-
-  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
-  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
-  strcat(buffer, "/");
-  strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
-  strcat(buffer, "/");
-  strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
-
-  return buffer;
-}
+  unsigned char opcode;
+  struct ins_format3 *instr;
+
+  instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+  /* Pull the opcode */
+  opcode = instr->opcode_addr >> 1;
+  switch (opcode)
+  {
+    case AIC_OP_JMP:
+    case AIC_OP_JC:
+    case AIC_OP_JNC:
+    case AIC_OP_CALL:
+    case AIC_OP_JNE:
+    case AIC_OP_JNZ:
+    case AIC_OP_JE:
+    case AIC_OP_JZ:
+    {
+      int address_offset;
+      struct ins_format3 new_instr;
+      unsigned int address;
+      struct patch *patch;
+      int i;
+
+      address_offset = 0;
+      new_instr = *instr;  /* Strucure copy */
+      address = new_instr.address;
+      address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8;
+      for (i = 0; i < NUMBER(patches); i++)
+      {
+        patch = &patches[i];
+        if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
+            (((patch->options & options) != 0) && (patch->negative == TRUE)))
+        {
+          if (address >= patch->end)
+          {
+            address_offset += patch->end - patch->begin;
+          }
+        }
+      }
+      address -= address_offset;
+      new_instr.address = address &0xFF;
+      new_instr.opcode_addr &= ~ADDR_HIGH_BIT;
+      new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+      outsb(p->base + SEQRAM, &new_instr.immediate, 4);
+      break;
+    }
+
+    case AIC_OP_OR:
+    case AIC_OP_AND:
+    case AIC_OP_XOR:
+    case AIC_OP_ADD:
+    case AIC_OP_ADC:
+    case AIC_OP_ROL:
+      outsb(p->base + SEQRAM, &instr->immediate, 4);
+      break;
+
+    default:
+      panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+      break;
+  }
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_loadseq
+ *
+ * Description:
+ *   Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(struct aic7xxx_host *p)
+{
+  int options;
+  struct patch *cur_patch;
+  int i;
+  int downloaded;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+  }
+  options = 1;  /* Code for all options. */
+  downloaded = 0;
+  if ((p->flags & ULTRA_ENABLED) != 0)
+    options |= ULTRA;
+  if (p->bus_type == AIC_TWIN)
+    options |= TWIN_CHANNEL;
+  if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+    options |= SCB_PAGING;
+
+  cur_patch = patches;
+  outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  for (i = 0; i < sizeof(seqprog) / 4;  i++)
+  {
+    cur_patch = aic7xxx_next_patch(cur_patch, options, i);
+    if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+    {
+      /* Skip this instruction for this configuration. */
+      continue;
+    }
+    aic7xxx_download_instr(p, options, i);
+    downloaded++;
+  }
+
+  outb(FASTMODE, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  if (aic7xxx_verbose)
+  {
+     printk(" %d instructions downloaded\n", downloaded);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_delay
+ *
+ * Description:
+ *   Delay for specified amount of time.  We use udelay because the timer
+ *   interrupt is not guaranteed to be enabled.  This will cause an
+ *   infinite loop since jiffies (clock ticks) is not updated.
+ *-F*************************************************************************/
+static void
+aic7xxx_delay(int seconds)
+{
+  int i;
+
+  /*                        
+   * Call udelay() for 1 millisecond inside a loop for  
+   * the requested amount of seconds.
+   */
+  for (i=0; i < seconds*1000; i++)
+  {
+    udelay(1000);  /* Delay for 1 millisecond. */
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   rcs_version
+ *
+ * Description:
+ *   Return a string containing just the RCS version number from either
+ *   an Id or Revision RCS clause.
+ *-F*************************************************************************/
+const char *
+rcs_version(const char *version_info)
+{
+  static char buf[10];
+  char *bp, *ep;
+
+  bp = NULL;
+  strcpy(buf, "????");
+  if (!strncmp(version_info, "$Id: ", 5))
+  {
+    if ((bp = strchr(version_info, ' ')) != NULL)
+    {
+      bp++;
+      if ((bp = strchr(bp, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+  else
+  {
+    if (!strncmp(version_info, "$Revision: ", 11))
+    {
+      if ((bp = strchr(version_info, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+
+  if (bp != NULL)
+  {
+    if ((ep = strchr(bp, ' ')) != NULL)
+    {
+      register int len = ep - bp;
+
+      strncpy(buf, bp, len);
+      buf[len] = '\0';
+    }
+  }
+
+  return buf;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_info
+ *
+ * Description:
+ *   Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *notused)
+{
+  static char buffer[128];
+
+  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+#if 0
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+#endif
+
+  return buffer;
+}
 
 /*+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.
+ *   How much data should be transferred for this SCSI command?  Assume
+ *   all segments are to be transferred except for the last sg_last
+ *   segments.  This will allow us to compute underflow easily.  To
+ *   calculate the total length of the command, use sg_last = 0.  To
+ *   calculate the length of all but the last 2 SG segments, use
+ *   sg_last = 2.
  *-F*************************************************************************/
 static unsigned
 aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
@@ -1176,7 +1414,7 @@
 
   if (cmd->use_sg)
   {
-    for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+    for (i = length = 0; i < segments; i++)
     {
       length += sg[i].length;
     }
@@ -1198,9 +1436,9 @@
  *-F*************************************************************************/
 static void
 aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
-    short period, unsigned char offset, int target, char channel)
+    unsigned char *period, unsigned char *offset, int target, char channel)
 {
-  int i;
+  int i = num_aic7xxx_syncrates;
   unsigned long ultra_enb_addr;
   unsigned char ultra_enb, sxfrctl0;
 
@@ -1208,11 +1446,11 @@
    * If the offset is 0, then the device is requesting asynchronous
    * transfers.
    */
-  if (offset != 0)
+  if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
   {
     for (i = 0; i < num_aic7xxx_syncrates; i++)
     {
-      if ((aic7xxx_syncrates[i].period - period) >= 0)
+      if (*period <= aic7xxx_syncrates[i].period)
       {
         /*
          * Watch out for Ultra speeds when ultra is not enabled and
@@ -1228,99 +1466,57 @@
            */
           continue;
         }
-        *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
+        *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
+        *period = aic7xxx_syncrates[i].period;
 
-        /*
-         * Ensure Ultra mode is set properly for this target.
-         */
-        ultra_enb_addr = ULTRA_ENB;
-        if ((channel == 'B') || (target > 7))
-        {
-          ultra_enb_addr++;
-        }
-        ultra_enb = inb(p->base + ultra_enb_addr);
-        sxfrctl0 = inb(p->base + SXFRCTL0);
-        if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
+        if (aic7xxx_verbose)
         {
-          ultra_enb |= 0x01 << (target & 0x07);
-          sxfrctl0 |= ULTRAEN;
+          printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
+                 "offset %d.\n", p->host_no, target, channel,
+                 aic7xxx_syncrates[i].english, *offset);
         }
-        else
-        {
-          ultra_enb &= ~(0x01 << (target & 0x07));
-          sxfrctl0 &= ~ULTRAEN;
-        }
-        outb(ultra_enb, p->base + ultra_enb_addr);
-        outb(sxfrctl0, p->base + SXFRCTL0);
-
-        printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
-               "offset %d.\n", p->host_no, target, channel,
-               aic7xxx_syncrates[i].english, offset);
-        return;
+        break;
       }
     }
   }
 
-  /*
-   * Default to asynchronous transfer
-   */
-  *scsirate = 0;
-  printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
-         p->host_no, target, channel);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_putscb
- *
- * Description:
- *   Transfer a SCB to the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  outb(SCBAUTO, SCBCNT + base);
+  if (i >= num_aic7xxx_syncrates)
+  {
+    /*
+     * Use asynchronous transfers.
+     */
+    *scsirate = 0;
+    *period = 0;
+    *offset = 0;
+    if (aic7xxx_verbose)
+    {
+      printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
+             p->host_no, target, channel);
+    }
+  }
 
   /*
-   * 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.
-   *
-   * We can do 16bit transfers on all but 284x.
+   * Ensure Ultra mode is set properly for this target.
    */
-  if (p->type == AIC_284x)
+  ultra_enb_addr = ULTRA_ENB;
+  if ((channel == 'B') || (target > 7))
   {
-    outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+    ultra_enb_addr++;
+  }
+  ultra_enb = inb(p->base + ultra_enb_addr);
+  sxfrctl0 = inb(p->base + SXFRCTL0);
+  if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
+  {
+    ultra_enb |= 0x01 << (target & 0x07);
+    sxfrctl0 |= FAST20;
   }
   else
   {
-    outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+    ultra_enb &= ~(0x01 << (target & 0x07));
+    sxfrctl0 &= ~FAST20;
   }
-
-  outb(0, SCBCNT + base);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_getscb
- *
- * Description:
- *   Get a SCB from the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  /*
-   * This is almost identical to aic7xxx_putscb().
-   */
-  outb(SCBAUTO, SCBCNT + base);
-  insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
-  outb(0, SCBCNT + base);
+  outb(ultra_enb, p->base + ultra_enb_addr);
+  outb(sxfrctl0, p->base + SXFRCTL0);
 }
 
 /*+F*************************************************************************
@@ -1374,6 +1570,47 @@
 
 /*+F*************************************************************************
  * Function:
+ *   scbq_remove
+ *
+ * Description:
+ *   Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+  if (queue->head == scb)
+  {
+    /* At beginning of queue, remove from head. */
+    scbq_remove_head(queue);
+  }
+  else
+  {
+    struct aic7xxx_scb *curscb = queue->head;
+
+    /*
+     * Search until the next scb is the one we're looking for, or
+     * we run out of queue.
+     */
+    while ((curscb != NULL) && (curscb->q_next != scb))
+    {
+      curscb = curscb->q_next;
+    }
+    if (curscb != NULL)
+    {
+      /* Found it. */
+      curscb->q_next = scb->q_next;
+      if (scb->q_next == NULL)
+      {
+        /* Update the tail when removing the tail. */
+        queue->tail = curscb;
+      }
+    }
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
  *   scbq_insert_tail
  *
  * Description:
@@ -1403,23 +1640,87 @@
  *   to be reset and all devices on that channel must be aborted.
  *-F*************************************************************************/
 static int
-aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel,
+    int lun, unsigned char tag)
 {
-  int targ = (scb->target_channel_lun >> 4) & 0x0F;
-  char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+  char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int slun = scb->hscb->target_channel_lun & 0x07;
+  int match;
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
-         target, channel, targ, chan);
+  printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
+         scb->cmd->device->host->host_no, target, channel, targ, chan);
 #endif
-  if (target == ALL_TARGETS)
+  match = ((chan == channel) || (channel == ALL_CHANNELS));
+  if (match != 0)
+    match = ((targ == target) || (target == ALL_TARGETS));
+  if (match != 0)
+    match = ((lun == slun) || (lun == ALL_LUNS));
+  if (match != 0)
+    match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+  return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ *   Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+  /*
+   * Invalidate the tag so that aic7xxx_find_scb doesn't think
+   * it's active
+   */
+  outb(SCB_LIST_NULL, p->base + SCB_TAG);
+
+  outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
+  outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ *   Removes the current SCB from the disconnected list and adds it
+ *   to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+{
+  unsigned char next;
+  unsigned char prev;
+
+  outb(scbptr, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
+  prev = inb(p->base + SCB_PREV);
+
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
+
+  if (prev != SCB_LIST_NULL)
   {
-    return (chan == channel);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   else
   {
-    return ((chan == channel) && (targ == target));
+    outb(next, p->base + DISCONNECTED_SCBH);
+  }
+
+  if (next != SCB_LIST_NULL)
+  {
+    outb(next, p->base + SCBPTR);
+    outb(prev, p->base + SCB_PREV);
   }
+  return next;
 }
 
 /*+F*************************************************************************
@@ -1427,120 +1728,154 @@
  *   aic7xxx_busy_target
  *
  * Description:
- *   Set the specified target active.
+ *   Set the specified target busy.
  *-F*************************************************************************/
 static void
-aic7xxx_busy_target(unsigned char target, char channel, int base)
+aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, unsigned char scbid)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned int  scb_offset;
 
-  if ((target > 0x07) || (channel == 'B'))
-  {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
-  }
-  active = inb(active_port);
-  active |= (0x01 << (target & 0x07));
-  outb(active, active_port);
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
+
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  outb(scbid, p->base + scb_offset);
+  outb(active_scb, p->base + SCBPTR);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_unbusy_target
+ *   aic7xxx_index_busy_target
  *
  * Description:
- *   Set the specified target inactive.
+ *   Returns the index of the busy target, and optionally sets the
+ *   target inactive.
  *-F*************************************************************************/
-static void
-aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+static unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, int unbusy)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned char busy_scbid;
+  unsigned int  scb_offset;
+
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
 
-  if ((target > 0x07) || (channel == 'B'))
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  busy_scbid = inb(p->base + scb_offset);
+  if (unbusy)
   {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
+    outb(SCB_LIST_NULL, p->base + scb_offset);
   }
-  active = inb(active_port);
-  active &= ~(0x01 << (target & 0x07));
-  outb(active, active_port);
+  outb(active_scb, p->base + SCBPTR);
+  return (busy_scbid);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_allocate_scb
+ *   aic7xxx_find_scb
  *
  * Description:
- *   Get a free SCB either from one already assigned to a hardware
- *   slot, or one that will require an SCB to be paged out before
- *   use.  If there are none, attempt to allocate a new one.
+ *   Look through the SCB array of the card and attempt to find the
+ *   hardware SCB that corresponds to the passed in SCB.  Return
+ *   SCB_LIST_NULL if unsuccessful.  This routine assumes that the
+ *   card is already paused.
  *-F*************************************************************************/
-static struct aic7xxx_scb *
-aic7xxx_allocate_scb(struct aic7xxx_host *p)
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_scb *scbp = NULL;
-  int maxscbs;
+  unsigned char saved_scbptr;
+  unsigned char curindex;
 
-  scbp = p->scb_link->free_scbs.head;
-  if (scbp != NULL)
+  saved_scbptr = inb(p->base + SCBPTR);
+  curindex = 0;
+  for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
   {
-    scbq_remove_head(&p->scb_link->free_scbs);
+    outb(curindex, p->base + SCBPTR);
+    if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+    {
+      break;
+    }
   }
-  else
+  outb(saved_scbptr, p->base + SCBPTR);
+  if (curindex >= p->scb_data->maxhscbs)
   {
-    /*
-     * This should always be NULL if paging is not enabled.
-     */
-    scbp = p->page_scbs.head;
-    if (scbp != NULL)
-    {
-      scbq_remove_head(&p->page_scbs);
-    }
-    else
-    {
-      /*
-       * Set limit the SCB allocation to the maximum number of
-       * hardware SCBs if paging is not enabled; otherwise use
-       * the maximum (255).
-       */
-      if (p->flags & PAGE_ENABLED)
-        maxscbs = p->maxscbs;
-      else
-        maxscbs = p->maxhscbs;
-      if (p->scb_link->numscbs < maxscbs)
-      {
-        int scb_index = p->scb_link->numscbs;
-        int scb_size = sizeof(struct aic7xxx_scb);
+    curindex = SCB_LIST_NULL;
+  }
 
-        p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA);
-        scbp = (p->scb_array[scb_index]);
-        if (scbp != NULL)
-        {
-          memset(scbp, 0, sizeof(*scbp));
-          scbp->tag = scb_index;
-          if (scb_index < p->maxhscbs)
-            scbp->position = scb_index;
-          else
-	    scbp->position = SCB_LIST_NULL;
-          p->scb_link->numscbs++;
-        }
+  return (curindex);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_allocate_scb
+ *
+ * Description:
+ *   Get an SCB from the free list or by allocating a new one.
+ *-F*************************************************************************/
+static struct aic7xxx_scb *
+aic7xxx_allocate_scb(struct aic7xxx_host *p)
+{
+  struct aic7xxx_scb   *scbp = NULL;
+  struct aic7xxx_hwscb *hscbp = NULL;
+#ifdef AGRESSIVE
+  long processor_flags;
+
+  save_flags(processor_flags);
+  cli();
+#endif
+
+  scbp = p->scb_data->free_scbs.head;
+  if (scbp != NULL)
+  {
+    scbq_remove_head(&p->scb_data->free_scbs);
+  }
+  else
+  {
+    if (p->scb_data->numscbs < p->scb_data->maxscbs)
+    {
+      int scb_index = p->scb_data->numscbs;
+      int scb_size = sizeof(struct aic7xxx_scb) +
+                     sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
+
+      scbp = kmalloc(scb_size, GFP_ATOMIC);
+      if (scbp != NULL)
+      {
+        memset(scbp, 0, sizeof(struct aic7xxx_scb));
+        hscbp = &p->scb_data->hscbs[scb_index];
+        scbp->hscb = hscbp;
+        scbp->sg_list = (struct hw_scatterlist *) &scbp[1];
+        memset(hscbp, 0, sizeof(struct aic7xxx_hwscb));
+        hscbp->tag = scb_index;
+        p->scb_data->numscbs++;
+        /*
+         * Place in the scb array; never is removed
+         */
+        p->scb_data->scb_array[scb_index] = scbp;
       }
     }
   }
+#ifdef AIC7XXX_DEBUG
   if (scbp != NULL)
   {
-#ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs++;
-#endif
+    p->activescbs++;
   }
+#endif
+
+#ifdef AGRESSIVE
+  restore_flags(processor_flags);
+#endif
   return (scbp);
 }
 
@@ -1580,6 +1915,7 @@
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
+    p->device_status[TARGET_INDEX(cmd)].active_cmds--;
     cmd->scsi_done(cmd);
   }
   p->completeq.tail = NULL;
@@ -1590,53 +1926,29 @@
  *   aic7xxx_free_scb
  *
  * Description:
- *   Free the scb and update the page, waiting, free scb lists.
+ *   Free the scb and insert into the free scb list.
  *-F*************************************************************************/
 static void
 aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_scb *wscb;
+  struct aic7xxx_hwscb *hscb;
+  long flags;
+
+  hscb = scb->hscb;
+  save_flags(flags);
+  cli();
 
-  scb->state = SCB_FREE;
+  scb->flags = SCB_FREE;
   scb->cmd = NULL;
-  scb->control = 0;
-  scb->state = 0;
+  hscb->control = 0;
+  hscb->target_status = 0;
 
-  if (scb->position == SCB_LIST_NULL)
-  {
-    scbq_insert_head(&p->page_scbs, scb);
-  }
-  else
-  {
-    /*
-     * If there are any SCBS on the waiting queue, assign the slot of this
-     * "freed" SCB to the first one.  We'll run the waiting queues after
-     * all command completes for a particular interrupt are completed or
-     * when we start another command.
-     */
-    wscb = p->waiting_scbs.head;
-    if (wscb != NULL)
-    {
-      scbq_remove_head(&p->waiting_scbs);
-      wscb->position = scb->position;
-      scbq_insert_tail(&p->assigned_scbs, wscb);
-      wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ;
-
-      /* 
-       * The "freed" SCB will need to be assigned a slot before being
-       * used, so put it in the page_scbs queue.
-       */
-      scb->position = SCB_LIST_NULL;
-      scbq_insert_head(&p->page_scbs, scb);
-    }
-    else
-    {
-      scbq_insert_head(&p->scb_link->free_scbs, scb);
-    }
+  scbq_insert_head(&p->scb_data->free_scbs, scb);
 #ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs--;  /* For debugging purposes. */
+  p->activescbs--;  /* For debugging purposes. */
 #endif
-  }
+
+  restore_flags(flags);
 }
 
 /*+F*************************************************************************
@@ -1651,68 +1963,115 @@
 {
   Scsi_Cmnd *cmd = scb->cmd;
 
+  if (scb->flags & SCB_RECOVERY_SCB)
+  {
+    p->flags &= ~IN_TIMEOUT;
+  }
+  if (cmd->result == DID_OK)
+  {
+    if (scb->flags & SCB_ABORTED)
+    {
+      cmd->result = (DID_RESET << 16);
+    }
+  }
+  if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  {
+    unsigned short mask;
+
+    mask = 0x01 << TARGET_INDEX(scb->cmd);
+    if (scb->flags & SCB_MSGOUT_WDTR)
+    {
+      p->wdtr_pending &= ~mask;
+    }
+    if (scb->flags & SCB_MSGOUT_SDTR)
+    {
+      p->sdtr_pending &= ~mask;
+    }
+  }
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
+#ifdef AIC7XXX_PROC_STATS
+  if ( (cmd->cmnd[0] != TEST_UNIT_READY) &&
+       (cmd->cmnd[0] != INQUIRY) )
+  {
+    int actual;
+
+    /*
+     * XXX: we should actually know how much actually transferred
+     * XXX: for each command, but apparently that's too difficult.
+     */
+    actual = aic7xxx_length(cmd, 0);
+    if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
+        && (aic7xxx_error(cmd) == 0))
+    {
+      struct aic7xxx_xferstats *sp;
+      long *ptr;
+      int x;
+
+      sp = &p->stats[((cmd->channel << 3) | cmd->target) & 0xf][cmd->lun & 0x7];
+      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 */
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_done_aborted_scbs
+ *   aic7xxx_run_done_queue
  *
  * Description:
- *   Calls the scsi_done() for the Scsi_Cmnd of each scb in the
- *   aborted list, and adds each scb to the free list.
+ *   Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ *   aborted list, and adds each scb to the free list.  If complete
+ *   is TRUE, we also process the commands complete list.
  *-F*************************************************************************/
 static void
-aic7xxx_done_aborted_scbs(struct aic7xxx_host *p)
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
 {
-  Scsi_Cmnd *cmd;
   struct aic7xxx_scb *scb;
   int i;
 
-  for (i = 0; i < p->scb_link->numscbs; i++)
+  for (i = 0; i < p->scb_data->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if (scb->state & SCB_QUEUED_FOR_DONE)
+    scb = p->scb_data->scb_array[i];
+    if (scb->flags & SCB_QUEUED_FOR_DONE)
     {
 #ifdef AIC7XXX_DEBUG_ABORT
-      printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n",
-      scb->position, TCL_OF_SCB(scb));
+      printk("(scsi%d:%d:%d) Aborting scb %d\n",
+             p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
 #endif
-      /*
-       * Process the command after marking the scb as free
-       * and adding it to the free list.
-       */
-      cmd = scb->cmd;
-      p->device_status[TARGET_INDEX(cmd)].flags = 0;
-      aic7xxx_free_scb(p, scb);
-      cmd->scsi_done(cmd);  /* call the done function */
+      aic7xxx_done(p, scb);
     }
   }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_add_waiting_scb
- *
- * Description:
- *   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)
-{
-  unsigned char next;
-  unsigned char curscb;
-
-  curscb = inb(SCBPTR + base);
-  next = inb(WAITING_SCBH + base);
-
-  outb(scb->position, SCBPTR + base);
-  outb(next, SCB_NEXT + base);
-  outb(scb->position, WAITING_SCBH + base);
-
-  outb(curscb, SCBPTR + base);
+  if (complete)
+  {
+    aic7xxx_done_cmds_complete(p);
+  }
 }
 
 /*+F*************************************************************************
@@ -1725,26 +2084,23 @@
  *-F*************************************************************************/
 static unsigned char
 aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
-    unsigned char prev)
+    unsigned char scbpos, unsigned char prev)
 {
   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(SCB_NEXT + base);
+  curscb = inb(p->base + SCBPTR);
+  outb(scbpos, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
 
   /*
    * Clear the necessary fields
    */
-  outb(0, SCB_CONTROL + base);
-  outb(SCB_LIST_NULL, SCB_NEXT + base);
-  aic7xxx_unbusy_target(target, channel, base);
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
 
   /*
    * Update the waiting list
@@ -1754,22 +2110,23 @@
     /*
      * First in the list
      */
-    outb(next, WAITING_SCBH + base);
+    outb(next, p->base + WAITING_SCBH);
   }
   else
   {
     /*
      * Select the scb that pointed to us and update its next pointer.
      */
-    outb(prev, SCBPTR + base);
-    outb(next, SCB_NEXT + base);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   /*
    * 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_QUEUED_FOR_DONE;
+  outb(curscb, p->base + SCBPTR);
+  scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  scb->flags &= ~SCB_ACTIVE;
   scb->cmd->result = (DID_RESET << 16);
 
   return (next);
@@ -1777,6 +2134,75 @@
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_search_qinfifo
+ *
+ * Description:
+ *   Search the queue-in FIFO for matching SCBs and conditionally
+ *   requeue.  Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
+    int lun, unsigned char tag, int flags, int requeue)
+{
+  unsigned char saved_queue[AIC7XXX_MAXSCB];
+  int      queued = inb(p->base + QINCNT) & p->qcntmask;
+  int      i;
+  int      found;
+  struct aic7xxx_scb *scbp;
+  scb_queue_type removed_scbs;
+
+  found = 0;
+  scbq_init (&removed_scbs);
+  for (i = 0; i < (queued - found); i++)
+  {
+    saved_queue[i] = inb(p->base + QINFIFO);
+    scbp = p->scb_data->scb_array[saved_queue[i]];
+    if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+    {
+       /*
+        * We found an scb that needs to be removed.
+        */
+       if (requeue)
+       {
+         scbq_insert_head(&removed_scbs, scbp);
+       }
+       else
+       {
+         scbp->flags = flags;
+         scbp->flags &= ~SCB_ACTIVE;
+         /*
+          * XXX - Don't know what error to use here.
+          */
+         aic7xxx_error(scbp->cmd) = DID_RESET;
+       }
+       i--;
+       found++;
+    }
+  }
+  /* Now put the saved scbs back. */
+  for (queued = 0; queued < i; queued++)
+    outb(saved_queue[queued], p->base + QINFIFO);
+
+  if (requeue)
+  {
+    scbp = removed_scbs.head;
+    while (scbp != NULL)
+    {
+      scbq_remove_head(&removed_scbs);
+      /*
+       * XXX - Shouldn't we be adding this to the free list?
+       */
+      scbq_insert_head(&p->waiting_scbs, scbp);
+      scbp->flags |= SCB_WAITINGQ;
+      scbp = removed_scbs.head;
+    }
+  }
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_reset_device
  *
  * Description:
@@ -1784,131 +2210,280 @@
  *   all active and queued scbs for that target/channel.
  *-F*************************************************************************/
 static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+                     int lun, unsigned char tag)
 {
-  int base = p->base;
-  struct aic7xxx_scb *scb;
+  struct aic7xxx_scb *scbp;
   unsigned char active_scb;
   int i = 0;
-  int found = 0;
+  int found;
 
   /*
    * Restore this when we're done
    */
-  active_scb = inb(SCBPTR + base);
+  active_scb = inb(p->base + SCBPTR);
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n",
-         target, channel, active_scb);
+  printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
+         p->host_no, target, CHAN_TO_INT(channel), active_scb);
 #endif
+
   /*
-   * Search the QINFIFO.
+   * Deal with the busy target and linked next issues.
    */
   {
-    int saved_queue[AIC7XXX_MAXSCB];
-    int queued = inb(QINCNT + base) & p->qcntmask;
+    int min_target, max_target;
+    unsigned char busy_scbid;
 
-    for (i = 0; i < (queued - found); i++)
+    /* Make all targets 'relative' to bus A. */
+    if (target == ALL_TARGETS)
     {
-      saved_queue[i] = inb(QINFIFO + base);
-      outb(saved_queue[i], SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      if (aic7xxx_match_scb(scb, target, channel))
+      switch (channel)
       {
-        /*
-         * We found an scb that needs to be aborted.
-         */
-#ifdef AIC7XXX_DEBUG_ABORT
-        printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n",
-               saved_queue[i], TCL_OF_SCB(scb));
-#endif
-        scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-        scb->cmd->result = (DID_RESET << 16);
-        outb(0, SCB_CONTROL + base);
-        i--;
-        found++;
+        case 'A':
+  	  min_target = 0;
+  	  max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+  	  break;
+        case 'B':
+  	  min_target = 8;
+  	  max_target = 15;
+  	  break;
+        case ALL_CHANNELS:
+        default:
+  	  min_target = 0;
+  	  max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+  	  break;
       }
     }
-    /*
-     * Now put the saved scbs back.
-     */
-    for (queued = 0; queued < i; queued++)
+    else
+    { 
+      min_target = target + channel == 'B' ? 8 : 0;
+      max_target = min_target;
+    }
+
+    for (i = min_target; i <= max_target; i++)
     {
-      outb(saved_queue[queued], QINFIFO + base);
+      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
+      if (busy_scbid < p->scb_data->numscbs)
+      {
+  	struct aic7xxx_scb *busy_scb;
+  	struct aic7xxx_scb *next_scb;
+  	unsigned char next_scbid;
+
+  	busy_scb = p->scb_data->scb_array[busy_scbid];
+  
+  	next_scbid = busy_scb->hscb->data_count >> 24;
+
+  	if (next_scbid == SCB_LIST_NULL)
+        {
+  	  busy_scbid = aic7xxx_find_scb(p, busy_scb);
+
+  	  if (busy_scbid != SCB_LIST_NULL)
+          {
+  	    outb(busy_scbid, p->base + SCBPTR);
+  	    next_scbid = inb(p->base + SCB_LINKED_NEXT);
+  	  }
+  	}
+
+  	if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
+        {
+  	  aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
+  	}
+
+  	if (next_scbid != SCB_LIST_NULL)
+        {
+  	  next_scb = p->scb_data->scb_array[next_scbid];
+  	  if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+  	    continue;
+          }
+  	  /* Requeue for later processing */
+  	  scbq_insert_head(&p->waiting_scbs, next_scb);
+  	  next_scb->flags |= SCB_WAITINGQ;
+  	}
+      }
     }
   }
 
+  found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+      SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
+
   /*
    * Search waiting for selection list.
    */
   {
-    unsigned char next, prev;
+    unsigned char next, prev, scb_index;
 
-    next = inb(WAITING_SCBH + base);  /* Start at head of list. */
+    next = inb(p->base + WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
 
     while (next != SCB_LIST_NULL)
     {
-      outb(next, SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      /*
-       * Select the SCB.
-       */
-      if (aic7xxx_match_scb(scb, target, channel))
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index >= p->scb_data->numscbs)
       {
-        next = aic7xxx_abort_waiting_scb(p, scb, prev);
+        panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
+              scb_index, p->scb_data->numscbs);
+      }
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        unsigned char linked_next;
+
+        next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+        if (linked_next != SCB_LIST_NULL)
+        {
+          struct aic7xxx_scb *next_scb;
+          /*
+           * Requeue the waiting SCB via the waiting list.
+           */
+          next_scb = p->scb_data->scb_array[linked_next];
+          if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+            scbq_insert_head(&p->waiting_scbs, next_scb);
+            next_scb->flags |= SCB_WAITINGQ;
+          }
+        }
         found++;
       }
       else
       {
         prev = next;
-        next = inb(SCB_NEXT + base);
+        next = inb(p->base + SCB_NEXT);
       }
     }
   }
 
   /*
-   * 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.
+   * Go through disconnected list and remove any entries we have queued
+   * for completion, zeroing their control byte too.
    */
-  for (i = 0; i < p->scb_link->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+    unsigned char next, prev, scb_index;
+
+    next = inb(p->base + DISCONNECTED_SCBH);
+    prev = SCB_LIST_NULL;
+
+    while (next != SCB_LIST_NULL)
     {
-      /*
-       * Ensure the target is "free"
-       */
-      aic7xxx_unbusy_target(target, channel, base);
-      if (! (scb->state & SCB_PAGED_OUT))
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index > p->scb_data->numscbs)
+      {
+        panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
+              "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
+      }
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        next = aic7xxx_rem_scb_from_disc_list(p, next);
+      }
+      else
+      {
+        prev = next;
+        next = inb(p->base + SCB_NEXT);
+      }
+    }
+  }
+
+  /*
+   * Go through the hardware SCB array looking for commands that
+   * were active but not on any list.
+   */
+  for (i = 0; i < p->scb_data->maxhscbs; i++)
+  {
+    unsigned char scbid;
+
+    outb(i, p->base + SCBPTR);
+    scbid = inb(p->base + SCB_TAG);
+    if (scbid < p->scb_data->numscbs)
+    {
+      scbp = p->scb_data->scb_array[scbid];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
       {
-        outb(scb->position, SCBPTR + base);
-        outb(0, SCB_CONTROL + base);
+        aic7xxx_add_curscb_to_free_list(p);
       }
-      scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-      scb->cmd->result = (DID_RESET << 16);
+    }
+  }
+
+  /*
+   * Go through the entire SCB array now and look for commands for
+   * for this target that are stillactive.  These are other (most likely
+   * tagged) commands that were disconnected when the reset occurred.
+   */
+  for (i = 0; i < p->scb_data->numscbs; i++)
+  {
+    scbp = p->scb_data->scb_array[i];
+    if (((scbp->flags & SCB_ACTIVE) != 0) &&
+        aic7xxx_match_scb(scbp, target, channel, lun, tag))
+    {
+      scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+      scbp->flags &= ~SCB_ACTIVE;
+      aic7xxx_error(scbp->cmd) = DID_RESET;
+
       found++;
+
+      if ((scbp->flags & SCB_WAITINGQ) != 0)
+      {
+        scbq_remove(&p->waiting_scbs, scbp);
+        scbp->flags &= ~SCB_WAITINGQ;
+      }
     }
   }
 
-  outb(active_scb, SCBPTR + base);
+  outb(active_scb, p->base + SCBPTR);
   return (found);
 }
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_clear_intstat
+ *
+ * Description:
+ *   Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+  /* Clear any interrupt conditions this may have caused. */
+  outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0);
+  outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+       CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1);
+  outb(CLRSCSIINT, p->base + CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_reset_current_bus
  *
  * Description:
  *   Reset the current SCSI bus.
  *-F*************************************************************************/
 static void
-aic7xxx_reset_current_bus(int base)
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
 {
-  outb(SCSIRSTO, SCSISEQ + base);
+  unsigned char scsiseq;
+
+  /* Disable reset interrupts. */
+  outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+
+  /* Turn on the bus reset. */
+  scsiseq = inb(p->base + SCSISEQ);
+  outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+
+  udelay(1000);
+
+  /* Turn off the bus reset. */
+  outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+
+  aic7xxx_clear_intstat(p);
+
+  /* Re-enable reset interrupts. */
+  outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+
   udelay(1000);
-  outb(0, SCSISEQ + base);
 }
 
 /*+F*************************************************************************
@@ -1921,25 +2496,24 @@
 static int
 aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
 {
-  int base = p->base;
-  unsigned char sblkctl;
-  char cur_channel;
   unsigned long offset, offset_max;
   int found;
+  unsigned char sblkctl;
+  char cur_channel;
 
+  pause_sequencer(p);
   /*
-   * Clean up all the state information for the
-   * pending transactions on this bus.
+   * Clean up all the state information for the pending transactions
+   * on this bus.
    */
-  found = aic7xxx_reset_device(p, ALL_TARGETS, channel);
+  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
   if (channel == 'B')
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
     p->sdtr_pending &= 0x00FF;
-    outb(0, ACTIVE_B + base);
-    offset = TARG_SCRATCH + base + 8;
-    offset_max = TARG_SCRATCH + base + 16;
+    offset = TARG_SCRATCH + 8;
+    offset_max = TARG_SCRATCH + 16;
   }
   else
   {
@@ -1949,132 +2523,100 @@
       p->needwdtr = p->needwdtr_copy;
       p->sdtr_pending = 0x0;
       p->wdtr_pending = 0x0;
-      outb(0, ACTIVE_A + base);
-      outb(0, ACTIVE_B + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 16;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 16;
     }
     else
     {
+      /* Channel A */
       p->needsdtr |= (p->needsdtr_copy & 0x00FF);
       p->sdtr_pending &= 0xFF00;
-      outb(0, ACTIVE_A + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 8;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 8;
     }
   }
+
   while (offset < offset_max)
   {
     /*
-     * Revert to async/narrow transfers
-     * until we renegotiate.
+     * Revert to async/narrow transfers until we renegotiate.
      */
     u_char targ_scratch;
-    targ_scratch = inb(offset);
+
+    targ_scratch = inb(p->base + offset);
     targ_scratch &= SXFR;
-    outb(targ_scratch, offset);
+    outb(targ_scratch, p->base + offset);
     offset++;
   }
 
   /*
    * Reset the bus and unpause/restart the controller
    */
-
-  /*
-   * Case 1: Command for another bus is active
-   */
-  sblkctl = inb(SBLKCTL + base);
+  sblkctl = inb(p->base + SBLKCTL);
   cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
   if (cur_channel != channel)
   {
+    /*
+     * Case 1: Command for another bus is active
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
-           channel);
+    printk("scsi%d: Stealthily resetting channel %c\n",
+           p->host_no, channel);
 #endif
     /*
-     * Stealthily reset the other bus without upsetting the current bus
+     * Stealthily reset the other bus without upsetting the current bus.
      */
-    outb(sblkctl ^ SELBUSB, SBLKCTL + base);
+    outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    outb(sblkctl, SBLKCTL + base);
-
-    UNPAUSE_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    outb(sblkctl, p->base + SBLKCTL);
+    unpause_sequencer(p, /* unpause_always */ FALSE);
   }
-  /*
-   * Case 2: A command from this bus is active or we're idle
-   */
   else
   {
+    /*
+     * Case 2: A command from this bus is active or we're idle.
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Resetting current channel %c\n",
-           channel);
+    printk("scsi%d: Resetting current channel %c\n",
+           p->host_no, channel);
 #endif
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+#if 0
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
+#endif
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    RESTART_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    restart_sequencer(p);
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
+    printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
 #endif
   }
 
   /*
-   * Cause the mid-level SCSI code to delay any further 
-   * queueing by the bus settle time for us.
-   */
-  p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-
-  /*
    * 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;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_page_scb
- *
- * Description:
- *   Swap in_scbp for out_scbp down in the cards SCB array.
- *   We assume that the SCB for out_scbp is already selected in SCBPTR.
- *
- *-F*************************************************************************/
-static inline void
-aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp,
-    struct aic7xxx_scb *in_scbp)
-{
-  int index;
-
-  /* Page-out */
-#if 0
-printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
-       out_scbp->cmd->target, in_scbp->cmd->target);
-#endif
-  aic7xxx_getscb(p, out_scbp);
-  out_scbp->state |= SCB_PAGED_OUT;
-  if (!(out_scbp->control & TAG_ENB))
-  {
-    /* Stick in non-tagged array */
-    index = (out_scbp->target_channel_lun >> 4) | 
-            (out_scbp->target_channel_lun & SELBUSB);
-    p->pagedout_ntscbs[index] = out_scbp;
-  }
-
-  /* Page-in */
-  in_scbp->position = out_scbp->position;
-  out_scbp->position = SCB_LIST_NULL;
-  aic7xxx_putscb(p, in_scbp);
-  in_scbp->state &= ~SCB_PAGED_OUT;
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  return (found);
 }
 
 /*+F*************************************************************************
@@ -2082,1159 +2624,1339 @@
  *   aic7xxx_run_waiting_queues
  *
  * Description:
- *   Scan the assigned_scbs and waiting_scbs queues.  For scbs in the
- *   assigned_scbs queue, we download and start them.  For scbs in the
- *   waiting_scbs queue, we page in as many as we can being careful
- *   not to cause a deadlock for a reconnecting target.
- *
+ *   Scan the awaiting_scbs queue downloading and starting as many
+ *   scbs as we can.
  *-F*************************************************************************/
 static inline void
 aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb *scb;
-  u_char cur_scb, intstat;
-  u_long base = p->base;
-  long flags;
 
-  if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL))
+  if (p->waiting_scbs.head == NULL)
     return;
 
-  save_flags(flags);
-  cli();
-
-  PAUSE_SEQUENCER(p);
-  cur_scb = inb(SCBPTR + base);
-  intstat = inb(INTSTAT + base);
-
+  pause_sequencer(p);
   /*
    * First handle SCBs that are waiting but have been assigned a slot.
    */
-  scb = p->assigned_scbs.head;
-  while (scb != NULL)
-  {
-    scbq_remove_head(&(p->assigned_scbs));
-    outb(scb->position, SCBPTR + base);
-    aic7xxx_putscb(p, scb);
-    /* Mark this as an active command. */
-    scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE;
-    outb(scb->position, QINFIFO + base);
-    scb = p->assigned_scbs.head;
-  }
-
-  /* Now deal with SCBs that require paging. */
   scb = p->waiting_scbs.head;
-  if (scb != NULL)
+  while (scb != NULL)
   {
-    u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-    u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN);
-    int count = 0;
-    u_char next_scb;
-
-    while (scb != NULL)
+    if (p->curqincnt >= p->qfullcount)
     {
-      /* Attempt to page this SCB in */
-      if (disc_scb == SCB_LIST_NULL)
-        break;
-
-      /*
-       * Advance disc_scb to the next one in the list.
-       */
-      outb(disc_scb, SCBPTR + base);
-      next_scb = inb(SCB_NEXT + base); 
-
-      /*
-       * We have to be careful about when we allow an SCB to be paged out. 
-       * There must always be at least one slot availible for a reconnecting
-       * target in case it references an SCB that has been paged out.  Our
-       * heuristic is that either the disconnected list has at least two
-       * entries in it or there is one entry and the sequencer is activily
-       * working on an SCB which implies that it will either complete or
-       * disconnect before another reconnection can occur.
-       */
-      if ((next_scb != SCB_LIST_NULL) || active)
+      p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
+      if (p->curqincnt >= p->qfullcount)
       {
-        u_char out_scbi;
-        struct aic7xxx_scb *out_scbp;
-
-        scbq_remove_head(&(p->waiting_scbs));
-
-        /*
-         * Find the in-core SCB for the one we're paging out.
-         */
-        out_scbi = inb(SCB_TAG + base); 
-        out_scbp = (p->scb_array[out_scbi]);
-
-        /* Do the page out and mark the paged in SCB as active. */
-        aic7xxx_page_scb(p, out_scbp, scb);
-
-        /* Mark this as an active command. */
-        scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE;
-
-        /* Queue the command */
-        outb(scb->position, QINFIFO + base);
-        count++;
-
-        /* Advance to the next disconnected SCB */
-        disc_scb = next_scb;
-        scb = p->waiting_scbs.head;
+        break;
       }
-      else
-        scb = NULL;
     }
 
-    if (count)
+    /*
+     * We have some space.
+     */
+    scbq_remove_head(&(p->waiting_scbs));
+    scb->flags &= ~SCB_WAITINGQ;
+
+    outb(scb->hscb->tag, p->base + QINFIFO);
+
+    if ((p->flags & PAGE_ENABLED) != 0)
     {
-      /* 
-       * Update the head of the disconnected list.
+      /*
+       * We only care about this statistic when paging
+       * since it's impossible to overflow the qinfifo
+       * in the non-paging case.
        */
-      outb(disc_scb, DISCONNECTED_SCBH + base);
-      if (disc_scb != SCB_LIST_NULL)
-      {
-        outb(disc_scb, SCBPTR + base);
-        outb(SCB_LIST_NULL, SCB_PREV + base);
-      }
+      p->curqincnt++;
     }
+    scb = p->waiting_scbs.head;
   }
-  /* Restore old position */
-  outb(cur_scb, SCBPTR + base);
 
-  /*
-   * Guard against unpausing the sequencer if there is an interrupt
-   * waiting to happen.
-   */
-  if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
+  unpause_sequencer(p, FALSE);
+}
 
-  restore_flags(flags);
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_construct_sdtr
+ *
+ * Description:
+ *   Constucts a synchronous data transfer message in the message
+ *   buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char period, unsigned char offset)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_SDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(period,           p->base + MSG_OUT + 3 + start_byte);
+  outb(offset,           p->base + MSG_OUT + 4 + start_byte);
+  outb(start_byte + 5,   p->base + MSG_LEN);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_isr
+ *   aic7xxx_construct_wdtr
  *
  * Description:
- *   SCSI controller interrupt handler.
+ *   Constucts a wide data transfer message in the message buffer
+ *   on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char bus_width)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_WDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(bus_width,        p->base + MSG_OUT + 3 + start_byte);
+  outb(start_byte + 4,   p->base + MSG_LEN);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_calc_residual
  *
- *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
- *         be disabled all through this function unless we say otherwise.
+ * Description:
+ *   Calculate the residual data not yet transferred.
  *-F*************************************************************************/
 static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  int base, intstat, actual, scb_index, run_aborted_queue = FALSE;
-  struct aic7xxx_host *p;
-  struct aic7xxx_scb *scb = NULL;
-  short         transfer;
-  unsigned char ha_flags, scsi_id, bus_width;
-  unsigned char offset, rate, scratch, scratch_offset;
-  unsigned char max_offset, rej_byte;
-  unsigned short target_mask;
-  char channel;
-  unsigned int addr; /* must be 32 bits */
+  struct aic7xxx_hwscb *hscb;
   Scsi_Cmnd *cmd;
+  int actual;
 
-  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+  cmd = scb->cmd;
+  hscb = scb->hscb;
 
   /*
-   * Search for the host with a pending interrupt.  If we can't find
-   * one, then we've encountered a spurious interrupt.
+   *  Don't destroy valid residual information with
+   *  residual coming from a check sense operation.
    */
-  while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
+  if (((scb->hscb->control & DISCONNECTED) == 0) &&
+      (scb->flags & SCB_SENSE) == 0)
   {
-    if (p->next == NULL)
-    {
-      p = NULL;
-    }
-    else
+    /*
+     *  We had an underflow. At this time, there's only
+     *  one other driver that bothers to check for this,
+     *  and cmd->underflow seems to be set rather half-
+     *  heartedly in the higher-level SCSI code.
+     */
+    actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count);
+
+    actual -= (hscb->residual_data_count[2] << 16) |
+              (hscb->residual_data_count[1] <<  8) |
+              hscb->residual_data_count[0];
+
+    if (actual < cmd->underflow)
     {
-      p = (struct aic7xxx_host *) p->next->hostdata;
+      printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+             "Wanted at least %u, got %u, residual SG count %d.\n",
+             p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+             hscb->residual_SG_segment_count);
+      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+      aic7xxx_status(cmd) = hscb->target_status;
     }
   }
 
-  if (p == NULL)
-    return;
-
   /*
-   * Keep track of interrupts for /proc/scsi
+   * Clean out the residual information in the SCB for the
+   * next consumer.
    */
-  p->isr_count++;
+  hscb->residual_data_count[2] = 0;
+  hscb->residual_data_count[1] = 0;
+  hscb->residual_data_count[0] = 0;
+  hscb->residual_SG_segment_count = 0;
+}
 
-  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_device_reset
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+{
+  unsigned short targ_mask;
+  unsigned char  targ_scratch;
+  int scratch_offset = target;
+  int found;
+
+  if (channel == 'B')
   {
-    /*
-     * We must only have one card at this IRQ and it must have been
-     * added to the board data before the spurious interrupt occurred.
-     * It is sufficient that we check isr_count and not the spurious
-     * interrupt count.
-     */
-    printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
-    return;
+    scratch_offset += 8;
   }
-
-  base = p->base;
+  targ_mask = (0x01 << scratch_offset);
   /*
-   * Handle all the interrupt sources - especially for SCSI
-   * interrupts, we won't get a second chance at them.
+   * Go back to async/narrow transfers and renegotiate.
    */
-  intstat = inb(INTSTAT + base);
+  p->needsdtr |= p->needsdtr_copy & targ_mask;
+  p->needwdtr |= p->needwdtr_copy & targ_mask;
+  p->sdtr_pending &= ~targ_mask;
+  p->wdtr_pending &= ~targ_mask;
+  targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+  targ_scratch &= SXFR;
+  outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+  found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+  printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
+         "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
 
-  /*
-   * Indicate that we're in the interrupt handler.
-   */
-  p->flags |= IN_ISR;
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_seqint
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  struct aic7xxx_scb *scb;
+  unsigned short target_mask;
+  unsigned char target, scratch_offset;
+  char channel;
 
-  if (intstat & BRKADRINT)
+  if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
   {
-    int i;
-    unsigned char errno = inb(ERROR + base);
-
-    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
-    for (i = 0; i < NUMBER(hard_error); i++)
-    {
-      if (errno & hard_error[i].errno)
-      {
-        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
-      }
-    }
-    panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
-          inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
+    target = (inb(p->base + SELID) >> 4) & 0x0F;
   }
-
-  if (intstat & SEQINT)
+  else
   {
-    /*
-     * Although the sequencer is paused immediately on
-     * a SEQINT, an interrupt for a SCSIINT condition will
-     * unpaused the sequencer before this point.
-     */
-    PAUSE_SEQUENCER(p);
+    target = (inb(p->base + SCSIID) >> 4) & 0x0F;
+  }
+  scratch_offset = target;
+  channel = 'A';
+  if (inb(p->base + SBLKCTL) & SELBUSB)
+  {
+    channel = 'B';
+    scratch_offset += 8;
+  }
+  target_mask = (0x01 << scratch_offset);
 
-    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 NO_MATCH:
+      {
+        /*
+         * This could be for a normal abort request.  Figure out
+         * which SCB we were trying to find and only give an error
+         * if we didn't ask for this to happen.
+         */
+        unsigned char scb_index;
+        unsigned char busy_scbid;
+        unsigned char arg1;
+
+        busy_scbid = aic7xxx_index_busy_target(p, target, channel,
+            /*unbusy*/ FALSE);
+        arg1 = inb(p->base + ARG_1);
 
-    switch (intstat & SEQINT_MASK)
-    {
-      case NO_MATCH:
-    	if (p->flags & PAGE_ENABLED)
+        if (arg1 == SCB_LIST_NULL)
         {
-    	  /* SCB Page-in request */
-    	  struct aic7xxx_scb *outscb;
-    	  u_char arg_1 = inb(ARG_1 + base);
-          int use_disconnected = FALSE;
-
-          /*
-           * The sequencer expects this value upon return.  Assume
-           * we will find the paged out SCB and set the value now.
-           * If we don't, and one of the methods used to acquire an
-           * SCB calls aic7xxx_done(), we will end up in our queue
-           * routine and unpause the sequencer without giving it the
-           * correct return value, which causes a hang.
-           */
-    	  outb(SCB_PAGEDIN, RETURN_1 + base);
-    	  if (arg_1 == SCB_LIST_NULL)
-          {
-    	    /* Non-tagged command */
-    	    int index = scsi_id;
-            if (channel == 'B')
-            {
-              index |= SELBUSB;
-            }
-    	    scb = p->pagedout_ntscbs[index];
-    	  }
-    	  else
-    	    scb = (p->scb_array[arg_1]);
+          /* untagged request */
+          scb_index = busy_scbid;
+        }
+        else
+        {
+          scb_index = arg1;
+        }
 
-          if (!(scb->state & SCB_PAGED_OUT))
+        if (scb_index < p->scb_data->numscbs)
+        {
+          scb = p->scb_data->scb_array[scb_index];
+          if (scb->hscb->control & ABORT_SCB)
           {
-  	    printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting "
-  	  	  "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		  p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-    	    aic7xxx_unbusy_target(scsi_id, channel, base);
-  	    outb(CLRSELTIMEO, CLRSINT1 + base);
-            outb(0, RETURN_1 + base);
+            /*
+             * We expected this.  Let the busfree handler take care
+             * of this when we the abort is finially sent.  Set
+             * IDENTIFY_SEEN so that the busfree handler knows that
+             * there is an SCB to cleanup.
+             */
+            outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
+            printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
+                   p->host_no, TC_OF_SCB(scb));
             break;
           }
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+               "target - Issuing BUS DEVICE RESET.\n",
+               p->host_no, target, CHAN_TO_INT(channel));
+
+        printk(KERN_WARNING "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+               inb(p->base + SAVED_TCL), arg1,
+               (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+        aic7xxx_handle_device_reset(p, target, channel);
+      }
+      break;
 
-    	  /*
-    	   * Now to pick the SCB to page out.  Either take a free SCB, an
-           * assigned SCB, an SCB that just completed, or the first one
-           * on the disconnected SCB list.
-    	   */
-    	  if (p->scb_link->free_scbs.head != NULL)
-          {
-    	    outscb = p->scb_link->free_scbs.head;
-    	    scbq_remove_head(&p->scb_link->free_scbs);
-    	    scb->position = outscb->position;
-    	    outscb->position = SCB_LIST_NULL;
-    	    scbq_insert_head(&p->page_scbs, outscb);
-    	    outb(scb->position, SCBPTR + base);
-    	    aic7xxx_putscb(p, scb);
-    	    scb->state &= ~SCB_PAGED_OUT;
-    	  }
-    	  else if (p->assigned_scbs.head != NULL)
-          {
-            outscb = p->assigned_scbs.head;
-            scbq_remove_head(&p->assigned_scbs);
-            scb->position = outscb->position;
-            outscb->position = SCB_LIST_NULL;
-            scbq_insert_head(&p->waiting_scbs, outscb);
-            outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ;
-            outb(scb->position, SCBPTR + base);
-    	    aic7xxx_putscb(p, scb);
-            scb->state &= ~SCB_PAGED_OUT;
-          }
-          else if (intstat & CMDCMPLT)
+    case NO_MATCH_BUSY:
+      {
+        /*
+         * XXX - Leave this as a panic for the time being since it
+         * indicates a bug in the timeout code for this to happen.
+         */
+        unsigned char scb_index;
+
+        scb_index = inb(p->base + CUR_SCBID);
+        scb = p->scb_data->scb_array[scb_index];
+
+        panic("scsi%d:  Target %d, channel %c, Target busy link failure, "
+              "but busy SCB exists!\n",
+              p->host_no, target, channel);
+      }
+      break;
+
+    case SEND_REJECT:
+      {
+        unsigned char rej_byte;
+
+        rej_byte = inb(p->base + REJBYTE);
+        printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
+               "received from target, SEQ_FLAGS=0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel), rej_byte,
+               inb(p->base + SEQ_FLAGS));
+      }
+      break;
+
+    case NO_IDENT:
+      {
+        /*
+         * The reconnecting target either did not send an identify
+         * message, or did, but we didn't find and SCB to match and
+         * before it could respond to our ATN/abort, it hit a dataphase.
+         * The only safe thing to do is to blow it away with a bus
+         * reset.
+         */
+        int found;
+
+        printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
+               "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel),
+               inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
+
+        found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+
+        printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
+     	       "%d SCBs aborted\n", p->host_no, channel, found);
+      }
+      break;
+
+    case BAD_PHASE:
+      if (inb(p->base + LASTPHASE) == P_BUSFREE)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
+               p->host_no, CHAN_TO_INT(channel), target);
+        restart_sequencer(p);
+      }
+      else
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
+               "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+      }
+      break;
+
+    case EXTENDED_MSG:
+      {
+	unsigned char message_length;
+	unsigned char message_code;
+        unsigned char scb_index;
+
+	message_length = inb(p->base + MSGIN_EXT_LEN);
+	message_code = inb(p->base + MSGIN_EXT_OPCODE);
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+
+	switch (message_code)
+	{
+          case MSG_EXT_SDTR:
           {
-            int scb_index;
+            unsigned char period;
+            unsigned char offset;
+            unsigned char saved_offset;
+            unsigned char targ_scratch;
+            unsigned char max_offset;
+            unsigned char rate;
 
-            outb(CLRCMDINT, CLRINT + base);
-            scb_index = inb(QOUTFIFO + base);
-            if (!(inb(QOUTCNT + base) & p->qcntmask))
-            {
-              intstat &= ~CMDCMPLT;
-            }
-            outscb = (p->scb_array[scb_index]);
-            if (!(outscb->state & SCB_ACTIVE))
+            if (message_length != MSG_EXT_SDTR_LEN)
             {
-	      printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
-	             "during NO_MATCH interrupt\n", scb_index, p->host_no);
-              use_disconnected = TRUE;
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
             }
+
+            period = inb(p->base + MSGIN_EXT_BYTES);
+            saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1);
+            targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+            if (targ_scratch & WIDEXFER)
+              max_offset = MAX_OFFSET_16BIT;
             else
+              max_offset = MAX_OFFSET_8BIT;
+            offset = MIN(saved_offset, max_offset);
+
+            aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+
+            /*
+             * Preserve the WideXfer flag.
+             */
+            targ_scratch = rate | (targ_scratch & WIDEXFER);
+
+            /*
+             * Update both the target scratch area and current SCSIRATE.
+             */
+            outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(targ_scratch, p->base + SCSIRATE);
+
+            /*
+             * See if we initiated Sync Negotiation and didn't have
+             * have to fall down to async transfers.
+             */
+            if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
             {
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              outb(scb->position, SCBPTR + base);
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
-              outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16);
-              if ((outscb->cmd->flags & WAS_SENSE) && 
-                 !(outscb->cmd->flags & ASKED_FOR_SENSE))
+              /* We started it. */
+              if (saved_offset == offset)
               {
-                /*
-                 * Got sense information.
-                 */
-	        outscb->cmd->flags &= ASKED_FOR_SENSE;
+        	/*
+        	 * Don't send an SDTR back to the target.
+        	 */
+        	outb(0, p->base + RETURN_1);
+              }
+              else
+              {
+        	/* We went too low - force async. */
+        	outb(SEND_REJ, p->base + RETURN_1);
               }
-              p->device_status[TARGET_INDEX(outscb->cmd)].flags
-                |= DEVICE_SUCCESS;
-              aic7xxx_done(p, outscb);
             }
+            else
+            {
+              /*
+               * Send our own SDTR in reply.
+               *
+               * We want to see this message as we don't expect a target
+               * to send us a SDTR request first.
+               */
+              printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+              aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
+              outb(SEND_MSG, p->base + RETURN_1);
+            }
+            /*
+             * Clear the flags.
+             */
+            p->needsdtr &= ~target_mask;
+            break;
           }
-          else
-          {
-            use_disconnected = TRUE;
-          }
-          if (use_disconnected)
+
+          case MSG_EXT_WDTR:
           {
-    	    u_char tag;
-    	    u_char next;
-    	    u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-    	    if (disc_scb != SCB_LIST_NULL)
+            unsigned char scratch, bus_width;
+
+            if (message_length != MSG_EXT_WDTR_LEN)
             {
-    	      outb(disc_scb, SCBPTR + base);
-    	      tag = inb(SCB_TAG + base);
-    	      outscb = (p->scb_array[tag]);
-    	      next = inb(SCB_NEXT + base);
-    	      if (next != SCB_LIST_NULL)
-              {
-    	        outb(next, SCBPTR + base);
-    	        outb(SCB_LIST_NULL, SCB_PREV + base);
-    	        outb(disc_scb, SCBPTR + base);
-    	      }
-    	      outb(next, DISCONNECTED_SCBH + base);
-    	      aic7xxx_page_scb(p, outscb, scb);
-    	    }
-            else if (inb(QINCNT + base) & p->qcntmask)
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
+            }
+
+            bus_width = inb(p->base + MSGIN_EXT_BYTES);
+            scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+            if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
             {
-              /* Pull one of our queued commands as a last resort. */
-              disc_scb = inb(QINFIFO + base);
-              outb(disc_scb, SCBPTR + base);
-              tag = inb(SCB_TAG + base);
-              outscb = (p->scb_array[tag]);
-              if ((outscb->control & 0x23) != TAG_ENB)
+              /*
+               * Don't send an WDTR back to the target, since we asked first.
+               */
+              outb(0, p->base + RETURN_1);
+              switch (bus_width)
               {
-                /*
-                 * This is not a simple tagged command so its position
-                 * in the queue matters.  Take the command at the end of
-                 * the queue instead.
-                 */
-                int i;
-                int saved_queue[AIC7XXX_MAXSCB];
-                int queued = inb(QINCNT + base) & p->qcntmask;
-
-                /* Count the command we removed already */
-                saved_queue[0] = disc_scb;
-                queued++;
-
-                /* Empty the input queue. */
-                for (i = 1; i < queued; i++)
-                {
-                  saved_queue[i] = inb(QINFIFO + base);
-                }
-
-                /* Put everyone back but the last entry. */
-                queued--;
-                for (i = 0; i < queued; i++)
-                {
-                  outb(saved_queue[i], QINFIFO + base);
-                }
-
-                outb(saved_queue[queued], SCBPTR + base);
-                tag = inb(SCB_TAG + base);
-                outscb = (p->scb_array[tag]);
+        	case BUS_8_BIT:
+        	  scratch &= 0x7F;
+        	  break;
+
+        	case BUS_16_BIT:
+                  if (aic7xxx_verbose)
+                  {
+        	    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+  	  	         "bit transfers.\n", p->host_no, target, channel);
+                  }
+        	  scratch |= WIDEXFER;
+        	  break;
+
+        	case BUS_32_BIT:
+        	  outb(SEND_REJ, p->base + RETURN_1);
+                  /* No verbose here!  We want to see this condition. */
+        	  printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+  			"requesting 32 bit transfers, rejecting...\n",
+                	 p->host_no, target, channel);
+        	  break;
+
+        	default:
+        	  break;
               }
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              scbq_insert_head(&p->waiting_scbs, outscb);
-              outscb->state |= SCB_WAITINGQ;
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
             }
             else
             {
-  	      printk(KERN_WARNING "scsi%d: Page-in request with no candidates "
-  	  	    "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		    p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-              aic7xxx_unbusy_target(scsi_id, channel, base);
-              outb(CLRSELTIMEO, CLRSINT1 + base);
-              outb(0, RETURN_1 + base);
+              /*
+               * Send our own WDTR in reply.
+               */
+              switch (bus_width)
+              {
+        	case BUS_8_BIT:
+        	  scratch &= 0x7F;
+        	  break;
+
+        	case BUS_32_BIT:
+        	case BUS_16_BIT:
+        	  if (p->bus_type == AIC_WIDE)
+        	  {
+                    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+  			   "bit transfers.\n", p->host_no, target, channel);
+                    bus_width = BUS_16_BIT;
+                    scratch |= WIDEXFER;
+        	  }
+        	  else
+        	  {
+                    bus_width = BUS_8_BIT;
+                    scratch &= 0x7F;  /* XXX - FreeBSD doesn't do this. */
+        	  }
+        	  break;
+
+        	default:
+        	  break;
+              }
+              aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
+              outb(SEND_MSG, p->base + RETURN_1);
             }
-          }
-    	}
-    	else
-        {
-  	  printk(KERN_WARNING "scsi%d: No active SCB for reconnecting "
-  	  	"target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-    	  aic7xxx_unbusy_target(scsi_id, channel, base);
-    	  outb(0, SCB_CONTROL + base);
-  	  outb(CLRSELTIMEO, CLRSINT1 + base);
-          outb(0, RETURN_1 + base);
-        }
-  	break;
-
-      case BAD_PHASE:
-	panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
-	break;
-
-      case SEND_REJECT:
-        rej_byte = inb(REJBYTE + base);
-        if ((rej_byte & 0xF0) == 0x20)
-        {
-          scb_index = inb(SCB_TAG + base);
-          scb = (p->scb_array[scb_index]);
-          printk(KERN_WARNING "scsi%d: Tagged message received without identify."
-                 "Disabling tagged commands for target %d channel %c.\n",
-                  p->host_no, scsi_id, channel);
-          scb->cmd->device->tagged_supported = 0;
-          scb->cmd->device->tagged_queue = 0;
-        }
-        else
-        {
-          printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received "
-                 "from target %d channel %c.\n",
-                 p->host_no, rej_byte, scsi_id, channel);
-        }
-	break;
-
-      case NO_IDENT:
-	panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY "
-	      "message. SAVED_TCL 0x%x.\n",
-              p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-	break;
+            p->needwdtr &= ~target_mask;
+            outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(scratch, p->base + SCSIRATE);
+            break;
+	  }  /* case MSG_EXT_WDTR */
 
-      case SDTR_MSG:
-	/*
-	 * Help the sequencer to translate the negotiated
-	 * transfer rate. Transfer is 1/4 the period
-	 * in ns as is returned by the sync negotiation
-	 * message. So, we must multiply by four.
-	 */
-	transfer = (inb(ARG_1 + base) << 2);
-	offset = inb(ACCUM + base);
-	scratch = inb(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.
-	 */
-	if (scratch & WIDEXFER)
-	{
-	  max_offset = 0x08;
-	}
-	else
-	{
-	  max_offset = 0x0F;
-	}
-	aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
-                         scsi_id, channel);
-	/*
-	 * Preserve the wide transfer flag.
-	 */
-	scratch = rate | (scratch & WIDEXFER);
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	if ((scratch & 0x0F) == 0)
-	{
-          /*
-           * One of two things happened.  Either the device requested
-           * asynchronous data transfers, or it requested a synchronous
-           * data transfer rate that was so low that asynchronous
-           * transfers are faster (not to mention the controller won't
-           * support them).  In both cases the synchronous data transfer
-           * rate and the offset are set to 0 indicating asynchronous
-           * transfers.
-           *
-           * If the device requested an asynchronous transfer, then
-           * accept the request.  If the device is being forced to
-           * asynchronous data transfers and this is the first time
-           * we've seen the request, accept the request.  If we've
-           * already seen the request, then attempt to force
-           * asynchronous data transfers by rejecting the message.
-           */
-          if ((offset == 0) || (p->sdtr_pending & target_mask))
-          {
+          default:
             /*
-             * Device requested asynchronous transfers or we're
-             * forcing asynchronous transfers for the first time.
+             * Unknown extended message - reject it.
              */
-            outb(0, RETURN_1 + base);
-          }
-          else
-          {
-            /*
-	     * The first time in forcing asynchronous transfers
-             * failed, so we try sending a reject message.
-	     */
-	    outb(SEND_REJ, RETURN_1 + base);
-          }
-	}
-	else
-	{
-	  /*
-	   * See if we initiated Sync Negotiation
-	   */
-	  if (p->sdtr_pending & target_mask)
-	  {
-	    /*
-	     * Don't send an SDTR back to the target.
-	     */
-	    outb(0, RETURN_1 + base);
-	  }
-	  else
-	  {
-	    /*
-	     * Send our own SDTR in reply.
-	     */
-	    printk("aic7xxx: Sending SDTR!!\n");
-	    outb(SEND_SDTR, RETURN_1 + base);
-	  }
-	}
-	/*
-	 * Clear the flags.
-	 */
-	p->needsdtr &= ~target_mask;
-	p->sdtr_pending &= ~target_mask;
-	break;
-
-      case WDTR_MSG:
-      {
-	bus_width = inb(ARG_1 + base);
-	printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c "
-	       "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr);
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
-	if (p->wdtr_pending & target_mask)
-	{
-	  /*
-	   * Don't send an WDTR back to the target, since we asked first.
-	   */
-	  outb(0, RETURN_1 + base);
-	  switch (bus_width)
-	  {
-	    case BUS_8_BIT:
-	      scratch &= 0x7F;
-	      break;
-
-	    case BUS_16_BIT:
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-	      scratch |= 0x80;
-	      break;
-
-	    case BUS_32_BIT:
-	      outb(SEND_REJ, RETURN_1 + base);
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit "
-                     "transfers, rejecting...\n", p->host_no, scsi_id, channel);
-	      break;
-	  }
-	}
-	else
-	{
-	  /*
-	   * Send our own WDTR in reply.
-	   */
-	  printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no);
-	  switch (bus_width)
-	  {
-	    case BUS_8_BIT:
-	      scratch &= 0x7F;
-	      break;
-
-	    case BUS_32_BIT:
-	      /*
-               * Negotiate 16 bits.
-               */
-	      bus_width = BUS_16_BIT;
-	      /* Yes, we mean to fall thru here. */
-
-	    case BUS_16_BIT:
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-	      scratch |= 0x80;
-	      break;
-	  }
-	  outb(bus_width | SEND_WDTR, RETURN_1 + base);
-	}
-	p->needwdtr &= ~target_mask;
-	p->wdtr_pending &= ~target_mask;
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	break;
-      }
+            outb(SEND_REJ, p->base + RETURN_1);
+            break;
+	}  /* switch (message_code) */
+      }  /* case EXTENDED_MSG */
+      break;
 
-      case REJECT_MSG:
+    case REJECT_MSG:
       {
 	/*
-	 * What we care about here is if we had an
-	 * outstanding SDTR or WDTR message for this
-	 * target. If we did, this is a signal that
-	 * the target is refusing negotiation.
+	 * What we care about here is if we had an outstanding SDTR
+	 * or WDTR message for this target. If we did, this is a
+	 * signal that the target is refusing negotiation.
 	 */
+	unsigned char targ_scratch;
+        unsigned char scb_index;
 
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+	targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
 
-	if (p->wdtr_pending & target_mask)
+	if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
 	{
-	  /*
-	   * note 8bit xfers and clear flag
-	   */
-	  scratch &= 0x7F;
-	  p->needwdtr &= ~target_mask;
-	  p->wdtr_pending &= ~target_mask;
-	  printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
-                 "negotiation; using 8 bit transfers.\n",
-                 p->host_no, scsi_id, channel);
+          /*
+           * note 8bit xfers and clear flag
+           */
+          targ_scratch &= 0x7F;
+          p->needwdtr &= ~target_mask;
+          printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+  		 "negotiation; using 8 bit transfers.\n",
+  		 p->host_no, target, channel);
 	}
 	else
 	{
-	  if (p->sdtr_pending & target_mask)
-	  {
-	    /*
-	     * note asynch xfers and clear flag
-	     */
-	    scratch &= 0xF0;
-	    p->needsdtr &= ~target_mask;
-	    p->sdtr_pending &= ~target_mask;
-	    printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
-                   "synchronous negotiation; using asynchronous transfers.\n",
-                   p->host_no, scsi_id, channel);
-	  }
-	  /*
-	   * Otherwise, we ignore it.
-	   */
+          if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
+          {
+            /*
+             * note asynch xfers and clear flag
+             */
+            targ_scratch &= 0xF0;
+            p->needsdtr &= ~target_mask;
+            printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+  		   "synchronous negotiation; using asynchronous transfers.\n",
+  		   p->host_no, target, channel);
+          }
+          /*
+           * Otherwise, we ignore it.
+           */
 	}
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	break;
+        outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+        outb(targ_scratch, p->base + SCSIRATE);
       }
+      break;
 
-      case BAD_STATUS:
-        /* The sequencer will notify us when a command has an error that
-         * would be of interest to the kernel.  This allows us to leave
-         * the sequencerrunning in the common case of command completes
-         * without error.
-         */
+    case BAD_STATUS:
+      {
+	unsigned char scb_index;
+	struct aic7xxx_hwscb *hscb;
+	Scsi_Cmnd *cmd;
+
+	/* The sequencer will notify us when a command has an error that
+	 * would be of interest to the kernel.  This allows us to leave
+	 * the sequencer running in the common case of command completes
+	 * without error.  The sequencer will have DMA'd the SCB back
+	 * up to us, so we can reference the drivers SCB array.
+	 */
+	scb_index = inb(p->base + SCB_TAG);
+	scb = p->scb_data->scb_array[scb_index];
+	hscb = scb->hscb;
 
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	outb(0, RETURN_1 + base);   /* CHECK_CONDITION may change this */
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+	/*
+	 * Set the default return value to 0 indicating not to send
+	 * sense.  The sense code will change this if needed and this
+	 * reduces code duplication.
+	 */
+	outb(0, p->base + RETURN_1);
+	if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
 	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+          printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+        	 "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
+        	 intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
 	}
 	else
 	{
-	  cmd = scb->cmd;
-          scb->target_status = inb(SCB_TARGET_STATUS + base);
-	  aic7xxx_status(cmd) = scb->target_status;
+          cmd = scb->cmd;
+  	  hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+          aic7xxx_status(cmd) = hscb->target_status;
 
-	  cmd->result |= scb->target_status;
+          cmd->result |= hscb->target_status;
 
-	  switch (status_byte(scb->target_status))
-	  {
-	    case GOOD:
-              printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
-	      break;
-
-	    case CHECK_CONDITION:
-	      if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
-	      {
-                unsigned char tcl;
-		unsigned int  req_buf; /* must be 32 bits */
+          switch (status_byte(hscb->target_status))
+          {
+            case GOOD:
+  	      printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
+                     "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+              break;
 
-                tcl = scb->target_channel_lun;
+            case CHECK_CONDITION:
+              if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+              {
+        	unsigned int addr;    /* must be 32 bits */
+        	/*
+        	 * XXX - How do we save the residual (if there is one).
+        	 */
+                aic7xxx_calculate_residual(p, scb);
+
+        	/*
+  		 * Send a sense command to the requesting target.
+        	 * XXX - revisit this and get rid of the memcopys.
+  		 */
+        	memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+        	       sizeof(generic_sense));
+
+        	scb->sense_cmd[1] = (cmd->lun << 5);
+        	scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+        	scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
+        	scb->sg_list[0].length = sizeof(cmd->sense_buffer);
+        	cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
 
-		/*
-                 * Send a sense command to the requesting target.
+                /*
+                 * XXX - We should allow disconnection, but can't as it
+                 * might allow overlapped tagged commands.
                  */
-		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[4] = sizeof(cmd->sense_buffer);
-
-		scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
-		scb->sg_list[0].length = sizeof(cmd->sense_buffer);
-		req_buf = VIRT_TO_BUS(&scb->sg_list[0]);
-		cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
-
-                scb->control = scb->control & DISCENB;
-		scb->target_channel_lun = tcl;
-		addr = VIRT_TO_BUS(scb->sense_cmd);
-		scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
-		memcpy(scb->SCSI_cmd_pointer, &addr,
-		       sizeof(scb->SCSI_cmd_pointer));
-		scb->SG_segment_count = 1;
-		memcpy(scb->SG_list_pointer, &req_buf,
-		       sizeof(scb->SG_list_pointer));
-                scb->data_count = scb->sg_list[0].length;
-		memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-		       sizeof(scb->data_pointer));
+  		/* hscb->control &= DISCENB; */
+                hscb->control = 0;
+        	hscb->target_status = 0;
+        	hscb->SG_segment_count = 1;
+
+        	addr = VIRT_TO_BUS(&scb->sg_list[0]);
+        	memcpy(&hscb->SG_list_pointer, &addr,
+        	       sizeof(hscb->SG_list_pointer));
+
+        	memcpy(&hscb->data_pointer, &(scb->sg_list[0].address),
+        	       sizeof(hscb->data_pointer));
+        	/* Maintain SCB_LINKED_NEXT */
+        	hscb->data_count &= 0xFF000000;
+  		hscb->data_count |= scb->sg_list[0].length;
+
+        	addr = VIRT_TO_BUS(scb->sense_cmd);
+        	memcpy(&hscb->SCSI_cmd_pointer, &addr,
+        	       sizeof(hscb->SCSI_cmd_pointer));
+        	hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
 
-                aic7xxx_putscb(p, scb);
+                scb->sg_count = hscb->SG_segment_count;
+        	scb->flags |= SCB_SENSE;
                 /*
-                 * Ensure that the target is "BUSY" so we don't get overlapping
-                 * commands if we happen to be doing tagged I/O.
+                 * Ensure the target is busy since this will be an
+                 * an untagged request.
                  */
-		aic7xxx_busy_target(scsi_id, channel, base);
+                aic7xxx_busy_target(p, target, channel, hscb->tag);
+        	outb(SEND_SENSE, p->base + RETURN_1);
+              }  /* first time sense, no errors */
+  	      else
+  	      {
+        	if (aic7xxx_error(cmd) == 0)
+  		{
+        	  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+  		}
+  	      }
+              break;
 
-                aic7xxx_add_waiting_scb(base, scb);
-		outb(SEND_SENSE, RETURN_1 + base);
-	      }  /* first time sense, no errors */
-              else
+            case QUEUE_FULL:
+#ifdef NOT_YET
+              if (scb->hscb->control & TAG_ENB)
               {
-                cmd->flags &= ~ASKED_FOR_SENSE;
-	        if (aic7xxx_error(cmd) == 0)
-                {
-		  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-                }
+        	if (cmd->device->queue_depth > 2)
+        	{
+                  cmd->device->queue_depth--;  /* Not correct */
+                  printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
+                	 "reduced to %d\n", p->host_no,
+                	 TC_OF_SCB(scb), cmd->device->queue_depth);
+        	}
+        	/*
+        	 * XXX - Requeue this unconditionally?
+        	 */
+
+        	/*
+        	 * We'd like to be able to give the SCB some more time
+        	 * (untimeout, then timeout).
+        	 */
+        	break;
               }
-	      break;
+#endif
+              printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+                     "queue depth %d, active %d\n", p->host_no,
+                     TC_OF_SCB(scb), cmd->device->queue_depth,
+                     p->device_status[TARGET_INDEX(cmd)].active_cmds);
+
+              /* Else treat this as if it was a BUSY condition. */
+              scb->hscb->target_status = (BUSY << 1) |
+                  (scb->hscb->target_status & 0x01);
+              /* Fall through to the BUSY case. */
+
+            case BUSY:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
+                     p->host_no, TC_OF_SCB(scb));
+              if (!aic7xxx_error(cmd))
+              {
+  		/*
+        	 * The mid-level SCSI code should be fixed to
+        	 * retry the command at a later time instead of
+        	 * trying right away.
+        	 */
+        	aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
+              }
+              udelay(1000);  /*  A small pause (1ms) to help the drive */
+              break;
 
-	    case BUSY:
-	      printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n",
-                     p->host_no, scb->target_channel_lun);
-	      if (!aic7xxx_error(cmd))
-	      {
-                /* The error code here used to be DID_BUS_BUSY,
-                 * but after extensive testing, it has been determined
-                 * that a DID_BUS_BUSY return is a waste of time.  If
-                 * the problem is something that will go away, then it
-                 * will, if it isn't, then you don't want the endless
-                 * looping that you get with a DID_BUS_BUSY.  Better
-                 * to be on the safe side and specify an error condition
-                 * that will eventually lead to a reset or abort of some
-                 * sort instead of an endless loop.
-                 */
-	        aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      }
-	      break;
-
-	    case QUEUE_FULL:
-	      printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no);
-              scb->state |= SCB_ASSIGNEDQ;
-              scbq_insert_tail(&p->assigned_scbs, scb);
-	      break;
-
-	    default:
-	      printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n",
-		     p->host_no, scb->target_status);
-	      if (!aic7xxx_error(cmd))
-	      {
-		aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      }
-	      break;
-	  }  /* end switch */
+            default:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+                     "status 0x%x.\n", p->host_no,
+        	     TC_OF_SCB(scb), scb->hscb->target_status);
+              if (!aic7xxx_error(cmd))
+              {
+        	aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+              }
+              break;
+          }  /* end switch */
 	}  /* end else of */
-	break;
-
-      case RESIDUAL:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-	}
-	else
-	{
-	  cmd = scb->cmd;
-	  /*
-	   *  Don't destroy valid residual information with
-	   *  residual coming from a check sense operation.
-	   */
-	  if (!(cmd->flags & WAS_SENSE))
-	  {
-	    /*
-	     *  We had an underflow. At this time, there's only
-	     *  one other driver that bothers to check for this,
-	     *  and cmd->underflow seems to be set rather half-
-	     *  heartedly in the higher-level SCSI code.
-	     */
-	    actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
-
-	    actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
-		      (inb(SCB_RESID_DCNT1 + base) <<  8) |
-		      inb(SCB_RESID_DCNT0 + base);
-
-	    if (actual < cmd->underflow)
-	    {
-	      printk(KERN_WARNING "scsi%d: Target %d underflow - "
-		     "Wanted at least %u, got %u, residual SG count %d.\n",
-		     p->host_no, cmd->target, cmd->underflow, actual,
-                     inb(SCB_RESID_SGCNT + base));
-	      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      aic7xxx_status(cmd) = scb->target_status;
-	    }
-	  }
-	}
-	break;
+      }
+      break;
 
-      case ABORT_TAG:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-	}
-	else
-	{
-	  cmd = scb->cmd;
-	  /*
-	   * We didn't receive a valid tag back from the target
-	   * on a reconnect.
-	   */
-	  printk("scsi%d: Invalid tag received on target %d, channel %c, "
-                 "lun %d - Sending ABORT_TAG.\n", p->host_no,
-		  scsi_id, channel, cmd->lun & 0x07);
+    case AWAITING_MSG:
+      {
+	unsigned char scb_index;
+        unsigned char message_offset;
 
-	  cmd->result = (DID_RETRY_COMMAND << 16);
-          aic7xxx_done(p, scb);
-	}
-	break;
+	scb_index = inb(p->base + SCB_TAG);
+	scb = p->scb_data->scb_array[scb_index];
 
-      case AWAITING_MSG:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+	/*
+	 * This SCB had a MK_MESSAGE set in its control byte informing
+	 * the sequencer that we wanted to send a special message to
+	 * this target.
+	 */
+        message_offset = inb(p->base + MSG_LEN);
+	if (scb->flags & SCB_DEVICE_RESET)
 	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+          outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+          outb(1, p->base + MSG_LEN);
+          printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
+        	 p->host_no, TC_OF_SCB(scb));
 	}
-	else
+        else if (scb->flags & SCB_ABORT)
+        {
+          if ((scb->hscb->control & TAG_ENB) != 0)
+          {
+            outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+          }
+          else
+          {
+            outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+          }
+          outb(message_offset + 1, p->base + MSG_LEN);
+          printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
+                 p->host_no, TC_OF_SCB(scb));
+        }
+	else if (scb->flags & SCB_MSGOUT_WDTR)
 	{
-	  /*
-	   * This SCB had a zero length command, informing the sequencer
-	   * that we wanted to send a special message to this target.
-	   * We only do this for BUS_DEVICE_RESET messages currently.
-	   */
-	   if (scb->state & SCB_DEVICE_RESET)
-	   {
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
-          scsi_id);
-#endif
-	     outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-	     outb(1, MSG_LEN + base);
-	   }
-	   else
-	   {
-	     panic("scsi%d: AWAITING_SCB for an SCB that does "
-		   "not have a waiting message.\n", p->host_no);
-	   }
-	}
-	break;
-
-      case IMMEDDONE:
-        scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n",
-         scsi_id, scb_index, scb->state);
-#endif
-        if (scb->state & SCB_DEVICE_RESET)
+          aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+        }
+        else if (scb->flags & SCB_MSGOUT_SDTR)
         {
-          int found;
+          unsigned char target_scratch;
+          unsigned short ultra_enable;
+          int i, sxfr;
 
           /*
-           * Go back to async/narrow transfers and renegotiate.
+           * Pull the user defined setting from scratch RAM.
            */
-          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(TARG_SCRATCH + base + scratch_offset);
-          scratch &= SXFR;
-          outb(scratch, TARG_SCRATCH + base + scratch_offset);
-          found = aic7xxx_reset_device(p, (int) scsi_id, channel);
-          printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs "
-                 "aborted.\n", p->host_no, found);
-          /* Indicate that we want to call aic7xxx_done_aborted_scbs() */
-          run_aborted_queue = TRUE;
+          target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+          sxfr = target_scratch & SXFR;
+          ultra_enable = inb(p->base + ULTRA_ENB) |
+              (inb(p->base + ULTRA_ENB + 1) << 8);
+          if (ultra_enable & target_mask)
+          {
+            sxfr |= 0x100;
+          }
+          for (i = 0; i < num_aic7xxx_syncrates; i++)
+          {
+            if (sxfr == aic7xxx_syncrates[i].rate)
+            break;
+          }
+          aic7xxx_construct_sdtr(p, message_offset,
+                                 aic7xxx_syncrates[i].period,
+                                 target_scratch & WIDEXFER ?
+                                 MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
         }
-        else
+        else 
         {
-          panic("scsi%d: Immediate complete for unknown operation.\n",
-                p->host_no);
-        }
-        break;
+          panic("aic7xxx: AWAITING_MSG for an SCB that does "
+                "not have a waiting message.");
+	}
+      }
+      break;
 
-      case DATA_OVERRUN:
+    case DATA_OVERRUN:
       {
-        unsigned int overrun;
-
-        scb = (p->scb_array[inb(base + SCB_TAG)]);
-        overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) |
-                  (inb(base + STCNT2) << 16);
-        overrun =0x00FFFFFF - overrun;
-        printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing "
-               "a retry.\n", p->host_no, overrun);
-        aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
-        break;
+	unsigned char scb_index = inb(p->base + SCB_TAG);
+        unsigned char lastphase = inb(p->base + LASTPHASE);
+	unsigned int i, overrun;
+
+	scb = (p->scb_data->scb_array[scb_index]);
+	overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
+  		  (inb(p->base + STCNT + 2) << 16);
+	overrun = 0x00FFFFFF - overrun;
+	printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
+               "in %s phase, tag %d; forcing a retry.\n",
+               p->host_no, TC_OF_SCB(scb), overrun,
+               lastphase == P_DATAIN ? "Data-In" : "Data-Out",
+               scb->hscb->tag);
+        printk(KERN_WARNING "%s seen Data Phase.  Length = %d, NumSGs = %d.\n",
+               inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+               aic7xxx_length(scb->cmd, 0), scb->sg_count);
+        for (i = 0; i < scb->sg_count; i++)
+        {
+          printk(KERN_INFO "     sg[%d] - Addr 0x%x : Length %d\n",
+                 i, scb->sg_list[i].address, scb->sg_list[i].length);
+        }
+	/*
+	 * XXX - What do we really want to do on an overrun?  The
+	 *       mid-level SCSI code should handle this, but for now,
+	 *       we'll just indicate that the command should retried.
+	 */
+	aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
       }
+      break;
 
-#if AIC7XXX_NOT_YET
-      /* XXX Fill these in later */
-      case MESG_BUFFER_BUSY:
-        break;
-      case MSGIN_PHASEMIS:
-        break;
-#endif
+/* #if AIC7XXX_NOT_YET */
+    /* XXX Fill these in later */
+    case MSG_BUFFER_BUSY:
+      printk("aic7xxx: Message buffer busy.\n");
+      break;
+    case MSGIN_PHASEMIS:
+      printk("aic7xxx: Message-in phasemis.\n");
+      break;
+/*#endif */
+
+    case ABORT_CMDCMPLT:
+      /* This interrupt serves to pause the sequencer until we can clean
+       * up the QOUTFIFO allowing us to handle any abort SCBs that may
+       * completed yet still have an SCB in the QINFIFO or waiting for
+       * selection queue.  By the time we get here, we should have
+       * already cleaned up the queues, so all we need to do is unpause
+       * the sequencer.
+       */
+      break;
+
+    default:		   /* unknown */
+      printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+             p->host_no, intstat, inb(p->base + SCSISIGI));
+      break;
+  }
+
+  /*
+   * Clear the sequencer interrupt and unpause the sequencer.
+   */
+  outb(CLRSEQINT, p->base + CLRINT);
+  unpause_sequencer(p, /* unpause always */ TRUE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_scsiint
+ *
+ * Description:
+ *   Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  unsigned char scb_index;
+  unsigned char status;
+  struct aic7xxx_scb *scb;
+
+  scb_index = inb(p->base + SCB_TAG);
+  status = inb(p->base + SSTAT1);
 
-      default:               /* unknown */
-	printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
-	       p->host_no, intstat, inb(SCSISIGI + base));
-	break;
+  if (scb_index < p->scb_data->numscbs)
+  {
+    scb = p->scb_data->scb_array[scb_index];
+    if ((scb->flags & SCB_ACTIVE) == 0)
+    {
+      scb = NULL;
     }
+  }
+  else
+  {
+    scb = NULL;
+  }
 
+  if ((status & SCSIRSTI) != 0)
+  {
+    char channel;
+
+    channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+
+    printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
+           p->host_no, channel);
     /*
-     * Clear the sequencer interrupt and unpause the sequencer.
+     * Go through and abort all commands for the channel, but do not
+     * reset the channel again.
      */
-    outb(CLRSEQINT, CLRINT + base);
-    UNPAUSE_SEQUENCER(p);
+    aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+    scb = NULL;
   }
-
-  if (intstat & SCSIINT)
+  else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
   {
-    int status = inb(SSTAT1 + base);
-    scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
-    channel = 'A';
-    if (inb(SBLKCTL + base) & SELBUSB)
-    {
-      channel = 'B';
+    /*
+     * First look at what phase we were last in.  If it's message-out,
+     * chances are pretty good that the bus free was in response to
+     * one of our abort requests.
+     */
+    unsigned char lastphase = inb(p->base + LASTPHASE);
+    unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F;
+    char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+    int printerror = TRUE;
+
+    outb(0, p->base + SCSISEQ);
+    if (lastphase == P_MESGOUT)
+    {
+      unsigned char sindex;
+      unsigned char message;
+
+      sindex = inb(p->base + SINDEX);
+      message = inb(p->base + sindex - 1);
+
+      if (message == MSG_ABORT)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_ABORT_TAG)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_BUS_DEV_RESET)
+      {
+        aic7xxx_handle_device_reset(p, target, channel);
+        scb = NULL;
+        printerror = 0;
+      }
     }
+    if (printerror != 0)
+    {
+      if (scb != NULL)
+      {
+        unsigned char tag;
+
+        if ((scb->hscb->control & TAG_ENB) != 0)
+        {
+          tag = scb->hscb->tag;
+        }
+        else
+        {
+          tag = SCB_LIST_NULL;
+        }
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+      }
+      else
+      {
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+      }
+      printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
+             "SEQADDR = 0x%x\n", p->host_no, lastphase,
+             (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    }
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+    outb(CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if ((status & SELTO) != 0)
+  {
+    unsigned char scbptr;
+    unsigned char nextscb;
+    Scsi_Cmnd *cmd;
+
+    scbptr = inb(p->base + WAITING_SCBH);
+    outb(scbptr, p->base + SCBPTR);
+    scb_index = inb(p->base + SCB_TAG);
 
-    scb_index = inb(SCB_TAG + base);
-    scb = (p->scb_array[scb_index]);
-    if (status & SCSIRSTI)
+    scb = NULL;
+    if (scb_index < p->scb_data->numscbs)
     {
-      PAUSE_SEQUENCER(p);
-      printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
-             p->host_no, channel);
-      /*
-       * Go through and abort all commands for the channel, but do not
-       * reset the channel again.
-       */
-      aic7xxx_reset_channel(p, channel, FALSE);
-      run_aborted_queue = TRUE;
+      scb = p->scb_data->scb_array[scb_index];
+      if ((scb->flags & SCB_ACTIVE) == 0)
+      {
+        scb = NULL;
+      }
     }
-    else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+    if (scb == NULL)
+    {
+      printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
+             p->host_no, scb_index);
+      printk(KERN_WARNING "        SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+             "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ),
+             inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+             inb(p->base + SSTAT0), inb(p->base + SSTAT1));
+    }
+    else
     {
-      printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no);
       /*
-       * Turn off the interrupt and set status to zero, so that it
-       * falls through the rest of the SCSIINT code.
+       * XXX - If we queued an abort tag, go clean up the disconnected list.
        */
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
+      cmd = scb->cmd;
+      cmd->result = (DID_TIME_OUT << 16);
+
+      /*
+       * Clear an pending messages for the timed out
+       * target and mark the target as free.
+       */
+      outb(0, p->base + MSG_LEN);
+      aic7xxx_index_busy_target(p, cmd->target,
+          cmd->channel ? 'B': 'A', /*unbusy*/ TRUE);
+      outb(0, p->base + SCB_CONTROL);
+
+      /*
+       * Shift the waiting for selection queue forward
+       */
+      nextscb = inb(p->base + SCB_NEXT);
+      outb(nextscb, p->base + WAITING_SCBH);
+
+      /*
+       * Put this SCB back on the free list.
+       */
+      aic7xxx_add_curscb_to_free_list(p);
+    }
+    /*
+     * Stop the selection.
+     */
+    outb(0, p->base + SCSISEQ);
+    outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if (scb == NULL)
+  {
+    printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+           "during scsiint 0x%x scb(%d)\n"
+           "      SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+           p->host_no, status, scb_index, inb(p->base + SIMODE0),
+           inb(p->base + SIMODE1), inb(p->base + SSTAT0),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    /*
+     * Turn off the interrupt and set status to zero, so that it
+     * falls through the rest of the SCSIINT code.
+     */
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  else if (status & SCSIPERR)
+  {
+    /*
+     * Determine the bus phase and queue an appropriate message.
+     */
+    char  *phase;
+    Scsi_Cmnd *cmd;
+    unsigned char mesg_out = MSG_NOOP;
+    unsigned char lastphase = inb(p->base + LASTPHASE);
+
+    cmd = scb->cmd;
+    switch (lastphase)
+    {
+      case P_DATAOUT:
+        phase = "Data-Out";
+        break;
+      case P_DATAIN:
+        phase = "Data-In";
+        mesg_out = MSG_INITIATOR_DET_ERR;
+        break;
+      case P_COMMAND:
+        phase = "Command";
+        break;
+      case P_MESGOUT:
+        phase = "Message-Out";
+        break;
+      case P_STATUS:
+        phase = "Status";
+        mesg_out = MSG_INITIATOR_DET_ERR;
+        break;
+      case P_MESGIN:
+        phase = "Message-In";
+        mesg_out = MSG_PARITY_ERROR;
+        break;
+      default:
+        phase = "unknown";
+        break;
+    }
+
+    /*
+     * A parity error has occurred during a data
+     * transfer phase. Flag it and continue.
+     */
+    printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
+           p->host_no, TC_OF_SCB(scb), phase);
+
+    /*
+     * 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_NOOP)
+    {
+      outb(mesg_out, p->base + MSG_OUT);
+      outb(1, p->base + MSG_LEN);
       scb = NULL;
     }
-    else if (status & SCSIPERR)
+    else
     {
-      char  *phase;
-      unsigned char mesg_out = MSG_NOP;
-      unsigned char lastphase = inb(LASTPHASE + base);
+      /*
+       * Should we allow the target to make this decision for us?
+       */
+      cmd->result = DID_RETRY_COMMAND << 16;
+    }
+    outb(CLRSCSIPERR, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+  }
+  else
+  {
+    /*
+     * We don't know what's going on. Turn off the
+     * interrupt source and try to continue.
+     */
+    printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  if (scb != NULL)
+  {
+    aic7xxx_done(p, scb);
+    aic7xxx_done_cmds_complete(p);
+  }
+}
 
-      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;
-      }
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_isr
+ *
+ * Description:
+ *   SCSI controller interrupt handler.
+ *
+ *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ *         be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct aic7xxx_host *p;
+  unsigned char intstat;
+  unsigned long flags;
 
-      /*
-       * A parity error has occurred during a data
-       * transfer phase. Flag it and continue.
-       */
-      printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, "
-             "channel %d, lun %d.\n", p->host_no, phase,
-             cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
+  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
 
-      /*
-       * 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);
-        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);
+  /*
+   * Search for the host with a pending interrupt.  If we can't find
+   * one, then we've encountered a spurious interrupt.
+   */
+  while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND))
+  {
+    if (p->next == NULL)
+    {
+      p = NULL;
     }
-    else if (status & SELTO)
+    else
     {
-      unsigned char waiting;
+      p = (struct aic7xxx_host *) p->next->hostdata;
+    }
+  }
 
-      cmd = scb->cmd;
+  if (p == NULL)
+    return;
 
-      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);
-      /*
-       * Stop the selection.
-       */
-      outb(0, SCSISEQ + base);
-      outb(0, SCB_CONTROL + base);
-      outb(CLRSELTIMEO, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+  /*
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
+   */
+  intstat = inb(p->base + INTSTAT);
 
-      /*
-       * Shift the waiting for selection queue forward
-       */
-      waiting = inb(WAITING_SCBH + base);
-      outb(waiting, SCBPTR + base);
-      waiting = inb(SCB_NEXT + base);
-      outb(waiting, WAITING_SCBH + base);
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
 
-      RESTART_SEQUENCER(p);
-      aic7xxx_done(p, scb);
-    }
-    else if (!(status & BUSFREE))
+  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+  {
+    /*
+     * We must only have one card at this IRQ and it must have been
+     * added to the board data before the spurious interrupt occurred.
+     * It is sufficient that we check isr_count and not the spurious
+     * interrupt count.
+     */
+    printk("scsi%d: Encountered spurious interrupt.\n", p->host_no);
+    if (intstat)
     {
-      /*
-       * We don't know what's going on. Turn off the
-       * interrupt source and try to continue.
-       */
-      printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
+      /* Try clearing all interrupts. */
+      outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
     }
+    return;
+  }
+
+  if (p->flags & IN_ISR)
+  {
+    printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+           p->host_no);
+    return;
   }
 
-  if (run_aborted_queue)
-    aic7xxx_done_aborted_scbs(p);
+  /*
+   * Indicate that we're in the interrupt handler.
+   */
+  save_flags(flags);
+  cli();
+  p->flags |= IN_ISR;
 
   if (intstat & CMDCMPLT)
   {
-    int complete;
+    struct aic7xxx_scb *scb = NULL;
+    Scsi_Cmnd *cmd;
+    unsigned char qoutcnt;
+    unsigned char scb_index;
+    int i, interrupts_cleared = 0;
 
     /*
      * The sequencer will continue running when it
      * issues this interrupt. There may be >1 commands
      * finished, so loop until we've processed them all.
      */
-    do {
-      complete = inb(QOUTFIFO + base);
+    qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
 
-      scb = (p->scb_array[complete]);
-      if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+#if 1
+  if (qoutcnt >= p->qfullcount - 1)
+    printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
+           "qoutcnt = %d.\n", qoutcnt);
+#endif
+    while (qoutcnt > 0)
+    {
+      if ((p->flags & PAGE_ENABLED) != 0)
       {
-	printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n"
-	       "       QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, "
-               "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base),
-               inb(QINCNT + base), scb->state, (unsigned long) scb->cmd,
-               scb->position);
-	outb(CLRCMDINT, CLRINT + base);
-	continue;
+        p->cmdoutcnt += qoutcnt;
+        if (p->cmdoutcnt >= p->qfullcount)
+        {
+          /*
+           * Since paging only occurs on aic78x0 chips, we can use
+           * Auto Access Pause to clear the command count.
+           */
+          outb(0, p->base + CMDOUTCNT);
+          p->cmdoutcnt = 0;
+        }
       }
-      cmd = scb->cmd;
-      cmd->result |= (aic7xxx_error(cmd) << 16);
-      if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+      for (i = 0; i < qoutcnt; i++)
       {
-        /*
-         * Got sense information.
-         */
-	cmd->flags &= ASKED_FOR_SENSE;
+        scb_index = inb(p->base + QOUTFIFO);
+        scb = p->scb_data->scb_array[scb_index];
+        if (scb == NULL)
+        {
+	  printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
+	         "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+                 inb(p->base + QOUTCNT), inb(p->base + QINCNT));
+          continue;
+        }
+        else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+        {
+	  printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
+	         "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+                 p->host_no, scb_index, inb(p->base + QOUTCNT),
+                 inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
+	  continue;
+        }
+        cmd = scb->cmd;
+        if (scb->hscb->residual_SG_segment_count != 0)
+        {
+          aic7xxx_calculate_residual(p, scb);
+        }
+        if ((scb->flags & SCB_QUEUED_ABORT) != 0)
+        {
+          /*
+           * Have to clean up any possible entries in the
+           * waiting queue and the QINFIFO.
+           */
+          int target;
+          char channel;
+          int lun;
+          unsigned char tag;
+
+          tag = SCB_LIST_NULL;
+          target = cmd->target;
+          lun = cmd->lun;
+          channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+          if (scb->hscb->control & TAG_ENB)
+          {
+            tag = scb->hscb->tag;
+          }
+          aic7xxx_reset_device(p, target, channel, lun, tag);
+          /*
+           * Run the done queue, but don't complete the commands; we
+           * do this once at the end of the loop.
+           */
+          aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+        }
+        cmd->result |= (aic7xxx_error(cmd) << 16);
+        p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+        aic7xxx_done(p, scb);
       }
-      p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
-
       /*
        * Clear interrupt status before checking the output queue again.
        * This eliminates a race condition whereby a command could
@@ -3242,56 +3964,152 @@
        * so notification of the command being complete never made it
        * back up to the kernel.
        */
-      outb(CLRCMDINT, CLRINT + base);
-      aic7xxx_done(p, scb);
+      outb(CLRCMDINT, p->base + CLRINT);
+      interrupts_cleared++;
+      qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
+    }
 
-#ifdef AIC7XXX_PROC_STATS
-      /*
-       * XXX: we should actually know how much actually transferred
-       * XXX: for each command, but apparently that's too difficult.
-       */
-      actual = aic7xxx_length(cmd, 0);
-      if (!(cmd->flags & WAS_SENSE) && (actual > 0))
+    if (interrupts_cleared == 0)
+    {
+      outb(CLRCMDINT, p->base + CLRINT);
+    }
+
+    aic7xxx_done_cmds_complete(p);
+  }
+
+  if (intstat & BRKADRINT)
+  {
+    int i;
+    unsigned char errno = inb(p->base + ERROR);
+
+    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+    for (i = 0; i < NUMBER(hard_error); i++)
+    {
+      if (errno & hard_error[i].errno)
       {
-        struct aic7xxx_xferstats *sp;
-        long *ptr;
-        int x;
+        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
+      }
+    }
+    printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
+           inb(p->base + ERROR),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
+    aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  }
+
+  if (intstat & SEQINT)
+  {
+    aic7xxx_handle_seqint(p, intstat);
+  }
+
+  if (intstat & SCSIINT)
+  {
+    aic7xxx_handle_scsiint(p, intstat);
+  }
+
+  if (p->waiting_scbs.head != NULL)
+  {
+    aic7xxx_run_waiting_queues(p);
+  }
+
+  p->flags &= ~IN_ISR;
+  restore_flags(flags);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_device_queue_depth
+ *
+ * Description:
+ *   Determines the queue depth for a given device.  There are two ways
+ *   a queue depth can be obtained for a tagged queueing device.  One
+ *   way is the default queue depth which is determined by whether
+ *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
+ *   default queue depth (dependent on the number of hardware SCBs).
+ *   The other way we determine queue depth is through the use of the
+ *   aic7xxx_tag_info array which is enabled by defining
+ *   AIC7XXX_TAGGED_QUEUEING_BY_DEVICE.  This array can be initialized
+ *   with queue depths for individual devices.  It also allows tagged
+ *   queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+  int default_depth = 2;
+
+  device->queue_depth = default_depth;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+  if (device->tagged_supported)
+  {
+    unsigned short target_mask;
+    int tag_enabled = TRUE;
+
+    target_mask = (1 << (device->id | (device->channel << 3)));
 
-        sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
-        sp->xfers++;
+#ifdef AIC7XXX_CMDS_PER_LUN
+    default_depth = AIC7XXX_CMDS_PER_LUN;
+#else
+    if (p->scb_data->maxhscbs <= 4)
+    {
+      default_depth = 4;  /* Not many SCBs to work with. */
+    }
+    else
+    {
+      default_depth = 8;
+    }
+#endif
+ 
+    if (!(p->discenable & target_mask))
+    {
+      printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+             "enable tagged queueing.\n",
+             p->host_no, device->id, device->channel);
+    }
+    else
+    {
+#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+      device->queue_depth = default_depth;
+#else
+      if (p->instance >= NUMBER(aic7xxx_tag_info))
+      {
+        device->queue_depth = default_depth;
+      }
+      else
+      {
+        unsigned char  tindex;
 
-        if (cmd->request.cmd == WRITE)
+        tindex = device->id | (device->channel << 3);
+        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
         {
-          sp->w_total++;
-          sp->w_total512 += (actual >> 9);
-          ptr = sp->w_bins;
+          tag_enabled = FALSE;
+          device->queue_depth = 2;  /* Tagged queueing is disabled. */
         }
-        else
+        else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
         {
-          sp->r_total++;
-          sp->r_total512 += (actual >> 9);
-          ptr = sp->r_bins;
+          device->queue_depth = default_depth;
         }
-        for (x = 9; x <= 17; x++)
+        else
         {
-          if (actual < (1 << x))
-          {
-            ptr[x - 9]++;
-            break;
-          }
+          device->queue_depth =
+            aic7xxx_tag_info[p->instance].tag_commands[tindex];
         }
-        if (x > 17)
+      }
+#endif
+      if ((device->tagged_queue == 0) && tag_enabled)
+      {
+        if (aic7xxx_verbose)
         {
-          ptr[x - 9]++;
+    	  printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+    	         "queue depth %d.\n", p->host_no,
+    	         device->id, device->channel, device->queue_depth);
         }
+        device->tagged_queue = 1;
+        device->current_tag = SCB_LIST_NULL;
       }
-#endif /* AIC7XXX_PROC_STATS */
-
-    } while (inb(QOUTCNT + base) & p->qcntmask);
+    }
   }
-  aic7xxx_done_cmds_complete(p);
-  p->flags &= ~IN_ISR;
-  aic7xxx_run_waiting_queues(p);
+#endif
 }
 
 /*+F*************************************************************************
@@ -3306,59 +4124,18 @@
  *   algorithm for determining the queue depth based on the maximum
  *   SCBs for the controller.
  *-F*************************************************************************/
-static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host *host,
     Scsi_Device *scsi_devs)
 {
-  Scsi_Device *device = scsi_devs;
-  int tq_depth = 2;
+  Scsi_Device *device;
   struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
 
-#ifdef AIC7XXX_CMDS_PER_LUN
-  tq_depth = AIC7XXX_CMDS_PER_LUN;
-#else
-  {
-    if (p->maxhscbs <= 4)
-    {
-      tq_depth = 4;  /* Not many SCBs to work with. */
-    }
-    else
-    {
-      tq_depth = 8;
-    }
-  }
-#endif
-
   for (device = scsi_devs; device != NULL; device = device->next)
   {
     if (device->host == host)
     {
-      device->queue_depth = 2;
-#ifdef AIC7XXX_TAGGED_QUEUEING
-      if (device->tagged_supported)
-      {
-        unsigned short target_mask = (1 << device->id) | device->channel;
-
-        if (!(p->discenable & target_mask))
-        {
-          printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable "
-                 "tagged queueing for target %d, channel %d, LUN %d.\n",
-                 host->host_no, device->id, device->channel, device->lun);
-        }
-        else
-        {
-          device->queue_depth = tq_depth;
-          if (device->tagged_queue == 0)
-          {
-            printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, "
-	           "channel %d, LUN %d, queue depth %d.\n", host->host_no,
-                   device->id, device->channel, device->lun,
-                   device->queue_depth);
-            device->tagged_queue = 1;
-            device->current_tag = SCB_LIST_NULL;
-          }
-        }
-      }
-#endif
+      aic7xxx_device_queue_depth(p, device);
     }
   }
 }
@@ -3385,7 +4162,7 @@
  *   The fourth byte's lowest bit seems to be an enabled/disabled
  *   flag (rest of the bits are reserved?).
  *-F*************************************************************************/
-static aha_type
+static aha_chip_type
 aic7xxx_probe(int slot, int base, aha_status_type *bios)
 {
   int i;
@@ -3394,7 +4171,7 @@
   static struct {
     int n;
     unsigned char signature[sizeof(buf)];
-    aha_type type;
+    aha_chip_type type;
     int bios_disabled;
   } AIC7xxx[] = {
     { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
@@ -3433,7 +4210,8 @@
 	return (AIC7xxx[i].type);
       }
 
-      printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
+      printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+             "disabled at slot %d, ignored.\n", slot);
     }
   }
 
@@ -3460,10 +4238,9 @@
  *   useful in that it gives us an 800 nsec timer.  After a read from the
  *   SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
  *   later.
- *
  *-F*************************************************************************/
 static int
-read_2840_seeprom(int base, struct seeprom_config *sc)
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
 {
   int i = 0, k = 0;
   unsigned char temp;
@@ -3476,11 +4253,11 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0)	\
+  while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0)	\
   {						\
     ;  /* Do nothing */				\
   }						\
-  (void) inb(SEECTL_2840 + base);
+  (void) inb(p->base + SEECTL_2840);
 
   /*
    * Read the first 32 registers of the seeprom.  For the 2840,
@@ -3493,8 +4270,8 @@
     /*
      * Send chip select for one clock cycle.
      */
-    outb(CK_2840 | CS_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3503,11 +4280,11 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = CS_2840 | seeprom_read.bits[i];
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * Send the 6 bit address (MSB first, LSB last).
@@ -3517,11 +4294,11 @@
       temp = k;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = CS_2840 | temp;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3533,12 +4310,12 @@
     for (i = 0; i <= 16; i++)
     {
       temp = CS_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * The serial EEPROM has a checksum in the last word.  Keep a
@@ -3554,12 +4331,12 @@
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(CK_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(CK_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
   }
 
 #if 0
@@ -3588,6 +4365,53 @@
 
 /*+F*************************************************************************
  * Function:
+ *   acquire_seeprom
+ *
+ * Description:
+ *   Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+  int wait;
+
+  /*
+   * Request access of the memory port.  When access is
+   * granted, SEERDY will go high.  We use a 1 second
+   * timeout which should be near 1 second more than
+   * is needed.  Reason: after the 7870 chip reset, there
+   * should be no contention.
+   */
+  outb(SEEMS, p->base + SEECTL);
+  wait = 1000;  /* 1000 msec = 1 second */
+  while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+  {
+    wait--;
+    udelay(1000);  /* 1 msec */
+  }
+  if ((inb(p->base + SEECTL) & SEERDY) == 0)
+  {
+    outb(0, p->base + SEECTL);
+    return (0);
+  }
+  return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   release_seeprom
+ *
+ * Description:
+ *   Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline void
+release_seeprom(struct aic7xxx_host *p)
+{
+  outb(0, p->base + SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   read_seeprom
  *
  * Description:
@@ -3625,7 +4449,7 @@
  *   first).  The clock cycling from low to high initiates the next data
  *   bit to be sent from the chip.
  *
- *   The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ *   The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
  *   register.  After successful arbitration for the memory port, the
  *   SEECS bit of the SEECTL register is connected to the chip select.
  *   The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
@@ -3635,17 +4459,14 @@
  *   to this is when we first request access to the memory port.  The
  *   SEERDY goes high to signify that access has been granted and, for
  *   this case, has no implied timing.
- *
  *-F*************************************************************************/
 static int
-read_seeprom(int base, int offset, struct seeprom_config *sc,
-    seeprom_chip_type chip)
+read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
+    unsigned int len, seeprom_chip_type chip)
 {
   int i = 0, k;
-  unsigned long timeout;
   unsigned char temp;
   unsigned short checksum = 0;
-  unsigned short *seeprom = (unsigned short *) sc;
   struct seeprom_cmd {
     unsigned char len;
     unsigned char bits[3];
@@ -3653,43 +4474,33 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(SEECTL + base) & SEERDY) == 0)	\
+  while ((inb(p->base + SEECTL) & SEERDY) == 0)	\
   {						\
     ;  /* Do nothing */				\
   }
 
   /*
-   * Request access of the memory port.  When access is
-   * granted, SEERDY will go high.  We use a 1 second
-   * timeout which should be near 1 second more than
-   * is needed.  Reason: after the 7870 chip reset, there
-   * should be no contention.
+   * Request access of the memory port.
    */
-  outb(SEEMS, SEECTL + base);
-  timeout = jiffies + 100;  /* 1 second timeout */
-  while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
-  {
-    ; /* Do nothing!  Wait for access to be granted.  */
-  }
-  if ((inb(SEECTL + base) & SEERDY) == 0)
+  if (acquire_seeprom(p) == 0)
   {
-    outb(0, SEECTL + base);
     return (0);
   }
 
   /*
-   * Read the first 32 registers of the seeprom.  For the 7870,
-   * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
-   * but only the first 32 are used by Adaptec BIOS.  The loop
-   * will range from 0 to 31.
+   * Read 'len' registers of the seeprom.  For the 7870, the 93C46
+   * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+   * the first 32 are used by Adaptec BIOS.  Some adapters use the
+   * 93C56 SEEPROM which is a 2048-bit device.  The loop will range
+   * from 0 to 'len' - 1.
    */
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     /*
      * Send chip select for one clock cycle.
      */
-    outb(SEEMS | SEECK | SEECS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3698,25 +4509,25 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
     /*
-     * Send the 6 bit address (MSB first, LSB last).
+     * Send the 6 or 8 bit address (MSB first, LSB last).
      */
     for (i = ((int) chip - 1); i >= 0; i--)
     {
       temp = k + offset;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = SEEMS | SEECS | (temp << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3728,56 +4539,57 @@
     for (i = 0; i <= 16; i++)
     {
       temp = SEEMS | SEECS;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
-     * The serial EEPROM has a checksum in the last word.  Keep a
-     * running checksum for all words read except for the last
-     * word.  We'll verify the checksum after all words have been
-     * read.
+     * The serial EEPROM should have a checksum in the last word.
+     * Keep a running checksum for all words read except for the
+     * last word.  We'll verify the checksum after all words have
+     * been read.
      */
-    if (k < (sizeof(*sc) / 2) - 1)
+    if (k < (len - 1))
     {
-      checksum = checksum + seeprom[k];
+      checksum = checksum + scarray[k];
     }
 
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS | SEECK, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS | SEECK, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
   }
 
   /*
    * Release access to the memory port and the serial EEPROM.
    */
-  outb(0, SEECTL + base);
+  release_seeprom(p);
 
 #if 0
-  printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+  printk("Computed checksum 0x%x, checksum read 0x%x\n",
+         checksum, scarray[len - 1]);
   printk("Serial EEPROM:");
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     if (((k % 8) == 0) && (k != 0))
     {
       printk("\n              ");
     }
-    printk(" 0x%x", seeprom[k]);
+    printk(" 0x%x", scarray[k]);
   }
   printk("\n");
 #endif
 
-  if (checksum != sc->checksum)
+  if (checksum != scarray[len - 1])
   {
     return (0);
   }
@@ -3788,563 +4600,452 @@
 
 /*+F*************************************************************************
  * Function:
- *   detect_maxscb
+ *   write_brdctl
  *
  * Description:
- *   Detects the maximum number of SCBs for the controller and returns
- *   the count and a mask in config (config->maxscbs, config->qcntmask).
+ *   Writes a value to the BRDCTL register.
  *-F*************************************************************************/
-static void
-detect_maxscb(struct aic7xxx_host_config *config)
+static inline void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
 {
-  unsigned char sblkctl_reg;
-  int base, i;
-
-#ifdef AIC7XXX_PAGE_ENABLE
-  config->flags |= PAGE_ENABLED;
-#endif
-  base = config->base;
-  switch (config->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-    case AIC_284x:
-      /*
-       * Check for Rev C or E boards. Rev E boards can supposedly have
-       * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
-       * It's still not clear extactly what is different about the Rev E
-       * boards, but we think it allows 8 bit entries in the QOUTFIFO to
-       * support "paging" SCBs (more than 4 commands can be active at once).
-       *
-       * The Rev E boards have a read/write autoflush bit in the
-       * SBLKCTL register, while in the Rev C boards it is read only.
-       */
-      sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
-      outb(sblkctl_reg, SBLKCTL + base);
-      if (inb(SBLKCTL + base) == sblkctl_reg)
-      {
-        /*
-         * We detected a Rev E board, we allow paging on this board.
-         */
-        printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n",
-               board_names[config->type]);
-	outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
-      }
-      else
-      {
-        /* Do not allow paging. */
-        config->flags &= ~PAGE_ENABLED;
-        printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n",
-               board_names[config->type]);
-      }
-      break;
-
-    default:
-      break;
-  }
-
-  /*
-   * Walk the SCBs to determine how many there are.
-   */
-  i = 1;
-  outb(0, SCBPTR + base);
-  outb(0, SCBARRAY + base);
-
-  while (i < AIC7XXX_MAXSCB)
-  {
-    outb(i, SCBPTR + base);
-    outb(i, SCBARRAY + base);
-    if (inb(SCBARRAY + base) != i)
-      break;
-    outb(0, SCBPTR + base);
-    if (inb(SCBARRAY + base) != 0)
-      break;
-
-    outb(i, SCBPTR + base);      /* Clear the control byte. */
-    outb(0, SCBARRAY + base);
-
-    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->maxhscbs = i;
-  config->qcntmask |= i;
-  if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB))
-  {
-    config->maxscbs = AIC7XXX_MAXSCB;
-  }
-  else
-  {
-    config->flags &= ~PAGE_ENABLED;  /* Disable paging if we have 255 SCBs!. */
-    config->maxscbs = config->maxhscbs;
-  }
-
-  printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs);
-  if (config->flags & PAGE_ENABLED)
-    printk(", %d page-enabled SCBs.\n", config->maxscbs);
-  else
-    printk(", paging not enabled.\n");
+  unsigned char brdctl;
 
+  brdctl = BRDCS | BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl |= value;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDCS;
+  outb(brdctl, p->base + BRDCTL);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_register
+ *   read_brdctl
  *
  * Description:
- *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *   Reads the BRDCTL register.
  *-F*************************************************************************/
-static int
-aic7xxx_register(Scsi_Host_Template *template,
-    struct aic7xxx_host_config *config)
+static inline unsigned char
+read_brdctl(struct aic7xxx_host *p)
 {
-  int i;
-  unsigned char sblkctl, flags = 0;
-  int max_targets;
-  int found = 1;
-  unsigned int sram, base;
-  unsigned char target_settings;
-  unsigned char scsi_conf, host_conf;
-  unsigned short ultraenable = 0;
-  int have_seeprom = FALSE;
-  struct Scsi_Host *host;
-  struct aic7xxx_host *p;
-  struct seeprom_config sc;
-
-  base = config->base;
+  outb(BRDRW | BRDCS, p->base + BRDCTL);
+  return (inb(p->base + BRDCTL));
+}
 
-  /*
-   * Lock out other contenders for our i/o space.
-   */
-  request_region(base, MAXREG - MINREG, "aic7xxx");
+/*+F*************************************************************************
+ * Function:
+ *   configure_termination
+ *
+ * Description:
+ *   Configures the termination settings on PCI adapters that have
+ *   SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
+    unsigned short adapter_control, unsigned char max_targ)
+{
+  unsigned char brdctl_int, brdctl_ext;
+  int internal50_present;
+  int internal68_present = 0;
+  int external_present = 0;
+  int eprom_present;
+  int high_on;
+  int low_on;
+  int old_verbose;
+
+  if (acquire_seeprom(p))
+  {
+    if (adapter_control & CFAUTOTERM)
+    {
+      old_verbose = aic7xxx_verbose;
+      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination.  Please "
+                       "verify driver\n");
+      printk(KERN_INFO "         detected settings and use manual termination "
+                       "if necessary.\n"); 
 
-  switch (config->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-      /*
-       * Use the boot-time option for the interrupt trigger type.  If not
-       * supplied (-1), then we use BIOS settings to determine the interrupt
-       * trigger type (level or edge) and use this value for pausing and
-       * unpausing the sequencer.
-       */
-      switch (aic7xxx_irq_trigger)
-      {
-        case  0: config->unpause = INTEN;          /* Edge */
-                 break;
-        case  1: config->unpause = IRQMS | INTEN;  /* Level */
-                 break;
-        case -1:
-        default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN; 
-                 break;
-      }
-      config->pause = config->unpause | PAUSE;
+      /* Configure auto termination. */
+      outb(SEECS | SEEMS, p->base + SEECTL);
 
       /*
-       * For some 274x boards, we must clear the CHIPRST bit and pause
-       * the sequencer. For some reason, this makes the driver work.
-       * For 284x boards, we give it a CHIPRST just like the 294x boards.
+       * First read the status of our cables.  Set the rom bank to
+       * 0 since the bank setting serves as a multiplexor for the
+       * cable detection logic.  BRDDAT5 controls the bank switch.
        */
-      outb(config->pause | CHIPRST, HCNTRL + base);
-      aic7xxx_delay(1);
-      if (inb(HCNTRL + base) & CHIPRST)
-      {
-	printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
-      }
-      outb(config->pause, HCNTRL + base);
+      write_brdctl(p, 0);
 
       /*
-       * Just to be on the safe side with the 274x, we will re-read the irq
-       * since there was some issue about resetting the board.
+       * Now read the state of the internal connectors.  The
+       * bits BRDDAT6 and BRDDAT7 are 0 when cables are present
+       * set when cables are not present (BRDDAT6 is INT50 and
+       * BRDDAT7 is INT68).
        */
-      config->irq = inb(INTDEF + base) & 0x0F;
-      if ((config->type == AIC_7771) &&
-          (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
-      {
-        config->bios = AIC_DISABLED;
-        config->flags |= USE_DEFAULTS;
-      }
-      else
+      brdctl_int = read_brdctl(p);
+      internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
+      if (max_targ > 8)
       {
-        host_conf = inb(HOSTCONF + base);
-        config->bus_speed = host_conf & DFTHRSH;
-        config->busrtime = (host_conf << 2) & BOFF;
+        internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
       }
 
       /*
-       * Setup the FIFO threshold and the bus off time
+       * Set the rom bank to 1 and determine
+       * the other signals.
        */
-      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
-      outb(config->busrtime, BUSTIME + base);
+      write_brdctl(p, BRDDAT5);
 
       /*
-       * A reminder until this can be detected automatically.
+       * Now read the state of the external connectors.  BRDDAT6 is
+       * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is
+       * set when the eprom is present.
        */
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_284x:
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_284X;
-      config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
-      config->irq = inb(INTDEF + base) & 0x0F;
-      host_conf = inb(HOSTCONF + base);
-
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      have_seeprom = read_2840_seeprom(base, &sc);
-      if (!have_seeprom)
-      {
-	printk("aic7xxx: Unable to read SEEPROM.\n");
-      }
-      else
+      brdctl_ext = read_brdctl(p);
+      external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
+      eprom_present = brdctl_ext & BRDDAT7;
+      if (aic7xxx_verbose)
       {
-	printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        if (sc.bios_control & CF284XEXTEND)
-          config->flags |= EXTENDED_TRANSLATION;
-        if (!(sc.bios_control & CFBIOSEN))
+        if (max_targ > 8)
         {
-          /*
-           * 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;
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
+                 "Ext-68 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 internal68_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
         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.
-           */
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
+        printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, "
+               "brdctl_ext=0x%x\n",
+               eprom_present ? "is" : "not", brdctl_int, brdctl_ext);
       }
 
-      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(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      /*
-       * Remember if Ultra was enabled in case there is no SEEPROM.
-       * Fall through to the rest of the AIC_78xx code.
-       */
-      if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra)
-        config->flags |= ULTRA_ENABLED;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
       /*
-       * Grab the SCSI ID before chip reset in case there is no SEEPROM.
+       * Now set the termination based on what we found.  BRDDAT6
+       * controls wide termination enable.
        */
-      config->scsi_id = inb(SCSIID + base) & OID;
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_294X;
-      config->pause = config->unpause | PAUSE;
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
+      high_on = FALSE;
+      low_on = FALSE;
+      if ((max_targ > 8) &&
+          ((external_present == 0) || (internal68_present == 0)))
+      {
+        high_on = TRUE;
+      }
 
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      if ((config->type == AIC_7873) || (config->type == AIC_7883))
+      if ((internal50_present + internal68_present + external_present) <= 1)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c56_66);
+        low_on = TRUE;
       }
-      else
+          
+      if (internal50_present && internal68_present && external_present)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c46);
+        printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
+               "         Only two connectors on the adapter may be "
+               "used at a time!\n");
       }
-      if (!have_seeprom)
+
+      if (high_on == TRUE)
+        write_brdctl(p, BRDDAT6);
+      else
+        write_brdctl(p, 0);
+
+      if (low_on == TRUE)
+        *sxfrctl1 |= STPWEN;
+
+      if (aic7xxx_verbose)
       {
-        for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
-        {
-          if (inb(sram) != 0x00)
-            break;
-        }
-        if (sram == base + TARG_SCRATCH)
-        {
-          for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
-          {
-            if (inb(sram) != 0xFF)
-              break;
-          }
-        }
-        if ((sram != base + 0x60) && (config->scsi_id != 0))
+        if (max_targ > 8)
         {
-          config->flags &= ~USE_DEFAULTS;
-	  printk("\naic7xxx: Unable to read SEEPROM; "
-                 "using leftover BIOS values.\n");
+          printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+                 low_on ? "ON" : "OFF",
+                 high_on ? "ON" : "OFF");
         }
         else
         {
-          printk("\n");
-          printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default "
-                 "settings.\n");
-          config->flags |= USE_DEFAULTS;
-          config->flags &= ~ULTRA_ENABLED;
-          config->scsi_id = 7;
+          printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
         }
-        scsi_conf = ENSPCHK | RESET_SCSI;
+      }
+      aic7xxx_verbose = old_verbose;
+    }
+    else
+    {
+      if (adapter_control & CFSTERM)
+      {
+        *sxfrctl1 |= STPWEN;
+      }
+      outb(SEEMS | SEECS, p->base + SEECTL);
+      /*
+       * Configure high byte termination.
+       */
+      if (adapter_control & CFWSTERM)
+      {
+        write_brdctl(p, BRDDAT6);
       }
       else
       {
-	printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        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;
-          scsi_conf = ENSPCHK | RESET_SCSI;
-        }
-        else
-        {
-          scsi_conf = 0;
-          if (sc.adapter_control & CFRESETB)
-            scsi_conf |= RESET_SCSI;
-          if (sc.adapter_control & CFSPARITY)
-            scsi_conf |= ENSPCHK;
-	  if (sc.bios_control & CFEXTEND)
-            config->flags |= EXTENDED_TRANSLATION;
-	  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_7881) ||
-               (config->type == AIC_7882) || (config->type == AIC_7883) ||
-               (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN))
-          {
-            printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI "
-                   "speed.\n");
-            config->flags |= ULTRA_ENABLED;
-          }
-        }
+        write_brdctl(p, 0);
+      }
+      if (aic7xxx_verbose)
+      {
+        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+               (adapter_control & CFSTERM) ? "ON" : "OFF",
+               (adapter_control & CFWSTERM) ? "ON" : "OFF");
       }
+    }
+    release_seeprom(p);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   detect_maxscb
+ *
+ * Description:
+ *   Detects the maximum number of SCBs for the controller and returns
+ *   the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char max_scbid = 255;
 
-      outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base);
-      config->bus_speed = DFTHRSH_100;
-      outb(config->bus_speed, DSPCISTATUS + base);
+  /*
+   * It's possible that we've already done this for multichannel
+   * adapters.
+   */
+  if (p->scb_data->maxhscbs == 0)
+  {
+    /*
+     * We haven't initialized the SCB settings yet.  Walk the SCBs to
+     * determince how many there are.
+     */
+    outb(0, p->base + FREE_SCBH);
 
-      /*
-       * In case we are a wide card...
-       */
-      outb(config->scsi_id, SCSICONF + base + 1);
+    for (i = 0; i < AIC7XXX_MAXSCB; i++)
+    {
+      outb(i, p->base + SCBPTR);
+      outb(i, p->base + SCB_CONTROL);
+      if (inb(p->base + SCB_CONTROL) != i)
+        break;
+      outb(0, p->base + SCBPTR);
+      if (inb(p->base + SCB_CONTROL) != 0)
+        break;
 
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
+      outb(i, p->base + SCBPTR);
+      outb(0, p->base + SCB_CONTROL);   /* Clear the control byte. */
+      outb(i + 1, p->base + SCB_NEXT);  /* Set the next pointer. */
+      outb(SCB_LIST_NULL, p->base + SCB_TAG);  /* Make the tag invalid. */
 
-    default:
-      panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n");
+      /* Make the non-tagged targets not busy. */
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3);
+    }
+
+    /* Make sure the last SCB terminates the free list. */
+    outb(i - 1, p->base + SCBPTR);
+    outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+
+    /* Ensure we clear the first (0) SCBs control byte. */
+    outb(0, p->base + SCBPTR);
+    outb(0, p->base + SCB_CONTROL);
+
+    p->scb_data->maxhscbs = i;
   }
 
-  detect_maxscb(config);
+  if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB))
+  {
+    /* Determine the number of valid bits in the FIFOs. */
+    outb(max_scbid, p->base + QINFIFO);
+    max_scbid = inb(p->base + QINFIFO);
+    p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1);
+  }
+  else
+  {
+    p->scb_data->maxscbs = p->scb_data->maxhscbs;
+  }
+  if (p->scb_data->maxscbs == p->scb_data->maxhscbs)
+  {
+    /*
+     * Disable paging if the QINFIFO doesn't allow more SCBs than
+     * we have in hardware.
+     */
+    p->flags &= ~PAGE_ENABLED;
+  }
 
-  if (config->chip_type == AIC_777x)
+  /*
+   * Set the Queue Full Count.  Some cards have more queue space than
+   * SCBs.
+   */
+  switch (p->chip_class)
   {
-    if (config->pause & IRQMS)
-    {
-      printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n");
-    }
-    else
-    {
-      printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n");
-    }
+    case AIC_777x:
+      p->qfullcount = 4;
+      p->qcntmask = 0x07;
+      break;
+    case AIC_785x:
+    case AIC_786x:
+      p->qfullcount = 8;
+      p->qcntmask = 0x0f;
+      break;
+    case AIC_787x:
+    case AIC_788x:
+      if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB)
+      {
+        p->qfullcount = AIC7XXX_MAXSCB;
+        p->qcntmask = 0xFF;
+      }
+      else
+      {
+        p->qfullcount = 16;
+        p->qcntmask = 0x1F;
+      }
+      break;
   }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_register
+ *
+ * Description:
+ *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char sblkctl, flags = 0;
+  int max_targets;
+  int found = 1;
+  char channel_ids[] = {'A', 'B', 'C'};
+  unsigned char target_settings;
+  unsigned char scsi_conf, sxfrctl1;
+  unsigned short ultraenable = 0;
+  struct Scsi_Host *host;
+
+  /*
+   * Lock out other contenders for our i/o space.
+   */
+  request_region(p->base, MAXREG - MINREG, "aic7xxx");
 
   /*
    * Read the bus type from the SBLKCTL register. Set the FLAGS
    * register in the sequencer for twin and wide bus cards.
    */
-  sblkctl = inb(SBLKCTL + base);
-  if (config->flags & PAGE_ENABLED)
+  sblkctl = inb(p->base + SBLKCTL);
+  if (p->flags & PAGE_ENABLED)
     flags = PAGESCBS;
 
   switch (sblkctl & SELBUS_MASK)
   {
     case SELNARROW:     /* narrow/normal bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-      config->bus_type = AIC_SINGLE;
-      outb(flags | SINGLE_BUS, FLAGS + base);
+      p->scsi_id = inb(p->base + SCSICONF) & 0x07;
+      p->bus_type = AIC_SINGLE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS);
       break;
 
     case SELWIDE:     /* Wide bus */
-      config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
-      config->bus_type = AIC_WIDE;
-      printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
-	     board_names[config->type]);
-      outb(flags | WIDE_BUS, FLAGS + base);
+      p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID;
+      p->bus_type = AIC_WIDE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | WIDE_BUS, p->base + SEQ_FLAGS);
       break;
 
     case SELBUSB:     /* Twin bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-#ifdef AIC7XXX_TWIN_SUPPORT
-      config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
-      config->bus_type = AIC_TWIN;
-      printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n",
-	     board_names[config->type]);
-      outb(flags | TWIN_BUS, FLAGS + base);
-#else
-      config->bus_type = AIC_SINGLE;
-      printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n",
-	     board_names[config->type]);
-      outb(flags, FLAGS + base);
-#endif
+      p->scsi_id = inb(p->base + SCSICONF) & HSCSIID;
+      p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID;
+      p->bus_type = AIC_TWIN;
+      printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+             p->scsi_id, p->scsi_id_b);
+      outb(flags | TWIN_BUS, p->base + SEQ_FLAGS);
       break;
 
     default:
       printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
-	     "mail deang@teleport.com\n", inb(SBLKCTL + base));
-      outb(0, FLAGS + base);
-      return (0);
-  }
-
-  /*
-   * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will
-   * take the card out of diagnostic mode and make the host adapter
-   * LED follow bus activity (will not always be on).
-   */
-  outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
-
-  /*
-   * The IRQ level in i/o port 4 maps directly onto the real
-   * IRQ number. If it's ok, register it with the kernel.
-   *
-   * NB. the Adaptec documentation says the IRQ number is only
-   *     in the lower four bits; the ECU information shows the
-   *     high bit being used as well. Which is correct?
-   *
-   * The PCI cards get their interrupt from PCI BIOS.
-   */
-  if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15)))
-  {
-    printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, "
-          "ignoring.\n");
-    return (0);
+	     "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
+      outb(0, p->base + SEQ_FLAGS);
+      return (0);
   }
 
   /*
-   * Print out debugging information before re-enabling
-   * the card - a lot of registers on it can't be read
-   * when the sequencer is active.
-   */
-  debug_config(config);
-
-  /*
-   * Register each "host" and fill in the returned Scsi_Host
-   * structure as best we can. Some of the parameters aren't
-   * really relevant for bus types beyond ISA, and none of the
-   * high-level SCSI code looks at it anyway. Why are the fields
-   * there? Also save the pointer so that we can find the
-   * information when an IRQ is triggered.
+   * Detect SCB parameters and initialize the SCB array.
    */
-  host = scsi_register(template, sizeof(struct aic7xxx_host));
-  host->can_queue = config->maxscbs;
+  detect_maxscb(p);
+  printk("%d/%d SCBs, QFull %d, QMask 0x%x\n",
+         p->scb_data->maxhscbs, p->scb_data->maxscbs,
+         p->qfullcount, p->qcntmask);
+
+  host = p->host;
+
+  host->can_queue = p->scb_data->maxscbs;
   host->cmd_per_lun = 2;
+  host->sg_tablesize = AIC7XXX_MAX_SG;
   host->select_queue_depths = aic7xxx_select_queue_depth;
-  host->this_id = config->scsi_id;
-  host->io_port = config->base;
+  host->this_id = p->scsi_id;
+  host->io_port = p->base;
   host->n_io_port = 0xFF;
-  host->base = (unsigned char *)config->mbase;
-  host->irq = config->irq;
-  if (config->bus_type == AIC_WIDE)
+  host->base = (unsigned char *) p->mbase;
+  host->irq = p->irq;
+  if (p->bus_type == AIC_WIDE)
   {
     host->max_id = 16;
   }
-  if (config->bus_type == AIC_TWIN)
+  if (p->bus_type == AIC_TWIN)
   {
     host->max_channel = 1;
   }
 
-  p = (struct aic7xxx_host *) host->hostdata;
-
   p->host = host;
-  p->host_no = (int)host->host_no;
+  p->host_no = host->host_no;
   p->isr_count = 0;
-  p->base = base;
-  p->maxscbs = config->maxscbs;
-  p->maxhscbs = config->maxhscbs;
-  p->qcntmask = config->qcntmask;
-  p->mbase = (char *)config->mbase;
-  p->type = config->type;
-  p->chip_type = config->chip_type;
-  p->flags = config->flags;
-  p->chan_num = config->chan_num;
-  p->scb_link = &(p->scb_usage);
-#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS)
-  if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883)))
-  {
-    shared_3985_scbs = &(p->scb_usage);
-    p->scb_link = &(p->scb_usage);
-  }
-#endif
-  p->scb_link->numscbs = 0;
-  p->bus_type = config->bus_type;
-  p->seeprom = sc;
   p->next = NULL;
   p->completeq.head = NULL;
   p->completeq.tail = NULL;
-  scbq_init(&p->scb_link->free_scbs);
-  scbq_init(&p->page_scbs);
+  scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
-  scbq_init(&p->assigned_scbs);
-
-  p->unpause = config->unpause;
-  p->pause = config->pause;
 
-  for (i = 0; i <= 15; i++)
+  for (i = 0; i < NUMBER(p->device_status); i++)
   {
     p->device_status[i].commands_sent = 0;
     p->device_status[i].flags = 0;
+    p->device_status[i].active_cmds = 0;
     p->device_status[i].last_reset = 0;
   }
-  if (aic7xxx_boards[config->irq] == NULL)
+  if (aic7xxx_boards[p->irq] == NULL)
   {
+    int result;
+    int irq_flags = 0;
+
+#ifdef AIC7XXX_OLD_ISR_TYPE
+    irg_flags = SA_INTERRUPT;
+#endif
     /*
      * Warning! This must be done before requesting the irq.  It is
      * possible for some boards to raise an interrupt as soon as
@@ -4352,17 +5053,26 @@
      * kernel, an interrupt is triggered immediately.  Therefore, we
      * must ensure the board data is correctly set before the request.
      */
-    aic7xxx_boards[config->irq] = host;
+    aic7xxx_boards[p->irq] = host;
 
     /*
-     * Register IRQ with the kernel.
+     * Register IRQ with the kernel.  Only allow sharing IRQs with
+     * PCI devices.
      */
-    if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
-       "aic7xxx", NULL))
+    if (p->chip_class == AIC_777x)
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL));
+    }
+    else
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+                "aic7xxx", NULL));
+    }
+    if (result < 0)
     {
       printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
-             config->irq);
-      aic7xxx_boards[config->irq] = NULL;
+             p->irq);
+      aic7xxx_boards[p->irq] = NULL;
       return (0);
     }
   }
@@ -4373,79 +5083,74 @@
      * registered host adapter. Add this host adapter's Scsi_Host
      * to the beginning of the linked list of hosts at the same IRQ.
      */
-    p->next = aic7xxx_boards[config->irq];
-    aic7xxx_boards[config->irq] = host;
-  }
-
-  /*
-   * Load the sequencer program, then re-enable the board -
-   * resetting the AIC-7770 disables it, leaving the lights
-   * on with nobody home. On the PCI bus you *may* be home,
-   * but then your mailing address is dynamically assigned
-   * so no one can find you anyway :-)
-   */
-  printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
-  aic7xxx_loadseq(base);
-
-  /*
-   * Set Fast Mode and Enable the board
-   */
-  outb(FASTMODE, SEQCTL + base);
-
-  if (p->chip_type == AIC_777x)
-  {
-    outb(ENABLE, BCTL + base);
+    p->next = aic7xxx_boards[p->irq];
+    aic7xxx_boards[p->irq] = host;
   }
 
-  printk("done.\n");
-
   /*
    * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
    */
   if (p->bus_type == AIC_TWIN)
   {
     /*
-     * Select Channel B.
+     * The controller is gated to channel B after a chip reset; set
+     * bus B values first.
      */
-    outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
-
-    outb(config->scsi_id_b, SCSIID + base);
-    scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
-    outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-    outb(ENSELTIMO, SIMODE1 + base);
-#endif
+    outb(p->scsi_id_b, p->base + SCSIID);
+    scsi_conf = inb(p->base + SCSICONF + 1);
+    sxfrctl1 = inb(p->base + SXFRCTL1);
+    outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+         ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
     if (p->flags & ULTRA_ENABLED)
     {
-      outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
     }
     else
     {
-      outb(DFON | SPIOEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN, p->base + SXFRCTL0);
     }
 
-    /*
-     * Select Channel A
-     */
-    outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+    if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+    {
+      /* Reset SCSI bus B. */
+      if (aic7xxx_verbose)
+        printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+
+      aic7xxx_reset_current_bus(p);
+    }
+
+    /* Select channel A */
+    outb(SELNARROW, p->base + SBLKCTL);
   }
-  outb(config->scsi_id, SCSIID + base);
-  scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
-  outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-  outb(ENSELTIMO, SIMODE1 + base);
-#endif
+
+  outb(p->scsi_id, p->base + SCSIID);
+  scsi_conf = inb(p->base + SCSICONF);
+  sxfrctl1 = inb(p->base + SXFRCTL1);
+  outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+       ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
   if (p->flags & ULTRA_ENABLED)
   {
-    outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
   }
   else
   {
-    outb(DFON | SPIOEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN, p->base + SXFRCTL0);
+  }
+
+  if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+  {
+    /* Reset SCSI bus A. */
+    if (aic7xxx_verbose)
+      printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+
+    aic7xxx_reset_current_bus(p);
+
+    /*
+     * Delay for the reset delay.
+     */
+    aic7xxx_delay(AIC7XXX_RESET_DELAY);
   }
 
   /*
@@ -4472,67 +5177,47 @@
   /*
    * Grab the disconnection disable table and invert it for our needs
    */
-  if (have_seeprom)
+  if (p->flags & USE_DEFAULTS)
   {
-    p->discenable = 0x0;
+    printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
+           "device parameters.\n");
+    p->discenable = 0xFFFF;
   }
   else
   {
-    if (config->bios == AIC_DISABLED)
-    {
-      printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI "
-             "device parameters.\n");
-      p->discenable = 0xFFFF;
-    }
-    else
-    {
-      p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
-          inb(DISC_DSB + base));
-    }
+    p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
+        inb(p->base + DISC_DSB));
   }
 
   for (i = 0; i < max_targets; i++)
   {
-    if (config->flags & USE_DEFAULTS)
+    if (p->flags & USE_DEFAULTS)
     {
-      target_settings = 0;  /* 10 MHz */
+      target_settings = 0;  /* 10 or 20 MHz depending on Ultra enable */
       p->needsdtr_copy |= (0x01 << i);
       p->needwdtr_copy |= (0x01 << i);
+      if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+        ultraenable |= (0x01 << i);
     }
     else
     {
-      if (have_seeprom)
+      target_settings = inb(p->base + TARG_SCRATCH + i);
+      if (target_settings & 0x0F)
       {
-  	target_settings = ((sc.device_flags[i] & CFXFER) << 4);
-  	if (sc.device_flags[i] & CFSYNCH)
-  	{
-  	  p->needsdtr_copy |= (0x01 << i);
-  	}
-  	if (sc.device_flags[i] & CFWIDEB)
-  	{
-  	  p->needwdtr_copy |= (0x01 << i);
-  	}
-  	if (sc.device_flags[i] & CFDISC)
-        {
-          p->discenable |= (0x01 << i);
-        }
+        p->needsdtr_copy |= (0x01 << i);
+        /*
+         * Default to asynchronous transfers (0 offset)
+         */
+        target_settings &= 0xF0;
       }
-      else
+      if (target_settings & 0x80)
       {
-        target_settings = inb(TARG_SCRATCH + base + i);
-        if (target_settings & 0x0F)
-        {
-          p->needsdtr_copy |= (0x01 << i);
-          /*
-           * Default to asynchronous transfers (0 offset)
-           */
-          target_settings &= 0xF0;
-        }
-        if (target_settings & 0x80)
-        {
-          p->needwdtr_copy |= (0x01 << i);
-          target_settings &= 0x7F;
-        }
+        p->needwdtr_copy |= (0x01 << i);
+        /*
+         * Clear the wide flag. When wide negotiation is successful,
+         * we'll enable it.
+         */
+        target_settings &= 0x7F;
       }
       if (p->flags & ULTRA_ENABLED)
       {
@@ -4543,126 +5228,477 @@
           case 0x20:
             ultraenable |= (0x01 << i);
             break;
-          case 0x40:
+          case 0x40:  /* treat 10MHz as 10MHz without Ultra enabled */
             target_settings &= ~(0x70);
             break;
           default:
             break;
         }
       }
-    }
-    outb(target_settings, (TARG_SCRATCH + base + i));
+    }
+    outb(target_settings, p->base + TARG_SCRATCH + i);
+  }
+
+  /*
+   * If we are not wide, forget WDTR. This makes the driver
+   * work on some cards that don't leave these fields cleared
+   * when BIOS is not installed.
+   */
+  if (p->bus_type != AIC_WIDE)
+  {
+    p->needwdtr_copy = 0;
+  }
+  p->needsdtr = p->needsdtr_copy;
+  p->needwdtr = p->needwdtr_copy;
+  p->orderedtag = 0;
+  outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
+  outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
+
+  /*
+   * Set the number of available hardware SCBs.
+   */
+  outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
+
+  /*
+   * 2s compliment of maximum tag value.
+   */
+  i = p->scb_data->maxscbs;
+  outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
+
+  /*
+   * Allocate enough hardware scbs to handle the maximum number of
+   * concurrent transactions we can have.  We have to make sure that
+   * the allocated memory is contiguous memory.  The Linux kmalloc
+   * routine should only allocate contiguous memory, but note that
+   * this could be a problem if kmalloc() is changed.
+   */
+  if (p->scb_data->hscbs == NULL)
+  {
+    size_t array_size;
+    unsigned int hscb_physaddr;
+
+    array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+    p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
+    if (p->scb_data->hscbs == NULL)
+    {
+      printk("aic7xxx: Unable to allocate hardware SCB array; "
+             "failing detection.\n");
+      release_region(p->base, MAXREG - MINREG);
+      /*
+       * Ensure that we only free the IRQ when there is _not_ another
+       * aic7xxx adapter sharing this IRQ.  The adapters are always
+       * added to the beginning of the list, so we can grab the next
+       * pointer and place it back in the board array.
+       */
+      if (p->next == NULL)
+      {
+        free_irq(p->irq, aic7xxx_isr);
+      }
+      aic7xxx_boards[p->irq] = p->next;
+      return(0);
+    }
+    /* At least the control byte of each SCB needs to be 0. */
+    memset(p->scb_data->hscbs, 0, array_size);
+
+    /* Tell the sequencer where it can find the hardware SCB array. */
+    hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
+    outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR);
+    outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1);
+    outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2);
+    outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3);
+  }
+
+  /*
+   * QCount mask to deal with broken aic7850s that sporadically get
+   * garbage in the upper bits of their QCNT registers.
+    */
+  outb(p->qcntmask, p->base + QCNTMASK);
+
+  if (p->flags & PAGE_ENABLED)
+  {
+    outb(p->qfullcount, p->base + FIFODEPTH);
+    outb(0, p->base + CMDOUTCNT);
+  }
+
+  /*
+   * We don't have any waiting selections or disconnected SCBs.
+   */
+  outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+  outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+
+  /*
+   * Message out buffer starts empty
+   */
+  outb(0, p->base + MSG_LEN);
+
+  /*
+   * Load the sequencer program, then re-enable the board -
+   * resetting the AIC-7770 disables it, leaving the lights
+   * on with nobody home. On the PCI bus you *may* be home,
+   * but then your mailing address is dynamically assigned
+   * so no one can find you anyway :-)
+   */
+  aic7xxx_loadseq(p);
+
+  if (p->chip_class == AIC_777x)
+  {
+    outb(ENABLE, p->base + BCTL);  /* Enable the boards BUS drivers. */
+  }
+
+  /*
+   * Unpause the sequencer before returning and enable
+   * interrupts - we shouldn't get any until the first
+   * command is sent to us by the high-level SCSI code.
+   */
+  unpause_sequencer(p, /* unpause_always */ TRUE);
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_chip_reset
+ *
+ * Description:
+ *   Perform a chip reset on the aic7xxx SCSI controller.  The controller
+ *   is paused upon return.
+ *-F*************************************************************************/
+static void
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+  unsigned char hcntrl;
+  int wait;
+
+  /* Retain the IRQ type across the chip reset. */
+  hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+
+  /*
+   * For some 274x boards, we must clear the CHIPRST bit and pause
+   * the sequencer. For some reason, this makes the driver work.
+   */
+  outb(PAUSE | CHIPRST, p->base + HCNTRL);
+
+  /*
+   * In the future, we may call this function as a last resort for
+   * error handling.  Let's be nice and not do any unecessary delays.
+   */
+  wait = 1000;  /* 1 second (1000 * 1000 usec) */
+  while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0))
+  {
+    udelay(1000);  /* 1 msec = 1000 usec */
+    wait = wait - 1;
+  }
+
+  if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+  {
+    printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
+  }
+
+  outb(hcntrl | PAUSE, p->base + HCNTRL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_alloc
+ *
+ * Description:
+ *   Allocate and initialize a host structure.  Returns NULL upon error
+ *   and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase,
+    aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+{
+  struct aic7xxx_host *p = NULL;
+  struct Scsi_Host *host;
+
+  /*
+   * Allocate a storage area by registering us with the mid-level
+   * SCSI layer.
+   */
+  host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+  if (host != NULL)
+  {
+    p = (struct aic7xxx_host *) host->hostdata;
+    memset(p, 0, sizeof(struct aic7xxx_host));
+    p->host = host;
+
+    if (scb_data != NULL)
+    {
+      /*
+       * We are sharing SCB data areas; use the SCB data pointer
+       * provided.
+       */
+      p->scb_data = scb_data;
+      p->flags |= SHARED_SCBDATA;
+    }
+    else
+    {
+      /*
+       * We are not sharing SCB data; allocate one.
+       */
+      p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+      if (p->scb_data != NULL)
+      {
+        memset(p->scb_data, 0, sizeof(scb_data_type));
+        scbq_init (&p->scb_data->free_scbs);
+      }
+      else
+      {
+        /*
+         * For some reason we don't have enough memory.  Free the
+         * allocated memory for the aic7xxx_host struct, and return NULL.
+         */
+        scsi_unregister(host);
+        p = NULL;
+      }
+    }
+    if (p != NULL)
+    {
+      p->host_no = host->host_no;
+      p->base = base;
+      p->mbase = mbase;
+      p->maddr = NULL;
+      p->flags = flags;
+      p->chip_type = chip_type;
+      p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+      p->pause = p->unpause | PAUSE;
+    }
+  }
+  return (p);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_free
+ *
+ * Description:
+ *   Frees and releases all resources associated with an instance of
+ *   the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free (struct aic7xxx_host *p)
+{
+  int i;
+
+  /*
+   * We should be careful in freeing the scb_data area.  For those
+   * adapters sharing external SCB RAM(398x), there will be only one
+   * scb_data area allocated.  The flag SHARED_SCBDATA indicates if
+   * one adapter is sharing anothers SCB RAM.
+   */
+  if (!(p->flags & SHARED_SCBDATA))
+  {
+    /*
+     * Free the allocated hardware SCB space.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
+    /*
+     * Free the driver SCBs.  These were allocated on an as-need
+     * basis.
+     */
+    for (i = 0; i < p->scb_data->numscbs; i++)
+    {
+      kfree(p->scb_data->scb_array[i]);
+    }
+    /*
+     * Free the hardware SCBs.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
+
+    /*
+     * Free the SCB data area.
+     */
+    kfree(p->scb_data);
+  }
+  /*
+   * Free the instance of the device structure.
+   */
+  scsi_unregister(p->host);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_load_seeprom
+ *
+ * Description:
+ *   Load the seeprom and configure adapter and target settings.
+ *   Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static int
+load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+  int have_seeprom = 0;
+  int i, max_targets;
+  unsigned char target_settings, scsi_conf;
+  unsigned short scarray[128];
+  struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+  }
+  switch (p->chip_type)
+  {
+    case AIC_7770:  /* None of these adapters have seeproms. */
+    case AIC_7771:
+    case AIC_7850:
+    case AIC_7855:
+      break;
+
+    case AIC_284x:
+      have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+      break;
+
+    case AIC_7861:
+    case AIC_7870:
+    case AIC_7871:
+    case AIC_7872:
+    case AIC_7874:
+    case AIC_7881:
+    case AIC_7882:
+    case AIC_7884:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(*sc)/2, C46);
+      break;
+
+    case AIC_7860:  /* Motherboard Ultra controllers might have RAID port. */
+    case AIC_7880:
+      have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46);
+      if (!have_seeprom)
+      {
+        have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66);
+      }
+      break;
+
+    case AIC_7873:  /* The 3985 adapters use the 93c56 serial EEPROM. */
+    case AIC_7883:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(scarray)/2, C56_66);
+      break;
+
+    default:
+      break;
   }
 
-  /*
-   * If we are not wide, forget WDTR. This makes the driver
-   * work on some cards that don't leave these fields cleared
-   * when BIOS is not installed.
-   */
-  if (p->bus_type != AIC_WIDE)
+  if (!have_seeprom)
   {
-    p->needwdtr_copy = 0;
+    if (aic7xxx_verbose)
+    {
+      printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+    }
+    p->flags |= USE_DEFAULTS;
   }
-  p->needsdtr = p->needsdtr_copy;
-  p->needwdtr = p->needwdtr_copy;
-  p->orderedtag = 0;
-#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
-  outb(ultraenable & 0xFF, ULTRA_ENB + base);
-  outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1);
+  else
+  {
+    if (aic7xxx_verbose)
+    {
+      printk("done\n");
+    }
+    p->flags |= HAVE_SEEPROM;
 
-  /*
-   * Set the number of available SCBs.
-   */
-  outb(config->maxhscbs, SCBCOUNT + base);
+    /*
+     * Update the settings in sxfrctl1 to match the termination settings.
+     */
+    *sxfrctl1 = 0;
 
-  /*
-   * 2s compliment of maximum tag value.
-   */
-  i = p->maxscbs;
-  outb(-i & 0xFF, COMP_SCBCOUNT + base);
+    /*
+     * First process the settings that are different between the VLB
+     * and PCI adapter seeproms.
+     */
+    if (p->chip_class == AIC_777x)
+    {
+      /* VLB adapter seeproms */
+      if (sc->bios_control & CF284XEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
 
-  /*
-   * 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);
+      if (sc->adapter_control & CF284XSTERM)
+        *sxfrctl1 |= STPWEN;
+      /*
+       * The 284x SEEPROM doesn't have a max targets field.  We
+       * set it to 16 to make sure we take care of the 284x-wide
+       * adapters.  For narrow adapters, going through the extra
+       * 8 target entries will not cause any harm since they will
+       * will not be used.
+       *
+       * XXX - We should probably break out the bus detection
+       *       from the register function so we can use it here
+       *       to tell us how many targets there really are.
+       */
+      max_targets = 16;
+    }
+    else
+    {
+      /* PCI adapter seeproms */
+      if (sc->bios_control & CFEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
 
-  /*
-   * Clear the active flags - no targets are busy.
-   */
-  outb(0, ACTIVE_A + base);
-  outb(0, ACTIVE_B + base);
+      if (sc->adapter_control & CFSTERM)
+        *sxfrctl1 |= STPWEN;
 
-  /*
-   * We don't have any waiting selections or disconnected SCBs.
-   */
-  outb(SCB_LIST_NULL, WAITING_SCBH + base);
-  outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base);
+      /* Limit to 16 targets just in case. */
+      max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
+    }
 
-  /*
-   * Message out buffer starts empty
-   */
-  outb(0, MSG_LEN + base);
+    for (i = 0; i < max_targets; i++)
+    {
+      target_settings = (sc->device_flags[i] & CFXFER) << 4;
+      if (sc->device_flags[i] & CFSYNCH)
+        target_settings |= SOFS;
+      if (sc->device_flags[i] & CFWIDEB)
+        target_settings |= WIDEXFER;
+      if (sc->device_flags[i] & CFDISC)
+        p->discenable |= (0x01 << i);
+      outb(target_settings, p->base + TARG_SCRATCH + i);
+    }
+    outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
+    outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
 
-  /*
-   * Reset the SCSI bus. Is this necessary?
-   *   There may be problems for a warm boot without resetting
-   *   the SCSI bus. Either BIOS settings in scratch RAM
-   *   will not get reinitialized, or devices may stay at
-   *   previous negotiated settings (SDTR and WDTR) while
-   *   the driver will think that no negotiations have been
-   *   performed.
-   *
-   * Some devices need a long time to "settle" after a SCSI
-   * bus reset.
-   */
-  if (!aic7xxx_no_reset)
-  {
-    printk("aic7xxx: Resetting the SCSI bus...");
-    if (p->bus_type == AIC_TWIN)
+    p->scsi_id = sc->brtime_id & CFSCSIID;
+
+    scsi_conf = (p->scsi_id & 0x7);
+    if (sc->adapter_control & CFSPARITY)
+      scsi_conf |= ENSPCHK;
+    if (sc->adapter_control & CFRESETB)
+      scsi_conf |= RESET_SCSI;
+
+    if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
     {
       /*
-       * Select Channel B.
+       * We allow the operator to override ultra enable through
+       * the boot prompt.
        */
-      outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+      if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
+      {
+        /* Treat us as a non-ultra card */
+        p->flags &= ~ULTRA_ENABLED;
+      }
+    }
 
-      outb(SCSIRSTO, SCSISEQ + base);
-      udelay(1000);
-      outb(0, SCSISEQ + base);
-
-      /* Ensure we don't get a RSTI interrupt from this. */
-      outb(CLRSCSIRSTI, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+    /* Set the host ID */
+    outb(scsi_conf, p->base + SCSICONF);
+    /* In case we are a wide card */
+    outb(p->scsi_id, p->base + SCSICONF + 1);
 
-     /*
-       * Select Channel A.
+    if (p->chip_class != AIC_777x)
+    {
+      /*
+       * Update the settings in sxfrctl1 to match the termination
+       * settings.
        */
-      outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+      *sxfrctl1 = 0;
+      configure_termination(p, sxfrctl1, sc->adapter_control,
+        (unsigned char) sc->max_targets & CFMAXTARG);
     }
-
-    outb(SCSIRSTO, SCSISEQ + base);
-    udelay(1000);
-    outb(0, SCSISEQ + base);
-
-    /* Ensure we don't get a RSTI interrupt from this. */
-    outb(CLRSCSIRSTI, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-
-    aic7xxx_delay(AIC7XXX_RESET_DELAY);
-
-    printk("done.\n");
   }
-
-  /*
-   * Unpause the sequencer before returning and enable
-   * interrupts - we shouldn't get any until the first
-   * command is sent to us by the high-level SCSI code.
-   */
-  UNPAUSE_SEQUENCER(p);
-  return (found);
+  return (have_seeprom);
 }
 
 /*+F*************************************************************************
@@ -4671,17 +5707,24 @@
  *
  * Description:
  *   Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe().  A sequence of
+ *       probe(), attach()/detach(), and init() makes more sense than
+ *       one do-it-all function.  This may be useful when (and if) the
+ *       mid-level SCSI code is overhauled.
  *-F*************************************************************************/
 int
 aic7xxx_detect(Scsi_Host_Template *template)
 {
-  int found = 0, slot, base;
-  unsigned char irq = 0;
+  int found = 0;
+  aha_status_type adapter_bios;
+  aha_chip_class_type chip_class;
+  aha_chip_type chip_type;
+  int slot, base;
+  int chan_num = 0;
+  unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0;
   int i;
-  struct aic7xxx_host_config config;
-
-  template->proc_dir = &proc_scsi_aic7xxx;
-  config.chan_num = 0;
+  struct aic7xxx_host *p;
 
   /*
    * Since we may allow sharing of IRQs, it is imperative
@@ -4690,11 +5733,15 @@
    * a NULL entry to indicate that no prior hosts have
    * been found/registered for that IRQ.
    */
-  for (i = 0; i <= NUMBER(aic7xxx_boards); i++)
+  for (i = 0; i < NUMBER(aic7xxx_boards); i++)
   {
     aic7xxx_boards[i] = NULL;
   }
 
+  template->proc_dir = &proc_scsi_aic7xxx;
+  template->name = aic7xxx_info(NULL);
+  template->sg_tablesize = AIC7XXX_MAX_SG;
+
   /*
    * Initialize the spurious count to 0.
    */
@@ -4716,33 +5763,174 @@
       continue;
     }
 
-    config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
-    if (config.type != AIC_NONE)
+    chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
+    if (chip_type != AIC_NONE)
     {
+
+      switch (chip_type)
+      {
+        case AIC_7770:
+        case AIC_7771:
+          printk("aic7xxx: <%s> at EISA %d\n",
+                 board_names[chip_type], slot);
+          break;
+        case AIC_284x:
+          printk("aic7xxx: <%s> at VLB %d\n",
+                 board_names[chip_type], slot);
+          break;
+        default:
+          break;
+      }
+
       /*
        * We found a card, allow 1 spurious interrupt.
        */
       aic7xxx_spurious_count = 1;
 
       /*
-       * We "find" a AIC-7770 if we locate the card
-       * signature and we can set it up and register
-       * it with the kernel without incident.
+       * Pause the card preserving the IRQ type.  Allow the operator
+       * to override the IRQ trigger.
        */
-      config.chip_type = AIC_777x;
-      config.base = base;
-      config.mbase = 0;
-      config.irq = irq;
-      config.parity = AIC_ENABLED;
-      config.low_term = AIC_UNKNOWN;
-      config.high_term = AIC_UNKNOWN;
-      config.flags = 0;
-      if (aic7xxx_extended)
-        config.flags |= EXTENDED_TRANSLATION;
-      config.bus_speed = DFTHRSH_100;
-      config.busrtime = BOFF;
-      found += aic7xxx_register(template, &config);
+      if (aic7xxx_irq_trigger == 1)
+        hcntrl = IRQMS;  /* Level */
+      else if (aic7xxx_irq_trigger == 0)
+        hcntrl = 0;  /* Edge */
+      else
+        hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
+      outb(hcntrl | PAUSE, base + HCNTRL);
+
+      irq = inb(INTDEF + base) & 0x0F;
+      switch (irq)
+      {
+        case 9:
+        case 10:
+        case 11:
+        case 12:
+        case 14:
+        case 15:
+          break;
+
+        default:
+          printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+          "level, ignoring.\n");
+          irq = 0;
+          break;
+      }
+
+      if (irq != 0)
+      {
+        p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
+        if (p == NULL)
+        {
+          printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+          continue;
+        }
+        p->irq = irq & 0x0F;
+        p->chip_class = AIC_777x;
+#ifdef AIC7XXX_PAGE_ENABLE
+        p->flags |= PAGE_ENABLED;
+#endif
+        p->instance = found;
+        if (aic7xxx_extended)
+        {
+          p->flags |= EXTENDED_TRANSLATION;
+        }
+        aic7xxx_chip_reset(p);
+
+        switch (p->chip_type)
+        {
+          case AIC_7770:
+          case AIC_7771:
+          {
+            unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL);
+
+            /*
+             * Get the primary channel information.  Right now we don't
+             * do anything with this, but someday we will be able to inform
+             * the mid-level SCSI code which channel is primary.
+             */
+            if (biosctrl & CHANNEL_B_PRIMARY)
+            {
+              p->flags |= FLAGS_CHANNEL_B_PRIMARY;
+            }
+
+            if ((biosctrl & BIOSMODE) == BIOSDISABLED)
+            {
+              p->flags |= USE_DEFAULTS;
+            }
+            break;
+          }
+
+          case AIC_284x:
+            if (!load_seeprom(p, &sxfrctl1))
+            {
+              if (aic7xxx_verbose)
+                printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+            }
+            break;
+
+          default:  /* Won't get here. */
+            break;
+        }
+        printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ",
+               (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq,
+               (p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+        /*
+         * Check for Rev C or E boards. Rev E boards can supposedly have
+         * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+         * It's still not clear extactly what is different about the Rev E
+         * boards, but we think it allows 8 bit entries in the QOUTFIFO to
+         * support "paging" SCBs (more than 4 commands can be active at once).
+         *
+         * The Rev E boards have a read/write autoflush bit in the
+         * SBLKCTL register, while in the Rev C boards it is read only.
+         */
+        sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS;
+        outb(sblkctl, p->base + SBLKCTL);
+        if (inb(p->base + SBLKCTL) == sblkctl)
+        {
+          /*
+           * We detected a Rev E board, we allow paging on this board.
+           */
+          printk("Revision >= E\n");
+          outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+        }
+        else
+        {
+          /* Do not allow paging. */
+          p->flags &= ~PAGE_ENABLED;
+          printk("Revision <= C\n");
+        }
+
+        if (aic7xxx_verbose)
+          printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                 (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
 
+        /*
+         * Set the FIFO threshold and the bus off time.
+         */
+        hostconf = inb(p->base + HOSTCONF);
+        outb(hostconf & DFTHRSH, p->base + BUSSPD);
+        outb((hostconf << 2) & BOFF, p->base + BUSTIME);
+
+        /*
+         * Try to initialize the card and register it with the kernel.
+         */
+        if (aic7xxx_register(template, p))
+        {
+          /*
+           * We successfully found a board and registered it.
+           */
+          found = found + 1;
+        }
+        else
+        {
+          /*
+           * Something went wrong; release and free all resources.
+           */
+          aic7xxx_free(p);
+        }
+      }
       /*
        * Disallow spurious interrupts.
        */
@@ -4758,15 +5946,15 @@
   {
     struct
     {
-      unsigned short vendor_id;
-      unsigned short device_id;
-      aha_type       card_type;
-      aha_chip_type  chip_type;
+      unsigned short      vendor_id;
+      unsigned short      device_id;
+      aha_chip_type       chip_type;
+      aha_chip_class_type chip_class;
     } 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_7860, AIC_7860, AIC_785x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
       {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},
@@ -4779,14 +5967,14 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
     };
 
-    int error;
+    int error, flags;
     int done = 0;
     unsigned int iobase, mbase;
     unsigned short index = 0;
     unsigned char pci_bus, pci_device_fn;
-    unsigned int  csize_lattime;
-    unsigned int  class_revid;
-    unsigned int  devconfig;
+    unsigned char ultra_enb = 0;
+    unsigned int  devconfig, class_revid;
+    scb_data_type *shared_scb_data = NULL;
     char rev_id[] = {'B', 'C', 'D'};
 
     for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
@@ -4803,36 +5991,33 @@
         }
         else  /* Found an Adaptec PCI device. */
         {
-          config.type = aic7xxx_pci_devices[i].card_type;
-          config.chip_type = aic7xxx_pci_devices[i].chip_type;
-          config.chan_num = 0;
-          config.bios = AIC_ENABLED;  /* Assume bios is enabled. */
-          config.flags = 0;
-          config.busrtime = 40;
-          switch (config.type)
+          chip_class = aic7xxx_pci_devices[i].chip_class;
+          chip_type = aic7xxx_pci_devices[i].chip_type;
+          chan_num = 0;
+          flags = 0;
+          switch (aic7xxx_pci_devices[i].chip_type)
           {
             case AIC_7850:
             case AIC_7855:
-            case AIC_7860:
-            case AIC_7861:
-              config.bios = AIC_DISABLED;
-              config.flags |= USE_DEFAULTS;
-              config.bus_speed = DFTHRSH_100;
+              flags |= USE_DEFAULTS;
               break;
 
             case AIC_7872:  /* 3940 */
             case AIC_7882:  /* 3940-Ultra */
-              config.chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
+              flags |= MULTI_CHANNEL;
+              chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
               number_of_3940s++;
               break;
 
             case AIC_7873:  /* 3985 */
             case AIC_7883:  /* 3985-Ultra */
-              config.chan_num = number_of_3985s;  /* Has 3 controllers */
+              chan_num = number_of_3985s;  /* Has 3 controllers */
+              flags |= MULTI_CHANNEL;
               number_of_3985s++;
               if (number_of_3985s == 3)
               {
                 number_of_3985s = 0;
+                shared_scb_data = NULL;
               }
               break;
 
@@ -4849,39 +6034,165 @@
                                             PCI_INTERRUPT_LINE, &irq);
           error += pcibios_read_config_dword(pci_bus, pci_device_fn,
                                             PCI_BASE_ADDRESS_1, &mbase);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             DEVCONFIG, &devconfig);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             CLASS_PROGIF_REVID, &class_revid);
+
+          printk("aic7xxx: <%s> at PCI %d\n",
+                 board_names[chip_type], PCI_SLOT(pci_device_fn));
 
           /*
-           * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+           * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
            * we mask it off.
            */
           iobase &= PCI_BASE_ADDRESS_IO_MASK;
 
+          p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags,
+                            shared_scb_data);
+
+          if (p == NULL)
+          {
+            printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+            continue;
+          }
+
+          /* Remember to set the channel number, irq, and chip class. */
+          p->chan_num = chan_num;
+          p->irq = irq;
+          p->chip_class = chip_class;
+#ifdef AIC7XXX_PAGE_ENABLE
+          p->flags |= PAGE_ENABLED;
+#endif
+          p->instance = found;
+
           /*
-           * Read the PCI burst size and latency timer.
+           * Remember how the card was setup in case there is no seeprom.
            */
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CSIZE_LATTIME, &csize_lattime);
-          printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d "
-                 "PCLKS\n", (int) (csize_lattime & CACHESIZE),
-                 (csize_lattime >> 8) & 0x000000ff);
+          p->scsi_id = inb(p->base + SCSIID) & OID;
+          if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+          {
+            p->flags |= ULTRA_ENABLED;
+            ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+          }
+	  sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CLASS_PROGIF_REVID, &class_revid);
-          if ((class_revid & DEVREVID) < 3)
+          aic7xxx_chip_reset(p);
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
+          if (devconfig & RAMPSM)
           {
-            printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type],
-                   rev_id[class_revid & DEVREVID]);
+            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
+                   "access.\n");
+            /*
+             * XXX - Assume 9 bit SRAM and enable parity checking.
+             */
+            devconfig |= EXTSCBPEN;
+
+            /*
+             * XXX - Assume fast SRAM and only enable 2 cycle access if we
+             *       are sharing the SRAM across multiple adapters (398x).
+             */
+            if ((devconfig & MPORTMODE) == 0)
+            {
+              devconfig |= EXTSCBTIME;
+            }
+            devconfig &= ~SCBRAMSEL;
+            pcibios_write_config_dword(pci_bus, pci_device_fn,
+                                       DEVCONFIG, devconfig);
           }
+#endif
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             DEVCONFIG, &devconfig);
-          if (error)
+          if ((p->flags & USE_DEFAULTS) == 0)
+          {
+            load_seeprom(p, &sxfrctl1);
+          }
+
+          /*
+           * Take the LED out of diagnostic mode
+           */
+          sblkctl = inb(p->base + SBLKCTL);
+          outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+
+          /*
+           * We don't know where this is set in the SEEPROM or by the
+           * BIOS, so we default to 100%.
+           */
+          outb(DFTHRSH_100, p->base + DSPCISTATUS);
+
+          if (p->flags & USE_DEFAULTS)
           {
-            panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
-                  error);
+            int j;
+            /*
+             * Default setup; should only be used if the adapter does
+             * not have a SEEPROM.
+             */
+            /*
+             * Check the target scratch area to see if someone set us
+             * up already.  We are previously set up if the scratch
+             * area contains something other than all zeroes and ones.
+             */
+            for (j = TARG_SCRATCH; j < 0x60; j++)
+            {
+              if (inb(p->base + j) != 0x00)      /* Check for all zeroes. */
+                break;
+            }
+            if (j == TARG_SCRATCH)
+            {
+              for (j = TARG_SCRATCH; j < 0x60; j++)
+              {
+                if (inb(p->base + 1) != 0xFF)    /* Check for all ones. */
+                  break;
+              }
+            }
+            if ((j != 0x60) && (p->scsi_id != 0))
+            {
+              p->flags &= ~USE_DEFAULTS;
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
+              }
+            }
+            else
+            {
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: No BIOS found; using default "
+                       "settings.\n");
+              }
+              /*
+               * Assume only one connector and always turn on
+               * termination.
+               */
+              sxfrctl1 = STPWEN;
+              p->scsi_id = 7;
+            }
+            outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+                 p->base + SCSICONF);
+            /* In case we are a wide card. */
+            outb(p->scsi_id, p->base + SCSICONF + 1);
+            if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0))
+            {
+              /*
+               * If there wasn't a BIOS or the board wasn't in this mode
+               * to begin with, turn off Ultra.
+               */
+              p->flags &= ~ULTRA_ENABLED;
+            }
           }
 
-          printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig);
+          /*
+           * Print some additional information about the adapter.
+           */
+          printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, "
+                 "IO Mem 0x%x, IRQ %d",
+                 (p->flags & USE_DEFAULTS) ? "dis" : "en",
+                 p->base, p->mbase, p->irq);
+          if ((class_revid & DEVREVID) < 3)
+          {
+            printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+          }
+          printk("\n");
 
           /*
            * I don't think we need to bother with allowing
@@ -4890,58 +6201,57 @@
            */
           aic7xxx_spurious_count = 1;
 
-          config.base = iobase;
-          config.mbase = mbase;
-          config.irq = irq;
-          config.parity = AIC_ENABLED;
-          config.low_term = AIC_UNKNOWN;
-          config.high_term = AIC_UNKNOWN;
           if (aic7xxx_extended)
-            config.flags |= EXTENDED_TRANSLATION;
-#ifdef AIC7XXX_SHARE_SCBs
-          if (devconfig & RAMPSM)
-#else
-          if ((devconfig & RAMPSM) && (config.type != AIC_7873) &&
-              (config.type != AIC_7883))
-#endif
+            p->flags |= EXTENDED_TRANSLATION;
+
+          if (aic7xxx_verbose)
+            printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                   (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+          /*
+           * Put our termination setting into sxfrctl1 now that the
+           * generic initialization is complete.
+           */
+          sxfrctl1 |= inb(p->base + SXFRCTL1);
+          outb(sxfrctl1, p->base + SXFRCTL1);
+
+          if (aic7xxx_register(template, p) == 0)
+          {
+            aic7xxx_free(p);
+          }
+          else
           {
+            found = found + 1;
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
             /*
-             * 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.
-             *
-             * It seems that early versions of the aic7870 didn't use these
-             * bits, hence the hack for the 3940 above.  I would guess that
-             * recent 3940s using later aic7870 or aic7880 chips do actually
-             * set RAMPSM.
+             * Set the shared SCB data once we've successfully probed a
+             * 398x adapter.
              *
-             * The documentation isn't clear, but it sounds like the value
-             * written to devconfig must not have RAMPSM set.  The second
-             * sixteen bits of the register are R/O anyway, so it shouldn't
-             * affect RAMPSM either way.
+             * Note that we can only do this if the use of external
+             * SCB RAM is enabled.
              */
-            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
-                   "access.\n");
-            devconfig &= ~(RAMPSM | SCBRAMSEL);
-            pcibios_write_config_dword(pci_bus, pci_device_fn,
-                                       DEVCONFIG, devconfig);
+            if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+            {
+              if (shared_scb_data == NULL)
+              {
+                shared_scb_data = p->scb_data;
+              }
+            }
+#endif
           }
-          found += aic7xxx_register(template, &config);
 
+          index++;
           /*
            * Disable spurious interrupts.
            */
           aic7xxx_spurious_count = 0;
-
-          index++;
         }  /* Found an Adaptec PCI device. */
       }
     }
   }
 #endif CONFIG_PCI
 
-  template->name = aic7xxx_info(NULL);
   return (found);
 }
 
@@ -4957,45 +6267,45 @@
 aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
     struct aic7xxx_scb *scb)
 {
-  unsigned int addr; /* must be 32 bits */
   unsigned short mask;
+  struct aic7xxx_hwscb *hscb;
 
   mask = (0x01 << TARGET_INDEX(cmd));
+  hscb = scb->hscb;
+
   /*
    * Setup the control byte if we need negotiation and have not
    * already requested it.
    */
-#ifdef AIC7XXX_TAGGED_QUEUEING
-  if (cmd->device->tagged_queue)
+  if (p->discenable & mask)
   {
-    cmd->tag = scb->tag;
-    cmd->device->current_tag = scb->tag;
-    scb->control |= TAG_ENB;
-    p->device_status[TARGET_INDEX(cmd)].commands_sent++;
-    if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200)
-    {
-      scb->control |= 0x02;
-      p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
-    }
-#if 0
-    if (p->orderedtag & mask)
+    hscb->control |= DISCENB;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+    if (cmd->device->tagged_queue)
     {
-      scb->control |= 0x02;
-      p->orderedtag = p->orderedtag & ~mask;
+      cmd->tag = hscb->tag;
+      p->device_status[TARGET_INDEX(cmd)].commands_sent++;
+      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+      {
+        hscb->control |= MSG_SIMPLE_Q_TAG;
+      }
+      else
+      {
+        hscb->control |= MSG_ORDERED_Q_TAG;
+        p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+      }
     }
-#endif
-  }
-#endif
-  if (p->discenable & mask)
-  {
-    scb->control |= DISCENB;
+#endif  /* Tagged queueing */
   }
+
   if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
   {
     p->wdtr_pending |= mask;
-    scb->control |= NEEDWDTR;
+    hscb->control |= MK_MESSAGE;
+    scb->flags |= SCB_MSGOUT_WDTR;
 #if 0
-    printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
+    printk("scsi%d: Sending WDTR request to target %d.\n",
+           p->host_no, cmd->target);
 #endif
   }
   else
@@ -5003,19 +6313,20 @@
     if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
     {
       p->sdtr_pending |= mask;
-      scb->control |= NEEDSDTR;
+      hscb->control |= MK_MESSAGE;
+      scb->flags |= SCB_MSGOUT_SDTR;
 #if 0
-      printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
+      printk("scsi%d: Sending SDTR request to target %d.\n",
+             p->host_no, cmd->target);
 #endif
     }
   }
-
 #if 0
   printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
          "mask(0x%x).\n",
 	 cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
 #endif
-  scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+  hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
 	((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
 
   /*
@@ -5029,9 +6340,8 @@
    * XXX - this relies on the host data being stored in a
    *       little-endian format.
    */
-  addr = VIRT_TO_BUS(cmd->cmnd);
-  scb->SCSI_cmd_length = cmd->cmd_len;
-  memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+  hscb->SCSI_cmd_length = cmd->cmd_len;
+  hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
 
   if (cmd->use_sg)
   {
@@ -5051,15 +6361,16 @@
       scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
       scb->sg_list[i].length = (unsigned int) sg[i].length;
     }
-    scb->SG_segment_count = cmd->use_sg;
-    addr = VIRT_TO_BUS(scb->sg_list);
-    memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-    memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-           sizeof(scb->data_pointer));
-    scb->data_count = scb->sg_list[0].length;
+    hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list);
+    hscb->SG_segment_count = cmd->use_sg;
+    scb->sg_count = hscb->SG_segment_count;
+
+    /* Copy the first SG into the data pointer area. */
+    hscb->data_pointer = scb->sg_list[0].address;
+    hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
 #if 0
     printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
-           cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+           cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
 #endif
   }
   else
@@ -5068,28 +6379,23 @@
   printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
 	(unsigned long) cmd->request_buffer, cmd->request_bufflen);
 #endif
-    if (cmd->request_bufflen == 0)
+    if (cmd->request_bufflen)
     {
-      /*
-       * 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));
-      scb->data_count = 0;
+      hscb->SG_segment_count = 1;
+      scb->sg_count = 1;
+      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
+      scb->sg_list[0].length = cmd->request_bufflen;
+      hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]);
+      hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
+      hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer);
     }
     else
     {
-      scb->SG_segment_count = 1;
-      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
-      scb->sg_list[0].length = cmd->request_bufflen;
-      addr = VIRT_TO_BUS(&scb->sg_list[0]);
-      memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-      scb->data_count = scb->sg_list[0].length;
-      addr = VIRT_TO_BUS(cmd->request_buffer);
-      memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer));
+      hscb->SG_segment_count = 0;
+      scb->sg_count = 0;
+      hscb->SG_list_pointer = 0;
+      hscb->data_pointer = 0;
+      hscb->data_count = SCB_LIST_NULL << 24;
     }
   }
 }
@@ -5107,7 +6413,6 @@
   long processor_flags;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
-  u_char curscb, intstat;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   if (p->host != cmd->host)
@@ -5139,34 +6444,21 @@
 	cmd->lun & 0x07);
 #endif
 
-  /*
-   * This is a critical section, since we don't want the interrupt
-   * routine mucking with the host data or the card.  For this reason
-   * it is nice to know that this function can only be called in one
-   * of two ways from scsi.c  First, as part of a routine queue command,
-   * in which case, the irq for our card is disabled before this
-   * function is called.  This doesn't help us if there is more than
-   * one card using more than one IRQ in our system, therefore, we
-   * should disable all interrupts on these grounds alone.  Second,
-   * this can be called as part of the scsi_done routine, in which case
-   * we are in the aic7xxx_isr routine already and interrupts are
-   * disabled, therefore we should saveflags first, then disable the
-   * interrupts, do our work, then restore the CPU flags. If it weren't
-   * for the possibility of more than one card using more than one IRQ
-   * in our system, we wouldn't have to touch the interrupt flags at all.
-   */
-  save_flags(processor_flags);
-  cli();
-
+  if (p->device_status[TARGET_INDEX(cmd)].active_cmds
+      > cmd->device->queue_depth)
+  {
+    printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
+           p->host_no, cmd->target, cmd->channel);
+  }
   scb = aic7xxx_allocate_scb(p);
   if (scb == NULL)
   {
-    panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n");
+    panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
   }
   else
   {
     scb->cmd = cmd;
-    aic7xxx_position(cmd) = scb->tag;
+    aic7xxx_position(cmd) = scb->hscb->tag;
 #if 0
     debug_scb(scb);
 #endif;
@@ -5178,14 +6470,14 @@
     aic7xxx_buildscb(p, cmd, scb);
 
 #if 0
-    if (scb != (p->scb_array[scb->position]))
+    if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
     {
       printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
              "address.\n");
     }
     printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
-	   scb->position, (unsigned int) scb->cmd,
-	   scb->state, (unsigned int) p->free_scb);
+	   scb->hscb->tag, (unsigned int) scb->cmd,
+	   scb->flags, (unsigned int) p->free_scb);
 #endif
 
     /*
@@ -5200,70 +6492,28 @@
     cmd->host_scribble = NULL;
     memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
 
-    if (scb->position != SCB_LIST_NULL)
-    {
-      /* We've got a valid slot, yeah! */
-      if (p->flags & IN_ISR)
-      {
-        scbq_insert_tail(&p->assigned_scbs, scb);
-        scb->state |= SCB_ASSIGNEDQ;
-      }
-      else
-      {
-        /*
-         * Pause the sequencer so we can play with its registers -
-         * wait for it to acknowledge the pause.
-         *
-         * XXX - should the interrupts be left on while doing this?
-         */
-        PAUSE_SEQUENCER(p);
-        intstat = inb(INTSTAT + p->base);
-
-        /*
-         * Save the SCB pointer and put our own pointer in - this
-         * selects one of the four banks of SCB registers. Load
-         * the SCB, then write its pointer into the queue in FIFO
-         * and restore the saved SCB pointer.
-         */
-        curscb = inb(SCBPTR + p->base);
-        outb(scb->position, SCBPTR + p->base);
-        aic7xxx_putscb(p, scb);
-        outb(curscb, SCBPTR + p->base);
-        outb(scb->position, QINFIFO + p->base);
-        scb->state |= SCB_ACTIVE;
+    scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
-        /*
-         * Guard against unpausing the sequencer if there is an interrupt
-         * waiting to happen.
-         */
-        if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-        {
-          UNPAUSE_SEQUENCER(p);
-        }
-      }
-    }
-    else
+    save_flags(processor_flags);
+    cli();
+    scbq_insert_tail(&p->waiting_scbs, scb);
+    if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
     {
-      scb->state |= SCB_WAITINGQ;
-      scbq_insert_tail(&p->waiting_scbs, scb);
-      if (!(p->flags & IN_ISR))
-      {
-        aic7xxx_run_waiting_queues(p);
-      }
+      aic7xxx_run_waiting_queues(p);
     }
 
+    restore_flags(processor_flags);
 #if 0
     printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
-           (long) cmd, (long) scb->cmd, scb->position);
+           (long) cmd, (long) scb->cmd, scb->hscb->tag);
 #endif;
-    restore_flags(processor_flags);
   }
   return (0);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_abort_reset
+ *   aic7xxx_bus_device_reset
  *
  * Description:
  *   Abort or reset the current SCSI command(s).  If the scb has not
@@ -5275,204 +6525,257 @@
 static int
 aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  struct aic7xxx_scb  *scb;
+  struct aic7xxx_scb   *scb;
+  struct aic7xxx_hwscb *hscb;
   unsigned char bus_state;
-  int base, result = -1;
+  int result = -1;
   char channel;
 
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  hscb = scb->hscb;
+
+  /*
+   * Ensure that the card doesn't do anything behind our back.
+   * Also make sure that we didn't just miss an interrupt that
+   * could affect this abort/reset.
+   */
+  pause_sequencer(p);
+  while (inb(p->base + INTSTAT) & INT_PEND);
+  {
+    aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
+    pause_sequencer(p);
+  } 
+  if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
+  {
+    result = SCSI_RESET_NOT_RUNNING;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    return(result);
+  }
 
-  channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
-  if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS))
+
+  printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
+         p->host_no, TC_OF_SCB(scb), scb->flags);
+  bus_state = inb(p->base + LASTPHASE);
+
+  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:
+      /*
+       * We're not in a valid phase, so assume we're idle.
+       */
+      printk("while idle, LASTPHASE = 0x%x, ", bus_state);
+      break;
+  }
+  printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+         inb(p->base + SCSISIGI),
+         inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+         inb(p->base + SSTAT0), inb(p->base + SSTAT1));
 
-    if (scb->state & SCB_IN_PROGRESS)
+  channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
+  /*
+   * Determine our course of action.
+   */
+  if (scb->flags & SCB_ABORT)
+  {
+    /*
+     * Been down this road before; do a full bus reset.
+     */
+    scb->flags |= SCB_RECOVERY_SCB;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    result = -1;
+  }
+#if 0
+  else if (hscb->control & TAG_ENB)
     {
       /*
-       * Ensure that the card doesn't do anything
-       * behind our back.
+       * We could be starving this command; try sending and ordered tag
+       * command to the target we come from.
        */
-      PAUSE_SEQUENCER(p);
+      scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
+      p->orderedtag = p->orderedtag | 0xFF;
+      result = SCSI_RESET_PENDING;
+      unpause_sequencer(p, /* unpause_always */ TRUE);
+      printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
+             p->host_no);
+    }
+#endif
+  else
+  {
+    unsigned char active_scb_index, saved_scbptr;
+    struct aic7xxx_scb *active_scb;
 
-      printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
-      bus_state = inb(LASTPHASE + p->base);
+    /*
+     * Send an Abort Message:
+     * The target that is holding up the bus may not be the same as
+     * the one that triggered this timeout (different commands have
+     * different timeout lengths).  Our strategy here is to queue an
+     * abort message to the timed out target if it is disconnected.
+     * Otherwise, if we have an active target we stuff the message buffer
+     * with an abort message and assert ATN in the hopes that the target
+     * will let go of the bus and go to the mesgout phase.  If this
+     * fails, we'll get another timeout a few seconds later which will
+     * attempt a bus reset.
+     */
+    saved_scbptr = inb(p->base + SCBPTR);
+    active_scb_index = inb(p->base + SCB_TAG);
+    active_scb = p->scb_data->scb_array[active_scb_index];
 
-      switch (bus_state)
+    if (bus_state != P_BUSFREE)
+    {
+      if (active_scb_index >= p->scb_data->numscbs)
       {
-	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;
+        /*
+         * Perform a bus reset.
+         *
+         * XXX - We want to queue an abort for the timedout SCB
+         *       instead.
+         */
+        result = -1;
+        printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+               "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
       }
-      printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI));
-
-      /*
-       * 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
-       * and the SCB is not paged 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 | SCB_PAGED_OUT)))
+      else
       {
-#if 0
-	if (scb->control & TAG_ENB)
-	{
+        /* Send the abort message to the active SCB. */
+        outb(1, p->base + MSG_LEN);
+        if (active_scb->hscb->control & TAG_ENB)
+        {
+          outb(MSG_ABORT_TAG, p->base + MSG_OUT);
+        }
+        else
+        {
+          outb(MSG_ABORT, p->base + MSG_OUT);
+        }
+        outb(bus_state | ATNO, p->base + SCSISIGO);
+        printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
+               p->host_no);
+        active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
+        if (active_scb != scb)
+        {
           /*
-           * We could be starving this command; try sending and ordered tag
-           * command to the target we come from.
+           * 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.
            */
-          scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG;
-          p->orderedtag = p->orderedtag | 0xFF;
           result = SCSI_RESET_PENDING;
-          UNPAUSE_SEQUENCER(p);
-          printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n");
-	}
-#endif
-	unsigned char active_scb, control;
-	struct aic7xxx_scb *active_scbp;
+          aic7xxx_error(active_scb->cmd) = DID_RESET;
+        }
+        else
+        {
+          aic7xxx_error(scb->cmd) = DID_RESET;
+          result = SCSI_RESET_PENDING;
+        }
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+      }
+    }
+    else
+    {
+      unsigned char hscb_index, linked_next;
+      int disconnected;
 
-	/*
-	 * 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[inb(SCB_TAG + base)]);
-	control = inb(SCB_CONTROL + base);
+      disconnected = FALSE;
+      hscb_index = aic7xxx_find_scb(p, scb);
+      if (hscb_index == SCB_LIST_NULL)
+      {
+        disconnected = TRUE;
+        linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+      }
+      else
+      {
+        outb(hscb_index, p->base + SCBPTR);
+        if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+        {
+          disconnected = TRUE;
+        }
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+      }
+      if (disconnected)
+      {
+        /*
+         * Simply set the ABORT_SCB control bit and preserve the
+         * linked next pointer.
+         */
+        scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+        scb->hscb->data_count &= ~0xFF000000;
+        scb->hscb->data_count |= linked_next << 24;
+        if ((p->flags & PAGE_ENABLED) == 0)
+        {
+          scb->hscb->control &= ~DISCONNECTED;
+        }
+        scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+        if (hscb_index != SCB_LIST_NULL)
+        {
+          unsigned char scb_control;
 
-	/*
-	 * Test to see if scbp is disconnected
-	 */
-	outb(scb->position, SCBPTR + base);
-	if (inb(SCB_CONTROL + base) & DISCONNECTED)
-	{
-#ifdef AIC7XXX_DEBUG_ABORT
-          printk("aic7xxx: (abort_scb) scb %d is disconnected; "
-                 "bus device reset message queued.\n", scb->position);
-#endif
-          if (p->flags & PAGE_ENABLED)
-          {
-            /* Pull this SCB out of the disconnected list. */
-            u_char prev = inb(SCB_PREV + base);
-            u_char next = inb(SCB_NEXT + base);
-            if (prev == SCB_LIST_NULL)
-            {
-              /* Head of list */
-              outb(next, DISCONNECTED_SCBH + base);
-            }
-            else
-            {
-              outb(prev, SCBPTR + base);
-              outb(next, SCB_NEXT + base);
-              if (next != SCB_LIST_NULL)
-              {
-        	outb(next, SCBPTR + base);
-        	outb(prev, SCB_PREV + base);
-              }
-              outb(scb->position, SCBPTR + base);
-            }
-          }
-	  scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-          scb->control = scb->control & DISCENB;
-          scb->SCSI_cmd_length = 0;
-	  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);
-	  outb(active_scb, SCBPTR + base);
-          result = SCSI_RESET_PENDING;
-	  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);
-            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
-               *       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_error(scb->cmd) = DID_RESET;
-            /*
-             * Restore the active SCB and unpause the sequencer.
-             */
-            outb(active_scb, SCBPTR + base);
-            if (active_scbp != scb)
-            {
-              /*
-               * The mid-level SCSI code requested us to reset a command
-               * different from the one that we actually reset.  Return
-               * a "not running" indication and hope that the SCSI code
-               * will Do the Right Thing (tm).
-               */
-              result = SCSI_RESET_NOT_RUNNING;
-            }
-            else
-            {
-              result = SCSI_RESET_PENDING;
-            }
-            UNPAUSE_SEQUENCER(p);
-	  }
-	}
+          scb_control = inb(p->base + SCB_CONTROL);
+          outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+        }
+        /*
+         * Actually requeue this SCB in case we can select the
+         * device before it reconnects.  If the transaction we
+         * want to abort is not tagged, unbusy it first so that
+         * we don't get held back from sending the command.
+         */
+        if ((scb->hscb->control & TAG_ENB) == 0)
+        {
+          unsigned char target;
+          int lun;
+
+          target = scb->cmd->target;
+          lun = scb->cmd->lun;
+          aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
+              0, /* requeue */ TRUE);
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
+               p->host_no, TC_OF_SCB(scb));
+        scbq_insert_head(&p->waiting_scbs, scb);
+        scb->flags |= SCB_WAITINGQ;
+        outb(saved_scbptr, p->base + SCBPTR);
+        if ((p->flags & IN_ISR) == 0)
+        {
+          /*
+           * Processing the waiting queue may unpause us.
+           */
+          aic7xxx_run_waiting_queues(p);
+          /*
+           * If we are using AAP, aic7xxx_run_waiting_queues() will not
+           * unpause us, so ensure we are unpaused.
+           */
+          unpause_sequencer(p, /*unpause_always*/ FALSE);
+        }
+        else
+        {
+          unpause_sequencer(p, /*unpause_always*/ TRUE);
+        }
+        result = SCSI_RESET_PENDING;
+      }
+      else
+      {
+        scb->flags |= SCB_RECOVERY_SCB;
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+        result = -1;
       }
     }
   }
-  /* Make sure the sequencer is unpaused upon return. */
-  if (result == -1)
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
   return (result);
 }
 
@@ -5490,16 +6793,48 @@
   struct aic7xxx_scb  *scb = NULL;
   struct aic7xxx_host *p;
   int    base, result;
+  unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
 
+  save_flags(processor_flags);
+  cli();
+
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n",
-         scb->position, TCL_OF_SCB(scb));
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Abort called with no SCB for cmd.\n");
+  }
 #endif
 
+  if (p->flags & IN_TIMEOUT)
+  {
+    /*
+     * We've already started a recovery operation.
+     */
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+    {
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_PENDING);
+    }
+    else
+    {
+      /*
+       * This is the second time we've tried to abort the recovery
+       * SCB.  We want the mid-level SCSI code to call the reset
+       * function to reset the SCSI bus.
+       */
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_NOT_RUNNING);
+    }
+  }
   if (cmd->serial_number != cmd->serial_number_at_timeout)
   {
     result = SCSI_ABORT_NOT_RUNNING;
@@ -5508,14 +6843,34 @@
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
-  else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS)))
+  else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
   else
   {
-    result = SCSI_ABORT_SNOOZE;
+    /*
+     * XXX - Check use of IN_TIMEOUT to see if we're Doing the
+     *       Right Thing with it.
+     */
+    p->flags |= IN_TIMEOUT;
+    result = aic7xxx_bus_device_reset(p, scb->cmd);
+    switch (result)
+    {
+      case SCSI_RESET_NOT_RUNNING:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_NOT_RUNNING;
+        break;
+      case SCSI_RESET_PENDING:
+        result = SCSI_ABORT_PENDING;
+        break;
+      default:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_SNOOZE;
+        break;
+     }
   }
+  restore_flags(processor_flags);
   return (result);
 }
 
@@ -5535,18 +6890,27 @@
 {
   struct aic7xxx_scb *scb = NULL;
   struct aic7xxx_host *p;
-  int    base, found, tindex, min_target, max_target, result = -1;
+  int    base, found, tindex, min_target, max_target;
+  int    result = -1;
   char   channel = 'A';
   unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
   channel = cmd->channel ? 'B': 'A';
-  tindex = (cmd->channel << 4) | cmd->target;
+  tindex = (cmd->channel << 3) | cmd->target;
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
+#ifdef 0   /* AIC7XXX_DEBUG_ABORT */
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Reset called with no SCB for cmd.\n");
+  }
 #endif
 
   /* 
@@ -5561,34 +6925,45 @@
   if (scb->cmd != cmd)
     scb = NULL;
 
-  if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) 
-      && (scb != NULL))
+  if (p->flags & IN_TIMEOUT)
   {
     /*
-     * Attempt a bus device reset if commands have completed successfully
-     * since the last bus device reset, or it has been less than 100ms
-     * since the last reset.
+     * We've already started a recovery operation.
      */
-    if ((p->flags & DEVICE_SUCCESS) ||
-        ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
     {
-      if (cmd->serial_number != cmd->serial_number_at_timeout)
-      {
-        result = SCSI_RESET_NOT_RUNNING;
-      }
-      else
+      restore_flags(processor_flags);
+      return (SCSI_RESET_PENDING);
+    }
+  }
+  else
+  {
+    if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
+        && (scb != NULL))
+    {
+      /*
+       * Attempt a bus device reset if commands have completed successfully
+       * since the last bus device reset, or it has been less than 100ms
+       * since the last reset.
+       */
+      if ((p->flags & DEVICE_SUCCESS) ||
+          ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
       {
-        if (scb == NULL)
+	if (cmd->serial_number != cmd->serial_number_at_timeout)
+	{
+          result = SCSI_RESET_NOT_RUNNING;
+	}
+	else if (scb == NULL)
         {
           result = SCSI_RESET_NOT_RUNNING;
         }
         else if (flags & SCSI_RESET_ASYNCHRONOUS)
         {
-          if (scb->state & SCB_ABORTED)
+          if (scb->flags & SCB_ABORTED)
           {
             result = SCSI_RESET_PENDING;
           }
-          else if (!(scb->state & SCB_IN_PROGRESS))
+          else if (!(scb->flags & SCB_ACTIVE))
           {
             result = SCSI_RESET_NOT_RUNNING;
           }
@@ -5599,20 +6974,23 @@
           if ((flags & SCSI_RESET_SYNCHRONOUS) &&
               (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
           {
-            scb->state |= SCB_ABORTED;
+            scb->flags |= SCB_ABORTED;
             result = SCSI_RESET_PENDING;
           }
           else
           {
+            p->flags |= IN_TIMEOUT;
             result = aic7xxx_bus_device_reset(p, cmd);
             if (result == 0)
+            {
+              p->flags &= ~IN_TIMEOUT;
               result = SCSI_RESET_PENDING;
+            }
           }
-        }
+	}
       }
     }
   }
-
   if (result == -1)
   {
     /*
@@ -5625,11 +7003,11 @@
       {
 	result = SCSI_RESET_NOT_RUNNING;
       }
-      else if (!(scb->state & SCB_IN_PROGRESS))
+      else if (!(scb->flags & SCB_ACTIVE))
       {
 	result = SCSI_RESET_NOT_RUNNING;
       }
-      else if ((scb->state & SCB_ABORTED) &&
+      else if ((scb->flags & SCB_ABORTED) &&
                (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
       {
 	result = SCSI_RESET_PENDING;
@@ -5641,8 +7019,9 @@
       /*
        * The reset channel function assumes that the sequencer is paused.
        */
-      PAUSE_SEQUENCER(p);
+      pause_sequencer(p);
       found = aic7xxx_reset_channel(p, channel, TRUE);
+      p->flags = p->flags & ~IN_TIMEOUT;
 
       /*
        * If this is a synchronous reset and there is no SCB for this
@@ -5688,8 +7067,10 @@
       }
 
       result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+      p->flags &= ~IN_TIMEOUT;
     }
   }
+  aic7xxx_run_waiting_queues(p);
   restore_flags(processor_flags);
   return (result);
 }
@@ -5759,4 +7140,3 @@
  * tab-width: 8
  * End:
  */
-

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov