patch-2.1.96 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.1.95/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
@@ -38,7 +38,7 @@
  *  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.
  *
@@ -96,6 +96,73 @@
  *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
  *-M*************************************************************************/
 
+/*+M**************************************************************************
+ *
+ * Further driver modifications made by Doug Ledford <dledford@dialnet.net>
+ *
+ * Copyright (c) 1997-1998 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs.  Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ *  1: Import of the latest FreeBSD sequencer code for this driver
+ *  2: Modification of kernel code to accomodate different sequencer semantics
+ *  3: Extensive changes throughout kernel portion of driver to improve
+ *     abort/reset processing and error hanndling
+ *  4: Other work contributed by various people on the Internet
+ *  5: Changes to printk information and verbosity selection code
+ *  6: General reliability related changes, especially in IRQ management
+ *  7: Modifications to the default probe/attach order for supported cards
+ *  8: SMP friendliness has been improved
+ *
+ * Overall, this driver represents a significant departure from the official
+ * aic7xxx driver released by Dan Eischen in two ways.  First, in the code
+ * itself.  A diff between the two version of the driver is now a several
+ * thousand line diff.  Second, in approach to solving the same problem.  The
+ * problem is importing the FreeBSD aic7xxx driver code to linux can be a
+ * difficult and time consuming process, that also can be error prone.  Dan
+ * Eischen's official driver uses the approach that the linux and FreeBSD
+ * drivers should be as identical as possible.  To that end, his next version
+ * of this driver will be using a mid-layer code library that he is developing
+ * to moderate communications between the linux mid-level SCSI code and the
+ * low level FreeBSD driver.  He intends to be able to essentially drop the
+ * FreeBSD driver into the linux kernel with only a few minor tweaks to some
+ * include files and the like and get things working, making for fast easy
+ * imports of the FreeBSD code into linux.
+ *
+ * I disagree with Dan's approach.  Not that I don't think his way of doing
+ * things would be nice, easy to maintain, and create a more uniform driver
+ * between FreeBSD and Linux.  I have no objection to those issues.  My
+ * disagreement is on the needed functionality.  There simply are certain
+ * things that are done differently in FreeBSD than linux that will cause
+ * problems for this driver regardless of any middle ware Dan implements.
+ * The biggest example of this at the moment is interrupt semantics.  Linux
+ * doesn't provide the same protection techniques as FreeBSD does, nor can
+ * they be easily implemented in any middle ware code since they would truly
+ * belong in the kernel proper and would effect all drivers.  For the time
+ * being, I see issues such as these as major stumbling blocks to the 
+ * reliability of code based upon such middle ware.  Therefore, I choose to
+ * use a different approach to importing the FreeBSD code that doesn't
+ * involve any middle ware type code.  My approach is to import the sequencer
+ * code from FreeBSD wholesale.  Then, to only make changes in the kernel
+ * portion of the driver as they are needed for the new sequencer semantics.
+ * In this way, the portion of the driver that speaks to the rest of the
+ * linux kernel is fairly static and can be changed/modified to solve
+ * any problems one might encounter without concern for the FreeBSD driver.
+ *
+ * Note: If time and experience should prove me wrong that the middle ware
+ * code Dan writes is reliable in its operation, then I'll retract my above
+ * statements.  But, for those that don't know, I'm from Missouri (in the US)
+ * and our state motto is "The Show-Me State".  Well, before I will put
+ * faith into it, you'll have to show me that it works :)
+ *
+ *_M*************************************************************************/
+
 #ifdef MODULE
 #include <linux/module.h>
 #endif
@@ -103,6 +170,7 @@
 #include <stdarg.h>
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <linux/version.h>
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
@@ -112,6 +180,8 @@
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
+#include <linux/tqueue.h>
+#include <linux/tasks.h>
 #include "sd.h"
 #include "scsi.h"
 #include "hosts.h"
@@ -121,10 +191,11 @@
 #include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
 #include "aic7xxx_seq.h"
+
 #include <linux/stat.h>
-#include <linux/malloc.h>	/* for kmalloc() */
+#include <linux/malloc.h>        /* for kmalloc() */
 
-#include <linux/config.h>	/* for CONFIG_PCI */
+#include <linux/config.h>        /* for CONFIG_PCI */
 
 /*
  * To generate the correct addresses for the controller to issue
@@ -138,13 +209,13 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 4.1 $"
+#define AIC7XXX_C_VERSION  "5.0.13"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #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_CHANNELS -1
 #define ALL_LUNS -1
 #define MAX_TARGETS  16
 #define MAX_LUNS     8
@@ -155,75 +226,78 @@
 #  define FALSE 0
 #endif
 
+#ifndef KERNEL_VERSION
+#  define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
 /*
- * Defines for PCI bus support, testing twin bus support, DMAing of
- * SCBs, tagged queueing, commands (SCBs) per lun, and SCSI bus reset
- * delay time.
- *
- *   o PCI bus support - this has been implemented and working since
- *     the December 1, 1994 release of this driver. If you don't have
- *     a PCI bus, then you can configure your kernel without PCI
- *     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.  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
- *     tagged queueing. Therefore, the maximum commands per lun is
- *     set to 2. If you want to implement tagged queueing, ensure
- *     this define is not commented out.
- *
- *   o Commands per lun - If tagged queueing is enabled, then you
- *     may want to try increasing AIC7XXX_CMDS_PER_LUN to more
- *     than 2.  By default, we limit the SCBs per LUN to 2 with
- *     or without tagged queueing enabled.  If tagged queueing is
- *     disabled, the sequencer will keep the 2nd SCB in the input
- *     queue until the first one completes - so it is OK to to have
- *     more than 1 SCB queued.  If tagged queueing is enabled, then
- *     the sequencer will attempt to send the 2nd SCB to the device
- *     while the first SCB is executing and the device is disconnected.
- *     For adapters limited to 4 SCBs, you may want to actually
- *     decrease the commands per LUN to 1, if you often have more
- *     than 2 devices active at the same time.  This will allocate
- *     1 SCB for each device and ensure that there will always be
- *     a free SCB for up to 4 devices active at the same time.
- *     When SCB paging is enabled, set the commands per LUN to 8
- *     or higher (see SCB paging support below).  Note that if
- *     AIC7XXX_CMDS_PER_LUN is not defined and tagged queueing is
- *     enabled, the driver will attempt to set the commands per
- *     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
- *     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
- *     FreeBSD driver (by Justin Gibbs) and allows for up to 255
- *     active SCBs.  This will increase performance when tagged
- *     queueing is enabled.  Note that you should increase the
- *     AIC7XXX_CMDS_PER_LUN to 8 as most tagged queueing devices
- *     allow at least this many.
- *
- *  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, 01/26/96
- */
-
-/* Uncomment this for tagged queueing. */
-#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
-#define AIC7XXX_TAGGED_QUEUEING
+ * We need the bios32.h file if we are kernel version 2.1.92 or less.  The
+ * full set of pci_* changes wasn't in place until 2.1.93
+ */
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+#  if defined(__sparc_v9__) || defined(__powerpc__)
+#    error "PPC and Sparc platforms are only support under 2.1.x and above"
+#  endif
+#  include <linux/bios32.h>
+#endif
+
+#if !defined(__alpha__)
+#  define MMAPIO
+#endif
+
+#if defined(__powerpc__)
+#  ifdef mb
+#    undef mb
+#  endif
+#  define mb() \
+     __asm__ __volatile__("eieio" ::: "memory")
+#elif defined(__i386__)
+#  ifdef mb
+#    undef mb
+#  endif
+#  define mb() \
+     __asm__ __volatile__("lock ; addl $0,0(%%esp)": : :"memory")
+#elif defined(__alpha__)
+#  ifdef mb
+#    undef mb
+#  endif
+#  define mb() \
+     __asm__ __volatile__("mb": : :"memory")
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+#  include <asm/spinlock.h>
+#  include <linux/smp.h>
+#  define cpuid smp_processor_id()
+#  if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+#    define DRIVER_LOCK_INIT \
+       spin_lock_init(&p->spin_lock);
+#    define DRIVER_LOCK \
+       if(!p->cpu_lock_count[cpuid]) { \
+         spin_lock_irqsave(&p->spin_lock, cpu_flags); \
+         p->cpu_lock_count[cpuid]++; \
+       } else { \
+         p->cpu_lock_count[cpuid]++; \
+       }
+#    define DRIVER_UNLOCK \
+       if(--p->cpu_lock_count[cpuid] == 0) \
+         spin_unlock_irqrestore(&p->spin_lock, cpu_flags);
+#  else
+#    define DRIVER_LOCK_INIT
+#    define DRIVER_LOCK
+#    define DRIVER_UNLOCK
+#  endif
+#else
+#  define cpuid 0
+#  define DRIVER_LOCK_INIT
+#  define DRIVER_LOCK \
+       save_flags(cpu_flags); \
+       cli();
+#  define DRIVER_UNLOCK \
+       restore_flags(cpu_flags);
+#  define le32_to_cpu(x) (x)
+#  define cpu_to_le32(x) (x)
 #endif
 
 /*
@@ -238,7 +312,7 @@
 #ifdef CONFIG_AIC7XXX_RESET_DELAY
 #define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
 #else
-#define AIC7XXX_RESET_DELAY 15
+#define AIC7XXX_RESET_DELAY 5
 #endif
 
 /*
@@ -252,32 +326,18 @@
 #endif
 
 /*
- * Enable SCB paging.
- */
-#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
-#define AIC7XXX_PAGE_ENABLE
-#endif
-
-/*
- * 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.
- */
-#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
-#define AIC7XXX_USE_EXT_SCBRAM
-#endif
-
-/*
- * For debugging the abort/reset code.
- */
-#define AIC7XXX_DEBUG_ABORT
-
-/*
- * For general debug messages
- */
-#define AIC7XXX_DEBUG
-
-/*
+ * NOTE: Uncommenting the define below no longer has any effect, the
+ *       tagged queue value array is always active now.  I've added
+ *       a setup option to set this particular array and I'm hoping
+ *       insmod will be smart enough to set it properly as well.  It's
+ *       by use of this array that a person can disable tagged queueing.
+ *       The DEFAULT_TAG_COMMANDS define has been changed to disable
+ *       tagged queueing by default, so if your devices can handle tagged
+ *       queueing you will need to add a line to their lilo.conf file like:
+ *       append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ *       which will result in the first four devices on the first two
+ *       controllers being set to a tagged queue depth of 32.
+ *
  * 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
@@ -292,37 +352,36 @@
  * 
  * 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.
- *
+ * enabled, which is always now, the default is 8 commands per lun
+ * that indicates it supports tagged queueing.  All non-tagged devices
+ * use an internal queue depth of 3, with no more than one of those
+ * three commands active at one time.
  */
 /* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
 
-#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
 typedef struct
 {
-  unsigned char tag_commands[16];   /* Allow for wide/twin channel adapters. */
+  unsigned char tag_commands[16];   /* Allow for wide/twin 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).
+ * Make a define that will tell the driver not to use tagged queueing
+ * by default.
  */
-#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
+                              255, 255, 255, 255, 255, 255, 255, 255}
 
 /*
  * 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
+ * number of commands to use (see above).  When 255, 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.
+ * (> 0) and (< 255) the values in the array are used for the queue_depth.
+ * Note that the maximum value for an entry is 254, but you're insane if
+ * you try to use that many commands on one device.
  *
- * 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.
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
  *
  * 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
@@ -335,107 +394,42 @@
  * 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}},
+  {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255, 4, 4, 4}},
   {DEFAULT_TAG_COMMANDS},
-  {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+  {{255, 16, 4, 255, 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 {
-  AIC_NONE,
-  AIC_7770,	/* EISA aic7770 on motherboard */
-  AIC_7771,	/* EISA aic7771 on 274x */
-  AIC_284x,	/* VLB  aic7770 on 284x, BIOS disabled */
-  AIC_7850,	/* PCI  aic7850 */
-  AIC_7855,	/* PCI  aic7855 */
-  AIC_7860,	/* PCI  aic7860 (7850 Ultra) */
-  AIC_7861,     /* PCI  aic7861 on 2940AU */
-  AIC_7870,	/* PCI  aic7870 on motherboard */
-  AIC_7871,	/* PCI  aic7871 on 294x */
-  AIC_7872,	/* PCI  aic7872 on 3940 */
-  AIC_7873,	/* PCI  aic7873 on 3985 */
-  AIC_7874,	/* PCI  aic7874 on 294x Differential */
-  AIC_7880,	/* PCI  aic7880 on motherboard */
-  AIC_7881,	/* PCI  aic7881 on 294x Ultra */
-  AIC_7882,	/* PCI  aic7882 on 3940 Ultra */
-  AIC_7883,	/* PCI  aic7883 on 3985 Ultra */
-  AIC_7884	/* PCI  aic7884 on 294x Ultra Differential */
-} aha_chip_type;
-
-typedef enum {
-  AIC_777x,	/* AIC-7770 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 (ultra) */
-} aha_chip_class_type;
-
-typedef enum {
-  AIC_SINGLE,  /* Single Channel */
-  AIC_TWIN,    /* Twin Channel */
-  AIC_WIDE     /* Wide Channel */
-} aha_bus_type;
-
-typedef enum {
-  AIC_UNKNOWN,
-  AIC_ENABLED,
-  AIC_DISABLED
-} aha_status_type;
-
-typedef enum {
-  LIST_HEAD,
-  LIST_SECOND
-} insert_type;
-
-typedef enum {
-  ABORT_RESET_INACTIVE,
-  ABORT_RESET_PENDING,
-  ABORT_RESET_SUCCESS
-} aha_abort_reset_type;
+*/
 
 /*
  * Define an array of board names that can be indexed by aha_type.
  * Don't forget to change this when changing the types!
  */
 static const char *board_names[] = {
-  "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 */
+  "AIC-7xxx Unknown",                                   /* AIC_NONE */
+  "Adaptec AIC-7810 Hardware RAID Controller",          /* AIC_7810 */
+  "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 */
+  "Adaptec AIC-7895 Ultra SCSI host adapter"            /* AIC_7895 */
 };
 
 /*
@@ -460,58 +454,58 @@
 #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 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)
  */
-#define HID0		0x80   /* 0,1: msb of ID2, 2-7: ID1      */
-#define HID1		0x81   /* 0-4: ID3, 5-7: LSB ID2         */
-#define HID2		0x82   /* product                        */
-#define HID3		0x83   /* firmware revision              */
+#define HID0                0x80   /* 0,1: msb of ID2, 2-7: ID1      */
+#define HID1                0x81   /* 0-4: ID3, 5-7: LSB ID2         */
+#define HID2                0x82   /* product                        */
+#define HID3                0x83   /* firmware revision              */
 
 /*
  * AIC-7770 I/O range to reserve for a card
  */
-#define MINREG		0xC00
-#define MAXREG		0xCBF
+#define MINREG                0xC00
+#define MAXREG                0xCBF
 
-#define INTDEF		0x5C		/* Interrupt Definition Register */
+#define INTDEF                0x5C      /* Interrupt Definition Register */
 
 /*
  * AIC-78X0 PCI registers
  */
-#define	CLASS_PROGIF_REVID	0x08
-#define		DEVREVID	0x000000FFul
-#define		PROGINFC	0x0000FF00ul
-#define		SUBCLASS	0x00FF0000ul
-#define		BASECLASS	0xFF000000ul
-
-#define	CSIZE_LATTIME		0x0C
-#define		CACHESIZE	0x0000003Ful	/* only 5 bits */
-#define		LATTIME		0x0000FF00ul
-
-#define	DEVCONFIG		0x40
-#define		MPORTMODE	0x00000400ul	/* aic7870 only */
-#define		RAMPSM		0x00000200ul	/* aic7870 only */
-#define		VOLSENSE	0x00000100ul
-#define		SCBRAMSEL	0x00000080ul
-#define		MRDCEN		0x00000040ul
-#define		EXTSCBTIME	0x00000020ul	/* aic7870 only */
-#define		EXTSCBPEN	0x00000010ul	/* aic7870 only */
-#define		BERREN		0x00000008ul
-#define		DACEN		0x00000004ul
-#define		STPWLEVEL	0x00000002ul
-#define		DIFACTNEGEN	0x00000001ul	/* aic7870 only */
+#define        CLASS_PROGIF_REVID        0x08
+#define                DEVREVID        0x000000FFul
+#define                PROGINFC        0x0000FF00ul
+#define                SUBCLASS        0x00FF0000ul
+#define                BASECLASS        0xFF000000ul
+
+#define        CSIZE_LATTIME                0x0C
+#define                CACHESIZE        0x0000003Ful        /* only 5 bits */
+#define                LATTIME                0x0000FF00ul
+
+#define        DEVCONFIG                0x40
+#define                SCBSIZE32        0x00010400ul        /* aic789X only */
+#define                MPORTMODE        0x00000400ul        /* aic7870 only */
+#define                RAMPSM           0x00000200ul        /* aic7870 only */
+#define                VOLSENSE         0x00000100ul
+#define                SCBRAMSEL        0x00000080ul
+#define                MRDCEN           0x00000040ul
+#define                EXTSCBTIME       0x00000020ul        /* aic7870 only */
+#define                EXTSCBPEN        0x00000010ul        /* aic7870 only */
+#define                BERREN           0x00000008ul
+#define                DACEN            0x00000004ul
+#define                STPWLEVEL        0x00000002ul
+#define                DIFACTNEGEN      0x00000001ul        /* aic7870 only */
 
 
 /*
@@ -535,69 +529,72 @@
 /*
  * SCSI ID Configuration Flags
  */
-#define CFXFER		0x0007		/* synchronous transfer rate */
-#define CFSYNCH		0x0008		/* enable synchronous transfer */
-#define CFDISC		0x0010		/* enable disconnection */
-#define CFWIDEB		0x0020		/* wide bus device (wide card) */
-/* UNUSED		0x00C0 */
-#define CFSTART		0x0100		/* send start unit SCSI command */
-#define CFINCBIOS	0x0200		/* include in BIOS scan */
-#define CFRNFOUND	0x0400		/* report even if not found */
-/* UNUSED		0xF800 */
-  unsigned short device_flags[16];	/* words 0-15 */
+#define CFXFER                0x0007      /* synchronous transfer rate */
+#define CFSYNCH               0x0008      /* enable synchronous transfer */
+#define CFDISC                0x0010      /* enable disconnection */
+#define CFWIDEB               0x0020      /* wide bus device (wide card) */
+#define CFSYNCHISULTRA        0x0040      /* CFSYNC is an ultra offset */
+/* UNUSED                0x0080 */
+#define CFSTART               0x0100      /* send start unit SCSI command */
+#define CFINCBIOS             0x0200      /* include in BIOS scan */
+#define CFRNFOUND             0x0400      /* report even if not found */
+#define CFMULTILUN            0x0800      /* probe mult luns in BIOS scan */
+/* UNUSED                0xF000 */
+  unsigned short device_flags[16];        /* words 0-15 */
 
 /*
  * BIOS Control Bits
  */
-#define CFSUPREM	0x0001		/* support all removable drives */
-#define CFSUPREMB	0x0002		/* support removable drives for boot only */
-#define CFBIOSEN	0x0004		/* BIOS enabled */
-/* UNUSED		0x0008 */
-#define CFSM2DRV	0x0010		/* support more than two drives */
-#define CF284XEXTEND	0x0020		/* extended translation (284x cards) */
-/* UNUSED		0x0040 */
-#define CFEXTEND	0x0080		/* extended translation enabled */
-/* UNUSED		0xFF00 */
-  unsigned short bios_control;		/* word 16 */
+#define CFSUPREM        0x0001  /* support all removable drives */
+#define CFSUPREMB       0x0002  /* support removable drives for boot only */
+#define CFBIOSEN        0x0004  /* BIOS enabled */
+/* UNUSED                0x0008 */
+#define CFSM2DRV        0x0010  /* support more than two drives */
+#define CF284XEXTEND    0x0020  /* extended translation (284x cards) */
+/* UNUSED                0x0040 */
+#define CFEXTEND        0x0080  /* extended translation enabled */
+/* UNUSED                0xFF00 */
+  unsigned short bios_control;  /* word 16 */
 
 /*
  * Host Adapter Control Bits
  */
-#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 */
-#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 boot */
-/* UNUSED		0xFF80 */
-  unsigned short adapter_control;	/* word 17 */
+#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 */
+#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 boot */
+#define CFBPRIMARY      0x0100  /* Channel B primary on 7895 chipsets */
+/* UNUSED                0xFE80 */
+  unsigned short adapter_control;        /* word 17 */
 
 /*
  * Bus Release, Host Adapter ID
  */
-#define CFSCSIID	0x000F		/* host adapter SCSI ID */
-/* UNUSED		0x00F0 */
-#define CFBRTIME	0xFF00		/* bus release time */
-  unsigned short brtime_id;		/* word 18 */
+#define CFSCSIID        0x000F                /* host adapter SCSI ID */
+/* UNUSED                0x00F0 */
+#define CFBRTIME        0xFF00                /* bus release time */
+  unsigned short brtime_id;                /* word 18 */
 
 /*
  * Maximum targets
  */
-#define CFMAXTARG	0x00FF	/* maximum targets */
-/* UNUSED		0xFF00 */
-  unsigned short max_targets;		/* word 19 */
+#define CFMAXTARG        0x00FF        /* maximum targets */
+/* UNUSED                0xFF00 */
+  unsigned short max_targets;                /* word 19 */
 
-  unsigned short res_1[11];		/* words 20-30 */
-  unsigned short checksum;		/* word 31 */
+  unsigned short res_1[11];                /* words 20-30 */
+  unsigned short checksum;                /* word 31 */
 };
 
-#define SELBUS_MASK		0x0a
-#define 	SELNARROW	0x00
-#define 	SELBUSB		0x08
-#define SINGLE_BUS		0x00
+#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)
@@ -612,17 +609,22 @@
  * condition in this location. This then will modify a DID_OK status
  * into an appropriate error for the higher-level SCSI code.
  */
-#define aic7xxx_error(cmd)	((cmd)->SCp.Status)
+#define aic7xxx_error(cmd)        ((cmd)->SCp.Status)
 
 /*
  * Keep track of the targets returned status.
  */
-#define aic7xxx_status(cmd)	((cmd)->SCp.sent_command)
+#define aic7xxx_status(cmd)        ((cmd)->SCp.sent_command)
 
 /*
  * The position of the SCSI commands scb within the scb array.
  */
-#define aic7xxx_position(cmd)	((cmd)->SCp.have_data_in)
+#define aic7xxx_position(cmd)        ((cmd)->SCp.have_data_in)
+
+/*
+ * So we can keep track of our host structs
+ */
+static struct aic7xxx_host *first_aic7xxx = NULL;
 
 /*
  * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
@@ -637,14 +639,14 @@
 /*
  * Maximum number of SG segments these cards can support.
  */
-#define	AIC7XXX_MAX_SG 27
+#define        AIC7XXX_MAX_SG 128
 
 /*
  * 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
+#define AIC7XXX_MAXSCB        255
 
 
 struct aic7xxx_hwscb {
@@ -660,53 +662,118 @@
 /*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
-					 */
-#define SCB_PIO_TRANSFER_SIZE	26 	/* amount we need to upload/download
-					 * via PIO to initialize a transaction.
-					 */
-/*26*/  unsigned char next;             /* Used to thread SCBs awaiting selection
-                                         * or disconnected down in the sequencer.
-                                         */
+/*25*/  unsigned char tag;          /* Index into our kernel SCB array.
+                                     * Also used as the tag for tagged I/O
+                                     */
+#define SCB_PIO_TRANSFER_SIZE  26   /* amount we need to upload/download
+                                     * via PIO to initialize a transaction.
+                                     */
+/*26*/  unsigned char next;         /* Used to thread SCBs awaiting selection
+                                     * or disconnected down in the sequencer.
+                                     */
 /*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
-                                         */
+/*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_FREE                = 0x0000,
+        SCB_WDTR_16BIT          = 0x0001,
+        SCB_WAITINGQ            = 0x0002,
+        SCB_ACTIVE              = 0x0004,
+        SCB_SENSE               = 0x0008,
+        SCB_ABORT               = 0x0010,
+        SCB_DEVICE_RESET        = 0x0020,
+        SCB_RESET               = 0x0040,
+        SCB_RECOVERY_SCB        = 0x0080,
+        SCB_WAS_BUSY            = 0x0100,
+        SCB_MSGOUT_SDTR         = 0x0400,
+        SCB_MSGOUT_WDTR         = 0x0800,
+        SCB_MSGOUT_WDTR_8BIT    = 0x0800,
+        SCB_MSGOUT_WDTR_16BIT   = 0x0801,
+        SCB_MSGOUT_BITS         = SCB_MSGOUT_SDTR | SCB_MSGOUT_WDTR_16BIT,
+        SCB_QUEUED_ABORT        = 0x1000,
+        SCB_QUEUED_FOR_DONE     = 0x2000
 } scb_flag_type;
 
+typedef enum {
+        AHC_FNONE                 = 0x00000000,
+        AHC_PAGESCBS              = 0x00000001,
+        AHC_CHANNEL_B_PRIMARY     = 0x00000002,
+        AHC_USEDEFAULTS           = 0x00000004,
+        AHC_INDIRECT_PAGING       = 0x00000008,
+        AHC_CHNLB                 = 0x00000020,
+        AHC_CHNLC                 = 0x00000040,
+        AHC_EXTEND_TRANS_A        = 0x00000100,
+        AHC_EXTEND_TRANS_B        = 0x00000200,
+        AHC_TERM_ENB_A            = 0x00000400,
+        AHC_TERM_ENB_B            = 0x00000800,
+        AHC_HANDLING_REQINITS     = 0x00001000,
+        AHC_TARGETMODE            = 0x00002000,
+        AHC_NEWEEPROM_FMT         = 0x00004000,
+ /*
+  *  Here ends the FreeBSD defined flags and here begins the linux defined
+  *  flags.  NOTE: I did not preserve the old flag name during this change
+  *  specifically to force me to evaluate what flags were being used properly
+  *  and what flags weren't.  This way, I could clean up the flag usage on
+  *  a use by use basis.  Doug Ledford
+  */
+        AHC_A_SCANNED             = 0x00100000,
+        AHC_B_SCANNED             = 0x00200000,
+        AHC_MULTI_CHANNEL         = 0x00400000,
+        AHC_BIOS_ENABLED          = 0x00800000,
+        AHC_ABORT_PENDING         = 0x02000000,
+        AHC_RESET_PENDING         = 0x04000000,
+        AHC_IN_ISR                = 0x10000000,
+        AHC_IN_ABORT              = 0x20000000,
+        AHC_IN_RESET              = 0x40000000
+} ahc_flag_type;
+
+typedef enum {
+        AHC_NONE                  = 0x00000000,
+        AHC_ULTRA                 = 0x00000001,
+        AHC_WIDE                  = 0x00000002,
+        AHC_TWIN                  = 0x00000008,
+        AHC_AIC7770               = 0x00000010,
+        AHC_AIC7850               = 0x00000020,
+        AHC_AIC7860               = 0x00000021,
+        AHC_AIC7870               = 0x00000040,
+        AHC_AIC7880               = 0x00000041,
+        AHC_AIC7895               = 0x00000081,
+        AHC_AIC78x0               = 0x000000E0,
+        AHC_274                   = 0x00000110,
+        AHC_284                   = 0x00000210,
+        AHC_294AU                 = 0x00000421,
+        AHC_294                   = 0x00000440,
+        AHC_294U                  = 0x00000441,
+        AHC_394                   = 0x00000840,
+        AHC_394U                  = 0x00000841,
+        AHC_394AU                 = 0x00000881,
+        AHC_398                   = 0x00001040,
+        AHC_398U                  = 0x00001041,
+        AHC_39x                   = 0x00001880
+} ahc_type;
+
 struct aic7xxx_scb {
         struct aic7xxx_hwscb  *hscb;          /* corresponding hardware scb */
-	Scsi_Cmnd             *cmd;	      /* Scsi_Cmnd for this 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          sense_cmd[6];  /*
+        scb_flag_type          flags;         /* current state of scb */
+        struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        unsigned char          tag_action;
+        unsigned char          sg_count;
+        unsigned char          sense_cmd[6];  /*
                                                * Allocate 6 characters for
                                                * sense command.
                                                */
-        unsigned char          sg_count;
+        unsigned int           sg_length; /* We init this during buildscb so we
+                                           * don't have to calculate anything
+                                           * during underflow/overflow/stat code
+                                           */
 };
 
 /*
@@ -724,7 +791,7 @@
   { ILLHADDR,  "Illegal Host Access" },
   { ILLSADDR,  "Illegal Sequencer Address referenced" },
   { ILLOPCODE, "Illegal Opcode in sequencer program" },
-  { PARERR,    "Sequencer Ram Parity Error" }
+  { SQPARERR,  "Sequencer Ram Parity Error" }
 };
 
 static unsigned char
@@ -740,58 +807,110 @@
   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;
 
+typedef struct {
+  unsigned char period;
+  unsigned char offset;
+} syncinfo_type;
+
 /*
- * Define a structure used for each host adapter, only one per IRQ.
+ * Define a structure used for each host adapter.  Note, in order to avoid
+ * problems with architectures I can't test on (because I don't have one,
+ * such as the Alpha based systems) which happen to give faults for
+ * non-aligned memory accesses, care was taken to align this structure
+ * in a way that gauranteed all accesses larger than 8 bits were aligned
+ * on the appropriate boundary.  It's also organized to try and be more
+ * cache line efficient.  Be careful when changing this lest you might hurt
+ * overall performance and bring down the wrath of the masses.
  */
 struct aic7xxx_host {
+  /*
+   *  This is the first 64 bytes in the host struct
+   */
+
   struct Scsi_Host        *host;             /* pointer to scsi host */
-  struct aic7xxx_host     *next;             /* pointer to next aic7xxx device */
+  struct aic7xxx_host     *next;             /* allow for multiple IRQs */
   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 */
   unsigned long            base;             /* card base address */
-  unsigned long            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 */
-  unsigned short           needsdtr;
-  unsigned short           sdtr_pending;
-  unsigned short           needwdtr_copy;    /* default config */
-  unsigned short           needwdtr;
-  unsigned short           wdtr_pending;
-  unsigned short           orderedtag;
-  unsigned short           discenable;	     /* Targets allowed to disconnect */
-  aha_chip_type            chip_type;        /* card type */
-  aha_chip_class_type      chip_class;
-  aha_bus_type             bus_type;         /* normal/twin/wide bus */
-  unsigned char            chan_num;         /* for 39xx, channel number */
+  unsigned long            mbase;            /* I/O memory address */
+  volatile ahc_flag_type   flags;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+  spinlock_t               spin_lock;
+#endif
+  volatile unsigned char   cpu_lock_count[NR_CPUS];
+  ahc_type                 type;             /* card type */
+  unsigned long            last_reset;
+  unsigned long            isr_count;        /* Interrupt count */
+  unsigned long            spurious_int;
+  unsigned short           discenable;       /* Targets allowed to disconnect */
+  unsigned short           tagenable;        /* Targets using tagged I/O */
+  unsigned short           orderedtag;       /* Ordered Q tags allowed */
+  volatile unsigned char   activescbs;       /* active scbs */
+  unsigned char            max_activescbs;
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
-  unsigned char            qcntmask;
-  unsigned char            qfullcount;
-  unsigned char            cmdoutcnt;
-  unsigned char            curqincnt;
-  unsigned char            activescbs;       /* active scbs */
-  scb_queue_type           waiting_scbs;     /*
+  volatile unsigned char   qoutfifonext;
+  volatile unsigned char   qinfifonext;
+
+  /*
+   * MAX_TARGETS is currently == 16, so that makes these entries the next
+   * 64 bytes
+   */
+
+#define  DEVICE_PRESENT                 0x01
+#define  BUS_DEVICE_RESET_PENDING       0x02
+#define  DEVICE_TIMEOUT                 0x04
+#define  DEVICE_PRINT_SDTR              0x08
+#define  DEVICE_PRINT_WDTR              0x10
+#define  DEVICE_SUCCESS                 0x20
+#define  DEVICE_TAGGED_SUCCESS          0x40
+  volatile unsigned char   dev_flags[MAX_TARGETS];
+  volatile unsigned char   dev_active_cmds[MAX_TARGETS];
+  unsigned char            dev_temp_queue_depth[MAX_TARGETS];
+  unsigned char            dev_commands_sent[MAX_TARGETS];
+
+  /*
+   * The next 64.... 
+   */
+
+  long                     dev_last_reset[MAX_TARGETS];
+
+  /*
+   * The next 64....
+   */
+
+  unsigned char            dev_mid_level_queue_depth[MAX_TARGETS];
+  unsigned char            dev_last_queue_full[MAX_TARGETS];
+  unsigned char            dev_last_queue_full_count[MAX_TARGETS];
+  unsigned char            dev_max_queue_depth[MAX_TARGETS];
+
+  /*
+   * The next 128....
+   */
+
+  volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
+
+  /*
+   *
+   */
+
+  struct timer_list        dev_timer[MAX_TARGETS];
+
+  /*
+   * The next 64....
+   */
+
+  unsigned char            msg_buf[9];       /* The message for the target */
+  unsigned char            msg_type;
+#define MSG_TYPE_NONE              0x00
+#define MSG_TYPE_INITIATOR_MSGOUT  0x01
+#define MSG_TYPE_INITIATOR_MSGIN   0x02
+  unsigned char            msg_len;          /* Length of message */
+  unsigned char            msg_index;        /* Index into msg_buf array */
+  syncinfo_type            syncinfo[MAX_TARGETS];
+  volatile scb_queue_type  waiting_scbs;     /*
                                               * SCBs waiting for space in
                                               * the QINFIFO.
                                               */
@@ -801,14 +920,41 @@
     Scsi_Cmnd *head;
     Scsi_Cmnd *tail;
   } completeq;
-  struct aic7xxx_device_status {
-    long last_reset;
-#define  DEVICE_SUCCESS                 0x01
-#define  BUS_DEVICE_RESET_PENDING       0x02
-    int  flags;
-    int  commands_sent;
-    int  active_cmds;
-  } device_status[16];
+
+
+  /*
+   * We put the less frequently used host structure items after the more
+   * frequently used items to try and ease the burden on the cache subsystem.
+   * These entries are not *commonly* accessed, whereas the preceding entries
+   * are accessed very often.  The only exceptions are the qinfifo, qoutfifo,
+   * and untagged_scbs array.  But, they are often accessed only once and each
+   * access into these arrays is likely to blow a cache line, so they are put
+   * down here so we can minimize the number of cache lines required to hold
+   * the preceeding entries.
+   */
+
+  volatile unsigned char   untagged_scbs[256];
+  volatile unsigned char   qoutfifo[256];
+  volatile unsigned char   qinfifo[256];
+  unsigned short           needsdtr;
+  unsigned short           sdtr_pending;
+  unsigned short           needwdtr;
+  unsigned short           wdtr_pending;
+  int                      instance;         /* aic7xxx instance number */
+  int                      scsi_id;          /* host adapter SCSI ID */
+  int                      scsi_id_b;        /* channel B for twin adapters */
+  unsigned int             bios_address;
+  int                      board_name_index;
+  unsigned long            reset_start;
+  unsigned short           needsdtr_copy;    /* default config */
+  unsigned short           needwdtr_copy;    /* default config */
+  unsigned short           ultraenb;         /* Ultra mode target list */
+  unsigned short           bios_control;     /* bios control - SEEPROM */
+  unsigned short           adapter_control;  /* adapter control - SEEPROM */
+  unsigned char            pci_bus;
+  unsigned char            pci_device_fn;
+  unsigned char            irq;              /* IRQ for this adapter */
+
 #ifdef AIC7XXX_PROC_STATS
   /*
    * Statistics Kept:
@@ -820,6 +966,10 @@
    *    < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
    *
    * Total amounts read/written above 512 bytes (amts under ignored)
+   *
+   * NOTE: Enabling this feature is likely to cause a noticeable performance
+   * decrease as the accesses into the stats structures blows apart multiple
+   * cache lines and is CPU time consuming.
    */
   struct aic7xxx_xferstats {
     long xfers;                              /* total xfer count */
@@ -841,7 +991,7 @@
 static struct {
   short period;
   /* Rates in Ultra mode have bit 8 of sxfr set */
-#define		ULTRA_SXFR 0x100
+#define                ULTRA_SXFR 0x100
   short rate;
   const char *english;
 } aic7xxx_syncrates[] = {
@@ -861,55 +1011,34 @@
 static int num_aic7xxx_syncrates =
     sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);
 
-#ifdef CONFIG_PCI
-static int number_of_3940s = 0;
-static int number_of_3985s = 0;
-#endif /* CONFIG_PCI */
-
-#ifdef AIC7XXX_DEBUG
-
-#if 0
-static void
-debug_scb(struct aic7xxx_scb *scb)
-{
-  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,
-    le32_to_cpu(hscb->SCSI_cmd_pointer) );
-  printk("        datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
-    le32_to_cpu(hscb->data_count),
-    le32_to_cpu(hscb->data_pointer),
-    hscb->SG_segment_count,
-    le32_to_cpu(hscb->SG_list_pointer));
-  printk("        sg_addr:%lx sg_len:%ld\n",
-    le32_to_cpu(hscb->sg_list[0].address),
-    le32_to_cpu(hscb->sg_list[0].length));
-}
-#endif
-
-#else
-#  define debug_scb(x)
-#endif AIC7XXX_DEBUG
-
-#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
-                        (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1),  \
+                        (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
                         ((scb->hscb)->target_channel_lun & 0x07)
 
-#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 CTL_OF_CMD(cmd) ((cmd->channel) & 0x01),  \
+                        ((cmd->target) & 0x0f), \
+                        ((cmd->lun) & 0x07)
 
 #define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
 
 /*
+ * A nice little define to make doing our printks a little easier
+ */
+
+#define WARN_LEAD KERN_WARNING "(scsi%d:%d:%d:%d) "
+#define INFO_LEAD KERN_INFO "(scsi%d:%d:%d:%d) "
+
+/*
  * XXX - these options apply unilaterally to _all_ 274x/284x/294x
  *       cards in the system.  This should be fixed.
  */
+static int aic7xxx_7895_irq_hack = -1;       /* This enables a hack to fix
+                                              * IRQ settings on buggy 7895
+                                              * MB controller setups
+                                              *  -1 == Disable this hack
+                                              *   0 == Use the Channel A IRQ
+                                              *   1 == Use the Channel B IRQ
+                                              */
 static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
 static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
 static int aic7xxx_irq_trigger = -1;         /*
@@ -917,9 +1046,96 @@
                                               *  0 use edge triggered
                                               *  1 use level triggered
                                               */
-static int aic7xxx_enable_ultra = 0;         /* enable ultra SCSI speeds */
-static int aic7xxx_verbose = 0;	             /* verbose messages */
-static struct aic7xxx_host *first_aic7xxx = NULL; /* list of all our devices */
+static int aic7xxx_reverse_scan = 0;         /*
+                                              * Set this to anything but 0
+                                              * to make the probe code
+                                              * reverse the order of PCI
+                                              * devices
+                                              */
+static int aic7xxx_override_term = 0;        /*
+                                              * Set this to non-0 to make the
+                                              * driver override any BIOS
+                                              * configured termination
+                                              * settings based upon the
+                                              * results of the cable detect
+                                              * logic.  This only applies
+                                              * to cards that have cable
+                                              * detection logic and a SEEPROM
+                                              */
+static int aic7xxx_panic_on_abort = 0;       /*
+                                              * Set this to non-0 in order
+                                              * to force the driver to panic
+                                              * the kernel and print out
+                                              * debugging info on an abort
+                                              * or reset call into the
+                                              * driver.
+                                              */
+
+/*
+ * So that insmod can find the variable and make it point to something
+ */
+#ifdef MODULE
+static char * aic7xxx = NULL;
+
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+
+#endif
+
+/*
+ * See the comments earlier in the file for what this item is all about
+ * If you have more than 4 controllers, you will need to increase the
+ * the number of items in the array below.  Additionally, if you don't
+ * want to have lilo pass a humongous config line to the aic7xxx driver,
+ * then you can get in and manually adjust these instead of leaving them
+ * at the default.  Pay attention to the comments earlier in this file
+ * concerning this array if you are going to hand modify these values.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS}
+};
+
+#define VERBOSE_NORMAL         0x0000
+#define VERBOSE_NEGOTIATION    0x0001
+#define VERBOSE_SEQINT         0x0002
+#define VERBOSE_SCSIINT        0x0004
+#define VERBOSE_PROBE          0x0008
+#define VERBOSE_PROBE2         0x0010
+#define VERBOSE_QUEUE          0x0020
+#define VERBOSE_MINOR_ERROR    0x0040
+#define VERBOSE_QUEUE_FULL     0x0080
+#define VERBOSE_ABORT          0x0f00
+#define VERBOSE_ABORT_MID      0x0100
+#define VERBOSE_ABORT_FIND     0x0200
+#define VERBOSE_ABORT_PROCESS  0x0400
+#define VERBOSE_ABORT_RETURN   0x0800
+#define VERBOSE_RESET          0xf000
+#define VERBOSE_RESET_MID      0x1000
+#define VERBOSE_RESET_FIND     0x2000
+#define VERBOSE_RESET_PROCESS  0x4000
+#define VERBOSE_RESET_RETURN   0x8000
+static int aic7xxx_verbose = VERBOSE_NORMAL | VERBOSE_NEGOTIATION |
+           VERBOSE_PROBE;                     /* verbose messages */
 
 
 /****************************************************************************
@@ -929,52 +1145,44 @@
  *
  ***************************************************************************/
 
+
 static inline unsigned char
 aic_inb(struct aic7xxx_host *p, long port)
 {
-  if (p->maddr != NULL)
-    return (p->maddr[port]);
+  unsigned char x;
+  if(p->maddr)
+    x = p->maddr[port];
   else
-    return (inb(p->base + port));
+    x = inb(p->base + port);
+  mb();
+  return(x);
 }
 
 static inline void
 aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
 {
-  if (p->maddr != NULL)
+  if(p->maddr)
     p->maddr[port] = val;
   else
     outb(val, p->base + port);
+  mb();
 }
 
 static inline void
 aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
 {
-  if (p->maddr != NULL)
+  if(p->maddr)
   {
-#if defined(__alpha__) || defined(__sparc_v9__) || defined(__powerpc__)
     int i;
 
     for (i=0; i < size; i++)
     {
       p->maddr[port] = valp[i];
     }
-#else
-    __asm __volatile("
-      cld;
-    1:  lodsb;
-      movb %%al,(%0);
-      loop 1b"      :
-              :
-      "r" (p->maddr + port),
-      "S" (valp), "c" (size)  :
-      "%esi", "%ecx", "%eax");
-#endif
   }
   else
-  {
     outsb(p->base + port, valp, size);
-  }
+  mb();
 }
 
 /*+F*************************************************************************
@@ -991,6 +1199,7 @@
 {
   int   i, n;
   char *p;
+  char *end;
 
   static struct {
     const char *name;
@@ -999,25 +1208,112 @@
     { "extended",    &aic7xxx_extended },
     { "no_reset",    &aic7xxx_no_reset },
     { "irq_trigger", &aic7xxx_irq_trigger },
-    { "ultra",       &aic7xxx_enable_ultra },
     { "verbose",     &aic7xxx_verbose },
-    { NULL,          NULL }
+    { "reverse_scan",&aic7xxx_reverse_scan },
+    { "7895_irq_hack", &aic7xxx_7895_irq_hack },
+    { "override_term", &aic7xxx_override_term },
+    { "panic_on_abort", &aic7xxx_panic_on_abort },
+    { "tag_info",    NULL }
   };
 
-  for (p = strtok(s, ","); p; p = strtok(NULL, ","))
+  end = strchr(s, '\0');
+
+  for (p = strtok(s, ",."); p; p = strtok(NULL, ",."))
   {
-    for (i = 0; options[i].name; i++)
+    for (i = 0; i < NUMBER(options); i++)
     {
       n = strlen(options[i].name);
       if (!strncmp(options[i].name, p, n))
       {
-        if (p[n] == ':')
+        if (!strncmp(p, "tag_info", n))
+        {
+          if (p[n] == ':')
+          {
+            char *base;
+            char *tok, *tok_end, *tok_end2;
+            char tok_list[] = { '.', ',', '{', '}', '\0' };
+            int i, instance = -1, device = -1;
+            unsigned char done = FALSE;
+
+            base = p;
+            tok = base + n + 1;  /* Forward us just past the ':' */
+            tok_end = strchr(tok, '\0');
+            if (tok_end < end)
+              *tok_end = ',';
+            while(!done)
+            {
+              switch(*tok)
+              {
+                case '{':
+                  if (instance == -1)
+                    instance = 0;
+                  else if (device == -1)
+                    device = 0;
+                  tok++;
+                  break;
+                case '}':
+                  if (device != -1)
+                    device = -1;
+                  else if (instance != -1)
+                    instance = -1;
+                  tok++;
+                  break;
+                case ',':
+                case '.':
+                  if (instance == -1)
+                    done = TRUE;
+                  else if (device >= 0)
+                    device++;
+                  else if (instance >= 0)
+                    instance++;
+                  if ( (device >= MAX_TARGETS) || 
+                       (instance >= NUMBER(aic7xxx_tag_info)) )
+                    done = TRUE;
+                  tok++;
+                  if (!done)
+                  {
+                    base = tok;
+                  }
+                  break;
+                case '\0':
+                  done = TRUE;
+                  break;
+                default:
+                  done = TRUE;
+                  tok_end = strchr(tok, '\0');
+                  for(i=0; tok_list[i]; i++)
+                  {
+                    tok_end2 = strchr(tok, tok_list[i]);
+                    if ( (tok_end2) && (tok_end2 < tok_end) )
+                    {
+                      tok_end = tok_end2;
+                      done = FALSE;
+                    }
+                  }
+                  if ( (instance >= 0) && (device >= 0) &&
+                       (instance < NUMBER(aic7xxx_tag_info)) &&
+                       (device < MAX_TARGETS) )
+                    aic7xxx_tag_info[instance].tag_commands[device] =
+                      simple_strtoul(tok, NULL, 0) & 0xff;
+                  tok = tok_end;
+                  break;
+              }
+            }
+            while((p != base) && (p != NULL))
+              p = strtok(NULL, ",.");
+          }
+        }
+        else if (p[n] == ':')
         {
           *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
         }
+        else if (!strncmp(p, "verbose", n))
+        {
+          *(options[i].flag) = 0xff69;
+        }
         else
         {
-          *(options[i].flag) = !0;
+          *(options[i].flag) = ~(*(options[i].flag));
         }
       }
     }
@@ -1036,8 +1332,8 @@
 static inline void
 pause_sequencer(struct aic7xxx_host *p)
 {
-  outb(p->pause, p->base + HCNTRL);
-  while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+  aic_outb(p, p->pause, HCNTRL);
+  while ((aic_inb(p, HCNTRL) & PAUSE) == 0)
   {
     ;
   }
@@ -1055,9 +1351,10 @@
 unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
 {
   if (unpause_always ||
-      ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
+      ( !(aic_inb(p, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) &&
+        !(p->flags & AHC_HANDLING_REQINITS) ) )
   {
-    outb(p->unpause, p->base + HCNTRL);
+    aic_outb(p, p->unpause, HCNTRL);
   }
 }
 
@@ -1073,17 +1370,16 @@
 restart_sequencer(struct aic7xxx_host *p)
 {
   /* Set the sequencer address to 0. */
-  outb(0, p->base + SEQADDR0);
-  outb(0, p->base + SEQADDR1);
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
 
   /*
    * Reset and unpause the sequencer.  The reset is suppose to
-   * start the sequencer running, but we do an unpause to make
-   * sure.
+   * start the sequencer running, so we immediately do a pause_sequencer
+   * since some of our code expects the sequencer paused after a restart
    */
-  outb(SEQRESET | FASTMODE, p->base + SEQCTL);
-
-  unpause_sequencer(p, /*unpause_always*/ TRUE);
+  aic_outb(p, SEQRESET | FASTMODE, SEQCTL);
+  pause_sequencer(p);
 }
 
 
@@ -1135,11 +1431,12 @@
 aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
 {
   unsigned char opcode;
-  struct ins_format3 *instr;
+  struct ins_format3 instr;
+  unsigned char dconsts[4] = { 0, 0, 0, 0 };
 
-  instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+  instr = *(struct ins_format3 *) &seqprog[instrptr * 4];
   /* Pull the opcode */
-  opcode = instr->opcode_addr >> 1;
+  opcode = (instr.opcode_addr & ~DOWNLOAD_CONST_IMMEDIATE) >> 1;
   switch (opcode)
   {
     case AIC_OP_JMP:
@@ -1152,15 +1449,13 @@
     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;
+      address = instr.address;
+      address |= (instr.opcode_addr & ADDR_HIGH_BIT) << 8;
       for (i = 0; i < NUMBER(patches); i++)
       {
         patch = &patches[i];
@@ -1174,20 +1469,23 @@
         }
       }
       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;
+      instr.address = address & 0xFF;
+      instr.opcode_addr &= ~ADDR_HIGH_BIT;
+      instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
     }
-
+       /*  Fall through  */
     case AIC_OP_OR:
     case AIC_OP_AND:
     case AIC_OP_XOR:
     case AIC_OP_ADD:
     case AIC_OP_ADC:
+      if (instr.opcode_addr & DOWNLOAD_CONST_IMMEDIATE)
+      {
+        instr.immediate = dconsts[instr.immediate];
+      }
+      instr.opcode_addr &= ~DOWNLOAD_CONST_IMMEDIATE;
     case AIC_OP_ROL:
-      outsb(p->base + SEQRAM, &instr->immediate, 4);
+      aic_outsb(p, SEQRAM, &instr.immediate, 4);
       break;
 
     default:
@@ -1212,23 +1510,60 @@
   int i;
   int downloaded;
 
-  if (aic7xxx_verbose)
+  if (aic7xxx_verbose & VERBOSE_PROBE)
   {
-    printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+    printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
   }
+  if (aic7xxx_verbose & VERBOSE_PROBE2)
+    printk("\n");
   options = 1;  /* Code for all options. */
   downloaded = 0;
-  if ((p->flags & ULTRA_ENABLED) != 0)
+  if (p->type & AHC_ULTRA)
+  {
     options |= ULTRA;
-  if (p->bus_type == AIC_TWIN)
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+      printk(KERN_INFO "(scsi%d)   Will download code for option ULTRA\n",
+        p->host_no);
+  }
+  if (p->type & AHC_TWIN)
+  {
     options |= TWIN_CHANNEL;
-  if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+      printk(KERN_INFO "(scsi%d)   Will download code for option "
+        "TWIN_CHANNEL\n", p->host_no);
+  }
+  if (p->type & AHC_WIDE)
+  {
+    options |= WIDE;
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+      printk(KERN_INFO "(scsi%d)   Will download code for option WIDE\n",
+        p->host_no);
+  }
+  /* if (p->scb_data->maxscbs > p->scb_data->maxhscbs)  this should always
+                                                        be true, don't test,
+                                                        just do.            */
+  {
     options |= SCB_PAGING;
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+      printk(KERN_INFO "(scsi%d)   Will download code for option SCB_PAGING\n",
+        p->host_no);
+  }
+  /* We don't actually support target mode yet, so leave this out 
+  if (p->flags & AHC_TARGETMODE)
+    options |= TARGET_MODE; */
+
+  if ( (options & ~(ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01)) )
+  {
+    printk(KERN_INFO "(scsi%d) Unknown bits set in the options field, "
+      "correcting.\n", p->host_no);
+    options &= ULTRA|TWIN_CHANNEL|WIDE|SCB_PAGING|0x01;
+  }
+
 
   cur_patch = patches;
-  outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
-  outb(0, p->base + SEQADDR0);
-  outb(0, p->base + SEQADDR1);
+  aic_outb(p, PERRORDIS | LOADRAM, SEQCTL);
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
 
   for (i = 0; i < sizeof(seqprog) / 4;  i++)
   {
@@ -1242,13 +1577,16 @@
     downloaded++;
   }
 
-  outb(FASTMODE, p->base + SEQCTL);
-  outb(0, p->base + SEQADDR0);
-  outb(0, p->base + SEQADDR1);
+  aic_outb(p, FASTMODE, SEQCTL);
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+
+  if (aic7xxx_verbose & VERBOSE_PROBE2)
+    printk(KERN_INFO "(scsi%d) Download complete,", p->host_no);
 
-  if (aic7xxx_verbose)
+  if (aic7xxx_verbose & VERBOSE_PROBE)
   {
-     printk(" %d instructions downloaded\n", downloaded);
+    printk(" %d instructions downloaded\n", downloaded);
   }
 }
 
@@ -1264,7 +1602,7 @@
 static void
 aic7xxx_delay(int seconds)
 {
-  int i;
+  unsigned int i;
 
   /*                        
    * Call udelay() for 1 millisecond inside a loop for  
@@ -1278,139 +1616,65 @@
 
 /*+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)
+aic7xxx_info(struct Scsi_Host *dooh)
 {
-  static char buffer[128];
+  static char buffer[256];
+  char *bp;
+  struct aic7xxx_host *p;
 
-  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
+  bp = &buffer[0];
+  p = (struct aic7xxx_host *)dooh->hostdata;
+  memset(bp, 0, sizeof(buffer));
+  strcpy(bp, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+  strcat(bp, AIC7XXX_C_VERSION);
+  strcat(bp, "/");
+  strcat(bp, AIC7XXX_H_VERSION);
+  strcat(bp, "\n");
+  strcat(bp, "       <");
+  strcat(bp, board_names[p->board_name_index]);
+  strcat(bp, ">");
 
-  return buffer;
+  return(bp);
 }
 
+
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_length
+ *   aic7xxx_scsirate
  *
  * Description:
- *   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.
+ *   Look up the valid period to SCSIRATE conversion in our table
  *-F*************************************************************************/
-static unsigned
-aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+static unsigned char
+aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
+    unsigned char *period, unsigned char *offset, int target, int channel,
+    int set)
 {
-  int i, segments;
-  unsigned length;
-  struct scatterlist *sg;
+  int i = num_aic7xxx_syncrates;
+  unsigned char response_period;
+  unsigned char tindex;
+  unsigned short target_mask;
+  unsigned char lun;
 
-  segments = cmd->use_sg - sg_last;
-  sg = (struct scatterlist *) cmd->request_buffer;
+  tindex = target | (channel << 3);
+  target_mask = 0x01 << tindex;
+  lun = aic_inb(p, SCB_TCL) & 0x07;
 
-  if (cmd->use_sg)
+  response_period = *period;
+
+  /*
+   * If the offset is 0, then the device is requesting asynchronous
+   * transfers.
+   */
+  if ((*period != 0) && (*offset != 0))
   {
-    for (i = length = 0; i < segments; i++)
-    {
-      length += sg[i].length;
-    }
-  }
-  else
-  {
-    length = cmd->request_bufflen;
-  }
-
-  return (length);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_scsirate
- *
- * Description:
- *   Look up the valid period to SCSIRATE conversion in our table
- *-F*************************************************************************/
-static void
-aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
-    unsigned char *period, unsigned char *offset, int target, char channel)
-{
-  int i = num_aic7xxx_syncrates;
-  unsigned long ultra_enb_addr;
-  unsigned char ultra_enb, sxfrctl0;
-
-  /*
-   * If the offset is 0, then the device is requesting asynchronous
-   * transfers.
-   */
-  if ((*period <= aic7xxx_syncrates[i - 1].period) && *offset != 0)
-  {
-    for (i = 0; i < num_aic7xxx_syncrates; i++)
+    for (i = 0; i < num_aic7xxx_syncrates; i++)
     {
       if (*period <= aic7xxx_syncrates[i].period)
       {
@@ -1418,7 +1682,7 @@
          * Watch out for Ultra speeds when ultra is not enabled and
          * vice-versa.
          */
-        if (!(p->flags & ULTRA_ENABLED) &&
+        if (!(p->type & AHC_ULTRA) &&
             (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
         {
           /*
@@ -1431,11 +1695,31 @@
         *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
         *period = aic7xxx_syncrates[i].period;
 
-        if (aic7xxx_verbose)
+        /*
+         * When responding to a target that requests
+         * sync, that rate may fall between two rates
+         * that we can output, but still be a rate
+         * that we can receive.  Because of this,
+         * we may want to respond to the target with
+         * the same rate that it sent to us even
+         * if the period we use to send data to it
+         * is lower.  Only lower the response period
+         * if we must.
+         */
+        if ((i == 0) ||
+           ((aic7xxx_syncrates[i-1].rate & ULTRA_SXFR) != 0
+             && (p->type & AHC_ULTRA) == 0))
+        {
+          response_period = *period;
+        }
+
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
         {
-          printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
-                 "offset %d.\n", p->host_no, target, channel,
+          printk(INFO_LEAD "Synchronous at %sMHz, "
+                 "offset %d.\n", p->host_no, channel, target, lun,
                  aic7xxx_syncrates[i].english, *offset);
+          p->dev_flags[tindex] &= ~ DEVICE_PRINT_SDTR;
         }
         break;
       }
@@ -1450,35 +1734,43 @@
     *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);
+    response_period = 0;
+    if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+         (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+    {
+      printk(INFO_LEAD "Using asynchronous transfers.\n",
+             p->host_no, channel, target, lun);
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
     }
   }
 
   /*
    * Ensure Ultra mode is set properly for this target.
    */
-  ultra_enb_addr = ULTRA_ENB;
-  if ((channel == 'B') || (target > 7))
+  if ( (*scsirate != 0) && 
+       (aic7xxx_syncrates[i].rate & ULTRA_SXFR) )
   {
-    ultra_enb_addr++;
+    p->ultraenb |= target_mask;
   }
-  ultra_enb = inb(p->base + ultra_enb_addr);
-  sxfrctl0 = inb(p->base + SXFRCTL0);
-  if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
+  else
   {
-    ultra_enb |= 0x01 << (target & 0x07);
-    sxfrctl0 |= FAST20;
+    p->ultraenb &= ~target_mask;
   }
-  else
+  if (set)
   {
-    ultra_enb &= ~(0x01 << (target & 0x07));
+    unsigned char sxfrctl0;
+
+    sxfrctl0 = aic_inb(p, SXFRCTL0);
     sxfrctl0 &= ~FAST20;
+    if (p->ultraenb & target_mask)
+    {
+      sxfrctl0 |= FAST20;
+    }
+    aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
+    aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1);
+    aic_outb(p, sxfrctl0, SXFRCTL0);
   }
-  outb(ultra_enb, p->base + ultra_enb_addr);
-  outb(sxfrctl0, p->base + SXFRCTL0);
+  return(response_period);
 }
 
 /*+F*************************************************************************
@@ -1490,7 +1782,7 @@
  *
  *-F*************************************************************************/
 static inline void
-scbq_init(scb_queue_type *queue)
+scbq_init(volatile scb_queue_type *queue)
 {
   queue->head = NULL;
   queue->tail = NULL;
@@ -1505,7 +1797,7 @@
  *
  *-F*************************************************************************/
 static inline void
-scbq_insert_head(scb_queue_type *queue, struct aic7xxx_scb *scb)
+scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
 {
   scb->q_next = queue->head;
   queue->head = scb;
@@ -1521,13 +1813,16 @@
  *   Remove an SCB from the head of the list.
  *
  *-F*************************************************************************/
-static inline void
-scbq_remove_head(scb_queue_type *queue)
+static __inline struct aic7xxx_scb *
+scbq_remove_head(volatile scb_queue_type *queue)
 {
+  struct aic7xxx_scb * scbp;
+  scbp = queue->head;
   if (queue->head != NULL)
     queue->head = queue->head->q_next;
   if (queue->head == NULL)       /* If list is now empty, update tail. */
     queue->tail = NULL;
+  return(scbp);
 }
 
 /*+F*************************************************************************
@@ -1539,7 +1834,7 @@
  *
  *-F*************************************************************************/
 static inline void
-scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+scbq_remove(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
 {
   if (queue->head == scb)
   {
@@ -1580,12 +1875,11 @@
  *
  *-F*************************************************************************/
 static inline void
-scbq_insert_tail(scb_queue_type *queue, struct aic7xxx_scb *scb)
+scbq_insert_tail(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
 {
   scb->q_next = NULL;
   if (queue->tail != NULL)       /* Add the scb at the end of the list. */
     queue->tail->q_next = scb;
-
   queue->tail = scb;             /* Update the tail. */
   if (queue->head == NULL)       /* If list was empty, update head. */
     queue->head = queue->tail;
@@ -1602,18 +1896,14 @@
  *   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,
-    int lun, unsigned char tag)
+aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+    int target, int channel, int lun, unsigned char tag)
 {
   int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
-  char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int chan = (scb->hscb->target_channel_lun >> 3) & 0x01;
   int slun = scb->hscb->target_channel_lun & 0x07;
   int match;
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  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
   match = ((chan == channel) || (channel == ALL_CHANNELS));
   if (match != 0)
     match = ((targ == target) || (target == ALL_TARGETS));
@@ -1622,6 +1912,14 @@
   if (match != 0)
     match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
 
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+  {
+    printk(KERN_INFO "(scsi%d:%d:%d:%d:tag%d) %s search criteria"
+      " (scsi%d:%d:%d:%d:tag%d)\n", p->host_no, CTL_OF_SCB(scb),
+      scb->hscb->tag, (match) ? "matches" : "doesn't match",
+      p->host_no, channel, target, lun, tag);
+  }
+
   return (match);
 }
 
@@ -1639,10 +1937,11 @@
    * Invalidate the tag so that aic7xxx_find_scb doesn't think
    * it's active
    */
-  outb(SCB_LIST_NULL, p->base + SCB_TAG);
+  aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+  aic_outb(p, 0, SCB_CONTROL);
 
-  outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
-  outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+  aic_outb(p, aic_inb(p, FREE_SCBH), SCB_NEXT);
+  aic_outb(p, aic_inb(p, SCBPTR), FREE_SCBH);
 }
 
 /*+F*************************************************************************
@@ -1659,28 +1958,25 @@
   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);
-
+  aic_outb(p, scbptr, SCBPTR);
+  next = aic_inb(p, SCB_NEXT);
+  prev = aic_inb(p, SCB_PREV);
   aic7xxx_add_curscb_to_free_list(p);
 
   if (prev != SCB_LIST_NULL)
   {
-    outb(prev, p->base + SCBPTR);
-    outb(next, p->base + SCB_NEXT);
+    aic_outb(p, prev, SCBPTR);
+    aic_outb(p, next, SCB_NEXT);
   }
   else
   {
-    outb(next, p->base + DISCONNECTED_SCBH);
+    aic_outb(p, next, DISCONNECTED_SCBH);
   }
 
   if (next != SCB_LIST_NULL)
   {
-    outb(next, p->base + SCBPTR);
-    outb(prev, p->base + SCB_PREV);
+    aic_outb(p, next, SCBPTR);
+    aic_outb(p, prev, SCB_PREV);
   }
   return next;
 }
@@ -1692,23 +1988,10 @@
  * Description:
  *   Set the specified target busy.
  *-F*************************************************************************/
-static void
-aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
-    char channel, unsigned char scbid)
+static __inline void
+aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  unsigned char active_scb;
-  unsigned char info_scb;
-  unsigned int  scb_offset;
-
-  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);
+  p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
 }
 
 /*+F*************************************************************************
@@ -1719,28 +2002,17 @@
  *   Returns the index of the busy target, and optionally sets the
  *   target inactive.
  *-F*************************************************************************/
-static unsigned char
-aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
-    char channel, int unbusy)
+static __inline unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
+    int unbusy)
 {
-  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;
-
-  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);
+  busy_scbid = p->untagged_scbs[tcl];
   if (unbusy)
   {
-    outb(SCB_LIST_NULL, p->base + scb_offset);
+    p->untagged_scbs[tcl] = SCB_LIST_NULL;
   }
-  outb(active_scb, p->base + SCBPTR);
   return (busy_scbid);
 }
 
@@ -1760,17 +2032,17 @@
   unsigned char saved_scbptr;
   unsigned char curindex;
 
-  saved_scbptr = inb(p->base + SCBPTR);
+  saved_scbptr = aic_inb(p, SCBPTR);
   curindex = 0;
   for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
   {
-    outb(curindex, p->base + SCBPTR);
-    if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+    aic_outb(p, curindex, SCBPTR);
+    if (aic_inb(p, SCB_TAG) == scb->hscb->tag)
     {
       break;
     }
   }
-  outb(saved_scbptr, p->base + SCBPTR);
+  aic_outb(p, saved_scbptr, SCBPTR);
   if (curindex >= p->scb_data->maxhscbs)
   {
     curindex = SCB_LIST_NULL;
@@ -1787,58 +2059,83 @@
  *   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)
+aic7xxx_allocate_scb(struct aic7xxx_host *p, int force_alloc)
 {
   struct aic7xxx_scb   *scbp = NULL;
-  struct aic7xxx_hwscb *hscbp = NULL;
-#ifdef AGRESSIVE
-  long processor_flags;
+  int scb_size = sizeof(struct aic7xxx_scb) +
+                 sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
+  int i;
+  unsigned long scb_count = 0;
+  struct hw_scatterlist *hsgp;
+  struct aic7xxx_scb *scb_ap;
 
-  save_flags(processor_flags);
-  cli();
-#endif
 
-  scbp = p->scb_data->free_scbs.head;
-  if (scbp != NULL)
+  if (force_alloc == FALSE)
   {
-    scbq_remove_head(&p->scb_data->free_scbs);
+    scbp = scbq_remove_head(&p->scb_data->free_scbs);
+    if (scbp != NULL)
+      return(scbp);
   }
-  else
+  /*
+   * Either there wasn't an SCB or this is a strictly allocation call
+   */
+
+  if (p->scb_data->numscbs < p->scb_data->maxscbs)
   {
-    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++;
+
+    /*
+     * Optimize for 30 scbs at a time, but allow a final allocation of
+     * fewer than 30 scbs.  Except on 64 bit platforms, we optimize for
+     * 29 SCBs at a time because a pointer is 4 bytes larger and we don't
+     * want to overrun this suppossedly 32K allocation to 64K and waste
+     * tons of space.
+     */
+    if( sizeof(void *) == sizeof(int) )
+      scb_count = MIN(30, p->scb_data->maxscbs - p->scb_data->numscbs);
+    else
+      scb_count = MIN(29, p->scb_data->maxscbs - p->scb_data->numscbs);
+    
+    scb_ap = (struct aic7xxx_scb *)kmalloc(scb_size * scb_count, GFP_ATOMIC);
+    if (scb_ap != NULL)
+    {
+      if (aic7xxx_verbose & VERBOSE_QUEUE)
+      {
+        if (p->scb_data->numscbs == 0)
+          printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
+            p->host_no, -1, -1, -1, scb_count);
+        else
+          printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
+            p->host_no, -1, -1, -1, scb_count);
+      }
+      memset(scb_ap, 0, scb_count * scb_size);
+      hsgp = (struct hw_scatterlist *) &scb_ap[scb_count];
+      for (i=0; i < scb_count; i++)
+      {
+        scbp = &scb_ap[i];
+        scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs];
+        scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG];
+        memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb));
+        scbp->hscb->tag = p->scb_data->numscbs;
         /*
          * Place in the scb array; never is removed
          */
-        p->scb_data->scb_array[scb_index] = scbp;
+        p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
+        scbq_insert_head(&p->scb_data->free_scbs, scbp);
       }
     }
+    else
+    {
+      return(NULL);
+    }
   }
-#ifdef AIC7XXX_DEBUG
-  if (scbp != NULL)
+  if (force_alloc == TRUE)
   {
-    p->activescbs++;
+    return((struct aic7xxx_scb *)scb_count);
+  }
+  else
+  {
+    return(scbq_remove_head(&p->scb_data->free_scbs));
   }
-#endif
-
-#ifdef AGRESSIVE
-  restore_flags(processor_flags);
-#endif
-  return (scbp);
 }
 
 /*+F*************************************************************************
@@ -1853,11 +2150,8 @@
 static inline void
 aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  if (p->completeq.tail == NULL)
-    p->completeq.head = cmd;
-  else
-    p->completeq.tail->host_scribble = (char *) cmd;
-  p->completeq.tail = cmd;
+  cmd->host_scribble = (char *)p->completeq.head;
+  p->completeq.head = cmd;
 }
 
 /*+F*************************************************************************
@@ -1871,16 +2165,21 @@
 aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
 {
   Scsi_Cmnd *cmd;
-
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned int cpu_flags = 0;
+#endif
+  
+  DRIVER_LOCK
   while (p->completeq.head != NULL)
   {
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
-    p->device_status[TARGET_INDEX(cmd)].active_cmds--;
+    DRIVER_UNLOCK
     cmd->scsi_done(cmd);
+    DRIVER_LOCK
   }
-  p->completeq.tail = NULL;
+  DRIVER_UNLOCK
 }
 
 /*+F*************************************************************************
@@ -1893,24 +2192,17 @@
 static void
 aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_hwscb *hscb;
-  long flags;
-
-  hscb = scb->hscb;
-  save_flags(flags);
-  cli();
 
   scb->flags = SCB_FREE;
   scb->cmd = NULL;
-  hscb->control = 0;
-  hscb->target_status = 0;
+  scb->sg_count = 0;
+  scb->sg_length = 0;
+  scb->tag_action = 0;
+  scb->hscb->control = 0;
+  scb->hscb->target_status = 0;
+  scb->hscb->target_channel_lun = SCB_LIST_NULL;
 
   scbq_insert_head(&p->scb_data->free_scbs, scb);
-#ifdef AIC7XXX_DEBUG
-  p->activescbs--;  /* For debugging purposes. */
-#endif
-
-  restore_flags(flags);
 }
 
 /*+F*************************************************************************
@@ -1924,25 +2216,31 @@
 aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
   Scsi_Cmnd *cmd = scb->cmd;
+  int tindex = TARGET_INDEX(cmd);
+  struct aic7xxx_scb *scbp;
+  unsigned char queue_depth;
 
   if (scb->flags & SCB_RECOVERY_SCB)
   {
-    p->flags &= ~IN_TIMEOUT;
+    p->flags &= ~AHC_ABORT_PENDING;
   }
-  if (cmd->result == DID_OK)
+  if (scb->flags & SCB_RESET)
   {
-    if (scb->flags & SCB_ABORTED)
-    {
-      cmd->result = (DID_RESET << 16);
-    }
+      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
+        (cmd->result & 0xffff);
+  }
+  else if (scb->flags & SCB_ABORT)
+  {
+      cmd->result = (DID_RESET << 16) | (SUGGEST_RETRY << 24) | 
+        (cmd->result & 0xffff);
   }
   if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
   {
     unsigned short mask;
     int message_error = FALSE;
 
-    mask = 0x01 << TARGET_INDEX(scb->cmd);
-
+    mask = 0x01 << tindex;
+ 
     /*
      * Check to see if we get an invalid message or a message error
      * after failing to negotiate a wide or sync transfer message.
@@ -1959,6 +2257,17 @@
       p->wdtr_pending &= ~mask;
       if (message_error)
       {
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_WDTR) )
+        {
+          printk(INFO_LEAD "Device failed to complete Wide Negotiation "
+            "processing and\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "returned a sense error code for invalid message, "
+            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
+            CTL_OF_SCB(scb));
+          p->dev_flags[tindex] &= ~DEVICE_PRINT_WDTR;
+        }
         p->needwdtr &= ~mask;
         p->needwdtr_copy &= ~mask;
       }
@@ -1968,27 +2277,74 @@
       p->sdtr_pending &= ~mask;
       if (message_error)
       {
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_SDTR) )
+        {
+          printk(INFO_LEAD "Device failed to complete Sync Negotiation "
+            "processing and\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "returned a sense error code for invalid message, "
+            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
+            CTL_OF_SCB(scb));
+          p->dev_flags[tindex] &= ~DEVICE_PRINT_SDTR;
+        }
         p->needsdtr &= ~mask;
         p->needsdtr_copy &= ~mask;
       }
     }
   }
-  aic7xxx_free_scb(p, scb);
-  aic7xxx_queue_cmd_complete(p, cmd);
+  queue_depth = p->dev_temp_queue_depth[tindex];
+  if (queue_depth >= p->dev_active_cmds[tindex])
+  {
+    scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+    if (scbp)
+      scbq_insert_tail(&p->waiting_scbs, scbp);
+    if ( (queue_depth > p->dev_active_cmds[tindex]) && scbp)
+    {
+      scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+      if (scbp)
+        scbq_insert_tail(&p->waiting_scbs, scbp);
+    }
+  }
+  if ( (p->dev_timer[tindex].expires) &&
+      ((p->dev_active_cmds[tindex] == 1) ||
+       (p->dev_max_queue_depth[tindex] ==
+        p->dev_temp_queue_depth[tindex])) )
+  {
+    del_timer(&p->dev_timer[tindex]);
+    p->dev_timer[tindex].expires = 0;
+    p->dev_temp_queue_depth[tindex] = 
+          p->dev_max_queue_depth[tindex];
+  }
+  p->dev_active_cmds[tindex]--;
+  p->activescbs--;
+
+  /*
+   * If this was an untagged I/O, unbusy the target so the sequencer won't
+   * mistake things later
+   */
+  if (aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, FALSE) ==
+      scb->hscb->tag)
+  {
+    aic7xxx_index_busy_target(p, scb->hscb->target_channel_lun, TRUE);
+  }
 
 #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.
+     * 
+     * We set a lower limit of 512 bytes on the transfer length.  We
+     * ignore anything less than this because we don't have a real
+     * reason to count it.  Read/Writes to tapes are usually about 20K
+     * and disks are a minimum of 512 bytes unless you want to count
+     * non-read/write commands (such as TEST_UNIT_READY) which we don't
      */
-    actual = aic7xxx_length(cmd, 0);
-    if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
-        && (aic7xxx_error(cmd) == 0))
+    actual = scb->sg_length;
+    if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
     {
       struct aic7xxx_xferstats *sp;
       long *ptr;
@@ -1997,7 +2353,14 @@
       sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
       sp->xfers++;
 
-      if (cmd->request.cmd == WRITE)
+      /*
+       * For block devices, cmd->request.cmd is always == either READ or
+       * WRITE.  For character devices, this isn't always set properly, so
+       * we check data_cmnd[0].  This catches the conditions for st.c, but
+       * I'm still not sure if request.cmd is valid for sg devices.
+       */
+      if ( (cmd->request.cmd == WRITE) || (cmd->data_cmnd[0] == WRITE_6) ||
+           (cmd->data_cmnd[0] == WRITE_FILEMARKS) )
       {
         sp->w_total++;
         sp->w_total512 += (actual >> 9);
@@ -2024,6 +2387,10 @@
     }
   }
 #endif /* AIC7XXX_PROC_STATS */
+
+  aic7xxx_free_scb(p, scb);
+  aic7xxx_queue_cmd_complete(p, cmd);
+
 }
 
 /*+F*************************************************************************
@@ -2039,20 +2406,25 @@
 aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
 {
   struct aic7xxx_scb *scb;
-  int i;
+  int i, found = 0;
 
   for (i = 0; i < p->scb_data->numscbs; i++)
   {
     scb = p->scb_data->scb_array[i];
     if (scb->flags & SCB_QUEUED_FOR_DONE)
     {
-#ifdef AIC7XXX_DEBUG_ABORT
-      printk("(scsi%d:%d:%d) Aborting scb %d\n",
-             p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
-#endif
+      if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+        printk(INFO_LEAD "Aborting scb %d\n",
+             p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+      found++;
       aic7xxx_done(p, scb);
     }
   }
+  if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
+  {
+    printk(INFO_LEAD "%d commands found and queued for "
+        "completion.\n", p->host_no, -1, -1, -1, found);
+  }
   if (complete)
   {
     aic7xxx_done_cmds_complete(p);
@@ -2076,14 +2448,9 @@
   /*
    * Select the SCB we want to abort and pull the next pointer out of it.
    */
-  curscb = inb(p->base + SCBPTR);
-  outb(scbpos, p->base + SCBPTR);
-  next = inb(p->base + SCB_NEXT);
-
-  /*
-   * Clear the necessary fields
-   */
-  outb(0, p->base + SCB_CONTROL);
+  curscb = aic_inb(p, SCBPTR);
+  aic_outb(p, scbpos, SCBPTR);
+  next = aic_inb(p, SCB_NEXT);
 
   aic7xxx_add_curscb_to_free_list(p);
 
@@ -2095,25 +2462,21 @@
     /*
      * First in the list
      */
-    outb(next, p->base + WAITING_SCBH);
+    aic_outb(p, next, WAITING_SCBH);
   }
   else
   {
     /*
      * Select the scb that pointed to us and update its next pointer.
      */
-    outb(prev, p->base + SCBPTR);
-    outb(next, p->base + SCB_NEXT);
+    aic_outb(p, prev, SCBPTR);
+    aic_outb(p, next, SCB_NEXT);
   }
   /*
    * Point us back at the original scb position and inform the SCSI
    * system that the command has been aborted.
    */
-  outb(curscb, p->base + SCBPTR);
-  scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-  scb->flags &= ~SCB_ACTIVE;
-  scb->cmd->result = (DID_RESET << 16);
-
+  aic_outb(p, curscb, SCBPTR);
   return (next);
 }
 
@@ -2126,219 +2489,381 @@
  *   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)
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, int channel,
+    int lun, unsigned char tag, int flags, int requeue,
+    volatile scb_queue_type *queue)
 {
-  unsigned char saved_queue[AIC7XXX_MAXSCB];
-  int      queued = inb(p->base + QINCNT) & p->qcntmask;
-  int      i;
   int      found;
+  unsigned char qinpos, qintail;
   struct aic7xxx_scb *scbp;
-  scb_queue_type removed_scbs;
 
   found = 0;
-  scbq_init (&removed_scbs);
-  for (i = 0; i < (queued - found); i++)
+  qinpos = aic_inb(p, QINPOS);
+  qintail = p->qinfifonext;
+
+  p->qinfifonext = qinpos;
+
+  while (qinpos != qintail)
   {
-    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))
+    scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
+    if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
     {
        /*
         * We found an scb that needs to be removed.
         */
-       if (requeue)
+       if (requeue && (queue != NULL))
+       {
+         if ( !(scbp->flags & SCB_WAITINGQ) )
+         {
+           scbq_insert_tail(queue, scbp);
+           p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]--;
+           p->activescbs--;
+           scbp->flags |= SCB_WAITINGQ;
+         }
+         if ( !(scbp->tag_action & TAG_ENB) )
+         {
+           aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+             TRUE);
+         }
+       }
+       else if (requeue)
        {
-         scbq_insert_head(&removed_scbs, scbp);
+         p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
        }
        else
        {
-         scbp->flags = flags;
-         scbp->flags &= ~SCB_ACTIVE;
-         /*
-          * XXX - Don't know what error to use here.
-          */
-         aic7xxx_error(scbp->cmd) = DID_RESET;
+        /*
+         * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
+         * flags we were called with, presumeably so aic7xxx_run_done_queue
+         * can find this scb
+         */
+         scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
+         if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+                                       FALSE) == scbp->hscb->tag)
+         {
+           aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+             TRUE);
+         }
        }
-       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)
+    else
     {
-      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;
+      p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
     }
   }
+  /*
+   * Now that we've done the work, clear out any left over commands in the
+   * qinfifo and update the KERNEL_QINPOS down on the card.
+   *
+   *  NOTE: This routine expect the sequencer to already be paused when
+   *        it is run....make sure it's that way!
+   */
+  qinpos = p->qinfifonext;
+  while(qinpos != qintail)
+  {
+    p->qinfifo[qinpos++] = SCB_LIST_NULL;
+  }
+  aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
 
   return (found);
 }
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_scb_on_qoutfifo
+ *
+ * Description:
+ *   Is the scb that was passed to us currently on the qoutfifo?
+ *-F*************************************************************************/
+static int
+aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int i=0;
+
+  while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
+  {
+    if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
+      return TRUE;
+    else
+      i++;
+  }
+  return FALSE;
+}
+
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_reset_device
  *
  * Description:
  *   The device at the given target/channel has been reset.  Abort
- *   all active and queued scbs for that target/channel.
+ *   all active and queued scbs for that target/channel.  This function
+ *   need not worry about linked next pointers because if was a MSG_ABORT_TAG
+ *   then we had a tagged command (no linked next), if it was MSG_ABORT or
+ *   MSG_BUS_DEV_RESET then the device won't know about any commands any more
+ *   and no busy commands will exist, and if it was a bus reset, then nothing
+ *   knows about any linked next commands any more.  In all cases, we don't
+ *   need to worry about the linked next or busy scb, we just need to clear
+ *   them.
  *-F*************************************************************************/
-static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+static void
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
                      int lun, unsigned char tag)
 {
   struct aic7xxx_scb *scbp;
-  unsigned char active_scb;
-  int i = 0;
-  int found;
+  unsigned char active_scb, tcl;
+  int i = 0, j, init_lists = FALSE;
 
   /*
    * Restore this when we're done
    */
-  active_scb = inb(p->base + SCBPTR);
-
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
-         p->host_no, target, CHAN_TO_INT(channel), active_scb);
-#endif
+  active_scb = aic_inb(p, SCBPTR);
 
+  if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
+    printk(INFO_LEAD "Reset device, active_scb %d\n",
+         p->host_no, channel, target, lun, active_scb);
   /*
    * Deal with the busy target and linked next issues.
    */
   {
     int min_target, max_target;
-    unsigned char busy_scbid;
+    struct aic7xxx_scb *scbp, *prev_scbp;
 
     /* Make all targets 'relative' to bus A. */
     if (target == ALL_TARGETS)
     {
       switch (channel)
       {
-        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 0:
+                 min_target = 0;
+                 max_target = (p->type & AHC_WIDE) ? 15 : 7;
+                 break;
+        case 1:
+                 min_target = 8;
+                 max_target = 15;
+                 break;
         case ALL_CHANNELS:
         default:
-  	  min_target = 0;
-  	  max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
-  	  break;
+                 min_target = 0;
+                 max_target = (p->type & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
+                 break;
       }
     }
     else
     { 
-      min_target = target + channel == 'B' ? 8 : 0;
+      min_target = target | (channel << 3);
       max_target = min_target;
     }
 
+
     for (i = min_target; i <= max_target; i++)
     {
-      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
-      if (busy_scbid < p->scb_data->numscbs)
+      if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+        printk(INFO_LEAD "Cleaning up status information "
+          "and delayed_scbs.\n", p->host_no, channel, i, lun);
+      if ( !(p->dev_flags[i] & DEVICE_TAGGED_SUCCESS) &&
+            (p->dev_active_cmds[i]) &&
+            (p->tagenable & (0x01 << i)) )
+      {
+        printk(INFO_LEAD "Device appears to be choking on tagged commands.\n",
+          p->host_no, channel, i, lun);
+        printk(INFO_LEAD "Will use untagged I/O instead.\n", p->host_no,
+          channel, i, lun);
+        p->dev_max_queue_depth[i] = 1;
+        p->dev_temp_queue_depth[i] = 1;
+        p->tagenable &= ~(0x01 << i);
+        p->orderedtag &= ~(0x01 << i);
+      }
+      p->dev_flags[i] &= ~BUS_DEVICE_RESET_PENDING;
+      if ( tag == SCB_LIST_NULL )
+      {
+        p->dev_flags[i] |= DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+        p->dev_last_reset[i] = jiffies;
+        p->dev_last_queue_full_count[i] = 0;
+        p->dev_last_queue_full[i] = 0;
+        p->dev_temp_queue_depth[i] =
+          p->dev_max_queue_depth[i];
+  /*
+   * In case this isn't a full bus reset, we want to add a 4 second timer in
+   * here so that we can delay all re-sent commands for this device for the
+   * 4 seconds and then have our timer routine pick them back up.
+   */
+        if( (p->dev_timer[i].prev != NULL) ||
+            (p->dev_timer[i].next != NULL) )
+        {
+          del_timer(&p->dev_timer[i]);
+        }
+        p->dev_timer[i].expires = jiffies + (3 * HZ);
+        add_timer(&p->dev_timer[i]);
+      }
+      for(j=0; j<MAX_LUNS; j++)
       {
-  	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 = le32_to_cpu(busy_scb->hscb->data_count) >> 24;
-
-  	if (next_scbid == SCB_LIST_NULL)
+        if (channel == 1)
+          tcl = ((i << 4) & 0x70) | (channel << 3) | j;
+        else
+          tcl = (i << 4) | (channel << 3) | j;
+        if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
+             (tag == SCB_LIST_NULL) )
+          aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
+      }
+      j = 0; 
+      prev_scbp = NULL; 
+      scbp = p->delayed_scbs[i].head;
+      while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+      {
+        prev_scbp = scbp;
+        scbp = scbp->q_next;
+        if ( prev_scbp == scbp )
+        {
+          if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+            printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+              "in the delayed_scbs queue!\n", p->host_no, channel, i, lun);
+          scbp = NULL;
+          prev_scbp->q_next = NULL;
+          p->delayed_scbs[i].tail = prev_scbp;
+        }
+        if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
         {
-  	  busy_scbid = aic7xxx_find_scb(p, busy_scb);
-
-  	  if (busy_scbid != SCB_LIST_NULL)
+          scbq_remove(&p->delayed_scbs[i], prev_scbp);
+          if ( !(prev_scbp->flags & SCB_QUEUED_ABORT) )
           {
-  	    outb(busy_scbid, p->base + SCBPTR);
-  	    next_scbid = inb(p->base + SCB_LINKED_NEXT);
-  	  }
-  	}
+            p->dev_active_cmds[i]++;
+            p->activescbs++;
+          }
+          prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+          prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+        }
+      }
+      if ( j > p->scb_data->numscbs )
+      {
+        if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+          printk(WARN_LEAD "Yikes!! There's a loop in the "
+            "delayed_scbs queue!\n", p->host_no, channel, i, lun);
+        scbq_init(&p->delayed_scbs[i]);
+      }
+      if ( (p->delayed_scbs[i].head == NULL) &&
+           (p->dev_timer[i].expires) )
+      {
+        del_timer(&p->dev_timer[i]);
+        p->dev_timer[i].expires = 0;
+      }
+    }
+  }
 
-  	if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
-        {
-  	  aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
-  	}
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+    printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
+  aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+      SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
+
+/*
+ *  Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
+ *  ABORT/RESET commands.
+ */
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+    printk(INFO_LEAD "Cleaning waiting_scbs.\n", p->host_no, channel,
+      target, lun );
+  {
+    struct aic7xxx_scb *scbp, *prev_scbp;
 
-  	if (next_scbid != SCB_LIST_NULL)
+    j = 0; 
+    prev_scbp = NULL; 
+    scbp = p->waiting_scbs.head;
+    while ( (scbp != NULL) && (j++ <= p->scb_data->numscbs) )
+    {
+      prev_scbp = scbp;
+      scbp = scbp->q_next;
+      if ( prev_scbp == scbp )
+      {
+        if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+          printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+            "in the waiting_scbs queue!\n", p->host_no, CTL_OF_SCB(scbp));
+        scbp = NULL;
+        prev_scbp->q_next = NULL;
+        p->waiting_scbs.tail = prev_scbp;
+      }
+      if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
+      {
+        scbq_remove(&p->waiting_scbs, prev_scbp);
+        if ( !(prev_scbp->flags & SCB_QUEUED_ABORT) )
         {
-  	  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;
-  	}
+          p->dev_active_cmds[TARGET_INDEX(prev_scbp->cmd)]++;
+          p->activescbs++;
+        }
+        prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+        prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
       }
     }
+    if ( j > p->scb_data->numscbs )
+    {
+      if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+        printk(WARN_LEAD "Yikes!! There's a loop in the "
+          "waiting_scbs queue!\n", p->host_no, channel, target, lun);
+      scbq_init(&p->waiting_scbs);
+    }
   }
 
-  found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
-      SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
 
   /*
    * Search waiting for selection list.
    */
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+    printk(INFO_LEAD "Cleaning waiting for selection "
+      "list.\n", p->host_no, channel, target, lun);
   {
     unsigned char next, prev, scb_index;
 
-    next = inb(p->base + WAITING_SCBH);  /* Start at head of list. */
+    next = aic_inb(p, WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
-
-    while (next != SCB_LIST_NULL)
+    j = 0;
+    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
     {
-      outb(next, p->base + SCBPTR);
-      scb_index = inb(p->base + SCB_TAG);
+      aic_outb(p, next, SCBPTR);
+      scb_index = aic_inb(p, SCB_TAG);
       if (scb_index >= p->scb_data->numscbs)
       {
-        panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
-              scb_index, p->scb_data->numscbs);
+       /*
+        * No aic7xxx_verbose check here.....we want to see this since it
+        * means either the kernel driver or the sequencer screwed things up
+        */
+        printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
+          "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+          p->scb_data->numscbs);
+        next = aic_inb(p, SCB_NEXT);
+        aic7xxx_add_curscb_to_free_list(p);
       }
-      scbp = p->scb_data->scb_array[scb_index];
-      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      else
       {
-        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)
+        scbp = p->scb_data->scb_array[scb_index];
+        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
         {
-          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))
+          next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+          scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+          scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+          if (prev == SCB_LIST_NULL)
           {
-            scbq_insert_head(&p->waiting_scbs, next_scb);
-            next_scb->flags |= SCB_WAITINGQ;
+            /*
+             * This is either the first scb on the waiting list, or we
+             * have already yanked the first and haven't left any behind.
+             * Either way, we need to turn off the selection hardware if
+             * it isn't already off.
+             */
+            aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
           }
         }
-        found++;
-      }
-      else
-      {
-        prev = next;
-        next = inb(p->base + SCB_NEXT);
+        else
+        {
+          prev = next;
+          next = aic_inb(p, SCB_NEXT);
+        }
       }
     }
+    if ( j > p->scb_data->maxhscbs )
+    {
+      printk(WARN_LEAD "Yikes!!  There is a loop in the waiting for "
+        "selection list!\n", p->host_no, channel, target, lun);
+      init_lists = TRUE;
+    }
   }
 
   /*
@@ -2348,47 +2873,114 @@
   {
     unsigned char next, prev, scb_index;
 
-    next = inb(p->base + DISCONNECTED_SCBH);
+    next = aic_inb(p, DISCONNECTED_SCBH);
     prev = SCB_LIST_NULL;
-
-    while (next != SCB_LIST_NULL)
+    j = 0;
+    while ( (next != SCB_LIST_NULL) && (j++ <= p->scb_data->maxhscbs) )
     {
-      outb(next, p->base + SCBPTR);
-      scb_index = inb(p->base + SCB_TAG);
+      aic_outb(p, next, SCBPTR);
+      scb_index = aic_inb(p, 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);
+        printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
+          "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+          p->scb_data->numscbs);
+        next = aic7xxx_rem_scb_from_disc_list(p, next);
       }
-      scbp = p->scb_data->scb_array[scb_index];
-      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      else
       {
-        next = aic7xxx_rem_scb_from_disc_list(p, next);
+        scbp = p->scb_data->scb_array[scb_index];
+        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+        {
+          next = aic7xxx_rem_scb_from_disc_list(p, next);
+          scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+          scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+          scbp->hscb->control = 0;
+        }
+        else
+        {
+          prev = next;
+          next = aic_inb(p, SCB_NEXT);
+        }
+      }
+    }
+    if ( j > p->scb_data->maxhscbs )
+    {
+      printk(WARN_LEAD "Yikes!!  There is a loop in the disconnected list!\n",
+        p->host_no, channel, target, lun);
+      init_lists = TRUE;
+    }
+  }
+
+  /*
+   * Walk the free list making sure no entries on the free list have
+   * a valid SCB_TAG value or SCB_CONTROL byte.
+   */
+  {
+    unsigned char next;
+
+    j = 0;
+    next = aic_inb(p, FREE_SCBH);
+    while ( (next != SCB_LIST_NULL) && (j++ < p->scb_data->maxhscbs) )
+    {
+      aic_outb(p, next, SCBPTR);
+      if ( aic_inb(p, SCB_TAG) < p->scb_data->numscbs )
+      {
+        printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
+          target, lun);
+        init_lists = TRUE;
+        next = SCB_LIST_NULL;
       }
       else
       {
-        prev = next;
-        next = inb(p->base + SCB_NEXT);
+        aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+        aic_outb(p, 0, SCB_CONTROL);
+        next = aic_inb(p, SCB_NEXT);
       }
     }
+    if ( j > p->scb_data->maxhscbs )
+    {
+      printk(WARN_LEAD "Yikes!!  There is a loop in the free list!\n",
+        p->host_no, channel, target, lun);
+      init_lists = TRUE;
+    }
   }
 
   /*
    * 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++)
+  if (init_lists)
+  {
+    aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
+    aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+    aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
+  }
+  for (i = p->scb_data->maxhscbs; i >= 0; --i)
   {
     unsigned char scbid;
 
-    outb(i, p->base + SCBPTR);
-    scbid = inb(p->base + SCB_TAG);
-    if (scbid < p->scb_data->numscbs)
+    aic_outb(p, i, SCBPTR);
+    if (init_lists)
+    {
+      aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+      aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+      aic_outb(p, SCB_LIST_NULL, SCB_PREV);
+      aic_outb(p, 0, SCB_CONTROL);
+      aic7xxx_add_curscb_to_free_list(p);
+    }
+    else
     {
-      scbp = p->scb_data->scb_array[scbid];
-      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      scbid = aic_inb(p, SCB_TAG);
+      if (scbid < p->scb_data->numscbs)
       {
-        aic7xxx_add_curscb_to_free_list(p);
+        scbp = p->scb_data->scb_array[scbid];
+        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+        {
+          aic_outb(p, 0, SCB_CONTROL);
+          aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+          aic7xxx_add_curscb_to_free_list(p);
+        }
       }
     }
   }
@@ -2397,31 +2989,28 @@
    * 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.
+   * Any commands we find here we know this about, it wasn't on any queue,
+   * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
+   * lists, so it really must have been a paged out SCB.  In that case,
+   * we shouldn't need to bother with updating any counters, just mark
+   * the correct flags and go on.
    */
   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))
+    if ((scbp->flags & SCB_ACTIVE) &&
+        aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
+        !aic7xxx_scb_on_qoutfifo(p, scbp))
     {
-      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;
-      }
+      scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+      scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
     }
   }
 
-  outb(active_scb, p->base + SCBPTR);
-  return (found);
+  aic_outb(p, active_scb, SCBPTR);
 }
 
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_clear_intstat
@@ -2433,10 +3022,10 @@
 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);
+  aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
+  aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+       CLRPHASECHG | CLRREQINIT, CLRSINT1);
+  aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT, CLRINT);
 }
 
 /*+F*************************************************************************
@@ -2449,32 +3038,26 @@
 static void
 aic7xxx_reset_current_bus(struct aic7xxx_host *p)
 {
-  unsigned long processor_flags;
   unsigned char scsiseq;
 
-  save_flags(processor_flags);
-  cli();
-
   /* Disable reset interrupts. */
-  outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+  aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
 
   /* Turn on the bus reset. */
-  scsiseq = inb(p->base + SCSISEQ);
-  outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+  scsiseq = aic_inb(p, SCSISEQ);
+  aic_outb(p, scsiseq | SCSIRSTO, SCSISEQ);
 
   udelay(1000);
 
   /* Turn off the bus reset. */
-  outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+  aic_outb(p, scsiseq & ~SCSIRSTO, SCSISEQ);
 
   aic7xxx_clear_intstat(p);
 
   /* Re-enable reset interrupts. */
-  outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+  aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
 
   udelay(1000);
-
-  restore_flags(processor_flags);
 }
 
 /*+F*************************************************************************
@@ -2484,22 +3067,19 @@
  * Description:
  *   Reset the channel.
  *-F*************************************************************************/
-static int
-aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
+static void
+aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
 {
   unsigned long offset, offset_max;
-  int found;
   unsigned char sblkctl;
-  char cur_channel;
+  int cur_channel;
+
+  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+    printk(INFO_LEAD "Reset channel called, %s initiate reset.\n",
+      p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
 
-  pause_sequencer(p);
-  /*
-   * Clean up all the state information for the pending transactions
-   * on this bus.
-   */
-  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
-  if (channel == 'B')
+  if (channel == 1)
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
     p->sdtr_pending &= 0x00FF;
@@ -2508,7 +3088,7 @@
   }
   else
   {
-    if (p->bus_type == AIC_WIDE)
+    if (p->type & AHC_WIDE)
     {
       p->needsdtr = p->needsdtr_copy;
       p->needwdtr = p->needwdtr_copy;
@@ -2534,80 +3114,77 @@
      */
     u_char targ_scratch;
 
-    targ_scratch = inb(p->base + offset);
+    targ_scratch = aic_inb(p, offset);
     targ_scratch &= SXFR;
-    outb(targ_scratch, p->base + offset);
-    offset++;
+    aic_outb(p, targ_scratch, offset++);
   }
 
   /*
    * Reset the bus and unpause/restart the controller
    */
-  sblkctl = inb(p->base + SBLKCTL);
-  cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
+  sblkctl = aic_inb(p, SBLKCTL);
+  cur_channel = (sblkctl & SELBUSB) >> 3;
   if (cur_channel != channel)
   {
     /*
      * Case 1: Command for another bus is active
      */
-#ifdef AIC7XXX_DEBUG_ABORT
-    printk("scsi%d: Stealthily resetting channel %c\n",
-           p->host_no, channel);
-#endif
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Stealthily resetting idle channel.\n", p->host_no,
+        channel, -1, -1);
     /*
      * Stealthily reset the other bus without upsetting the current bus.
      */
-    outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
-    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+    aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
     if (initiate_reset)
     {
       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(0, p->base + SCSISEQ);
+    aic_outb(p, 0, SCSISEQ);
     aic7xxx_clear_intstat(p);
-    outb(sblkctl, p->base + SBLKCTL);
-    unpause_sequencer(p, /* unpause_always */ FALSE);
+    aic_outb(p, sblkctl, SBLKCTL);
   }
   else
   {
     /*
      * Case 2: A command from this bus is active or we're idle.
      */
-#ifdef AIC7XXX_DEBUG_ABORT
-    printk("scsi%d: Resetting current channel %c\n",
-           p->host_no, channel);
-#endif
-    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Resetting currently active channel.\n", p->host_no,
+        channel, -1, -1);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+      SIMODE1);
+    p->flags &= ~AHC_HANDLING_REQINITS;
+    p->msg_type = MSG_TYPE_NONE;
+    p->msg_len = 0;
     if (initiate_reset)
     {
       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(0, p->base + SCSISEQ);
+    aic_outb(p, 0, SCSISEQ);
     aic7xxx_clear_intstat(p);
+  }
+  if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+    printk(INFO_LEAD "Channel reset\n", p->host_no, channel, -1, -1);
+  /*
+   * Clean up all the state information for the pending transactions
+   * on this bus.
+   */
+  aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+
+  if ( !(p->type & AHC_TWIN) )
+  {
     restart_sequencer(p);
-#ifdef AIC7XXX_DEBUG_ABORT
-    printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
-#endif
   }
 
   /*
    * Now loop through all the SCBs that have been marked for abortion,
    * and call the scsi_done routines.
    */
-  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
-  return (found);
+  if(!(p->flags & AHC_IN_ISR))
+    aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  return;
 }
 
 /*+F*************************************************************************
@@ -2622,47 +3199,108 @@
 aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb *scb;
+  int tindex;
+  int sent;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
+
 
   if (p->waiting_scbs.head == NULL)
     return;
 
-  pause_sequencer(p);
+  sent = 0;
+
   /*
    * First handle SCBs that are waiting but have been assigned a slot.
    */
-  scb = p->waiting_scbs.head;
-  while (scb != NULL)
+  DRIVER_LOCK
+  while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
   {
-    if (p->curqincnt >= p->qfullcount)
+    tindex = TARGET_INDEX(scb->cmd);
+    if ( (p->dev_active_cmds[tindex] >=
+          p->dev_temp_queue_depth[tindex]) ||
+         (p->dev_last_reset[tindex] >= (jiffies + (3 * HZ))) )
     {
-      p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
-      if (p->curqincnt >= p->qfullcount)
-      {
-        break;
-      }
+        scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+    }
+    else
+    {
+        scb->flags &= ~SCB_WAITINGQ;
+        if ( !(scb->flags & SCB_QUEUED_ABORT) )
+        {
+          p->dev_active_cmds[tindex]++;
+          p->activescbs++;
+        }
+        if ( !(scb->tag_action) )
+        {
+          aic7xxx_busy_target(p, scb);
+        }
+        p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+        sent++;
+    }
+  }
+  if (sent)
+  {
+    if(p->type & AHC_AIC78x0)
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    else
+    {
+      pause_sequencer(p);
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+      unpause_sequencer(p, FALSE);
     }
+    if (p->activescbs > p->max_activescbs)
+      p->max_activescbs = p->activescbs;
+  }
+  DRIVER_UNLOCK
+}
 
-    /*
-     * We have some space.
-     */
-    scbq_remove_head(&(p->waiting_scbs));
-    scb->flags &= ~SCB_WAITINGQ;
 
-    outb(scb->hscb->tag, p->base + QINFIFO);
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_timer
+ *
+ * Description:
+ *   Take expired extries off of delayed queues and place on waiting queue
+ *   then run waiting queue to start commands.
+ ***************************************************************************/
+static void
+aic7xxx_timer(struct aic7xxx_host *p)
+{
+  int i;
+  unsigned long cpu_flags = 0;
+  struct aic7xxx_scb *scb;
 
-    if ((p->flags & PAGE_ENABLED) != 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)   
+  DRIVER_LOCK
+#else
+  spin_lock_irqsave(&io_request_lock, cpu_flags);
+#endif
+  for(i=0; i<MAX_TARGETS; i++)
+  {
+    if ( (p->dev_timer[i].expires) && 
+          (p->dev_timer[i].expires <= jiffies) )
     {
-      /*
-       * We only care about this statistic when paging
-       * since it's impossible to overflow the qinfifo
-       * in the non-paging case.
-       */
-      p->curqincnt++;
+      p->dev_timer[i].expires = 0;
+      if ( (p->dev_timer[i].prev != NULL) ||
+           (p->dev_timer[i].next != NULL) )
+      {
+        del_timer(&p->dev_timer[i]);
+      }
+      p->dev_temp_queue_depth[i] =  p->dev_max_queue_depth[i];
+      while ( (scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL )
+      {
+        scbq_insert_tail(&p->waiting_scbs, scb);
+      }
     }
-    scb = p->waiting_scbs.head;
   }
-
-  unpause_sequencer(p, FALSE);
+  aic7xxx_run_waiting_queues(p);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)   
+  DRIVER_UNLOCK
+#else
+  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+#endif
 }
 
 /*+F*************************************************************************
@@ -2674,15 +3312,15 @@
  *   buffer on the sequencer.
  *-F*************************************************************************/
 static void
-aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
-    unsigned char period, unsigned char offset)
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, 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);
+  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+  p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
+  p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
+  p->msg_buf[p->msg_index++] = period;
+  p->msg_buf[p->msg_index++] = offset;
+  p->msg_len += 5;
 }
 
 /*+F*************************************************************************
@@ -2694,14 +3332,13 @@
  *   on the sequencer.
  *-F*************************************************************************/
 static void
-aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
-    unsigned char bus_width)
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, 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);
+  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+  p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
+  p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
+  p->msg_buf[p->msg_index++] = bus_width;
+  p->msg_len += 4;
 }
 
 /*+F*************************************************************************
@@ -2716,7 +3353,7 @@
 {
   struct aic7xxx_hwscb *hscb;
   Scsi_Cmnd *cmd;
-  int actual;
+  int actual, i;
 
   cmd = scb->cmd;
   hscb = scb->hscb;
@@ -2734,18 +3371,22 @@
      *  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 = scb->sg_length;
+    for (i=1; i < hscb->residual_SG_segment_count; i++)
+    {
+      actual -= scb->sg_list[scb->sg_count - i].length;
+    }
     actual -= (hscb->residual_data_count[2] << 16) |
               (hscb->residual_data_count[1] <<  8) |
               hscb->residual_data_count[0];
 
     if (actual < cmd->underflow)
     {
-      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);
+      if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+        printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
+          "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
+          (cmd->request.cmd == WRITE) ? "wrote" : "read", actual,
+          hscb->residual_SG_segment_count);
       aic7xxx_error(cmd) = DID_RETRY_COMMAND;
       aic7xxx_status(cmd) = hscb->target_status;
     }
@@ -2769,31 +3410,29 @@
  *   Interrupt handler for sequencer interrupts (SEQINT).
  *-F*************************************************************************/
 static void
-aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, int channel)
 {
   unsigned short targ_mask;
   unsigned char  targ_scratch;
   int scratch_offset = target;
-  int found;
 
-  if (channel == 'B')
-  {
-    scratch_offset += 8;
-  }
+  scratch_offset += channel << 3;
+
   targ_mask = (0x01 << scratch_offset);
   /*
    * Go back to async/narrow transfers and renegotiate.
    */
-  p->needsdtr |= p->needsdtr_copy & targ_mask;
-  p->needwdtr |= p->needwdtr_copy & targ_mask;
+  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 = aic_inb(p, 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);
+  aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
+  aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+    printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
+      target, -1);
   aic7xxx_run_done_queue(p, /*complete*/ TRUE);
 }
 
@@ -2809,107 +3448,35 @@
 {
   struct aic7xxx_scb *scb;
   unsigned short target_mask;
-  unsigned char target, scratch_offset;
+  unsigned char target, scratch_offset, lun;
+  unsigned char queue_flag = FALSE;
   char channel;
 
-  if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
-  {
-    target = (inb(p->base + SELID) >> 4) & 0x0F;
-  }
-  else
-  {
-    target = (inb(p->base + SCSIID) >> 4) & 0x0F;
-  }
-  scratch_offset = target;
-  channel = 'A';
-  if (inb(p->base + SBLKCTL) & SELBUSB)
-  {
-    channel = 'B';
-    scratch_offset += 8;
-  }
+  target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
+  channel = (aic_inb(p, SBLKCTL) >> 3) & 0x01;
+  scratch_offset = target + (channel << 3);
+  lun = aic_inb(p, SAVED_TCL) & 0x07;
   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);
-
-        if (arg1 == SCB_LIST_NULL)
-        {
-          /* untagged request */
-          scb_index = busy_scbid;
-        }
-        else
-        {
-          scb_index = arg1;
-        }
-
-        if (scb_index < p->scb_data->numscbs)
-        {
-          scb = p->scb_data->scb_array[scb_index];
-          if (scb->hscb->control & ABORT_SCB)
-          {
-            /*
-             * 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;
-
-    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);
+        printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
+               "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
+        printk(WARN_LEAD "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+               p->host_no, channel, target, lun,
+               aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
+               (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
       }
       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));
+        if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+          printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
+            "target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
+            aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
       }
       break;
 
@@ -2917,559 +3484,652 @@
       {
         /*
          * The reconnecting target either did not send an identify
-         * message, or did, but we didn't find and SCB to match and
+         * message, or did, but we didn't find an 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));
+        if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
+          printk(INFO_LEAD "Target did not send an IDENTIFY message; "
+            "LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
+            lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
 
-        found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+        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)
+      if (aic_inb(p, LASTPHASE) == P_BUSFREE)
       {
-        printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
-               p->host_no, CHAN_TO_INT(channel), target);
+        if (aic7xxx_verbose & VERBOSE_SEQINT)
+          printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
+            target, lun);
         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);
+        if (aic7xxx_verbose & VERBOSE_SEQINT)
+          printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
+            channel, target, lun);
       }
       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];
+        p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+        p->msg_len = 0;
+        p->msg_index = 0;
 
-	switch (message_code)
-	{
-          case MSG_EXT_SDTR:
-          {
-            unsigned char period;
-            unsigned char offset;
-            unsigned char saved_offset;
-            unsigned char targ_scratch;
-            unsigned char max_offset;
-            unsigned char rate;
+       /*
+        * We have to clear the SEQINT *BEFORE* we set the REQINIT handler
+        * active or else VLB and edge triggered EISA cards could loose the
+        * first REQINIT and cause a bus hang/reset cycle.
+        */
+        aic_outb(p, CLRSEQINT, CLRINT);
 
-            if (message_length != MSG_EXT_SDTR_LEN)
-            {
-              outb(SEND_REJ, p->base + RETURN_1);
-              break;
-            }
+       /*      
+        * To actually receive the message, simply turn on
+        * REQINIT interrupts and let our interrupt handler
+        * do the rest (REQINIT should already be true).
+        */
+        p->flags |= AHC_HANDLING_REQINITS;
+        aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
 
-            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);
+       /*
+        * We don't want the sequencer unpaused yet so we return early
+        */
+        return;
+      }
 
-            if (targ_scratch & WIDEXFER)
-              max_offset = MAX_OFFSET_16BIT;
-            else
-              max_offset = MAX_OFFSET_8BIT;
-            offset = MIN(saved_offset, max_offset);
+    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.
+         */
+        unsigned char targ_scratch;
+        unsigned char scb_index;
+        unsigned char last_msg;
 
-            aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+        scb_index = aic_inb(p, SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+        targ_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
+        last_msg = aic_inb(p, LAST_MSG);
 
+        if ( (last_msg == MSG_IDENTIFYFLAG) &&
+             (scb->tag_action != 0 ) &&
+             !(p->flags & AHC_HANDLING_REQINITS) )
+        {
+          if ((scb->tag_action == MSG_ORDERED_Q_TAG) &&
+              (p->dev_flags[scratch_offset] & DEVICE_TAGGED_SUCCESS))
+          {
             /*
-             * Preserve the WideXfer flag.
+             * OK...the device seems able to accept tagged commands, but
+             * not ordered tag commands, only simple tag commands.  So, we
+             * disable ordered tag commands and go on with life just like
+             * normal.
              */
-            targ_scratch = rate | (targ_scratch & WIDEXFER);
-
+            p->orderedtag &= ~target_mask;
+            scb->tag_action = MSG_SIMPLE_Q_TAG;
+            scb->hscb->control &= ~SCB_TAG_TYPE;
+            scb->hscb->control |= MSG_SIMPLE_Q_TAG;
+            aic_outb(p, scb->hscb->control, SCB_CONTROL);
             /*
-             * Update both the target scratch area and current SCSIRATE.
+             * OK..we set the tag type to simple tag command, now we re-assert
+             * ATNO and hope this will take us into the identify phase again
+             * so we can resend the tag type and info to the device.
              */
-            outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
-            outb(targ_scratch, p->base + SCSIRATE);
-
+          }
+          else
+          {
+            unsigned char i, reset = 0;
+            struct aic7xxx_scb *scbp;
+            int old_verbose;
             /*
-             * See if we initiated Sync Negotiation and didn't have
-             * have to fall down to async transfers.
+             * Hmmmm....the device is flaking out on tagged commands.  The
+             * bad thing is that we already have tagged commands enabled in
+             * the device struct in the mid level code.  We also have a queue
+             * set according to the tagged queue depth.  Gonna have to live
+             * with it by controlling our queue depth internally and making
+             * sure we don't set the tagged command flag any more.
              */
-            if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
-            {
-              /* We started it. */
-              if (saved_offset == offset)
-              {
-        	/*
-        	 * 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);
-              }
-            }
-            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);
-            }
+            p->tagenable &= ~target_mask;
+            p->orderedtag &= ~target_mask;
+            p->dev_max_queue_depth[scratch_offset] =
+               p->dev_temp_queue_depth[scratch_offset] = 1;
             /*
-             * Clear the flags.
+             * We set this command up as a bus device reset.  However, we have
+             * to clear the tag type as it's causing us problems.  We shouldnt
+             * have to worry about any other commands being active, since if
+             * the device is refusing tagged commands, this should be the
+             * first tagged command sent to the device, however, we do have
+             * to worry about any other tagged commands that may already be
+             * in the qinfifo.  The easiest way to do this, is to issue a BDR,
+             * send all the commands back to the mid level code, then let them
+             * come back and get rebuilt as untagged commands.
              */
-            p->needsdtr &= ~target_mask;
-            break;
-          }
-
-          case MSG_EXT_WDTR:
-          {
-            unsigned char scratch, bus_width;
-
-            if (message_length != MSG_EXT_WDTR_LEN)
-            {
-              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)
-            {
-              /*
-               * Don't send an WDTR back to the target, since we asked first.
-               */
-              outb(0, p->base + RETURN_1);
-              switch (bus_width)
-              {
-        	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;
-              }
-            }
-            else
+            scb->tag_action = 0;
+            scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
+            scb->hscb->control |= MK_MESSAGE;
+            aic_outb(p,  scb->hscb->control, SCB_CONTROL);
+
+            old_verbose = aic7xxx_verbose;
+            aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
+            for (i=0; i!=p->scb_data->numscbs; i++)
             {
-              int send_reject = FALSE;
-
-              /*
-               * 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. */
-                    send_reject = TRUE;
-        	  }
-        	  break;
-
-        	default:
-        	  break;
-              }
-              if (send_reject)
-              {
-                outb(SEND_REJ, p->base + RETURN_1);
-                printk(KERN_WARNING "scsi%d: Target %d, channel %c, initiating "
-                       "wide negotiation on a narrow bus - rejecting!",
-                       p->host_no, target, channel);
-              }
-              else
+              scbp = p->scb_data->scb_array[i];
+              if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
               {
-                aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
-                outb(SEND_MSG, p->base + RETURN_1);
+                if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
+                {
+                  aic7xxx_reset_device(p, target, channel, lun, i);
+                  reset++;
+                }
               }
             }
-            p->needwdtr &= ~target_mask;
-            outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
-            outb(scratch, p->base + SCSIRATE);
-            break;
-	  }  /* case MSG_EXT_WDTR */
-
-          default:
+            aic7xxx_verbose = old_verbose;
             /*
-             * Unknown extended message - reject it.
+             * Wait until after the for loop to set the busy index since
+             * aic7xxx_reset_device will clear the busy index during its
+             * operation.
              */
-            outb(SEND_REJ, p->base + RETURN_1);
-            break;
-	}  /* switch (message_code) */
-      }  /* case EXTENDED_MSG */
-      break;
-
-    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.
-	 */
-	unsigned char targ_scratch;
-        unsigned char scb_index;
-
-        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 ((scb->flags & SCB_MSGOUT_WDTR) != 0)
-	{
+            aic7xxx_busy_target(p, scb);
+            printk(INFO_LEAD "Device is refusing tagged commands, using "
+              "untagged I/O.\n", p->host_no, channel, target, lun);
+          }
+          aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+        }
+        else if ( (last_msg == MSG_IDENTIFYFLAG) && 
+                  (scb->flags & SCB_MSGOUT_WDTR) )
+        {
           /*
            * 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 ((scb->flags & SCB_MSGOUT_SDTR) != 0)
+          p->needwdtr_copy &= ~target_mask;
+          p->wdtr_pending &= ~target_mask;
+          if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+               (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
           {
-            /*
-             * 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);
+            printk(INFO_LEAD "Refusing WIDE negotiation; using 8 bit "
+              "transfers.\n", p->host_no, CTL_OF_SCB(scb));
+            p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+          }
+          scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+          p->syncinfo[scratch_offset].offset = MAX_OFFSET_8BIT;
+          if (p->needsdtr & target_mask)
+          {
+            p->sdtr_pending |= target_mask;
+            scb->flags |= SCB_MSGOUT_SDTR;
+            aic_outb(p, HOST_MSG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+          }
+        }
+        else if (scb->flags & SCB_MSGOUT_SDTR)
+        {
+         /*
+          * note asynch xfers and clear flag
+          */
+          targ_scratch &= 0xF0;
+          p->needsdtr &= ~target_mask;
+          p->needsdtr_copy &= ~target_mask;
+          p->sdtr_pending &= ~target_mask;
+          p->syncinfo[scratch_offset].period = 0;
+          p->syncinfo[scratch_offset].offset = 0;
+          if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+               (p->dev_flags[scratch_offset] & DEVICE_PRINT_SDTR) )
+          {
+            printk(INFO_LEAD "Refusing synchronous negotiation; using "
+              "asynchronous transfers.\n", p->host_no, CTL_OF_SCB(scb));
+            p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_SDTR;
           }
+        }
+        else if (aic7xxx_verbose & VERBOSE_SEQINT)
+        {
           /*
            * Otherwise, we ignore it.
            */
-	}
-        outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
-        outb(targ_scratch, p->base + SCSIRATE);
+          printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause.  "
+            "Ignoring.\n", p->host_no, channel, target, lun);
+        }
+        aic_outb(p, targ_scratch, TARG_SCRATCH + scratch_offset);
+        aic_outb(p, targ_scratch, SCSIRATE);
       }
       break;
 
     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;
-
-	/*
-	 * 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, flags 0x%x, cmd 0x%lx.\n", p->host_no,
-        	 intstat, scb_index, scb->flags, (unsigned long) scb->cmd);
-	}
-	else
-	{
+        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 = aic_inb(p, SCB_TAG);
+        if (scb_index > p->scb_data->numscbs)
+        {
+          aic_outb(p, 0, RETURN_1);
+          printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
+            p->host_no, channel, target, lun, intstat, scb_index);
+          break;
+        }
+        scb = p->scb_data->scb_array[scb_index];
+        hscb = scb->hscb;
+
+        /*
+         * 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.
+         */
+        aic_outb(p, 0, RETURN_1);
+        if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+        {
+          printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
+            " cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
+            scb_index, scb->flags, (unsigned long) scb->cmd);
+        }
+        else
+        {
           cmd = scb->cmd;
-  	  hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+          hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
           aic7xxx_status(cmd) = hscb->target_status;
 
-          cmd->result |= hscb->target_status;
+          cmd->result = hscb->target_status;
 
           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));
+              if (aic7xxx_verbose & VERBOSE_SEQINT)
+                printk(INFO_LEAD "Interrupted for status of GOOD???\n",
+                  p->host_no, CTL_OF_SCB(scb));
               break;
 
+            case COMMAND_TERMINATED:
             case CHECK_CONDITION:
-              if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+              if ( !(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 = cpu_to_le32(VIRT_TO_BUS(&cmd->sense_buffer));
-        	scb->sg_list[0].length = cpu_to_le32(sizeof(cmd->sense_buffer));
-        	cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+                /*
+                 * XXX - How do we save the residual (if there is one).
+                 */
+                if ( hscb->residual_SG_segment_count != 0 )
+                  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 = 
+                  cpu_to_le32(VIRT_TO_BUS(&cmd->sense_buffer[0]));
+                scb->sg_list[0].length = 
+                  cpu_to_le32(sizeof(cmd->sense_buffer));
 
                 /*
                  * XXX - We should allow disconnection, but can't as it
                  * might allow overlapped tagged commands.
                  */
-  		/* hscb->control &= DISCENB; */
+                  /* hscb->control &= DISCENB; */
                 hscb->control = 0;
-        	hscb->target_status = 0;
-        	hscb->SG_segment_count = 1;
-
-        	addr = VIRT_TO_BUS(&scb->sg_list[0]);
-                hscb->SG_list_pointer = cpu_to_le32(addr);
+                hscb->target_status = 0;
+                hscb->SG_list_pointer = 
+                  cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0]));
                 hscb->data_pointer = scb->sg_list[0].address;
+                hscb->data_count = scb->sg_list[0].length;
+                hscb->SCSI_cmd_pointer = 
+                  cpu_to_le32(VIRT_TO_BUS(&scb->sense_cmd[0]));
+                hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+                hscb->residual_SG_segment_count = 0;
+                hscb->residual_data_count[0] = 0;
+                hscb->residual_data_count[1] = 0;
+                hscb->residual_data_count[2] = 0;
+
+                scb->sg_count = hscb->SG_segment_count = 1;
+                scb->sg_length = sizeof(cmd->sense_buffer);
+                scb->flags &= ~SCB_MSGOUT_BITS;
+                scb->tag_action = 0;
+                /*
+                 * This problem could be caused if the target has lost power
+                 * or found some other way to loose the negotiation settings,
+                 * so if needed, we'll re-negotiate while doing the sense cmd.
+                 * However, if this SCB already was attempting to negotiate,
+                 * then we assume this isn't the problem and skip this part.
+                 */
+                if ( !(scb->flags & SCB_MSGOUT_BITS) )
+                {
+                  if ( p->needwdtr_copy & target_mask )
+                  {
+                    p->needwdtr |= target_mask;
+                    p->wdtr_pending |= target_mask;
+                    hscb->control |= MK_MESSAGE;
+                    scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+                  }
+                  if ( p->needsdtr_copy & target_mask )
+                  {
+                    p->needsdtr |= target_mask;
+                    if ((hscb->control & MK_MESSAGE) == 0)
+                    {
+                      p->sdtr_pending |= target_mask;
+                      hscb->control |= MK_MESSAGE;
+                      scb->flags |= SCB_MSGOUT_SDTR;
+                    }
+                  }
+                }
 
-        	/* Maintain SCB_LINKED_NEXT */
-        	hscb->data_count &= cpu_to_le32(0xFF000000);
-  		hscb->data_count |= scb->sg_list[0].length;
-
-        	addr = VIRT_TO_BUS(scb->sense_cmd);
-                hscb->SCSI_cmd_pointer = cpu_to_le32(addr);
-        	hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
-
-                scb->sg_count = hscb->SG_segment_count;
-        	scb->flags |= SCB_SENSE;
+                scb->flags |= SCB_SENSE;
                 /*
                  * Ensure the target is busy since this will be an
                  * an untagged request.
                  */
-                aic7xxx_busy_target(p, target, channel, hscb->tag);
-        	outb(SEND_SENSE, p->base + RETURN_1);
+                aic7xxx_busy_target(p, scb);
+                aic_outb(p, SEND_SENSE, RETURN_1);
+                aic7xxx_error(cmd) = DID_OK;
+                break;
               }  /* first time sense, no errors */
-  	      else
-  	      {
-        	if (aic7xxx_error(cmd) == 0)
-  		{
-        	  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-  		}
-  	      }
+              aic7xxx_error(cmd) = DID_OK;
+              scb->flags &= ~SCB_SENSE;
               break;
 
             case QUEUE_FULL:
-#ifdef NOT_YET
-              if (scb->hscb->control & TAG_ENB)
+              queue_flag = TRUE;    /* Mark that this is a QUEUE_FULL and */
+            case BUSY:              /* drop through to here */
+            {
+              struct aic7xxx_scb *next_scbp, *prev_scbp;
+              unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
+              /*
+               * We have to look three places for queued commands:
+               *  1: QINFIFO
+               *  2: p->waiting_scbs queue
+               *  3: WAITING_SCBS list on card (for commands that are started
+               *     but haven't yet made it to the device)
+               */
+              aic7xxx_search_qinfifo(p, target, channel, lun,
+                SCB_LIST_NULL, 0, TRUE,
+                &p->delayed_scbs[scratch_offset]);
+              next_scbp = p->waiting_scbs.head;
+              while ( next_scbp != NULL )
               {
-        	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;
+                prev_scbp = next_scbp;
+                next_scbp = next_scbp->q_next;
+                if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
+                     SCB_LIST_NULL) )
+                {
+                  scbq_remove(&p->waiting_scbs, prev_scbp);
+                  scbq_insert_tail(&p->delayed_scbs[scratch_offset],
+                    prev_scbp);
+                }
               }
-#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))
+              next_scbp = NULL;
+              active_hscb = aic_inb(p, SCBPTR);
+              prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
+              next_hscb = aic_inb(p, WAITING_SCBH);
+              while (next_hscb != SCB_LIST_NULL)
+              {
+                aic_outb(p, next_hscb, SCBPTR);
+                scb_index = aic_inb(p, SCB_TAG);
+                next_scbp = p->scb_data->scb_array[scb_index];
+                if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
+                    SCB_LIST_NULL) )
+                {
+                  scbq_insert_head(&p->delayed_scbs[scratch_offset],
+                    next_scbp);
+                  next_scbp->flags |= SCB_WAITINGQ;
+                  p->dev_active_cmds[scratch_offset]--;
+                  p->activescbs--;
+                  next_hscb = aic_inb(p, SCB_NEXT);
+                  aic_outb(p, 0, SCB_CONTROL);
+                  aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+                  aic7xxx_add_curscb_to_free_list(p);
+                  if (prev_hscb == SCB_LIST_NULL)
+                  {
+                    aic_outb(p, 0, SCSISEQ);    /* We were first on the list,
+                                                 * so we kill the selection
+                                                 * hardware.  Let the sequencer
+                                                 * re-init the hardware itself
+                                                 */
+                    aic_outb(p, next_hscb, WAITING_SCBH);
+                  }
+                  else
+                  {
+                    aic_outb(p, prev_hscb, SCBPTR);
+                    aic_outb(p, next_hscb, SCB_NEXT);
+                  }
+                }
+                else
+                {
+                  prev_hscb = next_hscb;
+                  next_hscb = aic_inb(p, SCB_NEXT);
+                }
+              }
+              aic_outb(p, active_hscb, SCBPTR);
+              scbq_insert_head(&p->delayed_scbs[scratch_offset], scb);
+              p->dev_active_cmds[scratch_offset]--;
+              p->activescbs--;
+              scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
+                  
+              if (p->dev_timer[scratch_offset].expires == 0) 
               {
-  		/*
-        	 * 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);
+                if ( p->dev_active_cmds[scratch_offset] )
+                {
+                  p->dev_timer[scratch_offset].expires = jiffies + (HZ * 2);
+                  add_timer(&p->dev_timer[scratch_offset]);
+                }
+                else
+                {
+                  p->dev_timer[scratch_offset].expires = jiffies + (HZ / 2);
+                  add_timer(&p->dev_timer[scratch_offset]);
+                }
               }
-              udelay(1000);  /*  A small pause (1ms) to help the drive */
-              break;
+              if (aic7xxx_verbose & VERBOSE_QUEUE_FULL)
+              {
+                if (queue_flag)
+                  printk(INFO_LEAD "Queue full received; queue depth %d, "
+                    "active %d\n", p->host_no, CTL_OF_SCB(scb),
+                    p->dev_max_queue_depth[scratch_offset],
+                    p->dev_active_cmds[scratch_offset]);
+                else
+                  printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
 
+              }
+              if (queue_flag)
+              {
+                p->dev_temp_queue_depth[scratch_offset] = 
+                  p->dev_active_cmds[scratch_offset];
+                if ( p->dev_last_queue_full[scratch_offset] !=
+                     p->dev_active_cmds[scratch_offset] )
+                {
+                  p->dev_last_queue_full[scratch_offset] = 
+                      p->dev_active_cmds[scratch_offset];
+                  p->dev_last_queue_full_count[scratch_offset] = 0;
+                }
+                else
+                {
+                  p->dev_last_queue_full_count[scratch_offset]++;
+                }
+                if ( (p->dev_last_queue_full_count[scratch_offset] > 14) &&
+                     (p->dev_active_cmds[scratch_offset] > 4) )
+                {
+                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
+                    printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
+                      CTL_OF_SCB(scb), p->dev_active_cmds[scratch_offset]);
+                  p->dev_max_queue_depth[scratch_offset] = 
+                      p->dev_active_cmds[scratch_offset];
+                  p->dev_last_queue_full[scratch_offset] = 0;
+                  p->dev_last_queue_full_count[scratch_offset] = 0;
+                }
+              }
+              break;
+            }
+            
             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_verbose & VERBOSE_SEQINT)
+                printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
+                     CTL_OF_SCB(scb), scb->hscb->target_status);
               if (!aic7xxx_error(cmd))
               {
-        	aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+                aic7xxx_error(cmd) = DID_RETRY_COMMAND;
               }
               break;
           }  /* end switch */
-	}  /* end else of */
+        }  /* end else of */
       }
       break;
 
     case AWAITING_MSG:
       {
-	unsigned char scb_index;
-        unsigned char message_offset;
+        unsigned char scb_index;
 
-	scb_index = inb(p->base + SCB_TAG);
-	scb = p->scb_data->scb_array[scb_index];
+        scb_index = aic_inb(p, SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+        p->msg_index = p->msg_len = 0;
+        /*
+         * 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.
+         */
 
-	/*
-	 * 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)
-	{
-          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));
-	}
+        if (scb->flags & SCB_DEVICE_RESET)
+        {
+          p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
+          p->msg_len++;
+          if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+            printk(INFO_LEAD "Bus device reset mailed.\n",
+                 p->host_no, CTL_OF_SCB(scb));
+        }
         else if (scb->flags & SCB_ABORT)
         {
-          if ((scb->hscb->control & TAG_ENB) != 0)
+          if (scb->hscb->control & TAG_ENB)
           {
-            outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+            if (aic_inb(p, MSG_OUT) == MSG_IDENTIFYFLAG)
+            {
+              p->msg_buf[p->msg_index++] = scb->tag_action;
+              p->msg_buf[p->msg_index++] = scb->hscb->tag;
+              p->msg_len += 2;
+            }
+            p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
           }
           else
           {
-            outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+            p->msg_buf[p->msg_index++] = MSG_ABORT;
           }
-          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)
-	{
-          aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+          p->msg_len++;
+          if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+            printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
+              CTL_OF_SCB(scb));
+        }
+        else if (scb->flags & SCB_MSGOUT_WDTR)
+        {
+          aic7xxx_construct_wdtr(p, (scb->flags & SCB_WDTR_16BIT));
         }
         else if (scb->flags & SCB_MSGOUT_SDTR)
         {
-          unsigned char target_scratch;
-          unsigned short ultra_enable;
-          int i, sxfr;
+          unsigned char period, offset;
 
           /*
            * Pull the user defined setting from scratch RAM.
            */
-          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++)
+          period = p->syncinfo[scratch_offset].period;
+          offset = p->syncinfo[scratch_offset].offset;
+          if ( (p->needsdtr_copy & target_mask) == 0)
           {
-            if (sxfr == aic7xxx_syncrates[i].rate)
-            break;
+            period = 0;
+            offset = 0;
           }
-          aic7xxx_construct_sdtr(p, message_offset,
-                                 aic7xxx_syncrates[i].period,
-                                 target_scratch & WIDEXFER ?
-                                 MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
+          aic7xxx_construct_sdtr(p, period, offset);
         }
         else 
         {
           panic("aic7xxx: AWAITING_MSG for an SCB that does "
-                "not have a waiting message.");
-	}
+                "not have a waiting message.\n");
+        }
+        /*
+         * We've set everything up to send our message, now to actually do
+         * so we need to enable reqinit interrupts and let the interrupt
+         * handler do the rest.  We don't want to unpause the sequencer yet
+         * though so we'll return early.  We also have to make sure that
+         * we clear the SEQINT *BEFORE* we set the REQINIT handler active
+         * or else it's possible on VLB cards to loose the first REQINIT
+         * interrupt.  Edge triggered EISA cards could also loose this
+         * interrupt, although PCI and level triggered cards should not
+         * have this problem since they continually interrupt the kernel
+         * until we take care of the situation.
+         */
+        aic_outb(p, CLRSEQINT, CLRINT);
+        p->msg_index = 0;
+        p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+        p->flags |= AHC_HANDLING_REQINITS;
+        aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+        return;
       }
       break;
 
     case DATA_OVERRUN:
       {
-	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++)
+        unsigned char scb_index = aic_inb(p, SCB_TAG);
+        unsigned char lastphase = aic_inb(p, LASTPHASE);
+        unsigned int i;
+
+        scb = (p->scb_data->scb_array[scb_index]);
+        /*
+         * 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.
+         *    If we retrieved sense info on this target, then the 
+         *    base SENSE info should have been saved prior to the
+         *    overrun error.  In that case, we return DID_OK and let
+         *    the mid level code pick up on the sense info.  Otherwise
+         *    we return DID_ERROR so the command will get retried.
+         */
+        if ( !(scb->flags & SCB_SENSE) )
         {
-          printk(KERN_INFO "     sg[%d] - Addr 0x%x : Length %d\n",
-                 i,
+          printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
+            p->host_no, CTL_OF_SCB(scb), 
+            (lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
+          printk(KERN_WARNING "  %s seen Data Phase. Length=%d, NumSGs=%d.\n",
+            (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
+            scb->sg_length, scb->sg_count);
+          for (i = 0; i < scb->sg_count; i++)
+          {
+            printk(KERN_WARNING "     sg[%d] - Addr 0x%x : Length %d\n",
+                 i, 
                  le32_to_cpu(scb->sg_list[i].address),
-                 le32_to_cpu(scb->sg_list[i].length));
+                 le32_to_cpu(scb->sg_list[i].length) );
+          }
+          aic7xxx_error(scb->cmd) = DID_ERROR;
         }
-	/*
-	 * 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;
+        else
+          printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
+            p->host_no, CTL_OF_SCB(scb));
+      }
+      break;
+
+    case TRACEPOINT:
+      {
+        printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no, channel,
+          target, lun);
+      }
+      break;
+
+    case TRACEPOINT2:
+      {
+        printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no, channel,
+          target, lun);
       }
       break;
 
-/* #if AIC7XXX_NOT_YET */
+#if AIC7XXX_NOT_YET 
     /* XXX Fill these in later */
     case MSG_BUFFER_BUSY:
       printk("aic7xxx: Message buffer busy.\n");
@@ -3477,33 +4137,376 @@
     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;
+#endif
 
-    default:		   /* unknown */
-      printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
-             p->host_no, intstat, inb(p->base + SCSISIGI));
+    default:                   /* unknown */
+      printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+             p->host_no, channel, target, lun, intstat,
+             aic_inb(p, SCSISIGI));
       break;
   }
 
   /*
    * Clear the sequencer interrupt and unpause the sequencer.
    */
-  outb(CLRSEQINT, p->base + CLRINT);
+  aic_outb(p, CLRSEQINT, CLRINT);
   unpause_sequencer(p, /* unpause always */ TRUE);
 }
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_parse_msg
+ *
+ * Description:
+ *   Parses incoming messages into actions on behalf of
+ *   aic7xxx_handle_reqinit
+ *_F*************************************************************************/
+static int
+aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int reject, done;
+  unsigned char target_scratch, scratch_offset;
+  unsigned short target_mask;
+
+  reject = done = FALSE;
+  scratch_offset = TARGET_INDEX(scb->cmd);
+  target_scratch = aic_inb(p, TARG_SCRATCH + scratch_offset);
+  target_mask = (0x01 << scratch_offset);
+
+  /*
+   * Parse as much of the message as is availible,
+   * rejecting it if we don't support it.  When
+   * the entire message is availible and has been
+   * handled, return TRUE indicating that we have
+   * parsed an entire message.
+   */
+
+  if (p->msg_buf[0] != MSG_EXTENDED)
+  {
+    reject = TRUE;
+  }
+
+  /*
+   * Just accept the length byte outright and perform
+   * more checking once we know the message type.
+   */
+
+  if ( !reject && (p->msg_len > 2) )
+  {
+    switch(p->msg_buf[2])
+    {
+      case MSG_EXT_SDTR:
+      {
+        unsigned char period, response_period, offset;
+        unsigned char max_offset, saved_offset, rate;
+
+        if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
+        {
+          reject = TRUE;
+          break;
+        }
+
+        if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
+        {
+          break;
+        }
+
+        period = p->msg_buf[3];
+        saved_offset = p->msg_buf[4];
+
+        if (target_scratch & WIDEXFER)
+        {
+          max_offset = MAX_OFFSET_16BIT;
+        }
+        else
+        {
+          max_offset = MAX_OFFSET_8BIT;
+        }
+        offset = MIN(saved_offset, max_offset);
+        response_period = aic7xxx_scsirate(p, &rate, &period,
+          &offset, scb->cmd->target, scb->cmd->channel, /* set */ TRUE);
+        /* Preserve the WideXfer flag */
+        target_scratch = rate | (target_scratch & WIDEXFER);
+
+        /*
+         * Update the TARGET_SCRATCH, the SCSIRATE, and our syncinfo
+         * areas.
+         */
+        aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
+        aic_outb(p, target_scratch, SCSIRATE);
+        p->syncinfo[scratch_offset].period = response_period;
+        p->syncinfo[scratch_offset].offset = offset;
+
+        /*
+         * Did we start this, if not, or if we went to low and had to
+         * go async, then send an SDTR back to the target
+         */
+        p->needsdtr &= ~target_mask;
+        if (scb->flags & SCB_MSGOUT_SDTR)
+        {
+          if (saved_offset != offset)
+          {
+            p->needsdtr_copy &= ~target_mask;
+            reject = TRUE; 
+          }
+          scb->flags &= ~SCB_MSGOUT_SDTR;
+          p->sdtr_pending &= ~target_mask;
+        }
+        else
+        {
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_SDTR;
+          p->sdtr_pending |= target_mask;
+          aic_outb(p, HOST_MSG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+        }
+        done = TRUE;
+        break;
+      }
+      case MSG_EXT_WDTR:
+      {
+        unsigned char bus_width;
+          
+        if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
+        {
+          reject = TRUE;
+          break;
+        }
+
+        if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
+        {
+          break;
+        }
+
+        bus_width = p->msg_buf[3];
+        p->needwdtr &= ~target_mask;
+        if (scb->flags & SCB_MSGOUT_WDTR)
+        {
+          switch(bus_width)
+          {
+            default:
+            {
+              reject = TRUE;
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+              {
+                printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+                  p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+              }
+            } /* We fall through on purpose */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              p->needwdtr_copy &= ~target_mask;
+              target_scratch &= 0x7f;
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+              {
+                printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
+                  p->host_no, CTL_OF_SCB(scb));
+                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+              }
+              break;
+            }
+            case MSG_EXT_WDTR_BUS_16_BIT:
+            {
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+              {
+                printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
+                  p->host_no, CTL_OF_SCB(scb));
+                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+              }
+              target_scratch |= WIDEXFER;
+              break;
+            }
+          }
+          scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+          p->wdtr_pending &= ~target_mask;
+        }
+        else
+        {
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          switch(bus_width)
+          {
+            default:
+            {
+              if (p->type & AHC_WIDE)
+              {
+                bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+                p->needwdtr_copy |= target_mask;
+                scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+                target_scratch |= WIDEXFER;
+                if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+                     (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+                {
+                  printk(INFO_LEAD "Using wide (16 bit) transfers.\n",
+                    p->host_no, CTL_OF_SCB(scb));
+                  p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+                }
+                break;
+              }
+            } /* Fall through if we aren't a wide card */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              p->needwdtr_copy &= ~target_mask;
+              scb->flags |= SCB_MSGOUT_WDTR_8BIT;
+              target_scratch &= 0x7f;
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+                   (p->dev_flags[scratch_offset] & DEVICE_PRINT_WDTR) )
+              {
+                printk(INFO_LEAD "Using narrow (8 bit) transfers.\n",
+                  p->host_no, CTL_OF_SCB(scb));
+                p->dev_flags[scratch_offset] &= ~DEVICE_PRINT_WDTR;
+              }
+              break;
+            }
+          }
+          aic_outb(p, HOST_MSG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+          p->wdtr_pending |= target_mask;
+        }
+        aic_outb(p, target_scratch, SCSIRATE);
+        aic_outb(p, target_scratch, TARG_SCRATCH + scratch_offset);
+        p->syncinfo[scratch_offset].offset =
+          (bus_width == MSG_EXT_WDTR_BUS_8_BIT) ? 
+          MAX_OFFSET_8BIT : MAX_OFFSET_16BIT;
+        if ( !(p->wdtr_pending & target_mask) && !reject)
+        {
+          /*
+           * We've successfully completed the wide negotiation, so let's start
+           * up the sync negotiation now.
+           */
+          scb->flags &= ~SCB_MSGOUT_WDTR_16BIT;
+          if ((p->needsdtr & target_mask) && !(p->sdtr_pending & target_mask))
+          {
+            p->sdtr_pending |= target_mask;
+            scb->flags |= SCB_MSGOUT_SDTR;
+            aic_outb(p, HOST_MSG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+          }
+        }
+        done = TRUE;
+        break;
+      }
+      default:
+      {
+        reject = TRUE;
+        break;
+      }
+    } /* end of switch(p->msg_type) */
+  } /* end of if (!reject && (p->msg_len > 2)) */
+
+  if (reject)
+  {
+    aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
+    aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+  }
+  return(done);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_reqinit
+ *
+ * Description:
+ *   Interrupt handler for REQINIT interrupts (used to transfer messages to
+ *    and from devices).
+ *_F*************************************************************************/
+static void
+aic7xxx_handle_reqinit(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  unsigned char lastbyte;
+  unsigned char phasemis;
+  int done;
+
+  switch(p->msg_type)
+  {
+    case MSG_TYPE_INITIATOR_MSGOUT:
+      {
+        if (p->msg_len == 0)
+          panic("aic7xxx: REQINIT with no active message!\n");
+
+        lastbyte = (p->msg_index == (p->msg_len - 1));
+        phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK) != P_MESGOUT;
+
+        if (lastbyte || phasemis)
+        {
+          /* Time to end the message */
+          p->msg_len = 0;
+          p->msg_type = MSG_TYPE_NONE;
+          /*
+           * NOTE-TO-MYSELF: If you clear the REQINIT after you
+           * disable REQINITs, then cases of REJECT_MSG stop working
+           * and hang the bus
+           */
+          aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          p->flags &= ~AHC_HANDLING_REQINITS;
+
+          if (phasemis == 0)
+          {
+            aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
+            aic_outb(p, 0, RETURN_1);
+          }
+          else
+          {
+            aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
+          }
+          unpause_sequencer(p, TRUE);
+        }
+        else
+        {
+          /*
+           * Present the byte on the bus (clearing REQINIT) but don't
+           * unpause the sequencer.
+           */
+          aic_outb(p, CLRREQINIT, CLRSINT1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          aic_outb(p,  p->msg_buf[p->msg_index++], SCSIDATL);
+        }
+        break;
+      }
+    case MSG_TYPE_INITIATOR_MSGIN:
+      {
+        phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK ) != P_MESGIN;
+
+        if (phasemis == 0)
+        {
+          p->msg_len++;
+          /* Pull the byte in without acking it */
+          p->msg_buf[p->msg_index] = aic_inb(p, SCSIBUSL);
+          done = aic7xxx_parse_msg(p, scb);
+          /* Ack the byte */
+          aic_outb(p, CLRREQINIT, CLRSINT1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          aic_inb(p, SCSIDATL);
+          p->msg_index++;
+        }
+        if (phasemis || done)
+        {
+          /* Time to end our message session */
+          p->msg_len = 0;
+          p->msg_type = MSG_TYPE_NONE;
+          p->flags &= ~AHC_HANDLING_REQINITS;
+          aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          unpause_sequencer(p, TRUE);
+        }
+        break;
+      }
+    default:
+      {
+        panic("aic7xxx: Unknown REQINIT message type.\n");
+        break;
+      }
+  } /* End of switch(p->msg_type) */
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_handle_scsiint
  *
  * Description:
@@ -3516,8 +4519,8 @@
   unsigned char status;
   struct aic7xxx_scb *scb;
 
-  scb_index = inb(p->base + SCB_TAG);
-  status = inb(p->base + SSTAT1);
+  scb_index = aic_inb(p, SCB_TAG);
+  status = aic_inb(p, SSTAT1);
 
   if (scb_index < p->scb_data->numscbs)
   {
@@ -3532,14 +4535,35 @@
     scb = NULL;
   }
 
+
+  if ( (p->flags & AHC_HANDLING_REQINITS) && (status & REQINIT) )
+  {
+    if (scb)
+    {
+      aic7xxx_handle_reqinit(p, scb);
+    }
+    else
+    {
+      p->flags &= ~AHC_HANDLING_REQINITS;
+      aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+      aic_outb(p, CLRREQINIT, CLRSINT1);
+      aic_outb(p, CLRSCSIINT, CLRINT);
+      p->msg_type = MSG_TYPE_NONE;
+      p->msg_index = 0;
+      p->msg_len = 0;
+    }
+    return;
+  }
+
   if ((status & SCSIRSTI) != 0)
   {
-    char channel;
+    int channel;
 
-    channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+    channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
 
-    printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
-           p->host_no, channel);
+    if (aic7xxx_verbose & VERBOSE_RESET)
+      printk(WARN_LEAD "Someone else reset the channel!!\n",
+           p->host_no, channel, -1, -1);
     /*
      * Go through and abort all commands for the channel, but do not
      * reset the channel again.
@@ -3554,35 +4578,27 @@
      * 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';
+    unsigned char lastphase = aic_inb(p, LASTPHASE);
+    unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
+    unsigned char target = (saved_tcl >> 4) & 0x0F;
+    int channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
     int printerror = TRUE;
 
-    outb(0, p->base + SCSISEQ);
+    aic_outb(p, 0, SCSISEQ);
     if (lastphase == P_MESGOUT)
     {
-      unsigned char sindex;
       unsigned char message;
 
-      sindex = inb(p->base + SINDEX);
-      message = inb(p->base + sindex - 1);
+      message = aic_inb(p, SINDEX);
 
-      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)
+      if ((message == MSG_ABORT) || (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);
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "SCB %d abort delivered.\n", p->host_no,
+            CTL_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS,
+                (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
+        aic7xxx_run_done_queue(p, FALSE);
         scb = NULL;
         printerror = 0;
       }
@@ -3607,20 +4623,26 @@
         {
           tag = SCB_LIST_NULL;
         }
-        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
+        aic7xxx_run_done_queue(p, FALSE);
       }
       else
-      {
-        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+      {  /* Since we don't really know what happened here, we'll wait */
+         /* for the commands to timeout and get aborted if need be    */
+        aic7xxx_add_curscb_to_free_list(p);
       }
-      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);
+      printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
+             "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
+             (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+      scb = NULL;
+    }
+    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+      SIMODE1);
+    p->flags &= ~AHC_HANDLING_REQINITS;
+    aic_outb(p, CLRBUSFREE | CLRREQINIT, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
     restart_sequencer(p);
+    unpause_sequencer(p, TRUE);
   }
   else if ((status & SELTO) != 0)
   {
@@ -3628,9 +4650,18 @@
     unsigned char nextscb;
     Scsi_Cmnd *cmd;
 
-    scbptr = inb(p->base + WAITING_SCBH);
-    outb(scbptr, p->base + SCBPTR);
-    scb_index = inb(p->base + SCB_TAG);
+    scbptr = aic_inb(p, WAITING_SCBH);
+    if (scbptr >= p->scb_data->maxhscbs)
+    {
+      scb_index = SCB_LIST_NULL;
+      printk(WARN_LEAD "Bad scbptr %d during SELTO.\n", 
+        p->host_no, -1, -1, -1, scbptr);
+    }
+    else
+    {
+      aic_outb(p, scbptr, SCBPTR);
+      scb_index = aic_inb(p, SCB_TAG);
+    }
 
     scb = NULL;
     if (scb_index < p->scb_data->numscbs)
@@ -3643,63 +4674,80 @@
     }
     if (scb == NULL)
     {
-      printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
-             p->host_no, scb_index);
+      printk(WARN_LEAD "Referenced SCB %d not valid during SELTO.\n",
+             p->host_no, -1, -1, -1, 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));
+             "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
+             aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+             aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
     }
     else
     {
-      /*
-       * XXX - If we queued an abort tag, go clean up the disconnected list.
-       */
       cmd = scb->cmd;
       cmd->result = (DID_TIME_OUT << 16);
 
       /*
-       * Clear an pending messages for the timed out
-       * target and mark the target as free.
+       * Clear out this hardware SCB
+       */
+      aic_outb(p, 0, SCB_CONTROL);
+
+      /*
+       * Clear out a few values in the card that are in an undetermined
+       * state.
        */
-      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);
+      aic_outb(p, MSG_NOOP, MSG_OUT);
 
       /*
        * Shift the waiting for selection queue forward
        */
-      nextscb = inb(p->base + SCB_NEXT);
-      outb(nextscb, p->base + WAITING_SCBH);
+      nextscb = aic_inb(p, SCB_NEXT);
+      aic_outb(p, nextscb, WAITING_SCBH);
 
       /*
        * Put this SCB back on the free list.
        */
       aic7xxx_add_curscb_to_free_list(p);
+      /*
+       * XXX - If we queued an abort tag, go clean up the disconnected list.
+       * We know that this particular SCB had to be the queued abort since
+       * the disconnected SCB would have gotten a reconnect instead.
+       * However, if this is an abort command, then DID_TIMEOUT isn't
+       * appropriate, neither is returning the command for that matter.
+       * What we need to do then is to let the command timeout again so
+       * we get a reset since this abort just failed.
+       */
+      if (p->flags & SCB_QUEUED_ABORT)
+      {
+        cmd->result = 0;
+        scb->flags &= ~SCB_QUEUED_ABORT;
+        scb = NULL;
+      }
     }
     /*
      * Stop the selection.
      */
-    outb(0, p->base + SCSISEQ);
-    outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
-    outb(CLRSCSIINT, p->base + CLRINT);
+    aic_outb(p, 0, SCSISEQ);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+    p->flags &= ~AHC_HANDLING_REQINITS;
+    aic_outb(p, CLRSELTIMEO | CLRBUSFREE | CLRREQINIT, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
     restart_sequencer(p);
+    unpause_sequencer(p, TRUE);
   }
   else if (scb == NULL)
   {
-    printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+    printk(WARN_LEAD "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));
+           p->host_no, -1, -1, -1, status, scb_index, aic_inb(p, SIMODE0),
+           aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
+           (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, 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);
+    aic_outb(p, status, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
     unpause_sequencer(p, /* unpause always */ TRUE);
     scb = NULL;
   }
@@ -3711,7 +4759,7 @@
     char  *phase;
     Scsi_Cmnd *cmd;
     unsigned char mesg_out = MSG_NOOP;
-    unsigned char lastphase = inb(p->base + LASTPHASE);
+    unsigned char lastphase = aic_inb(p, LASTPHASE);
 
     cmd = scb->cmd;
     switch (lastphase)
@@ -3746,8 +4794,8 @@
      * 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);
+    printk(WARN_LEAD "Parity error during phase %s.\n",
+           p->host_no, CTL_OF_SCB(scb), phase);
 
     /*
      * We've set the hardware to assert ATN if we get a parity
@@ -3757,19 +4805,11 @@
      */
     if (mesg_out != MSG_NOOP)
     {
-      outb(mesg_out, p->base + MSG_OUT);
-      outb(1, p->base + MSG_LEN);
+      aic_outb(p, mesg_out, MSG_OUT);
       scb = NULL;
     }
-    else
-    {
-      /*
-       * 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);
+    aic_outb(p, CLRSCSIPERR, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
     unpause_sequencer(p, /* unpause_always */ TRUE);
   }
   else
@@ -3778,19 +4818,87 @@
      * 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);
+    if (aic7xxx_verbose & VERBOSE_SCSIINT)
+      printk(INFO_LEAD "Unknown SCSIINT status, SSTAT1(0x%x).\n",
+        p->host_no, -1, -1, -1, status);
+    aic_outb(p, status, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
     unpause_sequencer(p, /* unpause always */ TRUE);
     scb = NULL;
   }
   if (scb != NULL)
   {
     aic7xxx_done(p, scb);
-    aic7xxx_done_cmds_complete(p);
   }
 }
 
+#ifdef CONFIG_PCI
+
+#define  DPE 0x80
+#define  SSE 0x40
+#define  RMA 0x20
+#define  RTA 0x10
+#define  STA 0x08
+#define  DPR 0x01
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_pci_intr
+ *
+ * Description:
+ *   Check the scsi card for PCI errors and clear the interrupt
+ *
+ *   NOTE: If you don't have this function and a 2940 card encounters
+ *         a PCI error condition, the machine will end up locked as the
+ *         interrupt handler gets slammed with non-stop PCI error interrupts
+ *-F*************************************************************************/
+static void
+aic7xxx_pci_intr(struct aic7xxx_host *p)
+{
+  unsigned char status1;
+  int error;
+
+  error = 0;
+  error = pcibios_read_config_byte(p->pci_bus, p->pci_device_fn,
+                                            PCI_STATUS, &status1);
+
+  if (error == 0)
+  {
+    if (status1 & DPE)
+      printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+        "phase.\n", p->host_no, -1, -1, -1);
+    if (status1 & SSE)
+      printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+        -1, -1, -1);
+    if (status1 & RMA)
+      printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+        -1, -1, -1);
+    if (status1 & RTA)
+      printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+        -1, -1, -1);
+    if (status1 & STA)
+      printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+        -1, -1, -1);
+    if (status1 & DPR)
+      printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+        "PERR#\n", p->host_no, -1, -1, -1);
+  }
+  else
+  {
+    printk(WARN_LEAD "Error reading PCI config register during PCI ERROR"
+      "interrupt.\n", p->host_no, -1, -1, -1);
+    aic_outb(p,  CLRPARERR, CLRINT);
+    return;
+  }
+  
+  pcibios_write_config_byte(p->pci_bus, p->pci_device_fn,
+                                            PCI_STATUS, status1);
+  if (status1 & (DPR|RMA|RTA))
+    aic_outb(p,  CLRPARERR, CLRINT);
+
+}
+#endif
+
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_isr
@@ -3801,169 +4909,138 @@
 static void
 aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
 {
-  struct aic7xxx_host *p = (struct aic7xxx_host *) dev_id;
+  struct aic7xxx_host *p;
   unsigned char intstat;
-  unsigned long flags;
 
-  /*
-   * Handle all the interrupt sources - especially for SCSI
-   * interrupts, we won't get a second chance at them.
-   */
-  intstat = inb(p->base + INTSTAT);
-  if (! (intstat & INT_PEND))	/* Interrupt for another device */
-    return;
+  p = (struct aic7xxx_host *)dev_id;
 
   /*
-   * Keep track of interrupts for /proc/scsi
+   * Just a few sanity checks.  Make sure p != NULL, that we have an
+   * interrupt pending, and that we aren't already in our int handler.
+   * Also, if PCI, then we are going to check for a PCI bus error status
+   * should we get too many spurious interrupts.
    */
-  p->isr_count++;
-
-  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+  if (p == NULL)
   {
-    /*
-     * 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)
-    {
-      /* Try clearing all interrupts. */
-      outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
-    }
+    printk(KERN_WARNING "aic7xxx: ISR routine called with NULL dev_id\n");
     return;
   }
-
-  if (p->flags & IN_ISR)
+  else if (!(aic_inb(p, INTSTAT) & INT_PEND))
+  {
+#ifdef CONFIG_PCI
+    if ((p->type & AHC_AIC78x0) && (p->spurious_int > 500))
+    {
+      if ( aic_inb(p, ERROR) & PCIERRSTAT )
+      {
+        aic7xxx_pci_intr(p);
+      }
+      p->spurious_int = 0;
+    }
+    else
+    {
+      p->spurious_int++;
+    }
+#endif
+    return;
+  }
+  else if (p->flags & AHC_IN_ISR)
   {
-    printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
-           p->host_no);
     return;
   }
 
   /*
-   * Indicate that we're in the interrupt handler.
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
    */
-  save_flags(flags);
-  cli();
-  p->flags |= IN_ISR;
+  intstat = aic_inb(p, INTSTAT);
+  p->spurious_int = 0;
 
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
+  p->flags |= AHC_IN_ISR;
+
+  /*
+   * Indicate that we're in the interrupt handler.
+   */
   if (intstat & CMDCMPLT)
   {
     struct aic7xxx_scb *scb = NULL;
     Scsi_Cmnd *cmd;
-    unsigned char qoutcnt;
     unsigned char scb_index;
-    int i, interrupts_cleared = 0;
 
     /*
+     * Clear interrupt status before running the completion loop.
+     * This eliminates a race condition whereby a command could
+     * complete between the last check of qoutfifo and the
+     * CLRCMDINT statement.  This would result in us thinking the
+     * qoutfifo was empty when it wasn't, and in actuality be a lost
+     * completion interrupt.  With multiple devices or tagged queueing
+     * this could be very bad if we caught all but the last completion
+     * and no more are imediately sent.
+     */
+    aic_outb(p, CLRCMDINT, CLRINT);
+    /*
      * The sequencer will continue running when it
      * issues this interrupt. There may be >1 commands
      * finished, so loop until we've processed them all.
      */
-    qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
 
-#if 1
-  if (qoutcnt >= p->qfullcount - 1)
-    printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
-           "qoutcnt = %d.\n", qoutcnt);
-#endif
-    while (qoutcnt > 0)
+    while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL)
     {
-      if ((p->flags & PAGE_ENABLED) != 0)
+      scb_index = p->qoutfifo[p->qoutfifonext];
+      p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL;
+      if ( scb_index >= p->scb_data->numscbs )
+        scb = NULL;
+      else
+        scb = p->scb_data->scb_array[scb_index];
+      if (scb == NULL)
       {
-        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;
-        }
+        printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no,
+          -1, -1, -1, scb_index);
+        continue;
       }
-      for (i = 0; i < qoutcnt; i++)
+      else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
       {
-        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)
+        printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags "
+          "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags,
+          (unsigned long) scb->cmd);
+        continue;
+      }
+      switch (status_byte(scb->hscb->target_status))
+      {
+        case QUEUE_FULL:
+        case BUSY:
+          scb->hscb->target_status = 0;
+          scb->cmd->result = 0;
+          aic7xxx_error(scb->cmd) = DID_OK;
+          break;
+        default:
+          cmd = scb->cmd;
+          if (scb->hscb->residual_SG_segment_count != 0)
           {
-            tag = scb->hscb->tag;
+            aic7xxx_calculate_residual(p, scb);
           }
-          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);
-      }
-      /*
-       * Clear interrupt status before checking the output queue again.
-       * This eliminates a race condition whereby a command could
-       * complete between the queue poll and the interrupt clearing,
-       * so notification of the command being complete never made it
-       * back up to the kernel.
-       */
-      outb(CLRCMDINT, p->base + CLRINT);
-      interrupts_cleared++;
-      qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
-    }
-
-    if (interrupts_cleared == 0)
-    {
-      outb(CLRCMDINT, p->base + CLRINT);
+          cmd->result |= (aic7xxx_error(cmd) << 16);
+          if (scb->tag_action)
+            p->dev_flags[TARGET_INDEX(cmd)] |= 
+               DEVICE_TAGGED_SUCCESS | DEVICE_SUCCESS | DEVICE_PRESENT;
+          else
+            p->dev_flags[TARGET_INDEX(cmd)] |= 
+               DEVICE_SUCCESS | DEVICE_PRESENT;
+          aic7xxx_done(p, scb);
+          break;
+      }      
     }
-
-    aic7xxx_done_cmds_complete(p);
   }
 
   if (intstat & BRKADRINT)
   {
     int i;
-    unsigned char errno = inb(p->base + ERROR);
+    unsigned char errno = aic_inb(p, ERROR);
 
-    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+    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)
@@ -3971,11 +5048,15 @@
         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);
+    printk(KERN_ERR "(scsi%d)   LINE=%d\n", p->host_no,
+      (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
+    aic7xxx_reset_channel(p, 0, TRUE);
+    if ( p->type & AHC_TWIN )
+    {
+      aic7xxx_reset_channel(p, 1, TRUE);
+      restart_sequencer(p);
+    }
+    aic_outb(p, CLRBRKADRINT, CLRINT);
   }
 
   if (intstat & SEQINT)
@@ -3987,14 +5068,37 @@
   {
     aic7xxx_handle_scsiint(p, intstat);
   }
-
-  if (p->waiting_scbs.head != NULL)
+  if(!(p->flags & (AHC_IN_ABORT | AHC_IN_RESET)))
   {
+    aic7xxx_done_cmds_complete(p);
     aic7xxx_run_waiting_queues(p);
   }
+  p->flags &= ~AHC_IN_ISR;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   do_aic7xxx_isr
+ *
+ * Description:
+ *   This is a gross hack to solve a problem in linux kernels 2.1.85 and
+ *   above.  Please, children, do not try this at home, and if you ever see
+ *   anything like it, please inform the Gross Hack Police immediately
+ *-F*************************************************************************/
+static void
+do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+  unsigned long cpu_flags;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,95)
 
-  p->flags &= ~IN_ISR;
-  restore_flags(flags);
+  spin_lock_irqsave(&io_request_lock, cpu_flags);
+  aic7xxx_isr(irq, dev_id, regs);
+  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+#else
+  DRIVER_LOCK
+  aic7xxx_isr(irq, dev_id, regs);
+  DRIVER_UNLOCK
+#endif
 }
 
 /*+F*************************************************************************
@@ -4017,54 +5121,58 @@
 static void
 aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
 {
-  int default_depth = 2;
+  int default_depth = 3;
+  unsigned char tindex;
+  unsigned short target_mask;
+
+  tindex = device->id | (device->channel << 3);
+  target_mask = (1 << tindex);
 
   device->queue_depth = default_depth;
-#ifdef AIC7XXX_TAGGED_QUEUEING
+  p->dev_mid_level_queue_depth[tindex] = 3;
+  p->dev_temp_queue_depth[tindex] = 1;
+  p->dev_max_queue_depth[tindex] = 1;
+  p->tagenable &= ~target_mask;
+
   if (device->tagged_supported)
   {
-    unsigned short target_mask;
     int tag_enabled = TRUE;
 
-    target_mask = (1 << (device->id | (device->channel << 3)));
-
 #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;
-    }
+    default_depth = 8;  /* Not many SCBs to work with. */
 #endif
  
     if (!(p->discenable & target_mask))
     {
-      printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+      if (aic7xxx_verbose & VERBOSE_QUEUE)
+        printk(INFO_LEAD "Disconnection disabled, unable to "
              "enable tagged queueing.\n",
-             p->host_no, device->id, device->channel);
+             p->host_no, device->channel, device->id, device->lun);
     }
     else
     {
-#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
-      device->queue_depth = default_depth;
-#else
       if (p->instance >= NUMBER(aic7xxx_tag_info))
       {
+        static int print_warning = TRUE;
+        if(print_warning)
+        {
+          printk(KERN_INFO "aic7xxx: WARNING, insufficient tag_info instances for"
+                           " installed controllers.\n");
+          printk(KERN_INFO "aic7xxx: Please update the aic7xxx_tag_info array in"
+                           " the aic7xxx.c source file.\n");
+          print_warning = FALSE;
+        }
         device->queue_depth = default_depth;
       }
       else
       {
-        unsigned char  tindex;
 
-        tindex = device->id | (device->channel << 3);
-        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
+        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 255)
         {
           tag_enabled = FALSE;
-          device->queue_depth = 2;  /* Tagged queueing is disabled. */
+          device->queue_depth = 3;  /* Tagged queueing is disabled. */
         }
         else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
         {
@@ -4076,21 +5184,24 @@
             aic7xxx_tag_info[p->instance].tag_commands[tindex];
         }
       }
-#endif
       if ((device->tagged_queue == 0) && tag_enabled)
       {
-        if (aic7xxx_verbose)
+        if (aic7xxx_verbose & VERBOSE_QUEUE)
         {
-    	  printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
-    	         "queue depth %d.\n", p->host_no,
-    	         device->id, device->channel, device->queue_depth);
-        }
+              printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
+                p->host_no, device->channel, device->id,
+                device->lun, device->queue_depth);
+        }
+        p->dev_max_queue_depth[tindex] = device->queue_depth;
+        p->dev_temp_queue_depth[tindex] = device->queue_depth;
+        p->dev_mid_level_queue_depth[tindex] = device->queue_depth;
+        p->tagenable |= target_mask;
+        p->orderedtag |= target_mask;
         device->tagged_queue = 1;
         device->current_tag = SCB_LIST_NULL;
       }
     }
   }
-#endif
 }
 
 /*+F*************************************************************************
@@ -4111,17 +5222,29 @@
 {
   Scsi_Device *device;
   struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+  int scbnum;
 
+  scbnum = 0;
   for (device = scsi_devs; device != NULL; device = device->next)
   {
     if (device->host == host)
     {
       aic7xxx_device_queue_depth(p, device);
+      scbnum += device->queue_depth;
     }
   }
+  while (scbnum > p->scb_data->numscbs)
+  {
+    /*
+     * Pre-allocate the needed SCBs to get around the possibility of having
+     * to allocate some when memory is more or less exhausted and we need
+     * the SCB in order to perform a swap operation (possible deadlock)
+     */
+    if ( aic7xxx_allocate_scb(p, TRUE) == NULL )
+      return;
+  }
 }
 
-#if !defined(__sparc_v9__) && !defined(__powerpc__)
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_probe
@@ -4144,8 +5267,8 @@
  *   The fourth byte's lowest bit seems to be an enabled/disabled
  *   flag (rest of the bits are reserved?).
  *-F*************************************************************************/
-static aha_chip_type
-aic7xxx_probe(int slot, int base, aha_status_type *bios)
+static ahc_type
+aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
 {
   int i;
   unsigned char buf[4];
@@ -4153,13 +5276,13 @@
   static struct {
     int n;
     unsigned char signature[sizeof(buf)];
-    aha_chip_type type;
+    ahc_type type;
     int bios_disabled;
   } AIC7xxx[] = {
-    { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
-    { 4, { 0x04, 0x90, 0x77, 0x70 }, AIC_7770, FALSE }, /* motherboard 7770  */
-    { 4, { 0x04, 0x90, 0x77, 0x56 }, AIC_284x, FALSE }, /* 284x BIOS enabled */
-    { 4, { 0x04, 0x90, 0x77, 0x57 }, AIC_284x, TRUE }   /* 284x BIOS disabled */
+    { 4, { 0x04, 0x90, 0x77, 0x71 }, AHC_274, FALSE }, /* host adapter 274x */
+    { 4, { 0x04, 0x90, 0x77, 0x70 }, AHC_AIC7770, FALSE },  /* mb 7770  */
+    { 4, { 0x04, 0x90, 0x77, 0x56 }, AHC_284, FALSE }, /* 284x BIOS enabled */
+    { 4, { 0x04, 0x90, 0x77, 0x57 }, AHC_284, TRUE }   /* 284x BIOS disabled */
   };
 
   /*
@@ -4183,13 +5306,13 @@
       {
         if (AIC7xxx[i].bios_disabled)
         {
-          *bios = AIC_DISABLED;
+          *flags |= AHC_USEDEFAULTS;
         }
         else
         {
-          *bios = AIC_ENABLED;
+          *flags |= AHC_BIOS_ENABLED;
         }
-	return (AIC7xxx[i].type);
+        return (AIC7xxx[i].type);
       }
 
       printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
@@ -4197,9 +5320,8 @@
     }
   }
 
-  return (AIC_NONE);
+  return (AHC_NONE);
 }
-#endif /* __sparc_v9__ or __powerpc__ */
 
 /*+F*************************************************************************
  * Function:
@@ -4236,11 +5358,11 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0)	\
-  {						\
-    ;  /* Do nothing */				\
-  }						\
-  (void) inb(p->base + SEECTL_2840);
+  while ((aic_inb(p, STATUS_2840) & EEPROM_TF) == 0)        \
+  {                                                \
+    ;  /* Do nothing */                                \
+  }                                                \
+  (void) aic_inb(p, SEECTL_2840);
 
   /*
    * Read the first 32 registers of the seeprom.  For the 2840,
@@ -4253,7 +5375,7 @@
     /*
      * Send chip select for one clock cycle.
      */
-    outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+    aic_outb(p, CK_2840 | CS_2840, SEECTL_2840);
     CLOCK_PULSE(p);
 
     /*
@@ -4263,10 +5385,10 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = CS_2840 | seeprom_read.bits[i];
-      outb(temp, p->base + SEECTL_2840);
+      aic_outb(p, temp, SEECTL_2840);
       CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, p->base + SEECTL_2840);
+      aic_outb(p, temp, SEECTL_2840);
       CLOCK_PULSE(p);
     }
     /*
@@ -4277,10 +5399,10 @@
       temp = k;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = CS_2840 | temp;
-      outb(temp, p->base + SEECTL_2840);
+      aic_outb(p, temp, SEECTL_2840);
       CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, p->base + SEECTL_2840);
+      aic_outb(p, temp, SEECTL_2840);
       CLOCK_PULSE(p);
     }
 
@@ -4293,11 +5415,11 @@
     for (i = 0; i <= 16; i++)
     {
       temp = CS_2840;
-      outb(temp, p->base + SEECTL_2840);
+      aic_outb(p, temp, SEECTL_2840);
       CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
-      outb(temp, p->base + SEECTL_2840);
+      seeprom[k] = (seeprom[k] << 1) | (aic_inb(p, STATUS_2840) & DI_2840);
+      aic_outb(p, temp, SEECTL_2840);
       CLOCK_PULSE(p);
     }
     /*
@@ -4314,11 +5436,11 @@
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(0, p->base + SEECTL_2840);
+    aic_outb(p, 0, SEECTL_2840);
     CLOCK_PULSE(p);
-    outb(CK_2840, p->base + SEECTL_2840);
+    aic_outb(p, CK_2840, SEECTL_2840);
     CLOCK_PULSE(p);
-    outb(0, p->base + SEECTL_2840);
+    aic_outb(p, 0, SEECTL_2840);
     CLOCK_PULSE(p);
   }
 
@@ -4365,16 +5487,16 @@
    * is needed.  Reason: after the 7870 chip reset, there
    * should be no contention.
    */
-  outb(SEEMS, p->base + SEECTL);
+  aic_outb(p, SEEMS, SEECTL);
   wait = 1000;  /* 1000 msec = 1 second */
-  while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+  while ((wait > 0) && ((aic_inb(p, SEECTL) & SEERDY) == 0))
   {
     wait--;
     udelay(1000);  /* 1 msec */
   }
-  if ((inb(p->base + SEECTL) & SEERDY) == 0)
+  if ((aic_inb(p, SEECTL) & SEERDY) == 0)
   {
-    outb(0, p->base + SEECTL);
+    aic_outb(p, 0, SEECTL);
     return (0);
   }
   return (1);
@@ -4390,7 +5512,7 @@
 static inline void
 release_seeprom(struct aic7xxx_host *p)
 {
-  outb(0, p->base + SEECTL);
+  aic_outb(p, 0, SEECTL);
 }
 
 /*+F*************************************************************************
@@ -4444,8 +5566,8 @@
  *   this case, has no implied timing.
  *-F*************************************************************************/
 static int
-read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
-    unsigned int len, 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 char temp;
@@ -4457,9 +5579,9 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(p->base + SEECTL) & SEERDY) == 0)	\
-  {						\
-    ;  /* Do nothing */				\
+  while ((aic_inb(p, SEECTL) & SEERDY) == 0)        \
+  {                                                \
+    ;  /* Do nothing */                                \
   }
 
   /*
@@ -4482,7 +5604,7 @@
     /*
      * Send chip select for one clock cycle.
      */
-    outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+    aic_outb(p, SEEMS | SEECK | SEECS, SEECTL);
     CLOCK_PULSE(p);
 
     /*
@@ -4492,10 +5614,10 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
-      outb(temp, p->base + SEECTL);
+      aic_outb(p, temp, SEECTL);
       CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, p->base + SEECTL);
+      aic_outb(p, temp, SEECTL);
       CLOCK_PULSE(p);
     }
     /*
@@ -4506,10 +5628,10 @@
       temp = k + offset;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = SEEMS | SEECS | (temp << 1);
-      outb(temp, p->base + SEECTL);
+      aic_outb(p, temp, SEECTL);
       CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, p->base + SEECTL);
+      aic_outb(p, temp, SEECTL);
       CLOCK_PULSE(p);
     }
 
@@ -4522,11 +5644,11 @@
     for (i = 0; i <= 16; i++)
     {
       temp = SEEMS | SEECS;
-      outb(temp, p->base + SEECTL);
+      aic_outb(p, temp, SEECTL);
       CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
-      outb(temp, p->base + SEECTL);
+      scarray[k] = (scarray[k] << 1) | (aic_inb(p, SEECTL) & SEEDI);
+      aic_outb(p, temp, SEECTL);
       CLOCK_PULSE(p);
     }
 
@@ -4544,11 +5666,11 @@
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(SEEMS, p->base + SEECTL);
+    aic_outb(p, SEEMS, SEECTL);
     CLOCK_PULSE(p);
-    outb(SEEMS | SEECK, p->base + SEECTL);
+    aic_outb(p, SEEMS | SEECK, SEECTL);
     CLOCK_PULSE(p);
-    outb(SEEMS, p->base + SEECTL);
+    aic_outb(p, SEEMS, SEECTL);
     CLOCK_PULSE(p);
   }
 
@@ -4571,7 +5693,6 @@
   }
   printk("\n");
 #endif
-
   if (checksum != scarray[len - 1])
   {
     return (0);
@@ -4594,13 +5715,13 @@
   unsigned char brdctl;
 
   brdctl = BRDCS | BRDSTB;
-  outb(brdctl, p->base + BRDCTL);
+  aic_outb(p, brdctl, BRDCTL);
   brdctl |= value;
-  outb(brdctl, p->base + BRDCTL);
+  aic_outb(p, brdctl, BRDCTL);
   brdctl &= ~BRDSTB;
-  outb(brdctl, p->base + BRDCTL);
+  aic_outb(p, brdctl, BRDCTL);
   brdctl &= ~BRDCS;
-  outb(brdctl, p->base + BRDCTL);
+  aic_outb(p, brdctl, BRDCTL);
 }
 
 /*+F*************************************************************************
@@ -4613,8 +5734,77 @@
 static inline unsigned char
 read_brdctl(struct aic7xxx_host *p)
 {
-  outb(BRDRW | BRDCS, p->base + BRDCTL);
-  return (inb(p->base + BRDCTL));
+  aic_outb(p, BRDRW | BRDCS, BRDCTL);
+  return (aic_inb(p, BRDCTL));
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic785x_cable_detect
+ *
+ * Description:
+ *   Detect the cables that are present on aic785x class controller chips
+ *-F*************************************************************************/
+static void
+aic785x_cable_detect(struct aic7xxx_host *p, int *int_50,
+    int *ext_present, int *eeprom)
+{
+  unsigned char brdctl;
+
+  aic_outb(p, BRDRW | BRDCS, BRDCTL);
+  aic_outb(p, 0, BRDCTL);
+  brdctl = aic_inb(p, BRDCTL);
+  *int_50 = !(brdctl & BRDDAT5);
+  *ext_present = !(brdctl & BRDDAT6);
+  *eeprom = ( aic_inb(p, SPIOCAP) & EEPROM ) != 0;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic787x_cable_detect
+ *
+ * Description:
+ *   Detect the cables that are present on aic787x class controller chips
+ *
+ * NOTE: This functions assumes the SEEPROM will have already been aquired
+ *       prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic787x_cable_detect(struct aic7xxx_host *p, int *int_50, int *int_68,
+    int *ext_present, int *eeprom)
+{
+  unsigned char brdctl;
+
+  /*
+   * 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.
+   */
+  write_brdctl(p, 0);
+
+  /*
+   * Now we read the state of the two internal connectors.  BRDDAT6
+   * is internal 50, BRDDAT7 is internal 68.  For each, the cable is
+   * present if the bit is 0
+   */
+  brdctl = read_brdctl(p);
+  *int_50 = !(brdctl & BRDDAT6);
+  *int_68 = !(brdctl & BRDDAT7);
+
+  /*
+   * Set the bank bit in brdctl and then read the external cable state
+   * and the EEPROM status
+   */
+  write_brdctl(p, BRDDAT5);
+  brdctl = read_brdctl(p);
+
+  *ext_present = !(brdctl & BRDDAT6);
+  *eeprom = (brdctl & BRDDAT7);
+
+  /*
+   * We're done, the calling function will release the SEEPROM for us
+   */
+  
 }
 
 /*+F*************************************************************************
@@ -4629,82 +5819,72 @@
 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"); 
-
+      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination on "
+                       "controller:\n");
+      printk(KERN_INFO "aic7xxx: <%s> at ", board_names[p->board_name_index]);
+      switch(p->type & 0x1ff1)
+      {
+        case AHC_AIC7770:
+        case AHC_274:
+          printk("EISA slot %d\n", p->pci_device_fn);
+          break;
+        case AHC_284:
+          printk("VLB slot %d\n", p->pci_device_fn);
+          break;
+        default:
+          printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn),
+            PCI_FUNC(p->pci_device_fn));
+          break;
+      }
+      printk(KERN_INFO "aic7xxx: Please verify driver detected settings are "
+        "correct.\n");
+      printk(KERN_INFO "aic7xxx: If not, then please properly set the device "
+        "termination\n");
+      printk(KERN_INFO "aic7xxx: in the Adaptec SCSI BIOS by hitting CTRL-A "
+        "when prompted\n");
+      printk(KERN_INFO "aic7xxx: during machine bootup.\n");
       /* Configure auto termination. */
-      outb(SEECS | SEEMS, p->base + SEECTL);
-
-      /*
-       * 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.
-       */
-      write_brdctl(p, 0);
+      aic_outb(p, SEECS | SEEMS, SEECTL);
 
-      /*
-       * 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).
-       */
-      brdctl_int = read_brdctl(p);
-      internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
-      if (max_targ > 8)
+      if ( (p->type & AHC_AIC7860) == AHC_AIC7860 )
       {
-        internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
+        aic785x_cable_detect(p, &internal50_present, &external_present,
+          &eprom_present);
       }
-
-      /*
-       * Set the rom bank to 1 and determine
-       * the other signals.
-       */
-      write_brdctl(p, BRDDAT5);
-
-      /*
-       * 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.
-       */
-      brdctl_ext = read_brdctl(p);
-      external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
-      eprom_present = brdctl_ext & BRDDAT7;
-      if (aic7xxx_verbose)
-      {
-        if (max_targ > 8)
-        {
-          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
-        {
-          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);
+      else
+      {
+        aic787x_cable_detect(p, &internal50_present, &internal68_present,
+          &external_present, &eprom_present);
       }
+      if (max_targ > 8)
+      {
+        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
+      {
+        printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+               internal50_present ? "YES" : "NO",
+               external_present ? "YES" : "NO");
+        internal68_present = 0;
+      }
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(KERN_INFO "aic7xxx: EEPROM %s present.\n",
+             eprom_present ? "is" : "is not");
 
       /*
        * Now set the termination based on what we found.  BRDDAT6
@@ -4718,16 +5898,18 @@
         high_on = TRUE;
       }
 
-      if ((internal50_present + internal68_present + external_present) <= 1)
+      if ( ( (internal50_present ? 1 : 0) +
+             (internal68_present ? 1 : 0) +
+             (external_present   ? 1 : 0) ) <= 1)
       {
         low_on = TRUE;
       }
           
       if (internal50_present && internal68_present && external_present)
       {
-        printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
-               "         Only two connectors on the adapter may be "
-               "used at a time!\n");
+        printk(KERN_INFO "aic7xxx: Illegal cable configuration!!  Only two\n");
+        printk(KERN_INFO "aic7xxx: connectors on the SCSI controller may be "
+               "in use at a time!\n");
       }
 
       if (high_on == TRUE)
@@ -4738,28 +5920,23 @@
       if (low_on == TRUE)
         *sxfrctl1 |= STPWEN;
 
-      if (aic7xxx_verbose)
+      if (max_targ > 8)
       {
-        if (max_targ > 8)
-        {
-          printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
-                 low_on ? "ON" : "OFF",
-                 high_on ? "ON" : "OFF");
-        }
-        else
-        {
-          printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
-        }
+        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+              low_on ? "ON" : "OFF", high_on ? "ON" : "OFF");
+      }
+      else
+      {
+        printk(KERN_INFO "aic7xxx: Termination %s\n",
+          low_on ? "Enabled" : "Disabled");
       }
-      aic7xxx_verbose = old_verbose;
     }
     else
     {
       if (adapter_control & CFSTERM)
-      {
         *sxfrctl1 |= STPWEN;
-      }
-      outb(SEEMS | SEECS, p->base + SEECTL);
+
+      aic_outb(p, SEEMS | SEECS, SEECTL);
       /*
        * Configure high byte termination.
        */
@@ -4771,7 +5948,7 @@
       {
         write_brdctl(p, 0);
       }
-      if (aic7xxx_verbose)
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
       {
         printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
                (adapter_control & CFSTERM) ? "ON" : "OFF",
@@ -4794,7 +5971,6 @@
 detect_maxscb(struct aic7xxx_host *p)
 {
   int i;
-  unsigned char max_scbid = 255;
 
   /*
    * It's possible that we've already done this for multichannel
@@ -4806,90 +5982,35 @@
      * We haven't initialized the SCB settings yet.  Walk the SCBs to
      * determince how many there are.
      */
-    outb(0, p->base + FREE_SCBH);
+    aic_outb(p, 0, FREE_SCBH);
 
     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)
+      aic_outb(p, i, SCBPTR);
+      aic_outb(p, i, SCB_CONTROL);
+      if (aic_inb(p, SCB_CONTROL) != i)
         break;
-      outb(0, p->base + SCBPTR);
-      if (inb(p->base + SCB_CONTROL) != 0)
+      aic_outb(p, 0, SCBPTR);
+      if (aic_inb(p, SCB_CONTROL) != 0)
         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. */
-
-      /* 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);
+      aic_outb(p, i, SCBPTR);
+      aic_outb(p, 0, SCB_CONTROL);   /* Clear the control byte. */
+      aic_outb(p, i + 1, SCB_NEXT);  /* Set the next pointer. */
+      aic_outb(p, SCB_LIST_NULL, SCB_TAG);  /* Make the tag invalid. */
     }
 
     /* Make sure the last SCB terminates the free list. */
-    outb(i - 1, p->base + SCBPTR);
-    outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+    aic_outb(p, i - 1, SCBPTR);
+    aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
 
     /* Ensure we clear the first (0) SCBs control byte. */
-    outb(0, p->base + SCBPTR);
-    outb(0, p->base + SCB_CONTROL);
+    aic_outb(p, 0, SCBPTR);
+    aic_outb(p, 0, SCB_CONTROL);
 
     p->scb_data->maxhscbs = i;
   }
 
-  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;
-  }
-
-  /*
-   * Set the Queue Full Count.  Some cards have more queue space than
-   * SCBs.
-   */
-  switch (p->chip_class)
-  {
-    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*************************************************************************
@@ -4900,15 +6021,14 @@
  *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
  *-F*************************************************************************/
 static int
-aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
+  int reset_delay)
 {
-  int i;
-  unsigned char sblkctl, flags = 0;
-  int max_targets, irq_flags = 0;
+  int i, result;
+  int max_targets;
   int found = 1;
-  char channel_ids[] = {'A', 'B', 'C'};
   unsigned char target_settings;
-  unsigned char scsi_conf, sxfrctl1;
+  unsigned char term, scsi_conf, sxfrctl1;
   unsigned short ultraenable = 0;
   struct Scsi_Host *host;
 
@@ -4917,78 +6037,12 @@
    */
   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(p->base + SBLKCTL);
-  if (p->flags & PAGE_ENABLED)
-    flags = PAGESCBS;
-
-  switch (sblkctl & SELBUS_MASK)
-  {
-    case SELNARROW:     /* narrow/normal bus */
-      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 */
-      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 */
-      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(p->base + SBLKCTL));
-      outb(0, p->base + SEQ_FLAGS);
-      return (0);
-  }
-
-  /*
-   * Detect SCB parameters and initialize the SCB array.
-   */
-  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;
+  p->scb_data->maxscbs = AIC7XXX_MAXSCB;
+  host->can_queue = AIC7XXX_MAXSCB;
+  host->cmd_per_lun = 3;
   host->sg_tablesize = AIC7XXX_MAX_SG;
   host->select_queue_depths = aic7xxx_select_queue_depth;
   host->this_id = p->scsi_id;
@@ -4996,110 +6050,231 @@
   host->n_io_port = 0xFF;
   host->base = (unsigned char *) p->mbase;
   host->irq = p->irq;
-  if (p->bus_type == AIC_WIDE)
+  if (p->type & AHC_WIDE)
   {
     host->max_id = 16;
   }
-  if (p->bus_type == AIC_TWIN)
+  if (p->type & AHC_TWIN)
   {
     host->max_channel = 1;
   }
 
   p->host = host;
+  p->last_reset = 0;
   p->host_no = host->host_no;
   p->isr_count = 0;
+  p->next = NULL;
   p->completeq.head = NULL;
   p->completeq.tail = NULL;
   scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
 
-  for (i = 0; i < NUMBER(p->device_status); i++)
+  for (i = 0; i < NUMBER(p->untagged_scbs); 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;
+    p->untagged_scbs[i] = SCB_LIST_NULL;
+    p->qinfifo[i] = SCB_LIST_NULL;
+    p->qoutfifo[i] = SCB_LIST_NULL;
+  }
+  /*
+   * We currently have no commands of any type
+   */
+  p->qinfifonext = 0;
+  p->qoutfifonext = 0;
+
+  for (i = 0; i < MAX_TARGETS; i++)
+  {
+    p->dev_commands_sent[i] = 0;
+    p->dev_flags[i] = DEVICE_PRINT_WDTR | DEVICE_PRINT_SDTR;
+    p->dev_active_cmds[i] = 0;
+    p->dev_last_reset[i] = 0;
+    p->dev_last_queue_full[i] = 0;
+    p->dev_last_queue_full_count[i] = 0;
+    p->dev_max_queue_depth[i] = 1;
+    p->dev_temp_queue_depth[i] = 1;
+    p->dev_mid_level_queue_depth[i] = 3;
+    scbq_init(&p->delayed_scbs[i]);
+    init_timer(&p->dev_timer[i]);
+    p->dev_timer[i].expires = 0;
+    p->dev_timer[i].data = (unsigned long)p;
+    p->dev_timer[i].function = (void *)aic7xxx_timer;
+    p->syncinfo[i].period = 0;
+    p->syncinfo[i].offset = 0;
+  }
+
+  printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
+    board_names[p->board_name_index]);
+  switch(p->type & 0x1ff1)
+  {
+    case AHC_AIC7770:
+    case AHC_274:
+      printk("EISA slot %d\n", p->pci_device_fn);
+      break;
+    case AHC_284:
+      printk("VLB slot %d\n", p->pci_device_fn);
+      break;
+    default:
+      printk("PCI %d/%d\n", PCI_SLOT(p->pci_device_fn),
+        PCI_FUNC(p->pci_device_fn));
+      break;
   }
-
-  /*
-   * Request an IRQ for the board. Only allow sharing IRQs with PCI devices.
-   */
-#ifdef AIC7XXX_OLD_ISR_TYPE
-  irq_flags = SA_INTERRUPT;
-#endif
-  if (p->chip_class != AIC_777x)
-    irq_flags |= SA_SHIRQ;
-  if (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", p) < 0)
+  if (p->type & AHC_TWIN)
   {
-    printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
-           p->irq);
-    return (0);
+    printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+           p->host_no, p->scsi_id, p->scsi_id_b);
   }
-
-  /*
-   * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
-   */
-  if (p->bus_type == AIC_TWIN)
+  else
   {
-    /*
-     * The controller is gated to channel B after a chip reset; set
-     * bus B values first.
-     */
-    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)
+    char *channel;
+
+    channel = "";
+
+    if ((p->type & AHC_39x) != 0)
+    {
+      channel = " A";
+
+      if ( (p->flags & (AHC_CHNLB|AHC_CHNLC)) != 0 )
+      {
+        channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+      }
+    }
+    if (p->type & AHC_WIDE)
     {
-      outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
+      printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
     }
     else
     {
-      outb(DFON | SPIOEN, p->base + SXFRCTL0);
+      printk(KERN_INFO "(scsi%d) Narrow ", p->host_no);
     }
+    printk("Channel%s, SCSI ID=%d, ", channel, p->scsi_id);
+  }
+  aic_outb(p, 0, SEQ_FLAGS);
 
+  /*
+   * Detect SCB parameters and initialize the SCB array.
+   */
+  detect_maxscb(p);
+  printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
+  printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
+    p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
+    p->base, p->irq);
+  printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
+    p->host_no, p->mbase, (unsigned long)p->maddr);
+
+
+
+  /*
+   * Register IRQ with the kernel.  Only allow sharing IRQs with
+   * PCI devices.
+   */
+  if ((p->type & AHC_AIC7770) == AHC_AIC7770)
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+  }
+  else
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
+              "aic7xxx", p));
+    if (result < 0)
+    {
+      result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+              "aic7xxx", p));
+    }
+  }
+  if (result < 0)
+  {
+    printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring.\n",
+           p->host_no, p->irq);
+    return (0);
+  }
+
+  /*
+   * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
+   */
+  if (p->type & AHC_TWIN)
+  {
+    /*
+     * The controller is gated to channel B after a chip reset; set
+     * bus B values first.
+     */
+    term = ((p->flags & AHC_TERM_ENB_B) != 0) ? STPWEN : 0;
+    aic_outb(p, p->scsi_id_b, SCSIID);
+    scsi_conf = aic_inb(p, SCSICONF + 1);
+    sxfrctl1 = aic_inb(p, SXFRCTL1);
+    aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | 
+         ENSTIMER | ACTNEGEN, SXFRCTL1);
+    aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+    if (p->type & AHC_ULTRA)
+    {
+      aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
+    }
+    else
+    {
+      aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+    }
+    if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+    {
+      scsi_conf &= ~0x07;
+      scsi_conf |= p->scsi_id_b;
+      aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF + 1);
+    }
     if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
     {
       /* Reset SCSI bus B. */
-      if (aic7xxx_verbose)
-        printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+      if (aic7xxx_verbose & VERBOSE_PROBE)
+        printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
 
       aic7xxx_reset_current_bus(p);
     }
 
     /* Select channel A */
-    outb(SELNARROW, p->base + SBLKCTL);
+    aic_outb(p, SELNARROW, SBLKCTL);
   }
 
-  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)
+  term = ((p->flags & AHC_TERM_ENB_A) != 0) ? STPWEN : 0;
+  aic_outb(p, p->scsi_id, SCSIID);
+  scsi_conf = aic_inb(p, SCSICONF);
+  sxfrctl1 = aic_inb(p, SXFRCTL1);
+  aic_outb(p, (scsi_conf & (ENSPCHK | STIMESEL)) | term | 
+       ENSTIMER | ACTNEGEN, SXFRCTL1);
+  aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+  if (p->type & AHC_ULTRA)
   {
-    outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
+    aic_outb(p, DFON | SPIOEN | FAST20, SXFRCTL0);
   }
   else
   {
-    outb(DFON | SPIOEN, p->base + SXFRCTL0);
+    aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+  }
+  if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
+  {
+    scsi_conf &= ~0x07;
+    scsi_conf |= p->scsi_id;
+    aic_outb(p, scsi_conf | (term) ? TERM_ENB : 0, SCSICONF);
   }
 
+
   if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
   {
     /* Reset SCSI bus A. */
-    if (aic7xxx_verbose)
-      printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+    if (aic7xxx_verbose & VERBOSE_PROBE)
+    {  /* In case we are a 3940, 3985, or 7895, print the right channel */
+      char *channel = "";
+      if (p->flags & AHC_MULTI_CHANNEL)
+      {
+        channel = " A";
+        if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+          channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+      }
+      printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
+    }
 
     aic7xxx_reset_current_bus(p);
 
     /*
      * Delay for the reset delay.
      */
-    aic7xxx_delay(AIC7XXX_RESET_DELAY);
+    if (!reset_delay)
+      aic7xxx_delay(AIC7XXX_RESET_DELAY);
   }
 
   /*
@@ -5114,7 +6289,10 @@
   p->sdtr_pending = 0x0;
   p->needwdtr_copy = 0x0;
   p->wdtr_pending = 0x0;
-  if (p->bus_type == AIC_SINGLE)
+  p->tagenable = 0x0;
+  p->ultraenb = 0x0;
+  p->discenable = 0xffff;
+  if ((p->type & (AHC_TWIN|AHC_WIDE)) == 0)
   {
     max_targets = 8;
   }
@@ -5126,31 +6304,36 @@
   /*
    * Grab the disconnection disable table and invert it for our needs
    */
-  if (p->flags & USE_DEFAULTS)
+  if (p->flags & AHC_USEDEFAULTS)
   {
-    printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
-           "device parameters.\n");
-    p->discenable = 0xFFFF;
+    printk(KERN_INFO "(scsi%d) Host adapter BIOS disabled. Using default SCSI "
+           "device parameters.\n", p->host_no);
+    if (p->type & AHC_ULTRA)
+      p->ultraenb = 0xffff;
+    p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
   }
   else
   {
-    p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
-        inb(p->base + DISC_DSB));
+    p->discenable = ~((aic_inb(p, DISC_DSB + 1) << 8) |
+        aic_inb(p, DISC_DSB));
+    if (p->type & AHC_ULTRA)
+      p->ultraenb = (aic_inb(p, ULTRA_ENB + 1) << 8) | 
+          aic_inb(p, ULTRA_ENB);
   }
 
   for (i = 0; i < max_targets; i++)
   {
-    if (p->flags & USE_DEFAULTS)
+    if (p->flags & AHC_USEDEFAULTS)
     {
       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))
+      if (p->type & AHC_ULTRA)
         ultraenable |= (0x01 << i);
     }
     else
     {
-      target_settings = inb(p->base + TARG_SCRATCH + i);
+      target_settings = aic_inb(p, TARG_SCRATCH + i);
       if (target_settings & 0x0F)
       {
         p->needsdtr_copy |= (0x01 << i);
@@ -5168,24 +6351,33 @@
          */
         target_settings &= 0x7F;
       }
-      if (p->flags & ULTRA_ENABLED)
+    }
+
+    aic_outb(p, target_settings, TARG_SCRATCH + i);
+    if (p->needsdtr_copy & (0x01 << i))
+    {
+      short sxfr, j;
+
+      sxfr = target_settings & SXFR;
+      if ((p->ultraenb & (1 << i)) != 0)
       {
-        switch (target_settings & 0x70)
-        {
-          case 0x00:
-          case 0x10:
-          case 0x20:
-            ultraenable |= (0x01 << i);
-            break;
-          case 0x40:  /* treat 10MHz as 10MHz without Ultra enabled */
-            target_settings &= ~(0x70);
-            break;
-          default:
-            break;
-        }
+        /* Want an ultra speed in the table */
+        sxfr |= 0x100;
       }
+      for (j = 0; j < NUMBER(aic7xxx_syncrates); j++)
+      {
+        if (sxfr == aic7xxx_syncrates[j].rate)
+          break;
+      }
+      p->syncinfo[i].period = aic7xxx_syncrates[j].period;
+      p->syncinfo[i].offset =
+       (p->type & AHC_WIDE) ? MAX_OFFSET_16BIT : MAX_OFFSET_8BIT;
+    }
+    else
+    {
+      p->syncinfo[i].period = 0;
+      p->syncinfo[i].offset = 0;
     }
-    outb(target_settings, p->base + TARG_SCRATCH + i);
   }
 
   /*
@@ -5193,26 +6385,14 @@
    * work on some cards that don't leave these fields cleared
    * when BIOS is not installed.
    */
-  if (p->bus_type != AIC_WIDE)
+  if ( !(p->type & AHC_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);
+  aic_outb(p, 0, ULTRA_ENB);
+  aic_outb(p, 0, ULTRA_ENB + 1);
 
   /*
    * Allocate enough hardware scbs to handle the maximum number of
@@ -5230,8 +6410,8 @@
     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");
+      printk("(scsi%d) Unable to allocate hardware SCB array; "
+             "failing detection.\n", p->host_no);
       release_region(p->base, MAXREG - MINREG);
       free_irq(p->irq, p);
       return(0);
@@ -5241,40 +6421,35 @@
 
     /* 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);
+    aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR);
+    aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1);
+    aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2);
+    aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3);
+
+    /* Set up the fifo areas at the same time */
+    hscb_physaddr = VIRT_TO_BUS(&p->untagged_scbs[0]);
+    aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR);
+    aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
+    aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
+    aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
+
+    /* The Q-FIFOs we just set up are all empty */
+    aic_outb(p, 0, QINPOS);
+    aic_outb(p, 0, KERNEL_QINPOS);
+    aic_outb(p, 0, QOUTPOS);
 
-  /*
-   * Set FIFO depth and command out count.  These are only used when
-   * paging is enabled and should not be touched for AIC-7770 based
-   * adapters; FIFODEPTH and CMDOUTCNT overlay SCSICONF and SCSICONF+1
-   * which are used to control termination.
-   */
-  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);
+  aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+  aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
 
   /*
    * Message out buffer starts empty
    */
-  outb(0, p->base + MSG_LEN);
+  aic_outb(p, MSG_NOOP, MSG_OUT);
 
   /*
    * Load the sequencer program, then re-enable the board -
@@ -5285,24 +6460,24 @@
    */
   aic7xxx_loadseq(p);
 
-  if (p->chip_class == AIC_777x)
+  if ( (p->type & AHC_AIC7770) == AHC_AIC7770 )
   {
-    outb(ENABLE, p->base + BCTL);  /* Enable the boards BUS drivers. */
+    aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
   }
 
   /*
+   * Link us into the list of valid hosts
+   */
+  p->next = first_aic7xxx;
+  first_aic7xxx = p;
+
+  /*
    * 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);
 
-  /*
-   * Add it to our list of adapters.
-   */
-  p->next = first_aic7xxx;
-  first_aic7xxx = p;
-
   return (found);
 }
 
@@ -5314,38 +6489,56 @@
  *   Perform a chip reset on the aic7xxx SCSI controller.  The controller
  *   is paused upon return.
  *-F*************************************************************************/
-static void
+int
 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;
+  hcntrl = (aic_inb(p, 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);
+  aic_outb(p, PAUSE | CHIPRST, 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))
+  while ((wait > 0) && ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0))
   {
     udelay(1000);  /* 1 msec = 1000 usec */
     wait = wait - 1;
   }
 
-  if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+  if ((aic_inb(p, HCNTRL) & CHIPRSTACK) == 0)
   {
     printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
   }
 
-  outb(hcntrl | PAUSE, p->base + HCNTRL);
+  aic_outb(p, hcntrl | PAUSE, HCNTRL);
+
+  switch( aic_inb(p, SBLKCTL) & 0x0a )
+  {
+    case 0:  /* normal narrow card */
+      break;
+    case 2:  /* Wide card */
+      p->type |= AHC_WIDE;
+      break;
+    case 8:  /* Twin card */
+      p->type |= AHC_TWIN;
+      p->flags |= AHC_MULTI_CHANNEL;
+      break;
+    default: /* hmmm...we don't know what this is */
+      printk(KERN_WARNING "aic7xxx: Unsupported adapter type %d, ignoring.\n",
+        aic_inb(p, SBLKCTL) & 0x0a);
+      return(-1);
+  }
+  return(0);
 }
 
 /*+F*************************************************************************
@@ -5357,8 +6550,7 @@
  *   and a pointer to a aic7xxx_host struct upon success.
  *-F*************************************************************************/
 static struct aic7xxx_host *
-aic7xxx_alloc(Scsi_Host_Template *sht, unsigned long base, unsigned long mbase,
-    aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+aic7xxx_alloc(Scsi_Host_Template *sht, struct aic7xxx_host *temp)
 {
   struct aic7xxx_host *p = NULL;
   struct Scsi_Host *host;
@@ -5375,46 +6567,45 @@
     memset(p, 0, sizeof(struct aic7xxx_host));
     p->host = host;
 
-    if (scb_data != NULL)
+    p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+    if (p->scb_data != NULL)
     {
-      /*
-       * We are sharing SCB data areas; use the SCB data pointer
-       * provided.
-       */
-      p->scb_data = scb_data;
-      p->flags |= SHARED_SCBDATA;
+      memset(p->scb_data, 0, sizeof(scb_data_type));
+      scbq_init (&p->scb_data->free_scbs);
     }
     else
     {
       /*
-       * We are not sharing SCB data; allocate one.
+       * For some reason we don't have enough memory.  Free the
+       * allocated memory for the aic7xxx_host struct, and return NULL.
        */
-      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;
-      }
+      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;
+      p->base = temp->base;
+      p->mbase = temp->mbase;
+      p->maddr = temp->maddr;
+      p->flags = temp->flags;
+      p->type = temp->type;
+      p->unpause = temp->unpause;
+      p->pause = temp->pause;
+      p->pci_bus = temp->pci_bus;
+      p->pci_device_fn = temp->pci_device_fn;
+      p->bios_address = temp->bios_address;
+      p->irq = temp->irq;
+      p->scsi_id = temp->scsi_id;
+      p->scsi_id_b = temp->scsi_id_b;
+      p->discenable = temp->discenable;
+      p->ultraenb = temp->ultraenb;
+      p->tagenable = 0;
+      p->orderedtag = 0;
+      p->board_name_index = temp->board_name_index;
+      p->adapter_control = temp->adapter_control;
+      p->bios_control = temp->bios_control;
+      DRIVER_LOCK_INIT
     }
   }
   return (p);
@@ -5429,49 +6620,48 @@
  *   the driver (struct aic7xxx_host *).
  *-F*************************************************************************/
 static void
-aic7xxx_free (struct aic7xxx_host *p)
+aic7xxx_free(struct aic7xxx_host *p)
 {
-  int i;
+  int i, jump;
 
   /*
-   * 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.
+   * Free the allocated hardware SCB space.
    */
-  if (!(p->flags & SHARED_SCBDATA))
+  if (p->scb_data->hscbs != NULL)
   {
-    /*
-     * 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);
+    kfree(p->scb_data->hscbs);
+  }
+  /*
+   * Free the driver SCBs.  These were allocated on an as-need
+   * basis. However, we allocated them 30 at a time up until the
+   * very last allocation (if there was one).  So, we need to free
+   * every 30th pointer to free the array (this also frees the
+   * SG_array structs as well).
+   *
+   * Note, on 64 bit machines we allocate 29 at a time instead.
+   */
+  jump = (sizeof(int) == sizeof(void *)) ? 30 : 29;
+  for (i = 0; i < p->scb_data->numscbs; i += jump)
+  {
+    kfree(p->scb_data->scb_array[i]);
   }
   /*
+   * Free the SCB data area.
+   */
+  kfree(p->scb_data);
+
+  /*
    * Free the instance of the device structure.
    */
+
+  /*
+   * XXXXXXXX  FIXXXXXMEEEEEE.  How do we unmap the I/O range we have mapped
+   * if we are doing MMAPed I/O ??????????  Our biggest concern is the issue
+   * of possibly calling unmap on an area that *might* be used on another
+   * controller as well (aka, the 4096 byte MMAPed area is back to back
+   * with another controller, and the PAGE_SIZE is greater then 4096, allowing
+   * us to remap in a shared page).
+   */
   scsi_unregister(p->host);
 }
 
@@ -5492,46 +6682,42 @@
   unsigned short scarray[128];
   struct seeprom_config *sc = (struct seeprom_config *) scarray;
 
-  if (aic7xxx_verbose)
+  if (aic7xxx_verbose & VERBOSE_PROBE2)
   {
     printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
   }
-  switch (p->chip_type)
+  switch (p->type & 0x00001ff1)
   {
-    case AIC_7770:  /* None of these adapters have seeproms. */
-    case AIC_7771:
-    case AIC_7855:
+    case AHC_AIC7770:  /* None of these adapters have seeproms. */
+    case AHC_274:
       break;
 
-    case AIC_284x:
+    case AHC_284:
       have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
       break;
 
-    case AIC_7850:  /* The 2910B is a 7850 with a seeprom. */
-    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),
+    case AHC_AIC7850:  /* The 2910B is a 7850 with a seeprom. */
+    case AHC_294AU:
+    case AHC_AIC7870:  /* For these controllers we try the three possible */
+    case AHC_AIC7895:  /* SEEPROM read types.  If none works, then we are */
+    case AHC_294:      /* SOL.  This should catch any SEEPROM variety     */
+    case AHC_394:      /* Adaptec or some motherboard manufacturer might  */
+    case AHC_294U:     /* throw at us, and since we perform a checksum    */
+    case AHC_394U:     /* during the read, we should get bogus seeprom    */
+    case AHC_AIC7860:  /* reads. */
+    case AHC_AIC7880:
+    case AHC_398:
+    case AHC_398U:
+      have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
                                   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),
+        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                  scarray, sizeof(scarray)/2, C46);
+      if (!have_seeprom)
+        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                  scarray, sizeof(*sc)/2, C56_66);
+      if (!have_seeprom)
+        have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
                                   scarray, sizeof(scarray)/2, C56_66);
       break;
 
@@ -5541,19 +6727,19 @@
 
   if (!have_seeprom)
   {
-    if (aic7xxx_verbose)
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
     {
       printk("\naic7xxx: No SEEPROM available; using defaults.\n");
     }
-    p->flags |= USE_DEFAULTS;
+    p->flags |= AHC_USEDEFAULTS;
+    p->flags &= ~AHC_BIOS_ENABLED;
   }
   else
   {
-    if (aic7xxx_verbose)
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
     {
       printk("done\n");
     }
-    p->flags |= HAVE_SEEPROM;
 
     /*
      * Update the settings in sxfrctl1 to match the termination settings.
@@ -5564,14 +6750,17 @@
      * First process the settings that are different between the VLB
      * and PCI adapter seeproms.
      */
-    if (p->chip_class == AIC_777x)
+    if (p->type & AHC_284)
     {
       /* VLB adapter seeproms */
       if (sc->bios_control & CF284XEXTEND)
-        p->flags |= EXTENDED_TRANSLATION;
+        p->flags |= AHC_EXTEND_TRANS_A;
 
       if (sc->adapter_control & CF284XSTERM)
+      {
         *sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_A;
+      }
       /*
        * 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
@@ -5589,30 +6778,90 @@
     {
       /* PCI adapter seeproms */
       if (sc->bios_control & CFEXTEND)
-        p->flags |= EXTENDED_TRANSLATION;
+        p->flags |= AHC_EXTEND_TRANS_A;
 
       if (sc->adapter_control & CFSTERM)
+      {
         *sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_A;
+      }
 
       /* Limit to 16 targets just in case. */
       max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
     }
 
+    p->discenable = 0;
+
+    for (i = 0; i < max_targets; i++)
+    {
+      if( (p->type & AHC_ULTRA) &&
+         !(sc->adapter_control & CFULTRAEN) &&
+          (sc->device_flags[i] & CFSYNCHISULTRA) )
+      {
+        p->flags |= AHC_NEWEEPROM_FMT;
+        break;
+      }
+    }
+
     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);
+      }
+      if (p->flags & AHC_NEWEEPROM_FMT)
+      {
+        if (sc->device_flags[i] & CFSYNCHISULTRA)
+        {
+          p->ultraenb |= (0x01 << i);
+        }
+      }
+      else if (sc->adapter_control & CFULTRAEN)
+      {
+        p->ultraenb |= (0x01 << i);
+      }
+      if ( ((target_settings & 0x70) == 0x40) &&
+           (p->ultraenb & (0x01 << i)) )
+      {
+        target_settings &= ~0x70;
+        p->ultraenb &= ~(0x01 << i);
+      }
+      aic_outb(p, target_settings, TARG_SCRATCH + i);
     }
-    outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
-    outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
+    aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
+    aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+    aic_outb(p,  (p->ultraenb & 0xFF), ULTRA_ENB);
+    aic_outb(p,  ((p->ultraenb >> 8) & 0xFF), ULTRA_ENB + 1);
 
     p->scsi_id = sc->brtime_id & CFSCSIID;
+    p->adapter_control = sc->adapter_control;
+    p->bios_control = sc->bios_control;
+
+    if (p->bios_control & CFBIOSEN)
+    {
+      p->flags &= ~AHC_USEDEFAULTS;
+      p->flags |= AHC_BIOS_ENABLED;
+    }
+    else
+    {
+      p->flags &= ~AHC_BIOS_ENABLED;
+      p->flags |= AHC_USEDEFAULTS;
+    }
+
+    if ((p->type & 0x1ff1) == AHC_AIC7895)
+    {
+      if (p->adapter_control & CFBPRIMARY)
+        p->flags |= AHC_CHANNEL_B_PRIMARY;
+    }
 
     scsi_conf = (p->scsi_id & 0x7);
     if (sc->adapter_control & CFSPARITY)
@@ -5621,37 +6870,39 @@
      * The 7850 controllers with a seeprom, do not honor the CFRESETB
      * flag in the seeprom.  Assume that we want to reset the SCSI bus.
      */
-    if ((sc->adapter_control & CFRESETB) || (p->chip_class == AIC_7850))
+    if (sc->adapter_control & CFRESETB)
       scsi_conf |= RESET_SCSI;
-
-    if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
-    {
-      /*
-       * We allow the operator to override ultra enable through
-       * the boot prompt.
-       */
-      if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
-      {
-        /* Treat us as a non-ultra card */
-        p->flags &= ~ULTRA_ENABLED;
-      }
-    }
+    /*
+     * We may be a 2842, if so, preserve the TERM_ENB bit in scsi conf
+     */
+    if ( (p->flags & AHC_TERM_ENB_A) && 
+        ((p->type & AHC_AIC7770) == AHC_AIC7770) )
+      scsi_conf |= TERM_ENB;
+    /*
+     * If this is an Ultra card, is Ultra mode enabled?  If not, disable
+     * it in the host struct as well
+     */
+    if ( (p->type & AHC_ULTRA) && 
+        !(sc->adapter_control & CFULTRAEN) &&
+        !(p->flags & AHC_NEWEEPROM_FMT) )
+      p->type &= ~AHC_ULTRA;
 
     /* Set the host ID */
-    outb(scsi_conf, p->base + SCSICONF);
+    aic_outb(p, scsi_conf, SCSICONF);
     /* In case we are a wide card */
-    outb(p->scsi_id, p->base + SCSICONF + 1);
+    aic_outb(p, p->scsi_id, SCSICONF + 1);
 
-    if (p->chip_class != AIC_777x)
+    if ((p->type & AHC_AIC7860) == AHC_AIC7860)
     {
+      if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
       /*
        * Update the settings in sxfrctl1 to match the termination
        * settings.
        */
-      *sxfrctl1 = 0;
-      configure_termination(p, sxfrctl1, sc->adapter_control,
-        (unsigned char) sc->max_targets & CFMAXTARG);
+        configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
     }
+    else if (have_seeprom && ((p->type & AHC_AIC7770) != AHC_AIC7770))
+      configure_termination(p, sxfrctl1, sc->adapter_control, max_targets);
   }
   return (have_seeprom);
 }
@@ -5671,28 +6922,40 @@
 int
 aic7xxx_detect(Scsi_Host_Template *template)
 {
+  struct aic7xxx_host *temp_p = NULL;
+  struct aic7xxx_host *current_p = NULL;
+  struct aic7xxx_host *list_p = NULL;
   int found = 0;
-#if !defined(__sparc_v9__) && !defined(__powerpc__)
-  aha_status_type adapter_bios;
-  unsigned char hcntrl, hostconf, irq = 0;
-  int slot, base;
+  ahc_flag_type flags = 0;
+  ahc_type type;
+  unsigned char sxfrctl1;
+#if defined(__i386__) || defined(__alpha__)
+  unsigned char hcntrl, hostconf;
+  unsigned int slot, base;
+#endif
+
+#ifdef MODULE
+  /*
+   * If we are called as a module, the aic7xxx pointer may not be null
+   * and it would point to our bootup string, just like on the lilo
+   * command line.  IF not NULL, then process this config string with
+   * aic7xxx_setup
+   */
+  if(aic7xxx)
+    aic7xxx_setup(aic7xxx, NULL);
+
 #endif
-  aha_chip_class_type chip_class;
-  aha_chip_type chip_type;
-  int chan_num = 0;
-  unsigned char sxfrctl1, sblkctl;
-  int i;
-  struct aic7xxx_host *p;
 
   template->proc_dir = &proc_scsi_aic7xxx;
-  template->name = aic7xxx_info(NULL);
   template->sg_tablesize = AIC7XXX_MAX_SG;
 
-#if !defined(__sparc_v9__) && !defined(__powerpc__)
+
+#if defined(__i386__) || defined(__alpha__)
   /*
    * EISA/VL-bus card signature probe.
    */
-  for (slot = MINSLOT; slot <= MAXSLOT; slot++)
+  slot = MINSLOT;
+  while (slot <= MAXSLOT)
   {
     base = SLOTBASE(slot) + MINREG;
 
@@ -5702,354 +6965,571 @@
        * Some other driver has staked a
        * claim to this i/o region already.
        */
-      continue;
+      slot++;
+      continue; /* back to the beginning of the for loop */
     }
-
-    chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
-    if (chip_type != AIC_NONE)
+    flags = 0;
+    type = aic7xxx_probe(slot, base + HID0, &flags);
+    switch (type)
     {
-
-      switch (chip_type)
-      {
-        case AIC_7770:
-        case AIC_7771:
+      case AHC_AIC7770:
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
           printk("aic7xxx: <%s> at EISA %d\n",
-                 board_names[chip_type], slot);
-          break;
-        case AIC_284x:
+               board_names[2], slot);
+        break;
+      case AHC_274:
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("aic7xxx: <%s> at EISA %d\n",
+               board_names[3], slot);
+        break;
+      case AHC_284:
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
           printk("aic7xxx: <%s> at VLB %d\n",
-                 board_names[chip_type], slot);
-          break;
-        default:
-          break;
-      }
+               board_names[4], slot);
+        break;
+      default:
+        slot++;
+        continue; /* back to the beginning of the while loop */
+    }
+    temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
+    if (temp_p == NULL)
+    {
+      printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+      slot++;
+      continue; /* back to the beginning of the while loop */
+    }
+    /*
+     * Pause the card preserving the IRQ type.  Allow the operator
+     * to override the IRQ trigger.
+     */
+    if (aic7xxx_irq_trigger == 1)
+      hcntrl = IRQMS;  /* Level */
+    else if (aic7xxx_irq_trigger == 0)
+      hcntrl = 0;  /* Edge */
+    else
+      hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
+    memset(temp_p, 0, sizeof(struct aic7xxx_host));
+    temp_p->unpause = hcntrl | INTEN;
+    temp_p->pause = hcntrl | PAUSE | INTEN;
+    temp_p->base = base;
+    temp_p->type = type;
+    temp_p->flags = flags | AHC_PAGESCBS;
+    temp_p->mbase = 0;
+    temp_p->maddr = 0;
+    temp_p->pci_bus = 0;
+    temp_p->pci_device_fn = slot;
+    aic_outb(temp_p, hcntrl | PAUSE, HCNTRL);
+    while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+    if (aic7xxx_chip_reset(temp_p) == -1)
+      temp_p->irq = 0;
+    else
+      temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
+    switch (temp_p->irq)
+    {
+      case 9:
+      case 10:
+      case 11:
+      case 12:
+      case 14:
+      case 15:
+        break;
 
-      /*
-       * Pause the card preserving the IRQ type.  Allow the operator
-       * to override the IRQ trigger.
-       */
-      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);
+      default:
+        printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+          "level %d, ignoring.\n", temp_p->irq);
+        kfree(temp_p);
+        slot++;
+        continue; /* back to the beginning of the while loop */
+    }
 
-      p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
-      if (p == NULL)
-      {
-        printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
-        continue;
-      }
-      aic7xxx_chip_reset(p);
+    /*
+     * We are commited now, everything has been checked and this card
+     * has been found, now we just set it up
+     */
+    /*
+     * Insert our new struct into the list at the end
+     */
+    if (list_p == NULL)
+    {
+      list_p = current_p = temp_p;
+    }
+    else
+    {
+      current_p = list_p;
+      while (current_p->next != NULL)
+        current_p = current_p->next;
+      current_p->next = temp_p;
+    }
+    if (aic7xxx_extended)
+    {
+      temp_p->flags |= AHC_EXTEND_TRANS_A;
+      if (temp_p->flags & AHC_MULTI_CHANNEL)
+        temp_p->flags |= AHC_EXTEND_TRANS_B;
+    }
 
-      irq = inb(INTDEF + base) & 0x0F;
-      switch (irq)
+    switch (temp_p->type & 0x1ff1)
+    {
+      case AHC_AIC7770:
+        temp_p->board_name_index = 2;
+      case AHC_274:
       {
-        case 9:
-        case 10:
-        case 11:
-        case 12:
-        case 14:
-        case 15:
-          break;
-
-        default:
-          printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
-          "level %d, ignoring.\n", irq);
-          irq = 0;
-          aic7xxx_free(p);
-          break;
-      }
+        temp_p->bios_control = aic_inb(temp_p, HA_274_BIOSCTRL);
 
-      if (irq != 0)
-      {
-        p->irq = irq & 0x0F;
-        p->chip_class = AIC_777x;
-#ifdef AIC7XXX_PAGE_ENABLE
-        p->flags |= PAGE_ENABLED;
-#endif
-        p->instance = found;
-        if (aic7xxx_extended)
+        /*
+         * 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 (temp_p->board_name_index == 0)
+          temp_p->board_name_index = 3;
+        if (temp_p->bios_control & CHANNEL_B_PRIMARY)
         {
-          p->flags |= EXTENDED_TRANSLATION;
+          temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
         }
 
-        switch (p->chip_type)
+        if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
+        {
+          temp_p->flags |= AHC_USEDEFAULTS;
+          temp_p->flags &= ~AHC_BIOS_ENABLED;
+        }
+        else
         {
-          case AIC_7770:
-          case AIC_7771:
+          temp_p->flags &= ~AHC_USEDEFAULTS;
+          temp_p->flags |= AHC_BIOS_ENABLED;
+          if ( (temp_p->bios_control & 0x20) == 0 )
           {
-            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)
+            switch(temp_p->bios_control & 0x07)
             {
-              p->flags |= USE_DEFAULTS;
+              case 0x0:
+                temp_p->bios_address = 0xcc000;
+                break;
+              case 0x1:
+                temp_p->bios_address = 0xd0000;
+                break;
+              case 0x2:
+                temp_p->bios_address = 0xd4000;
+                break;
+              case 0x3:
+                temp_p->bios_address = 0xd8000;
+                break;
+              case 0x4:
+                temp_p->bios_address = 0xdc000;
+                break;
+              case 0x5:
+                temp_p->bios_address = 0xe0000;
+                break;
+              case 0x6:
+                temp_p->bios_address = 0xe4000;
+                break;
+              case 0x7:
+                temp_p->bios_address = 0xe8000;
+                break;
+              default:
+                break; /* can't get here */
             }
-            break;
           }
-
-          case AIC_284x:
-            if (!load_seeprom(p, &sxfrctl1))
+          else
+          {
+            switch(temp_p->bios_control & 0x06)
             {
-              if (aic7xxx_verbose)
-                printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+              case 0x0:
+                temp_p->bios_address = 0xd0000;
+                break;
+              case 0x2:
+                temp_p->bios_address = 0xd8000;
+                break;
+              case 0x4:
+                temp_p->bios_address = 0xe0000;
+                break;
+              case 0x6:
+                temp_p->bios_address = 0xe8000;
+                break;
+              default:
+                break; /* can't get here */
             }
-            break;
-
-          default:  /* Won't get here. */
-            break;
+          }
         }
-        printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, 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)
+        temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
+        temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
+        if (temp_p->flags & AHC_USEDEFAULTS)
         {
-          /*
-           * We detected a Rev E board, we allow paging on this board.
-           */
-          printk("Revision >= E\n");
-          outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+          temp_p->scsi_id = temp_p->scsi_id_b = 7;
+          temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
         }
         else
         {
-          /* Do not allow paging. */
-          p->flags &= ~PAGE_ENABLED;
-          printk("Revision <= C\n");
+          if ( ((temp_p->adapter_control >> 8) & TERM_ENB) != 0 )
+            temp_p->flags |= AHC_TERM_ENB_A;
+          if ( (temp_p->adapter_control & TERM_ENB) != 0 )
+            temp_p->flags |= AHC_TERM_ENB_B;
+          temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
+          temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
         }
+        break;
+      }
 
-        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))
+      case AHC_284:
+        load_seeprom(temp_p, &sxfrctl1);
+        temp_p->board_name_index = 4;
+        switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
         {
-          /*
-           * We successfully found a board and registered it.
-           */
-          found = found + 1;
-        }
-        else
-        {
-          /*
-           * Something went wrong; release and free all resources.
-           */
-          aic7xxx_free(p);
+          case 0x00:
+            temp_p->bios_address = 0xe0000;
+            break;
+          case 0x20:
+            temp_p->bios_address = 0xc8000;
+            break;
+          case 0x40:
+            temp_p->bios_address = 0xd0000;
+            break;
+          case 0x60:
+            temp_p->bios_address = 0xd8000;
+            break;
+          default:
+            break; /* can't get here */
         }
-      }
+        break;
+
+      default:  /* Won't get here. */
+        break;
+    }
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {
+      printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n",
+        (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base,
+        temp_p->irq,
+        (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+             (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis");
     }
+
+    /*
+     * Set the FIFO threshold and the bus off time.
+     */
+    hostconf = aic_inb(temp_p, HOSTCONF);
+    aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD);
+    aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME);
+    slot++;
+    found++;
   }
-#endif /* __sparc_v9__ or __powerpc__ */
+
+#endif /* defined(__i386__) || defined(__alpha__) */
 
 #ifdef CONFIG_PCI
   /*
    * PCI-bus probe.
    */
-  if (pci_present())
+  if (pcibios_present())
   {
     struct
     {
       unsigned short      vendor_id;
       unsigned short      device_id;
-      aha_chip_type       chip_type;
-      aha_chip_class_type chip_class;
+      ahc_type            type;
+      ahc_flag_type       flags;
+      int                 board_name_index;
     } 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_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},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AIC_7873, AIC_787x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AIC_7874, AIC_787x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AIC_7880, AIC_788x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AIC_7881, AIC_788x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AIC_7882, AIC_788x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AIC_7883, AIC_788x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
+       AHC_FNONE,                                            1 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
+       AHC_PAGESCBS | AHC_USEDEFAULTS,                       5 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
+       AHC_PAGESCBS | AHC_USEDEFAULTS,                       6 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,  7 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_294AU,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,  8 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                      9 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_294,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     10 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_394,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     11 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_398,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     12 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_294,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     13 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     14 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_294U,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     15 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_394U,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     16 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_398U,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     17 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_294U,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED,                     18 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED, 19 }
     };
 
-    int error, flags;
-    unsigned short index = 0;
-    unsigned char ultra_enb = 0;
-    unsigned int  devconfig, class_revid;
-    scb_data_type *shared_scb_data = NULL;
-    char rev_id[] = {'B', 'C', 'D'};
+    unsigned short command;
+    unsigned int  devconfig, i;
+#ifdef MMAPIO
+    unsigned long page_offset;
+#endif
+    struct aic7xxx_host *first_7895 = NULL;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
     struct pci_dev *pdev = NULL;
-    unsigned long iobase, mbase;
-    unsigned int irq;
+#else
+    int index;
+    unsigned int piobase, mmapbase;
+    unsigned char pci_bus, pci_devfn;
+#endif
 
     for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
+    {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+      pdev = NULL;
       while ((pdev = pci_find_device(aic7xxx_pci_devices[i].vendor_id,
                                      aic7xxx_pci_devices[i].device_id,
-				    pdev)))
+                                     pdev)))
+#else
+      index = 0;
+      while (!(pcibios_find_device(aic7xxx_pci_devices[i].vendor_id,
+                                   aic7xxx_pci_devices[i].device_id,
+                                   index++, &pci_bus, &pci_devfn)) )
+#endif
       {
-        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)
+        if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
         {
-          case AIC_7855:
-            flags |= USE_DEFAULTS;
-            break;
-
-          case AIC_7872:  /* 3940 */
-          case AIC_7882:  /* 3940-Ultra */
-            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 */
-            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;
-
-          default:
-            break;
+          if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
+          {
+            printk(KERN_INFO "aic7xxx: The 7810 RAID controller is not "
+              "supported by\n");
+            printk(KERN_INFO "         this driver, we are ignoring it.\n");
+          }
         }
+        else if ( (temp_p = kmalloc(sizeof(struct aic7xxx_host),
+                                    GFP_ATOMIC)) != NULL )
+        {
+          memset(temp_p, 0, sizeof(struct aic7xxx_host));
+          temp_p->type = aic7xxx_pci_devices[i].type;
+          temp_p->flags = aic7xxx_pci_devices[i].flags;
+          temp_p->board_name_index = aic7xxx_pci_devices[i].board_name_index;
 
-        /*
-         * Read sundry information from PCI BIOS.
-         */
-	iobase = pdev->base_address[0];
-	mbase = pdev->base_address[1];
-	irq = pdev->irq;
-        error = pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
-        error += pci_read_config_dword(pdev, CLASS_PROGIF_REVID, &class_revid);
-        printk("aic7xxx: <%s> at PCI %d\n",
-               board_names[chip_type], PCI_SLOT(pdev->devfn));
+          /*
+           * Read sundry information from PCI BIOS.
+           */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+          temp_p->irq = pdev->irq;
+          temp_p->pci_bus = pdev->bus->number;
+          temp_p->pci_device_fn = pdev->devfn;
+          temp_p->base = pdev->base_address[0];
+          temp_p->mbase = pdev->base_address[1];
+          pci_read_config_word(pdev, PCI_COMMAND, &command);
+          pci_write_config_word(pdev, PCI_COMMAND,
+             command | PCI_COMMAND_MASTER |
+             PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+#else
+          temp_p->pci_bus = pci_bus;
+          temp_p->pci_device_fn = pci_devfn;
+          pcibios_read_config_byte(pci_bus, pci_devfn, PCI_INTERRUPT_LINE,
+            &temp_p->irq);
+          pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_0,
+            &piobase);
+          temp_p->base = piobase;
+          pcibios_read_config_dword(pci_bus, pci_devfn, PCI_BASE_ADDRESS_1,
+            &mmapbase);
+          temp_p->mbase = mmapbase;
+          pcibios_read_config_word(pci_bus, pci_devfn, PCI_COMMAND, &command);
+          pcibios_write_config_word(pci_bus, pci_devfn, PCI_COMMAND,
+            command | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY |
+            PCI_COMMAND_IO);
+#endif
+
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic7xxx_pci_devices[i].board_name_index],
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
 
-        /*
-         * 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) {
-          unsigned short pci_command;
-
-          /* Enable bus mastering since this thing must do DMA. */
-          pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
-          pci_command |= PCI_COMMAND_MASTER;
-#ifdef __powerpc__
-          /* Enable I/O and memory-space access */
-          pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+          /*
+           * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
+           * we mask it off.
+           */
+          temp_p->base &= PCI_BASE_ADDRESS_IO_MASK;
+          temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
+          temp_p->unpause = (aic_inb(temp_p, HCNTRL) & IRQMS) | INTEN;
+          temp_p->pause = temp_p->unpause | PAUSE;
+
+#ifdef MMAPIO
+          base = temp_p->mbase & PAGE_MASK;
+          page_offset = temp_p->mbase - base;
+          /*
+           * replace the next line with this one if you are using 2.1.x:
+           * temp_p->maddr = ioremap(base, page_offset + 256);
+           */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+          temp_p->maddr = ioremap(base, page_offset + 256);
+#else
+          temp_p->maddr = vremap(base, page_offset + 256);
 #endif
-          pci_write_config_word(pdev, PCI_COMMAND, pci_command);
-        } else {
-          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;
+          if(temp_p->maddr)
+          {
+            temp_p->maddr += page_offset;
+          }
 #endif
-        p->instance = found;
+
+          aic_outb(temp_p, temp_p->pause, HCNTRL);
+          while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+
+          temp_p->bios_address = 0;
 
           /*
            * Remember how the card was setup in case there is no seeprom.
            */
-          p->scsi_id = inb(p->base + SCSIID) & OID;
-          if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+          temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
+          /*
+           * Get current termination setting
+           */
+          sxfrctl1 = aic_inb(temp_p, SXFRCTL1) & STPWEN;
+
+          if (aic7xxx_chip_reset(temp_p) == -1)
           {
-            p->flags |= ULTRA_ENABLED;
-            ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
           }
-	  sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
 
-          aic7xxx_chip_reset(p);
-
-#ifdef AIC7XXX_USE_EXT_SCBRAM
-          if (devconfig & RAMPSM)
+          switch (temp_p->type & 0x1ff1)
           {
-            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
-                   "access.\n");
-            /*
-             * XXX - Assume 9 bit SRAM and enable parity checking.
-             */
-            devconfig |= EXTSCBPEN;
+            case AHC_394:   /* 3940 */
+            case AHC_394U:  /* 3940-Ultra */
+              temp_p->flags |= AHC_MULTI_CHANNEL;
+              switch(PCI_SLOT(temp_p->pci_device_fn))
+              {
+                case 5:
+                  temp_p->flags |= AHC_CHNLB;
+                  break;
+                default:
+                  break;
+              }
+              break;
 
-            /*
-             * 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);
-          }
+            case AHC_398:   /* 3985 */
+            case AHC_398U:  /* 3985-Ultra */
+              temp_p->flags |= AHC_MULTI_CHANNEL;
+              switch(PCI_SLOT(temp_p->pci_device_fn))
+              {
+                case 8:
+                  temp_p->flags |= AHC_CHNLB;
+                  break;
+                case 12:
+                  temp_p->flags |= AHC_CHNLC;
+                  break;
+                default:
+                  break;
+              }
+              break;
+
+            case AHC_AIC7895:
+              temp_p->flags |= AHC_MULTI_CHANNEL;
+              if (PCI_FUNC(temp_p->pci_device_fn) != 0)
+              {
+                temp_p->flags |= AHC_CHNLB;
+              }
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
+              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+              devconfig |= SCBSIZE32;
+              pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#else
+              pcibios_read_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                &devconfig);
+              devconfig |= SCBSIZE32;
+              pcibios_write_config_dword(pci_bus, pci_devfn, DEVCONFIG,
+                devconfig);
 #endif
+              if (aic7xxx_7895_irq_hack != -1)
+              {
+                if (first_7895 == NULL)
+                {
+                  printk(KERN_INFO "aic7xxx: Using 7895_irq_hack.  Please "
+                    "upgrade your motherboard BIOS\n");
+                  first_7895 = temp_p;
+                }
+                else if (aic7xxx_7895_irq_hack == 0)
+                {
+                  if (temp_p->flags & AHC_CHNLB)
+                    temp_p->irq = first_7895->irq;
+                  else
+                    first_7895->irq = temp_p->irq;
+                  first_7895 = NULL;
+                }
+                else
+                {
+                  if ( !(temp_p->flags & AHC_CHNLB) )
+                    temp_p->irq = first_7895->irq;
+                  else
+                    first_7895->irq = temp_p->irq;
+                  first_7895 = NULL;
+                }
+              }
+              break;
+            default:
+              break;
+          }
+
+          /*
+           * Loading of the SEEPROM needs to come after we've set the flags
+           * to indicate possible CHNLB and CHNLC assigments.  Otherwise,
+           * on 394x and 398x cards we'll end up reading the wrong settings
+           * for channels B and C
+           */
+          if ( !(load_seeprom(temp_p, &sxfrctl1)) )
+          {
+            temp_p->flags |= AHC_USEDEFAULTS;
+            if (sxfrctl1 & STPWEN)
+              temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
+            temp_p->scsi_id = temp_p->scsi_id_b = 7;
+          }
 
-          if ((p->flags & USE_DEFAULTS) == 0)
+          /*
+           * and then we need another switch based on the type in order to
+           * make sure the channel B primary flag is set properly on 7895
+           * controllers....Arrrgggghhh!!!
+           */
+          switch(temp_p->type & 0x1ff1)
           {
-            load_seeprom(p, &sxfrctl1);
+            case AHC_AIC7895:
+              current_p = list_p;
+              while(current_p != NULL)
+              {
+                if ( (current_p->pci_bus == temp_p->pci_bus) &&
+                     (PCI_SLOT(current_p->pci_device_fn) ==
+                      PCI_SLOT(temp_p->pci_device_fn)) )
+                {
+                  if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
+                    temp_p->flags |= 
+                      (current_p->flags & AHC_CHANNEL_B_PRIMARY);
+                  else
+                    current_p->flags |=
+                      (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
+                }
+                current_p = current_p->next;
+              }
+              break;
+            default:
+              break;
           }
 
           /*
            * Take the LED out of diagnostic mode
            */
-          sblkctl = inb(p->base + SBLKCTL);
-          outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+          aic_outb(temp_p, 
+            (aic_inb(temp_p, SBLKCTL) & ~(DIAGLEDEN | DIAGLEDON)),
+            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);
+          aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
 
-          if (p->flags & USE_DEFAULTS)
+          if (temp_p->flags & AHC_USEDEFAULTS)
           {
             int j;
+            unsigned char k;
             /*
              * Default setup; should only be used if the adapter does
              * not have a SEEPROM.
@@ -6061,115 +7541,315 @@
              */
             for (j = TARG_SCRATCH; j < 0x60; j++)
             {
-              if (inb(p->base + j) != 0x00)      /* Check for all zeroes. */
+              k = aic_inb(temp_p, j);
+              /* Check for all zeros and ones.  Break out if we pass */
+              if( (k != 0x00) && (k != 0xff) )
                 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))
+            /* If j makes it to 0x60, then all entries are either 0x00 or
+             * 0xff.  We would then assume we have *not* been initialized
+             * and drop through here.  OTOH, if even one entry is inited,
+             * then as long as we appear to have a valid SCSI ID, we'll use
+             * the leftover BIOS values.
+             */
+            if ((j != 0x60) && (temp_p->scsi_id != 0))
             {
-              p->flags &= ~USE_DEFAULTS;
-              if (aic7xxx_verbose)
+              temp_p->flags &= ~AHC_USEDEFAULTS;
+              if (aic7xxx_verbose & VERBOSE_PROBE2)
               {
                 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.
                */
+              temp_p->flags &= ~AHC_BIOS_ENABLED;
+              temp_p->flags |= AHC_TERM_ENB_A | AHC_TERM_ENB_B;
               sxfrctl1 = STPWEN;
-              p->scsi_id = 7;
+              temp_p->scsi_id = 7;
             }
-            outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
-                 p->base + SCSICONF);
+            aic_outb(temp_p, (temp_p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+                 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;
-            }
+            aic_outb(temp_p, temp_p->scsi_id, SCSICONF + 1);
           }
-
-          /*
-           * Print some additional information about the adapter.
-           */
-          printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, "
-                 "IO Mem 0x%lx, IRQ %x",
-                 (p->flags & USE_DEFAULTS) ? "dis" : "en",
-                 p->base, p->mbase, p->irq);
-          if ((class_revid & DEVREVID) < 3)
+          else /* not using defaults */
           {
-            printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+            if (sxfrctl1 & STPWEN)
+              temp_p->flags |= AHC_TERM_ENB_A;
           }
-          printk("\n");
 
           if (aic7xxx_extended)
-            p->flags |= EXTENDED_TRANSLATION;
-
-          if (aic7xxx_verbose)
-            printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-                   (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+            temp_p->flags |= AHC_EXTEND_TRANS_A;
 
           /*
            * 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)
+          sxfrctl1 |= aic_inb(temp_p, SXFRCTL1);
+          aic_outb(temp_p, sxfrctl1, SXFRCTL1);
+          if ( list_p == NULL )
           {
-            aic7xxx_free(p);
+            list_p = current_p = temp_p;
           }
           else
           {
-            found = found + 1;
+            current_p = list_p;
+            while(current_p->next != NULL)
+              current_p = current_p->next;
+            current_p->next = temp_p;
+          }
+          temp_p->next = NULL;
+          found++;
+        }  /* Found an Adaptec PCI device. */
+        else /* Well, we found one, but we couldn't get any memory */
+        {
+          printk("aic7xxx: Found <%s>\n", 
+            board_names[aic7xxx_pci_devices[i].board_name_index]);
+          printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
+            "skipping.\n");
+        }
+      } /* while(pdev=....) */
+    } /* for PCI_DEVICES */
+  } /* PCI BIOS present */
+#endif CONFIG_PCI
+  /*
+   * Now, we re-order the probed devices by BIOS address and BUS class.
+   * In general, we follow this algorithm to make the adapters show up
+   * in the same order under linux that the computer finds them.
+   *  1: All VLB/EISA cards with BIOS_ENABLED first, according to BIOS
+   *     address, going from lowest to highest.
+   *  2: All PCI controllers with BIOS_ENABLED next, according to BIOS
+   *     address, going from lowest to highest.
+   *  3: Remaining VLB/EISA controllers going in slot order.
+   *  4: Remaining PCI controllers, going in PCI device order (reversable)
+   */
 
-#ifdef AIC7XXX_USE_EXT_SCBRAM
+  {
+    struct aic7xxx_host *vlb_enab, *vlb_disab, *pci;
+    struct aic7xxx_host *prev_p;
+    struct aic7xxx_host *p;
+    unsigned char left;
+
+    prev_p = vlb_enab = vlb_disab = pci = NULL;
+
+    temp_p = list_p;
+    while (temp_p != NULL)
+    {
+      switch(temp_p->type)
+      {
+        case AHC_AIC7770:
+        case AHC_274:
+        case AHC_284:
+          if (temp_p->flags & AHC_BIOS_ENABLED)
+          {
+            if (vlb_enab == NULL)
+            {
+              vlb_enab = temp_p;
+              temp_p = temp_p->next;
+              vlb_enab->next = NULL;
+            }
+            else
+            {
+              current_p = vlb_enab;
+              prev_p = NULL;
+              while ( (current_p != NULL) &&
+                      (current_p->bios_address < temp_p->bios_address))
+              {
+                prev_p = current_p;
+                current_p = current_p->next;
+              }
+              if (prev_p != NULL)
+              {
+                prev_p->next = temp_p;
+                temp_p = temp_p->next;
+                prev_p->next->next = current_p;
+              }
+              else
+              {
+                vlb_enab = temp_p;
+                temp_p = temp_p->next;
+                vlb_enab->next = current_p;
+              }
+            }
+          }
+          else
+          {
+            if (vlb_disab == NULL)
+            {
+              vlb_disab = temp_p;
+              temp_p = temp_p->next;
+              vlb_disab->next = NULL;
+            }
+            else
+            {
+              current_p = vlb_disab;
+              prev_p = NULL;
+              while ( (current_p != NULL) &&
+                      (current_p->base < temp_p->base))
+              {
+                prev_p = current_p;
+                current_p = current_p->next;
+              }
+              if (prev_p != NULL)
+              {
+                prev_p->next = temp_p;
+                temp_p = temp_p->next;
+                prev_p->next->next = current_p;
+              }
+              else
+              {
+                vlb_disab = temp_p;
+                temp_p = temp_p->next;
+                vlb_disab->next = current_p;
+              }
+            }
+          }
+          break;
+        default:  /* All PCI controllers fall through to default */
+          if (pci == NULL)
+          {
+            pci = temp_p;
+            temp_p = temp_p->next;
+            pci->next = NULL;
+          }
+          else
+          {
+            current_p = pci;
+            prev_p = NULL;
+            if (!aic7xxx_reverse_scan)
+            {
+              while ( (current_p != NULL) &&
+                      ( (PCI_SLOT(current_p->pci_device_fn) |
+                        (current_p->pci_bus << 8)) < 
+                        (PCI_SLOT(temp_p->pci_device_fn) |
+                        (temp_p->pci_bus << 8)) ) )
+              {
+                prev_p = current_p;
+                current_p = current_p->next;
+              }
+            }
+            else
+            {
+              while ( (current_p != NULL) &&
+                      ( (PCI_SLOT(current_p->pci_device_fn) |
+                        (current_p->pci_bus << 8)) > 
+                        (PCI_SLOT(temp_p->pci_device_fn) |
+                        (temp_p->pci_bus << 8)) ) )
+              {
+                prev_p = current_p;
+                current_p = current_p->next;
+              }
+            }
             /*
-             * Set the shared SCB data once we've successfully probed a
-             * 398x adapter.
-             *
-             * Note that we can only do this if the use of external
-             * SCB RAM is enabled.
+             * Are we dealing with a 7985 where we need to sort the
+             * channels as well, if so, the bios_address values should
+             * be the same
              */
-            if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+            if ( (current_p) && (temp_p->flags & AHC_MULTI_CHANNEL) &&
+                 (temp_p->pci_bus == current_p->pci_bus) &&
+                 (PCI_SLOT(temp_p->pci_device_fn) ==
+                  PCI_SLOT(current_p->pci_device_fn)) )
             {
-              if (shared_scb_data == NULL)
+              if (temp_p->flags & AHC_CHNLB)
               {
-                shared_scb_data = p->scb_data;
+                if ( !(temp_p->flags & AHC_CHANNEL_B_PRIMARY) )
+                {
+                  prev_p = current_p;
+                  current_p = current_p->next;
+                }
+              }
+              else
+              {
+                if (temp_p->flags & AHC_CHANNEL_B_PRIMARY)
+                {
+                  prev_p = current_p;
+                  current_p = current_p->next;
+                }
               }
             }
-#endif
+            if (prev_p != NULL)
+            {
+              prev_p->next = temp_p;
+              temp_p = temp_p->next;
+              prev_p->next->next = current_p;
+            }
+            else
+            {
+              pci = temp_p;
+              temp_p = temp_p->next;
+              pci->next = current_p;
+            }
           }
-
-          index++;
-      }  /* Found an Adaptec PCI device. */
+          break;
+      }  /* End of switch(temp_p->type) */
+    } /* End of while (temp_p != NULL) */
+    /*
+     * At this point, the cards have been broken into 4 sorted lists, now
+     * we run through the lists in order and register each controller
+     */
+    left = found;
+    temp_p = vlb_enab;
+    while(temp_p != NULL)
+    {
+      template->name = board_names[temp_p->board_name_index];
+      p = aic7xxx_alloc(template, temp_p);
+      if (p != NULL)
+      {
+        p->instance = found - left;
+        if (aic7xxx_register(template, p, (--left)) == 0)
+        {
+          found--;
+          aic7xxx_free(p);
+        }
+      }
+      current_p = temp_p;
+      temp_p = (struct aic7xxx_host *)temp_p->next;
+      kfree(current_p);
+    }
+    temp_p = pci;
+    while(temp_p != NULL)
+    {
+      template->name = board_names[temp_p->board_name_index];
+      p = aic7xxx_alloc(template, temp_p);
+      if (p != NULL)
+      {
+        p->instance = found - left;
+        if (aic7xxx_register(template, p, (--left)) == 0)
+        {
+          found--;
+          aic7xxx_free(p);
+        }
+      }
+      current_p = temp_p;
+      temp_p = (struct aic7xxx_host *)temp_p->next;
+      kfree(current_p);
+    }
+    temp_p = vlb_disab;
+    while(temp_p != NULL)
+    {
+      template->name = board_names[temp_p->board_name_index];
+      p = aic7xxx_alloc(template, temp_p);
+      if (p != NULL)
+      {
+        p->instance = found - left;
+        if (aic7xxx_register(template, p, (--left)) == 0)
+        {
+          found--;
+          aic7xxx_free(p);
+        }
+      }
+      current_p = temp_p;
+      temp_p = (struct aic7xxx_host *)temp_p->next;
+      kfree(current_p);
+    }
   }
-#endif CONFIG_PCI
-
   return (found);
 }
 
-
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_buildscb
@@ -6191,57 +7871,61 @@
    * Setup the control byte if we need negotiation and have not
    * already requested it.
    */
+  hscb->control = 0;
+  scb->tag_action = 0;
   if (p->discenable & mask)
   {
     hscb->control |= DISCENB;
-#ifdef AIC7XXX_TAGGED_QUEUEING
-    if (cmd->device->tagged_queue)
+    if (p->tagenable & mask)
     {
       cmd->tag = hscb->tag;
-      p->device_status[TARGET_INDEX(cmd)].commands_sent++;
-      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+      p->dev_commands_sent[TARGET_INDEX(cmd)]++;
+      if (p->dev_commands_sent[TARGET_INDEX(cmd)] < 200)
       {
         hscb->control |= MSG_SIMPLE_Q_TAG;
+        scb->tag_action = MSG_SIMPLE_Q_TAG;
       }
       else
       {
-        hscb->control |= MSG_ORDERED_Q_TAG;
-        p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+        if (p->orderedtag & mask)
+        {
+          hscb->control |= MSG_ORDERED_Q_TAG;
+          scb->tag_action = MSG_ORDERED_Q_TAG;
+        }
+        else
+        {
+          hscb->control |= MSG_SIMPLE_Q_TAG;
+          scb->tag_action = MSG_SIMPLE_Q_TAG;
+        }
+        p->dev_commands_sent[TARGET_INDEX(cmd)] = 0;
       }
     }
-#endif  /* Tagged queueing */
   }
-
-  if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
+  if ( (p->needwdtr & mask) &&
+      !(p->wdtr_pending & mask) &&
+      !(scb->tag_action))
   {
     p->wdtr_pending |= mask;
     hscb->control |= MK_MESSAGE;
-    scb->flags |= SCB_MSGOUT_WDTR;
-#if 0
-    printk("scsi%d: Sending WDTR request to target %d.\n",
-           p->host_no, cmd->target);
-#endif
+    if (p->needwdtr_copy & mask)
+      scb->flags |= SCB_MSGOUT_WDTR_16BIT;
+    else
+      scb->flags |= SCB_MSGOUT_WDTR_8BIT;
   }
   else
   {
-    if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
+    if ( (p->needsdtr & mask) &&
+        !(p->sdtr_pending & mask) &&
+        !(p->wdtr_pending & mask) &&
+        !(scb->tag_action) )
     {
       p->sdtr_pending |= mask;
       hscb->control |= MK_MESSAGE;
       scb->flags |= SCB_MSGOUT_SDTR;
-#if 0
-      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
   hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
-	((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+        ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
 
   /*
    * The interpretation of request_buffer and request_bufflen
@@ -6253,8 +7937,6 @@
   /*
    * XXX - this relies on the host data being stored in a
    *       little-endian format.
-   *
-   * No longer is that an issue, I've "big-endian'ified" this driver. -DaveM
    */
   hscb->SCSI_cmd_length = cmd->cmd_len;
   hscb->SCSI_cmd_pointer = cpu_to_le32(VIRT_TO_BUS(cmd->cmnd));
@@ -6272,46 +7954,42 @@
     int i;
 
     sg = (struct scatterlist *)cmd->request_buffer;
+    scb->sg_length = 0;
     for (i = 0; i < cmd->use_sg; i++)
     {
       scb->sg_list[i].address = cpu_to_le32(VIRT_TO_BUS(sg[i].address));
-      scb->sg_list[i].length = cpu_to_le32((unsigned int) sg[i].length);
+      scb->sg_list[i].length = cpu_to_le32(sg[i].length);
+      scb->sg_length += sg[i].length;
     }
     hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(scb->sg_list));
     hscb->SG_segment_count = cmd->use_sg;
-    scb->sg_count = hscb->SG_segment_count;
+    scb->sg_count = cmd->use_sg;
 
     /* 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 | cpu_to_le32(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), le32_to_cpu(hscb->data_count));
-#endif
+    hscb->data_count = scb->sg_list[0].length;
   }
   else
   {
-#if 0
-  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)
     {
-      hscb->SG_segment_count = 1;
       scb->sg_count = 1;
       scb->sg_list[0].address = cpu_to_le32(VIRT_TO_BUS(cmd->request_buffer));
       scb->sg_list[0].length = cpu_to_le32(cmd->request_bufflen);
+      scb->sg_length = cmd->request_bufflen;
+      hscb->SG_segment_count = 1;
       hscb->SG_list_pointer = cpu_to_le32(VIRT_TO_BUS(&scb->sg_list[0]));
-      hscb->data_count = scb->sg_list[0].length | cpu_to_le32(SCB_LIST_NULL << 24);
-      hscb->data_pointer = cpu_to_le32(VIRT_TO_BUS(cmd->request_buffer));
+      hscb->data_count = scb->sg_list[0].length;
+      hscb->data_pointer = scb->sg_list[0].address;
     }
     else
     {
-      hscb->SG_segment_count = 0;
       scb->sg_count = 0;
+      scb->sg_length = 0;
+      hscb->SG_segment_count = 0;
       hscb->SG_list_pointer = 0;
+      hscb->data_count = 0;
       hscb->data_pointer = 0;
-      hscb->data_count = cpu_to_le32(SCB_LIST_NULL << 24);
     }
   }
 }
@@ -6326,58 +8004,53 @@
 int
 aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
 {
-  long processor_flags;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
+  int tindex = TARGET_INDEX(cmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  if (p->host != cmd->host)
-  {
-    printk(KERN_INFO "scsi%d: Internal host structure != scsi.c host "
-      "structure.\n", p->host_no);
-  }
-
   /*
    * Check to see if channel was scanned.
    */
-  if (!(p->flags & A_SCANNED) && (cmd->channel == 0))
+  if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
   {
-    printk(KERN_INFO "scsi%d: Scanning channel A for devices.\n", p->host_no);
-    p->flags |= A_SCANNED;
+    printk(INFO_LEAD "Scanning channel for devices.\n",
+      p->host_no, 0, -1, -1);
+    p->flags |= AHC_A_SCANNED;
   }
   else
   {
-    if (!(p->flags & B_SCANNED) && (cmd->channel == 1))
+    if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1))
     {
-      printk(KERN_INFO "scsi%d: Scanning channel B for devices.\n", p->host_no);
-      p->flags |= B_SCANNED;
+      printk(INFO_LEAD "Scanning channel for devices.\n",
+        p->host_no, 1, -1, -1);
+      p->flags |= AHC_B_SCANNED;
     }
   }
 
-#if 0
-  printk("aic7xxx: (queue) cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n",
-	cmd->cmnd[0], cmd->cmd_len, cmd->target, cmd->channel,
-	cmd->lun & 0x07);
-#endif
-
-  if (p->device_status[TARGET_INDEX(cmd)].active_cmds
-      > cmd->device->queue_depth)
+  if (p->dev_active_cmds[tindex] > cmd->device->queue_depth)
   {
-    printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
-           p->host_no, cmd->target, cmd->channel);
+    printk(WARN_LEAD "Commands queued exceeds queue "
+           "depth, active=%d\n",
+           p->host_no, CTL_OF_CMD(cmd), 
+           p->dev_active_cmds[tindex]);
+    if ( p->dev_active_cmds[tindex] > 220 )
+      p->dev_active_cmds[tindex] = 0;
   }
-  scb = aic7xxx_allocate_scb(p);
+  DRIVER_LOCK
+  scb = aic7xxx_allocate_scb(p, FALSE);
+  DRIVER_UNLOCK
   if (scb == NULL)
   {
-    panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
+    panic("(scsi%d) aic7xxx_queue:Couldn't get a free SCB.\n", p->host_no);
   }
   else
   {
     scb->cmd = cmd;
     aic7xxx_position(cmd) = scb->hscb->tag;
-#if 0
-    debug_scb(scb);
-#endif;
 
     /*
      * Construct the SCB beforehand, so the sequencer is
@@ -6385,17 +8058,6 @@
      */
     aic7xxx_buildscb(p, cmd, scb);
 
-#if 0
-    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->hscb->tag, (unsigned int) scb->cmd,
-	   scb->flags, (unsigned int) p->free_scb);
-#endif
-
     /*
      * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
      * is set up properly, and the parity error flag is reset, then send
@@ -6410,19 +8072,20 @@
 
     scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
-    save_flags(processor_flags);
-    cli();
-    scbq_insert_tail(&p->waiting_scbs, scb);
-    if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
+    DRIVER_LOCK
+    if (p->delayed_scbs[tindex].head != NULL) 
+    {
+        scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+    }
+    else
+    {
+        scbq_insert_tail(&p->waiting_scbs, scb);
+    }
+    if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
     {
       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->hscb->tag);
-#endif;
+    DRIVER_UNLOCK
   }
   return (0);
 }
@@ -6443,103 +8106,58 @@
 {
   struct aic7xxx_scb   *scb;
   struct aic7xxx_hwscb *hscb;
-  unsigned char bus_state;
   int result = -1;
-  char channel;
+  int channel;
+  unsigned char saved_scbptr, lastphase;
+  unsigned char hscb_index;
+  int disconnected;
 
   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 *) p, (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);
-  }
-
-
-  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)
+  lastphase = aic_inb(p, LASTPHASE);
+  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
   {
-    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(INFO_LEAD "Bus Device reset, scb flags 0x%x, ",
+         p->host_no, CTL_OF_SCB(scb), scb->flags);
+    switch (lastphase)
+    {
+      case P_DATAOUT:
+        printk("Data-Out phase\n");
+        break;
+      case P_DATAIN:
+        printk("Data-In phase\n");
+        break;
+      case P_COMMAND:
+        printk("Command phase\n");
+        break;
+      case P_MESGOUT:
+        printk("Message-Out phase\n");
+        break;
+      case P_STATUS:
+        printk("Status phase\n");
+        break;
+      case P_MESGIN:
+        printk("Message-In phase\n");
+        break;
+      default:
       /*
        * We're not in a valid phase, so assume we're idle.
        */
-      printk("while idle, LASTPHASE = 0x%x, ", bus_state);
-      break;
+        printk("while idle, LASTPHASE = 0x%x\n", lastphase);
+        break;
+    }
+    printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
+         "0x%x\n", p->host_no, CTL_OF_SCB(scb),
+         aic_inb(p, SCSISIGI),
+         aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+         aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
   }
-  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));
 
-  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)
-    {
-      /*
-       * We could be starving this command; try sending and ordered tag
-       * command to the target we come from.
-       */
-      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;
+  channel = cmd->channel;
 
     /*
-     * Send an Abort Message:
+     * Send a Device Reset 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
@@ -6550,154 +8168,155 @@
      * 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];
+  saved_scbptr = aic_inb(p, SCBPTR);
+  disconnected = FALSE;
 
-    if (bus_state != P_BUSFREE)
+  if (lastphase != P_BUSFREE)
+  {
+    if (aic_inb(p, SCB_TAG) >= p->scb_data->numscbs)
     {
-      if (active_scb_index >= p->scb_data->numscbs)
-      {
-        /*
-         * 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);
-      }
-      else
-      {
-        /* 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)
-        {
-          /*
-           * 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.
-           */
-          result = SCSI_RESET_PENDING;
-          aic7xxx_error(active_scb->cmd) = DID_RESET;
-        }
-        else
-        {
-          aic7xxx_error(scb->cmd) = DID_RESET;
-          result = SCSI_RESET_PENDING;
-        }
-        unpause_sequencer(p, /* unpause_always */ TRUE);
-      }
+      printk(WARN_LEAD "Invalid SCB ID %d is active, "
+             "SCB flags = 0x%x.\n", p->host_no,
+            CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
+      return(SCSI_RESET_ERROR);
     }
-    else
-    {
-      unsigned char hscb_index, linked_next;
-      int disconnected;
-
-      disconnected = FALSE;
-      hscb_index = aic7xxx_find_scb(p, scb);
-      if (hscb_index == SCB_LIST_NULL)
+    if (scb->hscb->tag == aic_inb(p, SCB_TAG))
+    { 
+      if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
       {
-        disconnected = TRUE;
-        linked_next = (le32_to_cpu(scb->hscb->data_count) >> 24) & 0xFF;
+        /* Send the abort message to the active SCB. */
+        aic_outb(p, MSG_BUS_DEV_RESET, MSG_OUT);
+        aic_outb(p, lastphase | ATNO, SCSISIGO);
+        if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+          printk(INFO_LEAD "Device reset message in "
+                "message buffer\n", p->host_no, CTL_OF_SCB(scb));
+        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+        aic7xxx_error(scb->cmd) = DID_RESET;
+        p->dev_flags[TARGET_INDEX(scb->cmd)] &= 
+                ~DEVICE_SUCCESS;
+        p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
+                BUS_DEVICE_RESET_PENDING;
+        return(SCSI_RESET_PENDING);
       }
       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)
-      {
+        /* We want to send out the message, but it could screw an already */
+        /* in place and being used message.  Instead, we return an error  */
+        /* to try and start the bus reset phase since this command is     */
+        /* probably hung (aborts failed, and now reset is failing).  We   */
+        /* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
+        /* any more on this device, but instead will escalate to a bus or */
+        /* host reset (additionally, we won't try to abort any more).     */
+        printk(WARN_LEAD "Device reset, Message buffer "
+                "in use\n", p->host_no, CTL_OF_SCB(scb));
+        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+        aic7xxx_error(scb->cmd) = DID_RESET;
+        p->dev_flags[TARGET_INDEX(scb->cmd)] &= 
+                ~DEVICE_SUCCESS;
+        p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
+                BUS_DEVICE_RESET_PENDING;
+        return(SCSI_RESET_ERROR);
+      }
+    }
+  } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
+  hscb_index = aic7xxx_find_scb(p, scb);
+  if (hscb_index == SCB_LIST_NULL)
+  {
+    disconnected = (aic7xxx_scb_on_qoutfifo(p, scb)) ? FALSE : TRUE;
+  }
+  else
+  {
+    aic_outb(p, hscb_index, SCBPTR);
+    if (aic_inb(p, SCB_CONTROL) & DISCONNECTED)
+    {
+      disconnected = TRUE;
+    }
+  }
+  if (disconnected)
+  {
         /*
-         * Simply set the ABORT_SCB control bit and preserve the
-         * linked next pointer.
+         * Simply set the MK_MESSAGE flag and the SEQINT handler will do
+         * the rest on a reconnect.
          */
-        scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
-        scb->hscb->data_count &= cpu_to_le32(~0xFF000000);
-        scb->hscb->data_count |= cpu_to_le32(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;
-
-          scb_control = inb(p->base + SCB_CONTROL);
-          outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
-        }
+    scb->hscb->control |= MK_MESSAGE;
+    scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+    p->dev_flags[TARGET_INDEX(scb->cmd)] &= ~DEVICE_SUCCESS;
+    p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
+        BUS_DEVICE_RESET_PENDING;
+    if (hscb_index != SCB_LIST_NULL)
+    {
+      unsigned char scb_control;
+
+      aic_outb(p, hscb_index, SCBPTR);
+      scb_control = aic_inb(p, SCB_CONTROL);
+      aic_outb(p, scb_control | MK_MESSAGE, 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.
+         * want to abort is not tagged, then this will be the only
+         * outstanding command and we can simply shove it on the
+         * qoutfifo and be done.  If it is tagged, then it goes right
+         * in with all the others, no problem :)  We need to add it
+         * to the qinfifo and let the sequencer know it is there.
+         * Now, the only problem left to deal with is, *IF* this
+         * command completes, in spite of the MK_MESSAGE bit in the
+         * control byte, then we need to pick that up in the interrupt
+         * routine and clean things up.  This *shouldn't* ever happen.
          */
-        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;
-      }
-    }
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Queueing device reset "
+           "command.\n", p->host_no, CTL_OF_SCB(scb));
+    p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    scb->flags |= SCB_QUEUED_ABORT;
+    result = SCSI_RESET_PENDING;
   }
+  else if (result == -1)
+  {
+    result = SCSI_RESET_ERROR;
+  }
+  aic_outb(p, saved_scbptr, SCBPTR);
   return (result);
 }
 
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_panic_abort
+ *
+ * Description:
+ *   Abort the current SCSI command(s).
+ *-F*************************************************************************/
+void
+aic7xxx_panic_abort(struct aic7xxx_host *p)
+{
+  int i;
+
+  printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION);
+  printk("Controller type:\n    %s\n", board_names[p->board_name_index]);
+  for(i=0; i<MAX_TARGETS; i++)
+  {
+    if(p->dev_flags[i] & DEVICE_PRESENT)
+    {
+      printk(INFO_LEAD "dev_flags=0x%x, WDTR:%s, SDTR:%s, q_depth=%d:%d\n",
+        p->host_no, 0, i, 0, p->dev_flags[i],
+        (p->needwdtr_copy & (1 << i)) ? "Yes" : "No",
+        (p->needsdtr_copy & (1 << i)) ? "Yes" : "No",
+        p->dev_max_queue_depth[i], p->dev_mid_level_queue_depth[i]);
+    }
+  }
+  printk("SIMODE0=0x%x, SIMODE1=0x%x, SSTAT0=0x%x, SSTAT1=0x%x, INTSTAT=0x%x\n",
+    aic_inb(p, SIMODE0), aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
+    aic_inb(p, SSTAT1), aic_inb(p, INTSTAT) );
+  printk("p->flags=0x%x, p->type=0x%x, sequencer %s paused\n",
+     p->flags, p->type,
+    (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
+  panic("Stopping to debug\n");
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_abort
  *
  * Description:
@@ -6708,86 +8327,309 @@
 {
   struct aic7xxx_scb  *scb = NULL;
   struct aic7xxx_host *p;
-  int    base, result;
-  unsigned long processor_flags;
+  int    result, found=0;
+  unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
+  Scsi_Cmnd *cmd_next, *cmd_prev;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
 
-  save_flags(processor_flags);
-  cli();
+  /*
+   * I added a new config option to the driver: "panic_on_abort" that will
+   * cause the driver to panic and the machine to stop on the first abort
+   * or reset call into the driver.  At that point, it prints out a lot of
+   * usefull information for me which I can then use to try and debug the
+   * problem.  Simply enable the boot time prompt in order to activate this
+   * code.
+   */
+  if (aic7xxx_panic_on_abort)
+    aic7xxx_panic_abort(p);
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  if (scb != NULL)
+  DRIVER_LOCK
+
+/*
+ *  Run the isr to grab any command in the QOUTFIFO and any other misc.
+ *  assundry tasks.  This should also set up the bh handler if there is
+ *  anything to be done, but it won't run until we are done here since
+ *  we are following a straight code path without entering the scheduler
+ *  code.
+ */
+
+  pause_sequencer(p);
+  while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
   {
-    printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
-           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+    aic7xxx_isr(p->irq, p, (void *)NULL);
+    pause_sequencer(p);
   }
-  else
+
+  if ((scb == NULL) || (cmd->serial_number != cmd->serial_number_at_timeout))
+                      /*  Totally bogus cmd since it points beyond our  */
+  {                   /*  valid SCB range or doesn't even match it's own*/
+                      /*  timeout serial number.                        */
+    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+      printk(INFO_LEAD "Abort called with bogus Scsi_Cmnd "
+        "pointer.\n", p->host_no, CTL_OF_CMD(cmd));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_NOT_RUNNING);
+  }
+  if (scb->cmd != cmd)  /*  Hmmm...either this SCB is currently free with a */
+  {                     /*  NULL cmd pointer (NULLed out when freed) or it  */
+                        /*  has already been recycled for another command   */
+                        /*  Either way, this SCB has nothing to do with this*/
+                        /*  command and we need to deal with cmd without    */
+                        /*  touching the SCB.                               */
+                        /*  The theory here is to return a value that will  */
+                        /*  make the queued for complete command actually   */
+                        /*  finish successfully, or to indicate that we     */
+                        /*  don't have this cmd any more and the mid level  */
+                        /*  code needs to find it.                          */
+    cmd_next = p->completeq.head;
+    cmd_prev = NULL;
+    while (cmd_next != NULL) 
+    {
+      if (cmd_next == cmd) 
+      {
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "Abort called for command "
+          "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
+        if ( cmd_prev == NULL )
+          p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+        else
+          cmd_prev->host_scribble = cmd_next->host_scribble;
+        cmd_next->done(cmd_next);
+        unpause_sequencer(p, FALSE);
+        DRIVER_UNLOCK
+        return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful
+                                         * completion */
+      }                                  
+      cmd_prev = cmd_next;
+      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+    }
+    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+      printk(INFO_LEAD "Abort called for already completed"
+        " command.\n", p->host_no, CTL_OF_CMD(cmd));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_NOT_RUNNING);
+  }
+    
+/*   At this point we know the following:
+ *     the SCB pointer is valid
+ *     the command pointer passed in to us and the scb->cmd pointer match
+ *     this then means that the command we need to abort is the same as the
+ *     command held by the scb pointer and is a valid abort request.
+ *   Now, we just have to figure out what to do from here.  Current plan is:
+ *     if we have already been here on this command, escalate to a reset
+ *     if scb is on waiting list or QINFIFO, send it back as aborted
+ *     if scb is on WAITING_SCB list in sequencer, free scb and send back
+ *     if scb is disconnected and not completed, abort with abort message
+ *     if scb is currently running, then it may be causing the bus to hang
+ *       so we want a return value that indicates a reset would be appropriate
+ *       if the command does not finish shortly
+ *     if scb is already complete but not on completeq, we're screwed because
+ *       this can't happen (except if the command is in the QOUTFIFO, in which
+ *       case we would like it to complete successfully instead of having to
+ *       to be re-done)
+ *   All other scenarios already dealt with by previous code.
+ */
+
+  if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
   {
-    printk("aic7xxx: Abort called with no SCB for cmd.\n");
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+      printk(INFO_LEAD "SCB aborted once already, "
+        "escalating.\n", p->host_no, CTL_OF_SCB(scb));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_SNOOZE);
+  }
+  if ( (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) || 
+          (p->dev_flags[TARGET_INDEX(scb->cmd)] & 
+           BUS_DEVICE_RESET_PENDING) )
+  {
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+      printk(INFO_LEAD "Reset/Abort pending for this "
+        "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_PENDING);
   }
-#endif
 
-  if (p->flags & IN_TIMEOUT)
+  found = 0;
+  p->flags |= AHC_IN_ABORT;
+  if (aic7xxx_verbose & VERBOSE_ABORT)
+    printk(INFO_LEAD "Aborting scb %d, flags 0x%x\n",
+         p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+
+/*
+ *   First, let's check to see if the currently running command is our target
+ *    since if it is, the return is fairly easy and quick since we don't want
+ *    to touch the command in case it might complete, but we do want a timeout
+ *    in case it's actually hung, so we really do nothing, but tell the mid
+ *    level code to reset the timeout.
+ */
+
+  if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
   {
-    /*
-     * We've already started a recovery operation.
-     */
-    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
-    {
-      restore_flags(processor_flags);
-      return (SCSI_ABORT_PENDING);
-    }
-    else
+   /*
+    *  Check to see if the sequencer is just sitting on this command, or
+    *   if it's actively being run.
+    */
+    result = aic_inb(p, LASTPHASE);
+    switch (result)
     {
-      /*
-       * 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);
+      case P_DATAOUT:    /*    For any of these cases, we can assume we are */
+      case P_DATAIN:     /*    an active command and act according.  For    */
+      case P_COMMAND:    /*    anything else we are going to fall on through*/
+      case P_STATUS:     /*    The SCSI_ABORT_SNOOZE will give us two abort */
+      case P_MESGOUT:    /*    chances to finish and then escalate to a     */
+      case P_MESGIN:     /*    reset call                                   */
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "SCB is currently active.  "
+                "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
+        unpause_sequencer(p, FALSE);
+        p->flags &= ~AHC_IN_ABORT;
+        scb->flags |= SCB_RECOVERY_SCB; /*  Note the fact that we've been  */
+        p->flags |= AHC_ABORT_PENDING;  /*  here so we will know not to    */
+        DRIVER_UNLOCK                   /*  muck with other SCBs if this   */
+        return(SCSI_ABORT_PENDING);     /*  one doesn't complete and clear */
+        break;                          /*  out.                           */
+      default:
+        break;
     }
   }
-  if (cmd->serial_number != cmd->serial_number_at_timeout)
+
+  if ((found == 0) && (scb->flags & SCB_WAITINGQ))
   {
-    result = SCSI_ABORT_NOT_RUNNING;
+      int tindex = TARGET_INDEX(cmd);
+     
+      if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
+        printk(INFO_LEAD "SCB found on waiting list and "
+            "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+      scbq_remove(&p->waiting_scbs, scb);
+      scbq_remove(&p->delayed_scbs[tindex], scb);
+      p->dev_active_cmds[tindex]++;
+      p->activescbs++;
+      scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+      scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+      found = 1;
   }
-  else if (scb == NULL)
+
+/*
+ *  We just checked the waiting_q, now for the QINFIFO
+ */
+  if ( found == 0 )
   {
-    result = SCSI_ABORT_NOT_RUNNING;
+    if ( ((found = aic7xxx_search_qinfifo(p, cmd->target, 
+                     cmd->channel,
+                     cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
+                     FALSE, NULL)) != 0) &&
+                    (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
+      printk(INFO_LEAD "SCB found in QINFIFO and "
+        "aborted.\n", p->host_no, CTL_OF_SCB(scb));
   }
-  else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
+
+/*
+ *  QINFIFO, waitingq, completeq done.  Next, check WAITING_SCB list in card
+ */
+
+  if ( found == 0 )
   {
-    result = SCSI_ABORT_NOT_RUNNING;
+    unsigned char scb_next_ptr;
+    prev_hscbptr = SCB_LIST_NULL;
+    saved_hscbptr = aic_inb(p, SCBPTR);
+    next_hscbptr = aic_inb(p, WAITING_SCBH);
+    while ( next_hscbptr != SCB_LIST_NULL )
+    {
+      aic_outb(p,  next_hscbptr, SCBPTR );
+      if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
+      {
+        found = 1;
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "SCB found on hardware waiting"
+            " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+        if ( prev_hscbptr == SCB_LIST_NULL )
+        {
+            aic_outb(p, aic_inb(p, SCB_NEXT), WAITING_SCBH);
+            aic_outb(p, 0, SCSISEQ);    /* stop the selection since we just
+                                         * grabbed the scb out from under the
+                                         * card */
+        }
+        else
+        {
+            scb_next_ptr = aic_inb(p, SCB_NEXT);
+            aic_outb(p, prev_hscbptr, SCBPTR);
+            aic_outb(p, scb_next_ptr, SCB_NEXT);
+            aic_outb(p, next_hscbptr, SCBPTR);
+        }
+        aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+        aic_outb(p, 0, SCB_CONTROL);
+        aic7xxx_add_curscb_to_free_list(p);
+        scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+        break;
+      }
+      prev_hscbptr = next_hscbptr;
+      next_hscbptr = aic_inb(p, SCB_NEXT);
+    }
+    aic_outb(p,  saved_hscbptr, SCBPTR );
   }
-  else
+        
+/*
+ *  Hmmm...completeq, QOUTFIFO, QINFIFO, WAITING_SCBH, waitingq all checked.
+ *  OK...the sequencer's paused, interrupts are off, and we haven't found the
+ *  command anyplace where it could be easily aborted.  Time for the hard
+ *  work.  We also know the command is valid.  This essentially means the
+ *  command is disconnected, or connected but not into any phases yet, which
+ *  we know due to the tests we ran earlier on the current active scb phase.
+ *  At this point we can queue the abort tag and go on with life.
+ */
+
+  if ( found == 0 )
   {
-    /*
-     * 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;
-     }
+    p->flags |= AHC_ABORT_PENDING;
+    scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+    scb->hscb->control |= MK_MESSAGE;
+    result=aic7xxx_find_scb(p, scb);
+    if ( result != SCB_LIST_NULL ) 
+    {
+      saved_hscbptr = aic_inb(p, SCBPTR);
+      aic_outb(p, result, SCBPTR);
+      tmp_char = aic_inb(p, SCB_CONTROL);
+      aic_outb(p,  tmp_char | MK_MESSAGE, SCB_CONTROL);
+      aic_outb(p, saved_hscbptr, SCBPTR);
+    }
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+      printk(INFO_LEAD "SCB disconnected.  Queueing Abort"
+        " SCB.\n", p->host_no, CTL_OF_SCB(scb));
+    p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
   }
-  restore_flags(processor_flags);
-  return (result);
+  if (found)
+  {
+    aic7xxx_run_done_queue(p, TRUE);
+    aic7xxx_run_waiting_queues(p);
+  }
+  p->flags &= ~AHC_IN_ABORT;
+  unpause_sequencer(p, FALSE);
+  DRIVER_UNLOCK
+
+/*
+ *  On the return value.  If we found the command and aborted it, then we know
+ *  it's already sent back and there is no reason for a further timeout, so
+ *  we use SCSI_ABORT_SUCCESS.  On the queued abort side, we aren't so certain
+ *  there hasn't been a bus hang or something that might keep the abort from
+ *  from completing.  Therefore, we use SCSI_ABORT_PENDING.  The first time this
+ *  is passed back, the timeout on the command gets extended, the second time
+ *  we pass this back, the mid level SCSI code calls our reset function, which
+ *  would shake loose a hung bus.
+ */
+  if ( found != 0 )
+    return(SCSI_ABORT_SUCCESS);
+  else
+    return(SCSI_ABORT_PENDING); 
 }
 
 
@@ -6806,190 +8648,259 @@
 {
   struct aic7xxx_scb *scb = NULL;
   struct aic7xxx_host *p;
-  unsigned long base;
-  int    found, tindex, min_target, max_target;
+  int    tindex;
   int    result = -1;
-  char   channel = 'A';
-  unsigned long processor_flags;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
+#define DEVICE_RESET 0x01
+#define BUS_RESET    0x02
+#define HOST_RESET   0x04
+#define FAIL         0x08
+#define RESET_DELAY  0x10
+  int        action;
+  Scsi_Cmnd *cmd_prev, *cmd_next;
+
+
+  if ( cmd == NULL )
+  {
+    printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL Scsi_Cmnd "
+      "pointer, failing.\n");
+    return(SCSI_RESET_SNOOZE);
+  }
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
-  channel = cmd->channel ? 'B': 'A';
   tindex = TARGET_INDEX(cmd);
 
-#if 0   /* AIC7XXX_DEBUG_ABORT */
-  if (scb != NULL)
+  /*
+   * I added a new config option to the driver: "panic_on_abort" that will
+   * cause the driver to panic and the machine to stop on the first abort
+   * or reset call into the driver.  At that point, it prints out a lot of
+   * usefull information for me which I can then use to try and debug the
+   * problem.  Simply enable the boot time prompt in order to activate this
+   * code.
+   */
+  if (aic7xxx_panic_on_abort)
+    aic7xxx_panic_abort(p);
+
+  DRIVER_LOCK
+
+  pause_sequencer(p);
+  while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
   {
-    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);
+    aic7xxx_isr(p->irq, p, (void *)NULL );
+    pause_sequencer(p);
   }
-  else
+
+  if (scb == NULL)
   {
-    printk("aic7xxx: Reset called with no SCB for cmd.\n");
+    if (aic7xxx_verbose & VERBOSE_RESET_MID)
+      printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd"
+           "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
+    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
+    {
+      action = HOST_RESET;
+    }
+    else
+    {
+      action = BUS_RESET;
+    }
   }
-#endif
-
-  /* 
-   * This routine is called by scsi.c, in which case the interrupts
-   * very well may be on when we are called.  As such, we need to save
-   * the flags to be sure, then turn interrupts off, and then call our
-   * various method funtions which all assume interrupts are off.
-   */
-  save_flags(processor_flags);
-  cli();
-
-  if (scb->cmd != cmd)
-    scb = NULL;
-
-  if (p->flags & IN_TIMEOUT)
+  else if (scb->cmd != cmd) 
   {
-    /*
-     * We've already started a recovery operation.
-     */
-    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+    if (aic7xxx_verbose & VERBOSE_RESET_MID)
+    printk(INFO_LEAD "Reset called with recycled SCB "
+        "for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
+    cmd_prev = NULL;
+    cmd_next = p->completeq.head;
+    while ( cmd_next != NULL )
+    {
+      if (cmd_next == cmd)
+      {
+        if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+          printk(INFO_LEAD "Reset, found cmd on completeq"
+          ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
+        unpause_sequencer(p, FALSE);
+        DRIVER_UNLOCK
+        return(SCSI_RESET_NOT_RUNNING);
+      }
+      cmd_prev = cmd_next;
+      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+    }
+    if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
+    {
+      if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+        printk(INFO_LEAD "Reset, cmd not found,"
+          " failing.\n", p->host_no, CTL_OF_CMD(cmd));
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_NOT_RUNNING);
+    }
+    else
     {
-      restore_flags(processor_flags);
-      return (SCSI_RESET_PENDING);
+      if (aic7xxx_verbose & VERBOSE_RESET_MID)
+        printk(INFO_LEAD "Reset called, no scb, "
+          "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
+      scb = NULL;
+      action = HOST_RESET;
     }
   }
   else
   {
-    if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
-        && (scb != NULL))
+    if (aic7xxx_verbose & VERBOSE_RESET_MID)
+      printk(INFO_LEAD "Reset called, scb %d, flags "
+        "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+    if ( aic7xxx_scb_on_qoutfifo(p, scb) )
     {
-      /*
-       * 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 (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->flags & SCB_ABORTED)
-          {
-            result = SCSI_RESET_PENDING;
-          }
-          else if (!(scb->flags & SCB_ACTIVE))
-          {
-            result = SCSI_RESET_NOT_RUNNING;
-          }
-        }
-
-        if (result == -1)
-        {
-          if ((flags & SCSI_RESET_SYNCHRONOUS) &&
-              (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
-          {
-            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(aic7xxx_verbose & VERBOSE_RESET_RETURN)
+        printk(INFO_LEAD "SCB on qoutfifo, returning.\n", p->host_no,
+          CTL_OF_SCB(scb));
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_NOT_RUNNING);
+    }
+    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
+    {
+      action = HOST_RESET;
+    }
+    else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
+    {
+      action = BUS_RESET;
+    }
+    else 
+    {
+      action = DEVICE_RESET;
     }
   }
-  if (result == -1)
+  if ( (action & DEVICE_RESET) && 
+        (p->dev_flags[tindex] & BUS_DEVICE_RESET_PENDING) )
   {
-    /*
-     * The bus device reset failed; try resetting the channel.
-     */
-    if (!(flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET))
-        && (flags & SCSI_RESET_ASYNCHRONOUS))
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Bus device reset already sent to "
+        "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
+    action = BUS_RESET;
+  }
+  if ( (action & DEVICE_RESET) &&
+       (scb->flags & SCB_QUEUED_ABORT) )
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
     {
-      if (scb == NULL)
-      {
-	result = SCSI_RESET_NOT_RUNNING;
-      }
-      else if (!(scb->flags & SCB_ACTIVE))
-      {
-	result = SCSI_RESET_NOT_RUNNING;
-      }
-      else if ((scb->flags & SCB_ABORTED) &&
-               (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
-      {
-	result = SCSI_RESET_PENDING;
-      }
+      printk(INFO_LEAD "Have already attempted to reach "
+        "device with queued\n", p->host_no, CTL_OF_CMD(cmd));
+      printk(INFO_LEAD "message, will escalate to bus "
+        "reset.\n", p->host_no, CTL_OF_CMD(cmd));
     }
-
-    if (result == -1)
+    action = BUS_RESET;
+  }
+  if ( (action & DEVICE_RESET) && 
+       (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) )
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+     printk(INFO_LEAD "Bus device reset stupid when "
+        "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
+    action = BUS_RESET;
+  }
+  if ( (action & BUS_RESET) && !(p->type & AHC_TWIN) )
+  {
+    action = HOST_RESET;
+  }
+  if ( ((jiffies - p->dev_last_reset[tindex]) < (HZ * 3)) &&
+       !(action & (HOST_RESET | BUS_RESET)))
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
     {
-      /*
-       * The reset channel function assumes that the sequencer is paused.
-       */
-      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
-       * command, perform completion processing.
-       *
-       */
-      if ((flags & SCSI_RESET_SYNCHRONOUS) && (scb == NULL))
+      printk(INFO_LEAD "Reset called too soon after last "
+        "reset without requesting\n", p->host_no, CTL_OF_CMD(cmd));
+      printk(INFO_LEAD "bus or host reset, escalating.\n", p->host_no,
+        CTL_OF_CMD(cmd));
+    }
+    action = BUS_RESET;
+  }
+  if ( ((jiffies - p->last_reset) < (HZ * 3)) &&
+        (action & (HOST_RESET | BUS_RESET)) )
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Reset called too soon after "
+        "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
+    action = RESET_DELAY;
+  }
+  if ( (action & (BUS_RESET | HOST_RESET)) && (p->flags & AHC_IN_RESET)
+        && ((jiffies - p->reset_start) > (2 * HZ * 3)) )
+  {
+    printk(KERN_ERR "(scsi%d:%d:%d:%d) Yikes!!  Card must have left to go "
+        "back to Adaptec!!\n", p->host_no, CTL_OF_CMD(cmd));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_RESET_SNOOZE);
+  }
+/*
+ *  By this point, we want to already know what we are going to do and
+ *  only have the following code implement our course of action.
+ */
+  switch (action)
+  {
+    case RESET_DELAY:
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_PENDING);
+      break;
+    case FAIL:
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_ERROR);
+      break;
+    case DEVICE_RESET:
+      p->flags |= AHC_IN_RESET;
+      result = aic7xxx_bus_device_reset(p, cmd);
+      aic7xxx_run_done_queue(p, TRUE);
+      /*  We can't rely on run_waiting_queues to unpause the sequencer for
+       *  PCI based controllers since we use AAP */
+      aic7xxx_run_waiting_queues(p);
+      unpause_sequencer(p, FALSE);
+      p->flags &= ~AHC_IN_RESET;
+      DRIVER_UNLOCK
+      return(result);
+      break;
+    case BUS_RESET:
+    case HOST_RESET:
+    default:
+      p->reset_start = jiffies;
+      p->flags |= AHC_IN_RESET;
+      aic7xxx_reset_channel(p, cmd->channel, TRUE);
+      if ( (p->type & AHC_TWIN) && (action & HOST_RESET) )
       {
-	cmd->result = DID_RESET << 16;
-	cmd->scsi_done(cmd);
+        aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
+        restart_sequencer(p);
       }
-
-      switch (p->bus_type)
+      if (scb == NULL)
       {
-	case AIC_TWIN:
-	  if (channel == 'B')
-	  {
-            min_target = 8;
-            max_target = 15;
-	  }
-	  else
-	  {
-            min_target = 0;
-            max_target = 7;
-	  }
-	  break;
-
-	case AIC_WIDE:
-	  min_target = 0;
-	  max_target = 15;
-	  break;
-
-	case AIC_SINGLE:
-        default:
-	  min_target = 0;
-	  max_target = 7;
-	  break;
+        cmd->result = DID_RESET << 16;
+        cmd->done(cmd);
       }
-
-      for (tindex = min_target; tindex <= max_target; tindex++)
+      p->last_reset = jiffies;
+      if (action != HOST_RESET)
+        result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+      else
       {
-	p->device_status[tindex].last_reset = jiffies;
-      }
-
-      result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
-      p->flags &= ~IN_TIMEOUT;
-    }
+        result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+        aic_outb(p,  aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE),
+          SIMODE1);
+        aic7xxx_clear_intstat(p);
+        p->flags &= ~AHC_HANDLING_REQINITS;
+        p->msg_type = MSG_TYPE_NONE;
+        p->msg_index = 0;
+        p->msg_len = 0;
+      }
+      p->flags &= ~AHC_IN_RESET;
+      /*  We can't rely on run_waiting_queues to unpause the sequencer for
+       *  PCI based controllers since we use AAP */
+      aic7xxx_run_waiting_queues(p);
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(result);
+      break;
   }
-  aic7xxx_run_waiting_queues(p);
-  restore_flags(processor_flags);
-  return (result);
 }
 
 /*+F*************************************************************************
@@ -7016,7 +8927,7 @@
   sectors = 32;
   cylinders = disk->capacity / (heads * sectors);
 
-  if ((p->flags & EXTENDED_TRANSLATION) && (cylinders > 1024))
+  if ((p->flags & AHC_EXTEND_TRANS_A) && (cylinders > 1024))
   {
     heads = 255;
     sectors = 63;

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