## Automatically generated incremental diff
## From:   linux-2.0.37-pre8
## To:     linux-2.0.37-pre9
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.0.37-pre8/arch/alpha/Makefile linux-2.0.37-pre9/arch/alpha/Makefile
--- linux-2.0.37-pre8/arch/alpha/Makefile	1998-06-03 15:17:46.000000000 -0700
+++ linux-2.0.37-pre9/arch/alpha/Makefile	2003-08-15 15:04:42.000000000 -0700
@@ -27,9 +27,9 @@
 CFLAGS := $(CFLAGS) -mno-fp-regs
 
 # determine if we can use the BWX instructions with GAS
-$(shell rm -f /tmp/GAS_VER)
+dummy=$(shell rm -f /tmp/GAS_VER)
 #$(shell $(AS) --version >& /tmp/GAS_VER)
-$(shell $(AS) --version > /tmp/GAS_VER 2>&1)
+dummy=$(shell $(AS) --version > /tmp/GAS_VER 2>&1)
 OLD_GAS := $(shell if cat /tmp/GAS_VER | grep 'version 2.7' > /dev/null; then echo yes; else echo no; fi)
 
 ifneq ($(OLD_GAS),yes)
diff -urN linux-2.0.37-pre8/arch/i386/kernel/bios32.c linux-2.0.37-pre9/arch/i386/kernel/bios32.c
--- linux-2.0.37-pre8/arch/i386/kernel/bios32.c	1997-08-04 08:38:56.000000000 -0700
+++ linux-2.0.37-pre9/arch/i386/kernel/bios32.c	2003-08-15 15:04:42.000000000 -0700
@@ -730,7 +730,7 @@
     outb (0x00, 0xCFB);
     outb (0x00, 0xCF8);
     outb (0x00, 0xCFA);
-    if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) {
+    if (inb (0xCF8) == 0x00 && inb (0xCFA) == 0x00) {
 	restore_flags(flags);
 	printk("pcibios_init: Using configuration type 2\n");
 	return &pci_direct_conf2;
diff -urN linux-2.0.37-pre8/arch/i386/mm/init.c linux-2.0.37-pre9/arch/i386/mm/init.c
--- linux-2.0.37-pre8/arch/i386/mm/init.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/arch/i386/mm/init.c	2003-08-15 15:04:42.000000000 -0700
@@ -140,7 +140,7 @@
 		 */
 		if (!smp_scan_config(639*0x400,0x400))	/* Scan the top 1K of base RAM */
 		{
-			if(!(smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */
+			if(!smp_scan_config(0xF0000,0x10000)) /* Scan the 64K of bios */
 			{
 				/*
 				 * If it is an SMP machine we should know now, unless the
diff -urN linux-2.0.37-pre8/CREDITS linux-2.0.37-pre9/CREDITS
--- linux-2.0.37-pre8/CREDITS	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/CREDITS	2003-08-15 15:04:42.000000000 -0700
@@ -1646,6 +1646,7 @@
 E: lnz@dandelion.com
 W: http://www.dandelion.com/Linux/
 D: BusLogic SCSI driver
+D: Mylex DAC960 PCI RAID driver
 D: Miscellaneous kernel fixes
 S: 3078 Sulphur Spring Court
 S: San Jose, California 95148
diff -urN linux-2.0.37-pre8/Documentation/Configure.help linux-2.0.37-pre9/Documentation/Configure.help
--- linux-2.0.37-pre8/Documentation/Configure.help	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/Documentation/Configure.help	2003-08-15 15:04:42.000000000 -0700
@@ -337,6 +337,12 @@
   Documentation/modules.txt.
   It's pretty unlikely that you have one of these: say N.
 
+Mylex DAC960/DAC1100 PCI RAID Controller support
+CONFIG_BLK_DEV_DAC960
+  This driver adds support for the Mylex DAC960, AcceleRAID, and
+  eXtremeRAID PCI RAID controllers.  See README.DAC960 for further
+  information about this driver.
+
 Parallel port IDE device support
 CONFIG_PARIDE
   There are many external CD-ROM and disk devices that connect through
@@ -1726,66 +1732,102 @@
 Adaptec AIC7xxx chipset SCSI controller support
 CONFIG_SCSI_AIC7XXX
   This is support for the various aic7xxx based Adaptec SCSI
-  controllers.  These include the 274x EISA cards, 284x VLB cards,
-  294x PCI cards, 394x PCI cards, 3985 PCI card, and several versions
-  of the Adaptec built-in SCSI controllers on various PC motherboards.
+  controllers. These include the 274x EISA cards; 284x VLB cards; 2902,
+  2910, 293x, 294x, 394x, 3985 and several other PCI and motherboard based
+  SCSI controllers from Adaptec.  It does not support the AAA-13x RAID
+  controllers from Adaptec, nor will it likely ever support them.  It
+  does not support the 2920 cards from Adaptec that use the Future Domain
+  SCSI controller chip.  For those cards, you need the "Future Domain
+  16xx SCSI support" driver.
+
+  In general, if the controller is based on an Adaptec SCSI controller
+  chip from the aic777x series or the aic78xx series, it should work.  The
+  only exception is the 7810 which is specifically not supported (that's the
+  RAID controller chip on the AAA-13x cards).
+
   Information on the configuration options for this controller can be
-  found by checking the README.aic7xxx file, usually in
-  /usr/src/linux/drivers/scsi.
+  found by checking the help file for each of the available
+  configuration options. You should read drivers/scsi/README.aic7xxx
+  at a minimum before contacting the maintainer with any questions.  
+  The SCSI-HOWTO, available via FTP (user: anonymous) at
+  ftp://metalab.unc.edu/pub/Linux/docs/HOWTO can also be of great help.
+
+  If you want to compile this driver as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called aic7xxx.o.
 
-Override driver defaults for commands per LUN
-CONFIG_OVERRIDE_CMDS
-  Use this option to allow you to override the default maximum number
-  of commands that a single device on the aic7xxx controller is
-  allowed to have active at one time.  This option only effects tagged
-  queueing capable devices.  The driver uses a "failsafe" value of 8
-  by default.  This is much lower than many devices can handle, but
-  left in place for safety sake.
-  NOTE:  This does not actually enabled tagged queueing on any
-  particular device.  The driver has changed in this respect.  Please
-  see the file README.aic7xxx in /usr/src/linux/drivers/scsi for more
-  information on how to get particular devices to use tagged command
-  queueing.
-  Default: N
-
-Maximum number of commands per LUN
-CONFIG_AIC7XXX_CMDS_PER_LUN
-  Specify the maximum number of commands per lun you would like to
-  allocate per device.  Reasonable figures are in the range of 14 to
-  32 commands per device, but depending on hardware could be increased
-  or decreased from that figure.  If the number is too high for any
-  particular device, the driver will automatically compensate usually
-  after only 10 minutes of uptime and will issue a message to alert
-  you to the fact that the number of commands for that device has been
-  reduced.  It will not hinder performance if a portion of your
-  devices eventually have their commands per lun reduced, but is a
-  waste of memory if all of your devices end up reducing this number
-  down to a more reasonable figure.  Default: 24
+Enable or Disable Tagged Command Queueing by default
+CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  This option causes the aic7xxx driver to attempt to use tagged command
+  queueing on any devices that claim to support it.  If this is set to yes,
+  you can still turn off TCQ on troublesome devices with the use of the
+  tag_info boot parameter.  See /usr/src/linux/drivers/scsi/README.aic7xxx
+  for more information on that and other aic7xxx setup commands.  If this
+  option is turned off, you may still enable TCQ on known good devices by
+  use of the tag_info boot parameter.
+  
+  If you are unsure about your devices then it is safest to say N here.
+  
+  However, TCQ can increase performance on some hard drives by as much
+  as 50% or more, so I would recommend that if you say N here, that you
+  at least read the README.aic7xxx file so you will know how to enable
+  this option manually should your drives prove to be safe in regards
+  to TCQ.
+
+  Conversely, certain drives are known to lock up or cause bus resets when
+  TCQ is enabled on them.  If you have a Western Digital Enterprise SCSI
+  drive for instance, then don't even bother to enable TCQ on it as the
+  drive will become unreliable, and it will actually reduce performance.
+
+Default number of TCQ commands per device
+CONFIG_AIC7XXX_CMDS_PER_DEVICE
+  Specify the number of commands you would like to allocate per SCSI
+  device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+  Reasonable figures are in the range of 8 to 24 commands per device,
+  but depending on hardware could be increased or decreased from that
+  figure. If the number is too high for any particular device, the
+  driver will automatically compensate usually after only 10 minutes
+  of uptime. It will not hinder performance if some of your devices
+  eventually have their command depth reduced, but is a waste of memory
+  if all of your devices end up reducing this number down to a more
+  reasonable figure.
+  
+  NOTE: Certain very broken drives are known to lock up when given more
+  commands than they like to deal with.  Quantum Fireball drives are the
+  most common in this category.  For the Quantum Fireball drives I would
+  suggest no more than 8 commands per device.
+
+  Default: 8
 
 Collect statistics to report in /proc
 CONFIG_AIC7XXX_PROC_STATS
   This option tells the driver to keep track of how many commands have
   been sent to each particular device and report that information to
-  the user via the /proc/scsi/aic7xxx/x file, where x is the number
-  of the aic7xxx controller you want the information on.  This adds
-  a small amount of overhead to each and every SCSI command the
-  aic7xxx driver handles, so if you aren't really interested in this
-  information, it is best to leave it disabled.  Default: N
+  the user via the /proc/scsi/aic7xxx/n file, where n is the number of
+  the aic7xxx controller you want the information on. This adds a
+  small amount of overhead to each and every SCSI command the aic7xxx
+  driver handles, so if you aren't really interested in this
+  information, it is best to leave it disabled. This will only work if
+  you also say Y to "/proc filesystem support", below. 
+
+  If unsure, say N.
 
 Delay in seconds after SCSI bus reset
 CONFIG_AIC7XXX_RESET_DELAY
   This sets how long the driver will wait after resetting the SCSI bus
   before attempting to communicate with the devices on the SCSI bus
-  again.  This delay will be used during the reset phase at bootup
-  time as well as after any reset that might occur during normal
-  operation.  Reasonable numbers range anywhere from 5 to 15 seconds
-  depending on your devices. DAT tape drives are notorious for needing
-  more time after a bus reset to be ready for the next command, but
-  most hard drives and CD-ROM devices are ready in only a few seconds.
-  This option has a maximum upper limit of 20 seconds to avoid bad
-  interactions between the aic7xxx driver and the rest of the Linux
-  kernel.  The default value has been reduced.  If this doesn't work
-  with your hardware, try increasing this value.  Default: 5
+  again. This delay will be used during the reset phase at bootup time
+  as well as after any reset that might occur during normal operation.
+  Reasonable numbers range anywhere from 5 to 15 seconds depending on
+  your devices. DAT tape drives are notorious for needing more time
+  after a bus reset to be ready for the next command, but most hard
+  drives and CD-ROM devices are ready in only a few seconds. This
+  option has a maximum upper limit of 20 seconds to avoid bad
+  interactions between the aic7xxx driver and the rest of the linux
+  kernel. The default value has been reduced to 5 seconds. If this
+  doesn't work with your hardware, try increasing this value.
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
diff -urN linux-2.0.37-pre8/drivers/block/genhd.c linux-2.0.37-pre9/drivers/block/genhd.c
--- linux-2.0.37-pre8/drivers/block/genhd.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/block/genhd.c	2003-08-15 15:04:42.000000000 -0700
@@ -86,7 +86,6 @@
 			maj = "hd";
 	}
 #endif
-#ifdef CONFIG_BLK_DEV_DAC960
 	if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7)
 	  {
 	    int controller = hd->major - DAC960_MAJOR;
@@ -98,7 +97,6 @@
 			 maj, controller, minor >> hd->minor_shift, partition);
 	    return buf;
 	  }
-#endif
 #if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE)
 	if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) {
 		int ctlr = hd->major - COMPAQ_SMART2_MAJOR;
@@ -124,11 +122,9 @@
 	char buf[40];
 	hd->part[minor].start_sect = start;
 	hd->part[minor].nr_sects   = size;
-#ifdef CONFIG_BLK_DEV_DAC960
 	if (hd->major >= DAC960_MAJOR+0 && hd->major <= DAC960_MAJOR+7)
 		printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
 	else
-#endif
 #if defined(CONFIG_BLK_CPQ_DA) || defined(CONFIG_BLK_CPQ_DA_MODULE)
 	if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) 
 		printk(" p%d", (minor & ((1 << hd->minor_shift) - 1)));
diff -urN linux-2.0.37-pre8/drivers/block/ll_rw_blk.c linux-2.0.37-pre9/drivers/block/ll_rw_blk.c
--- linux-2.0.37-pre8/drivers/block/ll_rw_blk.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/block/ll_rw_blk.c	2003-08-15 15:04:42.000000000 -0700
@@ -263,6 +263,11 @@
 	short		 disk_index;
 
 	switch (major) {
+		case DAC960_MAJOR+0:
+			disk_index = (minor & 0x00f8) >> 3;
+			if (disk_index < 4)
+				drive_stat_acct(req->cmd, req->nr_sectors, disk_index);
+			break;
 		case SCSI_DISK_MAJOR:
 			disk_index = (minor & 0x0070) >> 4;
 			if (disk_index < 4)
@@ -359,7 +364,7 @@
 	lock_buffer(bh);
 
 	if (blk_size[major])
-		if (((blk_size[major][MINOR(bh->b_rdev)]<<1)+count) < sector) {
+               if (blk_size[major][MINOR(bh->b_rdev)] < (sector + count)>>1) {
 			bh->b_state &= (1 << BH_Lock) | (1 << BH_FreeOnIO);
                         /* This may well happen - the kernel calls bread()
                            without checking the size of the device, e.g.,
diff -urN linux-2.0.37-pre8/drivers/pci/pci.c linux-2.0.37-pre9/drivers/pci/pci.c
--- linux-2.0.37-pre8/drivers/pci/pci.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/pci/pci.c	2003-08-15 15:04:42.000000000 -0700
@@ -527,8 +527,10 @@
  	DEVICE( INTEL,		INTEL_82450GX,	"82450GX Orion P6"),
 	DEVICE(	KTI,		KTI_ET32P2,	"ET32P2"),
 	DEVICE( ADAPTEC,	ADAPTEC_7810,	"AIC-7810 RAID"),
+	DEVICE( ADAPTEC,	ADAPTEC_7821,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
+	DEVICE( ADAPTEC,	ADAPTEC_3860,	"AIC-7860"),
 	DEVICE( ADAPTEC,	ADAPTEC_5800,	"AIC-5800"),
 	DEVICE( ADAPTEC,	ADAPTEC_1480A,	"AIC-1480A"),
 	DEVICE( ADAPTEC,	ADAPTEC_7860,	"AIC-7860"),
@@ -544,12 +546,26 @@
 	DEVICE( ADAPTEC,	ADAPTEC_7882,	"AIC-7882U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7883,	"AIC-7883U"),
 	DEVICE( ADAPTEC,	ADAPTEC_7884,	"AIC-7884U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7885,	"AIC-7885U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7886,	"AIC-7886U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7887,	"AIC-7887U"),
+	DEVICE( ADAPTEC,	ADAPTEC_7888,	"AIC-7888U"),
 	DEVICE( ADAPTEC,	ADAPTEC_1030,	"ABA-1030 DVB receiver"),
-  DEVICE( ADAPTEC2, ADAPTEC2_2940U2, "AHA-2940U2"),
-  DEVICE( ADAPTEC2, ADAPTEC2_7890, "AIC-7890/1"),
-  DEVICE( ADAPTEC2, ADAPTEC2_3940U2, "AHA-3940U2"),
-  DEVICE( ADAPTEC2, ADAPTEC2_3950U2D, "AHA-3950U2D"),
-  DEVICE( ADAPTEC2, ADAPTEC2_7896, "AIC-7896/7"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_2940U2,"AHA-2940U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_2930U2,"AHA-2930U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7890B, "AIC-7890/1"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7890,	"AIC-7890/1"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_3940U2,"AHA-3940U2"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_3950U2D, "AHA-3950U2D"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7896, "AIC-7896/7"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892A, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892B, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892D, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7892P, "AIC-7892"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899A, "AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899B, "AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899D, "AIC-7899"),
+	DEVICE( ADAPTEC2,	ADAPTEC2_7899P, "AIC-7899"),
   	DEVICE( ATRONICS,	ATRONICS_2015,	"IDE-2015PL"),
 	DEVICE( TIGERJET,	TIGERJET_300,	"Tiger300 ISDN"),
 	DEVICE( ARK,		ARK_STING,	"Stingray"),
diff -urN linux-2.0.37-pre8/drivers/scsi/aic7xxx.c linux-2.0.37-pre9/drivers/scsi/aic7xxx.c
--- linux-2.0.37-pre8/drivers/scsi/aic7xxx.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/aic7xxx.c	2003-08-15 15:04:42.000000000 -0700
@@ -100,7 +100,7 @@
  *
  * Further driver modifications made by Doug Ledford <dledford@redhat.com>
  *
- * Copyright (c) 1997-1998 Doug Ledford
+ * Copyright (c) 1997-1999 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
@@ -336,6 +336,7 @@
 #include "aic7xxx/sequencer.h"
 #include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include <scsi/scsicam.h>
 
 #include <linux/stat.h>
 #include <linux/malloc.h>        /* for kmalloc() */
@@ -354,7 +355,7 @@
     0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "5.1.7"
+#define AIC7XXX_C_VERSION  "5.1.13"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
 #define MIN(a,b)        (((a) < (b)) ? (a) : (b))
@@ -447,8 +448,10 @@
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
-#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE 8
 #endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
@@ -493,7 +496,7 @@
  *
  * *** Determining commands per LUN ***
  * 
- * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its
  * own algorithm to determine the commands/LUN.  If SCB paging is
  * enabled, which is always now, the default is 8 commands per lun
  * that indicates it supports tagged queueing.  All non-tagged devices
@@ -511,8 +514,13 @@
  * Make a define that will tell the driver not to use tagged queueing
  * by default.
  */
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
+                              0, 0, 0, 0, 0, 0, 0, 0}
+#else
 #define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
                               255, 255, 255, 255, 255, 255, 255, 255}
+#endif
 
 /*
  * Modify this as you see fit for your system.  By setting tag_commands
@@ -551,6 +559,27 @@
 };
 */
 
+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 an array of board names that can be indexed by aha_type.
  * Don't forget to change this when changing the types!
@@ -577,11 +606,14 @@
   "Adaptec AHA-2944 Ultra SCSI host adapter",           /* AIC_7884 */
   "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
   "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
+  "Adaptec AHA-293X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
   "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
   "Adaptec AHA-394X Ultra2 SCSI host adapter",          /* AIC_7897 */
   "Adaptec AHA-395X Ultra2 SCSI host adapter",          /* AIC_7897 */
   "Adaptec PCMCIA SCSI controller",                     /* card bus stuff */
+  "Adaptec AIC-7892 Ultra 160/m SCSI host adapter",     /* AIC_7892 */
+  "Adaptec AIC-7899 Ultra 160/m SCSI host adapter",     /* AIC_7899 */
 };
 
 /*
@@ -884,6 +916,7 @@
   *  and what flags weren't.  This way, I could clean up the flag usage on
   *  a use by use basis.  Doug Ledford
   */
+        AHC_RESET_DELAY           = 0x00080000,
         AHC_A_SCANNED             = 0x00100000,
         AHC_B_SCANNED             = 0x00200000,
         AHC_MULTI_CHANNEL         = 0x00400000,
@@ -910,6 +943,8 @@
   AHC_AIC7890          = 0x0006,
   AHC_AIC7895          = 0x0007,
   AHC_AIC7896          = 0x0008,
+  AHC_AIC7892          = 0x0009,
+  AHC_AIC7899          = 0x000a,
   AHC_VL               = 0x0100,
   AHC_EISA             = 0x0200,
   AHC_PCI              = 0x0400,
@@ -926,6 +961,7 @@
   AHC_QUEUE_REGS       = 0x0040,
   AHC_SG_PRELOAD       = 0x0080,
   AHC_SPIOCAP          = 0x0100,
+  AHC_ULTRA160         = 0x0200,
   AHC_AIC7770_FE       = AHC_FENONE,
   AHC_AIC7850_FE       = AHC_SPIOCAP,
   AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
@@ -935,6 +971,8 @@
                          AHC_QUEUE_REGS|AHC_SG_PRELOAD,
   AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
   AHC_AIC7896_FE       = AHC_AIC7890_FE,
+  AHC_AIC7892_FE       = AHC_AIC7890_FE|AHC_ULTRA160,
+  AHC_AIC7899_FE       = AHC_AIC7890_FE|AHC_ULTRA160,
 } ahc_feature;
 
 struct aic7xxx_scb {
@@ -1042,6 +1080,10 @@
   unsigned long            isr_count;        /* Interrupt count */
   unsigned long            spurious_int;
   scb_data_type           *scb_data;
+  volatile unsigned short  needsdtr;
+  volatile unsigned short  sdtr_pending;
+  volatile unsigned short  needwdtr;
+  volatile unsigned short  wdtr_pending;
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
     Scsi_Cmnd *tail;
@@ -1073,12 +1115,15 @@
   volatile unsigned char   dev_temp_queue_depth[MAX_TARGETS];
   unsigned char            dev_commands_sent[MAX_TARGETS];
 
+  unsigned int             dev_timer_active; /* Which devs have a timer set */
+  struct timer_list        dev_timer;
+  unsigned long            dev_expires[MAX_TARGETS];
+
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
   spinlock_t               spin_lock;
   volatile unsigned char   cpu_lock_count[NR_CPUS];
 #endif
 
-  unsigned short           dev_timer_active; /* Which devs have a timer set */
 
 #ifdef AIC7XXX_FAKE_NEGOTIATION_CMDS
   Scsi_Cmnd               *dev_wdtr_cmnd[MAX_TARGETS];
@@ -1091,12 +1136,6 @@
 
   volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
 
-  unsigned long            dev_expires[MAX_TARGETS];
-  struct timer_list        dev_timer;
-
-  /*
-   * The next 64....
-   */
 
   unsigned char            msg_buf[9];       /* The message for the target */
   unsigned char            msg_type;
@@ -1123,16 +1162,11 @@
   volatile unsigned char   qoutfifo[256];
   volatile unsigned char   qinfifo[256];
   unsigned int             irq;              /* IRQ for this adapter */
-  volatile unsigned short  needsdtr;
-  volatile unsigned short  sdtr_pending;
-  volatile unsigned short  needwdtr;
-  volatile 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 */
@@ -1150,7 +1184,6 @@
   struct Scsi_Host        *host;             /* pointer to scsi host */
   int                      host_no;          /* SCSI host number */
   unsigned long            mbase;            /* I/O memory address */
-  unsigned long            last_reset;
   ahc_chip                 chip;             /* chip type */
 
   /*
@@ -1166,21 +1199,21 @@
    *
    * 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.  We keep the xfer count always
-   * for use by the aic7xxx_proc.c code, but only do the bins if the
-   * proc stats code is enabled.
+   * cache lines and is CPU time consuming.
+   *
+   * NOTE: Since it doesn't really buy us much, but consumes *tons* of RAM
+   * and blows apart all sorts of cache lines, I modified this so that we
+   * no longer look at the LUN.  All LUNs now go into the same bin on each
+   * device for stats purposes.
    */
   struct aic7xxx_xferstats {
-    long w_total;                            /* total writes */
-    long r_total;                            /* total reads */
+    long w_total;                          /* total writes */
+    long r_total;                          /* total reads */
 #ifdef AIC7XXX_PROC_STATS
-    long xfers;                              /* total xfer count */
-    long w_total512;                         /* 512 byte blocks written */
-    long r_total512;                         /* 512 byte blocks read */
-    long w_bins[10];                         /* binned write */
-    long r_bins[10];                         /* binned reads */
+    long w_bins[8];                       /* binned write */
+    long r_bins[8];                       /* binned reads */
 #endif /* AIC7XXX_PROC_STATS */
-  } stats[MAX_TARGETS][MAX_LUNS];            /* [(channel << 3)|target][lun] */
+  } stats[MAX_TARGETS];                    /* [(channel << 3)|target] */
 
 #if 0
   struct target_cmd       *targetcmds;
@@ -1412,35 +1445,6 @@
 
 #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
@@ -1510,6 +1514,8 @@
   return( timer->prev != NULL );
 }
 
+#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075
+
 #endif
 
 static inline unsigned char
@@ -2796,6 +2802,7 @@
   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)
@@ -2803,20 +2810,9 @@
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
-    sti();
     cmd->scsi_done(cmd);
-    cli();
   }
   DRIVER_UNLOCK
-#else
-  while (p->completeq.head != NULL)
-  {
-    cmd = p->completeq.head;
-    p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
-    cmd->host_scribble = NULL;
-    cmd->scsi_done(cmd);
-  }
-#endif
 }
 
 /*+F*************************************************************************
@@ -3090,7 +3086,7 @@
       int x;
 #endif /* AIC7XXX_PROC_STATS */
 
-      sp = &p->stats[TARGET_INDEX(cmd)][cmd->lun & 0x7];
+      sp = &p->stats[TARGET_INDEX(cmd)];
 
       /*
        * For block devices, cmd->request.cmd is always == either READ or
@@ -3107,8 +3103,6 @@
           aic7xxx_verbose &= 0xffff;
 #endif
 #ifdef AIC7XXX_PROC_STATS
-        sp->xfers++;
-        sp->w_total512 += (actual >> 9);
         ptr = sp->w_bins;
 #endif /* AIC7XXX_PROC_STATS */
       }
@@ -3120,23 +3114,27 @@
           aic7xxx_verbose &= 0xffff;
 #endif
 #ifdef AIC7XXX_PROC_STATS
-        sp->xfers++;
-        sp->r_total512 += (actual >> 9);
         ptr = sp->r_bins;
 #endif /* AIC7XXX_PROC_STATS */
       }
 #ifdef AIC7XXX_PROC_STATS
-      for (x = 9; x <= 17; x++)
+      x = -10;
+      while(actual)
       {
-        if (actual < (1 << x))
-        {
-          ptr[x - 9]++;
-          break;
-        }
+        actual >>= 1;
+        x++;
+      }
+      if (x < 0)
+      {
+        ptr[0]++;
       }
-      if (x > 17)
+      else if (x > 7)
       {
-        ptr[x - 9]++;
+        ptr[7]++;
+      }
+      else
+      {
+        ptr[x]++;
       }
 #endif /* AIC7XXX_PROC_STATS */
     }
@@ -3484,13 +3482,13 @@
             "delayed_scbs queue!\n", p->host_no, channel, i, lun);
         scbq_init(&p->delayed_scbs[i]);
       }
-      if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) ||
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
             time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
       {
         del_timer(&p->dev_timer);
         p->dev_timer.expires = p->dev_expires[i];
         add_timer(&p->dev_timer);
-        p->dev_timer_active |= (0x01 << p->scsi_id);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
       }
     }
   }
@@ -3623,7 +3621,7 @@
   if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
     printk(INFO_LEAD "Cleaning disconnected scbs "
       "list.\n", p->host_no, channel, target, lun);
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next, prev, scb_index;
 
@@ -3675,7 +3673,7 @@
    * Walk the free list making sure no entries on the free list have
    * a valid SCB_TAG value or SCB_CONTROL byte.
    */
-  if (p->features & AHC_PAGESCBS)
+  if (p->flags & AHC_PAGESCBS)
   {
     unsigned char next;
 
@@ -3956,12 +3954,6 @@
    */
   aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
-  /*
-   * Convince Mid Level SCSI code to leave us be for a little bit...
-   */
-  p->last_reset = jiffies;
-  p->host->last_reset = (jiffies + (HZ * AIC7XXX_RESET_DELAY));
-
   if ( !(p->features & AHC_TWIN) )
   {
     restart_sequencer(p);
@@ -4007,7 +3999,8 @@
     }
     if ( (p->dev_active_cmds[tindex] >=
           p->dev_temp_queue_depth[tindex]) ||
-         (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) )
+         (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) ||
+         (p->flags & AHC_RESET_DELAY) )
     {
       scbq_insert_tail(&p->delayed_scbs[tindex], scb);
     }
@@ -4126,14 +4119,17 @@
 #else
   spin_lock_irqsave(&io_request_lock, cpu_flags);
 #endif
-  p->dev_timer_active &= ~(0x01 << p->scsi_id);
+  p->dev_timer_active &= ~(0x01 << MAX_TARGETS);
+  if ( (p->dev_timer_active & (0x01 << p->scsi_id)) &&
+       time_after_eq(jiffies, p->dev_expires[p->scsi_id]) )
+  {
+    p->flags &= ~AHC_RESET_DELAY;
+    p->dev_timer_active &= ~(0x01 << p->scsi_id);
+  }
   for(i=0; i<MAX_TARGETS; i++)
   {
-    if ( i == p->scsi_id )
-    {
-      continue;
-    }
-    if ( (p->dev_timer_active & (0x01 << i)) &&
+    if ( (i != p->scsi_id) &&
+         (p->dev_timer_active & (0x01 << i)) &&
          time_after_eq(jiffies, p->dev_expires[i]) )
     {
       p->dev_timer_active &= ~(0x01 << i);
@@ -4159,7 +4155,7 @@
     }
     else if ( p->dev_timer_active & (0x01 << i) )
     {
-      if ( p->dev_timer_active & (0x01 << p->scsi_id) )
+      if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
       {
         if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
         {
@@ -4169,11 +4165,11 @@
       else
       {
         p->dev_timer.expires = p->dev_expires[i];
-        p->dev_timer_active |= (0x01 << p->scsi_id);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
       }
     }
   }
-  if ( p->dev_timer_active & (0x01 << p->scsi_id) )
+  if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
   {
     add_timer(&p->dev_timer);
   }
@@ -4899,10 +4895,10 @@
                 {
                   p->dev_expires[tindex] = jiffies + (HZ / 10);
                 }
-                if ( !(p->dev_timer_active & (0x01 << p->scsi_id)) )
+                if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) )
                 {
                   p->dev_timer.expires = p->dev_expires[tindex];
-                  p->dev_timer_active |= (0x01 << p->scsi_id);
+                  p->dev_timer_active |= (0x01 << MAX_TARGETS);
                   add_timer(&p->dev_timer);
                 }
                 else if ( time_after_eq(p->dev_timer.expires,
@@ -6368,7 +6364,7 @@
  *   Determines the queue depth for a given device.  There are two ways
  *   a queue depth can be obtained for a tagged queueing device.  One
  *   way is the default queue depth which is determined by whether
- *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   AIC7XXX_CMDS_PER_DEVICE is defined.  If it is defined, then it is used
  *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
  *   default queue depth (dependent on the number of hardware SCBs).
  *   The other way we determine queue depth is through the use of the
@@ -6396,11 +6392,7 @@
   {
     int tag_enabled = TRUE;
 
-#ifdef AIC7XXX_CMDS_PER_LUN
-    default_depth = AIC7XXX_CMDS_PER_LUN;
-#else
-    default_depth = 8;  /* Not many SCBs to work with. */
-#endif
+    default_depth = AIC7XXX_CMDS_PER_DEVICE;
  
     if (!(p->discenable & target_mask))
     {
@@ -6962,7 +6954,7 @@
   }
   printk("\n");
 #endif
-  if (checksum != scarray[len - 1])
+  if ( (checksum != scarray[len - 1]) || (checksum == 0) )
   {
     return (0);
   }
@@ -7446,7 +7438,6 @@
   }
 
   p->host = host;
-  p->last_reset = jiffies;
   p->host_no = host->host_no;
   host->unique_id = p->instance;
   p->isr_count = 0;
@@ -7532,10 +7523,9 @@
   }
   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);
   if (aic7xxx_verbose & VERBOSE_PROBE2)
   {
@@ -7846,6 +7836,11 @@
    */
   aic7xxx_loadseq(p);
 
+  /*
+   * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
+   */
+  aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
+
   if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
   {
     aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
@@ -8132,6 +8127,8 @@
         p->flags |= AHC_TERM_ENB_A;
       if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
         p->flags |= AHC_TERM_ENB_B;
+      aic_outb(p, 0, DISC_DSB);
+      aic_outb(p, 0, DISC_DSB + 1);
       break;
 
     case (AHC_AIC7770|AHC_VL):
@@ -8190,14 +8187,16 @@
     {
       printk("aic7xxx: Using leftover BIOS values.\n");
     }
-    if ( *sxfrctl1 & STPWEN )
+    if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
     {
       p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
       sc->adapter_control &= ~CFAUTOTERM;
       sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
     }
     if (aic7xxx_extended)
-      p->flags |= AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B;
+      p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+    else
+      p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
   }
   else
   {
@@ -8285,7 +8284,7 @@
     mask = (0x01 << i);
     if (!have_seeprom)
     {
-      if(aic_inb(p, SCSISEQ) != 0)
+      if (aic_inb(p, SCSISEQ) != 0)
       {
         /*
          * OK...the BIOS set things up and left behind the settings we need.
@@ -8345,20 +8344,30 @@
     }
     if (p->flags & AHC_NEWEEPROM_FMT)
     {
-      if (sc->device_flags[i] & CFSYNCHISULTRA)
+      if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+          !(p->features & AHC_ULTRA2) )
       {
-        p->ultraenb |= mask;
-      }
-      else if (sc->device_flags[i] & CFNEWULTRAFORMAT)
-      {
-        if ( ((sc->device_flags[i] & (CFSYNCHISULTRA | CFXFER)) == 0x03) &&
-             !(p->features & AHC_ULTRA2) )
+        /*
+         * I know of two different Ultra BIOSes that do this differently.
+         * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
+         * be == to 0x03 and SYNCISULTRA to be true to mean 40MByte/s
+         * while on the IBM Netfinity 5000 they want the same thing
+         * to be something else, while flags[i] & CFXFER == 0x03 and
+         * SYNCISULTRA false should be 40MByte/s.  So, we set both to
+         * 40MByte/s and the lower speeds be damned.  People will have
+         * to select around the conversely mapped lower speeds in order
+         * to select lower speeds on these boards.
+         */
+        if ((sc->device_flags[i] & (CFXFER)) == 0x03)
         {
           sc->device_flags[i] &= ~CFXFER;
           sc->device_flags[i] |= CFSYNCHISULTRA;
-          p->ultraenb |= mask;
         }
       }
+      if (sc->device_flags[i] & CFSYNCHISULTRA)
+      {
+        p->ultraenb |= mask;
+      }
     }
     else if (sc->adapter_control & CFULTRAEN)
     {
@@ -8789,6 +8798,14 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
        AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7860_FE,                                       7,
@@ -8831,6 +8848,18 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
        AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
        AHC_AIC7895_FE,                                      19,
@@ -8839,33 +8868,70 @@
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      20,
        32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      20,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
        AHC_AIC7890_FE,                                      21,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      22,
+       32, C46 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      22,
+       AHC_AIC7896_FE,                                      23,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      23,
+       AHC_AIC7896_FE,                                      24,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      24,
+       AHC_AIC7896_FE,                                      25,
        32, C56_66 },
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
        AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                      25,
+       AHC_AIC7860_FE,                                      26,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
        32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7899_FE,                                      28,
+       32, C56_66 },
     };
 
     unsigned short command;
     unsigned int  devconfig, i, oldverbose;
-#ifdef MMAPIO
-    unsigned long page_offset, base;
-#endif
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
     struct pci_dev *pdev = NULL;
 #else
@@ -9016,21 +9082,68 @@
           temp_p->mbase &= PCI_BASE_ADDRESS_MEM_MASK;
           temp_p->unpause = INTEN;
           temp_p->pause = temp_p->unpause | PAUSE;
+          if ( ((temp_p->base == 0) &&
+                (temp_p->mbase == 0)) ||
+               (temp_p->irq == 0) )
+          {
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
+            printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
+          }
 
 #ifdef MMAPIO
-          base = temp_p->mbase & PAGE_MASK;
-          page_offset = temp_p->mbase - base;
+          {
+            unsigned long page_offset, base;
+
+            base = temp_p->mbase & PAGE_MASK;
+            page_offset = temp_p->mbase - base;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-          temp_p->maddr = ioremap_nocache(base, page_offset + 256);
+            temp_p->maddr = ioremap_nocache(base, page_offset + 256);
 #else
-          temp_p->maddr = vremap(base, page_offset + 256);
+            temp_p->maddr = vremap(base, page_offset + 256);
 #endif
-          if(temp_p->maddr)
-          {
-            temp_p->maddr += page_offset;
+            if(temp_p->maddr)
+            {
+              temp_p->maddr += page_offset;
+              /*
+               * We need to check the I/O with the MMAPed address.  Some machines
+               * simply fail to work with MMAPed I/O and certain controllers.
+               */
+              if(aic_inb(temp_p, HCNTRL) == 0xff)
+              {
+                /*
+                 * OK.....we failed our test....go back to programmed I/O
+                 */
+                printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", 
+                  board_names[aic_pdevs[i].board_name_index],
+                  PCI_SLOT(temp_p->pci_device_fn),
+                  PCI_FUNC(temp_p->pci_device_fn));
+                printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
+                                 "Programmed I/O.\n");
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+                iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
+#else
+                vfree((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
+#endif
+                temp_p->maddr = 0;
+              }
+            }
           }
 #endif
 
+          /*
+           * We HAVE to make sure the first pause_sequencer() and all other
+           * subsequent I/O that isn't PCI config space I/O takes place
+           * after the MMAPed I/O region is configured and tested.  The
+           * problem is the PowerPC architecture that doesn't support
+           * programmed I/O at all, so we have to have the MMAP I/O set up
+           * for this pause to even work on those machines.
+           */
           pause_sequencer(temp_p);
 
           /*
@@ -9065,45 +9178,36 @@
           }
 
           /*
-           * Doing a switch based upon i is really gross, but since Justin
-           * changed around the chip ID stuff, we can't use that any more.
-           * Since we don't scan the devices the same way as FreeBSD, we end
-           * up doing this gross hack in order to avoid totally splitting
-           * away from Justin's init code in ahc_pci.c
+           * We need to set the CHNL? assignments before loading the SEEPROM
+           * The 3940 and 3985 cards (original stuff, not any of the later
+           * stuff) are 7870 and 7880 class chips.  The Ultra2 stuff falls
+           * under 7896 and 7897.  The 7895 is in a class by itself :)
            */
-          switch (i)
+          switch (temp_p->chip & AHC_CHIPID_MASK)
           {
-            case 7:   /* 3940 */
-            case 12:  /* 3940-Ultra */
-              switch(PCI_SLOT(temp_p->pci_device_fn))
+            case AHC_AIC7870: /* 3840 / 3985 */
+            case AHC_AIC7880: /* 3840 UW / 3985 UW */
+              if(temp_p->flags & AHC_MULTI_CHANNEL)
               {
-                case 5:
-                  temp_p->flags |= AHC_CHNLB;
-                  break;
-                default:
-                  break;
-              }
-              break;
-
-            case 8:   /* 3985 */
-            case 13:  /* 3985-Ultra */
-              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;
+                switch(PCI_SLOT(temp_p->pci_device_fn))
+                {
+                  case 5:
+                    temp_p->flags |= AHC_CHNLB;
+                    break;
+                  case 8:
+                    temp_p->flags |= AHC_CHNLB;
+                    break;
+                  case 12:
+                    temp_p->flags |= AHC_CHNLC;
+                    break;
+                  default:
+                    break;
+                }
               }
               break;
 
-            case 15:
-            case 18:
-            case 19:
-            case 20:
+            case AHC_AIC7895: /* 7895 */
+            case AHC_AIC7896: /* 7896/7 */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               if (PCI_FUNC(temp_p->pdev->devfn) != 0)
               {
@@ -9179,16 +9283,6 @@
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN | USCBSIZE32 |
                                 CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
-              /* FALLTHROUGH */
-            default:
-              /*
-               * We attempt to read a SEEPROM on *everything*.  If we fail,
-               * then we fail, but this covers things like 2910c cards that
-               * now have SEEPROMs with their 7856 chipset that we would
-               * otherwise ignore.  They still don't have a BIOS, but they
-               * have a SEEPROM that the SCSISelect utility on the Adaptec
-               * diskettes can configure.
-               */
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7850:
@@ -9200,14 +9294,13 @@
               aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
                                 CACHETHEN | MPARCKEN) & ~DPARCKEN,
                        DSCOMMAND0);
+              /* FALLTHROUGH */
+            default:
               aic7xxx_load_seeprom(temp_p, &sxfrctl1);
               break;
             case AHC_AIC7880:
               /*
-               * Only set the DSCOMMAND0 register if this is a Rev B.
-               * chipset.  For those, we also enable Ultra mode by
-               * force due to brain-damage on the part of some BIOSes
-               * We overload the devconfig variable here since we can.
+               * Check the rev of the chipset before we change DSCOMMAND0
                */
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92)
               pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
@@ -10892,23 +10985,14 @@
     }
     action = BUS_RESET;
   }
-  if ( ((jiffies - p->last_reset) < (HZ * 3)) &&
-        (action & (HOST_RESET | BUS_RESET)) )
+  if ( (p->flags & AHC_RESET_DELAY) &&
+       (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.
@@ -10940,15 +11024,23 @@
     case BUS_RESET:
     case HOST_RESET:
     default:
-      p->reset_start = jiffies;
-      p->flags |= AHC_IN_RESET;
+      p->flags |= AHC_IN_RESET | AHC_RESET_DELAY;
+      p->dev_expires[p->scsi_id] = jiffies + (3 * HZ);
+      p->dev_timer_active |= (0x01 << p->scsi_id);
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
+            time_after_eq(p->dev_timer.expires, p->dev_expires[p->scsi_id]) )
+      {
+        del_timer(&p->dev_timer);
+        p->dev_timer.expires = p->dev_expires[p->scsi_id];
+        add_timer(&p->dev_timer);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
+      }
       aic7xxx_reset_channel(p, cmd->channel, TRUE);
       if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
       {
         aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
         restart_sequencer(p);
       }
-      p->last_reset = jiffies;
       if (action != HOST_RESET)
         result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
       else
@@ -10970,11 +11062,13 @@
        * entirely and avoids getting a bogus dead command back through the
        * mid-level code due to too many retries.
        */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132)
       if ( flags & SCSI_RESET_SYNCHRONOUS )
       {
-        cmd->result = DID_RESET << 16;
+        cmd->result = DID_BUS_BUSY << 16;
         cmd->done(cmd);
       }
+#endif
       p->flags &= ~AHC_IN_RESET;
       /*
        * We can't rely on run_waiting_queues to unpause the sequencer for
@@ -11000,16 +11094,21 @@
 int
 aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
 {
-  int heads, sectors, cylinders;
+  int heads, sectors, cylinders, ret;
   struct aic7xxx_host *p;
+  struct buffer_head *bh;
 
   p = (struct aic7xxx_host *) disk->device->host->hostdata;
+  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024);
 
-  /*
-   * XXX - if I could portably find the card's configuration
-   *       information, then this could be autodetected instead
-   *       of left to a boot-time switch.
-   */
+  if ( bh )
+  {
+    ret = scsi_partsize(bh, disk->capacity, &geom[2], &geom[0], &geom[1]);
+    brelse(bh);
+    if ( ret != -1 )
+      return(ret);
+  }
+  
   heads = 64;
   sectors = 32;
   cylinders = disk->capacity / (heads * sectors);
diff -urN linux-2.0.37-pre8/drivers/scsi/aic7xxx_proc.c linux-2.0.37-pre9/drivers/scsi/aic7xxx_proc.c
--- linux-2.0.37-pre8/drivers/scsi/aic7xxx_proc.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/aic7xxx_proc.c	2003-08-15 15:04:42.000000000 -0700
@@ -31,7 +31,7 @@
 
 #define	BLS	(&aic7xxx_buffer[size])
 #define HDRB \
-"        < 512 512-1K   1-2K   2-4K   4-8K  8-16K 16-32K 32-64K 64-128K >128K"
+"             < 2K      2K+     4K+     8K+    16K+    32K+    64K+   128K+"
 
 #ifdef PROC_DEBUG
 extern int vsprintf(char *, const char *, va_list);
@@ -86,7 +86,7 @@
   int    size = 0;
   unsigned char i;
   struct aic7xxx_xferstats *sp;
-  unsigned char target, lun;
+  unsigned char target;
 
   HBAptr = NULL;
 
@@ -130,16 +130,12 @@
   size = 4096;
   for (target = 0; target < MAX_TARGETS; target++)
   {
-    for (lun = 0; lun < MAX_LUNS; lun++)
-    {
-      if ( ((lun == 0) && (p->dev_flags[target] & DEVICE_PRESENT)) ||
-           (p->stats[target][lun].r_total != 0) )
+    if (p->dev_flags[target] & DEVICE_PRESENT)
 #ifdef AIC7XXX_PROC_STATS
-        size += 512;
+      size += 512;
 #else
-        size += 256;
+      size += 256;
 #endif
-    }
   }
   if (aic7xxx_buffer_size != size)
   {
@@ -164,21 +160,17 @@
   size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Compile Options:\n");
-#ifdef AIC7XXX_RESET_DELAY
-  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
+#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
+#else
+  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
 #endif
-  size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Adapter Support Enabled\n");
-  size += sprintf(BLS, "                             Check below to see "
-                       "which\n"
-                       "                             devices use tagged "
-                       "queueing\n");
-  size += sprintf(BLS, "  AIC7XXX_PAGE_ENABLE    : Enabled (This is no longer "
-                       "an option)\n");
 #ifdef AIC7XXX_PROC_STATS
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
 #else
   size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
 #endif
+  size += sprintf(BLS, "  AIC7XXX_RESET_DELAY    : %d\n", AIC7XXX_RESET_DELAY);
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
   size += sprintf(BLS, "           SCSI Adapter: %s\n",
@@ -254,11 +246,7 @@
   }
   size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
   size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
-#ifdef AIC7XXX_CMDS_PER_LUN
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_LUN);
-#else
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", 8);
-#endif
+  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
   size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
                        "instance %d:\n", p->instance);
   size += sprintf(BLS, "      {");
@@ -273,89 +261,83 @@
   size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
 
   size += sprintf(BLS, "\n");
-  size += sprintf(BLS, "Statistics:\n");
+  size += sprintf(BLS, "Statistics:\n\n");
   for (target = 0; target < MAX_TARGETS; target++)
   {
-    for (lun = 0; lun < MAX_LUNS; lun++)
+    sp = &p->stats[target];
+    if ((p->dev_flags[target] & DEVICE_PRESENT) == 0)
     {
-      sp = &p->stats[target][lun];
-      if ( (!(p->dev_flags[target] & DEVICE_PRESENT)) ||
-           ((p->stats[target][lun].r_total == 0) && (lun != 0)) )
-      {
-        continue;
-      }
-      if (p->features & AHC_TWIN)
+      continue;
+    }
+    if (p->features & AHC_TWIN)
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, (target >> 3), (target & 0x7), 0);
+    }
+    else
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, 0, target, 0);
+    }
+    size += sprintf(BLS, "  Device using %s/%s",
+          (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
+          "Wide" : "Narrow",
+          (p->transinfo[target].cur_offset != 0) ?
+          "Sync transfers at " : "Async transfers.\n" );
+    if (p->transinfo[target].cur_offset != 0)
+    {
+      struct aic7xxx_syncrate *sync_rate;
+      int period = p->transinfo[target].cur_period;
+      int rate = (p->transinfo[target].cur_width ==
+                  MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+      sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
+      if (sync_rate != NULL)
       {
-        size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-            p->host_no, (target >> 3), (target & 0x7), lun);
+        size += sprintf(BLS, "%s MByte/sec, offset %d\n",
+                        sync_rate->rate[rate],
+                        p->transinfo[target].cur_offset );
       }
       else
       {
-        size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-            p->host_no, 0, target, lun);
-      }
-      size += sprintf(BLS, "  Device using %s/%s\n",
-            (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
-            "Wide" : "Narrow",
-            (p->transinfo[target].cur_offset != 0) ?
-            "Sync transfers at" : "Async transfers." );
-      if (p->transinfo[target].cur_offset != 0)
-      {
-        struct aic7xxx_syncrate *sync_rate;
-        int period = p->transinfo[target].cur_period;
-        int rate = (p->transinfo[target].cur_width ==
-                    MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
-
-        sync_rate = aic7xxx_find_syncrate(p, &period, AHC_SYNCRATE_ULTRA2);
-        if (sync_rate != NULL)
-        {
-          size += sprintf(BLS, "  %s MByte/sec, offset %d\n",
-                          sync_rate->rate[rate],
-                          p->transinfo[target].cur_offset );
-        }
-        else
-        {
-          size += sprintf(BLS, "  3.3 MByte/sec, offset %d\n",
-                          p->transinfo[target].cur_offset );
-        }
+        size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
+                        p->transinfo[target].cur_offset );
       }
-      size += sprintf(BLS, "    Device Negotiation Settings\n");
-      size += sprintf(BLS, "        Period Offset Bus Width\n");
-      size += sprintf(BLS, "User       %03d    %03d        %d\n",
-                      p->transinfo[target].user_period,
-                      p->transinfo[target].user_offset,
-                      p->transinfo[target].user_width);
-      size += sprintf(BLS, "Goal       %03d    %03d        %d\n",
-                      p->transinfo[target].goal_period,
-                      p->transinfo[target].goal_offset,
-                      p->transinfo[target].goal_width);
-      size += sprintf(BLS, "Current    %03d    %03d        %d\n",
-                      p->transinfo[target].cur_period,
-                      p->transinfo[target].cur_offset,
-                      p->transinfo[target].cur_width);
+    }
+    size += sprintf(BLS, "  Transinfo settings: ");
+    size += sprintf(BLS, "current(%d/%d/%d), ",
+                    p->transinfo[target].cur_period,
+                    p->transinfo[target].cur_offset,
+                    p->transinfo[target].cur_width);
+    size += sprintf(BLS, "goal(%d/%d/%d), ",
+                    p->transinfo[target].goal_period,
+                    p->transinfo[target].goal_offset,
+                    p->transinfo[target].goal_width);
+    size += sprintf(BLS, "user(%d/%d/%d)\n",
+                    p->transinfo[target].user_period,
+                    p->transinfo[target].user_offset,
+                    p->transinfo[target].user_width);
 #ifdef AIC7XXX_PROC_STATS
-      size += sprintf(BLS, "    Total transfers %ld (%ld read;%ld written)\n",
-          sp->xfers, sp->r_total, sp->w_total);
-      size += sprintf(BLS, "      blks(512) rd=%ld; blks(512) wr=%ld\n",
-          sp->r_total512, sp->w_total512);
-      size += sprintf(BLS, "%s\n", HDRB);
-      size += sprintf(BLS, " Reads:");
-      for (i = 0; i < NUMBER(sp->r_bins); i++)
-      {
-        size += sprintf(BLS, "%6ld ", sp->r_bins[i]);
-      }
-      size += sprintf(BLS, "\n");
-      size += sprintf(BLS, "Writes:");
-      for (i = 0; i < NUMBER(sp->w_bins); i++)
-      {
-        size += sprintf(BLS, "%6ld ", sp->w_bins[i]);
-      }
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
+    size += sprintf(BLS, "%s\n", HDRB);
+    size += sprintf(BLS, "   Reads:");
+    for (i = 0; i < NUMBER(sp->r_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->r_bins[i]);
+    }
+    size += sprintf(BLS, "\n");
+    size += sprintf(BLS, "  Writes:");
+    for (i = 0; i < NUMBER(sp->w_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->w_bins[i]);
+    }
+    size += sprintf(BLS, "\n");
 #else
-      size += sprintf(BLS, "    Total transfers: %ld/%ld read/written)\n",
-          sp->r_total, sp->w_total);
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
 #endif /* AIC7XXX_PROC_STATS */
-      size += sprintf(BLS, "\n\n");
-    }
+    size += sprintf(BLS, "\n\n");
   }
 
   if (size >= aic7xxx_buffer_size)
diff -urN linux-2.0.37-pre8/drivers/scsi/Config.in linux-2.0.37-pre9/drivers/scsi/Config.in
--- linux-2.0.37-pre8/drivers/scsi/Config.in	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/Config.in	2003-08-15 15:04:42.000000000 -0700
@@ -21,10 +21,8 @@
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
 if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
-    bool '   Override driver defaults for commands per LUN' CONFIG_OVERRIDE_CMDS N
-    if [ "$CONFIG_OVERRIDE_CMDS" != "n" ]; then
-      int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 24
-    fi
+    bool '   Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+    int  '   Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8
     bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
     int  '   Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
 fi
@@ -86,6 +84,9 @@
     bool '  Buggy EPP chipset support' CONFIG_SCSI_PPA_HAVE_PEDANTIC
 fi
 dep_tristate 'PAS16 SCSI support' CONFIG_SCSI_PAS16 $CONFIG_SCSI
+dep_tristate 'PCI-2000 support' CONFIG_SCSI_PCI2000 $CONFIG_SCSI
+dep_tristate 'PCI2220I support' CONFIG_SCSI_PCI2220I $CONFIG_SCSI
+dep_tristate 'PSI240I support' CONFIG_SCSI_PSI240I $CONFIG_SCSI
 dep_tristate 'Qlogic FAS SCSI support' CONFIG_SCSI_QLOGIC_FAS $CONFIG_SCSI
 if [ "$CONFIG_PCI" = "y" ]; then
   dep_tristate 'Qlogic ISP SCSI support' CONFIG_SCSI_QLOGIC_ISP $CONFIG_SCSI
diff -urN linux-2.0.37-pre8/drivers/scsi/hosts.c linux-2.0.37-pre9/drivers/scsi/hosts.c
--- linux-2.0.37-pre8/drivers/scsi/hosts.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/hosts.c	2003-08-15 15:04:42.000000000 -0700
@@ -201,6 +201,18 @@
 #include "inia100.h"
 #endif
 
+#ifdef CONFIG_SCSI_PCI2000
+#include "pci2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PCI2220I
+#include "pci2220i.h"
+#endif
+
+#ifdef CONFIG_SCSI_PSI240I
+#include "psi240i.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -360,6 +372,15 @@
 #ifdef CONFIG_SCSI_INITIO
     INI9100U,
 #endif
+#ifdef CONFIG_SCSI_PCI2000
+    PCI2000,
+#endif
+#ifdef CONFIG_SCSI_PCI2220I
+    PCI2220I,
+#endif
+#ifdef CONFIG_SCSI_PSI240I
+    PSI240I,
+#endif
 #ifdef CONFIG_BLK_DEV_IDESCSI
     IDESCSI,
 #endif
diff -urN linux-2.0.37-pre8/drivers/scsi/Makefile linux-2.0.37-pre9/drivers/scsi/Makefile
--- linux-2.0.37-pre8/drivers/scsi/Makefile	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/Makefile	2003-08-15 15:04:42.000000000 -0700
@@ -412,6 +412,30 @@
   endif
 endif
 
+ifeq ($(CONFIG_SCSI_PCI2000),y)
+L_OBJS += pci2000.o
+else
+    ifeq ($(CONFIG_SCSI_PCI2000),m)
+    M_OBJS += pci2000.o
+    endif
+endif
+
+ifeq ($(CONFIG_SCSI_PCI2220I),y)
+L_OBJS += pci2220i.o
+else
+    ifeq ($(CONFIG_SCSI_PCI2220I),m)
+    M_OBJS += pci2220i.o
+    endif
+endif
+
+ifeq ($(CONFIG_SCSI_PSI240I),y)
+L_OBJS += psi240i.o
+else
+    ifeq ($(CONFIG_SCSI_PSI240I),m)
+    M_OBJS += psi240i.o
+    endif
+endif
+
 ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
 L_OBJS += ide-scsi.o
 endif
diff -urN linux-2.0.37-pre8/drivers/scsi/pci2000.c linux-2.0.37-pre9/drivers/scsi/pci2000.c
--- linux-2.0.37-pre8/drivers/scsi/pci2000.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/pci2000.c	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,716 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2000i.c
+ *
+ *	Revisions	1.10	Jan-21-1999
+ *
+ *		- Fixed sign on message to reflect proper controller name.
+ *		- Added support for RAID status monitoring and control.
+ *
+ *-M*************************************************************************/
+#define PCI2000_VERSION		"1.10"
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2000.h"
+#include "psi_roy.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2000 =
+	{ PROC_SCSI_PCI2000, 7, "pci2000", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE	{int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+typedef struct
+	{
+	ULONG		address;
+	ULONG		length;
+	}	SCATGATH, *PSCATGATH;
+
+typedef struct
+	{
+	Scsi_Cmnd	*SCpnt;
+	SCATGATH	 scatGath[16];
+	UCHAR		 tag;
+	}	DEV2000, *PDEV2000;
+
+typedef struct
+	{
+	USHORT		 basePort;
+	USHORT		 mb0;
+	USHORT		 mb1;
+	USHORT		 mb2;
+	USHORT		 mb3;
+	USHORT		 mb4;
+	USHORT		 cmd;
+	USHORT		 tag;
+	DEV2000	 	 dev[MAX_BUS][MAX_UNITS];
+	}	ADAPTER2000, *PADAPTER2000;
+
+#define HOSTDATA(host) ((PADAPTER2000)&host->hostdata)
+
+
+static struct	Scsi_Host 	   *PsiHost[MAXADAPTER] = {NULL,};  // One for each adapter
+static			int				NumAdapters = 0;
+/****************************************************************
+ *	Name:			WaitReady	:LOCAL
+ *
+ *	Description:	Wait for controller ready.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE on not ready.
+ *
+ ****************************************************************/
+static int WaitReady (PADAPTER2000 padapter)
+	{
+	ULONG	timer;
+
+	timer = jiffies + TIMEOUT_COMMAND;								// calculate the timeout value
+	do	{
+		if ( !inb_p (padapter->cmd) )
+			return FALSE;
+		}	while ( timer > jiffies );									// test for timeout
+	return TRUE;
+	}
+/****************************************************************
+ *	Name:	OpDone	:LOCAL
+ *
+ *	Description:	Clean up operation and issue done to caller.
+ *
+ *	Parameters:		SCpnt	- Pointer to SCSI command structure.
+ *					status	- Caller status.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+static void OpDone (Scsi_Cmnd *SCpnt, ULONG status)
+	{
+	SCpnt->result = status;
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Command		:LOCAL
+ *
+ *	Description:	Issue queued command to the PCI-2000.
+ *
+ *	Parameters:		padapter - Pointer to adapter information structure.
+ *					cmd		 - PCI-2000 command byte.
+ *
+ *	Returns:		Non-zero command tag if operation is accepted.
+ *
+ ****************************************************************/
+static UCHAR Command (PADAPTER2000 padapter, UCHAR cmd)
+	{
+	outb_p (cmd, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	if ( inw_p (padapter->mb0) )
+		return 0;
+
+	return inb_p (padapter->mb1);
+	}
+/****************************************************************
+ *	Name:	BuildSgList		:LOCAL
+ *
+ *	Description:	Build the scatter gather list for controller.
+ *
+ *	Parameters:		SCpnt	 - Pointer to SCSI command structure.
+ *					padapter - Pointer to adapter information structure.
+ *					pdev	 - Pointer to adapter device structure.
+ *
+ *	Returns:		Non-zero in not scatter gather.
+ *
+ ****************************************************************/
+static int BuildSgList (Scsi_Cmnd *SCpnt, PADAPTER2000 padapter, PDEV2000 pdev)
+	{
+	int	z;
+
+	if ( SCpnt->use_sg )
+		{
+		for ( z = 0;  z < SCpnt->use_sg;  z++ )
+			{
+			pdev->scatGath[z].address = virt_to_bus (((struct scatterlist *)SCpnt->request_buffer)[z].address);
+			pdev->scatGath[z].length = ((struct scatterlist *)SCpnt->request_buffer)[z].length;
+			}
+		outl (virt_to_bus (pdev->scatGath), padapter->mb2);
+		outl ((SCpnt->use_sg << 24) | SCpnt->request_bufflen, padapter->mb3);
+		return FALSE;
+		}
+	outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+	outl (SCpnt->request_bufflen, padapter->mb3);
+	return TRUE;
+	}
+/*********************************************************************
+ *	Name:	PsiRaidCmd
+ *
+ *	Description:	Execute a simple command.
+ *
+ *	Parameters:		padapter - Pointer to adapter control structure.
+ *					cmd		 - Roy command byte.
+ *
+ *	Returns:		Return error status.
+ *
+ ********************************************************************/
+static int PsiRaidCmd (PADAPTER2000 padapter, char cmd)
+	{
+	if ( WaitReady (padapter) )						// test for command register ready
+		return DID_TIME_OUT;
+	outb_p (cmd, padapter->cmd);					// issue command
+	if ( WaitReady (padapter) )						// wait for adapter ready
+		return DID_TIME_OUT;
+	return DID_OK;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost = NULL;	// Pointer to host data block
+	PADAPTER2000		padapter;		// Pointer to adapter control structure
+	PDEV2000			pdev;
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				tag = 0;
+	UCHAR				tag0;
+	ULONG				error;
+	int					pun;
+	int					bus;
+	int					z;
+
+	DEB(printk ("\npci2000 recieved interrupt "));
+	for ( z = 0; z < NumAdapters;  z++ )										// scan for interrupt to process
+		{
+		if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+			{
+			tag = inb_p (HOSTDATA(PsiHost[z])->tag);
+			if (  tag )
+				{
+				shost = PsiHost[z];
+				break;
+				}
+			}
+		}
+
+	if ( !shost )
+		{
+		DEB (printk ("\npci2000: not my interrupt"));
+		return;
+		}
+
+	padapter = HOSTDATA(shost);
+
+	tag0 = tag & 0x7F;															// mask off the error bit
+	for ( bus = 0;  bus < MAX_BUS;  bus++ )										// scan the busses
+    	{
+		for ( pun = 0;  pun < MAX_UNITS;  pun++ )								// scan the targets
+    		{
+			pdev = &padapter->dev[bus][pun];
+			if ( !pdev->tag )
+    			continue;
+			if ( pdev->tag == tag0 )											// is this it?
+				{
+				pdev->tag = 0;
+				SCpnt = pdev->SCpnt;
+				goto irqProceed;
+    			}
+			}
+    	}
+
+	outb_p (0xFF, padapter->tag);												// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);											// complete the op
+	return;																		// done, but, with what?
+
+irqProceed:;
+	if ( tag & ERR08_TAGGED )												// is there an error here?
+		{
+		if ( WaitReady (padapter) )
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			return;
+			}
+
+		outb_p (tag0, padapter->mb0);										// get real error code
+		outb_p (CMD_ERROR, padapter->cmd);
+		if ( WaitReady (padapter) )											// wait for controller to suck up the op
+			{
+			OpDone (SCpnt, DID_TIME_OUT << 16);
+			return;
+			}
+
+		error = inl (padapter->mb0);										// get error data
+		outb_p (0xFF, padapter->tag);										// clear the op interrupt
+		outb_p (CMD_DONE, padapter->cmd);									// complete the op
+
+		DEB (printk ("status: %lX ", error));
+		if ( error == 0x00020002 )											// is this error a check condition?
+			{
+			if ( bus )														// are we doint SCSI commands?
+				{
+				OpDone (SCpnt, (DID_OK << 16) | 2);
+				return;
+				}
+			if ( *SCpnt->cmnd == SCSIOP_TEST_UNIT_READY )
+				OpDone (SCpnt, (DRIVER_SENSE << 24) | (DID_OK << 16) | 2);	// test caller we have sense data too
+			else
+				OpDone (SCpnt, DID_ERROR << 16);
+			return;
+			}
+		OpDone (SCpnt, DID_ERROR << 16);
+		return;
+		}
+
+	outb_p (0xFF, padapter->tag);											// clear the op interrupt
+	outb_p (CMD_DONE, padapter->cmd);										// complete the op
+	OpDone (SCpnt, DID_OK << 16);
+	}
+/****************************************************************
+ *	Name:	Pci2220i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2000_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER2000	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	int				rc		 = -1;								// command return code
+	UCHAR			bus		 = SCpnt->channel;
+	UCHAR			pun		 = SCpnt->target;
+	UCHAR			lun		 = SCpnt->lun;
+	UCHAR			cmd;
+	PDEV2000		pdev	 = &padapter->dev[bus][pun];
+
+	if ( !done )
+		{
+		printk("pci2000_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	SCpnt->scsi_done = done;
+	pdev->SCpnt = SCpnt;  									// Save this command data
+
+	if ( WaitReady (padapter) )
+		{
+		rc = DID_ERROR;
+		goto finished;
+		}
+
+	outw_p (pun | (lun << 8), padapter->mb0);
+
+	if ( bus )
+		{
+		DEB (if(*cdb) printk ("\nCDB: %X-  %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+		DEB (if(*cdb) printk ("\ntimeout_per_command: %d, timeout_total: %d, timeout: %d, internal_timout: %d", SCpnt->timeout_per_command,
+							  SCpnt->timeout_total, SCpnt->timeout, SCpnt->internal_timeout));
+		outl (SCpnt->timeout_per_command, padapter->mb1);
+		outb_p (CMD_SCSI_TIMEOUT, padapter->cmd);
+		if ( WaitReady (padapter) )
+			{
+			rc = DID_ERROR;
+			goto finished;
+			}
+
+		outw_p (pun | (lun << 8), padapter->mb0);
+		outw_p (SCpnt->cmd_len << 8, padapter->mb0 + 2);
+		outl (virt_to_bus (cdb), padapter->mb1);
+		if ( BuildSgList (SCpnt, padapter, pdev) )
+			cmd = CMD_SCSI_THRU;
+		else
+			cmd = CMD_SCSI_THRU_SG;
+		if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+			rc = DID_TIME_OUT;
+		goto finished;
+		}
+	else
+		{
+		if ( lun )
+			{
+			rc = DID_BAD_TARGET;
+			goto finished;
+			}
+		}
+
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			if ( cdb[2] == SC_MY_RAID )
+				{
+				switch ( cdb[3] ) 
+					{
+					case MY_SCSI_REBUILD:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_REBUILD) << 16);
+						return 0;
+					case MY_SCSI_ALARMMUTE:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_MUTE) << 16);
+						return 0;
+					case MY_SCSI_DEMOFAIL:
+						OpDone (SCpnt, PsiRaidCmd (padapter, CMD_RAID_FAIL) << 16);
+						return 0;
+					default:
+						if ( SCpnt->use_sg )
+							{
+							rc = DID_ERROR;
+							goto finished;
+							}
+						else
+							outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+						outl (cdb[5], padapter->mb0);
+						outl (cdb[3], padapter->mb3);
+						cmd = CMD_DASD_RAID_RQ;
+						break;
+					}
+				break;
+				}
+			
+			if ( SCpnt->use_sg )
+				{
+				outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+				}
+			else
+				{
+				outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+				}
+			outl (SCpnt->request_bufflen, padapter->mb3);
+			cmd = CMD_DASD_SCSI_INQ;
+			break;
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			outl (virt_to_bus (SCpnt->sense_buffer), padapter->mb2);
+			outl (sizeof (SCpnt->sense_buffer), padapter->mb3);
+			cmd = CMD_TEST_READY;
+			break;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			if ( SCpnt->use_sg )
+				{
+				outl (virt_to_bus (((struct scatterlist *)(SCpnt->request_buffer))->address), padapter->mb2);
+				}
+			else
+				{
+				outl (virt_to_bus (SCpnt->request_buffer), padapter->mb2);
+				}
+			outl (8, padapter->mb3);
+			cmd = CMD_DASD_CAP;
+			break;
+		case SCSIOP_VERIFY:						// verify CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ:						// read10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_READ6:						// read6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_READ;
+			else
+				cmd = CMD_READ_SG;
+			break;
+		case SCSIOP_WRITE:						// write10 CDB
+			outw_p ((USHORT)cdb[8] | ((USHORT)cdb[7] << 8), padapter->mb0 + 2);
+			outl (XSCSI2LONG (&cdb[2]), padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			outw_p (cdb[4], padapter->mb0 + 2);
+			outl ((SCSI2LONG (&cdb[1])) & 0x001FFFFF, padapter->mb1);
+			if ( BuildSgList (SCpnt, padapter, pdev) )
+				cmd = CMD_WRITE;
+			else
+				cmd = CMD_WRITE_SG;
+			break;
+		case SCSIOP_START_STOP_UNIT:
+			cmd = CMD_EJECT_MEDIA;
+			break;
+		case SCSIOP_MEDIUM_REMOVAL:
+			switch ( cdb[4] )
+				{
+				case 0:
+					cmd = CMD_UNLOCK_DOOR;
+					break;
+				case 1:
+					cmd = CMD_LOCK_DOOR;
+					break;
+				default:
+					cmd = 0;
+					break;
+				}
+			if ( cmd )
+				break;
+		default:
+			DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+			OpDone (SCpnt, DID_ERROR << 16);
+			return 0;
+		}
+
+	if ( (pdev->tag = Command (padapter, cmd)) == 0 )
+		rc = DID_TIME_OUT;
+finished:;
+	if ( rc != -1 )
+		OpDone (SCpnt, rc << 16);
+	return 0;
+	}
+/****************************************************************
+ *	Name:	internal_done :LOCAL
+ *
+ *	Description:	Done handler for non-queued commands
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+static void internal_done (Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2000_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("pci2000_command: ..calling pci2000_queuecommand\n"));
+
+	Pci2000_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2000_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					pci_index = 0;
+	struct Scsi_Host   *pshost;
+	PADAPTER2000	    padapter;
+	int					z, zz;
+	int					setirq;
+
+	if ( pcibios_present () )
+		{
+		for ( pci_index = 0;  pci_index <= MAXADAPTER;  ++pci_index )
+			{
+			UCHAR	pci_bus, pci_device_fn;
+
+			if ( pcibios_find_device (VENDOR_PSI, DEVICE_ROY_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+				break;
+
+			pshost = scsi_register (tpnt, sizeof(ADAPTER2000));
+			padapter = HOSTDATA(pshost);
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &padapter->basePort);
+			padapter->basePort &= 0xFFFE;
+			DEB (printk ("\nBase Regs = %#04X", padapter->basePort));			// get the base I/O port address
+			padapter->mb0	= padapter->basePort + RTR_MAILBOX;		   			// get the 32 bit mail boxes
+			padapter->mb1	= padapter->basePort + RTR_MAILBOX + 4;
+			padapter->mb2	= padapter->basePort + RTR_MAILBOX + 8;
+			padapter->mb3	= padapter->basePort + RTR_MAILBOX + 12;
+			padapter->mb4	= padapter->basePort + RTR_MAILBOX + 16;
+			padapter->cmd	= padapter->basePort + RTR_LOCAL_DOORBELL;			// command register
+			padapter->tag	= padapter->basePort + RTR_PCI_DOORBELL;			// tag/response register
+
+			if ( WaitReady (padapter) )
+				goto unregister;
+			outb_p (0x84, padapter->mb0);
+			outb_p (CMD_SPECIFY, padapter->cmd);
+			if ( WaitReady (padapter) )
+				goto unregister;
+
+			pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+			setirq = 1;
+			for ( z = 0;  z < pci_index;  z++ )									// scan for shared interrupts
+				{
+				if ( PsiHost[z]->irq == pshost->irq )							// if shared then, don't posses
+					setirq = 0;
+				}
+			if ( setirq )														// if not shared, posses
+				{
+				if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2000", NULL) )
+					{
+					printk ("Unable to allocate IRQ for PSI-2000 controller.\n");
+					goto unregister;
+					}
+				}
+			PsiHost[pci_index]	= pshost;										// save SCSI_HOST pointer
+
+			pshost->unique_id	= padapter->basePort;
+			pshost->max_id		= 16;
+			pshost->max_channel	= 1;
+
+			for ( zz = 0;  zz < MAX_BUS;  zz++ )
+				for ( z = 0; z < MAX_UNITS;  z++ )
+					padapter->dev[zz][z].tag = 0;
+			
+			printk("\nPSI-2000 Intelligent Storage SCSI CONTROLLER: at I/O = %X  IRQ = %d\n", padapter->basePort, pshost->irq);
+			printk("Version %s, Compiled %s %s\n\n", PCI2000_VERSION,  __DATE__, __TIME__);
+			continue;
+unregister:;
+			scsi_unregister (pshost);
+			}
+		}
+	NumAdapters = pci_index;
+	return pci_index;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Pci2000_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("pci2000_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Pci2000_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Pci2220i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2000_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	PADAPTER2000	    padapter;
+
+	padapter = HOSTDATA(disk->device->host);
+
+	if ( WaitReady (padapter) )
+		return 0;
+	outb_p (disk->device->id, padapter->mb0);
+	outb_p (CMD_GET_PARMS, padapter->cmd);
+	if ( WaitReady (padapter) )
+		return 0;
+
+	geom[0] = inb_p (padapter->mb2 + 3);
+	geom[1] = inb_p (padapter->mb2 + 2);
+	geom[2] = inw_p (padapter->mb2);
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2000;
+
+#include "scsi_module.c"
+#endif
+
diff -urN linux-2.0.37-pre8/drivers/scsi/pci2000.h linux-2.0.37-pre9/drivers/scsi/pci2000.h
--- linux-2.0.37-pre8/drivers/scsi/pci2000.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/pci2000.h	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,226 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2000.h
+ *
+ *	Description:	Header file for the SCSI driver for the PCI-2000
+ *					interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PCI2000_H
+#define _PCI2000_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		definition of standard data types		*/
+/************************************************/
+#define	CHAR	char
+#define	UCHAR	unsigned char
+#define	SHORT	short
+#define	USHORT	unsigned short
+#define	BOOL	long
+#define	LONG	long
+#define	ULONG	unsigned long
+#define	VOID	void
+
+typedef	CHAR	*PCHAR;
+typedef	UCHAR	*PUCHAR;
+typedef	SHORT	*PSHORT;
+typedef	USHORT	*PUSHORT;
+typedef	BOOL	*PBOOL;
+typedef	LONG	*PLONG;
+typedef	ULONG	*PULONG;
+typedef	VOID	*PVOID;
+
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+#endif
+
+// function prototypes
+int Pci2000_Detect			(Scsi_Host_Template *tpnt);
+int Pci2000_Command			(Scsi_Cmnd *SCpnt);
+int Pci2000_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2000_Abort			(Scsi_Cmnd *SCpnt);
+int Pci2000_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2000_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2000;
+
+#define PCI2000 { NULL, NULL,							\
+			&Proc_Scsi_Pci2000,/* proc_dir_entry */		\
+			NULL,		                				\
+			"PCI-2000 SCSI Intelligent Disk Controller",\
+			Pci2000_Detect,								\
+			NULL,										\
+			NULL,	 									\
+			Pci2000_Command,							\
+			Pci2000_QueueCommand,						\
+			Pci2000_Abort,								\
+			Pci2000_Reset,								\
+			NULL,										\
+			Pci2000_BiosParam,                 			\
+			16, 										\
+			-1, 										\
+			16,		 									\
+			1, 											\
+			0, 											\
+			0, 											\
+			DISABLE_CLUSTERING }
+
+#endif
diff -urN linux-2.0.37-pre8/drivers/scsi/pci2220i.c linux-2.0.37-pre9/drivers/scsi/pci2220i.c
--- linux-2.0.37-pre8/drivers/scsi/pci2220i.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/pci2220i.c	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,819 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2220i.c
+ *
+ *	Description:	SCSI driver for the PCI2220I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "pci2220i.h"
+#include "psi_dale.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Pci2220i =
+	{ PROC_SCSI_PCI2220I, 7, "pci2220i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#define STOP_HERE	{int st;for(st=0;st<100;st++){st=1;}}
+#else
+#define DEB(x)
+#define STOP_HERE
+#endif
+
+#define MAXADAPTER 4	/* Increase this and the sizes of the arrays below, if you need more. */
+
+#define	MAX_BUS_MASTER_BLOCKS	1		// This is the maximum we can bus master for (1024 bytes)
+
+#define	PORT_DATA				0
+#define	PORT_ERROR				1
+#define	PORT_SECTOR_COUNT		2
+#define	PORT_LBA_0				3
+#define	PORT_LBA_8				4
+#define	PORT_LBA_16				5
+#define	PORT_LBA_24				6
+#define	PORT_STAT_CMD			7
+#define	PORT_STAT_SEL			8
+#define	PORT_FAIL				9
+#define	PORT_ALT_STAT		   	10
+
+typedef struct
+	{
+	UCHAR		   	device;				// device code
+	UCHAR			byte6;				// device select register image
+	UCHAR			spigot;				// spigot number
+	UCHAR			sparebyte;			// placeholder
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	USHORT			spareword;			// placeholder
+	ULONG			blocks;				// number of blocks on device
+	}	OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+	{
+	USHORT		 ports[12];
+	USHORT		 regDmaDesc;					// address of the DMA discriptor register for direction of transfer
+	USHORT		 regDmaCmdStat;					// Byte #1 of DMA command status register
+	USHORT		 regDmaAddrPci;					// 32 bit register for PCI address of DMA
+	USHORT		 regDmaAddrLoc;					// 32 bit register for local bus address of DMA
+	USHORT		 regDmaCount;					// 32 bit register for DMA transfer count
+	USHORT		 regDmaMode;						// 32 bit register for DMA mode control
+	USHORT		 regRemap;						// 32 bit local space remap
+	USHORT		 regDesc;						// 32 bit local region descriptor
+	USHORT		 regRange;						// 32 bit local range
+	USHORT		 regIrqControl;					// 16 bit Interrupt enable/disable and status
+	USHORT		 regScratchPad;					// scratch pad I/O base address
+	USHORT		 regBase;						// Base I/O register for data space
+	USHORT		 basePort;						// PLX base I/O port
+	USHORT		 timingMode;					// timing mode currently set for adapter
+	ULONG		 timingAddress;					// address to use on adapter for current timing mode
+	OUR_DEVICE	 device[4];
+	IDE_STRUCT	 ide;
+	ULONG		 startSector;
+	USHORT		 sectorCount;
+	Scsi_Cmnd	*SCpnt;
+	VOID		*buffer;
+	USHORT		 expectingIRQ;
+	USHORT		 readPhase;
+	}	ADAPTER2220I, *PADAPTER2220I;
+
+#define HOSTDATA(host) ((PADAPTER2220I)&host->hostdata)
+
+
+static struct	Scsi_Host 	   *PsiHost[MAXADAPTER] = {NULL,};  // One for each adapter
+static			int				NumAdapters = 0;
+static			IDENTIFY_DATA	identifyData;
+static			SETUP			DaleSetup;
+
+/****************************************************************
+ *	Name:	WriteData	:LOCAL
+ *
+ *	Description:	Write data to device.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER2220I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+
+	timer = jiffies + TIMEOUT_DRQ;								// calculate the timeout value
+	do  {
+		if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+			{
+			outb_p (0, padapter->regDmaDesc);							// write operation
+			outl (padapter->timingAddress, padapter->regDmaAddrLoc);
+			outl (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+			outl ((ULONG)padapter->ide.ide.ide[2] * (ULONG)512, padapter->regDmaCount);
+			outb_p (1, padapter->regDmaMode);							// interrupts off
+			outb_p (0x03, padapter->regDmaCmdStat);						// kick the DMA engine in gear
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return 1;
+	}
+/****************************************************************
+ *	Name:	IdeCmd	:LOCAL
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER2220I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+	UCHAR	status;
+
+	outb_p (padapter->ide.ide.ides.spigot, pports[PORT_STAT_SEL]);	// select the spigot
+	outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]);			// select the drive
+	timer = jiffies + TIMEOUT_READY;							// calculate the timeout value
+	DEB(printk ("\npci2220i Issueing new command: 0x%X",padapter->ide.ide.ides.cmd));
+	do  {
+		status = inb_p (padapter->ports[PORT_STAT_CMD]);
+		if ( status & IDE_STATUS_DRDY )
+			{
+			outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+			outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+			outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+			outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+			padapter->expectingIRQ = 1;
+			outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+			if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+				return (WriteData (padapter));
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return status;
+	}
+/****************************************************************
+ *	Name:	SetupTransfer	:LOCAL
+ *
+ *	Description:	Setup a data transfer command.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *					drive	 - Drive/head register upper nibble only.
+ *
+ *	Returns:		TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER2220I padapter, UCHAR drive)
+	{
+	if ( padapter->sectorCount )
+		{
+		*(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+		padapter->ide.ide.ide[6] |= drive;
+//		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > MAX_BUS_MASTER_BLOCKS ) ? MAX_BUS_MASTER_BLOCKS : padapter->sectorCount;
+		padapter->sectorCount -= padapter->ide.ide.ides.sectors;	// bump the start and count for next xfer
+		padapter->startSector += padapter->ide.ide.ides.sectors;
+		return 0;
+		}
+	else
+		{
+		padapter->ide.ide.ides.cmd = 0;								// null out the command byte
+		padapter->SCpnt = NULL;
+		return 1;
+		}
+	}
+/****************************************************************
+ *	Name:	DecodeError	:LOCAL
+ *
+ *	Description:	Decode and process device errors.
+ *
+ *	Parameters:		pshost - Pointer to host data block.
+ *					status - Status register code.
+ *
+ *	Returns:		The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+	{
+	PADAPTER2220I	padapter = HOSTDATA(pshost);
+	UCHAR			error;
+
+	padapter->expectingIRQ = 0;
+	padapter->SCpnt = NULL;
+	if ( status & IDE_STATUS_WRITE_FAULT )
+		{
+		return DID_PARITY << 16;
+		}
+	if ( status & IDE_STATUS_BUSY )
+		return DID_BUS_BUSY << 16;
+
+	error = inb_p (padapter->ports[PORT_ERROR]);
+	DEB(printk ("\npci2220i error register: %x", error));
+	switch ( error )
+		{
+		case IDE_ERROR_AMNF:
+		case IDE_ERROR_TKONF:
+		case IDE_ERROR_ABRT:
+		case IDE_ERROR_IDFN:
+		case IDE_ERROR_UNC:
+		case IDE_ERROR_BBK:
+		default:
+			return DID_ERROR << 16;
+		}
+	return DID_ERROR << 16;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost = NULL;	// Pointer to host data block
+	PADAPTER2220I		padapter;		// Pointer to adapter control structure
+	USHORT		 	   *pports;			// I/O port array
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				status;
+	int					z;
+
+//	DEB(printk ("\npci2220i recieved interrupt\n"));
+
+	for ( z = 0; z < NumAdapters;  z++ )								// scan for interrupt to process
+		{
+		if ( PsiHost[z]->irq == (UCHAR)(irq & 0xFF) )
+			{
+			if ( inw_p (HOSTDATA(PsiHost[z])->regIrqControl) & 0x8000 )
+				{
+				shost = PsiHost[z];
+				break;
+				}
+			}
+		}
+
+	if ( !shost )
+		{
+		DEB (printk ("\npci2220i: not my interrupt"));
+		return;
+		}
+
+	padapter = HOSTDATA(shost);
+	pports = padapter->ports;
+	SCpnt = padapter->SCpnt;
+
+	if ( !padapter->expectingIRQ )
+		{
+		DEB(printk ("\npci2220i Unsolicited interrupt\n"));
+		return;
+		}
+	padapter->expectingIRQ = 0;
+
+	status = inb_p (padapter->ports[PORT_STAT_CMD]);					// read the device status
+	if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+		goto irqerror;
+
+	switch ( padapter->ide.ide.ides.cmd )								// decide how to handle the interrupt
+		{
+		case IDE_CMD_READ_MULTIPLE:
+			if ( padapter->readPhase == 1 )								// is this a bus master channel complete?
+				{
+				DEB(printk ("\npci2220i processing read interrupt cleanup"));
+				outb_p (0x08, padapter->regDmaCmdStat);					// cancel interrupt from DMA engine
+				padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+				if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+					{
+					SCpnt->result = DID_OK << 16;
+					padapter->SCpnt = NULL;
+					SCpnt->scsi_done (SCpnt);
+					return;
+					}
+				padapter->readPhase = 0;
+				if ( !(status = IdeCmd (padapter)) )
+					{
+					DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+					return;
+					}
+				}
+			if ( status & IDE_STATUS_DRQ )
+				{
+				DEB(printk ("\npci2220i processing read interrupt start bus master cycle"));
+				outb_p (8, padapter->regDmaDesc); 				   		// read operation
+				padapter->readPhase = 1;
+				padapter->expectingIRQ = 1;
+				outl   (padapter->timingAddress, padapter->regDmaAddrLoc);
+				outl   (virt_to_bus (padapter->buffer), padapter->regDmaAddrPci);
+				outl   ((ULONG)padapter->ide.ide.ides.sectors * (ULONG)512, padapter->regDmaCount);
+				outb_p (5, padapter->regDmaMode);				   		// interrupt enable/disable
+				outb_p (0x03, padapter->regDmaCmdStat);			   		// kick the DMA engine in gear
+				return;
+				}
+			break;
+
+		case IDE_CMD_WRITE_MULTIPLE:
+			DEB(printk ("\npci2220i processing write interrupt cleanup"));
+			padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+			if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+				{
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			if ( !(status = IdeCmd (padapter)) )
+				{
+				DEB (printk ("\npci2220i interrupt complete, waiting for another"));
+				return;
+				}
+			break;
+
+		case IDE_COMMAND_IDENTIFY:
+			{
+			PINQUIRYDATA	pinquiryData  = SCpnt->request_buffer;
+
+			DEB(printk ("\npci2220i processing verify interrupt cleanup"));
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+				memset (pinquiryData, 0, SCpnt->request_bufflen);		// Zero INQUIRY data structure.
+				pinquiryData->DeviceType = 0;
+				pinquiryData->Versions = 2;
+				pinquiryData->AdditionalLength = 35 - 4;
+
+				// Fill in vendor identification fields.
+				for ( z = 0;  z < 20;  z += 2 )
+					{
+					pinquiryData->VendorId[z]	  = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+					pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+					}
+
+				// Initialize unused portion of product id.
+				for ( z = 0;  z < 4;  z++ )
+					pinquiryData->ProductId[12 + z] = ' ';
+
+				// Move firmware revision from IDENTIFY data to
+				// product revision in INQUIRY data.
+				for ( z = 0;  z < 4;  z += 2 )
+					{
+					pinquiryData->ProductRevisionLevel[z]	 = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+					pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+					}
+
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			break;
+			}
+
+		default:
+			DEB(printk ("\npci2220i no real process here!"));
+			SCpnt->result = DID_OK << 16;
+			padapter->SCpnt = NULL;
+			SCpnt->scsi_done (SCpnt);
+			return;
+		}
+
+irqerror:;
+	DEB(printk ("\npci2220i error  Device Status: %X\n", status));
+	SCpnt->result = DecodeError (shost, status);
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Pci2220i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2220i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER2220I	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	POUR_DEVICE		pdev	 = &padapter->device[SCpnt->target];// Pointer to device information
+	UCHAR			rc;											// command return code
+
+	SCpnt->scsi_done = done;
+	padapter->ide.ide.ides.spigot = pdev->spigot;
+	padapter->buffer = SCpnt->request_buffer;
+	if (done)
+		{
+		if ( !pdev->device || SCpnt->lun )
+			{
+			SCpnt->result = DID_BAD_TARGET << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	else
+		{
+		printk("pci2220i_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	DEB (if(*cdb) printk ("\nCDB: %X-  %X %X %X %X %X %X %X %X %X %X ", SCpnt->cmd_len, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]));
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			{
+			padapter->ide.ide.ide[6] = pdev->byte6;
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+			break;
+			}
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			{
+			PREAD_CAPACITY_DATA	pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+			pdata->blksiz = 0x20000;
+			XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+			}
+
+		case SCSIOP_VERIFY:						// verify CDB
+			*(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+			padapter->ide.ide.ide[6] |= pdev->byte6;
+			padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+			break;
+
+		case SCSIOP_READ:						// read10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			padapter->readPhase = 0;
+			break;
+
+		case SCSIOP_READ6:						// read6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			padapter->readPhase = 0;
+			break;
+
+		case SCSIOP_WRITE:						// write10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+
+		default:
+			DEB (printk ("pci2220i_queuecommand: Unsupported command %02X\n", *cdb));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+		}
+
+	padapter->SCpnt = SCpnt;  									// Save this command data
+
+	rc = IdeCmd (padapter);
+	if ( rc )
+		{
+		padapter->expectingIRQ = 0;
+		DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+		SCpnt->result = DID_ERROR << 16;
+		done (SCpnt);
+		return 0;
+		}
+	if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+		{
+		if ( WriteData (padapter) )
+			{
+			padapter->expectingIRQ = 0;
+			DEB (printk ("pci2220i_queuecommand: %02X, %02X: Device failed to accept data\n", *cdb, padapter->ide.ide.ides.cmd));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	DEB (printk("  now waiting for initial interrupt "));
+	return 0;
+	}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Pci2220i_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("pci2220i_command: ..calling pci2220i_queuecommand\n"));
+
+	Pci2220i_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/****************************************************************
+ *	Name:			ReadFlash
+ *
+ *	Description:	Read information from controller Flash memory.
+ *
+ *	Parameters:		hostdata - Pointer to host interface data structure.
+ *					pdata	 - Pointer to data structures.
+ *					base	 - base address in Flash.
+ *					length	 - lenght of data space in bytes.
+ *
+ *	Returns:		Nothing.
+ *
+ ****************************************************************/
+VOID ReadFlash (PADAPTER2220I hostdata, VOID *pdata, ULONG base, ULONG length)
+	{
+	ULONG	 oldremap;
+	UCHAR	 olddesc;
+	ULONG	 z;
+	UCHAR	*pd = (UCHAR *)pdata;
+
+	oldremap = inl (hostdata->regRemap);									// save values to restore later
+	olddesc  = inb_p (hostdata->regDesc);
+
+	outl (base | 1, hostdata->regRemap);									// remap to Flash space as specified
+	outb_p (0x40, hostdata->regDesc);										// describe remap region as 8 bit
+	for ( z = 0;  z < length;  z++)											// get "length" data count
+		*pd++ = inb_p (hostdata->regBase + z);								// read in the data
+
+	outl (oldremap, hostdata->regRemap);									// restore remap register values
+	outb_p (olddesc, hostdata->regDesc);
+	}
+
+/****************************************************************
+ *	Name:	Pci2220i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Pci2220i_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					pci_index = 0;
+	struct Scsi_Host   *pshost;
+	PADAPTER2220I	    hostdata;
+	ULONG				modearray[] = {DALE_DATA_MODE2, DALE_DATA_MODE3, DALE_DATA_MODE4, DALE_DATA_MODE4P};
+	int					unit;
+	int					z;
+	int					setirq;
+
+	if ( pcibios_present () )
+		{
+		for ( pci_index = 0;  pci_index <= MAXADAPTER;  ++pci_index )
+			{
+			UCHAR	pci_bus, pci_device_fn;
+
+			if ( pcibios_find_device (VENDOR_PSI, DEVICE_DALE_1, pci_index, &pci_bus, &pci_device_fn) != 0 )
+				break;
+
+			pshost = scsi_register (tpnt, sizeof(ADAPTER2220I));
+			hostdata = HOSTDATA(pshost);
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &hostdata->basePort);
+			hostdata->basePort &= 0xFFFE;
+			DEB (printk ("\nBase Regs = %#04X", hostdata->basePort));
+			hostdata->regRemap		= hostdata->basePort + RTR_LOCAL_REMAP;				// 32 bit local space remap
+			DEB (printk (" %#04X", hostdata->regRemap));
+			hostdata->regDesc		= hostdata->basePort + RTR_REGIONS;	  				// 32 bit local region descriptor
+			DEB (printk (" %#04X", hostdata->regDesc));
+			hostdata->regRange		= hostdata->basePort + RTR_LOCAL_RANGE;				// 32 bit local range
+			DEB (printk (" %#04X", hostdata->regRange));
+			hostdata->regIrqControl	= hostdata->basePort + RTR_INT_CONTROL_STATUS;		// 16 bit interupt control and status
+			DEB (printk (" %#04X", hostdata->regIrqControl));
+			hostdata->regScratchPad	= hostdata->basePort + RTR_MAILBOX;	  				// 16 byte scratchpad I/O base address
+			DEB (printk (" %#04X", hostdata->regScratchPad));
+
+			pcibios_read_config_word (pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &hostdata->regBase);
+			hostdata->regBase &= 0xFFFE;
+			for ( z = 0;  z < 9;  z++ )													// build regester address array
+				hostdata->ports[z] = hostdata->regBase + 0x80 + (z * 4);
+			hostdata->ports[PORT_FAIL] = hostdata->regBase + REG_FAIL;
+			hostdata->ports[PORT_ALT_STAT] = hostdata->regBase + REG_ALT_STAT;
+			DEB (printk ("\nPorts ="));
+			DEB (for (z=0;z<11;z++) printk(" %#04X", hostdata->ports[z]););
+
+			hostdata->regDmaDesc	= hostdata->regBase + RTL_DMA1_DESC_PTR;			// address of the DMA discriptor register for direction of transfer
+			DEB (printk ("\nDMA Regs = %#04X", hostdata->regDmaDesc));
+			hostdata->regDmaCmdStat	= hostdata->regBase + RTL_DMA_COMMAND_STATUS + 1;	// Byte #1 of DMA command status register
+			DEB (printk (" %#04X", hostdata->regDmaCmdStat));
+			hostdata->regDmaAddrPci	= hostdata->regBase + RTL_DMA1_PCI_ADDR;			// 32 bit register for PCI address of DMA
+			DEB (printk (" %#04X", hostdata->regDmaAddrPci));
+			hostdata->regDmaAddrLoc	= hostdata->regBase + RTL_DMA1_LOCAL_ADDR;			// 32 bit register for local bus address of DMA
+			DEB (printk (" %#04X", hostdata->regDmaAddrLoc));
+			hostdata->regDmaCount	= hostdata->regBase + RTL_DMA1_COUNT;				// 32 bit register for DMA transfer count
+			DEB (printk (" %#04X", hostdata->regDmaCount));
+			hostdata->regDmaMode	= hostdata->regBase + RTL_DMA1_MODE + 1;			// 32 bit register for DMA mode control
+			DEB (printk (" %#04X", hostdata->regDmaMode));
+
+			if ( !inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES) )					// if no devices on this board
+				goto unregister;
+
+			pcibios_read_config_byte (pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pshost->irq);
+			setirq = 1;
+			for ( z = 0;  z < pci_index;  z++ )											// scan for shared interrupts
+				{
+				if ( PsiHost[z]->irq == pshost->irq )						// if shared then, don't posses
+					setirq = 0;
+				}
+			if ( setirq )																// if not shared, posses
+				{
+				if ( request_irq (pshost->irq, Irq_Handler, 0, "pci2220i", NULL) )
+					{
+					printk ("Unable to allocate IRQ for PSI-2220I controller.\n");
+					goto unregister;
+					}
+				}
+			PsiHost[pci_index]	= pshost;												// save SCSI_HOST pointer
+
+			pshost->unique_id	= hostdata->regBase;
+			pshost->max_id		= 4;
+
+			outb_p (0x01, hostdata->regRange);											// fix our range register because other drivers want to tromp on it
+
+			hostdata->timingMode	= inb_p (hostdata->regScratchPad + DALE_TIMING_MODE);
+			hostdata->timingAddress	= modearray[hostdata->timingMode - 2];
+			ReadFlash (hostdata, &DaleSetup, DALE_FLASH_SETUP, sizeof (SETUP));
+
+			for ( z = 0;  z < inb_p (hostdata->regScratchPad + DALE_NUM_DRIVES);  ++z )
+				{
+				unit = inb_p (hostdata->regScratchPad + DALE_CHANNEL_DEVICE_0 + z) & 0x0F;
+				hostdata->device[unit].device	 = inb_p (hostdata->regScratchPad + DALE_SCRATH_DEVICE_0 + unit);
+				hostdata->device[unit].byte6	 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+				hostdata->device[unit].spigot	 = (UCHAR)(1 << (unit >> 1));
+				hostdata->device[unit].sectors	 = DaleSetup.setupDevice[unit].sectors;
+				hostdata->device[unit].heads	 = DaleSetup.setupDevice[unit].heads;
+				hostdata->device[unit].cylinders = DaleSetup.setupDevice[unit].cylinders;
+				hostdata->device[unit].blocks	 = DaleSetup.setupDevice[unit].blocks;
+				DEB (printk ("\nHOSTDATA->device    = %X", hostdata->device[unit].device));
+				DEB (printk ("\n          byte6     = %X", hostdata->device[unit].byte6));
+				DEB (printk ("\n          spigot    = %X", hostdata->device[unit].spigot));
+				DEB (printk ("\n          sectors   = %X", hostdata->device[unit].sectors));
+				DEB (printk ("\n          heads     = %X", hostdata->device[unit].heads));
+				DEB (printk ("\n          cylinders = %X", hostdata->device[unit].cylinders));
+				DEB (printk ("\n          blocks    = %lX", hostdata->device[unit].blocks));
+				}
+
+			printk("\nPSI-2220I EIDE CONTROLLER: at I/O = %X/%X  IRQ = %d\n", hostdata->basePort, hostdata->regBase, pshost->irq);
+			printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+
+			NumAdapters++;
+			continue;
+unregister:;
+			scsi_unregister (pshost);
+			}
+		}
+	return NumAdapters;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Pci2220i_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("pci2220i_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Pci2220i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Pci2220i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Pci2220i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Pci2220i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	POUR_DEVICE	pdev;
+
+	pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+	geom[0] = pdev->heads;
+	geom[1] = pdev->sectors;
+	geom[2] = pdev->cylinders;
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PCI2220I;
+
+#include "scsi_module.c"
+#endif
+
diff -urN linux-2.0.37-pre8/drivers/scsi/pci2220i.h linux-2.0.37-pre9/drivers/scsi/pci2220i.h
--- linux-2.0.37-pre8/drivers/scsi/pci2220i.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/pci2220i.h	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,345 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		pci2220i.h
+ *
+ *	Description:	Header file for the SCSI driver for the PCI2220I
+ *					EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#ifndef _PCI2220I_H
+#define _PCI2220I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		Some defines that we like 				*/
+/************************************************/
+#define	CHAR		char
+#define	UCHAR		unsigned char
+#define	SHORT		short
+#define	USHORT		unsigned short
+#define	BOOL		unsigned short
+#define	LONG		long
+#define	ULONG		unsigned long
+#define	VOID		void
+
+/************************************************/
+/*		Timeout konstants		 				*/
+/************************************************/
+#define	TIMEOUT_READY				10		// 100 mSec
+#define	TIMEOUT_DRQ					40		// 400 mSec
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET		0x08
+#define IDE_COMMAND_READ			0x20
+#define IDE_COMMAND_WRITE			0x30
+#define IDE_COMMAND_RECALIBRATE		0x10
+#define IDE_COMMAND_SEEK			0x70
+#define IDE_COMMAND_SET_PARAMETERS	0x91
+#define IDE_COMMAND_VERIFY			0x40
+#define IDE_COMMAND_ATAPI_PACKET	0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY	0xA1
+#define	IDE_CMD_READ_MULTIPLE		0xC4
+#define	IDE_CMD_WRITE_MULTIPLE		0xC5
+#define	IDE_CMD_SET_MULTIPLE		0xC6
+#define IDE_COMMAND_WRITE_DMA		0xCA
+#define IDE_COMMAND_READ_DMA		0xC8
+#define IDE_COMMAND_IDENTIFY		0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR			0x01
+#define IDE_STATUS_INDEX			0x02
+#define IDE_STATUS_CORRECTED_ERROR	0x04
+#define IDE_STATUS_DRQ				0x08
+#define IDE_STATUS_DSC				0x10
+#define	IDE_STATUS_WRITE_FAULT		0x20
+#define IDE_STATUS_DRDY				0x40
+#define IDE_STATUS_BUSY				0x80
+
+// IDE error definitions
+#define	IDE_ERROR_AMNF				0x01
+#define	IDE_ERROR_TKONF				0x02
+#define	IDE_ERROR_ABRT				0x04
+#define	IDE_ERROR_MCR				0x08
+#define	IDE_ERROR_IDFN				0x10
+#define	IDE_ERROR_MC				0x20
+#define	IDE_ERROR_UNC				0x40
+#define	IDE_ERROR_BBK				0x80
+
+//	IDE interface structure
+typedef struct _IDE_STRUCT
+	{
+	union
+		{
+		UCHAR	ide[9];
+		struct
+			{
+			USHORT	data;
+			UCHAR	sectors;
+			UCHAR	lba[4];
+			UCHAR	cmd;
+			UCHAR	spigot;
+			}	ides;
+		} ide;
+	}	IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+	{
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+    USHORT TranslationFieldsValid:1;        // 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+    USHORT Reserved4[197];                  // 76
+	}	IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+	USHORT TranslationFieldsValid:1;     	// 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+	}	IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif	// PSI_EIDE_SCSIOP
+
+// function prototypes
+int Pci2220i_Detect			(Scsi_Host_Template *tpnt);
+int Pci2220i_Command			(Scsi_Cmnd *SCpnt);
+int Pci2220i_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Pci2220i_Abort			(Scsi_Cmnd *SCpnt);
+int Pci2220i_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Pci2220i_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Pci2220i;
+
+#define PCI2220I { NULL, NULL,						\
+			&Proc_Scsi_Pci2220i,/* proc_dir_entry */	\
+			NULL,		                			\
+			"PCI-2220I EIDE Disk Controller",		\
+			Pci2220i_Detect,							\
+			NULL,									\
+			NULL,	 								\
+			Pci2220i_Command,						\
+			Pci2220i_QueueCommand,					\
+			Pci2220i_Abort,							\
+			Pci2220i_Reset,							\
+			NULL,									\
+			Pci2220i_BiosParam,                 		\
+			1, 										\
+			-1, 									\
+			SG_NONE,		 						\
+			1, 										\
+			0, 										\
+			0, 										\
+			DISABLE_CLUSTERING }
+
+#endif
diff -urN linux-2.0.37-pre8/drivers/scsi/psi240i.c linux-2.0.37-pre9/drivers/scsi/psi240i.c
--- linux-2.0.37-pre8/drivers/scsi/psi240i.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/psi240i.c	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,717 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi240i.c
+ *
+ *	Description:	SCSI driver for the PSI240I EIDE interface card.
+ *
+ *-M*************************************************************************/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "psi240i.h"
+#include "psi_chip.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry Proc_Scsi_Psi240i =
+	{ PROC_SCSI_PSI240I, 7, "psi240i", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+//#define DEBUG 1
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#define MAXBOARDS 2	/* Increase this and the sizes of the arrays below, if you need more. */
+
+#define	PORT_DATA				0
+#define	PORT_ERROR				1
+#define	PORT_SECTOR_COUNT		2
+#define	PORT_LBA_0				3
+#define	PORT_LBA_8				4
+#define	PORT_LBA_16				5
+#define	PORT_LBA_24				6
+#define	PORT_STAT_CMD			7
+#define	PORT_SEL_FAIL			8
+#define	PORT_IRQ_STATUS			9
+#define	PORT_ADDRESS			10
+#define	PORT_FAIL				11
+#define	PORT_ALT_STAT		   	12
+
+typedef struct
+	{
+	UCHAR		   	device;				// device code
+	UCHAR			byte6;				// device select register image
+	UCHAR			spigot;				// spigot number
+	UCHAR			expectingIRQ;		// flag for expecting and interrupt
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	USHORT			spareword;			// placeholder
+	ULONG			blocks;				// number of blocks on device
+	}	OUR_DEVICE, *POUR_DEVICE;
+
+typedef struct
+	{
+	USHORT		 ports[13];
+	OUR_DEVICE	 device[8];
+	Scsi_Cmnd	*pSCmnd;
+	IDE_STRUCT	 ide;
+	ULONG		 startSector;
+	USHORT		 sectorCount;
+	Scsi_Cmnd	*SCpnt;
+	VOID		*buffer;
+	USHORT		 expectingIRQ;
+	}	ADAPTER240I, *PADAPTER240I;
+
+#define HOSTDATA(host) ((PADAPTER240I)&host->hostdata)
+
+static struct	Scsi_Host *PsiHost[6] = {NULL,};  /* One for each IRQ level (10-15) */
+static			IDENTIFY_DATA	identifyData;
+static			SETUP			ChipSetup;
+
+static	USHORT	portAddr[6] = {CHIP_ADRS_0, CHIP_ADRS_1, CHIP_ADRS_2, CHIP_ADRS_3, CHIP_ADRS_4, CHIP_ADRS_5};
+
+/****************************************************************
+ *	Name:	WriteData	:LOCAL
+ *
+ *	Description:	Write data to device.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		TRUE if drive does not assert DRQ in time.
+ *
+ ****************************************************************/
+static int WriteData (PADAPTER240I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+
+	timer = jiffies + TIMEOUT_DRQ;								// calculate the timeout value
+	do  {
+		if ( inb_p (pports[PORT_STAT_CMD]) & IDE_STATUS_DRQ )
+			{
+			outsw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ide[2] * 256);
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return 1;
+	}
+/****************************************************************
+ *	Name:	IdeCmd	:LOCAL
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *
+ *	Returns:		Zero if no error or status register contents on error.
+ *
+ ****************************************************************/
+static UCHAR IdeCmd (PADAPTER240I padapter)
+	{
+	ULONG	timer;
+	USHORT *pports = padapter->ports;
+	UCHAR	status;
+
+	outb_p (padapter->ide.ide.ides.spigot, pports[PORT_SEL_FAIL]);	// select the spigot
+	outb_p (padapter->ide.ide.ide[6], pports[PORT_LBA_24]);			// select the drive
+	timer = jiffies + TIMEOUT_READY;							// calculate the timeout value
+	do  {
+		status = inb_p (padapter->ports[PORT_STAT_CMD]);
+		if ( status & IDE_STATUS_DRDY )
+			{
+			outb_p (padapter->ide.ide.ide[2], pports[PORT_SECTOR_COUNT]);
+			outb_p (padapter->ide.ide.ide[3], pports[PORT_LBA_0]);
+			outb_p (padapter->ide.ide.ide[4], pports[PORT_LBA_8]);
+			outb_p (padapter->ide.ide.ide[5], pports[PORT_LBA_16]);
+			padapter->expectingIRQ = 1;
+			outb_p (padapter->ide.ide.ide[7], pports[PORT_STAT_CMD]);
+
+			if ( padapter->ide.ide.ides.cmd == IDE_CMD_WRITE_MULTIPLE )
+				return (WriteData (padapter));
+
+			return 0;
+			}
+		}	while ( timer > jiffies );									// test for timeout
+
+	padapter->ide.ide.ides.cmd = 0;									// null out the command byte
+	return status;
+	}
+/****************************************************************
+ *	Name:	SetupTransfer	:LOCAL
+ *
+ *	Description:	Setup a data transfer command.
+ *
+ *	Parameters:		padapter - Pointer adapter data structure.
+ *					drive	 - Drive/head register upper nibble only.
+ *
+ *	Returns:		TRUE if no data to transfer.
+ *
+ ****************************************************************/
+static int SetupTransfer (PADAPTER240I padapter, UCHAR drive)
+	{
+	if ( padapter->sectorCount )
+		{
+		*(ULONG *)padapter->ide.ide.ides.lba = padapter->startSector;
+		padapter->ide.ide.ide[6] |= drive;
+		padapter->ide.ide.ides.sectors = ( padapter->sectorCount > SECTORSXFER ) ? SECTORSXFER : padapter->sectorCount;
+		padapter->sectorCount -= padapter->ide.ide.ides.sectors;	// bump the start and count for next xfer
+		padapter->startSector += padapter->ide.ide.ides.sectors;
+		return 0;
+		}
+	else
+		{
+		padapter->ide.ide.ides.cmd = 0;								// null out the command byte
+		padapter->SCpnt = NULL;
+		return 1;
+		}
+	}
+/****************************************************************
+ *	Name:	DecodeError	:LOCAL
+ *
+ *	Description:	Decode and process device errors.
+ *
+ *	Parameters:		pshost - Pointer to host data block.
+ *					status - Status register code.
+ *
+ *	Returns:		The driver status code.
+ *
+ ****************************************************************/
+static ULONG DecodeError (struct Scsi_Host *pshost, UCHAR status)
+	{
+	PADAPTER240I	padapter = HOSTDATA(pshost);
+	UCHAR			error;
+
+	padapter->expectingIRQ = 0;
+	padapter->SCpnt = NULL;
+	if ( status & IDE_STATUS_WRITE_FAULT )
+		{
+		return DID_PARITY << 16;
+		}
+	if ( status & IDE_STATUS_BUSY )
+		return DID_BUS_BUSY << 16;
+
+	error = inb_p (padapter->ports[PORT_ERROR]);
+	DEB(printk ("\npsi240i error register: %x", error));
+	switch ( error )
+		{
+		case IDE_ERROR_AMNF:
+		case IDE_ERROR_TKONF:
+		case IDE_ERROR_ABRT:
+		case IDE_ERROR_IDFN:
+		case IDE_ERROR_UNC:
+		case IDE_ERROR_BBK:
+		default:
+			return DID_ERROR << 16;
+		}
+	return DID_ERROR << 16;
+	}
+/****************************************************************
+ *	Name:	Irq_Handler	:LOCAL
+ *
+ *	Description:	Interrupt handler.
+ *
+ *	Parameters:		irq		- Hardware IRQ number.
+ *					dev_id	-
+ *					regs	-
+ *
+ *	Returns:		TRUE if drive is not ready in time.
+ *
+ ****************************************************************/
+static void Irq_Handler (int irq, void *dev_id, struct pt_regs *regs)
+	{
+	struct Scsi_Host   *shost;			// Pointer to host data block
+	PADAPTER240I		padapter;		// Pointer to adapter control structure
+	USHORT		 	   *pports;			// I/O port array
+	Scsi_Cmnd		   *SCpnt;
+	UCHAR				status;
+	int					z;
+
+	DEB(printk ("\npsi240i recieved interrupt\n"));
+
+	shost = PsiHost[irq - 10];
+	if ( !shost )
+		panic ("Splunge!");
+
+	padapter = HOSTDATA(shost);
+	pports = padapter->ports;
+	SCpnt = padapter->SCpnt;
+
+	if ( !padapter->expectingIRQ )
+		{
+		DEB(printk ("\npsi240i Unsolicited interrupt\n"));
+		return;
+		}
+	padapter->expectingIRQ = 0;
+
+	status = inb_p (padapter->ports[PORT_STAT_CMD]);			// read the device status
+	if ( status & (IDE_STATUS_ERROR | IDE_STATUS_WRITE_FAULT) )
+		goto irqerror;
+
+	DEB(printk ("\npsi240i processing interrupt"));
+	switch ( padapter->ide.ide.ides.cmd )							// decide how to handle the interrupt
+		{
+		case IDE_CMD_READ_MULTIPLE:
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], padapter->buffer, (USHORT)padapter->ide.ide.ides.sectors * 256);
+				padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+				if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+					{
+					SCpnt->result = DID_OK << 16;
+					padapter->SCpnt = NULL;
+					SCpnt->scsi_done (SCpnt);
+					return;
+					}
+				if ( !(status = IdeCmd (padapter)) )
+					return;
+				}
+			break;
+
+		case IDE_CMD_WRITE_MULTIPLE:
+			padapter->buffer += padapter->ide.ide.ides.sectors * 512;
+			if ( SetupTransfer (padapter, padapter->ide.ide.ide[6] & 0xF0) )
+				{
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			if ( !(status = IdeCmd (padapter)) )
+				return;
+			break;
+
+		case IDE_COMMAND_IDENTIFY:
+			{
+			PINQUIRYDATA	pinquiryData  = SCpnt->request_buffer;
+
+			if ( status & IDE_STATUS_DRQ )
+				{
+				insw (pports[PORT_DATA], &identifyData, sizeof (identifyData) >> 1);
+
+				memset (pinquiryData, 0, SCpnt->request_bufflen);		// Zero INQUIRY data structure.
+				pinquiryData->DeviceType = 0;
+				pinquiryData->Versions = 2;
+				pinquiryData->AdditionalLength = 35 - 4;
+
+				// Fill in vendor identification fields.
+				for ( z = 0;  z < 20;  z += 2 )
+					{
+					pinquiryData->VendorId[z]	  = ((UCHAR *)identifyData.ModelNumber)[z + 1];
+					pinquiryData->VendorId[z + 1] = ((UCHAR *)identifyData.ModelNumber)[z];
+					}
+
+				// Initialize unused portion of product id.
+				for ( z = 0;  z < 4;  z++ )
+					pinquiryData->ProductId[12 + z] = ' ';
+
+				// Move firmware revision from IDENTIFY data to
+				// product revision in INQUIRY data.
+				for ( z = 0;  z < 4;  z += 2 )
+					{
+					pinquiryData->ProductRevisionLevel[z]	 = ((UCHAR *)identifyData.FirmwareRevision)[z + 1];
+					pinquiryData->ProductRevisionLevel[z + 1] = ((UCHAR *)identifyData.FirmwareRevision)[z];
+					}
+
+				SCpnt->result = DID_OK << 16;
+				padapter->SCpnt = NULL;
+				SCpnt->scsi_done (SCpnt);
+				return;
+				}
+			break;
+			}
+
+		default:
+			SCpnt->result = DID_OK << 16;
+			padapter->SCpnt = NULL;
+			SCpnt->scsi_done (SCpnt);
+			return;
+		}
+
+irqerror:;
+	DEB(printk ("\npsi240i error  Device Status: %X\n", status));
+	SCpnt->result = DecodeError (shost, status);
+	SCpnt->scsi_done (SCpnt);
+	}
+/****************************************************************
+ *	Name:	Psi240i_QueueCommand
+ *
+ *	Description:	Process a queued command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					done  - Pointer to done function to call.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Psi240i_QueueCommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+	{
+	UCHAR		   *cdb = (UCHAR *)SCpnt->cmnd;					// Pointer to SCSI CDB
+	PADAPTER240I	padapter = HOSTDATA(SCpnt->host);			// Pointer to adapter control structure
+	POUR_DEVICE		pdev	 = &padapter->device[SCpnt->target];// Pointer to device information
+	UCHAR			rc;											// command return code
+
+	SCpnt->scsi_done = done;
+	padapter->ide.ide.ides.spigot = pdev->spigot;
+	padapter->buffer = SCpnt->request_buffer;
+	if (done)
+		{
+		if ( !pdev->device || SCpnt->lun )
+			{
+			SCpnt->result = DID_BAD_TARGET << 16;
+			done (SCpnt);
+			return 0;
+			}
+		}
+	else
+		{
+		printk("psi240i_queuecommand: %02X: done can't be NULL\n", *cdb);
+		return 0;
+		}
+
+	switch ( *cdb )
+		{
+		case SCSIOP_INQUIRY:   					// inquiry CDB
+			{
+			padapter->ide.ide.ide[6] = pdev->byte6;
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_IDENTIFY;
+			break;
+			}
+
+		case SCSIOP_TEST_UNIT_READY:			// test unit ready CDB
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+
+		case SCSIOP_READ_CAPACITY:			  	// read capctiy CDB
+			{
+			PREAD_CAPACITY_DATA	pdata = (PREAD_CAPACITY_DATA)SCpnt->request_buffer;
+
+			pdata->blksiz = 0x20000;
+			XANY2SCSI ((UCHAR *)&pdata->blks, pdev->blocks);
+			SCpnt->result = DID_OK << 16;
+			done (SCpnt);
+			return 0;
+			}
+
+		case SCSIOP_VERIFY:						// verify CDB
+			*(ULONG *)padapter->ide.ide.ides.lba = XSCSI2LONG (&cdb[2]);
+			padapter->ide.ide.ide[6] |= pdev->byte6;
+			padapter->ide.ide.ide[2] = (UCHAR)((USHORT)cdb[8] | ((USHORT)cdb[7] << 8));
+			padapter->ide.ide.ides.cmd = IDE_COMMAND_VERIFY;
+			break;
+
+		case SCSIOP_READ:						// read10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			break;
+
+		case SCSIOP_READ6:						// read6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_READ_MULTIPLE;
+			break;
+
+		case SCSIOP_WRITE:						// write10 CDB
+			padapter->startSector = XSCSI2LONG (&cdb[2]);
+			padapter->sectorCount = (USHORT)cdb[8] | ((USHORT)cdb[7] << 8);
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+		case SCSIOP_WRITE6:						// write6  CDB
+			padapter->startSector = SCSI2LONG (&cdb[1]);
+			padapter->sectorCount = cdb[4];
+			SetupTransfer (padapter, pdev->byte6);
+			padapter->ide.ide.ides.cmd = IDE_CMD_WRITE_MULTIPLE;
+			break;
+
+		default:
+			DEB (printk ("psi240i_queuecommand: Unsupported command %02X\n", *cdb));
+			SCpnt->result = DID_ERROR << 16;
+			done (SCpnt);
+			return 0;
+		}
+
+	padapter->SCpnt = SCpnt;  									// Save this command data
+
+	rc = IdeCmd (padapter);
+	if ( rc )
+		{
+		padapter->expectingIRQ = 0;
+		DEB (printk ("psi240i_queuecommand: %02X, %02X: Device failed to respond for command\n", *cdb, padapter->ide.ide.ides.cmd));
+		SCpnt->result = DID_ERROR << 16;
+		done (SCpnt);
+		return 0;
+		}
+	DEB (printk("psi240i_queuecommand: %02X, %02X now waiting for interrupt ", *cdb, padapter->ide.ide.ides.cmd));
+	return 0;
+	}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+	{
+	SCpnt->SCp.Status++;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Command
+ *
+ *	Description:	Process a command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Status code.
+ *
+ ****************************************************************/
+int Psi240i_Command (Scsi_Cmnd *SCpnt)
+	{
+	DEB(printk("psi240i_command: ..calling psi240i_queuecommand\n"));
+
+	Psi240i_QueueCommand (SCpnt, internal_done);
+
+    SCpnt->SCp.Status = 0;
+	while (!SCpnt->SCp.Status)
+		barrier ();
+	return SCpnt->result;
+	}
+/***************************************************************************
+ *	Name:			ReadChipMemory
+ *
+ *	Description:	Read information from controller memory.
+ *
+ *	Parameters:		psetup	- Pointer to memory image of setup information.
+ *					base	- base address of memory.
+ *					length	- lenght of data space in bytes.
+ *					port	- I/O address of data port.
+ *
+ *	Returns:		Nothing.
+ *
+ **************************************************************************/
+void ReadChipMemory (void *pdata, USHORT base, USHORT length, USHORT port)
+	{
+	USHORT	z, zz;
+	UCHAR	*pd = (UCHAR *)pdata;
+	outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup data port
+	zz = 0;
+	while ( zz < length )
+		{
+		outw_p (base, port + REG_ADDRESS);				// setup address
+
+		for ( z = 0;  z < 8;  z++ )
+			{
+			if ( (zz + z) < length )
+			*pd++ = inb_p (port + z);	// read data byte
+			}
+		zz += 8;
+		base += 8;
+		}
+	}
+/****************************************************************
+ *	Name:	Psi240i_Detect
+ *
+ *	Description:	Detect and initialize our boards.
+ *
+ *	Parameters:		tpnt - Pointer to SCSI host template structure.
+ *
+ *	Returns:		Number of adapters found.
+ *
+ ****************************************************************/
+int Psi240i_Detect (Scsi_Host_Template *tpnt)
+	{
+	int					board;
+	int					count = 0;
+	int					unit;
+	int					z;
+	USHORT				port;
+	CHIP_CONFIG_N		chipConfig;
+	CHIP_DEVICE_N		chipDevice[8];
+	struct Scsi_Host   *pshost;
+	ULONG				flags;
+
+	for ( board = 0;  board < 6;  board++ )					// scan for I/O ports
+		{
+		port = portAddr[board];								// get base address to test
+		if ( check_region (port, 16) )						// test for I/O addresses available
+			continue;										//   nope
+		if ( inb_p (port + REG_FAIL) != CHIP_ID )			// do the first test for likley hood that it is us
+			continue;
+		outb_p (SEL_NONE, port + REG_SEL_FAIL);				// setup EEPROM/RAM access
+		outw (0, port + REG_ADDRESS);						// setup EEPROM address zero
+		if ( inb_p (port) != 0x55 )							// test 1st byte
+			continue;										//   nope
+		if ( inb_p (port + 1) != 0xAA )						// test 2nd byte
+			continue;										//   nope
+
+		// at this point our board is found and can be accessed.  Now we need to initialize
+		// our informatation and register with the kernel.
+
+
+		ReadChipMemory (&chipConfig, CHIP_CONFIG, sizeof (chipConfig), port);
+		ReadChipMemory (&chipDevice, CHIP_DEVICE, sizeof (chipDevice), port);
+		ReadChipMemory (&ChipSetup, CHIP_EEPROM_DATA, sizeof (ChipSetup), port);
+
+		if ( !chipConfig.numDrives )						// if no devices on this board
+			continue;
+
+		pshost = scsi_register (tpnt, sizeof(ADAPTER240I));
+
+		save_flags (flags);
+		cli ();
+		if ( request_irq (chipConfig.irq, Irq_Handler, 0, "psi240i", NULL) )
+			{
+			printk ("Unable to allocate IRQ for PSI-240I controller.\n");
+			restore_flags (flags);
+			goto unregister;
+			}
+
+		PsiHost[chipConfig.irq - 10] = pshost;
+		pshost->unique_id = port;
+		pshost->io_port = port;
+		pshost->n_io_port = 16;  /* Number of bytes of I/O space used */
+		pshost->irq = chipConfig.irq;
+
+		for ( z = 0;  z < 11;  z++ )						// build regester address array
+			HOSTDATA(pshost)->ports[z] = port + z;
+		HOSTDATA(pshost)->ports[11] = port + REG_FAIL;
+		HOSTDATA(pshost)->ports[12] = port + REG_ALT_STAT;
+		DEB (printk ("\nPorts ="));
+		DEB (for (z=0;z<13;z++) printk(" %#04X",HOSTDATA(pshost)->ports[z]););
+
+		for ( z = 0;  z < chipConfig.numDrives;  ++z )
+			{
+			unit = chipDevice[z].channel & 0x0F;
+			HOSTDATA(pshost)->device[unit].device	 = ChipSetup.setupDevice[unit].device;
+			HOSTDATA(pshost)->device[unit].byte6	 = (UCHAR)(((unit & 1) << 4) | 0xE0);
+			HOSTDATA(pshost)->device[unit].spigot	 = (UCHAR)(1 << (unit >> 1));
+			HOSTDATA(pshost)->device[unit].sectors	 = ChipSetup.setupDevice[unit].sectors;
+			HOSTDATA(pshost)->device[unit].heads	 = ChipSetup.setupDevice[unit].heads;
+			HOSTDATA(pshost)->device[unit].cylinders = ChipSetup.setupDevice[unit].cylinders;
+			HOSTDATA(pshost)->device[unit].blocks	 = ChipSetup.setupDevice[unit].blocks;
+			DEB (printk ("\nHOSTDATA->device    = %X", HOSTDATA(pshost)->device[unit].device));
+			DEB (printk ("\n          byte6     = %X", HOSTDATA(pshost)->device[unit].byte6));
+			DEB (printk ("\n          spigot    = %X", HOSTDATA(pshost)->device[unit].spigot));
+			DEB (printk ("\n          sectors   = %X", HOSTDATA(pshost)->device[unit].sectors));
+			DEB (printk ("\n          heads     = %X", HOSTDATA(pshost)->device[unit].heads));
+			DEB (printk ("\n          cylinders = %X", HOSTDATA(pshost)->device[unit].cylinders));
+			DEB (printk ("\n          blocks    = %lX", HOSTDATA(pshost)->device[unit].blocks));
+			}
+
+		restore_flags (flags);
+		printk("\nPSI-240I EIDE CONTROLLER: at I/O = %x  IRQ = %d\n", port, chipConfig.irq);
+		printk("(C) 1997 Perceptive Solutions, Inc. All rights reserved\n\n");
+		count++;
+		continue;
+
+unregister:;
+		scsi_unregister (pshost);
+		}
+	return count;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Abort
+ *
+ *	Description:	Process the Abort command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *
+ *	Returns:		Allways snooze.
+ *
+ ****************************************************************/
+int Psi240i_Abort (Scsi_Cmnd *SCpnt)
+	{
+	DEB (printk ("psi240i_abort\n"));
+	return SCSI_ABORT_SNOOZE;
+	}
+/****************************************************************
+ *	Name:	Psi240i_Reset
+ *
+ *	Description:	Process the Reset command from the SCSI manager.
+ *
+ *	Parameters:		SCpnt - Pointer to SCSI command structure.
+ *					flags - Flags about the reset command
+ *
+ *	Returns:		No active command at this time, so this means
+ *					that each time we got some kind of response the
+ *					last time through.  Tell the mid-level code to
+ *					request sense information in order to decide what
+ *					to do next.
+ *
+ ****************************************************************/
+int Psi240i_Reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+	{
+	return SCSI_RESET_PUNT;
+	}
+
+#include "sd.h"
+
+/****************************************************************
+ *	Name:	Psi240i_BiosParam
+ *
+ *	Description:	Process the biosparam request from the SCSI manager to
+ *					return C/H/S data.
+ *
+ *	Parameters:		disk - Pointer to SCSI disk structure.
+ *					dev	 - Major/minor number from kernel.
+ *					geom - Pointer to integer array to place geometry data.
+ *
+ *	Returns:		zero.
+ *
+ ****************************************************************/
+int Psi240i_BiosParam (Scsi_Disk *disk, kdev_t dev, int geom[])
+	{
+	POUR_DEVICE	pdev;
+
+	pdev = &(HOSTDATA(disk->device->host)->device[disk->device->id]);
+
+	geom[0] = pdev->heads;
+	geom[1] = pdev->sectors;
+	geom[2] = pdev->cylinders;
+	return 0;
+	}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = PSI240I;
+
+#include "scsi_module.c"
+#endif
+
diff -urN linux-2.0.37-pre8/drivers/scsi/psi240i.h linux-2.0.37-pre9/drivers/scsi/psi240i.h
--- linux-2.0.37-pre8/drivers/scsi/psi240i.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/psi240i.h	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,344 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi240i.h
+ *
+ *	Description:	Header file for the SCSI driver for the PSI240I
+ *					EIDE interface card.
+ *
+ *-M*************************************************************************/
+#ifndef _PSI240I_H
+#define _PSI240I_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+#ifndef	PSI_EIDE_SCSIOP
+#define	PSI_EIDE_SCSIOP	1
+
+/************************************************/
+/*		Some defines that we like 				*/
+/************************************************/
+#define	CHAR		char
+#define	UCHAR		unsigned char
+#define	SHORT		short
+#define	USHORT		unsigned short
+#define	BOOL		unsigned short
+#define	LONG		long
+#define	ULONG		unsigned long
+#define	VOID		void
+
+/************************************************/
+/*		Timeout konstants		 				*/
+/************************************************/
+#define	TIMEOUT_READY				10		// 100 mSec
+#define	TIMEOUT_DRQ					40		// 400 mSec
+
+/************************************************/
+/*		Misc. macros			 				*/
+/************************************************/
+#define ANY2SCSI(up, p)					\
+((UCHAR *)up)[0] = (((ULONG)(p)) >> 8);	\
+((UCHAR *)up)[1] = ((ULONG)(p));
+
+#define SCSI2LONG(up)					\
+( (((long)*(((UCHAR *)up))) << 16)		\
++ (((long)(((UCHAR *)up)[1])) << 8)		\
++ ((long)(((UCHAR *)up)[2])) )
+
+#define XANY2SCSI(up, p)				\
+((UCHAR *)up)[0] = ((long)(p)) >> 24;	\
+((UCHAR *)up)[1] = ((long)(p)) >> 16;	\
+((UCHAR *)up)[2] = ((long)(p)) >> 8;	\
+((UCHAR *)up)[3] = ((long)(p));
+
+#define XSCSI2LONG(up)					\
+( (((long)(((UCHAR *)up)[0])) << 24)	\
++ (((long)(((UCHAR *)up)[1])) << 16)	\
++ (((long)(((UCHAR *)up)[2])) <<  8)	\
++ ((long)(((UCHAR *)up)[3])) )
+
+/************************************************/
+/*		SCSI CDB operation codes 				*/
+/************************************************/
+#define SCSIOP_TEST_UNIT_READY		0x00
+#define SCSIOP_REZERO_UNIT			0x01
+#define SCSIOP_REWIND				0x01
+#define SCSIOP_REQUEST_BLOCK_ADDR	0x02
+#define SCSIOP_REQUEST_SENSE		0x03
+#define SCSIOP_FORMAT_UNIT			0x04
+#define SCSIOP_READ_BLOCK_LIMITS	0x05
+#define SCSIOP_REASSIGN_BLOCKS		0x07
+#define SCSIOP_READ6				0x08
+#define SCSIOP_RECEIVE				0x08
+#define SCSIOP_WRITE6				0x0A
+#define SCSIOP_PRINT				0x0A
+#define SCSIOP_SEND					0x0A
+#define SCSIOP_SEEK6				0x0B
+#define SCSIOP_TRACK_SELECT			0x0B
+#define SCSIOP_SLEW_PRINT			0x0B
+#define SCSIOP_SEEK_BLOCK			0x0C
+#define SCSIOP_PARTITION			0x0D
+#define SCSIOP_READ_REVERSE			0x0F
+#define SCSIOP_WRITE_FILEMARKS		0x10
+#define SCSIOP_FLUSH_BUFFER			0x10
+#define SCSIOP_SPACE				0x11
+#define SCSIOP_INQUIRY				0x12
+#define SCSIOP_VERIFY6				0x13
+#define SCSIOP_RECOVER_BUF_DATA		0x14
+#define SCSIOP_MODE_SELECT			0x15
+#define SCSIOP_RESERVE_UNIT			0x16
+#define SCSIOP_RELEASE_UNIT			0x17
+#define SCSIOP_COPY					0x18
+#define SCSIOP_ERASE				0x19
+#define SCSIOP_MODE_SENSE			0x1A
+#define SCSIOP_START_STOP_UNIT		0x1B
+#define SCSIOP_STOP_PRINT			0x1B
+#define SCSIOP_LOAD_UNLOAD			0x1B
+#define SCSIOP_RECEIVE_DIAGNOSTIC	0x1C
+#define SCSIOP_SEND_DIAGNOSTIC		0x1D
+#define SCSIOP_MEDIUM_REMOVAL		0x1E
+#define SCSIOP_READ_CAPACITY		0x25
+#define SCSIOP_READ					0x28
+#define SCSIOP_WRITE				0x2A
+#define SCSIOP_SEEK					0x2B
+#define SCSIOP_LOCATE				0x2B
+#define SCSIOP_WRITE_VERIFY			0x2E
+#define SCSIOP_VERIFY				0x2F
+#define SCSIOP_SEARCH_DATA_HIGH		0x30
+#define SCSIOP_SEARCH_DATA_EQUAL	0x31
+#define SCSIOP_SEARCH_DATA_LOW		0x32
+#define SCSIOP_SET_LIMITS			0x33
+#define SCSIOP_READ_POSITION		0x34
+#define SCSIOP_SYNCHRONIZE_CACHE	0x35
+#define SCSIOP_COMPARE				0x39
+#define SCSIOP_COPY_COMPARE			0x3A
+#define SCSIOP_WRITE_DATA_BUFF		0x3B
+#define SCSIOP_READ_DATA_BUFF		0x3C
+#define SCSIOP_CHANGE_DEFINITION	0x40
+#define SCSIOP_READ_SUB_CHANNEL		0x42
+#define SCSIOP_READ_TOC				0x43
+#define SCSIOP_READ_HEADER			0x44
+#define SCSIOP_PLAY_AUDIO			0x45
+#define SCSIOP_PLAY_AUDIO_MSF		0x47
+#define SCSIOP_PLAY_TRACK_INDEX		0x48
+#define SCSIOP_PLAY_TRACK_RELATIVE	0x49
+#define SCSIOP_PAUSE_RESUME			0x4B
+#define SCSIOP_LOG_SELECT			0x4C
+#define SCSIOP_LOG_SENSE			0x4D
+#define SCSIOP_MODE_SELECT10		0x55
+#define SCSIOP_MODE_SENSE10			0x5A
+#define SCSIOP_LOAD_UNLOAD_SLOT		0xA6
+#define SCSIOP_MECHANISM_STATUS		0xBD
+#define SCSIOP_READ_CD				0xBE
+
+// IDE command definitions
+#define IDE_COMMAND_ATAPI_RESET		0x08
+#define IDE_COMMAND_READ			0x20
+#define IDE_COMMAND_WRITE			0x30
+#define IDE_COMMAND_RECALIBRATE		0x10
+#define IDE_COMMAND_SEEK			0x70
+#define IDE_COMMAND_SET_PARAMETERS	0x91
+#define IDE_COMMAND_VERIFY			0x40
+#define IDE_COMMAND_ATAPI_PACKET	0xA0
+#define IDE_COMMAND_ATAPI_IDENTIFY	0xA1
+#define	IDE_CMD_READ_MULTIPLE		0xC4
+#define	IDE_CMD_WRITE_MULTIPLE		0xC5
+#define	IDE_CMD_SET_MULTIPLE		0xC6
+#define IDE_COMMAND_WRITE_DMA		0xCA
+#define IDE_COMMAND_READ_DMA		0xC8
+#define IDE_COMMAND_IDENTIFY		0xEC
+
+// IDE status definitions
+#define IDE_STATUS_ERROR			0x01
+#define IDE_STATUS_INDEX			0x02
+#define IDE_STATUS_CORRECTED_ERROR	0x04
+#define IDE_STATUS_DRQ				0x08
+#define IDE_STATUS_DSC				0x10
+#define	IDE_STATUS_WRITE_FAULT		0x20
+#define IDE_STATUS_DRDY				0x40
+#define IDE_STATUS_BUSY				0x80
+
+// IDE error definitions
+#define	IDE_ERROR_AMNF				0x01
+#define	IDE_ERROR_TKONF				0x02
+#define	IDE_ERROR_ABRT				0x04
+#define	IDE_ERROR_MCR				0x08
+#define	IDE_ERROR_IDFN				0x10
+#define	IDE_ERROR_MC				0x20
+#define	IDE_ERROR_UNC				0x40
+#define	IDE_ERROR_BBK				0x80
+
+//	IDE interface structure
+typedef struct _IDE_STRUCT
+	{
+	union
+		{
+		UCHAR	ide[9];
+		struct
+			{
+			USHORT	data;
+			UCHAR	sectors;
+			UCHAR	lba[4];
+			UCHAR	cmd;
+			UCHAR	spigot;
+			}	ides;
+		} ide;
+	}	IDE_STRUCT;
+
+// SCSI read capacity structure
+typedef	struct _READ_CAPACITY_DATA
+	{
+	ULONG blks;				/* total blocks (converted to little endian) */
+	ULONG blksiz;			/* size of each (converted to little endian) */
+	}	READ_CAPACITY_DATA, *PREAD_CAPACITY_DATA;
+
+// SCSI inquiry data
+typedef struct _INQUIRYDATA
+	{
+	UCHAR DeviceType			:5;
+	UCHAR DeviceTypeQualifier	:3;
+	UCHAR DeviceTypeModifier	:7;
+	UCHAR RemovableMedia		:1;
+    UCHAR Versions;
+    UCHAR ResponseDataFormat;
+    UCHAR AdditionalLength;
+    UCHAR Reserved[2];
+	UCHAR SoftReset				:1;
+	UCHAR CommandQueue			:1;
+	UCHAR Reserved2				:1;
+	UCHAR LinkedCommands		:1;
+	UCHAR Synchronous			:1;
+	UCHAR Wide16Bit				:1;
+	UCHAR Wide32Bit				:1;
+	UCHAR RelativeAddressing	:1;
+    UCHAR VendorId[8];
+    UCHAR ProductId[16];
+    UCHAR ProductRevisionLevel[4];
+    UCHAR VendorSpecific[20];
+    UCHAR Reserved3[40];
+	}	INQUIRYDATA, *PINQUIRYDATA;
+
+// IDE IDENTIFY data
+typedef struct _IDENTIFY_DATA
+	{
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+    USHORT TranslationFieldsValid:1;        // 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+    USHORT Reserved4[197];                  // 76
+	}	IDENTIFY_DATA, *PIDENTIFY_DATA;
+
+// Identify data without the Reserved4.
+typedef struct _IDENTIFY_DATA2 {
+    USHORT GeneralConfiguration;            // 00
+    USHORT NumberOfCylinders;               // 02
+    USHORT Reserved1;                       // 04
+    USHORT NumberOfHeads;                   // 06
+    USHORT UnformattedBytesPerTrack;        // 08
+    USHORT UnformattedBytesPerSector;       // 0A
+    USHORT SectorsPerTrack;                 // 0C
+    USHORT VendorUnique1[3];                // 0E
+    USHORT SerialNumber[10];                // 14
+    USHORT BufferType;                      // 28
+    USHORT BufferSectorSize;                // 2A
+    USHORT NumberOfEccBytes;                // 2C
+    USHORT FirmwareRevision[4];             // 2E
+    USHORT ModelNumber[20];                 // 36
+    UCHAR  MaximumBlockTransfer;            // 5E
+    UCHAR  VendorUnique2;                   // 5F
+    USHORT DoubleWordIo;                    // 60
+    USHORT Capabilities;                    // 62
+    USHORT Reserved2;                       // 64
+    UCHAR  VendorUnique3;                   // 66
+    UCHAR  PioCycleTimingMode;              // 67
+    UCHAR  VendorUnique4;                   // 68
+    UCHAR  DmaCycleTimingMode;              // 69
+	USHORT TranslationFieldsValid:1;     	// 6A
+    USHORT Reserved3:15;
+    USHORT NumberOfCurrentCylinders;        // 6C
+    USHORT NumberOfCurrentHeads;            // 6E
+    USHORT CurrentSectorsPerTrack;          // 70
+    ULONG  CurrentSectorCapacity;           // 72
+	}	IDENTIFY_DATA2, *PIDENTIFY_DATA2;
+
+#endif	// PSI_EIDE_SCSIOP
+
+// function prototypes
+int Psi240i_Detect			(Scsi_Host_Template *tpnt);
+int Psi240i_Command			(Scsi_Cmnd *SCpnt);
+int Psi240i_QueueCommand	(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *));
+int Psi240i_Abort			(Scsi_Cmnd *SCpnt);
+int Psi240i_Reset			(Scsi_Cmnd *SCpnt, unsigned int flags);
+int Psi240i_BiosParam		(Disk *disk, kdev_t dev, int geom[]);
+
+#ifndef NULL
+	#define NULL 0
+#endif
+
+extern struct proc_dir_entry Proc_Scsi_Psi240i;
+
+#define PSI240I { NULL, NULL,						\
+			&Proc_Scsi_Psi240i,/* proc_dir_entry */	\
+			NULL,		                			\
+			"PSI-240I EIDE Disk Controller",		\
+			Psi240i_Detect,							\
+			NULL,									\
+			NULL,	 								\
+			Psi240i_Command,						\
+			Psi240i_QueueCommand,					\
+			Psi240i_Abort,							\
+			Psi240i_Reset,							\
+			NULL,									\
+			Psi240i_BiosParam,                 		\
+			1, 										\
+			-1, 									\
+			SG_NONE,		 						\
+			1, 										\
+			0, 										\
+			0, 										\
+			DISABLE_CLUSTERING }
+
+#endif
diff -urN linux-2.0.37-pre8/drivers/scsi/psi_chip.h linux-2.0.37-pre9/drivers/scsi/psi_chip.h
--- linux-2.0.37-pre8/drivers/scsi/psi_chip.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/psi_chip.h	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,194 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PSI-240I device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:	psi_chip.h
+ *
+ *	Description:	This file contains the interface defines and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+#ifndef PSI_CHIP
+#define PSI_CHIP
+
+/************************************************/
+/*		Misc konstants							*/
+/************************************************/
+#define	CHIP_MAXDRIVES			8
+
+/************************************************/
+/*		Chip I/O addresses						*/
+/************************************************/
+#define	CHIP_ADRS_0				0x0130
+#define	CHIP_ADRS_1				0x0150
+#define	CHIP_ADRS_2				0x0190
+#define	CHIP_ADRS_3				0x0210
+#define	CHIP_ADRS_4				0x0230
+#define	CHIP_ADRS_5				0x0250
+
+/************************************************/
+/*		EEPROM locations		*/
+/************************************************/
+#define	CHIP_EEPROM_BIOS		0x0000		// BIOS base address
+#define	CHIP_EEPROM_DATA		0x2000	   	// SETUP data base address
+#define	CHIP_EEPROM_FACTORY		0x2400	   	// FACTORY data base address
+#define	CHIP_EEPROM_SETUP		0x3000	   	// SETUP PROGRAM base address
+
+#define	CHIP_EEPROM_SIZE		32768U	   	// size of the entire EEPROM
+#define	CHIP_EEPROM_BIOS_SIZE	8192	   	// size of the BIOS in bytes
+#define	CHIP_EEPROM_DATA_SIZE	4096	   	// size of factory, setup, log data block in bytes
+#define	CHIP_EEPROM_SETUP_SIZE	20480U	   	// size of the setup program in bytes
+
+/************************************************/
+/*		Chip Interrupts							*/
+/************************************************/
+#define	CHIP_IRQ_10				0x72
+#define	CHIP_IRQ_11				0x73
+#define	CHIP_IRQ_12				0x74
+
+/************************************************/
+/*		Chip Setup addresses		*/
+/************************************************/
+#define	CHIP_SETUP_BASE			0x0000C000L
+
+/************************************************/
+/*		Chip Register address offsets	*/
+/************************************************/
+#define	REG_DATA				0x00
+#define	REG_ERROR				0x01
+#define	REG_SECTOR_COUNT		0x02
+#define	REG_LBA_0				0x03
+#define	REG_LBA_8				0x04
+#define	REG_LBA_16				0x05
+#define	REG_LBA_24				0x06
+#define	REG_STAT_CMD			0x07
+#define	REG_SEL_FAIL			0x08
+#define	REG_IRQ_STATUS			0x09
+#define	REG_ADDRESS				0x0A
+#define	REG_FAIL				0x0C
+#define	REG_ALT_STAT		   	0x0E
+#define	REG_DRIVE_ADRS			0x0F
+
+/************************************************/
+/*		Chip RAM locations		*/
+/************************************************/
+#define	CHIP_DEVICE				0x8000
+#define	CHIP_DEVICE_0			0x8000
+#define CHIP_DEVICE_1			0x8008
+#define	CHIP_DEVICE_2			0x8010
+#define	CHIP_DEVICE_3			0x8018
+#define	CHIP_DEVICE_4			0x8020
+#define	CHIP_DEVICE_5			0x8028
+#define	CHIP_DEVICE_6			0x8030
+#define	CHIP_DEVICE_7			0x8038
+typedef struct
+	{
+	UCHAR	channel;		// channel of this device (0-8).
+	UCHAR	spt;			// Sectors Per Track.
+	ULONG	spc;			// Sectors Per Cylinder.
+	}	CHIP_DEVICE_N;
+
+#define	CHIP_CONFIG				0x8100		// address of boards configuration.
+typedef struct
+	{
+	UCHAR		irq;			// interrupt request channel number
+	UCHAR		numDrives;		// Number of accessable drives
+	UCHAR		fastFormat;	 	// Boolean for fast format enable
+	}	CHIP_CONFIG_N;
+
+#define	CHIP_MAP				0x8108 		// eight byte device type map.
+
+
+#define	CHIP_RAID				0x8120 		// array of RAID signature structures and LBA
+#define	CHIP_RAID_1				0x8120
+#define CHIP_RAID_2				0x8130
+#define	CHIP_RAID_3				0x8140
+#define	CHIP_RAID_4				0x8150
+
+/************************************************/
+/*		Chip Register Masks		*/
+/************************************************/
+#define	CHIP_ID					0x7B
+#define	SEL_RAM					0x8000
+#define	MASK_FAIL				0x80
+
+/************************************************/
+/*		Chip cable select bits		*/
+/************************************************/
+#define	SECTORSXFER				8
+
+/************************************************/
+/*		Chip cable select bits		*/
+/************************************************/
+#define	SEL_NONE				0x00
+#define	SEL_1					0x01
+#define	SEL_2					0x02
+#define	SEL_3					0x04
+#define	SEL_4					0x08
+
+/************************************************/
+/*		Programmable Interrupt Controller*/
+/************************************************/
+#define	PIC1					0x20		// first 8259 base port address
+#define	PIC2					0xA0		// second 8259 base port address
+#define	INT_OCW1				1			// Operation Control Word 1: IRQ mask
+#define	EOI						0x20		// non-specific end-of-interrupt
+
+/************************************************/
+/*		Device/Geometry controls				*/
+/************************************************/
+#define GEOMETRY_NONE		 	0x0			// No device
+#define GEOMETRY_AUTO			0x1			// Geometry set automatically
+#define GEOMETRY_USER		 	0x2			// User supplied geometry
+
+#define	DEVICE_NONE				0x0			// No device present
+#define	DEVICE_INACTIVE			0x1			// device present but not registered active
+#define	DEVICE_ATAPI			0x2			// ATAPI device (CD_ROM, Tape, Etc...)
+#define	DEVICE_DASD_NONLBA		0x3			// Non LBA incompatible device
+#define	DEVICE_DASD_LBA			0x4			// LBA compatible device
+
+/************************************************/
+/*		Setup Structure Definitions	*/
+/************************************************/
+typedef struct							// device setup parameters
+	{
+	UCHAR			geometryControl;	// geometry control flags
+	UCHAR		   	device;				// device code
+	USHORT			sectors;			// number of sectors per track
+	USHORT			heads;				// number of heads
+	USHORT			cylinders;			// number of cylinders for this device
+	ULONG			blocks;				// number of blocks on device
+	USHORT			spare1;
+	USHORT			spare2;
+	} SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct		// master setup structure
+	{
+	USHORT 			startupDelay;
+	USHORT 			promptBIOS;
+	USHORT 			fastFormat;
+	USHORT			spare2;
+	USHORT			spare3;
+	USHORT			spare4;
+	USHORT			spare5;
+	USHORT			spare6;
+	SETUP_DEVICE	setupDevice[8];
+	}	SETUP, *PSETUP;
+
+#endif
\ No newline at end of file
diff -urN linux-2.0.37-pre8/drivers/scsi/psi_dale.h linux-2.0.37-pre9/drivers/scsi/psi_dale.h
--- linux-2.0.37-pre8/drivers/scsi/psi_dale.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/psi_dale.h	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,187 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:		psi_dale.h
+ *
+ *	Description:	This file contains the interface defines and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef PSI_DALE
+#define PSI_DALE
+
+/************************************************/
+/*		Dale PCI setup							*/
+/************************************************/
+#define	VENDOR_PSI			0x1256
+#define	DEVICE_DALE_1		0x4401		/* 'D1' */
+
+/************************************************/
+/*		Misc konstants							*/
+/************************************************/
+#define	DALE_MAXDRIVES			4
+#define	SECTORSXFER				8
+#define	BYTES_PER_SECTOR		512
+#define	DEFAULT_TIMING_MODE		5
+
+/************************************************/
+/*		EEPROM locations						*/
+/************************************************/
+#define	DALE_FLASH_PAGE_SIZE	128				// number of bytes per page
+#define	DALE_FLASH_SIZE			65536L
+
+#define	DALE_FLASH_BIOS			0x00080000L		// BIOS base address
+#define	DALE_FLASH_SETUP		0x00088000L		// SETUP PROGRAM base address offset from BIOS
+#define	DALE_FLASH_RAID			0x00088400L		// RAID signature storage
+#define	DALE_FLASH_FACTORY		0x00089000L		// FACTORY data base address offset from BIOS
+
+#define	DALE_FLASH_BIOS_SIZE	32768U			// size of FLASH BIOS REGION
+
+/************************************************/
+/*		DALE Register address offsets			*/
+/************************************************/
+#define	REG_DATA				0x80
+#define	REG_ERROR				0x84
+#define	REG_SECTOR_COUNT		0x88
+#define	REG_LBA_0				0x8C
+#define	REG_LBA_8				0x90
+#define	REG_LBA_16				0x94
+#define	REG_LBA_24				0x98
+#define	REG_STAT_CMD			0x9C
+#define	REG_STAT_SEL			0xA0
+#define	REG_FAIL				0xB0
+#define	REG_ALT_STAT			0xB8
+#define	REG_DRIVE_ADRS			0xBC
+
+#define	DALE_DATA_SLOW			0x00040000L
+#define	DALE_DATA_MODE2			0x00040000L
+#define	DALE_DATA_MODE3			0x00050000L
+#define	DALE_DATA_MODE4			0x00060000L
+#define	DALE_DATA_MODE4P		0x00070000L
+
+#define RTR_LOCAL_RANGE					0x000
+#define RTR_LOCAL_REMAP					0x004
+#define RTR_EXP_RANGE					0x010
+#define RTR_EXP_REMAP					0x014
+#define RTR_REGIONS						0x018
+#define RTR_DM_MASK						0x01C
+#define RTR_DM_LOCAL_BASE				0x020
+#define RTR_DM_IO_BASE					0x024
+#define RTR_DM_PCI_REMAP				0x028
+#define RTR_DM_IO_CONFIG				0x02C
+#define RTR_MAILBOX						0x040
+#define RTR_LOCAL_DOORBELL				0x060
+#define RTR_PCI_DOORBELL				0x064
+#define RTR_INT_CONTROL_STATUS 			0x068
+#define RTR_EEPROM_CONTROL_STATUS		0x06C
+
+#define RTL_DMA0_MODE					0x00
+#define RTL_DMA0_PCI_ADDR				0x04
+#define RTL_DMA0_LOCAL_ADDR				0x08
+#define RTL_DMA0_COUNT					0x0C
+#define RTL_DMA0_DESC_PTR				0x10
+#define RTL_DMA1_MODE					0x14
+#define RTL_DMA1_PCI_ADDR				0x18
+#define RTL_DMA1_LOCAL_ADDR				0x1C
+#define RTL_DMA1_COUNT					0x20
+#define RTL_DMA1_DESC_PTR				0x24
+#define RTL_DMA_COMMAND_STATUS			0x28
+#define RTL_DMA_ARB0					0x2C
+#define RTL_DMA_ARB1					0x30
+
+/************************************************/
+/*		Dale Scratchpad locations				*/
+/************************************************/
+#define	DALE_CHANNEL_DEVICE_0	0		// device channel locations
+#define	DALE_CHANNEL_DEVICE_1	1
+#define	DALE_CHANNEL_DEVICE_2	2
+#define	DALE_CHANNEL_DEVICE_3	3
+
+#define	DALE_SCRATH_DEVICE_0	4		// device type codes
+#define	DALE_SCRATH_DEVICE_1	5
+#define DALE_SCRATH_DEVICE_2	6
+#define	DALE_SCRATH_DEVICE_3	7
+
+#define	DALE_RAID_0_STATUS		8
+#define DALE_RAID_1_STATUS		9
+
+#define	DALE_TIMING_MODE		12		// bus master timing mode (2, 3, 4, 5)
+#define	DALE_NUM_DRIVES			13		// number of addressable drives on this board
+#define	DALE_RAID_ON			14 		// RAID status On
+#define	DALE_LAST_ERROR			15		// Last error code from BIOS
+
+/************************************************/
+/*		Dale cable select bits					*/
+/************************************************/
+#define	SEL_NONE				0x00
+#define	SEL_1					0x01
+#define	SEL_2					0x02
+
+/************************************************/
+/*		Programmable Interrupt Controller		*/
+/************************************************/
+#define	PIC1					0x20				// first 8259 base port address
+#define	PIC2					0xA0				// second 8259 base port address
+#define	INT_OCW1				1					// Operation Control Word 1: IRQ mask
+#define	EOI						0x20				// non-specific end-of-interrupt
+
+/************************************************/
+/*		Device/Geometry controls				*/
+/************************************************/
+#define GEOMETRY_NONE	 	0x0			// No device
+#define GEOMETRY_SET		0x1			// Geometry set
+#define	GEOMETRY_LBA		0x2			// Geometry set in default LBA mode
+#define	GEOMETRY_PHOENIX	0x3			// Geometry set in Pheonix BIOS compatibility mode
+
+#define	DEVICE_NONE			0x0			// No device present
+#define	DEVICE_INACTIVE		0x1			// device present but not registered active
+#define	DEVICE_ATAPI		0x2			// ATAPI device (CD_ROM, Tape, Etc...)
+#define	DEVICE_DASD_NONLBA	0x3			// Non LBA incompatible device
+#define	DEVICE_DASD_LBA		0x4			// LBA compatible device
+
+/************************************************/
+/*		Setup Structure Definitions				*/
+/************************************************/
+typedef struct		// device setup parameters
+	{
+	UCHAR	geometryControl;	// geometry control flags
+	UCHAR	device;				// device code
+	USHORT	sectors;			// number of sectors per track
+	USHORT	heads;				// number of heads
+	USHORT	cylinders;			// number of cylinders for this device
+	ULONG	blocks;				// number of blocks on device
+	ULONG	realCapacity;		// number of real blocks on this device for drive changed testing
+	} SETUP_DEVICE, *PSETUP_DEVICE;
+
+typedef struct		// master setup structure
+	{
+	USHORT			startupDelay;
+	BOOL			promptBIOS;
+	BOOL			fastFormat;
+	BOOL			shareInterrupt;
+	BOOL			rebootRebuil;
+	USHORT			timingMode;
+	USHORT			spare5;
+	USHORT			spare6;
+	SETUP_DEVICE	setupDevice[4];
+	}	SETUP, *PSETUP;
+
+#endif
diff -urN linux-2.0.37-pre8/drivers/scsi/psi_roy.h linux-2.0.37-pre9/drivers/scsi/psi_roy.h
--- linux-2.0.37-pre8/drivers/scsi/psi_roy.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/psi_roy.h	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,337 @@
+/*+M*************************************************************************
+ * Perceptive Solutions, Inc. PCI-2000 device driver proc support for Linux.
+ *
+ * Copyright (c) 1997 Perceptive Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *	File Name:	psi_roy.h
+ *
+ *	Description:	This file contains the host interface command and
+ *					error codes.
+ *
+ *-M*************************************************************************/
+
+#ifndef	ROY_HOST
+#define	ROY_HOST
+
+/************************************************/
+/*		PCI setup								*/
+/************************************************/
+#define	VENDOR_PSI			0x1256
+#define	DEVICE_ROY_1		0x5201		/* 'R1' */
+
+/************************************************/
+/*		controller constants					*/
+/************************************************/
+#define MAXADAPTER			4			// Increase this and the sizes of the arrays below, if you need more.
+#define	MAX_BUS				2
+#define	MAX_UNITS			16
+#define	TIMEOUT_COMMAND		30 			// number of jiffies for command busy timeout
+
+/************************************************/
+/*		I/O address offsets						*/
+/************************************************/
+#define RTR_MAILBOX						0x040
+#define RTR_LOCAL_DOORBELL				0x060
+#define RTR_PCI_DOORBELL				0x064
+
+/************************************************/
+/*												*/
+/*			Host command codes					*/
+/*												*/
+/************************************************/
+#define	CMD_READ_CHS		0x01		/* read sectors as specified (CHS mode) */
+#define	CMD_READ			0x02		/* read sectors as specified (RBA mode) */
+#define	CMD_READ_SG			0x03		/* read sectors using scatter/gather list */
+#define	CMD_WRITE_CHS		0x04		/* write sectors as specified (CHS mode) */
+#define	CMD_WRITE			0x05		/* write sectors as specified (RBA mode) */
+#define	CMD_WRITE_SG		0x06		/* write sectors using scatter/gather list (LBA mode) */
+#define	CMD_READ_CHS_SG		0x07		/* read sectors using scatter/gather list (CHS mode) */
+#define	CMD_WRITE_CHS_SG	0x08		/* write sectors using scatter/gather list (CHS mode) */
+#define	CMD_VERIFY_CHS		0x09		/* verify data on sectors as specified (CHS mode) */
+#define	CMD_VERIFY			0x0A		/* verify data on sectors as specified (RBA mode) */
+#define	CMD_DASD_CDB		0x0B		/* process CDB for a DASD device */
+#define	CMD_DASD_CDB_SG		0x0C		/* process CDB for a DASD device with scatter/gather */
+
+#define	CMD_READ_ABS		0x10		/* read absolute disk */
+#define	CMD_WRITE_ABS		0x11		/* write absolute disk */
+#define	CMD_VERIFY_ABS		0x12		/* verify absolute disk */
+#define	CMD_TEST_READY		0x13		/* test unit ready and return status code */
+#define	CMD_LOCK_DOOR		0x14		/* lock device door */
+#define	CMD_UNLOCK_DOOR		0x15		/* unlock device door */
+#define	CMD_EJECT_MEDIA		0x16		/* eject the media */
+#define	CMD_UPDATE_CAP		0x17		/* update capacity information */
+#define	CMD_TEST_PRIV		0x18		/* test and setup private format media */
+
+
+#define	CMD_SCSI_THRU		0x30		/* SCSI pass through CDB */
+#define	CMD_SCSI_THRU_SG	0x31		/* SCSI pass through CDB with scatter/gather */
+#define	CMD_SCSI_REQ_SENSE	0x32		/* SCSI pass through request sense after check condition */
+
+#define	CMD_DASD_RAID_RQ	0x35		/* request DASD RAID drive data */
+#define	CMD_DASD_RAID_RQ0	0x31			/* byte 1 subcommand to query for RAID 0 informatation */
+#define	CMD_DASD_RAID_RQ1	0x32			/* byte 1 subcommand to query for RAID 1 informatation */
+#define	CMD_DASD_RAID_RQ5	0x33			/* byte 1 subcommand to query for RAID 5 informatation */
+
+#define	CMD_DASD_SCSI_INQ	0x36		/* do DASD inquire and return in SCSI format */
+#define	CMD_DASD_CAP		0x37		/* read DASD capacity */
+#define	CMD_DASD_INQ		0x38		/* do DASD inquire for type data and return SCSI/EIDE inquiry */
+#define	CMD_SCSI_INQ		0x39		/* do SCSI inquire */
+#define	CMD_READ_SETUP		0x3A		/* Get setup structures from controller */
+#define	CMD_WRITE_SETUP		0x3B		/* Put setup structures in controller and burn in flash */
+#define	CMD_READ_CONFIG		0x3C		/* Get the entire configuration and setup structures */
+#define	CMD_WRITE_CONFIG	0x3D		/* Put the entire configuration and setup structures in flash */
+
+#define	CMD_TEXT_DEVICE		0x3E		/* obtain device text */
+#define	CMD_TEXT_SIGNON		0x3F		/* get sign on banner */
+
+#define	CMD_QUEUE			0x40		/* any command below this generates a queue tag interrupt to host*/
+
+#define	CMD_PREFETCH		0x40		/* prefetch sectors as specified */
+#define	CMD_TEST_WRITE		0x41		/* Test a device for write protect */
+#define	CMD_LAST_STATUS		0x42		/* get last command status and error data*/
+#define	CMD_ABORT			0x43		/* abort command as specified */
+#define	CMD_ERROR			0x44		/* fetch error code from a tagged op */
+#define	CMD_DONE			0x45		/* done with operation */
+#define	CMD_DIAGNOSTICS		0x46		/* execute controller diagnostics and wait for results */
+#define	CMD_FEATURE_MODE	0x47		/* feature mode control word */
+#define	CMD_DASD_INQUIRE	0x48		/* inquire as to DASD SCSI device (32 possible) */
+#define	CMD_FEATURE_QUERY	0x49		/* query the feature control word */
+#define	CMD_DASD_EJECT		0x4A		/* Eject removable media for DASD type */
+#define	CMD_DASD_LOCK		0x4B		/* Lock removable media for DASD type */
+#define	CMD_DASD_TYPE		0x4C		/* obtain DASD device type */
+#define	CMD_NUM_DEV			0x4D		/* obtain the number of devices connected to the controller */
+#define	CMD_GET_PARMS		0x4E		/* obtain device parameters */
+#define	CMD_SPECIFY			0x4F		/* specify operating system for scatter/gather operations */
+
+#define	CMD_RAID_GET_DEV	0x50		/* read RAID device geometry */
+#define CMD_RAID_READ		0x51		/* read RAID 1 parameter block */
+#define	CMD_RAID_WRITE		0x52		/* write RAID 1 parameter block */
+#define	CMD_RAID_LITEUP		0x53		/* Light up the drive light for identification */
+#define	CMD_RAID_REBUILD	0x54		/* issue a RAID 1 pair rebuild */
+#define	CMD_RAID_MUTE		0x55		/* mute RAID failure alarm */
+#define	CMD_RAID_FAIL		0x56		/* induce a RAID failure */
+#define	CMD_RAID_STATUS		0x57		/* get status of RAID pair */
+#define	CMD_RAID_STOP		0x58		/* stop any reconstruct in progress */
+#define CMD_RAID_START		0x59		/* start reconstruct */
+#define	CMD_RAID0_READ		0x5A		/* read RAID 0 parameter block */
+#define	CMD_RAID0_WRITE		0x5B		/* write RAID 0 parameter block */
+#define	CMD_RAID5_READ		0x5C		/* read RAID 5 parameter block */
+#define	CMD_RAID5_WRITE		0x5D		/* write RAID 5 parameter block */
+
+#define	CMD_ERASE_TABLES	0x5F		/* erase partition table and RAID signatutures */
+
+#define	CMD_SCSI_GET		0x60		/* get SCSI pass through devices */
+#define	CMD_SCSI_TIMEOUT	0x61		/* set SCSI pass through timeout */
+#define	CMD_SCSI_ERROR		0x62		/* get SCSI pass through request sense length and residual data count */
+#define	CMD_GET_SPARMS		0x63		/* get SCSI bus and user parms */
+#define	CMD_SCSI_ABORT		0x64		/* abort by setting time-out to zero */
+
+#define	CMD_CHIRP_CHIRP		0x77		/* make a chirp chirp sound */
+#define	CMD_GET_LAST_DONE	0x78		/* get tag of last done in progress */
+#define	CMD_GET_FEATURES	0x79		/* get feature code and ESN */
+#define CMD_CLEAR_CACHE		0x7A		/* Clear cache on specified device */
+#define	CMD_BIOS_TEST		0x7B		/* Test whether or not to load BIOS */
+#define	CMD_WAIT_FLUSH		0x7C		/* wait for cache flushed and invalidate read cache */
+#define	CMD_RESET_BUS		0x7D		/* reset the SCSI bus */
+#define	CMD_STARTUP_QRY		0x7E		/* startup in progress query */
+#define	CMD_RESET			0x7F		/* reset the controller */
+
+#define	CMD_RESTART_RESET	0x80		/* reload and restart the controller at any reset issued */
+#define	CMD_SOFT_RESET		0x81		/* do a soft reset NOW! */
+
+/************************************************/
+/*												*/
+/*				Host return errors				*/
+/*												*/
+/************************************************/
+#define	ERR08_TAGGED		0x80		/* doorbell error ored with tag */
+
+#define	ERR16_NONE			0x0000		/* no errors */
+#define	ERR16_SC_COND_MET	0x0004		/* SCSI status - Condition Met */
+#define	ERR16_CMD			0x0101		/* command error */
+#define	ERR16_SC_CHECK_COND	0x0002		/* SCSI status - Check Condition */
+#define	ERR16_CMD_NOT		0x0201		/* command not supported */
+#define ERR16_NO_DEVICE     0x0301		/* invalid device selection */
+#define	ERR16_SECTOR		0x0202		/* bad sector */
+#define	ERR16_PROTECT		0x0303		/* write protected */
+#define	ERR16_NOSECTOR		0x0404		/* sector not found */
+#define	ERR16_MEDIA			0x0C0C		/* invalid media */
+#define	ERR16_CONTROL		0x2020		/* controller error */
+#define	ERR16_CONTROL_DMA	0x2120		/* controller DMA engine error */
+#define	ERR16_NO_ALARM		0x2220		/* alarm is not active */
+#define	ERR16_OP_BUSY		0x2320		/* operation busy */
+#define	ERR16_SEEK			0x4040		/* seek failure */
+#define	ERR16_DEVICE_FAIL	0x4140		/* device has failed */
+#define ERR16_TIMEOUT		0x8080		/* timeout error */
+#define	ERR16_DEV_NOT_READY	0xAAAA		/* drive not ready */
+#define	ERR16_UNDEFINED		0xBBBB		/* undefined error */
+#define	ERR16_WRITE_FAULT	0xCCCC		/* write fault */
+#define ERR16_INVALID_DEV	0x4001		/* invalid device access */
+#define	ERR16_DEVICE_BUSY	0x4002		/* device is busy */
+#define	ERR16_MEMORY		0x4003		/* device pass thru requires too much memory */
+#define	ERR16_NO_FEATURE	0x40FA		/* feature no implemented */
+#define	ERR16_NOTAG			0x40FD		/* no tag space available */
+#define	ERR16_NOT_READY		0x40FE		/* controller not ready error */
+#define	ERR16_SETUP_FLASH	0x5050		/* error when writing setup to flash memory */
+#define	ERR16_SETUP_SIZE	0x5051		/* setup block size error */
+#define	ERR16_SENSE			0xFFFF		/* sense opereration failed */
+#define	ERR16_SC_BUSY		0x0008		/* SCSI status - Busy */
+#define	ERR16_SC_RES_CONFL	0x0018		/* SCSI status - Reservation Conflict */
+#define	ERR16_SC_CMD_TERM	0x0022		/* SCSI status - Command Terminated */
+#define	ERR16_SC_OTHER		0x00FF		/* SCSI status - not recognized (any value masked) */
+#define	ERR16_MEDIA_CHANGED	0x8001		/* devices media has been changed */
+
+#define	ERR32_NONE			0x00000000	/* no errors */
+#define	ERR32_SC_COND_MET	0x00000004	/* SCSI status - Condition Met */
+#define	ERR32_CMD			0x00010101	/* command error */
+#define	ERR32_SC_CHECK_COND	0x00020002	/* SCSI status - Check Condition */
+#define	ERR32_CMD_NOT		0x00030201	/* command not supported */
+#define ERR32_NO_DEVICE     0x00040301	/* invalid device selection */
+#define	ERR32_SECTOR		0x00050202	/* bad sector */
+#define	ERR32_PROTECT		0x00060303	/* write protected */
+#define	ERR32_NOSECTOR		0x00070404	/* sector not found */
+#define	ERR32_MEDIA			0x00080C0C	/* invalid media */
+#define	ERR32_CONTROL		0x00092020	/* controller error */
+#define	ERR32_CONTROL_DMA	0x000A2120	/* Controller DMA error */
+#define	ERR32_NO_ALARM		0x000B2220 	/* alarm is not active */
+#define	ERR32_OP_BUSY		0x000C2320	/* operation busy */
+#define	ERR32_SEEK			0x000D4040	/* seek failure */
+#define	ERR32_DEVICE_FAIL	0x000E4140	/* device has failed */
+#define ERR32_TIMEOUT		0x000F8080	/* timeout error */
+#define	ERR32_DEV_NOT_READY	0x0010AAAA	/* drive not ready */
+#define	ERR32_UNDEFINED		0x0011BBBB	/* undefined error */
+#define	ERR32_WRITE_FAULT	0x0012CCCC	/* write fault */
+#define ERR32_INVALID_DEV	0x00134001	/* invalid device access */
+#define	ERR32_DEVICE_BUSY	0x00144002	/* device is busy */
+#define	ERR32_MEMORY		0x00154003	/* device pass thru requires too much memory */
+#define	ERR32_NO_FEATURE	0x001640FA	/* feature no implemented */
+#define	ERR32_NOTAG			0x001740FD	/* no tag space available */
+#define	ERR32_NOT_READY		0x001840FE	/* controller not ready error */
+#define	ERR32_SETUP_FLASH	0x00195050	/* error when writing setup to flash memory */
+#define	ERR32_SETUP_SIZE	0x001A5051	/* setup block size error */
+#define	ERR32_SENSE			0x001BFFFF	/* sense opereration failed */
+#define	ERR32_SC_BUSY		0x001C0008	/* SCSI status - Busy */
+#define	ERR32_SC_RES_CONFL	0x001D0018	/* SCSI status - Reservation Conflict */
+#define	ERR32_SC_CMD_TERM	0x001E0022	/* SCSI status - Command Terminated */
+#define	ERR32_SC_OTHER		0x001F00FF	/* SCSI status - not recognized (any value masked) */
+#define	ERR32_MEDIA_CHANGED	0x00208001	/* devices media has been changed */
+
+/************************************************/
+/*												*/
+/*	Host Operating System specification codes	*/
+/*												*/
+/************************************************/
+#define	SPEC_INTERRUPT		0x80		/* specification requires host interrupt */
+#define	SPEC_BACKWARD_SG	0x40		/* specification requires scatter/gather items reversed */
+#define	SPEC_DOS_BLOCK		0x01		/* DOS DASD blocking on pass through */
+#define	SPEC_OS2_V3			0x02		/* OS/2 Warp */
+#define	SPCE_SCO_3242		0x04		/* SCO 3.4.2.2 */
+#define	SPEC_QNX_4X			0x05		/* QNX 4.XX */
+#define	SPEC_NOVELL_NWPA	0x08		/* Novell NWPA scatter/gather support */
+
+/************************************************/
+/*												*/
+/*	Inquire structures							*/
+/*												*/
+/************************************************/
+typedef	struct	_CNT_SCSI_INQ
+	{
+	UCHAR	devt;						/* 00: device type */
+	UCHAR	devtm;						/* 01: device type modifier */
+	UCHAR	svers;						/* 02: SCSI version */
+	UCHAR	rfmt;						/* 03: response data format */
+	UCHAR	adlen;						/* 04: additional length of data */
+	UCHAR	res1;						/* 05: */
+	UCHAR	res2;						/* 06: */
+	UCHAR	fncs;						/* 07: functional capabilities */
+	UCHAR	vid[8];						/* 08: vendor ID */
+	UCHAR	pid[16];					/* 10: product ID */
+	UCHAR	rev[4];						/* 20: product revision */
+	}	CNT_SCSI_INQ;
+
+typedef	struct	_CNT_IDE_INQ
+	{
+	USHORT	GeneralConfiguration;		/* 00 */
+	USHORT	NumberOfCylinders;			/* 02 */
+	USHORT	Reserved1;					/* 04 */
+	USHORT	NumberOfHeads;				/* 06 */
+	USHORT	UnformattedBytesPerTrack;	/* 08 */
+	USHORT	UnformattedBytesPerSector;	/* 0A */
+	USHORT	SectorsPerTrack;			/* 0C */
+	USHORT	VendorUnique1[3];			/* 0E */
+	USHORT	SerialNumber[10];			/* 14 */
+	USHORT	BufferType;					/* 28 */
+	USHORT	BufferSectorSize;			/* 2A */
+	USHORT	NumberOfEccBytes;			/* 2C */
+	USHORT	FirmwareRevision[4];		/* 2E */
+	USHORT	ModelNumber[20];			/* 36 */
+	UCHAR	MaximumBlockTransfer;		/* 5E */
+	UCHAR	VendorUnique2;				/* 5F */
+	USHORT	DoubleWordIo;				/* 60 */
+	USHORT	Capabilities;				/* 62 */
+	USHORT	Reserved2;					/* 64 */
+	UCHAR	VendorUnique3;				/* 66 */
+	UCHAR	PioCycleTimingMode;			/* 67 */
+	UCHAR	VendorUnique4;				/* 68 */
+	UCHAR	DmaCycleTimingMode;			/* 69 */
+	USHORT	TranslationFieldsValid;		/* 6A */
+	USHORT	NumberOfCurrentCylinders;	/* 6C */
+	USHORT	NumberOfCurrentHeads;		/* 6E */
+	USHORT	CurrentSectorsPerTrack;		/* 70 */
+	ULONG	CurrentSectorCapacity;		/* 72 */
+	}	CNT_IDE_INQ;
+
+typedef struct	_DASD_INQUIRE
+	{
+	ULONG	type;						/* 0 = SCSI, 1 = IDE */
+	union
+		{
+		CNT_SCSI_INQ	scsi;			/* SCSI inquire data */
+		CNT_IDE_INQ		ide;			/* IDE inquire data */
+		}	inq;
+	}	DASD_INQUIRE;
+
+/************************************************/
+/*												*/
+/*	Device Codes								*/
+/*												*/
+/************************************************/
+#define DEVC_DASD			0x00		/* Direct-access Storage Device */
+#define DEVC_SEQACESS		0x01		/* Sequential-access device */
+#define DEVC_PRINTER		0x02		/* Printer device */
+#define DEVC_PROCESSOR		0x03		/* Processor device */
+#define DEVC_WRITEONCE		0x04		/* Write-once device */
+#define DEVC_CDROM			0x05		/* CD-ROM device */
+#define DEVC_SCANNER		0x06		/* Scanner device */
+#define DEVC_OPTICAL		0x07		/* Optical memory device */
+#define DEVC_MEDCHGR		0x08		/* Medium changer device */
+#define	DEVC_DASD_REMOVABLE	0x80		/* Direct-access storage device, Removable */
+#define	DEVC_NONE			0xFF		/* no device */
+
+// SCSI controls for RAID
+#define	SC_MY_RAID			0xBF			// our special CDB command byte for Win95... interface
+#define	MY_SCSI_QUERY0		0x31			// byte 1 subcommand to query driver for RAID 0 informatation
+#define	MY_SCSI_QUERY1		0x32			// byte 1 subcommand to query driver for RAID 1 informatation
+#define	MY_SCSI_QUERY5		0x33			// byte 1 subcommand to query driver for RAID 5 informatation
+#define	MY_SCSI_REBUILD		0x40			// byte 1 subcommand to reconstruct a mirrored pair
+#define MY_SCSI_DEMOFAIL	0x54			// byte 1 subcommand for RAID failure demonstration
+#define	MY_SCSI_ALARMMUTE	0x60			// byte 1 subcommand to mute any alarm currently on
+
+
+#endif
+
diff -urN linux-2.0.37-pre8/drivers/scsi/README.aic7xxx linux-2.0.37-pre9/drivers/scsi/README.aic7xxx
--- linux-2.0.37-pre8/drivers/scsi/README.aic7xxx	1998-11-15 10:33:06.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/README.aic7xxx	2003-08-15 15:04:42.000000000 -0700
@@ -17,6 +17,10 @@
     AHA-274xT               
     AHA-2842
     AHA-2910B               
+    AHA-2920C
+    AHA-2930
+    AHA-2930U
+    AHA-2930U2
     AHA-2940               
     AHA-2940W              
     AHA-2940U              
@@ -37,8 +41,10 @@
     AHA-3940U
     AHA-3940W
     AHA-3940UW
+    AHA-3940AUW
     AHA-3940U2W
     AHA-3950U2B
+    AHA-3950U2D
     AHA-3985
     AHA-3985U
     AHA-3985W
@@ -52,6 +58,7 @@
     AIC-787x
     AIC-788x
     AIC-789x
+    AIC-3860
 
     Bus Types
     ----------------------------
@@ -69,8 +76,30 @@
     AHA-398x - PCI RAID controllers with three separate SCSI controllers
                on-board.
 
-    NOTE: The AHA-2920 is NOT an AIC-7xxx based controller, and is not
-          handled by this driver.
+  Not Supported Devices
+  ------------------------------
+    Adaptec Cards
+    ----------------------------
+    AHA-2920 (Only the cards that use the Future Domain chipset are not
+              supported, any 2920 cards based on Adaptec AIC chipsets,
+	      such as the 2920C, are supported)
+    AAA-13x Raid Adapters
+    AAA-113x Raid Port Card
+
+    Motherboard Chipsets
+    ----------------------------
+    AIC-7810
+
+    Bus Types
+    ----------------------------
+    R - Raid Port busses are not supported.
+
+    The hardware RAID devices sold by Adaptec are *NOT* supported by this
+    driver (and will people please stop emailing me about them, they are
+    a totally separate beast from the bare SCSI controllers and this driver
+    can not be retrofitted in any sane manner to support the hardware RAID
+    features on those cards - Doug Ledford).
+    
 
   People
   ------------------------------
@@ -83,7 +112,7 @@
     Jess Johnson    jester@frenzy.com
       (AIC7xxx FAQ author)
     Doug Ledford    dledford@redhat.com
-      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP/FAQ maintainer)
+      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer)
     
     Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
     author of the driver. John has since retired from the project. Thanks
@@ -299,13 +328,13 @@
 	binary->hex conversion then send an email to the aic7xxx mailing
 	list and someone can help you out.
 
-    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to enable
-        tagged queueing on specific devices.  As of driver version 5.0.6, we
-        now globally enable tagged queueing by default, but we also disable
-        tagged queueing on all individual devices by default.  In order to
-        enable tagged queueing for certian devices at boot time, a user may
-        use this boot param.  The driver will then parse this message out
-        and enable the specific device entries that are present based upon
+    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
+        or enable Tagged Command Queueing (TCQ) on specific devices.  As of
+	driver version 5.1.11, TCQ is now either on or off by default
+	according to the setting you choose during the make config process.
+	In order to en/disable TCQ for certian devices at boot time, a user
+	may use this boot param.  The driver will then parse this message out
+        and en/disable the specific device entries that are present based upon
         the value given.  The param line is parsed in the following manner:
 
           { - first instance indicates the start of this parameter values
@@ -337,18 +366,13 @@
           commas with no value specified will simply increment to the next id
           without changing anything for the missing values.
 
-        tag_info:{{8,8},,{8,8}}
-          First adapter, scsi id 0 to 8, id 1 to 8, remainder stay at their
-          default.  Second adapter stays entirely at default.  Third
-          adapter, id 0 to 8, id 1 to 8, remainder at default (identical to
-          first adapter).
-
-        tag_info:{,,,{,,,64}}
+        tag_info:{,,,{,,,255}}
           First, second, and third adapters at default values.  Fourth
-          adapter, id 3 to 64.  Notice that leading commas simply increment
-          what the first number effects, and there are no need for trailing
-          commas.  When you close out an adapter, or the entire entry,
-          anything not explicitly set stays at the default value.
+          adapter, id 3 is disabled.  Notice that leading commas simply
+	  increment what the first number effects, and there are no need
+	  for trailing commas.  When you close out an adapter, or the
+	  entire entry, anything not explicitly set stays at the default
+	  value.
 
         A final note on this option.  The scanner I used for this isn't
         perfect or highly robust.  If you mess the line up, the worst that
@@ -400,10 +424,10 @@
     see this documentation, you need to use one of the advanced configuration
     programs (menuconfig and xconfig).  If you are using the "make menuconfig"
     method of configuring your kernel, then you would simply highlight the
-    option in question and hit the F1 key.  If you are using the "make xconfig"
-    method of configuring your kernel, then simply click on the help button next
-    to the option you have questions about.  The help information from the
-    Configure.help file will then get automatically displayed.
+    option in question and hit the ? key.  If you are using the "make xconfig"
+    method of configuring your kernel, then simply click on the help button
+    next to the option you have questions about.  The help information from
+    the Configure.help file will then get automatically displayed.
 
   /proc support
   ------------------------------
@@ -449,10 +473,16 @@
     ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
       - European Linux mirror of Teleport site
 
+  Web sites
+  ------------------------------
+    http://developer.redhat.com/aic7xxx/
+      - Primary web site maintained by Doug Ledford.  I haven't actually
+        put anything up yet....but I'm planning on it.  This information
+	is put here as an add for the vapor page :)
 
 Dean W. Gehnert
 deang@teleport.com
 
 $Revision: 3.0 $
 
-Modified by Doug Ledford 1998
+Modified by Doug Ledford 1998-9
diff -urN linux-2.0.37-pre8/drivers/scsi/scsi.c linux-2.0.37-pre9/drivers/scsi/scsi.c
--- linux-2.0.37-pre8/drivers/scsi/scsi.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/scsi.c	2003-08-15 15:04:42.000000000 -0700
@@ -295,6 +295,7 @@
 {"YAMAHA","CDR100","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 {"YAMAHA","CDR102","1.00", BLIST_NOLUN},	/* Locks up if polled for lun != 0 */
 {"nCipher","Fastness Crypto","*", BLIST_FORCELUN},
+{"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN},
 /*
  * Must be at end of list...
  */
diff -urN linux-2.0.37-pre8/drivers/scsi/scsicam.c linux-2.0.37-pre9/drivers/scsi/scsicam.c
--- linux-2.0.37-pre8/drivers/scsi/scsicam.c	1998-11-15 10:33:10.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/scsicam.c	2003-08-15 15:04:42.000000000 -0700
@@ -26,9 +26,8 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <scsi/scsicam.h>
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
-    unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
     unsigned int *secs);
 
@@ -56,7 +55,7 @@
 	return -1;
 
     /* try to infer mapping from partition table */
-    ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
+    ret_code = scsi_partsize (bh, (unsigned long) size, (unsigned int *) ip + 2, 
 	(unsigned int *) ip + 0, (unsigned int *) ip + 1);
     brelse (bh);
 
@@ -85,7 +84,7 @@
 }
 
 /*
- * Function : static int partsize(struct buffer_head *bh, unsigned long 
+ * Function : static int scsi_partsize(struct buffer_head *bh, unsigned long 
  *     capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
  *
  * Purpose : to determine the BIOS mapping used to create the partition
@@ -95,7 +94,7 @@
  *
  */
 
-static int partsize(struct buffer_head *bh, unsigned long capacity,
+int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
     unsigned int  *cyls, unsigned int *hds, unsigned int *secs) {
     struct partition *p, *largest = NULL;
     int i, largest_cyl;
diff -urN linux-2.0.37-pre8/drivers/scsi/scsi_syms.c linux-2.0.37-pre9/drivers/scsi/scsi_syms.c
--- linux-2.0.37-pre8/drivers/scsi/scsi_syms.c	1998-06-03 15:17:49.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/scsi_syms.c	2003-08-15 15:04:42.000000000 -0700
@@ -26,13 +26,11 @@
 #include "constants.h"
 
 #include "sd.h"
+#include <scsi/scsicam.h>
 /*
  * This source file contains the symbol table used by scsi loadable
  * modules.
  */
-extern int scsicam_bios_param (Disk * disk,
-                               int dev,	int *ip	); 
-
 
 extern void print_command (unsigned char *command);
 extern void print_sense(const char * devclass, Scsi_Cmnd * SCpnt);
@@ -47,6 +45,7 @@
     X(scsi_register),
     X(scsi_unregister),
     X(scsicam_bios_param),
+    X(scsi_partsize),
     X(allocate_device),
     X(scsi_do_cmd),
     X(scsi_command_size),
diff -urN linux-2.0.37-pre8/drivers/scsi/seagate.c linux-2.0.37-pre9/drivers/scsi/seagate.c
--- linux-2.0.37-pre8/drivers/scsi/seagate.c	1998-11-15 10:33:10.000000000 -0800
+++ linux-2.0.37-pre9/drivers/scsi/seagate.c	2003-08-15 15:04:42.000000000 -0700
@@ -164,7 +164,7 @@
 } Signature;
 	
 static const Signature signatures[] = {
-#ifdef CONFIG_SCSI_SEAGATE
+#if defined(CONFIG_SCSI_SEAGATE) || defined(CONFIG_SCSI_SEAGATE_MODULE)
 {"ST01 v1.7  (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
 {"SCSI BIOS 2.00  (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
 
diff -urN linux-2.0.37-pre8/drivers/scsi/tripace.c linux-2.0.37-pre9/drivers/scsi/tripace.c
--- linux-2.0.37-pre8/drivers/scsi/tripace.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/drivers/scsi/tripace.c	2003-08-15 15:04:42.000000000 -0700
@@ -620,7 +620,7 @@
 u32 insert_bit(u32, short int);
 static void Get_Base(Adapter *);
 
-void tc2550_intr(int, struct pt_regs *);
+void tc2550_intr(int, void *, struct pt_regs *);
 
 static void internal_done(Scsi_Cmnd *);
 
@@ -1314,7 +1314,7 @@
 	return 0;
 }
 
-void tc2550_intr(int irq, struct pt_regs *regs)
+void tc2550_intr(int irq, void *dev_id, struct pt_regs *regs)
 {
 	void (*my_done) (Scsi_Cmnd *) = NULL;
 
diff -urN linux-2.0.37-pre8/include/linux/pci.h linux-2.0.37-pre9/include/linux/pci.h
--- linux-2.0.37-pre8/include/linux/pci.h	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/include/linux/pci.h	2003-08-15 15:04:42.000000000 -0700
@@ -962,10 +962,12 @@
 
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
 #define PCI_DEVICE_ID_ADAPTEC_7810	0x1078
+#define PCI_DEVICE_ID_ADAPTEC_7821	0x2178
 #define PCI_DEVICE_ID_ADAPTEC_7850	0x5078
 #define PCI_DEVICE_ID_ADAPTEC_7855	0x5578
 #define PCI_DEVICE_ID_ADAPTEC_5800	0x5800
-#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075
+#define PCI_DEVICE_ID_ADAPTEC_3860	0x6038
+#define PCI_DEVICE_ID_ADAPTEC_1480A	0x6075
 #define PCI_DEVICE_ID_ADAPTEC_7860	0x6078
 #define PCI_DEVICE_ID_ADAPTEC_7861	0x6178
 #define PCI_DEVICE_ID_ADAPTEC_7870	0x7078
@@ -979,14 +981,28 @@
 #define PCI_DEVICE_ID_ADAPTEC_7882	0x8278
 #define PCI_DEVICE_ID_ADAPTEC_7883	0x8378
 #define PCI_DEVICE_ID_ADAPTEC_7884	0x8478
+#define PCI_DEVICE_ID_ADAPTEC_7885	0x8578
+#define PCI_DEVICE_ID_ADAPTEC_7886	0x8678
+#define PCI_DEVICE_ID_ADAPTEC_7887	0x8778
+#define PCI_DEVICE_ID_ADAPTEC_7888	0x8878
 #define PCI_DEVICE_ID_ADAPTEC_1030	0x8b78
 
 #define PCI_VENDOR_ID_ADAPTEC2		0x9005
 #define PCI_DEVICE_ID_ADAPTEC2_2940U2	0x0010
+#define PCI_DEVICE_ID_ADAPTEC2_2930U2	0x0011
+#define PCI_DEVICE_ID_ADAPTEC2_7890B	0x0013
 #define PCI_DEVICE_ID_ADAPTEC2_7890	0x001f
 #define PCI_DEVICE_ID_ADAPTEC2_3940U2	0x0050
 #define PCI_DEVICE_ID_ADAPTEC2_3950U2D	0x0051
 #define PCI_DEVICE_ID_ADAPTEC2_7896	0x005f
+#define PCI_DEVICE_ID_ADAPTEC2_7892A	0x0080
+#define PCI_DEVICE_ID_ADAPTEC2_7892B	0x0081
+#define PCI_DEVICE_ID_ADAPTEC2_7892D	0x0083
+#define PCI_DEVICE_ID_ADAPTEC2_7892P	0x008f
+#define PCI_DEVICE_ID_ADAPTEC2_7899A	0x00c0
+#define PCI_DEVICE_ID_ADAPTEC2_7899B	0x00c1
+#define PCI_DEVICE_ID_ADAPTEC2_7899D	0x00c3
+#define PCI_DEVICE_ID_ADAPTEC2_7899P	0x00cf
 
 #define PCI_VENDOR_ID_ATRONICS		0x907f
 #define PCI_DEVICE_ID_ATRONICS_2015	0x2015
diff -urN linux-2.0.37-pre8/include/linux/proc_fs.h linux-2.0.37-pre9/include/linux/proc_fs.h
--- linux-2.0.37-pre8/include/linux/proc_fs.h	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/include/linux/proc_fs.h	2003-08-15 15:04:42.000000000 -0700
@@ -153,6 +153,9 @@
 	PROC_SCSI_ATARI,
 	PROC_SCSI_GDTH,
 	PROC_SCSI_INI9100U,
+	PROC_SCSI_PCI2000,
+	PROC_SCSI_PCI2220I,
+	PROC_SCSI_PSI240I,
 	PROC_SCSI_IDESCSI,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
diff -urN linux-2.0.37-pre8/include/scsi/scsicam.h linux-2.0.37-pre9/include/scsi/scsicam.h
--- linux-2.0.37-pre8/include/scsi/scsicam.h	1996-05-01 21:48:55.000000000 -0700
+++ linux-2.0.37-pre9/include/scsi/scsicam.h	2003-08-15 15:04:42.000000000 -0700
@@ -14,4 +14,6 @@
 #define SCSICAM_H
 #include <linux/kdev_t.h>
 extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip);
+extern int scsi_partsize(struct buffer_head *bh, unsigned long capacity,
+           unsigned int  *cyls, unsigned int *hds, unsigned int *secs);
 #endif /* def SCSICAM_H */
diff -urN linux-2.0.37-pre8/kernel/sys.c linux-2.0.37-pre9/kernel/sys.c
--- linux-2.0.37-pre8/kernel/sys.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/kernel/sys.c	2003-08-15 15:04:42.000000000 -0700
@@ -226,6 +226,9 @@
 void ctrl_alt_del(void)
 {
 	if (C_A_D) {
+#ifdef CONFIG_BLK_DEV_DAC960
+		DAC960_Finalize();
+#endif
 #ifdef CONFIG_SCSI_GDTH
 		gdth_halt();
 #endif
diff -urN linux-2.0.37-pre8/linux/Documentation/README.DAC960 linux-2.0.37-pre9/linux/Documentation/README.DAC960
--- linux-2.0.37-pre8/linux/Documentation/README.DAC960	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/linux/Documentation/README.DAC960	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,670 @@
+	   Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux
+
+			Version 2.0.0 for Linux 2.0.36
+			Version 2.2.0 for Linux 2.2.3
+
+			      PRODUCTION RELEASE
+
+				 23 March 1999
+
+			       Leonard N. Zubkoff
+			       Dandelion Digital
+			       lnz@dandelion.com
+
+	 Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+
+				 INTRODUCTION
+
+Mylex, Inc. designs and manufactures a variety of high performance PCI RAID
+controllers.  Mylex Corporation is located at 34551 Ardenwood Blvd., Fremont,
+California 94555, USA and can be reached at 510/796-6100 or on the World Wide
+Web at http://www.mylex.com.  Mylex RAID Technical Support can be reached by
+electronic mail at support@mylex.com (for eXtremeRAID 1100 and older DAC960
+models) or techsup@mylex.com (for AcceleRAID models), by voice at 510/608-2400,
+or by FAX at 510/745-7715.  Contact information for offices in Europe and Japan
+is available on the Web site.
+
+The latest information on Linux support for DAC960 PCI RAID Controllers, as
+well as the most recent release of this driver, will always be available from
+my Linux Home Page at URL "http://www.dandelion.com/Linux/".  The Linux DAC960
+driver supports all current DAC960 PCI family controllers including the
+AcceleRAID models, as well as the eXtremeRAID 1100; see below for a complete
+list.  For simplicity, in most places this documentation refers to DAC960
+generically rather than explicitly listing all the models.
+
+Bug reports should be sent via electronic mail to "lnz@dandelion.com".  Please
+include with the bug report the complete configuration messages reported by the
+driver at startup, along with any subsequent system messages relevant to the
+controller's operation, and a detailed description of your system's hardware
+configuration.
+
+Please consult the DAC960 RAID controller documentation for detailed
+information regarding installation and configuration of the controllers.  This
+document primarily provides information specific to the Linux DAC960 support.
+
+
+				DRIVER FEATURES
+
+The DAC960 RAID controllers are supported solely as high performance RAID
+controllers, not as interfaces to arbitrary SCSI devices.  The Linux DAC960
+driver operates at the block device level, the same level as the SCSI and IDE
+drivers.  Unlike other RAID controllers currently supported on Linux, the
+DAC960 driver is not dependent on the SCSI subsystem, and hence avoids all the
+complexity and unnecessary code that would be associated with an implementation
+as a SCSI driver.  The DAC960 driver is designed for as high a performance as
+possible with no compromises or extra code for compatibility with lower
+performance devices.  The DAC960 driver includes extensive error logging and
+online configuration management capabilities.  Except for initial configuration
+of the controller and adding new disk drives, most everything can be handled
+from Linux while the system is operational.
+
+The DAC960 driver is architected to support up to 8 controllers per system.
+Each DAC960 controller can support up to 15 disk drives per channel, for a
+maximum of 45 drives on a three channel controller.  The drives installed on a
+controller are divided into one or more "Drive Groups", and then each Drive
+Group is subdivided further into 1 to 32 "Logical Drives".  Each Logical Drive
+has a specific RAID Level and caching policy associated with it, and it appears
+to Linux as a single block device.  Logical Drives are further subdivided into
+up to 7 partitions through the normal Linux and PC disk partitioning schemes.
+Logical Drives are also known as "System Drives", and Drive Groups are also
+called "Packs".  Both terms are in use in the Mylex documentation; I have
+chosen to standardize on the more generic "Logical Drive" and "Drive Group".
+
+DAC960 RAID disk devices are named in the style of the Device File System
+(DEVFS).  The device corresponding to Logical Drive D on Controller C is
+referred to as /dev/rd/cCdD, and the partitions are called /dev/rd/cCdDp1
+through /dev/rd/cCdDp7.  For example, partition 3 of Logical Drive 5 on
+Controller 2 is referred to as /dev/rd/c2d5p3.  Note that unlike with SCSI
+disks the device names will not change in the event of a disk drive failure.
+The DAC960 driver is assigned major numbers 48 - 55 with one major number per
+controller.  The 8 bits of minor number are divided into 5 bits for the Logical
+Drive and 3 bits for the partition.
+
+
+		 SUPPORTED DAC960/DAC1100 PCI RAID CONTROLLERS
+
+The following list comprises the supported DAC960 and DAC1100 PCI RAID
+Controllers as of the date of this document.  It is recommended that anyone
+purchasing a Mylex PCI RAID Controller not in the following table contact the
+author beforehand to verify that it is or will be supported.
+
+eXtremeRAID 1100 (DAC1164P)
+	    3 Wide Ultra-2/LVD SCSI channels
+	    233MHz StrongARM SA 110 Processor
+	    64 Bit PCI (backward compatible with 32 Bit PCI slots)
+	    16MB/32MB/64MB Parity SDRAM Memory with Battery Backup
+
+AcceleRAID 250 (DAC960PTL1)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Also includes one onboard Wide Ultra-2/LVD SCSI Channel
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+AcceleRAID 200 (DAC960PTL0)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Includes no onboard SCSI Channels
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+AcceleRAID 150 (DAC960PRL)
+	    Uses onboard Symbios SCSI chips on certain motherboards
+	    Also includes one onboard Wide Ultra-2/LVD SCSI Channel
+	    33MHz Intel i960RP RISC Processor
+	    4MB Parity EDO Memory
+
+DAC960PJ    1/2/3 Wide Ultra SCSI-3 Channels
+	    66MHz Intel i960RD RISC Processor
+	    4MB/8MB/16MB/32MB/64MB/128MB ECC EDO Memory
+
+DAC960PG    1/2/3 Wide Ultra SCSI-3 Channels
+	    33MHz Intel i960RP RISC Processor
+	    4MB/8MB ECC EDO Memory
+
+DAC960PU    1/2/3 Wide Ultra SCSI-3 Channels
+	    Intel i960CF RISC Processor
+	    4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory
+
+DAC960PD    1/2/3 Wide Fast SCSI-2 Channels
+	    Intel i960CF RISC Processor
+	    4MB/8MB EDRAM or 2MB/4MB/8MB/16MB/32MB DRAM Memory
+
+DAC960PL    1/2/3 Wide Fast SCSI-2 Channels
+	    Intel i960 RISC Processor
+	    2MB/4MB/8MB/16MB/32MB DRAM Memory
+
+For the eXtremeRAID 1100, firmware version 5.06-0-52 or above is required.
+
+For the AcceleRAID 250, 200, and 150, firmware version 4.06-0-57 or above is
+required.
+
+For the DAC960PJ and DAC960PG, firmware version 4.06-0-00 or above is required.
+
+For the DAC960PU, DAC960PD, and DAC960PL, firmware version 3.51-0-04 or above
+is required.
+
+Note that earlier revisions of the DAC960PU, DAC960PD, and DAC960PL controllers
+were delivered with version 2.xx firmware.  Version 2.xx firmware is not
+supported by this driver and no support is envisioned.  Contact Mylex RAID
+Technical Support to inquire about upgrading controllers with version 2.xx
+firmware to version 3.51-0-04.  Upgrading to version 3.xx firmware requires
+installation of higher capacity Flash ROM chips, and not all DAC960PD and
+DAC960PL controllers can be upgraded.
+
+Please note that not all SCSI disk drives are suitable for use with DAC960
+controllers, and only particular firmware versions of any given model may
+actually function correctly.  Similarly, not all motherboards have a BIOS that
+properly initializes the AcceleRAID 250, AcceleRAID 200, AcceleRAID 150,
+DAC960PJ, and DAC960PG because the Intel i960RD/RP is a multi-function device.
+If in doubt, contact Mylex RAID Technical Support (support@mylex.com) to verify
+compatibility.  Mylex makes available a hard disk compatibility list by FTP at
+ftp://ftp.mylex.com/pub/dac960/diskcomp.html.
+
+
+			      DRIVER INSTALLATION
+
+This distribution was prepared for Linux kernel version 2.0.36 or 2.2.3.
+
+To install the DAC960 RAID driver, you may use the following commands,
+replacing "/usr/src" with wherever you keep your Linux kernel source tree:
+
+  cd /usr/src
+  tar -xvzf DAC960-2.0.0.tar.gz (or DAC960-2.2.0.tar.gz)
+  mv README.DAC960 linux/Documentation
+  mv DAC960.[ch] linux/drivers/block
+  patch -p0 < DAC960.patch
+  cd linux
+  make config
+  make depend
+  make bzImage (or zImage)
+
+Then install "arch/i386/boot/bzImage" or "arch/i386/boot/zImage" as your
+standard kernel, run lilo if appropriate, and reboot.
+
+To create the necessary devices in /dev, the "make_rd" script included in
+"DAC960-Utilities.tar.gz" from http://www.dandelion.com/Linux/ may be used.
+LILO 21 and FDISK v2.9 include DAC960 support; also included in this archive
+are patches to LILO 20 and FDISK v2.8 that add DAC960 support, along with
+statically linked executables of LILO and FDISK.  This modified version of LILO
+will allow booting from a DAC960 controller and/or mounting the root file
+system from a DAC960.  Unfortunately, installing directly onto a DAC960 will be
+problematic until the various Linux distribution vendors update their
+installation utilities.  VA Research Linux Systems (http://www.varesearch.com)
+has created an installation CD capable of installing its modified Red Hat 5.2
+environment directly onto DAC960 controllers, and copies of this CD are being
+made available to owners of supported Mylex RAID products by VA Research in
+cooperation with Mylex.  Please see http://www.mylex.com/support/prsfrm.html to
+request an installation CD.
+
+
+			      INSTALLATION NOTES
+
+Before installing Linux or adding DAC960 logical drives to an existing Linux
+system, the controller must first be configured to provide one or more logical
+drives using the BIOS Configuration Utility or DACCF.  Please note that since
+there are only at most 6 usable partitions on each logical drive, systems
+requiring more partitions should subdivide a drive group into multiple logical
+drives, each of which can have up to 6 partitions.  Also, note that with large
+disk arrays it is advisable to enable the 8GB BIOS Geometry (255/63) rather
+than accepting the default 2GB BIOS Geometry (128/32); failing to so do will
+cause the logical drive geometry to have more than 65535 cylinders which will
+make it impossible for FDISK to be used properly.  The 8GB BIOS Geometry can be
+enabled by configuring the DAC960 BIOS, which is accessible via Alt-M during
+the BIOS initialization sequence.
+
+For maximum performance and the most efficient E2FSCK performance, it is
+recommended that EXT2 file systems be built with a 4KB block size and 16 block
+stride to match the DAC960 controller's 64KB default stripe size.  The command
+"mke2fs -b 4096 -R stride=16 <device>" is appropriate.
+
+
+		      DAC960 ANNOUNCEMENTS MAILING LIST
+
+The DAC960 Announcements Mailing List provides a forum for informing Linux
+users of new driver releases and other announcements regarding Linux support
+for DAC960 PCI RAID Controllers.  To join the mailing list, send a message to
+"dac960-announce-request@dandelion.com" with the line "subscribe" in the
+message body.
+
+
+		CONTROLLER CONFIGURATION AND STATUS MONITORING
+
+The DAC960 RAID controllers running firmware 4.06 or above include a Background
+Initialization facility so that system downtime is minimized both for initial
+installation and subsequent configuration of additional storage.  The BIOS
+Configuration Utility (accessible via Alt-R during the BIOS initialization
+sequence) is used to quickly configure the controller, and then the logical
+drives that have been created are available for immediate use even while they
+are still being initialized by the controller.  The primary need for online
+configuration and status monitoring is then to avoid system downtime when disk
+drives fail and must be replaced.  Note that with a SAF-TE (SCSI Accessed
+Fault-Tolerant Enclosure) enclosure, the controller is able to rebuild failed
+drives automatically as soon as a drive replacement is made available.
+
+The primary interfaces for controller configuration and status monitoring are
+special files created in the /proc/rd/... hierarchy along with the normal
+system console logging mechanism.  Whenever the system is operating, the DAC960
+driver queries each controller for status information every 10 seconds, and
+checks for additional conditions every 60 seconds.  The initial status of each
+controller is always available for controller N in /proc/rd/cN/initial_status,
+and the current status as of the last status monitoring query is available in
+/proc/rd/cN/current_status.  In addition, status changes are also logged by the
+driver to the system console and will appear in the log files maintained by
+syslog.  The progress of asynchronous rebuild or consistency check operations
+is also available in /proc/rd/cN/current_status, and progress messages are
+logged to the system console at most every 60 seconds.
+
+To simplify the monitoring process for custom software, the special file
+/proc/rd/status returns "OK" when all DAC960 controllers in the system are
+operating normally and no failures have occurred, or "ALERT" if any logical
+drives are offline or critical or any non-standby physical drives are dead.
+
+Configuration commands for controller N are available via the special file
+/proc/rd/cN/user_command.  A human readable command can be written to this
+special file to initiate a configuration operation, and the results of the
+operation can then be read back from the special file in addition to being
+logged to the system console.  The shell command sequence
+
+  echo "<configuration-command>" > /proc/rd/c0/user_command
+  cat /proc/rd/c0/user_command
+
+is typically used to execute configuration commands.  The configuration
+commands are:
+
+  flush-cache
+
+    The "flush-cache" command flushes the controller's cache.  The system
+    automatically flushes the cache at shutdown or if the driver module is
+    unloaded, so this command is only needed to be certain a write back cache
+    is flushed to disk before the system is powered off by a command to a UPS.
+    Note that the flush-cache command also stops an asynchronous rebuild or
+    consistency check, so it should not be used except when the system is being
+    halted.
+
+  kill <channel>:<target-id>
+
+    The "kill" command marks the physical drive <channel>:<target-id> as DEAD.
+    This command is provided primarily for testing, and should not be used
+    during normal system operation.
+
+  make-online <channel>:<target-id>
+
+    The "make-online" command changes the physical drive <channel>:<target-id>
+    from status DEAD to status ONLINE.  In cases where multiple physical drives
+    have been killed simultaneously, this command may be used to bring them
+    back online, after which a consistency check is advisable.
+
+    Warning: make-online should only be used on a dead physical drive that is
+    an active part of a drive group, never on a standby drive.
+
+  make-standby <channel>:<target-id>
+
+    The "make-standby" command changes physical drive <channel>:<target-id>
+    from status DEAD to status STANDBY.  It should only be used in cases where
+    a dead drive was replaced after an automatic rebuild was performed onto a
+    standby drive.  It cannot be used to add a standby drive to the controller
+    configuration if one was not created initially; the BIOS Configuration
+    Utility must be used for that currently.
+
+  rebuild <channel>:<target-id>
+
+    The "rebuild" command initiates an asynchronous rebuild onto physical drive
+    <channel>:<target-id>.  It should only be used when a dead drive has been
+    replaced.
+
+  check-consistency <logical-drive-number>
+
+    The "check-consistency" command initiates an asynchronous consistency check
+    of <logical-drive-number> with automatic restoration.  It can be used
+    whenever it is desired to verify the consistency of the redundancy
+    information.
+
+  cancel-rebuild
+  cancel-consistency-check
+
+    The "cancel-rebuild" and "cancel-consistency-check" commands cancel any
+    rebuild or consistency check operations previously initiated.
+
+
+	       EXAMPLE I - DRIVE FAILURE WITHOUT A STANDBY DRIVE
+
+The following annotated logs demonstrate the controller configuration and and
+online status monitoring capabilities of the Linux DAC960 Driver.  The test
+configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a
+DAC960PJ controller.  The physical drives are configured into a single drive
+group without a standby drive, and the drive group has been configured into two
+logical drives, one RAID-5 and one RAID-6.  First, here is the current status
+of the RAID configuration:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 *****
+Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+Configuring Mylex DAC960PJ PCI RAID Controller
+  Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB
+  PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned
+  PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9
+  Controller Queue Depth: 128, Maximum Blocks per Command: 128
+  Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33
+  Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru
+  No Rebuild or Consistency Check in Progress
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+The above messages indicate that everything is healthy, and /proc/rd/status
+returns "OK" indicating that there are no problems with any DAC960 controller
+in the system.  For demonstration purposes, while I/O is active Physical Drive
+1:1 is now disconnected, simulating a drive failure.  The failure is noted by
+the driver within 10 seconds of the controller's having detected it, and the
+driver logs the following console status messages indicating that Logical
+Drives 0 and 1 are now CRITICAL as a result of Physical Drive 1:1 being DEAD:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL
+DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:1 killed because of timeout on SCSI command
+DAC960#0: Physical Drive 1:1 is now DEAD
+
+The Sense Keys logged here are just Check Condition / Unit Attention conditions
+arising from a SCSI bus reset that is forced by the controller during its error
+recovery procedures.  Concurrently with the above, the driver status available
+from /proc/rd also reflects the drive failure.  The status message in
+/proc/rd/status has changed from "OK" to "ALERT":
+
+gwynedd:/u/lnz# cat /proc/rd/status
+ALERT
+
+and /proc/rd/c0/current_status has been updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Dead, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru
+  No Rebuild or Consistency Check in Progress
+
+Since there are no standby drives configured, the system can continue to access
+the logical drives in a performance degraded mode until the failed drive is
+replaced and a rebuild operation completed to restore the redundancy of the
+logical drives.  Once Physical Drive 1:1 is replaced with a properly
+functioning drive, or if the physical drive was killed without having failed
+(e.g., due to electrical problems on the SCSI bus), the user can instruct the
+controller to initiate a rebuild operation onto the newly replaced drive:
+
+gwynedd:/u/lnz# echo "rebuild 1:1" > /proc/rd/c0/user_command
+gwynedd:/u/lnz# cat /proc/rd/c0/user_command
+Rebuild of Physical Drive 1:1 Initiated
+
+The echo command instructs the controller to initiate an asynchronous rebuild
+operation onto Physical Drive 1:1, and the status message that results from the
+operation is then available for reading from /proc/rd/c0/user_command, as well
+as being logged to the console by the driver.
+
+Within 10 seconds of this command the driver logs the initiation of the
+asynchronous rebuild operation:
+
+DAC960#0: Rebuild of Physical Drive 1:1 Initiated
+DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01
+DAC960#0: Physical Drive 1:1 is now WRITE-ONLY
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 1% completed
+
+and /proc/rd/c0/current_status is updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Write-Only, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 6% completed
+
+As the rebuild progresses, the current status in /proc/rd/c0/current_status is
+updated every 10 seconds:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Write-Only, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 3305472 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 15% completed
+
+and every minute a progress message is logged to the console by the driver:
+
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 32% completed
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 63% completed
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 94% completed
+DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 94% completed
+
+Finally, the rebuild completes successfully.  The driver logs the status of the 
+logical and physical drives and the rebuild completion:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE
+DAC960#0: Physical Drive 1:1 is now ONLINE
+DAC960#0: Rebuild Completed Successfully
+
+/proc/rd/c0/current_status is updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 5498880 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 3305472 blocks, Write Thru
+  Rebuild Completed Successfully
+
+and /proc/rd/status indicates that everything is healthy once again:
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+
+		EXAMPLE II - DRIVE FAILURE WITH A STANDBY DRIVE
+
+The following annotated logs demonstrate the controller configuration and and
+online status monitoring capabilities of the Linux DAC960 Driver.  The test
+configuration comprises 6 1GB Quantum Atlas I disk drives on two channels of a
+DAC960PJ controller.  The physical drives are configured into a single drive
+group with a standby drive, and the drive group has been configured into two
+logical drives, one RAID-5 and one RAID-6.  First, here is the current status
+of the RAID configuration:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 *****
+Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+Configuring Mylex DAC960PJ PCI RAID Controller
+  Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB
+  PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned
+  PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9
+  Controller Queue Depth: 128, Maximum Blocks per Command: 128
+  Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33
+  Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Online, 2201600 blocks
+    1:3 - Disk: Standby, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru
+  No Rebuild or Consistency Check in Progress
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+The above messages indicate that everything is healthy, and /proc/rd/status
+returns "OK" indicating that there are no problems with any DAC960 controller
+in the system.  For demonstration purposes, while I/O is active Physical Drive
+1:2 is now disconnected, simulating a drive failure.  The failure is noted by
+the driver within 10 seconds of the controller's having detected it, and the
+driver logs the following console status messages:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now CRITICAL
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now CRITICAL
+DAC960#0: Physical Drive 1:1 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:3 Error Log: Sense Key = 6, ASC = 29, ASCQ = 02
+DAC960#0: Physical Drive 1:2 killed because of timeout on SCSI command
+DAC960#0: Physical Drive 1:2 is now DEAD
+DAC960#0: Physical Drive 1:2 killed because it was removed
+
+Since a standby drive is configured, the controller automatically begins
+rebuilding onto the standby drive:
+
+DAC960#0: Physical Drive 1:3 is now WRITE-ONLY
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed
+
+Concurrently with the above, the driver status available from /proc/rd also
+reflects the drive failure and automatic rebuild.  The status message in
+/proc/rd/status has changed from "OK" to "ALERT":
+
+gwynedd:/u/lnz# cat /proc/rd/status
+ALERT
+
+and /proc/rd/c0/current_status has been updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Dead, 2201600 blocks
+    1:3 - Disk: Write-Only, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 4% completed
+
+As the rebuild progresses, the current status in /proc/rd/c0/current_status is
+updated every 10 seconds:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Dead, 2201600 blocks
+    1:3 - Disk: Write-Only, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Critical, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Critical, 2754560 blocks, Write Thru
+  Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed
+
+and every minute a progress message is logged on the console by the driver:
+
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 40% completed
+DAC960#0: Rebuild in Progress: Logical Drive 0 (/dev/rd/c0d0) 76% completed
+DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 66% completed
+DAC960#0: Rebuild in Progress: Logical Drive 1 (/dev/rd/c0d1) 84% completed
+
+Finally, the rebuild completes successfully.  The driver logs the status of the 
+logical and physical drives and the rebuild completion:
+
+DAC960#0: Logical Drive 0 (/dev/rd/c0d0) is now ONLINE
+DAC960#0: Logical Drive 1 (/dev/rd/c0d1) is now ONLINE
+DAC960#0: Physical Drive 1:3 is now ONLINE
+DAC960#0: Rebuild Completed Successfully
+
+/proc/rd/c0/current_status is updated:
+
+***** DAC960 RAID Driver Version 2.0.0 of 23 March 1999 *****
+Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+Configuring Mylex DAC960PJ PCI RAID Controller
+  Firmware Version: 4.06-0-08, Channels: 3, Memory Size: 8MB
+  PCI Bus: 0, Device: 19, Function: 1, I/O Address: Unassigned
+  PCI Address: 0xFD4FC000 mapped at 0x8807000, IRQ Channel: 9
+  Controller Queue Depth: 128, Maximum Blocks per Command: 128
+  Driver Queue Depth: 127, Maximum Scatter/Gather Segments: 33
+  Stripe Size: 64KB, Segment Size: 8KB, BIOS Geometry: 255/63
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Dead, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru
+  Rebuild Completed Successfully
+
+and /proc/rd/status indicates that everything is healthy once again:
+
+gwynedd:/u/lnz# cat /proc/rd/status
+OK
+
+Note that the absence of a viable standby drive does not create an "ALERT"
+status.  Once dead Physical Drive 1:2 has been replaced, the controller must be
+told that this has occurred and that the newly replaced drive should become the
+new standby drive:
+
+gwynedd:/u/lnz# echo "make-standby 1:2" > /proc/rd/c0/user_command
+gwynedd:/u/lnz# cat /proc/rd/c0/user_command
+Make Standby of Physical Drive 1:2 Succeeded
+
+The echo command instructs the controller to make Physical Drive 1:2 into a
+standby drive, and the status message that results from the operation is then
+available for reading from /proc/rd/c0/user_command, as well as being logged to
+the console by the driver.  Within 60 seconds of this command the driver logs:
+
+DAC960#0: Physical Drive 1:2 Error Log: Sense Key = 6, ASC = 29, ASCQ = 01
+DAC960#0: Physical Drive 1:2 is now STANDBY
+DAC960#0: Make Standby of Physical Drive 1:2 Succeeded
+
+and /proc/rd/c0/current_status is updated:
+
+gwynedd:/u/lnz# cat /proc/rd/c0/current_status
+  ...
+  Physical Devices:
+    0:1 - Disk: Online, 2201600 blocks
+    0:2 - Disk: Online, 2201600 blocks
+    0:3 - Disk: Online, 2201600 blocks
+    1:1 - Disk: Online, 2201600 blocks
+    1:2 - Disk: Standby, 2201600 blocks
+    1:3 - Disk: Online, 2201600 blocks
+  Logical Drives:
+    /dev/rd/c0d0: RAID-5, Online, 4399104 blocks, Write Thru
+    /dev/rd/c0d1: RAID-6, Online, 2754560 blocks, Write Thru
+  Rebuild Completed Successfully
diff -urN linux-2.0.37-pre8/linux/drivers/block/DAC960.c linux-2.0.37-pre9/linux/drivers/block/DAC960.c
--- linux-2.0.37-pre8/linux/drivers/block/DAC960.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/linux/drivers/block/DAC960.c	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,3013 @@
+/*
+
+  Linux Driver for Mylex DAC960 and DAC1100 PCI RAID Controllers
+
+  Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+*/
+
+
+#define DAC960_DriverVersion			"2.0.0"
+#define DAC960_DriverDate			"23 March 1999"
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ioport.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include "DAC960.h"
+
+
+/*
+  DAC960_ControllerCount is the number of DAC960 Controllers detected.
+*/
+
+static int
+  DAC960_ControllerCount =			0;
+
+
+/*
+  DAC960_ActiveControllerCount is the number of Active DAC960 Controllers
+  detected.
+*/
+
+static int
+  DAC960_ActiveControllerCount =		0;
+
+
+/*
+  DAC960_Controllers is an array of pointers to the DAC960 Controller
+  structures.
+*/
+
+static DAC960_Controller_T
+  *DAC960_Controllers[DAC960_MaxControllers] =	{ NULL };
+
+
+/*
+  DAC960_FileOperations is the File Operations structure for DAC960 Logical
+  Disk Devices.
+*/
+
+static FileOperations_T
+  DAC960_FileOperations =
+    { lseek:		    NULL,
+      read:		    block_read,
+      write:		    block_write,
+      readdir:		    NULL,
+      select:		    NULL,
+      ioctl:		    DAC960_Ioctl,
+      mmap:		    NULL,
+      open:		    DAC960_Open,
+      release:		    DAC960_Release,
+      fsync:		    block_fsync,
+      fasync:		    NULL,
+      check_media_change:   NULL,
+      revalidate:	    NULL };
+
+
+/*
+  DAC960_ProcDirectoryEntry is the DAC960 /proc/rd directory entry.
+*/
+
+static PROC_DirectoryEntry_T
+  DAC960_ProcDirectoryEntry;
+
+
+/*
+  DAC960_AnnounceDriver announces the Driver Version and Date, Author's Name,
+  Copyright Notice, and Electronic Mail Address.
+*/
+
+static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller)
+{
+  DAC960_Announce("***** DAC960 RAID Driver Version "
+		  DAC960_DriverVersion " of "
+		  DAC960_DriverDate " *****\n", Controller);
+  DAC960_Announce("Copyright 1998-1999 by Leonard N. Zubkoff "
+		  "<lnz@dandelion.com>\n", Controller);
+}
+
+
+/*
+  DAC960_Failure prints a standardized error message, and then returns false.
+*/
+
+static boolean DAC960_Failure(DAC960_Controller_T *Controller,
+			      char *ErrorMessage)
+{
+  DAC960_Error("While configuring DAC960 PCI RAID Controller at\n",
+	       Controller);
+  if (Controller->IO_Address == 0)
+    DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		 "PCI Address 0x%X\n", Controller,
+		 Controller->Bus, Controller->Device,
+		 Controller->Function, Controller->PCI_Address);
+  else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+		    "0x%X PCI Address 0x%X\n", Controller,
+		    Controller->Bus, Controller->Device,
+		    Controller->Function, Controller->IO_Address,
+		    Controller->PCI_Address);
+  DAC960_Error("%s FAILED - DETACHING\n", Controller, ErrorMessage);
+  return false;
+}
+
+
+/*
+  DAC960_ClearCommand clears critical fields of Command.
+*/
+
+static inline void DAC960_ClearCommand(DAC960_Command_T *Command)
+{
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  CommandMailbox->Words[0] = 0;
+  CommandMailbox->Words[1] = 0;
+  CommandMailbox->Words[2] = 0;
+  CommandMailbox->Words[3] = 0;
+  Command->CommandStatus = 0;
+}
+
+
+/*
+  DAC960_AllocateCommand allocates a Command structure from Controller's
+  free list.
+*/
+
+static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T
+						       *Controller)
+{
+  DAC960_Command_T *Command = Controller->FreeCommands;
+  if (Command == NULL) return NULL;
+  Controller->FreeCommands = Command->Next;
+  Command->Next = NULL;
+  return Command;
+}
+
+
+/*
+  DAC960_DeallocateCommand deallocates Command, returning it to Controller's
+  free list.
+*/
+
+static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  Command->Next = Controller->FreeCommands;
+  Controller->FreeCommands = Command;
+}
+
+
+/*
+  DAC960_QueueCommand queues Command.
+*/
+
+static void DAC960_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_CommandMailbox_T *NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command - Controller->Commands;
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V5_Controller:
+      NextCommandMailbox = Controller->NextCommandMailbox;
+      DAC960_V5_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+      if (Controller->PreviousCommandMailbox1->Words[0] == 0 ||
+	  Controller->PreviousCommandMailbox2->Words[0] == 0)
+	if (Controller->DualModeMemoryMailboxInterface)
+	  DAC960_V5_MemoryMailboxNewCommand(ControllerBaseAddress);
+	else DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress);
+      Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1;
+      Controller->PreviousCommandMailbox1 = NextCommandMailbox;
+      if (++NextCommandMailbox > Controller->LastCommandMailbox)
+	NextCommandMailbox = Controller->FirstCommandMailbox;
+      Controller->NextCommandMailbox = NextCommandMailbox;
+      break;
+    case DAC960_V4_Controller:
+      NextCommandMailbox = Controller->NextCommandMailbox;
+      DAC960_V4_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+      if (Controller->PreviousCommandMailbox1->Words[0] == 0 ||
+	  Controller->PreviousCommandMailbox2->Words[0] == 0)
+	if (Controller->DualModeMemoryMailboxInterface)
+	  DAC960_V4_MemoryMailboxNewCommand(ControllerBaseAddress);
+	else DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress);
+      Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1;
+      Controller->PreviousCommandMailbox1 = NextCommandMailbox;
+      if (++NextCommandMailbox > Controller->LastCommandMailbox)
+	NextCommandMailbox = Controller->FirstCommandMailbox;
+      Controller->NextCommandMailbox = NextCommandMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_MailboxFullP(ControllerBaseAddress))
+	udelay(1);
+      DAC960_V3_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
+      DAC960_V3_NewCommand(ControllerBaseAddress);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ExecuteCommand executes Command and waits for completion.  It
+  returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  Semaphore_T Semaphore = MUTEX_LOCKED;
+  unsigned long ProcessorFlags;
+  Command->Semaphore = &Semaphore;
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  DAC960_QueueCommand(Command);
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+  down(&Semaphore);
+  return Command->CommandStatus == DAC960_NormalCompletion;
+}
+
+
+/*
+  DAC960_ExecuteType3 executes a DAC960 Type 3 Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3(DAC960_Controller_T *Controller,
+				   DAC960_CommandOpcode_T CommandOpcode,
+				   void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_ExecuteType3D executes a DAC960 Type 3D Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller,
+				    DAC960_CommandOpcode_T CommandOpcode,
+				    unsigned char Channel,
+				    unsigned char TargetID,
+				    void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3D.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_EnableMemoryMailboxInterface enables the Memory Mailbox Interface.
+*/
+
+static boolean DAC960_EnableMemoryMailboxInterface(DAC960_Controller_T
+						   *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_CommandMailbox_T *CommandMailboxesMemory;
+  DAC960_StatusMailbox_T *StatusMailboxesMemory;
+  DAC960_CommandMailbox_T CommandMailbox;
+  DAC960_CommandStatus_T CommandStatus;
+  void *SavedMemoryMailboxesAddress = NULL;
+  short NextCommandMailboxIndex = 0;
+  short NextStatusMailboxIndex = 0;
+  long TimeoutCounter = 1000000;
+  int i;
+  if (Controller->ControllerType == DAC960_V5_Controller)
+    DAC960_V5_RestoreMemoryMailboxInfo(Controller,
+				       &SavedMemoryMailboxesAddress,
+				       &NextCommandMailboxIndex,
+				       &NextStatusMailboxIndex);
+  else DAC960_V4_RestoreMemoryMailboxInfo(Controller,
+					  &SavedMemoryMailboxesAddress,
+					  &NextCommandMailboxIndex,
+					  &NextStatusMailboxIndex);
+  if (SavedMemoryMailboxesAddress == NULL)
+    CommandMailboxesMemory =
+      (DAC960_CommandMailbox_T *) __get_free_pages(GFP_KERNEL, 1, 0);
+  else CommandMailboxesMemory = SavedMemoryMailboxesAddress;
+  memset(CommandMailboxesMemory, 0, PAGE_SIZE << 1);
+  Controller->FirstCommandMailbox = CommandMailboxesMemory;
+  CommandMailboxesMemory += DAC960_CommandMailboxCount - 1;
+  Controller->LastCommandMailbox = CommandMailboxesMemory;
+  Controller->NextCommandMailbox =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  if (--NextCommandMailboxIndex < 0)
+    NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1;
+  Controller->PreviousCommandMailbox1 =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  if (--NextCommandMailboxIndex < 0)
+    NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1;
+  Controller->PreviousCommandMailbox2 =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  StatusMailboxesMemory =
+    (DAC960_StatusMailbox_T *) (CommandMailboxesMemory + 1);
+  Controller->FirstStatusMailbox = StatusMailboxesMemory;
+  StatusMailboxesMemory += DAC960_StatusMailboxCount - 1;
+  Controller->LastStatusMailbox = StatusMailboxesMemory;
+  Controller->NextStatusMailbox =
+    &Controller->FirstStatusMailbox[NextStatusMailboxIndex];
+  if (SavedMemoryMailboxesAddress != NULL) return true;
+  /* Enable the Memory Mailbox Interface. */
+  CommandMailbox.TypeX.CommandOpcode = 0x2B;
+  CommandMailbox.TypeX.CommandIdentifier = 0;
+  CommandMailbox.TypeX.CommandOpcode2 = 0x10;
+  CommandMailbox.TypeX.CommandMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstCommandMailbox);
+  CommandMailbox.TypeX.StatusMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstStatusMailbox);
+  for (i = 0; i < 2; i++)
+    switch (Controller->ControllerType)
+      {
+      case DAC960_V5_Controller:
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V5_HardwareMailboxEmptyP(ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	DAC960_V5_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+	DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress);
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V5_HardwareMailboxStatusAvailableP(
+		  ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	CommandStatus = DAC960_V5_ReadStatusRegister(ControllerBaseAddress);
+	DAC960_V5_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+	DAC960_V5_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+	if (CommandStatus == DAC960_NormalCompletion) return true;
+	Controller->DualModeMemoryMailboxInterface = true;
+	CommandMailbox.TypeX.CommandOpcode2 = 0x14;
+	break;
+      case DAC960_V4_Controller:
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (!DAC960_V4_HardwareMailboxFullP(ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	DAC960_V4_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+	DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress);
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V4_HardwareMailboxStatusAvailableP(
+		  ControllerBaseAddress))
+	      break;
+	    udelay(1);
+	  }
+	if (TimeoutCounter < 0) return false;
+	CommandStatus = DAC960_V4_ReadStatusRegister(ControllerBaseAddress);
+	DAC960_V4_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+	DAC960_V4_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+	if (CommandStatus == DAC960_NormalCompletion) return true;
+	Controller->DualModeMemoryMailboxInterface = true;
+	CommandMailbox.TypeX.CommandOpcode2 = 0x14;
+	break;
+      default:
+	break;
+      }
+  return false;
+}
+
+
+/*
+  DAC960_DetectControllers detects DAC960 PCI RAID Controllers by interrogating
+  the PCI Configuration Space for Controller Type.
+*/
+
+static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType)
+{
+  unsigned short VendorID = 0, DeviceID = 0, Index = 0;
+  unsigned char Bus, DeviceFunction, IRQ_Channel;
+  unsigned int BaseAddress0, BaseAddress1;
+  unsigned int MemoryWindowSize = 0;
+  switch (ControllerType)
+    {
+    case DAC960_V5_Controller:
+      VendorID = PCI_VENDOR_ID_DEC;
+      DeviceID = PCI_DEVICE_ID_DEC_21285;
+      MemoryWindowSize = DAC960_V5_RegisterWindowSize;
+      break;
+    case DAC960_V4_Controller:
+      VendorID = PCI_VENDOR_ID_MYLEX;
+      DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V4;
+      MemoryWindowSize = DAC960_V4_RegisterWindowSize;
+      break;
+    case DAC960_V3_Controller:
+      VendorID = PCI_VENDOR_ID_MYLEX;
+      DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V3;
+      MemoryWindowSize = DAC960_V3_RegisterWindowSize;
+      break;
+    }
+  while (pcibios_find_device(VendorID, DeviceID,
+			     Index++, &Bus, &DeviceFunction) == 0)
+    {
+      DAC960_Controller_T *Controller = (DAC960_Controller_T *)
+	kmalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC);
+      DAC960_IO_Address_T IO_Address = 0;
+      DAC960_PCI_Address_T PCI_Address = 0;
+      unsigned char Device = DeviceFunction >> 3;
+      unsigned char Function = DeviceFunction & 0x7;
+      unsigned short SubsystemVendorID, SubsystemDeviceID;
+      pcibios_read_config_dword(Bus, DeviceFunction,
+				PCI_BASE_ADDRESS_0, &BaseAddress0);
+      pcibios_read_config_dword(Bus, DeviceFunction,
+				PCI_BASE_ADDRESS_1, &BaseAddress1);
+      pcibios_read_config_word(Bus, DeviceFunction,
+			       PCI_SUBSYSTEM_VENDOR_ID, &SubsystemVendorID);
+      pcibios_read_config_word(Bus, DeviceFunction,
+			       PCI_SUBSYSTEM_ID, &SubsystemDeviceID);
+      pcibios_read_config_byte(Bus, DeviceFunction,
+			       PCI_INTERRUPT_LINE, &IRQ_Channel);
+      switch (ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  if (!(SubsystemVendorID == PCI_VENDOR_ID_MYLEX &&
+		SubsystemDeviceID == PCI_DEVICE_ID_MYLEX_DAC960P_V5))
+	    goto Ignore;
+	  PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	case DAC960_V4_Controller:
+	  PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	case DAC960_V3_Controller:
+	  IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	  PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	}
+      if (DAC960_ControllerCount == DAC960_MaxControllers)
+	{
+	  DAC960_Error("More than %d DAC960 Controllers detected - "
+		       "ignoring from Controller at\n",
+		       NULL, DAC960_MaxControllers);
+	  goto Ignore;
+	}
+      if (Controller == NULL)
+	{
+	  DAC960_Error("Unable to allocate Controller structure for "
+		       "Controller at\n", NULL);
+	  goto Ignore;
+	}
+      memset(Controller, 0, sizeof(DAC960_Controller_T));
+      Controller->ControllerNumber = DAC960_ControllerCount;
+      DAC960_Controllers[DAC960_ControllerCount++] = Controller;
+      DAC960_AnnounceDriver(Controller);
+      Controller->ControllerType = ControllerType;
+      Controller->IO_Address = IO_Address;
+      Controller->PCI_Address = PCI_Address;
+      Controller->Bus = Bus;
+      Controller->Device = Device;
+      Controller->Function = Function;
+      sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber);
+      /*
+	Acquire shared access to the IRQ Channel.
+      */
+      if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+	{
+	  DAC960_Error("IRQ Channel %d illegal for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      strcpy(Controller->FullModelName, "DAC960");
+      if (request_irq(IRQ_Channel, DAC960_InterruptHandler,
+		      SA_INTERRUPT | SA_SHIRQ, Controller->FullModelName,
+		      Controller) < 0)
+	{
+	  DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      Controller->IRQ_Channel = IRQ_Channel;
+      /*
+	Map the Controller Register Window.
+      */
+      if (MemoryWindowSize < PAGE_SIZE)
+	MemoryWindowSize = PAGE_SIZE;
+      Controller->MemoryMappedAddress =
+	ioremap_nocache(PCI_Address & PAGE_MASK, MemoryWindowSize);
+      Controller->BaseAddress =
+	Controller->MemoryMappedAddress + (PCI_Address & ~PAGE_MASK);
+      if (Controller->MemoryMappedAddress == NULL)
+	{
+	  DAC960_Error("Unable to map Controller Register Window for "
+		       "Controller at\n", Controller);
+	  goto Failure;
+	}
+      switch (ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  DAC960_V5_DisableInterrupts(Controller->BaseAddress);
+	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
+	    {
+	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
+			   "for Controller at\n", Controller);
+	      goto Failure;
+	    }
+	  DAC960_V5_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	case DAC960_V4_Controller:
+	  DAC960_V4_DisableInterrupts(Controller->BaseAddress);
+	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
+	    {
+	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
+			   "for Controller at\n", Controller);
+	      goto Failure;
+	    }
+	  DAC960_V4_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	case DAC960_V3_Controller:
+	  request_region(Controller->IO_Address, 0x80,
+			 Controller->FullModelName);
+	  DAC960_V3_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	}
+      DAC960_ActiveControllerCount++;
+      Controller->Commands[0].Controller = Controller;
+      Controller->Commands[0].Next = NULL;
+      Controller->FreeCommands = &Controller->Commands[0];
+      continue;
+    Failure:
+      if (IO_Address == 0)
+	DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		     "PCI Address 0x%X\n", Controller,
+		     Bus, Device, Function, PCI_Address);
+      else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+			"0x%X PCI Address 0x%X\n", Controller,
+			Bus, Device, Function, IO_Address, PCI_Address);
+      if (Controller == NULL) break;
+      if (Controller->IRQ_Channel > 0)
+	free_irq(IRQ_Channel, Controller);
+      if (Controller->MemoryMappedAddress != NULL)
+	iounmap(Controller->MemoryMappedAddress);
+      DAC960_Controllers[Controller->ControllerNumber] = NULL;
+    Ignore:
+      kfree(Controller);
+    }
+}
+
+
+/*
+  DAC960_ReadControllerConfiguration reads the Configuration Information
+  from Controller and initializes the Controller structure.
+*/
+
+static boolean DAC960_ReadControllerConfiguration(DAC960_Controller_T
+						  *Controller)
+{
+  DAC960_Enquiry2_T Enquiry2;
+  DAC960_Config2_T Config2;
+  int LogicalDriveNumber, Channel, TargetID;
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry,
+			   &Controller->Enquiry[0]))
+    return DAC960_Failure(Controller, "ENQUIRY");
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry2, &Enquiry2))
+    return DAC960_Failure(Controller, "ENQUIRY2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_ReadConfig2, &Config2))
+    return DAC960_Failure(Controller, "READ CONFIG2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_GetLogicalDriveInformation,
+			   &Controller->LogicalDriveInformation[0]))
+    return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION");
+  for (Channel = 0; Channel < Enquiry2.ActualChannels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      if (!DAC960_ExecuteType3D(Controller, DAC960_GetDeviceState,
+				Channel, TargetID,
+				&Controller->DeviceState[0][Channel][TargetID]))
+	  return DAC960_Failure(Controller, "GET DEVICE STATE");
+  /*
+    Initialize the Controller Model Name and Full Model Name fields.
+  */
+  switch (Enquiry2.HardwareID.SubModel)
+    {
+    case DAC960_P_PD_PU:
+      if (Enquiry2.SCSICapability.BusSpeed == DAC960_Ultra)
+	strcpy(Controller->ModelName, "DAC960PU");
+      else strcpy(Controller->ModelName, "DAC960PD");
+      break;
+    case DAC960_PL:
+      strcpy(Controller->ModelName, "DAC960PL");
+      break;
+    case DAC960_PG:
+      strcpy(Controller->ModelName, "DAC960PG");
+      break;
+    case DAC960_PJ:
+      strcpy(Controller->ModelName, "DAC960PJ");
+      break;
+    case DAC960_PR:
+      strcpy(Controller->ModelName, "DAC960PR");
+      break;
+    case DAC960_PT:
+      strcpy(Controller->ModelName, "DAC960PT");
+      break;
+    case DAC960_PTL0:
+      strcpy(Controller->ModelName, "DAC960PTL0");
+      break;
+    case DAC960_PRL:
+      strcpy(Controller->ModelName, "DAC960PRL");
+      break;
+    case DAC960_PTL1:
+      strcpy(Controller->ModelName, "DAC960PTL1");
+      break;
+    case DAC1164_P:
+      strcpy(Controller->ModelName, "DAC1164P");
+      break;
+    default:
+      return DAC960_Failure(Controller, "MODEL VERIFICATION");
+    }
+  strcpy(Controller->FullModelName, "Mylex ");
+  strcat(Controller->FullModelName, Controller->ModelName);
+  /*
+    Initialize the Controller Firmware Version field and verify that it
+    is a supported firmware version.  The supported firmware versions are:
+
+    DAC1164P		5.06 and above
+    DAC960PTL/PJ/PG	4.06 and above
+    DAC960PU/PD/PL	3.51 and above
+  */
+  sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d",
+	  Enquiry2.FirmwareID.MajorVersion, Enquiry2.FirmwareID.MinorVersion,
+	  Enquiry2.FirmwareID.FirmwareType, Enquiry2.FirmwareID.TurnID);
+  if (!((Controller->FirmwareVersion[0] == '5' &&
+	 strcmp(Controller->FirmwareVersion, "5.06") >= 0) ||
+	(Controller->FirmwareVersion[0] == '4' &&
+	 strcmp(Controller->FirmwareVersion, "4.06") >= 0) ||
+	(Controller->FirmwareVersion[0] == '3' &&
+	 strcmp(Controller->FirmwareVersion, "3.51") >= 0)))
+    {
+      DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION");
+      DAC960_Error("Firmware Version = '%s'\n", Controller,
+		   Controller->FirmwareVersion);
+      return false;
+    }
+  /*
+    Initialize the Controller Channels, Memory Size, and SAF-TE Fault
+    Management Enabled fields.
+  */
+  Controller->Channels = Enquiry2.ActualChannels;
+  Controller->MemorySize = Enquiry2.MemorySize >> 20;
+  Controller->SAFTE_FaultManagementEnabled =
+    Enquiry2.FaultManagementType == DAC960_SAFTE;
+  /*
+    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
+    Count, Maximum Blocks per Command, and Maximum Scatter/Gather Segments.
+    The Driver Queue Depth must be at most one less than the Controller Queue
+    Depth to allow for an automatic drive rebuild operation.
+  */
+  Controller->ControllerQueueDepth = Controller->Enquiry[0].MaxCommands;
+  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
+  Controller->LogicalDriveCount = Controller->Enquiry[0].NumberOfLogicalDrives;
+  Controller->MaxBlocksPerCommand = Enquiry2.MaxBlocksPerCommand;
+  Controller->MaxScatterGatherSegments = Enquiry2.MaxScatterGatherEntries;
+  /*
+    Initialize the Stripe Size, Segment Size, and Geometry Translation.
+  */
+  Controller->StripeSize = Config2.BlocksPerStripe * Config2.BlockFactor
+			   >> (10 - DAC960_BlockSizeBits);
+  Controller->SegmentSize = Config2.BlocksPerCacheLine * Config2.BlockFactor
+			    >> (10 - DAC960_BlockSizeBits);
+  switch (Config2.DriveGeometry)
+    {
+    case DAC960_Geometry_128_32:
+      Controller->GeometryTranslationHeads = 128;
+      Controller->GeometryTranslationSectors = 32;
+      break;
+    case DAC960_Geometry_255_63:
+      Controller->GeometryTranslationHeads = 255;
+      Controller->GeometryTranslationSectors = 63;
+      break;
+    default:
+      return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY");
+    }
+  /*
+    Initialize the Logical Drive Initial State.
+  */
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    Controller->LogicalDriveInitialState[LogicalDriveNumber] =
+      Controller->LogicalDriveInformation[0]
+		  [LogicalDriveNumber].LogicalDriveState;
+  return true;
+}
+
+
+/*
+  DAC960_ReportControllerConfiguration reports the configuration of
+  Controller.
+*/
+
+static boolean DAC960_ReportControllerConfiguration(DAC960_Controller_T
+						    *Controller)
+{
+  int LogicalDriveNumber, Channel, TargetID;
+  DAC960_Info("Configuring Mylex %s PCI RAID Controller\n",
+	      Controller, Controller->ModelName);
+  DAC960_Info("  Firmware Version: %s, Channels: %d, Memory Size: %dMB\n",
+	      Controller, Controller->FirmwareVersion,
+	      Controller->Channels, Controller->MemorySize);
+  DAC960_Info("  PCI Bus: %d, Device: %d, Function: %d, I/O Address: ",
+	      Controller, Controller->Bus,
+	      Controller->Device, Controller->Function);
+  if (Controller->IO_Address == 0)
+    DAC960_Info("Unassigned\n", Controller);
+  else DAC960_Info("0x%X\n", Controller, Controller->IO_Address);
+  DAC960_Info("  PCI Address: 0x%X mapped at 0x%lX, IRQ Channel: %d\n",
+	      Controller, Controller->PCI_Address,
+	      (unsigned long) Controller->BaseAddress,
+	      Controller->IRQ_Channel);
+  DAC960_Info("  Controller Queue Depth: %d, "
+	      "Maximum Blocks per Command: %d\n",
+	      Controller, Controller->ControllerQueueDepth,
+	      Controller->MaxBlocksPerCommand);
+  DAC960_Info("  Driver Queue Depth: %d, "
+	      "Maximum Scatter/Gather Segments: %d\n",
+	      Controller, Controller->DriverQueueDepth,
+	      Controller->MaxScatterGatherSegments);
+  DAC960_Info("  Stripe Size: %dKB, Segment Size: %dKB, "
+	      "BIOS Geometry: %d/%d\n", Controller,
+	      Controller->StripeSize,
+	      Controller->SegmentSize,
+	      Controller->GeometryTranslationHeads,
+	      Controller->GeometryTranslationSectors);
+  if (Controller->SAFTE_FaultManagementEnabled)
+    DAC960_Info("  SAF-TE Fault Management Enabled\n", Controller);
+  DAC960_Info("  Physical Devices:\n", Controller);
+  for (Channel = 0; Channel < Controller->Channels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      {
+	DAC960_DeviceState_T *DeviceState =
+	  &Controller->DeviceState[Controller->DeviceStateIndex]
+				  [Channel][TargetID];
+	if (!DeviceState->Present) continue;
+	switch (DeviceState->DeviceType)
+	  {
+	  case DAC960_OtherType:
+	    DAC960_Info("    %d:%d - Other\n", Controller, Channel, TargetID);
+	    break;
+	  case DAC960_DiskType:
+	    DAC960_Info("    %d:%d - Disk: %s, %d blocks\n", Controller,
+			Channel, TargetID,
+			(DeviceState->DeviceState == DAC960_Device_Dead
+			 ? "Dead"
+			 : DeviceState->DeviceState == DAC960_Device_WriteOnly
+			   ? "Write-Only"
+			   : DeviceState->DeviceState == DAC960_Device_Online
+			     ? "Online" : "Standby"),
+			DeviceState->DiskSize);
+	    break;
+	  case DAC960_SequentialType:
+	    DAC960_Info("    %d:%d - Sequential\n", Controller,
+			Channel, TargetID);
+	    break;
+	  case DAC960_CDROM_or_WORM_Type:
+	    DAC960_Info("    %d:%d - CD-ROM or WORM\n", Controller,
+			Channel, TargetID);
+	    break;
+	  }
+
+      }
+  DAC960_Info("  Logical Drives:\n", Controller);
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    {
+      DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+	&Controller->LogicalDriveInformation
+	   [Controller->LogicalDriveInformationIndex][LogicalDriveNumber];
+      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %d blocks, %s\n",
+		  Controller, Controller->ControllerNumber, LogicalDriveNumber,
+		  LogicalDriveInformation->RAIDLevel,
+		  (LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Online
+		   ? "Online"
+		   : LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Critical
+		     ? "Critical" : "Offline"),
+		  LogicalDriveInformation->LogicalDriveSize,
+		  (LogicalDriveInformation->WriteBack
+		   ? "Write Back" : "Write Thru"));
+    }
+  return true;
+}
+
+
+/*
+  DAC960_RegisterBlockDevice registers the Block Device structures
+  associated with Controller.
+*/
+
+static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  static void (*RequestFunctions[DAC960_MaxControllers])(void) =
+    { DAC960_RequestFunction0, DAC960_RequestFunction1,
+      DAC960_RequestFunction2, DAC960_RequestFunction3,
+      DAC960_RequestFunction4, DAC960_RequestFunction5,
+      DAC960_RequestFunction6, DAC960_RequestFunction7 };
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  GenericDiskInfo_T *GenericDiskInfo;
+  int MinorNumber;
+  /*
+    Register the Block Device Major Number for this DAC960 Controller.
+  */
+  if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0)
+    {
+      DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n",
+		   Controller, MajorNumber);
+      return false;
+    }
+  /*
+    Initialize the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn =
+    RequestFunctions[Controller->ControllerNumber];
+  /*
+    Initialize the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++)
+    {
+      Controller->BlockSizes[MinorNumber] = BLOCK_SIZE;
+      Controller->MaxSectorsPerRequest[MinorNumber] =
+	Controller->MaxBlocksPerCommand;
+      Controller->MaxSegmentsPerRequest[MinorNumber] =
+	Controller->MaxScatterGatherSegments;
+    }
+  Controller->GenericDiskInfo.part = Controller->DiskPartitions;
+  Controller->GenericDiskInfo.sizes = Controller->PartitionSizes;
+  blksize_size[MajorNumber] = Controller->BlockSizes;
+  max_sectors[MajorNumber] = Controller->MaxSectorsPerRequest;
+  max_segments[MajorNumber] = Controller->MaxSegmentsPerRequest;
+  /*
+    Initialize Read Ahead to 128 sectors.
+  */
+  read_ahead[MajorNumber] = 128;
+  /*
+    Complete initialization of the Generic Disk Information structure.
+  */
+  Controller->GenericDiskInfo.major = MajorNumber;
+  Controller->GenericDiskInfo.major_name = "rd";
+  Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits;
+  Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions;
+  Controller->GenericDiskInfo.max_nr = DAC960_MaxLogicalDrives;
+  Controller->GenericDiskInfo.init = DAC960_InitializeGenericDiskInfo;
+  Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount;
+  Controller->GenericDiskInfo.real_devices = Controller;
+  Controller->GenericDiskInfo.next = NULL;
+  /*
+    Install the Generic Disk Information structure at the end of the list.
+  */
+  if ((GenericDiskInfo = gendisk_head) != NULL)
+    {
+      while (GenericDiskInfo->next != NULL)
+	GenericDiskInfo = GenericDiskInfo->next;
+      GenericDiskInfo->next = &Controller->GenericDiskInfo;
+    }
+  else gendisk_head = &Controller->GenericDiskInfo;
+  /*
+    Indicate the Block Device Registration completed successfully,
+  */
+  return true;
+}
+
+
+/*
+  DAC960_UnregisterBlockDevice unregisters the Block Device structures
+  associated with Controller.
+*/
+
+static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  /*
+    Unregister the Block Device Major Number for this DAC960 Controller.
+  */
+  unregister_blkdev(MajorNumber, "rd");
+  /*
+    Remove the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn = NULL;
+  /*
+    Remove the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  Controller->GenericDiskInfo.part = NULL;
+  Controller->GenericDiskInfo.sizes = NULL;
+  blk_size[MajorNumber] = NULL;
+  blksize_size[MajorNumber] = NULL;
+  max_sectors[MajorNumber] = NULL;
+  max_segments[MajorNumber] = NULL;
+  /*
+    Remove the Generic Disk Information structure from the list.
+  */
+  if (gendisk_head != &Controller->GenericDiskInfo)
+    {
+      GenericDiskInfo_T *GenericDiskInfo = gendisk_head;
+      while (GenericDiskInfo != NULL &&
+	     GenericDiskInfo->next != &Controller->GenericDiskInfo)
+	GenericDiskInfo = GenericDiskInfo->next;
+      if (GenericDiskInfo != NULL)
+	GenericDiskInfo->next = GenericDiskInfo->next->next;
+    }
+  else gendisk_head = Controller->GenericDiskInfo.next;
+}
+
+
+/*
+  DAC960_InitializeController initializes Controller.
+*/
+
+static void DAC960_InitializeController(DAC960_Controller_T *Controller)
+{
+  if (DAC960_ReadControllerConfiguration(Controller) &&
+      DAC960_ReportControllerConfiguration(Controller) &&
+      DAC960_RegisterBlockDevice(Controller))
+    {
+      /*
+	Initialize the Command structures.
+      */
+      DAC960_Command_T *Commands = Controller->Commands;
+      int CommandIdentifier;
+      Controller->FreeCommands = NULL;
+      for (CommandIdentifier = 0;
+	   CommandIdentifier < Controller->DriverQueueDepth;
+	   CommandIdentifier++)
+	{
+	  Commands[CommandIdentifier].Controller = Controller;
+	  Commands[CommandIdentifier].Next = Controller->FreeCommands;
+	  Controller->FreeCommands = &Commands[CommandIdentifier];
+	}
+      /*
+	Initialize the Monitoring Timer.
+      */
+      init_timer(&Controller->MonitoringTimer);
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      Controller->MonitoringTimer.data = (unsigned long) Controller;
+      Controller->MonitoringTimer.function = DAC960_MonitoringTimerFunction;
+      add_timer(&Controller->MonitoringTimer);
+      Controller->ControllerInitialized = true;
+    }
+  else DAC960_FinalizeController(Controller);
+}
+
+
+/*
+  DAC960_FinalizeController finalizes Controller.
+*/
+
+static void DAC960_FinalizeController(DAC960_Controller_T *Controller)
+{
+  if (Controller->ControllerInitialized)
+    {
+      del_timer(&Controller->MonitoringTimer);
+      DAC960_Notice("Flushing Cache...", Controller);
+      DAC960_ExecuteType3(Controller, DAC960_Flush, NULL);
+      DAC960_Notice("done\n", Controller);
+      switch (Controller->ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  if (!Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V5_SaveMemoryMailboxInfo(Controller);
+	  break;
+	case DAC960_V4_Controller:
+	  if (!Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V4_SaveMemoryMailboxInfo(Controller);
+	  break;
+	case DAC960_V3_Controller:
+	  break;
+	}
+    }
+  free_irq(Controller->IRQ_Channel, Controller);
+  iounmap(Controller->MemoryMappedAddress);
+  if (Controller->IO_Address > 0)
+    release_region(Controller->IO_Address, 0x80);
+  DAC960_UnregisterBlockDevice(Controller);
+  DAC960_Controllers[Controller->ControllerNumber] = NULL;
+  kfree(Controller);
+}
+
+
+/*
+  DAC960_Initialize initializes the DAC960 Driver.
+*/
+
+void DAC960_Initialize(void)
+{
+  int ControllerNumber;
+  DAC960_DetectControllers(DAC960_V5_Controller);
+  DAC960_DetectControllers(DAC960_V4_Controller);
+  DAC960_DetectControllers(DAC960_V3_Controller);
+  if (DAC960_ActiveControllerCount == 0) return;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    if (DAC960_Controllers[ControllerNumber] != NULL)
+      DAC960_InitializeController(DAC960_Controllers[ControllerNumber]);
+  DAC960_CreateProcEntries();
+}
+
+
+/*
+  DAC960_Finalize finalizes the DAC960 Driver.
+*/
+
+void DAC960_Finalize(void)
+{
+  int ControllerNumber;
+  if (DAC960_ActiveControllerCount == 0) return;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    if (DAC960_Controllers[ControllerNumber] != NULL)
+      DAC960_FinalizeController(DAC960_Controllers[ControllerNumber]);
+  DAC960_DestroyProcEntries();
+}
+
+
+/*
+  DAC960_ProcessRequest attempts to remove one I/O Request from Controller's
+  I/O Request Queue and queues it to the Controller.  WaitForCommand is true if
+  this function should wait for a Command to become available if necessary.
+  This function returns true if an I/O Request was queued and false otherwise.
+*/
+
+static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller,
+				     boolean WaitForCommand)
+{
+  IO_Request_T **RequestQueuePointer =
+    &blk_dev[DAC960_MAJOR + Controller->ControllerNumber].current_request;
+  IO_Request_T *Request;
+  DAC960_Command_T *Command;
+  char *RequestBuffer;
+  while (true)
+    {
+      Request = *RequestQueuePointer;
+      if (Request == NULL || Request->rq_status == RQ_INACTIVE) return false;
+      Command = DAC960_AllocateCommand(Controller);
+      if (Command != NULL) break;
+      if (!WaitForCommand) return false;
+      sleep_on(&Controller->CommandWaitQueue);
+    }
+  DAC960_ClearCommand(Command);
+  if (Request->cmd == READ)
+    Command->CommandType = DAC960_ReadCommand;
+  else Command->CommandType = DAC960_WriteCommand;
+  Command->Semaphore = Request->sem;
+  Command->LogicalDriveNumber = DAC960_LogicalDriveNumber(Request->rq_dev);
+  Command->BlockNumber =
+    Request->sector
+    + Controller->GenericDiskInfo.part[MINOR(Request->rq_dev)].start_sect;
+  Command->BlockCount = Request->nr_sectors;
+  Command->SegmentCount = Request->nr_segments;
+  Command->BufferHeader = Request->bh;
+  RequestBuffer = Request->buffer;
+  Request->rq_status = RQ_INACTIVE;
+  *RequestQueuePointer = Request->next;
+  wake_up(&wait_for_request);
+  if (Command->SegmentCount == 1)
+    {
+      DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+      else CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(RequestBuffer);
+    }
+  else
+    {
+      DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+      DAC960_ScatterGatherSegment_T
+	*ScatterGatherList = Command->ScatterGatherList;
+      BufferHeader_T *BufferHeader = Command->BufferHeader;
+      char *LastDataEndPointer = NULL;
+      int SegmentNumber = 0;
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_ReadWithOldScatterGather;
+      else
+	CommandMailbox->Type5.CommandOpcode = DAC960_WriteWithOldScatterGather;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(ScatterGatherList);
+      CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount;
+      while (BufferHeader != NULL)
+	{
+	  if (BufferHeader->b_data == LastDataEndPointer)
+	    {
+	      ScatterGatherList[SegmentNumber-1].SegmentByteCount +=
+		BufferHeader->b_size;
+	      LastDataEndPointer += BufferHeader->b_size;
+	    }
+	  else
+	    {
+	      ScatterGatherList[SegmentNumber].SegmentDataPointer =
+		Virtual_to_Bus(BufferHeader->b_data);
+	      ScatterGatherList[SegmentNumber].SegmentByteCount =
+		BufferHeader->b_size;
+	      LastDataEndPointer = BufferHeader->b_data + BufferHeader->b_size;
+	      if (SegmentNumber++ > Controller->MaxScatterGatherSegments)
+		panic("DAC960: Scatter/Gather Segment Overflow\n");
+	    }
+	  BufferHeader = BufferHeader->b_reqnext;
+	}
+      if (SegmentNumber != Command->SegmentCount)
+	panic("DAC960: SegmentNumber != SegmentCount\n");
+    }
+  DAC960_QueueCommand(Command);
+  return true;
+}
+
+
+/*
+  DAC960_ProcessRequests attempts to remove as many I/O Requests as possible
+  from Controller's I/O Request Queue and queue them to the Controller.
+*/
+
+static inline void DAC960_ProcessRequests(DAC960_Controller_T *Controller)
+{
+  int Counter = 0;
+  while (DAC960_ProcessRequest(Controller, Counter++ == 0)) ;
+}
+
+
+/*
+  DAC960_RequestFunction0 is the I/O Request Function for DAC960 Controller 0.
+*/
+
+static void DAC960_RequestFunction0(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[0];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction1 is the I/O Request Function for DAC960 Controller 1.
+*/
+
+static void DAC960_RequestFunction1(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[1];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction2 is the I/O Request Function for DAC960 Controller 2.
+*/
+
+static void DAC960_RequestFunction2(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[2];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction3 is the I/O Request Function for DAC960 Controller 3.
+*/
+
+static void DAC960_RequestFunction3(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[3];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction4 is the I/O Request Function for DAC960 Controller 4.
+*/
+
+static void DAC960_RequestFunction4(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[4];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction5 is the I/O Request Function for DAC960 Controller 5.
+*/
+
+static void DAC960_RequestFunction5(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[5];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction6 is the I/O Request Function for DAC960 Controller 6.
+*/
+
+static void DAC960_RequestFunction6(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[6];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction7 is the I/O Request Function for DAC960 Controller 7.
+*/
+
+static void DAC960_RequestFunction7(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[7];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_ReadWriteError prints an appropriate error message for Command when
+  an error occurs on a Read or Write operation.
+*/
+
+static void DAC960_ReadWriteError(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  char *CommandName = "UNKNOWN";
+  switch (Command->CommandType)
+    {
+    case DAC960_ReadCommand:
+    case DAC960_ReadRetryCommand:
+      CommandName = "READ";
+      break;
+    case DAC960_WriteCommand:
+    case DAC960_WriteRetryCommand:
+      CommandName = "WRITE";
+      break;
+    case DAC960_MonitoringCommand:
+    case DAC960_ImmediateCommand:
+      break;
+    }
+  switch (Command->CommandStatus)
+    {
+    case DAC960_IrrecoverableDataError:
+      DAC960_Error("Irrecoverable Data Error on %s:\n",
+		   Controller, CommandName);
+      break;
+    case DAC960_LogicalDriveNonexistentOrOffline:
+      DAC960_Error("Logical Drive Nonexistent or Offline on %s:\n",
+		   Controller, CommandName);
+      break;
+    case DAC960_AccessBeyondEndOfLogicalDrive:
+      DAC960_Error("Attempt to Access Beyond End of Logical Drive "
+		   "on %s:\n", Controller, CommandName);
+      break;
+    case DAC960_BadDataEncountered:
+      DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName);
+      break;
+    default:
+      DAC960_Error("Unexpected Error Status %04X on %s:\n",
+		   Controller, Command->CommandStatus, CommandName);
+      break;
+    }
+  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %d..%d\n",
+	       Controller, Controller->ControllerNumber,
+	       Command->LogicalDriveNumber, Command->BlockNumber,
+	       Command->BlockNumber + Command->BlockCount - 1);
+  if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0)
+    DAC960_Error("  /dev/rd/c%dd%dp%d: relative blocks %d..%d\n",
+		 Controller, Controller->ControllerNumber,
+		 Command->LogicalDriveNumber,
+		 DAC960_PartitionNumber(Command->BufferHeader->b_rdev),
+		 Command->BufferHeader->b_rsector,
+		 Command->BufferHeader->b_rsector + Command->BlockCount - 1);
+}
+
+
+/*
+  DAC960_ProcessCompletedBuffer performs completion processing for an
+  individual Buffer.
+*/
+
+static inline void DAC960_ProcessCompletedBuffer(BufferHeader_T *BufferHeader,
+						 boolean SuccessfulIO)
+{
+  mark_buffer_uptodate(BufferHeader, SuccessfulIO);
+  unlock_buffer(BufferHeader);
+}
+
+
+/*
+  DAC960_ProcessCompletedCommand performs completion processing for Command.
+*/
+
+static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandType_T CommandType = Command->CommandType;
+  DAC960_CommandStatus_T CommandStatus = Command->CommandStatus;
+  BufferHeader_T *BufferHeader = Command->BufferHeader;
+  if (CommandType == DAC960_ReadCommand ||
+      CommandType == DAC960_WriteCommand)
+    {
+      if (CommandStatus == DAC960_NormalCompletion)
+	{
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, true);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	  add_blkdev_randomness(DAC960_MAJOR + Controller->ControllerNumber);
+	}
+      else if ((CommandStatus == DAC960_IrrecoverableDataError ||
+		CommandStatus == DAC960_BadDataEncountered) &&
+	       BufferHeader != NULL &&
+	       BufferHeader->b_reqnext != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  if (CommandType == DAC960_ReadCommand)
+	    {
+	      Command->CommandType = DAC960_ReadRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+	    }
+	  else
+	    {
+	      Command->CommandType = DAC960_WriteRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+	    }
+	  Command->BlockCount = BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(BufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      else
+	{
+	  if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline)
+	    DAC960_ReadWriteError(Command);
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	}
+    }
+  else if (CommandType == DAC960_ReadRetryCommand ||
+	   CommandType == DAC960_WriteRetryCommand)
+    {
+      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+      BufferHeader->b_reqnext = NULL;
+      /*
+	Perform completion processing for this single buffer.
+      */
+      if (CommandStatus == DAC960_NormalCompletion)
+	DAC960_ProcessCompletedBuffer(BufferHeader, true);
+      else
+	{
+	  if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline)
+	    DAC960_ReadWriteError(Command);
+	  DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	}
+      if (NextBufferHeader != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  Command->BlockNumber +=
+	    BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BlockCount =
+	    NextBufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BufferHeader = NextBufferHeader;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(NextBufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+    }
+  else if (CommandType == DAC960_MonitoringCommand)
+    {
+      DAC960_CommandOpcode_T CommandOpcode =
+	Command->CommandMailbox.Common.CommandOpcode;
+      unsigned int OldCriticalLogicalDriveCount = 0;
+      unsigned int NewCriticalLogicalDriveCount = 0;
+      if (CommandOpcode == DAC960_Enquiry)
+	{
+	  DAC960_Enquiry_T *OldEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex];
+	  DAC960_Enquiry_T *NewEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex ^= 1];
+	  OldCriticalLogicalDriveCount = OldEnquiry->CriticalLogicalDriveCount;
+	  NewCriticalLogicalDriveCount = NewEnquiry->CriticalLogicalDriveCount;
+	  if (NewEnquiry->StatusFlags.DeferredWriteError !=
+	      OldEnquiry->StatusFlags.DeferredWriteError)
+	    DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller,
+			    (NewEnquiry->StatusFlags.DeferredWriteError
+			     ? "TRUE" : "FALSE"));
+	  if ((NewCriticalLogicalDriveCount > 0 ||
+	       NewCriticalLogicalDriveCount != OldCriticalLogicalDriveCount) ||
+	      (NewEnquiry->OfflineLogicalDriveCount > 0 ||
+	       NewEnquiry->OfflineLogicalDriveCount !=
+	       OldEnquiry->OfflineLogicalDriveCount) ||
+	      (NewEnquiry->DeadDriveCount > 0 ||
+	       NewEnquiry->DeadDriveCount !=
+	       OldEnquiry->DeadDriveCount) ||
+	      (NewEnquiry->EventLogSequenceNumber !=
+	       OldEnquiry->EventLogSequenceNumber) ||
+	      Controller->MonitoringTimerCount == 0 ||
+	      (jiffies - Controller->SecondaryMonitoringTime
+	       >= DAC960_SecondaryMonitoringInterval))
+	    {
+	      Controller->NeedLogicalDriveInformation = true;
+	      Controller->NewEventLogSequenceNumber =
+		NewEnquiry->EventLogSequenceNumber;
+	      Controller->NeedErrorTableInformation = true;
+	      Controller->NeedDeviceStateInformation = true;
+	      Controller->DeviceStateChannel = 0;
+	      Controller->DeviceStateTargetID = 0;
+	      Controller->SecondaryMonitoringTime = jiffies;
+	    }
+	  if (NewEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress ||
+	      NewEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress ||
+	      OldEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress ||
+	      OldEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress)
+	    Controller->NeedRebuildProgress = true;
+	  if (OldEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress)
+	    switch (NewEnquiry->RebuildFlag)
+	      {
+	      case DAC960_NoStandbyRebuildOrCheckInProgress:
+		DAC960_Progress("Consistency Check Completed Successfully\n",
+				Controller);
+		break;
+	      case DAC960_StandbyRebuildInProgress:
+	      case DAC960_BackgroundRebuildInProgress:
+		break;
+	      case DAC960_BackgroundCheckInProgress:
+		Controller->NeedConsistencyCheckProgress = true;
+		break;
+	      case DAC960_StandbyRebuildCompletedWithError:
+		DAC960_Progress("Consistency Check Completed with Error\n",
+				Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_DriveFailed:
+		DAC960_Progress("Consistency Check Failed - "
+				"Physical Drive Failed\n", Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed:
+		DAC960_Progress("Consistency Check Failed - "
+				"Logical Drive Failed\n", Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_OtherCauses:
+		DAC960_Progress("Consistency Check Failed - Other Causes\n",
+				Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated:
+		DAC960_Progress("Consistency Check Successfully Terminated\n",
+				Controller);
+		break;
+	      }
+	  else if (NewEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress)
+	    Controller->NeedConsistencyCheckProgress = true;
+	}
+      else if (CommandOpcode == DAC960_GetLogicalDriveInformation)
+	{
+	  int LogicalDriveNumber;
+	  for (LogicalDriveNumber = 0;
+	       LogicalDriveNumber < Controller->LogicalDriveCount;
+	       LogicalDriveNumber++)
+	    {
+	      DAC960_LogicalDriveInformation_T *OldLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex]
+			     [LogicalDriveNumber];
+	      DAC960_LogicalDriveInformation_T *NewLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex ^ 1]
+			     [LogicalDriveNumber];
+	      if (NewLogicalDriveInformation->LogicalDriveState !=
+		  OldLogicalDriveInformation->LogicalDriveState)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Online
+				 ? "ONLINE"
+				 : NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Critical
+				 ? "CRITICAL" : "OFFLINE"));
+	      if (NewLogicalDriveInformation->WriteBack !=
+		  OldLogicalDriveInformation->WriteBack)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->WriteBack
+				 ? "WRITE BACK" : "WRITE THRU"));
+	    }
+	  Controller->LogicalDriveInformationIndex ^= 1;
+	}
+      else if (CommandOpcode == DAC960_PerformEventLogOperation)
+	{
+	  DAC960_EventLogEntry_T *EventLogEntry = &Controller->EventLogEntry;
+	  if (EventLogEntry->SequenceNumber ==
+	      Controller->OldEventLogSequenceNumber)
+	    {
+	      unsigned char SenseKey = EventLogEntry->SenseKey;
+	      unsigned char AdditionalSenseCode =
+		EventLogEntry->AdditionalSenseCode;
+	      unsigned char AdditionalSenseCodeQualifier =
+		EventLogEntry->AdditionalSenseCodeQualifier;
+	      if (SenseKey == 9 &&
+		  AdditionalSenseCode == 0x80 &&
+		  AdditionalSenseCodeQualifier < DAC960_EventMessagesCount)
+		DAC960_Critical("Physical Drive %d:%d %s\n", Controller,
+				EventLogEntry->Channel,
+				EventLogEntry->TargetID,
+				DAC960_EventMessages[
+				  AdditionalSenseCodeQualifier]);
+	      else if (!((SenseKey == 2 &&
+			  AdditionalSenseCode == 0x04 &&
+			  (AdditionalSenseCodeQualifier == 0x01 ||
+			   AdditionalSenseCodeQualifier == 0x02)) ||
+			 (SenseKey == 6 && AdditionalSenseCode == 0x29 &&
+			  Controller->MonitoringTimerCount == 0)))
+		DAC960_Critical("Physical Drive %d:%d Error Log: "
+				"Sense Key = %d, ASC = %02X, ASCQ = %02X\n",
+				Controller,
+				EventLogEntry->Channel,
+				EventLogEntry->TargetID,
+				SenseKey,
+				AdditionalSenseCode,
+				AdditionalSenseCodeQualifier);
+	    }
+	  Controller->OldEventLogSequenceNumber++;
+	}
+      else if (CommandOpcode == DAC960_GetErrorTable)
+	{
+	  DAC960_ErrorTable_T *OldErrorTable =
+	    &Controller->ErrorTable[Controller->ErrorTableIndex];
+	  DAC960_ErrorTable_T *NewErrorTable =
+	    &Controller->ErrorTable[Controller->ErrorTableIndex ^= 1];
+	  int Channel, TargetID;
+	  for (Channel = 0; Channel < Controller->Channels; Channel++)
+	    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+	      {
+		DAC960_ErrorTableEntry_T *NewErrorEntry =
+		  &NewErrorTable->ErrorTableEntries[Channel][TargetID];
+		DAC960_ErrorTableEntry_T *OldErrorEntry =
+		  &OldErrorTable->ErrorTableEntries[Channel][TargetID];
+		if ((NewErrorEntry->ParityErrorCount !=
+		     OldErrorEntry->ParityErrorCount) ||
+		    (NewErrorEntry->SoftErrorCount !=
+		     OldErrorEntry->SoftErrorCount) ||
+		    (NewErrorEntry->HardErrorCount !=
+		     OldErrorEntry->HardErrorCount) ||
+		    (NewErrorEntry->MiscErrorCount !=
+		     OldErrorEntry->MiscErrorCount))
+		  DAC960_Critical("Physical Drive %d:%d Errors: "
+				  "Parity = %d, Soft = %d, "
+				  "Hard = %d, Misc = %d\n",
+				  Controller, Channel, TargetID,
+				  NewErrorEntry->ParityErrorCount,
+				  NewErrorEntry->SoftErrorCount,
+				  NewErrorEntry->HardErrorCount,
+				  NewErrorEntry->MiscErrorCount);
+	      }
+	}
+      else if (CommandOpcode == DAC960_GetDeviceState)
+	{
+	  DAC960_DeviceState_T *OldDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  DAC960_DeviceState_T *NewDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex ^ 1]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  if (NewDeviceState->DeviceState != OldDeviceState->DeviceState)
+	    DAC960_Critical("Physical Drive %d:%d is now %s\n", Controller,
+			    Controller->DeviceStateChannel,
+			    Controller->DeviceStateTargetID,
+			    (NewDeviceState->DeviceState == DAC960_Device_Dead
+			     ? "DEAD"
+			     : NewDeviceState->DeviceState
+			       == DAC960_Device_WriteOnly
+			       ? "WRITE-ONLY"
+			       : NewDeviceState->DeviceState
+				 == DAC960_Device_Online
+				 ? "ONLINE" : "STANDBY"));
+	  if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+	    {
+	      Controller->DeviceStateChannel++;
+	      Controller->DeviceStateTargetID = 0;
+	    }
+	}
+      else if (CommandOpcode == DAC960_GetRebuildProgress)
+	{
+	  unsigned int LogicalDriveNumber =
+	    Controller->RebuildProgress.LogicalDriveNumber;
+	  unsigned int LogicalDriveSize =
+	    Controller->RebuildProgress.LogicalDriveSize;
+	  unsigned int BlocksCompleted =
+	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  switch (CommandStatus)
+	    {
+	    case DAC960_NormalCompletion:
+	      Controller->EphemeralProgressMessage = true;
+	      DAC960_Progress("Rebuild in Progress: "
+			      "Logical Drive %d (/dev/rd/c%dd%d) "
+			      "%d%% completed\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber,
+			      (100 * (BlocksCompleted >> 7))
+			      / (LogicalDriveSize >> 7));
+	      Controller->EphemeralProgressMessage = false;
+	      break;
+	    case DAC960_RebuildFailed_LogicalDriveFailure:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Logical Drive Failure\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_BadBlocksOnOther:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Bad Blocks on Other Drives\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_NewDriveFailed:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Failure of Drive Being Rebuilt\n", Controller);
+	      break;
+	    case DAC960_RebuildSuccessful:
+	    case DAC960_NoRebuildOrCheckInProgress:
+	      DAC960_Progress("Rebuild Completed Successfully\n", Controller);
+	      break;
+	    }
+	}
+      else if (CommandOpcode == DAC960_RebuildStat)
+	{
+	  unsigned int LogicalDriveNumber =
+	    Controller->RebuildProgress.LogicalDriveNumber;
+	  unsigned int LogicalDriveSize =
+	    Controller->RebuildProgress.LogicalDriveSize;
+	  unsigned int BlocksCompleted =
+	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  if (CommandStatus == DAC960_NormalCompletion)
+	    {
+	      Controller->EphemeralProgressMessage = true;
+	      DAC960_Progress("Consistency Check in Progress: "
+			      "Logical Drive %d (/dev/rd/c%dd%d) "
+			      "%d%% completed\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber,
+			      (100 * (BlocksCompleted >> 7))
+			      / (LogicalDriveSize >> 7));
+	      Controller->EphemeralProgressMessage = false;
+	    }
+	}
+      if (Controller->NeedLogicalDriveInformation &&
+	  NewCriticalLogicalDriveCount >= OldCriticalLogicalDriveCount)
+	{
+	  Controller->NeedLogicalDriveInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetLogicalDriveInformation;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->LogicalDriveInformation
+			   [Controller->LogicalDriveInformationIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NewEventLogSequenceNumber
+	  - Controller->OldEventLogSequenceNumber > 0)
+	{
+	  Command->CommandMailbox.Type3E.CommandOpcode =
+	    DAC960_PerformEventLogOperation;
+	  Command->CommandMailbox.Type3E.OperationType =
+	    DAC960_GetEventLogEntry;
+	  Command->CommandMailbox.Type3E.OperationQualifier = 1;
+	  Command->CommandMailbox.Type3E.SequenceNumber =
+	    Controller->OldEventLogSequenceNumber;
+	  Command->CommandMailbox.Type3E.BusAddress =
+	    Virtual_to_Bus(&Controller->EventLogEntry);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedErrorTableInformation)
+	{
+	  Controller->NeedErrorTableInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode = DAC960_GetErrorTable;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->ErrorTable[Controller->ErrorTableIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedDeviceStateInformation)
+	{
+	  while (Controller->DeviceStateChannel < Controller->Channels)
+	    {
+	      DAC960_DeviceState_T *OldDeviceState =
+		&Controller->DeviceState[Controller->DeviceStateIndex]
+					[Controller->DeviceStateChannel]
+					[Controller->DeviceStateTargetID];
+	      if (OldDeviceState->Present &&
+		  OldDeviceState->DeviceType == DAC960_DiskType)
+		{
+		  Command->CommandMailbox.Type3D.CommandOpcode =
+		    DAC960_GetDeviceState;
+		  Command->CommandMailbox.Type3D.Channel =
+		    Controller->DeviceStateChannel;
+		  Command->CommandMailbox.Type3D.TargetID =
+		    Controller->DeviceStateTargetID;
+		  Command->CommandMailbox.Type3D.BusAddress =
+		    Virtual_to_Bus(&Controller->DeviceState
+				      [Controller->DeviceStateIndex ^ 1]
+				      [Controller->DeviceStateChannel]
+				      [Controller->DeviceStateTargetID]);
+		  DAC960_QueueCommand(Command);
+		  return;
+		}
+	      if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+		{
+		  Controller->DeviceStateChannel++;
+		  Controller->DeviceStateTargetID = 0;
+		}
+	    }
+	  Controller->NeedDeviceStateInformation = false;
+	  Controller->DeviceStateIndex ^= 1;
+	}
+      if (Controller->NeedRebuildProgress)
+	{
+	  Controller->NeedRebuildProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetRebuildProgress;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedConsistencyCheckProgress)
+	{
+	  Controller->NeedConsistencyCheckProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode = DAC960_RebuildStat;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedLogicalDriveInformation &&
+	  NewCriticalLogicalDriveCount < OldCriticalLogicalDriveCount)
+	{
+	  Controller->NeedLogicalDriveInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetLogicalDriveInformation;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->LogicalDriveInformation
+			   [Controller->LogicalDriveInformationIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      Controller->MonitoringTimerCount++;
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      add_timer(&Controller->MonitoringTimer);
+    }
+  else if (CommandType == DAC960_ImmediateCommand)
+    {
+      up(Command->Semaphore);
+      Command->Semaphore = NULL;
+      return;
+    }
+  else panic("DAC960: Unknown Command Type %d\n", CommandType);
+  /*
+    Queue a Status Monitoring Command to the Controller using the just
+    completed Command if one was deferred previously due to lack of a
+    free Command when the Monitoring Timer Function was called.
+  */
+  if (Controller->MonitoringCommandDeferred)
+    {
+      Controller->MonitoringCommandDeferred = false;
+      DAC960_QueueMonitoringCommand(Command);
+      return;
+    }
+  /*
+    Execute a User Command using the just completed Command if one was
+    deferred previously due to lack of a free Command.
+  */
+  if (Controller->UserCommandDeferred)
+    {
+      Controller->UserCommandDeferred = false;
+      Controller->UserCommand = Command;
+      up(Controller->UserCommandSemaphore);
+      return;
+    }
+  /*
+    Deallocate the Command, and wake up any processes waiting on a free Command.
+  */
+  DAC960_DeallocateCommand(Command);
+  wake_up(&Controller->CommandWaitQueue);
+}
+
+
+/*
+  DAC960_InterruptHandler handles hardware interrupts from DAC960 Controllers.
+*/
+
+static void DAC960_InterruptHandler(int IRQ_Channel,
+				    void *DeviceIdentifier,
+				    Registers_T *InterruptRegisters)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) DeviceIdentifier;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_StatusMailbox_T *NextStatusMailbox;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockIH(Controller, &ProcessorFlags);
+  /*
+    Process Hardware Interrupts for Controller.
+  */
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V5_Controller:
+      DAC960_V5_AcknowledgeInterrupt(ControllerBaseAddress);
+      NextStatusMailbox = Controller->NextStatusMailbox;
+      while (NextStatusMailbox->Fields.Valid)
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    NextStatusMailbox->Fields.CommandIdentifier;
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+	  NextStatusMailbox->Word = 0;
+	  if (++NextStatusMailbox > Controller->LastStatusMailbox)
+	    NextStatusMailbox = Controller->FirstStatusMailbox;
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      Controller->NextStatusMailbox = NextStatusMailbox;
+      break;
+    case DAC960_V4_Controller:
+      DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress);
+      NextStatusMailbox = Controller->NextStatusMailbox;
+      while (NextStatusMailbox->Fields.Valid)
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    NextStatusMailbox->Fields.CommandIdentifier;
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+	  NextStatusMailbox->Word = 0;
+	  if (++NextStatusMailbox > Controller->LastStatusMailbox)
+	    NextStatusMailbox = Controller->FirstStatusMailbox;
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      Controller->NextStatusMailbox = NextStatusMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_StatusAvailableP(ControllerBaseAddress))
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    DAC960_V3_ReadStatusCommandIdentifier(ControllerBaseAddress);
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus =
+	    DAC960_V3_ReadStatusRegister(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeInterrupt(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeStatus(ControllerBaseAddress);
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      break;
+    }
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  while (DAC960_ProcessRequest(Controller, false)) ;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockIH(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_QueueMonitoringCommand queues a Monitoring Command to Controller.
+*/
+
+static void DAC960_QueueMonitoringCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_MonitoringCommand;
+  CommandMailbox->Type3.CommandOpcode = DAC960_Enquiry;
+  CommandMailbox->Type3.BusAddress =
+    Virtual_to_Bus(&Controller->Enquiry[Controller->EnquiryIndex ^ 1]);
+  DAC960_QueueCommand(Command);
+}
+
+
+/*
+  DAC960_MonitoringTimerFunction is the timer function for monitoring
+  the status of DAC960 Controllers.
+*/
+
+static void DAC960_MonitoringTimerFunction(unsigned long TimerData)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) TimerData;
+  DAC960_Command_T *Command;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Queue a Status Monitoring Command to Controller.
+  */
+  Command = DAC960_AllocateCommand(Controller);
+  if (Command != NULL)
+    DAC960_QueueMonitoringCommand(Command);
+  else Controller->MonitoringCommandDeferred = true;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_Open is the Device Open Function for the DAC960 Driver.
+*/
+
+static int DAC960_Open(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller;
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  if (Controller->LogicalDriveInformation
+		  [Controller->LogicalDriveInformationIndex]
+		  [LogicalDriveNumber].LogicalDriveState
+      == DAC960_LogicalDrive_Offline)
+    return -ENXIO;
+  if (Controller->LogicalDriveInitialState[LogicalDriveNumber]
+      == DAC960_LogicalDrive_Offline)
+    {
+      Controller->LogicalDriveInitialState[LogicalDriveNumber] =
+	DAC960_LogicalDrive_Online;
+      DAC960_InitializeGenericDiskInfo(&Controller->GenericDiskInfo);
+      resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+    }
+  if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0)
+    return -ENXIO;
+  /*
+    Increment Controller and Logical Drive Usage Counts.
+  */
+  Controller->ControllerUsageCount++;
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]++;
+  return 0;
+}
+
+
+/*
+  DAC960_Release is the Device Release Function for the DAC960 Driver.
+*/
+
+static void DAC960_Release(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+  /*
+    Force any buffered data to be written.
+  */
+  fsync_dev(Inode->i_rdev);
+  /*
+    Decrement the Logical Drive and Controller Usage Counts.
+  */
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]--;
+  Controller->ControllerUsageCount--;
+}
+
+
+/*
+  DAC960_Ioctl is the Device Ioctl Function for the DAC960 Driver.
+*/
+
+static int DAC960_Ioctl(Inode_T *Inode, File_T *File,
+			unsigned int Request, unsigned long Argument)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  int PartitionNumber, ErrorCode;
+  unsigned short Cylinders;
+  DiskGeometry_T *Geometry;
+  DAC960_Controller_T *Controller;
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  switch (Request)
+    {
+    case HDIO_GETGEO:
+      /* Get BIOS Disk Geometry. */
+      Geometry = (DiskGeometry_T *) Argument;
+      if (Geometry == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, Geometry, sizeof(DiskGeometry_T));
+      if (ErrorCode != 0) return ErrorCode;
+      Cylinders =
+	Controller->LogicalDriveInformation
+		    [Controller->LogicalDriveInformationIndex]
+		    [LogicalDriveNumber].LogicalDriveSize
+	/ (Controller->GeometryTranslationHeads *
+	   Controller->GeometryTranslationSectors);
+      put_user(Controller->GeometryTranslationHeads, &Geometry->heads);
+      put_user(Controller->GeometryTranslationSectors, &Geometry->sectors);
+      put_user(Cylinders, &Geometry->cylinders);
+      put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)]
+					  .start_sect, &Geometry->start);
+      return 0;
+    case BLKGETSIZE:
+      /* Get Device Size. */
+      if ((long *) Argument == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, (long *) Argument, sizeof(long));
+      if (ErrorCode != 0) return ErrorCode;
+      put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)].nr_sects,
+	       (long *) Argument);
+      return 0;
+    case BLKRAGET:
+      /* Get Read-Ahead. */
+      if ((int *) Argument == NULL) return -EINVAL;
+      ErrorCode = verify_area(VERIFY_WRITE, (int *) Argument, sizeof(int));
+      if (ErrorCode != 0) return ErrorCode;
+      put_user(read_ahead[MAJOR(Inode->i_rdev)], (int *) Argument);
+      return 0;
+    case BLKRASET:
+      /* Set Read-Ahead. */
+      if (!suser()) return -EACCES;
+      if (Argument > 256) return -EINVAL;
+      read_ahead[MAJOR(Inode->i_rdev)] = Argument;
+      return 0;
+    case BLKFLSBUF:
+      /* Flush Buffers. */
+      if (!suser()) return -EACCES;
+      fsync_dev(Inode->i_rdev);
+      invalidate_buffers(Inode->i_rdev);
+      return 0;
+    case BLKRRPART:
+      /* Re-Read Partition Table. */
+      if (!suser()) return -EACCES;
+      if (Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 1)
+	return -EBUSY;
+      for (PartitionNumber = 0;
+	   PartitionNumber < DAC960_MaxPartitions;
+	   PartitionNumber++)
+	{
+	  KernelDevice_T Device = DAC960_KernelDevice(ControllerNumber,
+						      LogicalDriveNumber,
+						      PartitionNumber);
+	  int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber,
+					       PartitionNumber);
+	  if (Controller->GenericDiskInfo.part[MinorNumber].nr_sects == 0)
+	    continue;
+	  /*
+	    Flush all changes and invalidate buffered state.
+	  */
+	  sync_dev(Device);
+	  invalidate_inodes(Device);
+	  invalidate_buffers(Device);
+	  /*
+	    Clear existing partition sizes.
+	  */
+	  if (PartitionNumber > 0)
+	    {
+	      Controller->GenericDiskInfo.part[MinorNumber].start_sect = 0;
+	      Controller->GenericDiskInfo.part[MinorNumber].nr_sects = 0;
+	    }
+	  /*
+	    Reset the Block Size so that the partition table can be read.
+	  */
+	  set_blocksize(Device, BLOCK_SIZE);
+	}
+      resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+      return 0;
+    }
+  return -EINVAL;
+}
+
+
+/*
+  DAC960_GenericDiskInit is the Generic Disk Information Initialization
+  Function for the DAC960 Driver.
+*/
+
+static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo)
+{
+  DAC960_Controller_T *Controller =
+    (DAC960_Controller_T *) GenericDiskInfo->real_devices;
+  DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+    Controller->LogicalDriveInformation
+		[Controller->LogicalDriveInformationIndex];
+  int LogicalDriveNumber;
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)].nr_sects =
+      LogicalDriveInformation[LogicalDriveNumber].LogicalDriveSize;
+}
+
+
+/*
+  DAC960_Message prints Driver Messages.
+*/
+
+static void DAC960_Message(DAC960_MessageLevel_T MessageLevel,
+			   char *Format,
+			   DAC960_Controller_T *Controller,
+			   ...)
+{
+  static char Buffer[DAC960_LineBufferSize];
+  static boolean BeginningOfLine = true;
+  va_list Arguments;
+  int Length = 0;
+  va_start(Arguments, Controller);
+  Length = vsprintf(Buffer, Format, Arguments);
+  va_end(Arguments);
+  if (Controller == NULL)
+    printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	   DAC960_ControllerCount, Buffer);
+  else if (MessageLevel == DAC960_AnnounceLevel ||
+	   MessageLevel == DAC960_InfoLevel)
+    {
+      if (!Controller->ControllerInitialized)
+	{
+	  strcpy(&Controller->InitialStatusBuffer[
+		    Controller->InitialStatusLength], Buffer);
+	  Controller->InitialStatusLength += Length;
+	  if (MessageLevel == DAC960_AnnounceLevel)
+	    {
+	      static int AnnouncementLines = 0;
+	      if (++AnnouncementLines <= 2)
+		printk("%sDAC960: %s", DAC960_MessageLevelMap[MessageLevel],
+		       Buffer);
+	    }
+	  else
+	    {
+	      if (BeginningOfLine)
+		{
+		  if (Buffer[0] != '\n' || Length > 1)
+		    printk("%sDAC960#%d: %s",
+			   DAC960_MessageLevelMap[MessageLevel],
+			   Controller->ControllerNumber, Buffer);
+		}
+	      else printk("%s", Buffer);
+	    }
+	}
+      else
+	{
+	  strcpy(&Controller->CurrentStatusBuffer[
+		    Controller->CurrentStatusLength], Buffer);
+	  Controller->CurrentStatusLength += Length;
+	}
+    }
+  else if (MessageLevel == DAC960_ProgressLevel)
+    {
+      strcpy(Controller->RebuildProgressBuffer, Buffer);
+      Controller->RebuildProgressLength = Length;
+      if (Controller->EphemeralProgressMessage)
+	{
+	  if (jiffies - Controller->LastProgressReportTime
+	      >= DAC960_ProgressReportingInterval)
+	    {
+	      printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+		     Controller->ControllerNumber, Buffer);
+	      Controller->LastProgressReportTime = jiffies;
+	    }
+	}
+      else printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+		  Controller->ControllerNumber, Buffer);
+    }
+  else if (MessageLevel == DAC960_UserCriticalLevel)
+    {
+      strcpy(&Controller->UserStatusBuffer[Controller->UserStatusLength],
+	     Buffer);
+      Controller->UserStatusLength += Length;
+      if (Buffer[0] != '\n' || Length > 1)
+	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	       Controller->ControllerNumber, Buffer);
+    }
+  else
+    {
+      if (BeginningOfLine)
+	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	       Controller->ControllerNumber, Buffer);
+      else printk("%s", Buffer);
+    }
+  BeginningOfLine = (Buffer[Length-1] == '\n');
+}
+
+
+/*
+  DAC960_AllocateUserCommand allocates a Command structure for a User Command.
+  If a User Command is already active, NULL is returned.
+*/
+
+static DAC960_Command_T *DAC960_AllocateUserCommand(DAC960_Controller_T
+						    *Controller)
+{
+  Semaphore_T Semaphore = MUTEX_LOCKED;
+  DAC960_Command_T *Command = NULL;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Acquire exclusive access to the User Command facility.
+  */
+  if (Controller->UserCommandActive) goto Done;
+  Controller->UserCommandActive = true;
+  /*
+    Allocate a Command.  If none is available, set the User Command Deferred
+    flag to tell the Interrupt Handler to save the next Command completed.
+  */
+  Command = DAC960_AllocateCommand(Controller);
+  if (Command == NULL)
+    {
+      Controller->UserCommandDeferred = true;
+      Controller->UserCommandSemaphore = &Semaphore;
+      DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+      down(&Semaphore);
+      DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+      Command = Controller->UserCommand;
+      Controller->UserCommand = NULL;
+    }
+  /*
+    Initialize the Command.
+  */
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  /*
+    Release exclusive access to Controller.
+  */
+ Done:
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+  return Command;
+}
+
+
+/*
+  DAC960_DeallocateUserCommand deallocates a User Command.
+*/
+
+static void DAC960_DeallocateUserCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Deallocate the Command.
+  */
+  DAC960_DeallocateCommand(Command);
+  /*
+    Release exclusive access to the User Command facility.
+  */
+  Controller->UserCommandActive = false;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_ParsePhysicalDrive parses spaces followed by a Physical Drive
+  Channel:TargetID specification from a User Command string.  It updates
+  Channel and TargetID and returns true on success and returns false otherwise.
+*/
+
+static boolean DAC960_ParsePhysicalDrive(DAC960_Controller_T *Controller,
+					 char *UserCommandString,
+					 unsigned char *Channel,
+					 unsigned char *TargetID)
+{
+  char *NewUserCommandString = UserCommandString;
+  unsigned long XChannel, XTargetID;
+  while (*UserCommandString == ' ') UserCommandString++;
+  if (UserCommandString == NewUserCommandString)
+    return false;
+  XChannel = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != ':' ||
+      XChannel >= Controller->Channels)
+    return false;
+  UserCommandString = ++NewUserCommandString;
+  XTargetID = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != '\0' ||
+      XTargetID >= DAC960_MaxTargets)
+    return false;
+  *Channel = XChannel;
+  *TargetID = XTargetID;
+  return true;
+}
+
+
+/*
+  DAC960_ParseLogicalDrive parses spaces followed by a Logical Drive Number
+  specification from a User Command string.  It updates LogicalDriveNumber and
+  returns true on success and returns false otherwise.
+*/
+
+static boolean DAC960_ParseLogicalDrive(DAC960_Controller_T *Controller,
+					char *UserCommandString,
+					unsigned char *LogicalDriveNumber)
+{
+  char *NewUserCommandString = UserCommandString;
+  unsigned long XLogicalDriveNumber;
+  while (*UserCommandString == ' ') UserCommandString++;
+  if (UserCommandString == NewUserCommandString)
+    return false;
+  XLogicalDriveNumber =
+    simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != '\0' ||
+      XLogicalDriveNumber >= Controller->LogicalDriveCount)
+    return false;
+  *LogicalDriveNumber = XLogicalDriveNumber;
+  return true;
+}
+
+
+/*
+  DAC960_SetDeviceState sets the Device State for a Physical Drive.
+*/
+
+static void DAC960_SetDeviceState(DAC960_Controller_T *Controller,
+				  DAC960_Command_T *Command,
+				  unsigned char Channel,
+				  unsigned char TargetID,
+				  DAC960_PhysicalDeviceState_T DeviceState,
+				  const char *DeviceStateString)
+{
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  CommandMailbox->Type3D.CommandOpcode = DAC960_StartDevice;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.DeviceState = DeviceState;
+  CommandMailbox->Type3D.Modifier = 0;
+  DAC960_ExecuteCommand(Command);
+  switch (Command->CommandStatus)
+    {
+    case DAC960_NormalCompletion:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Succeeded\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_UnableToStartDevice:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Unable to Start Device\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_NoDeviceAtAddress:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "No Device at Address\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_InvalidChannelOrTargetOrModifier:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Invalid Channel or Target or Modifier\n",
+			  Controller, DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_ChannelBusy:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Channel Busy\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    default:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Unexpected Status %04X\n", Controller,
+			  DeviceStateString, Channel, TargetID,
+			  Command->CommandStatus);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ExecuteUserCommand executes a User Command.
+*/
+
+static boolean DAC960_ExecuteUserCommand(DAC960_Controller_T *Controller,
+					 char *UserCommand)
+{
+  DAC960_Command_T *Command;
+  DAC960_CommandMailbox_T *CommandMailbox;
+  unsigned char Channel, TargetID, LogicalDriveNumber;
+  Command = DAC960_AllocateUserCommand(Controller);
+  if (Command == NULL) return false;
+  CommandMailbox = &Command->CommandMailbox;
+  Controller->UserStatusLength = 0;
+  if (strcmp(UserCommand, "flush-cache") == 0)
+    {
+      CommandMailbox->Type3.CommandOpcode = DAC960_Flush;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Cache Flush Completed\n", Controller);
+    }
+  else if (strncmp(UserCommand, "kill", 4) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[4],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState != DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Dead, "Kill");
+      else DAC960_UserCritical("Kill of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "make-online", 11) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[11],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState == DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Online, "Make Online");
+      else DAC960_UserCritical("Make Online of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+
+    }
+  else if (strncmp(UserCommand, "make-standby", 12) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[12],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState == DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Standby, "Make Standby");
+      else DAC960_UserCritical("Make Standby of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "rebuild", 7) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[7],
+				     &Channel, &TargetID))
+    {
+      CommandMailbox->Type3D.CommandOpcode = DAC960_RebuildAsync;
+      CommandMailbox->Type3D.Channel = Channel;
+      CommandMailbox->Type3D.TargetID = TargetID;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Initiated\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_AttemptToRebuildOnlineDrive:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Attempt to Rebuild Online or "
+			      "Unresponsive Drive\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_NewDiskFailedDuringRebuild:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "New Disk Failed During Rebuild\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_InvalidDeviceAddress:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Invalid Device Address\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_RebuildOrCheckAlreadyInProgress:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Rebuild or Consistency Check Already "
+			      "in Progress\n", Controller, Channel, TargetID);
+	  break;
+	default:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Unexpected Status %04X\n", Controller,
+			      Channel, TargetID, Command->CommandStatus);
+	  break;
+	}
+    }
+  else if (strncmp(UserCommand, "check-consistency", 17) == 0 &&
+	   DAC960_ParseLogicalDrive(Controller, &UserCommand[17],
+				    &LogicalDriveNumber))
+    {
+      CommandMailbox->Type3C.CommandOpcode = DAC960_CheckConsistencyAsync;
+      CommandMailbox->Type3C.LogicalDriveNumber = LogicalDriveNumber;
+      CommandMailbox->Type3C.AutoRestore = true;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Initiated\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_DependentDiskIsDead:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Dependent Physical Drive is DEAD\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_InvalidOrNonredundantLogicalDrive:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Invalid or Nonredundant Logical Drive\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_RebuildOrCheckAlreadyInProgress:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - Rebuild or "
+			      "Consistency Check Already in Progress\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	default:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Unexpected Status %04X\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber, Command->CommandStatus);
+	  break;
+	}
+    }
+  else if (strcmp(UserCommand, "cancel-rebuild") == 0 ||
+	   strcmp(UserCommand, "cancel-consistency-check") == 0)
+    {
+      unsigned char OldRebuildRateConstant;
+      CommandMailbox->Type3R.CommandOpcode = DAC960_RebuildControl;
+      CommandMailbox->Type3R.RebuildRateConstant = 0xFF;
+      CommandMailbox->Type3R.BusAddress =
+	Virtual_to_Bus(&OldRebuildRateConstant);
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Rebuild or Consistency Check Cancelled\n",
+			      Controller);
+	  break;
+	default:
+	  DAC960_UserCritical("Cancellation of Rebuild or "
+			      "Consistency Check Failed - "
+			      "Unexpected Status %04X\n",
+			      Controller, Command->CommandStatus);
+	  break;
+	}
+    }
+  else DAC960_UserCritical("Illegal User Command: '%s'\n",
+			   Controller, UserCommand);
+  DAC960_DeallocateUserCommand(Command);
+  return true;
+}
+
+
+/*
+  DAC960_ProcReadStatus implements reading /proc/rd/status.
+*/
+
+static int DAC960_ProcReadStatus(Inode_T *Inode, File_T *File,
+				 char *Buffer, int Count)
+{
+  char *StatusMessage = "OK\n";
+  int StatusMessageOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      DAC960_Enquiry_T *Enquiry;
+      if (Controller == NULL) continue;
+      Enquiry = &Controller->Enquiry[Controller->EnquiryIndex];
+      if (Enquiry->CriticalLogicalDriveCount > 0 ||
+	  Enquiry->OfflineLogicalDriveCount > 0 ||
+	  Enquiry->DeadDriveCount > 0)
+	{
+	  StatusMessage = "ALERT\n";
+	  break;
+	}
+    }
+  StatusMessageOffset = File->f_pos;
+  BytesAvailable = strlen(StatusMessage) - StatusMessageOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer, &StatusMessage[StatusMessageOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadInitialStatus implements reading /proc/rd/cN/initial_status.
+*/
+
+static int DAC960_ProcReadInitialStatus(Inode_T *Inode, File_T *File,
+					char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  int InitialStatusOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->InitialStatusProcEntry.low_ino == Inode->i_ino) break;
+    }
+  InitialStatusOffset = File->f_pos;
+  BytesAvailable = Controller->InitialStatusLength - InitialStatusOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer,
+	      &Controller->InitialStatusBuffer[InitialStatusOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadCurrentStatus implements reading /proc/rd/cN/current_status.
+*/
+
+static int DAC960_ProcReadCurrentStatus(Inode_T *Inode, File_T *File,
+					char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  int CurrentStatusOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->CurrentStatusProcEntry.low_ino == Inode->i_ino) break;
+    }
+  Controller->CurrentStatusLength = 0;
+  DAC960_AnnounceDriver(Controller);
+  DAC960_ReportControllerConfiguration(Controller);
+  Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+  Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+  if (Controller->RebuildProgressLength > 0)
+    {
+      strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength],
+	     Controller->RebuildProgressBuffer);
+      Controller->CurrentStatusLength += Controller->RebuildProgressLength;
+    }
+  else
+    {
+      char *StatusMessage = "No Rebuild or Consistency Check in Progress\n";
+      strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength],
+	     StatusMessage);
+      Controller->CurrentStatusLength += strlen(StatusMessage);
+    }
+  CurrentStatusOffset = File->f_pos;
+  BytesAvailable = Controller->CurrentStatusLength - CurrentStatusOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer,
+	      &Controller->CurrentStatusBuffer[CurrentStatusOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadUserCommand implements reading /proc/rd/cN/user_command.
+*/
+
+static int DAC960_ProcReadUserCommand(Inode_T *Inode, File_T *File,
+				      char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  int UserStatusOffset, BytesAvailable;
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->UserCommandProcEntry.low_ino == Inode->i_ino) break;
+    }
+  UserStatusOffset = File->f_pos;
+  BytesAvailable = Controller->UserStatusLength - UserStatusOffset;
+  if (Count > BytesAvailable) Count = BytesAvailable;
+  if (Count <= 0) return 0;
+  memcpy_tofs(Buffer, &Controller->UserStatusBuffer[UserStatusOffset], Count);
+  File->f_pos += Count;
+  return Count;
+}
+
+
+/*
+  DAC960_ProcWriteUserCommand implements writing /proc/rd/cN/user_command.
+*/
+
+static int DAC960_ProcWriteUserCommand(Inode_T *Inode, File_T *File,
+				       const char *Buffer, int Count)
+{
+  DAC960_Controller_T *Controller = NULL;
+  char CommandBuffer[80];
+  int ControllerNumber, Length;
+  if (Count > sizeof(CommandBuffer)-1) return -EINVAL;
+  memcpy_fromfs(CommandBuffer, Buffer, Count);
+  CommandBuffer[Count] = '\0';
+  Length = strlen(CommandBuffer);
+  if (CommandBuffer[Length-1] == '\n')
+    CommandBuffer[--Length] = '\0';
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->UserCommandProcEntry.low_ino == Inode->i_ino) break;
+    }
+  return (DAC960_ExecuteUserCommand(Controller, CommandBuffer)
+	  ? Count : -EBUSY);
+}
+
+
+/*
+  DAC960_CreateProcEntries creates the /proc/rd/... entries for the DAC960
+  Driver.
+*/
+
+static void DAC960_CreateProcEntries(void)
+{
+  static PROC_DirectoryEntry_T StatusProcEntry;
+  static FileOperations_T
+    StatusFileOperations = { read: DAC960_ProcReadStatus };
+  static InodeOperations_T
+    StatusInodeOperations = { &StatusFileOperations };
+  static FileOperations_T
+    InitialStatusFileOperations = { read: DAC960_ProcReadInitialStatus };
+  static InodeOperations_T
+    InitialStatusInodeOperations = { &InitialStatusFileOperations };
+  static FileOperations_T
+    CurrentStatusFileOperations = { read: DAC960_ProcReadCurrentStatus };
+  static InodeOperations_T
+    CurrentStatusInodeOperations = { &CurrentStatusFileOperations };
+  static FileOperations_T
+    UserCommandFileOperations = { read: DAC960_ProcReadUserCommand,
+				  write: DAC960_ProcWriteUserCommand };
+  static InodeOperations_T
+    UserCommandInodeOperations = { &UserCommandFileOperations };
+  int ControllerNumber;
+  DAC960_ProcDirectoryEntry.name = "rd";
+  DAC960_ProcDirectoryEntry.namelen = strlen(DAC960_ProcDirectoryEntry.name);
+  DAC960_ProcDirectoryEntry.mode = S_IFDIR | S_IRUGO | S_IXUGO;
+  DAC960_ProcDirectoryEntry.ops = &proc_dir_inode_operations;
+  proc_register_dynamic(&proc_root, &DAC960_ProcDirectoryEntry);
+  StatusProcEntry.name = "status";
+  StatusProcEntry.namelen = strlen(StatusProcEntry.name);
+  StatusProcEntry.mode = S_IFREG | S_IRUGO;
+  StatusProcEntry.ops = &StatusInodeOperations;
+  proc_register_dynamic(&DAC960_ProcDirectoryEntry, &StatusProcEntry);
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      PROC_DirectoryEntry_T *ControllerProcEntry, *InitialStatusProcEntry;
+      PROC_DirectoryEntry_T *CurrentStatusProcEntry, *UserCommandProcEntry;
+      if (Controller == NULL) continue;
+      ControllerProcEntry = &Controller->ControllerProcEntry;
+      ControllerProcEntry->name = Controller->ControllerName;
+      ControllerProcEntry->namelen = strlen(ControllerProcEntry->name);
+      ControllerProcEntry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+      ControllerProcEntry->ops = &proc_dir_inode_operations;
+      proc_register_dynamic(&DAC960_ProcDirectoryEntry, ControllerProcEntry);
+      InitialStatusProcEntry = &Controller->InitialStatusProcEntry;
+      InitialStatusProcEntry->name = "initial_status";
+      InitialStatusProcEntry->namelen = strlen(InitialStatusProcEntry->name);
+      InitialStatusProcEntry->mode = S_IFREG | S_IRUGO;
+      InitialStatusProcEntry->ops = &InitialStatusInodeOperations;
+      proc_register_dynamic(ControllerProcEntry, InitialStatusProcEntry);
+      CurrentStatusProcEntry = &Controller->CurrentStatusProcEntry;
+      CurrentStatusProcEntry->name = "current_status";
+      CurrentStatusProcEntry->namelen = strlen(CurrentStatusProcEntry->name);
+      CurrentStatusProcEntry->mode = S_IFREG | S_IRUGO;
+      CurrentStatusProcEntry->ops = &CurrentStatusInodeOperations;
+      proc_register_dynamic(ControllerProcEntry, CurrentStatusProcEntry);
+      UserCommandProcEntry = &Controller->UserCommandProcEntry;
+      UserCommandProcEntry->name = "user_command";
+      UserCommandProcEntry->namelen = strlen(UserCommandProcEntry->name);
+      UserCommandProcEntry->mode = S_IFREG | S_IWUSR | S_IRUSR;
+      UserCommandProcEntry->ops = &UserCommandInodeOperations;
+      proc_register_dynamic(ControllerProcEntry, UserCommandProcEntry);
+    }
+}
+
+
+/*
+  DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the DAC960
+  Driver.
+*/
+
+static void DAC960_DestroyProcEntries(void)
+{
+  proc_unregister(&proc_root, DAC960_ProcDirectoryEntry.low_ino);
+}
diff -urN linux-2.0.37-pre8/linux/drivers/block/DAC960.h linux-2.0.37-pre9/linux/drivers/block/DAC960.h
--- linux-2.0.37-pre8/linux/drivers/block/DAC960.h	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.0.37-pre9/linux/drivers/block/DAC960.h	2003-08-15 15:04:42.000000000 -0700
@@ -0,0 +1,2071 @@
+/*
+
+  Linux Driver for Mylex DAC960 and DAC1100 PCI RAID Controllers
+
+  Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+*/
+
+
+/*
+  DAC960_DriverVersion protects the private portion of this file.
+*/
+
+#ifdef DAC960_DriverVersion
+
+
+/*
+  Define the maximum number of DAC960 Controllers supported by this driver.
+*/
+
+#define DAC960_MaxControllers			8
+
+
+/*
+  Define the maximum number of Controller Channels supported by this driver.
+*/
+
+#define DAC960_MaxChannels			3
+
+
+/*
+  Define the maximum number of Targets per Channel supported by this driver.
+*/
+
+#define DAC960_MaxTargets			16
+
+
+/*
+  Define the maximum number of Logical Drives supported by any DAC960 model.
+*/
+
+#define DAC960_MaxLogicalDrives			32
+
+
+/*
+  Define the maximum number of Partitions allowed for each Logical Drive.
+*/
+
+#define DAC960_MaxPartitions			8
+#define DAC960_MaxPartitionsBits		3
+
+
+/*
+  Define the maximum Driver Queue Depth and Controller Queue Depth supported
+  by any DAC960 model.
+*/
+
+#define DAC960_MaxDriverQueueDepth		127
+#define DAC960_MaxControllerQueueDepth		128
+
+
+/*
+  Define the maximum number of Scatter/Gather Segments supported by any
+  DAC960 model.
+*/
+
+#define DAC960_MaxScatterGatherSegments		33
+
+
+/*
+  Define the DAC960 Controller Monitoring Timer Interval.
+*/
+
+#define DAC960_MonitoringTimerInterval		(10 * HZ)
+
+
+/*
+  Define the DAC960 Controller Secondary Monitoring Interval.
+*/
+
+#define DAC960_SecondaryMonitoringInterval	(60 * HZ)
+
+
+/*
+  Define the DAC960 Controller Progress Reporting Interval.
+*/
+
+#define DAC960_ProgressReportingInterval	(60 * HZ)
+
+
+/*
+  Define the number of Command Mailboxes and Status Mailboxes used by the
+  Memory Mailbox Interface.
+*/
+
+#define DAC960_CommandMailboxCount		256
+#define DAC960_StatusMailboxCount		1024
+
+
+/*
+  Define macros to extract the Controller Number, Logical Drive Number, and
+  Partition Number from a Kernel Device, and to construct a Major Number, Minor
+  Number, and Kernel Device from the Controller Number, Logical Drive Number,
+  and Partition Number.  There is one Major Number assigned to each Controller.
+  The associated Minor Number is divided into the Logical Drive Number and
+  Partition Number.
+*/
+
+#define DAC960_ControllerNumber(Device) \
+  (MAJOR(Device) - DAC960_MAJOR)
+
+#define DAC960_LogicalDriveNumber(Device) \
+  (MINOR(Device) >> DAC960_MaxPartitionsBits)
+
+#define DAC960_PartitionNumber(Device) \
+  (MINOR(Device) & (DAC960_MaxPartitions - 1))
+
+#define DAC960_MajorNumber(ControllerNumber) \
+  (DAC960_MAJOR + (ControllerNumber))
+
+#define DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber) \
+   (((LogicalDriveNumber) << DAC960_MaxPartitionsBits) | (PartitionNumber))
+
+#define DAC960_MinorCount			(DAC960_MaxLogicalDrives \
+						 * DAC960_MaxPartitions)
+
+#define DAC960_KernelDevice(ControllerNumber,				       \
+			    LogicalDriveNumber,				       \
+			    PartitionNumber)				       \
+   MKDEV(DAC960_MajorNumber(ControllerNumber),				       \
+	 DAC960_MinorNumber(LogicalDriveNumber, PartitionNumber))
+
+
+/*
+  Define the DAC960 Controller fixed Block Size and Block Size Bits.
+*/
+
+#define DAC960_BlockSize			512
+#define DAC960_BlockSizeBits			9
+
+
+/*
+  Define the Controller Line, Status Buffer, Rebuild Progress, and
+  User Message Sizes.
+*/
+
+#define DAC960_LineBufferSize			100
+#define DAC960_StatusBufferSize			5000
+#define DAC960_RebuildProgressSize		200
+#define DAC960_UserMessageSize			200
+
+
+/*
+  Define the Driver Message Levels.
+*/
+
+typedef enum DAC960_MessageLevel
+{
+  DAC960_AnnounceLevel =			0,
+  DAC960_InfoLevel =				1,
+  DAC960_NoticeLevel =				2,
+  DAC960_WarningLevel =				3,
+  DAC960_ErrorLevel =				4,
+  DAC960_ProgressLevel =			5,
+  DAC960_CriticalLevel =			6,
+  DAC960_UserCriticalLevel =			7
+}
+DAC960_MessageLevel_T;
+
+static char
+  *DAC960_MessageLevelMap[] =
+    { KERN_NOTICE, KERN_NOTICE, KERN_NOTICE, KERN_WARNING,
+      KERN_ERR, KERN_CRIT, KERN_CRIT, KERN_CRIT };
+
+
+/*
+  Define Driver Message macros.
+*/
+
+#define DAC960_Announce(Format, Arguments...) \
+  DAC960_Message(DAC960_AnnounceLevel, Format, ##Arguments)
+
+#define DAC960_Info(Format, Arguments...) \
+  DAC960_Message(DAC960_InfoLevel, Format, ##Arguments)
+
+#define DAC960_Notice(Format, Arguments...) \
+  DAC960_Message(DAC960_NoticeLevel, Format, ##Arguments)
+
+#define DAC960_Warning(Format, Arguments...) \
+  DAC960_Message(DAC960_WarningLevel, Format, ##Arguments)
+
+#define DAC960_Error(Format, Arguments...) \
+  DAC960_Message(DAC960_ErrorLevel, Format, ##Arguments)
+
+#define DAC960_Progress(Format, Arguments...) \
+  DAC960_Message(DAC960_ProgressLevel, Format, ##Arguments)
+
+#define DAC960_Critical(Format, Arguments...) \
+  DAC960_Message(DAC960_CriticalLevel, Format, ##Arguments)
+
+#define DAC960_UserCritical(Format, Arguments...) \
+  DAC960_Message(DAC960_UserCriticalLevel, Format, ##Arguments)
+
+
+/*
+  Define the types of DAC960 Controllers that are supported.
+*/
+
+typedef enum
+{
+  DAC960_V5_Controller =			1,	/* DAC1164P */
+  DAC960_V4_Controller =			2,	/* DAC960PTL/PJ/PG */
+  DAC960_V3_Controller =			3	/* DAC960PU/PD/PL */
+}
+DAC960_ControllerType_T;
+
+
+/*
+  Define a Boolean data type.
+*/
+
+typedef enum { false, true } __attribute__ ((packed)) boolean;
+
+
+/*
+  Define a 32 bit I/O Address data type.
+*/
+
+typedef unsigned int DAC960_IO_Address_T;
+
+
+/*
+  Define a 32 bit PCI Bus Address data type.
+*/
+
+typedef unsigned int DAC960_PCI_Address_T;
+
+
+/*
+  Define a 32 bit Bus Address data type.
+*/
+
+typedef unsigned int DAC960_BusAddress_T;
+
+
+/*
+  Define a 32 bit Byte Count data type.
+*/
+
+typedef unsigned int DAC960_ByteCount_T;
+
+
+/*
+  Define types for some of the structures that interface with the rest
+  of the Linux Kernel and I/O Subsystem.
+*/
+
+typedef struct buffer_head BufferHeader_T;
+typedef struct file File_T;
+typedef struct file_operations FileOperations_T;
+typedef struct gendisk GenericDiskInfo_T;
+typedef struct hd_geometry DiskGeometry_T;
+typedef struct hd_struct DiskPartition_T;
+typedef struct inode Inode_T;
+typedef struct inode_operations InodeOperations_T;
+typedef kdev_t KernelDevice_T;
+typedef struct proc_dir_entry PROC_DirectoryEntry_T;
+typedef unsigned long ProcessorFlags_T;
+typedef struct pt_regs Registers_T;
+typedef struct request IO_Request_T;
+typedef struct semaphore Semaphore_T;
+typedef struct timer_list Timer_T;
+typedef struct wait_queue WaitQueue_T;
+
+
+/*
+  Define the DAC960 V5 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V5_RegisterWindowSize		0x80
+
+typedef enum
+{
+  DAC960_V5_InboundDoorBellRegisterOffset =	0x60,
+  DAC960_V5_OutboundDoorBellRegisterOffset =	0x61,
+  DAC960_V5_InterruptMaskRegisterOffset =	0x34,
+  DAC960_V5_CommandOpcodeRegisterOffset =	0x50,
+  DAC960_V5_CommandIdentifierRegisterOffset =	0x51,
+  DAC960_V5_MailboxRegister2Offset =		0x52,
+  DAC960_V5_MailboxRegister3Offset =		0x53,
+  DAC960_V5_MailboxRegister4Offset =		0x54,
+  DAC960_V5_MailboxRegister5Offset =		0x55,
+  DAC960_V5_MailboxRegister6Offset =		0x56,
+  DAC960_V5_MailboxRegister7Offset =		0x57,
+  DAC960_V5_MailboxRegister8Offset =		0x58,
+  DAC960_V5_MailboxRegister9Offset =		0x59,
+  DAC960_V5_MailboxRegister10Offset =		0x5A,
+  DAC960_V5_MailboxRegister11Offset =		0x5B,
+  DAC960_V5_MailboxRegister12Offset =		0x5C,
+  DAC960_V5_StatusCommandIdentifierRegOffset =	0x5D,
+  DAC960_V5_StatusRegisterOffset =		0x5E
+}
+DAC960_V5_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V5 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V5_InboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean HardwareMailboxNewCommand:1;		/* Bit 0 */
+    boolean AcknowledgeHardwareMailboxStatus:1;		/* Bit 1 */
+    boolean GenerateInterrupt:1;			/* Bit 2 */
+    boolean ControllerReset:1;				/* Bit 3 */
+    boolean MemoryMailboxNewCommand:1;			/* Bit 4 */
+    unsigned char :3;					/* Bits 5-7 */
+  } Write;
+  struct {
+    boolean HardwareMailboxEmpty:1;			/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V5_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V5 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V5_OutboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean AcknowledgeHardwareMailboxInterrupt:1;	/* Bit 0 */
+    boolean AcknowledgeMemoryMailboxInterrupt:1;	/* Bit 1 */
+    unsigned char :6;					/* Bits 2-7 */
+  } Write;
+  struct {
+    boolean HardwareMailboxStatusAvailable:1;		/* Bit 0 */
+    boolean MemoryMailboxStatusAvailable:1;		/* Bit 1 */
+    unsigned char :6;					/* Bits 2-7 */
+  } Read;
+}
+DAC960_V5_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V5 Interrupt Mask Register.
+*/
+
+typedef union DAC960_V5_InterruptMaskRegister
+{
+  unsigned char All;
+  struct {
+    unsigned char :2;					/* Bits 0-1 */
+    boolean DisableInterrupts:1;			/* Bit 2 */
+    unsigned char :5;					/* Bits 3-7 */
+  } Bits;
+}
+DAC960_V5_InterruptMaskRegister_T;
+
+
+/*
+  Define the DAC960 V4 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V4_RegisterWindowSize		0x2000
+
+typedef enum
+{
+  DAC960_V4_InboundDoorBellRegisterOffset =	0x0020,
+  DAC960_V4_OutboundDoorBellRegisterOffset =	0x002C,
+  DAC960_V4_InterruptMaskRegisterOffset =	0x0034,
+  DAC960_V4_CommandOpcodeRegisterOffset =	0x1000,
+  DAC960_V4_CommandIdentifierRegisterOffset =	0x1001,
+  DAC960_V4_MailboxRegister2Offset =		0x1002,
+  DAC960_V4_MailboxRegister3Offset =		0x1003,
+  DAC960_V4_MailboxRegister4Offset =		0x1004,
+  DAC960_V4_MailboxRegister5Offset =		0x1005,
+  DAC960_V4_MailboxRegister6Offset =		0x1006,
+  DAC960_V4_MailboxRegister7Offset =		0x1007,
+  DAC960_V4_MailboxRegister8Offset =		0x1008,
+  DAC960_V4_MailboxRegister9Offset =		0x1009,
+  DAC960_V4_MailboxRegister10Offset =		0x100A,
+  DAC960_V4_MailboxRegister11Offset =		0x100B,
+  DAC960_V4_MailboxRegister12Offset =		0x100C,
+  DAC960_V4_StatusCommandIdentifierRegOffset =	0x1018,
+  DAC960_V4_StatusRegisterOffset =		0x101A
+}
+DAC960_V4_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V4_InboundDoorBellRegister
+{
+  unsigned int All;
+  struct {
+    boolean HardwareMailboxNewCommand:1;		/* Bit 0 */
+    boolean AcknowledgeHardwareMailboxStatus:1;		/* Bit 1 */
+    boolean GenerateInterrupt:1;			/* Bit 2 */
+    boolean ControllerReset:1;				/* Bit 3 */
+    boolean MemoryMailboxNewCommand:1;			/* Bit 4 */
+    unsigned int :27;					/* Bits 5-31 */
+  } Write;
+  struct {
+    boolean HardwareMailboxFull:1;			/* Bit 0 */
+    unsigned int :31;					/* Bits 1-31 */
+  } Read;
+}
+DAC960_V4_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V4_OutboundDoorBellRegister
+{
+  unsigned int All;
+  struct {
+    boolean AcknowledgeHardwareMailboxInterrupt:1;	/* Bit 0 */
+    boolean AcknowledgeMemoryMailboxInterrupt:1;	/* Bit 1 */
+    unsigned int :30;					/* Bits 2-31 */
+  } Write;
+  struct {
+    boolean HardwareMailboxStatusAvailable:1;		/* Bit 0 */
+    boolean MemoryMailboxStatusAvailable:1;		/* Bit 1 */
+    unsigned int :30;					/* Bits 2-31 */
+  } Read;
+}
+DAC960_V4_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V4 Interrupt Mask Register.
+*/
+
+typedef union DAC960_V4_InterruptMaskRegister
+{
+  unsigned int All;
+  struct {
+    unsigned int MessageUnitInterruptMask1:2;		/* Bits 0-1 */
+    boolean DisableInterrupts:1;			/* Bit 2 */
+    unsigned int MessageUnitInterruptMask2:5;		/* Bits 3-7 */
+    unsigned int Reserved0:24;				/* Bits 8-31 */
+  } Bits;
+}
+DAC960_V4_InterruptMaskRegister_T;
+
+
+/*
+  Define the DAC960 V3 Controller Interface Register Offsets.
+*/
+
+#define DAC960_V3_RegisterWindowSize		0x80
+
+typedef enum
+{
+  DAC960_V3_CommandOpcodeRegisterOffset =	0x00,
+  DAC960_V3_CommandIdentifierRegisterOffset =	0x01,
+  DAC960_V3_MailboxRegister2Offset =		0x02,
+  DAC960_V3_MailboxRegister3Offset =		0x03,
+  DAC960_V3_MailboxRegister4Offset =		0x04,
+  DAC960_V3_MailboxRegister5Offset =		0x05,
+  DAC960_V3_MailboxRegister6Offset =		0x06,
+  DAC960_V3_MailboxRegister7Offset =		0x07,
+  DAC960_V3_MailboxRegister8Offset =		0x08,
+  DAC960_V3_MailboxRegister9Offset =		0x09,
+  DAC960_V3_MailboxRegister10Offset =		0x0A,
+  DAC960_V3_MailboxRegister11Offset =		0x0B,
+  DAC960_V3_MailboxRegister12Offset =		0x0C,
+  DAC960_V3_StatusCommandIdentifierRegOffset =	0x0D,
+  DAC960_V3_StatusRegisterOffset =		0x0E,
+  DAC960_V3_InboundDoorBellRegisterOffset =	0x40,
+  DAC960_V3_OutboundDoorBellRegisterOffset =	0x41,
+  DAC960_V3_InterruptEnableRegisterOffset =	0x43
+}
+DAC960_V3_RegisterOffsets_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Inbound Door Bell Register.
+*/
+
+typedef union DAC960_V3_InboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean NewCommand:1;				/* Bit 0 */
+    boolean AcknowledgeStatus:1;			/* Bit 1 */
+    boolean GenerateInterrupt:1;			/* Bit 2 */
+    boolean ControllerReset:1;				/* Bit 3 */
+    unsigned char :4;					/* Bits 4-7 */
+  } Write;
+  struct {
+    boolean MailboxFull:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V3_InboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Outbound Door Bell Register.
+*/
+
+typedef union DAC960_V3_OutboundDoorBellRegister
+{
+  unsigned char All;
+  struct {
+    boolean AcknowledgeInterrupt:1;			/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Write;
+  struct {
+    boolean StatusAvailable:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Read;
+}
+DAC960_V3_OutboundDoorBellRegister_T;
+
+
+/*
+  Define the structure of the DAC960 V3 Interrupt Enable Register.
+*/
+
+typedef union DAC960_V3_InterruptEnableRegister
+{
+  unsigned char All;
+  struct {
+    boolean EnableInterrupts:1;				/* Bit 0 */
+    unsigned char :7;					/* Bits 1-7 */
+  } Bits;
+}
+DAC960_V3_InterruptEnableRegister_T;
+
+
+/*
+  Define the DAC960 Command Identifier type.
+*/
+
+typedef unsigned char DAC960_CommandIdentifier_T;
+
+
+/*
+  Define the DAC960 Command Opcodes.
+*/
+
+typedef enum
+{
+  /* I/O Commands */
+  DAC960_ReadExtended =				0x33,
+  DAC960_WriteExtended =			0x34,
+  DAC960_ReadAheadExtended =			0x35,
+  DAC960_ReadExtendedWithScatterGather =	0xB3,
+  DAC960_WriteExtendedWithScatterGather =	0xB4,
+  DAC960_Read =					0x36,
+  DAC960_ReadWithOldScatterGather =		0xB6,
+  DAC960_Write =				0x37,
+  DAC960_WriteWithOldScatterGather =		0xB7,
+  DAC960_DCDB =					0x04,
+  DAC960_DCDBWithScatterGather =		0x84,
+  DAC960_Flush =				0x0A,
+  /* Controller Status Related Commands */
+  DAC960_Enquiry =				0x53,
+  DAC960_Enquiry2 =				0x1C,
+  DAC960_GetLogicalDriveElement =		0x55,
+  DAC960_GetLogicalDriveInformation =		0x19,
+  DAC960_IOPortRead =				0x39,
+  DAC960_IOPortWrite =				0x3A,
+  DAC960_GetSDStats =				0x3E,
+  DAC960_GetPDStats =				0x3F,
+  DAC960_PerformEventLogOperation =		0x72,
+  /* Device Related Commands */
+  DAC960_StartDevice =				0x10,
+  DAC960_GetDeviceState =			0x50,
+  DAC960_StopChannel =				0x13,
+  DAC960_StartChannel =				0x12,
+  DAC960_ResetChannel =				0x1A,
+  /* Commands Associated with Data Consistency and Errors */
+  DAC960_Rebuild =				0x09,
+  DAC960_RebuildAsync =				0x16,
+  DAC960_CheckConsistency =			0x0F,
+  DAC960_CheckConsistencyAsync =		0x1E,
+  DAC960_RebuildStat =				0x0C,
+  DAC960_GetRebuildProgress =			0x27,
+  DAC960_RebuildControl =			0x1F,
+  DAC960_ReadBadBlockTable =			0x0B,
+  DAC960_ReadBadDataTable =			0x25,
+  DAC960_ClearBadDataTable =			0x26,
+  DAC960_GetErrorTable =			0x17,
+  DAC960_AddCapacityAsync =			0x2A,
+  /* Configuration Related Commands */
+  DAC960_ReadConfig2 =				0x3D,
+  DAC960_WriteConfig2 =				0x3C,
+  DAC960_ReadConfigurationOnDisk =		0x4A,
+  DAC960_WriteConfigurationOnDisk =		0x4B,
+  DAC960_ReadConfiguration =			0x4E,
+  DAC960_ReadBackupConfiguration =		0x4D,
+  DAC960_WriteConfiguration =			0x4F,
+  DAC960_AddConfiguration =			0x4C,
+  DAC960_ReadConfigurationLabel =		0x48,
+  DAC960_WriteConfigurationLabel =		0x49,
+  /* Firmware Upgrade Related Commands */
+  DAC960_LoadImage =				0x20,
+  DAC960_StoreImage =				0x21,
+  DAC960_ProgramImage =				0x22,
+  /* Diagnostic Commands */
+  DAC960_SetDiagnosticMode =			0x31,
+  DAC960_RunDiagnostic =			0x32,
+  /* Subsystem Service Commands */
+  DAC960_GetSubsystemData =			0x70,
+  DAC960_SetSubsystemParameters =		0x71
+}
+__attribute__ ((packed))
+DAC960_CommandOpcode_T;
+
+
+/*
+  Define the DAC960 Command Status Codes.
+*/
+
+#define DAC960_NormalCompletion			0x0000	/* Common */
+#define DAC960_CheckConditionReceived		0x0002	/* Common */
+#define DAC960_NoDeviceAtAddress		0x0102	/* Common */
+#define DAC960_InvalidDeviceAddress		0x0105	/* Common */
+#define DAC960_InvalidParameter			0x0105	/* Common */
+#define DAC960_IrrecoverableDataError		0x0001	/* I/O */
+#define DAC960_LogicalDriveNonexistentOrOffline	0x0002	/* I/O */
+#define DAC960_AccessBeyondEndOfLogicalDrive	0x0105	/* I/O */
+#define DAC960_BadDataEncountered		0x010C	/* I/O */
+#define DAC960_DeviceBusy			0x0008	/* DCDB */
+#define DAC960_DeviceNonresponsive		0x000E	/* DCDB */
+#define DAC960_CommandTerminatedAbnormally	0x000F	/* DCDB */
+#define DAC960_UnableToStartDevice		0x0002	/* Device */
+#define DAC960_InvalidChannelOrTargetOrModifier	0x0105	/* Device */
+#define DAC960_ChannelBusy			0x0106	/* Device */
+#define DAC960_ChannelNotStopped		0x0002	/* Device */
+#define DAC960_AttemptToRebuildOnlineDrive	0x0002	/* Consistency */
+#define DAC960_RebuildBadBlocksEncountered	0x0003	/* Consistency */
+#define DAC960_NewDiskFailedDuringRebuild	0x0004	/* Consistency */
+#define DAC960_RebuildOrCheckAlreadyInProgress	0x0106	/* Consistency */
+#define DAC960_DependentDiskIsDead		0x0002	/* Consistency */
+#define DAC960_InconsistentBlocksFound		0x0003	/* Consistency */
+#define DAC960_InvalidOrNonredundantLogicalDrive 0x0105	/* Consistency */
+#define DAC960_NoRebuildOrCheckInProgress	0x0105	/* Consistency */
+#define DAC960_RebuildInProgress_DataValid	0x0000	/* Consistency */
+#define DAC960_RebuildFailed_LogicalDriveFailure 0x0002	/* Consistency */
+#define DAC960_RebuildFailed_BadBlocksOnOther	0x0003	/* Consistency */
+#define DAC960_RebuildFailed_NewDriveFailed	0x0004	/* Consistency */
+#define DAC960_RebuildSuccessful		0x0100	/* Consistency */
+#define DAC960_AddCapacityInProgress		0x0004	/* Consistency */
+#define DAC960_AddCapacityFailedOrSuspended	0x00F4	/* Consistency */
+#define DAC960_Config2ChecksumError		0x0002	/* Configuration */
+#define DAC960_ConfigurationSuspended		0x0106	/* Configuration */
+#define DAC960_FailedToConfigureNVRAM		0x0105	/* Configuration */
+#define DAC960_ConfigurationNotSavedStateChange	0x0106	/* Configuration */
+#define DAC960_SubsystemNotInstalled		0x0001	/* Subsystem */
+#define DAC960_SubsystemFailed			0x0002	/* Subsystem */
+#define DAC960_SubsystemBusy			0x0106	/* Subsystem */
+
+typedef unsigned short DAC960_CommandStatus_T;
+
+
+/*
+  Define the Enquiry reply structure.
+*/
+
+typedef struct DAC960_Enquiry
+{
+  unsigned char NumberOfLogicalDrives;			/* Byte 0 */
+  unsigned int :24;					/* Bytes 1-3 */
+  unsigned int LogicalDriveSizes[32];			/* Bytes 4-131 */
+  unsigned short FlashAge;				/* Bytes 132-133 */
+  struct {
+    boolean DeferredWriteError:1;			/* Byte 134 Bit 0 */
+    boolean BatteryLow:1;				/* Byte 134 Bit 1 */
+    unsigned char :6;					/* Byte 134 Bits 2-7 */
+  } StatusFlags;
+  unsigned char :8;					/* Byte 135 */
+  unsigned char MinorFirmwareVersion;			/* Byte 136 */
+  unsigned char MajorFirmwareVersion;			/* Byte 137 */
+  enum {
+    DAC960_NoStandbyRebuildOrCheckInProgress =			0x00,
+    DAC960_StandbyRebuildInProgress =				0x01,
+    DAC960_BackgroundRebuildInProgress =			0x02,
+    DAC960_BackgroundCheckInProgress =				0x03,
+    DAC960_StandbyRebuildCompletedWithError =			0xFF,
+    DAC960_BackgroundRebuildOrCheckFailed_DriveFailed =		0xF0,
+    DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed =	0xF1,
+    DAC960_BackgroundRebuildOrCheckFailed_OtherCauses =		0xF2,
+    DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated =	0xF3
+  } __attribute__ ((packed)) RebuildFlag;		/* Byte 138 */
+  unsigned char MaxCommands;				/* Byte 139 */
+  unsigned char OfflineLogicalDriveCount;		/* Byte 140 */
+  unsigned char :8;					/* Byte 141 */
+  unsigned short EventLogSequenceNumber;		/* Bytes 142-143 */
+  unsigned char CriticalLogicalDriveCount;		/* Byte 144 */
+  unsigned int :24;					/* Bytes 145-147 */
+  unsigned char DeadDriveCount;				/* Byte 148 */
+  unsigned char :8;					/* Byte 149 */
+  unsigned char RebuildCount;				/* Byte 150 */
+  struct {
+    unsigned char :3;					/* Byte 151 Bits 0-2 */
+    boolean BatteryBackupUnitPresent:1;			/* Byte 151 Bit 3 */
+    unsigned char :3;					/* Byte 151 Bits 4-6 */
+    unsigned char :1;					/* Byte 151 Bit 7 */
+  } MiscFlags;
+  struct {
+    unsigned char TargetID;
+    unsigned char Channel;
+  } DeadDrives[21];					/* Bytes 152-194 */
+  unsigned char Reserved[62];				/* Bytes 195-255 */
+}
+__attribute__ ((packed))
+DAC960_Enquiry_T;
+
+
+/*
+  Define the Enquiry2 reply structure.
+*/
+
+typedef struct DAC960_Enquiry2
+{
+  struct {
+    enum {
+      DAC960_P_PD_PU =				0x01,
+      DAC960_PL =				0x02,
+      DAC960_PG =				0x10,
+      DAC960_PJ =				0x11,
+      DAC960_PR =				0x12,
+      DAC960_PT =				0x13,
+      DAC960_PTL0 =				0x14,
+      DAC960_PRL =				0x15,
+      DAC960_PTL1 =				0x16,
+      DAC1164_P =				0x20
+    } __attribute__ ((packed)) SubModel;		/* Byte 0 */
+    unsigned char ActualChannels;			/* Byte 1 */
+    enum {
+      DAC960_FiveChannelBoard =			0x01,
+      DAC960_ThreeChannelBoard =		0x02,
+      DAC960_TwoChannelBoard =			0x03,
+      DAC960_ThreeChannelASIC_DAC =		0x04
+    } __attribute__ ((packed)) Model;			/* Byte 2 */
+    enum {
+      DAC960_EISA_Controller =			0x01,
+      DAC960_MicroChannel_Controller =		0x02,
+      DAC960_PCI_Controller =			0x03,
+      DAC960_SCSItoSCSI_Controller =		0x08
+    } __attribute__ ((packed)) ProductFamily;		/* Byte 3 */
+  } HardwareID;						/* Bytes 0-3 */
+  /* MajorVersion.MinorVersion-FirmwareType-TurnID */
+  struct {
+    unsigned char MajorVersion;				/* Byte 4 */
+    unsigned char MinorVersion;				/* Byte 5 */
+    unsigned char TurnID;				/* Byte 6 */
+    char FirmwareType;					/* Byte 7 */
+  } FirmwareID;						/* Bytes 4-7 */
+  unsigned char :8;					/* Byte 8 */
+  unsigned int :24;					/* Bytes 9-11 */
+  unsigned char ConfiguredChannels;			/* Byte 12 */
+  unsigned char ActualChannels;				/* Byte 13 */
+  unsigned char MaxTargets;				/* Byte 14 */
+  unsigned char MaxTags;				/* Byte 15 */
+  unsigned char MaxLogicalDrives;			/* Byte 16 */
+  unsigned char MaxArms;				/* Byte 17 */
+  unsigned char MaxSpans;				/* Byte 18 */
+  unsigned char :8;					/* Byte 19 */
+  unsigned int :32;					/* Bytes 20-23 */
+  unsigned int MemorySize;				/* Bytes 24-27 */
+  unsigned int CacheSize;				/* Bytes 28-31 */
+  unsigned int FlashMemorySize;				/* Bytes 32-35 */
+  unsigned int NonVolatileMemorySize;			/* Bytes 36-39 */
+  struct {
+    enum {
+      DAC960_DRAM =				0x00,
+      DAC960_EDO =				0x01,
+      DAC960_SDRAM =				0x02
+    } __attribute__ ((packed)) RamType:3;		/* Byte 40 Bits 0-2 */
+    enum {
+      DAC960_None =				0x00,
+      DAC960_Parity =				0x01,
+      DAC960_ECC =				0x02
+    } __attribute__ ((packed)) ErrorCorrection:3;	/* Byte 40 Bits 3-5 */
+    boolean FastPageMode:1;				/* Byte 40 Bit 6 */
+    boolean LowPowerMemory:1;				/* Byte 40 Bit 7 */
+    unsigned char :8;					/* Bytes 41 */
+  } MemoryType;
+  unsigned short ClockSpeed;				/* Bytes 42-43 */
+  unsigned short MemorySpeed;				/* Bytes 44-45 */
+  unsigned short HardwareSpeed;				/* Bytes 46-47 */
+  unsigned int :32;					/* Bytes 48-51 */
+  unsigned int :32;					/* Bytes 52-55 */
+  unsigned char :8;					/* Byte 56 */
+  unsigned char :8;					/* Byte 57 */
+  unsigned short :16;					/* Bytes 58-59 */
+  unsigned short MaxCommands;				/* Bytes 60-61 */
+  unsigned short MaxScatterGatherEntries;		/* Bytes 62-63 */
+  unsigned short MaxDriveCommands;			/* Bytes 64-65 */
+  unsigned short MaxIODescriptors;			/* Bytes 66-67 */
+  unsigned short MaxCombinedSectors;			/* Bytes 68-69 */
+  unsigned char Latency;				/* Byte 70 */
+  unsigned char :8;					/* Byte 71 */
+  unsigned char SCSITimeout;				/* Byte 72 */
+  unsigned char :8;					/* Byte 73 */
+  unsigned short MinFreeLines;				/* Bytes 74-75 */
+  unsigned int :32;					/* Bytes 76-79 */
+  unsigned int :32;					/* Bytes 80-83 */
+  unsigned char RebuildRateConstant;			/* Byte 84 */
+  unsigned char :8;					/* Byte 85 */
+  unsigned char :8;					/* Byte 86 */
+  unsigned char :8;					/* Byte 87 */
+  unsigned int :32;					/* Bytes 88-91 */
+  unsigned int :32;					/* Bytes 92-95 */
+  unsigned short PhysicalDriveBlockSize;		/* Bytes 96-97 */
+  unsigned short LogicalDriveBlockSize;			/* Bytes 98-99 */
+  unsigned short MaxBlocksPerCommand;			/* Bytes 100-101 */
+  unsigned short BlockFactor;				/* Bytes 102-103 */
+  unsigned short CacheLineSize;				/* Bytes 104-105 */
+  struct {
+    enum {
+      DAC960_Narrow_8bit =			0x00,
+      DAC960_Wide_16bit =			0x01,
+      DAC960_Wide_32bit =			0x02
+    } __attribute__ ((packed)) BusWidth:2;		/* Byte 106 Bits 0-1 */
+    enum {
+      DAC960_Fast =				0x00,
+      DAC960_Ultra =				0x01,
+      DAC960_Ultra2 =				0x02
+    } __attribute__ ((packed)) BusSpeed:2;		/* Byte 106 Bits 2-3 */
+    boolean Differential:1;				/* Byte 106 Bit 4 */
+    unsigned char :3;					/* Byte 106 Bits 5-7 */
+  } SCSICapability;
+  unsigned char :8;					/* Byte 107 */
+  unsigned int :32;					/* Bytes 108-111 */
+  unsigned short FirmwareBuildNumber;			/* Bytes 112-113 */
+  enum {
+    DAC960_AEMI =				0x01,
+    DAC960_OEM1 =				0x02,
+    DAC960_OEM2 =				0x04,
+    DAC960_OEM3 =				0x08,
+    DAC960_Conner =				0x10,
+    DAC960_SAFTE =				0x20
+  } __attribute__ ((packed)) FaultManagementType;	/* Byte 114 */
+  unsigned char :8;					/* Byte 115 */
+  struct {
+    boolean Clustering:1;				/* Byte 116 Bit 0 */
+    boolean MylexOnlineRAIDExpansion:1;			/* Byte 116 Bit 1 */
+    unsigned int :30;					/* Bytes 116-119 */
+  } FirmwareFeatures;
+  unsigned int :32;					/* Bytes 120-123 */
+  unsigned int :32;					/* Bytes 124-127 */
+}
+DAC960_Enquiry2_T;
+
+
+/*
+  Define the Logical Drive State type.
+*/
+
+typedef enum
+{
+    DAC960_LogicalDrive_Online =		0x03,
+    DAC960_LogicalDrive_Critical =		0x04,
+    DAC960_LogicalDrive_Offline =		0xFF
+}
+__attribute__ ((packed))
+DAC960_LogicalDriveState_T;
+
+
+/*
+  Define the Get Logical Drive Information reply structure.
+*/
+
+typedef struct DAC960_LogicalDriveInformation
+{
+  unsigned int LogicalDriveSize;			/* Bytes 0-3 */
+  DAC960_LogicalDriveState_T LogicalDriveState;		/* Byte 4 */
+  unsigned char RAIDLevel:7;				/* Byte 5 Bits 0-6 */
+  boolean WriteBack:1;					/* Byte 5 Bit 7 */
+  unsigned int :16;					/* Bytes 6-7 */
+}
+DAC960_LogicalDriveInformation_T;
+
+
+/*
+  Define the Perform Event Log Operation Types.
+*/
+
+typedef enum
+{
+  DAC960_GetEventLogEntry =			0x00
+}
+__attribute__ ((packed))
+DAC960_PerformEventLogOpType_T;
+
+
+/*
+  Define the Get Event Log Entry reply structure.
+*/
+
+typedef struct DAC960_EventLogEntry
+{
+  unsigned char MessageType;				/* Byte 0 */
+  unsigned char MessageLength;				/* Byte 1 */
+  unsigned char TargetID:5;				/* Byte 2 Bits 0-4 */
+  unsigned char Channel:3;				/* Byte 2 Bits 5-7 */
+  unsigned char LogicalUnit:6;				/* Byte 3 Bits 0-5 */
+  unsigned char :2;					/* Byte 3 Bits 6-7 */
+  unsigned short SequenceNumber;			/* Bytes 4-5 */
+  unsigned char ErrorCode:7;				/* Byte 6 Bits 0-6 */
+  boolean Valid:1;					/* Byte 6 Bit 7 */
+  unsigned char SegmentNumber;				/* Byte 7 */
+  unsigned char SenseKey:4;				/* Byte 8 Bits 0-3 */
+  unsigned char :1;					/* Byte 8 Bit 4 */
+  boolean ILI:1;					/* Byte 8 Bit 5 */
+  boolean EOM:1;					/* Byte 8 Bit 6 */
+  boolean Filemark:1;					/* Byte 8 Bit 7 */
+  unsigned char Information[4];				/* Bytes 9-12 */
+  unsigned char AdditionalSenseLength;			/* Byte 13 */
+  unsigned char CommandSpecificInformation[4];		/* Bytes 14-17 */
+  unsigned char AdditionalSenseCode;			/* Byte 18 */
+  unsigned char AdditionalSenseCodeQualifier;		/* Byte 19 */
+  unsigned char Dummy[12];				/* Bytes 20-31 */
+}
+DAC960_EventLogEntry_T;
+
+#define DAC960_EventMessagesCount		13
+
+static char
+  *DAC960_EventMessages[DAC960_EventMessagesCount] =
+     { "killed because write recovery failed",
+       "killed because of SCSI bus reset failure",
+       "killed because of double check condition",
+       "killed because it was removed",
+       "killed because of gross error on SCSI chip",
+       "killed because of bad tag returned from drive",
+       "killed because of timeout on SCSI command",
+       "killed because of reset SCSI command issued from system",
+       "killed because busy or parity error count exceeded limit",
+       "killed because of 'kill drive' command from system",
+       "killed because of selection timeout",
+       "killed due to SCSI phase sequence error",
+       "killed due to unknown status" };
+
+
+/*
+  Define the Physical Device State type.
+*/
+
+typedef enum
+{
+    DAC960_Device_Dead =			0x00,
+    DAC960_Device_WriteOnly =			0x02,
+    DAC960_Device_Online =			0x03,
+    DAC960_Device_Standby =			0x10
+}
+__attribute__ ((packed))
+DAC960_PhysicalDeviceState_T;
+
+
+/*
+  Define the Get Device State reply structure.
+*/
+
+typedef struct DAC960_DeviceState
+{
+  boolean Present:1;					/* Byte 0 Bit 0 */
+  unsigned char :7;					/* Byte 0 Bits 1-7 */
+  enum {
+    DAC960_OtherType =				0x00,
+    DAC960_DiskType =				0x01,
+    DAC960_SequentialType =			0x02,
+    DAC960_CDROM_or_WORM_Type =			0x03
+    } __attribute__ ((packed)) DeviceType:2;		/* Byte 1 Bits 0-1 */
+  boolean :1;						/* Byte 1 Bit 2 */
+  boolean Fast20:1;					/* Byte 1 Bit 3 */
+  boolean Sync:1;					/* Byte 1 Bit 4 */
+  boolean Fast:1;					/* Byte 1 Bit 5 */
+  boolean Wide:1;					/* Byte 1 Bit 6 */
+  boolean TaggedQueuingSupported:1;			/* Byte 1 Bit 7 */
+  DAC960_PhysicalDeviceState_T DeviceState;		/* Byte 2 */
+  unsigned char :8;					/* Byte 3 */
+  unsigned char SynchronousMultiplier;			/* Byte 4 */
+  unsigned char SynchronousOffset:5;			/* Byte 5 Bits 0-4 */
+  unsigned char :3;					/* Byte 5 Bits 5-7 */
+  unsigned long DiskSize __attribute__ ((packed));	/* Bytes 6-9 */
+}
+DAC960_DeviceState_T;
+
+
+/*
+  Define the Get Rebuild Progress reply structure.
+*/
+
+typedef struct DAC960_RebuildProgress
+{
+  unsigned int LogicalDriveNumber;			/* Bytes 0-3 */
+  unsigned int LogicalDriveSize;			/* Bytes 4-7 */
+  unsigned int RemainingBlocks;				/* Bytes 8-11 */
+}
+DAC960_RebuildProgress_T;
+
+
+/*
+  Define the Error Table Entry and Get Error Table reply structure.
+*/
+
+typedef struct DAC960_ErrorTableEntry
+{
+  unsigned char ParityErrorCount;			/* Byte 0 */
+  unsigned char SoftErrorCount;				/* Byte 1 */
+  unsigned char HardErrorCount;				/* Byte 2 */
+  unsigned char MiscErrorCount;				/* Byte 3 */
+}
+DAC960_ErrorTableEntry_T;
+
+
+/*
+  Define the Get Error Table reply structure.
+*/
+
+typedef struct DAC960_ErrorTable
+{
+  DAC960_ErrorTableEntry_T
+    ErrorTableEntries[DAC960_MaxChannels][DAC960_MaxTargets];
+}
+DAC960_ErrorTable_T;
+
+
+/*
+  Define the Config2 reply structure.
+*/
+
+typedef struct DAC960_Config2
+{
+  unsigned char :1;					/* Byte 0 Bit 0 */
+  boolean ActiveNegationEnabled:1;			/* Byte 0 Bit 1 */
+  unsigned char :5;					/* Byte 0 Bits 2-6 */
+  boolean NoRescanIfResetReceivedDuringScan:1;		/* Byte 0 Bit 7 */
+  boolean StorageWorksSupportEnabled:1;			/* Byte 1 Bit 0 */
+  boolean HewlettPackardSupportEnabled:1;		/* Byte 1 Bit 1 */
+  boolean NoDisconnectOnFirstCommand:1;			/* Byte 1 Bit 2 */
+  unsigned char :2;					/* Byte 1 Bits 3-4 */
+  boolean AEMI_ARM:1;					/* Byte 1 Bit 5 */
+  boolean AEMI_OFM:1;					/* Byte 1 Bit 6 */
+  unsigned char :1;					/* Byte 1 Bit 7 */
+  enum {
+    DAC960_OEMID_Mylex =			0x00,
+    DAC960_OEMID_IBM =				0x08,
+    DAC960_OEMID_HP =				0x0A,
+    DAC960_OEMID_DEC =				0x0C,
+    DAC960_OEMID_Siemens =			0x10,
+    DAC960_OEMID_Intel =			0x12
+  } __attribute__ ((packed)) OEMID;			/* Byte 2 */
+  unsigned char OEMModelNumber;				/* Byte 3 */
+  unsigned char PhysicalSector;				/* Byte 4 */
+  unsigned char LogicalSector;				/* Byte 5 */
+  unsigned char BlockFactor;				/* Byte 6 */
+  boolean ReadAheadEnabled:1;				/* Byte 7 Bit 0 */
+  boolean LowBIOSDelay:1;				/* Byte 7 Bit 1 */
+  unsigned char :2;					/* Byte 7 Bits 2-3 */
+  boolean ReassignRestrictedToOneSector:1;		/* Byte 7 Bit 4 */
+  unsigned char :1;					/* Byte 7 Bit 5 */
+  boolean ForceUnitAccessDuringWriteRecovery:1;		/* Byte 7 Bit 6 */
+  boolean EnableLeftSymmetricRAID5Algorithm:1;		/* Byte 7 Bit 7 */
+  unsigned char DefaultRebuildRate;			/* Byte 8 */
+  unsigned char :8;					/* Byte 9 */
+  unsigned char BlocksPerCacheLine;			/* Byte 10 */
+  unsigned char BlocksPerStripe;			/* Byte 11 */
+  struct {
+    enum {
+      DAC960_Async =				0x00,
+      DAC960_Sync_8MHz =			0x01,
+      DAC960_Sync_5MHz =			0x02,
+      DAC960_Sync_10or20MHz =			0x03	/* Bits 0-1 */
+    } __attribute__ ((packed)) Speed:2;
+    boolean Force8Bit:1;				/* Bit 2 */
+    boolean DisableFast20:1;				/* Bit 3 */
+    unsigned char :3;					/* Bits 4-6 */
+    boolean EnableTaggedQueuing:1;			/* Bit 7 */
+  } __attribute__ ((packed)) ChannelParameters[6];	/* Bytes 12-17 */
+  unsigned char SCSIInitiatorID;			/* Byte 18 */
+  unsigned char :8;					/* Byte 19 */
+  enum {
+    DAC960_StartupMode_ControllerSpinUp =	0x00,
+    DAC960_StartupMode_PowerOnSpinUp =		0x01
+  } __attribute__ ((packed)) StartupMode;		/* Byte 20 */
+  unsigned char SimultaneousDeviceSpinUpCount;		/* Byte 21 */
+  unsigned char SecondsDelayBetweenSpinUps;		/* Byte 22 */
+  unsigned char Reserved1[29];				/* Bytes 23-51 */
+  boolean BIOSDisabled:1;				/* Byte 52 Bit 0 */
+  boolean CDROMBootEnabled:1;				/* Byte 52 Bit 1 */
+  unsigned char :3;					/* Byte 52 Bits 2-4 */
+  enum {
+    DAC960_Geometry_128_32 =			0x00,
+    DAC960_Geometry_255_63 =			0x01,
+    DAC960_Geometry_Reserved1 =			0x02,
+    DAC960_Geometry_Reserved2 =			0x03
+  } __attribute__ ((packed)) DriveGeometry:2;		/* Byte 52 Bits 5-6 */
+  unsigned char :1;					/* Byte 52 Bit 7 */
+  unsigned char Reserved2[9];				/* Bytes 53-61 */
+  unsigned short Checksum;				/* Bytes 62-63 */
+}
+DAC960_Config2_T;
+
+
+/*
+  Define the Scatter/Gather List Type 1 32 Bit Address 32 Bit Byte Count
+  structure.
+*/
+
+typedef struct DAC960_ScatterGatherSegment
+{
+  DAC960_BusAddress_T SegmentDataPointer;		/* Bytes 0-3 */
+  DAC960_ByteCount_T SegmentByteCount;			/* Bytes 4-7 */
+}
+DAC960_ScatterGatherSegment_T;
+
+
+/*
+  Define the 13 Byte DAC960 Command Mailbox structure.  Bytes 13-15 are
+  not used.  The Command Mailbox structure is padded to 16 bytes for
+  efficient access.
+*/
+
+typedef union DAC960_CommandMailbox
+{
+  unsigned int Words[4];				/* Words 0-3 */
+  unsigned char Bytes[16];				/* Bytes 0-15 */
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy[14];				/* Bytes 2-15 */
+  } __attribute__ ((packed)) Common;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy1[6];				/* Bytes 2-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy1[5];				/* Bytes 2-6 */
+    unsigned char LogicalDriveNumber:6;			/* Byte 7 Bits 0-6 */
+    boolean AutoRestore:1;				/* Byte 7 Bit 7 */
+    unsigned char Dummy2[8];				/* Bytes 8-15 */
+  } __attribute__ ((packed)) Type3C;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Channel;				/* Byte 2 */
+    unsigned char TargetID;				/* Byte 3 */
+    DAC960_PhysicalDeviceState_T DeviceState:5;		/* Byte 4 Bits 0-4 */
+    unsigned char Modifier:3;				/* Byte 4 Bits 5-7 */
+    unsigned char Dummy1[3];				/* Bytes 5-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3D;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    DAC960_PerformEventLogOpType_T OperationType;	/* Byte 2 */
+    unsigned char OperationQualifier;			/* Byte 3 */
+    unsigned short SequenceNumber;			/* Bytes 4-5 */
+    unsigned char Dummy1[2];				/* Bytes 6-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy2[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3E;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char Dummy1[2];				/* Bytes 2-3 */
+    unsigned char RebuildRateConstant;			/* Byte 4 */
+    unsigned char Dummy2[3];				/* Bytes 5-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char Dummy3[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) Type3R;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    struct {
+      unsigned short TransferLength:11;			/* Bytes 2-3 */
+      unsigned char LogicalDriveNumber:5;		/* Byte 3 Bits 3-7 */
+    } __attribute__ ((packed)) LD;
+    unsigned int LogicalBlockAddress;			/* Bytes 4-7 */
+    DAC960_BusAddress_T BusAddress;			/* Bytes 8-11 */
+    unsigned char ScatterGatherCount:6;			/* Byte 12 Bits 0-5 */
+    enum {
+      DAC960_ScatterGather_32BitAddress_32BitByteCount =	0x0,
+      DAC960_ScatterGather_32BitAddress_16BitByteCount =	0x1,
+      DAC960_ScatterGather_32BitByteCount_32BitAddress =	0x2,
+      DAC960_ScatterGather_16BitByteCount_32BitAddress =	0x3
+    } __attribute__ ((packed)) ScatterGatherType:2;	/* Byte 12 Bits 6-7 */
+    unsigned char Dummy[3];				/* Bytes 13-15 */
+  } __attribute__ ((packed)) Type5;
+  struct {
+    DAC960_CommandOpcode_T CommandOpcode;		/* Byte 0 */
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 1 */
+    unsigned char CommandOpcode2;			/* Byte 2 */
+    unsigned char :8;					/* Byte 3 */
+    DAC960_BusAddress_T CommandMailboxesBusAddress;	/* Bytes 4-7 */
+    DAC960_BusAddress_T StatusMailboxesBusAddress;	/* Bytes 8-11 */
+    unsigned char Dummy[4];				/* Bytes 12-15 */
+  } __attribute__ ((packed)) TypeX;
+}
+DAC960_CommandMailbox_T;
+
+
+/*
+  Define the DAC960 Controller Status Mailbox structure.
+*/
+
+typedef union DAC960_StatusMailbox
+{
+  unsigned int Word;					/* Bytes 0-3 */
+  struct {
+    DAC960_CommandIdentifier_T CommandIdentifier;	/* Byte 0 */
+    unsigned char :7;					/* Byte 1 Bits 0-6 */
+    boolean Valid:1;					/* Byte 1 Bit 7 */
+    DAC960_CommandStatus_T CommandStatus;		/* Bytes 2-3 */
+  } Fields;
+}
+DAC960_StatusMailbox_T;
+
+
+/*
+  Define the DAC960 Driver Command Types.
+*/
+
+typedef enum
+{
+  DAC960_ReadCommand =				1,
+  DAC960_WriteCommand =				2,
+  DAC960_ReadRetryCommand =			3,
+  DAC960_WriteRetryCommand =			4,
+  DAC960_MonitoringCommand =			5,
+  DAC960_ImmediateCommand =			6
+}
+DAC960_CommandType_T;
+
+
+/*
+  Define the DAC960 Driver Command structure.
+*/
+
+typedef struct DAC960_Command
+{
+  DAC960_CommandType_T CommandType;
+  DAC960_CommandMailbox_T CommandMailbox;
+  DAC960_CommandStatus_T CommandStatus;
+  struct DAC960_Controller *Controller;
+  struct DAC960_Command *Next;
+  Semaphore_T *Semaphore;
+  unsigned int LogicalDriveNumber;
+  unsigned int BlockNumber;
+  unsigned int BlockCount;
+  unsigned int SegmentCount;
+  BufferHeader_T *BufferHeader;
+  DAC960_ScatterGatherSegment_T
+    ScatterGatherList[DAC960_MaxScatterGatherSegments];
+}
+DAC960_Command_T;
+
+
+/*
+  Define the DAC960 Driver Controller structure.
+*/
+
+typedef struct DAC960_Controller
+{
+  void *BaseAddress;
+  void *MemoryMappedAddress;
+  DAC960_ControllerType_T ControllerType;
+  DAC960_IO_Address_T IO_Address;
+  DAC960_PCI_Address_T PCI_Address;
+  unsigned char ControllerNumber;
+  unsigned char ControllerName[4];
+  unsigned char ModelName[12];
+  unsigned char FullModelName[18];
+  unsigned char FirmwareVersion[14];
+  unsigned char Bus;
+  unsigned char Device;
+  unsigned char Function;
+  unsigned char IRQ_Channel;
+  unsigned char Channels;
+  unsigned char MemorySize;
+  unsigned char LogicalDriveCount;
+  unsigned char GeometryTranslationHeads;
+  unsigned char GeometryTranslationSectors;
+  unsigned short ControllerQueueDepth;
+  unsigned short DriverQueueDepth;
+  unsigned short MaxBlocksPerCommand;
+  unsigned short MaxScatterGatherSegments;
+  unsigned short StripeSize;
+  unsigned short SegmentSize;
+  unsigned short NewEventLogSequenceNumber;
+  unsigned short OldEventLogSequenceNumber;
+  unsigned short InitialStatusLength;
+  unsigned short CurrentStatusLength;
+  unsigned short UserStatusLength;
+  unsigned short RebuildProgressLength;
+  unsigned int ControllerUsageCount;
+  unsigned int EnquiryIndex;
+  unsigned int LogicalDriveInformationIndex;
+  unsigned int ErrorTableIndex;
+  unsigned int DeviceStateIndex;
+  unsigned int DeviceStateChannel;
+  unsigned int DeviceStateTargetID;
+  unsigned long MonitoringTimerCount;
+  unsigned long SecondaryMonitoringTime;
+  unsigned long LastProgressReportTime;
+  boolean DualModeMemoryMailboxInterface;
+  boolean SAFTE_FaultManagementEnabled;
+  boolean ControllerInitialized;
+  boolean UserCommandActive;
+  boolean UserCommandDeferred;
+  boolean MonitoringCommandDeferred;
+  boolean NeedLogicalDriveInformation;
+  boolean NeedErrorTableInformation;
+  boolean NeedDeviceStateInformation;
+  boolean NeedRebuildProgress;
+  boolean NeedConsistencyCheckProgress;
+  boolean EphemeralProgressMessage;
+  Timer_T MonitoringTimer;
+  Semaphore_T *UserCommandSemaphore;
+  GenericDiskInfo_T GenericDiskInfo;
+  DAC960_Command_T *FreeCommands;
+  DAC960_Command_T *UserCommand;
+  DAC960_CommandMailbox_T *FirstCommandMailbox;
+  DAC960_CommandMailbox_T *LastCommandMailbox;
+  DAC960_CommandMailbox_T *NextCommandMailbox;
+  DAC960_CommandMailbox_T *PreviousCommandMailbox1;
+  DAC960_CommandMailbox_T *PreviousCommandMailbox2;
+  DAC960_StatusMailbox_T *FirstStatusMailbox;
+  DAC960_StatusMailbox_T *LastStatusMailbox;
+  DAC960_StatusMailbox_T *NextStatusMailbox;
+  PROC_DirectoryEntry_T ControllerProcEntry;
+  PROC_DirectoryEntry_T InitialStatusProcEntry;
+  PROC_DirectoryEntry_T CurrentStatusProcEntry;
+  PROC_DirectoryEntry_T UserCommandProcEntry;
+  WaitQueue_T *CommandWaitQueue;
+  DAC960_Enquiry_T Enquiry[2];
+  DAC960_ErrorTable_T ErrorTable[2];
+  DAC960_EventLogEntry_T EventLogEntry;
+  DAC960_RebuildProgress_T RebuildProgress;
+  DAC960_LogicalDriveInformation_T
+    LogicalDriveInformation[2][DAC960_MaxLogicalDrives];
+  DAC960_LogicalDriveState_T LogicalDriveInitialState[DAC960_MaxLogicalDrives];
+  DAC960_DeviceState_T DeviceState[2][DAC960_MaxChannels][DAC960_MaxTargets];
+  DAC960_Command_T Commands[DAC960_MaxDriverQueueDepth];
+  DiskPartition_T DiskPartitions[DAC960_MinorCount];
+  int LogicalDriveUsageCount[DAC960_MaxLogicalDrives];
+  int PartitionSizes[DAC960_MinorCount];
+  int BlockSizes[DAC960_MinorCount];
+  int MaxSectorsPerRequest[DAC960_MinorCount];
+  int MaxSegmentsPerRequest[DAC960_MinorCount];
+  char InitialStatusBuffer[DAC960_StatusBufferSize];
+  char CurrentStatusBuffer[DAC960_StatusBufferSize];
+  char UserStatusBuffer[DAC960_UserMessageSize];
+  char RebuildProgressBuffer[DAC960_RebuildProgressSize];
+}
+DAC960_Controller_T;
+
+
+/*
+  DAC960_AcquireControllerLock acquires exclusive access to Controller.
+*/
+
+static inline
+void DAC960_AcquireControllerLock(DAC960_Controller_T *Controller,
+				  ProcessorFlags_T *ProcessorFlags)
+{
+  save_flags(*ProcessorFlags);
+  cli();
+}
+
+
+/*
+  DAC960_ReleaseControllerLock releases exclusive access to Controller.
+*/
+
+static inline
+void DAC960_ReleaseControllerLock(DAC960_Controller_T *Controller,
+				  ProcessorFlags_T *ProcessorFlags)
+{
+  restore_flags(*ProcessorFlags);
+}
+
+
+/*
+  DAC960_AcquireControllerLockRF acquires exclusive access to Controller,
+  but is only called from the request function when interrupts are disabled.
+*/
+
+static inline
+void DAC960_AcquireControllerLockRF(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_ReleaseControllerLockRF releases exclusive access to Controller,
+  but is only called from the request function when interrupts are disabled.
+*/
+
+static inline
+void DAC960_ReleaseControllerLockRF(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_AcquireControllerLockIH acquires exclusive access to Controller,
+  but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void DAC960_AcquireControllerLockIH(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  DAC960_ReleaseControllerLockIH releases exclusive access to Controller,
+  but is only called from the interrupt handler when interrupts are disabled.
+*/
+
+static inline
+void DAC960_ReleaseControllerLockIH(DAC960_Controller_T *Controller,
+				    ProcessorFlags_T *ProcessorFlags)
+{
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V5 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V5_HardwareMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.HardwareMailboxNewCommand = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeHardwareMailboxStatus = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_GenerateInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.GenerateInterrupt = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_ControllerReset(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.ControllerReset = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_MemoryMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.MemoryMailboxNewCommand = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V5_HardwareMailboxEmptyP(void *ControllerBaseAddress)
+{
+  DAC960_V5_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.HardwareMailboxEmpty;
+}
+
+static inline
+void DAC960_V5_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V5_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V5_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.HardwareMailboxStatusAvailable;
+}
+
+static inline
+boolean DAC960_V5_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V5_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.MemoryMailboxStatusAvailable;
+}
+
+static inline
+void DAC960_V5_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.DisableInterrupts = false;
+  writeb(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
+}
+
+static inline
+void DAC960_V5_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.DisableInterrupts = true;
+  writeb(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
+}
+
+static inline
+boolean DAC960_V5_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V5_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All =
+    readb(ControllerBaseAddress + DAC960_V5_InterruptMaskRegisterOffset);
+  return !InterruptMaskRegister.Bits.DisableInterrupts;
+}
+
+static inline
+void DAC960_V5_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  NextCommandMailbox->Words[1] = CommandMailbox->Words[1];
+  NextCommandMailbox->Words[2] = CommandMailbox->Words[2];
+  NextCommandMailbox->Words[3] = CommandMailbox->Words[3];
+  NextCommandMailbox->Words[0] = CommandMailbox->Words[0];
+}
+
+static inline
+void DAC960_V5_WriteHardwareMailbox(void *ControllerBaseAddress,
+				    DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V5_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V5_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V5_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V5_StatusRegisterOffset);
+}
+
+static inline
+void DAC960_V5_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  writel(0x743C485E,
+	 ControllerBaseAddress + DAC960_V5_CommandOpcodeRegisterOffset);
+  writel((unsigned long) Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset);
+  writew(Controller->NextCommandMailbox - Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset);
+  writew(Controller->NextStatusMailbox - Controller->FirstStatusMailbox,
+	 ControllerBaseAddress + DAC960_V5_MailboxRegister10Offset);
+}
+
+static inline
+void DAC960_V5_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller,
+					void **MemoryMailboxAddress,
+					short *NextCommandMailboxIndex,
+					short *NextStatusMailboxIndex)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  if (readl(ControllerBaseAddress
+	    + DAC960_V5_CommandOpcodeRegisterOffset) != 0x743C485E)
+    return;
+  *MemoryMailboxAddress =
+    (void *) readl(ControllerBaseAddress + DAC960_V5_MailboxRegister4Offset);
+  *NextCommandMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V5_MailboxRegister8Offset);
+  *NextStatusMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V5_MailboxRegister10Offset);
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V4 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V4_HardwareMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.HardwareMailboxNewCommand = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_AcknowledgeHardwareMailboxStatus(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeHardwareMailboxStatus = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_GenerateInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.GenerateInterrupt = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_ControllerReset(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.ControllerReset = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_MemoryMailboxNewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.MemoryMailboxNewCommand = true;
+  writel(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_HardwareMailboxFullP(void *ControllerBaseAddress)
+{
+  DAC960_V4_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.HardwareMailboxFull;
+}
+
+static inline
+void DAC960_V4_AcknowledgeHardwareMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  writel(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_AcknowledgeMemoryMailboxInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writel(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V4_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeHardwareMailboxInterrupt = true;
+  OutboundDoorBellRegister.Write.AcknowledgeMemoryMailboxInterrupt = true;
+  writel(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_HardwareMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.HardwareMailboxStatusAvailable;
+}
+
+static inline
+boolean DAC960_V4_MemoryMailboxStatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V4_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.MemoryMailboxStatusAvailable;
+}
+
+static inline
+void DAC960_V4_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3;
+  InterruptMaskRegister.Bits.DisableInterrupts = false;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F;
+  writel(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+}
+
+static inline
+void DAC960_V4_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All = 0;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask1 = 0x3;
+  InterruptMaskRegister.Bits.DisableInterrupts = true;
+  InterruptMaskRegister.Bits.MessageUnitInterruptMask2 = 0x1F;
+  writel(InterruptMaskRegister.All,
+	 ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+}
+
+static inline
+boolean DAC960_V4_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V4_InterruptMaskRegister_T InterruptMaskRegister;
+  InterruptMaskRegister.All =
+    readl(ControllerBaseAddress + DAC960_V4_InterruptMaskRegisterOffset);
+  return !InterruptMaskRegister.Bits.DisableInterrupts;
+}
+
+static inline
+void DAC960_V4_WriteCommandMailbox(DAC960_CommandMailbox_T *NextCommandMailbox,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  NextCommandMailbox->Words[1] = CommandMailbox->Words[1];
+  NextCommandMailbox->Words[2] = CommandMailbox->Words[2];
+  NextCommandMailbox->Words[3] = CommandMailbox->Words[3];
+  NextCommandMailbox->Words[0] = CommandMailbox->Words[0];
+}
+
+static inline
+void DAC960_V4_WriteHardwareMailbox(void *ControllerBaseAddress,
+				    DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V4_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V4_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V4_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V4_StatusRegisterOffset);
+}
+
+static inline
+void DAC960_V4_SaveMemoryMailboxInfo(DAC960_Controller_T *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  writel(0xAABBFFFF,
+	 ControllerBaseAddress + DAC960_V4_CommandOpcodeRegisterOffset);
+  writel((unsigned long) Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
+  writew(Controller->NextCommandMailbox - Controller->FirstCommandMailbox,
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset);
+  writew(Controller->NextStatusMailbox - Controller->FirstStatusMailbox,
+	 ControllerBaseAddress + DAC960_V4_MailboxRegister10Offset);
+}
+
+static inline
+void DAC960_V4_RestoreMemoryMailboxInfo(DAC960_Controller_T *Controller,
+					void **MemoryMailboxAddress,
+					short *NextCommandMailboxIndex,
+					short *NextStatusMailboxIndex)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  if (readl(ControllerBaseAddress
+	    + DAC960_V4_CommandOpcodeRegisterOffset) != 0xAABBFFFF)
+    return;
+  *MemoryMailboxAddress =
+    (void *) readl(ControllerBaseAddress + DAC960_V4_MailboxRegister4Offset);
+  *NextCommandMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V4_MailboxRegister8Offset);
+  *NextStatusMailboxIndex =
+    readw(ControllerBaseAddress + DAC960_V4_MailboxRegister10Offset);
+}
+
+
+/*
+  Define inline functions to provide an abstraction for reading and writing the
+  DAC960 V3 Controller Interface Registers.
+*/
+
+static inline
+void DAC960_V3_NewCommand(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.NewCommand = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_AcknowledgeStatus(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.AcknowledgeStatus = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_GenerateInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.GenerateInterrupt = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+void DAC960_V3_ControllerReset(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All = 0;
+  InboundDoorBellRegister.Write.ControllerReset = true;
+  writeb(InboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_MailboxFullP(void *ControllerBaseAddress)
+{
+  DAC960_V3_InboundDoorBellRegister_T InboundDoorBellRegister;
+  InboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_InboundDoorBellRegisterOffset);
+  return InboundDoorBellRegister.Read.MailboxFull;
+}
+
+static inline
+void DAC960_V3_AcknowledgeInterrupt(void *ControllerBaseAddress)
+{
+  DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All = 0;
+  OutboundDoorBellRegister.Write.AcknowledgeInterrupt = true;
+  writeb(OutboundDoorBellRegister.All,
+	 ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_StatusAvailableP(void *ControllerBaseAddress)
+{
+  DAC960_V3_OutboundDoorBellRegister_T OutboundDoorBellRegister;
+  OutboundDoorBellRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_OutboundDoorBellRegisterOffset);
+  return OutboundDoorBellRegister.Read.StatusAvailable;
+}
+
+static inline
+void DAC960_V3_EnableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All = 0;
+  InterruptEnableRegister.Bits.EnableInterrupts = true;
+  writeb(InterruptEnableRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+}
+
+static inline
+void DAC960_V3_DisableInterrupts(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All = 0;
+  InterruptEnableRegister.Bits.EnableInterrupts = false;
+  writeb(InterruptEnableRegister.All,
+	 ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+}
+
+static inline
+boolean DAC960_V3_InterruptsEnabledP(void *ControllerBaseAddress)
+{
+  DAC960_V3_InterruptEnableRegister_T InterruptEnableRegister;
+  InterruptEnableRegister.All =
+    readb(ControllerBaseAddress + DAC960_V3_InterruptEnableRegisterOffset);
+  return InterruptEnableRegister.Bits.EnableInterrupts;
+}
+
+static inline
+void DAC960_V3_WriteCommandMailbox(void *ControllerBaseAddress,
+				   DAC960_CommandMailbox_T *CommandMailbox)
+{
+  writel(CommandMailbox->Words[0],
+	 ControllerBaseAddress + DAC960_V3_CommandOpcodeRegisterOffset);
+  writel(CommandMailbox->Words[1],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister4Offset);
+  writel(CommandMailbox->Words[2],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister8Offset);
+  writeb(CommandMailbox->Bytes[12],
+	 ControllerBaseAddress + DAC960_V3_MailboxRegister12Offset);
+}
+
+static inline DAC960_CommandIdentifier_T
+DAC960_V3_ReadStatusCommandIdentifier(void *ControllerBaseAddress)
+{
+  return readb(ControllerBaseAddress
+	       + DAC960_V3_StatusCommandIdentifierRegOffset);
+}
+
+static inline DAC960_CommandStatus_T
+DAC960_V3_ReadStatusRegister(void *ControllerBaseAddress)
+{
+  return readw(ControllerBaseAddress + DAC960_V3_StatusRegisterOffset);
+}
+
+
+/*
+  Virtual_to_Bus and Bus_to_Virtual map between Kernel Virtual Addresses
+  and PCI Bus Addresses.
+*/
+
+static inline DAC960_BusAddress_T Virtual_to_Bus(void *VirtualAddress)
+{
+  return (DAC960_BusAddress_T) virt_to_bus(VirtualAddress);
+}
+
+static inline void *Bus_to_Virtual(DAC960_BusAddress_T BusAddress)
+{
+  return (void *) bus_to_virt(BusAddress);
+}
+
+
+/*
+  Define compatibility macros between Linux 2.0 and Linux 2.1.
+*/
+
+#if LINUX_VERSION_CODE < 0x20100
+
+#define MODULE_PARM(Variable, Type)
+#define ioremap_nocache(Offset, Size)	vremap(Offset, Size)
+#define iounmap(Address)		vfree(Address)
+
+#endif
+
+
+/*
+  Define prototypes for the forward referenced DAC960 Driver Internal Functions.
+*/
+
+static void DAC960_FinalizeController(DAC960_Controller_T *);
+static void DAC960_RequestFunction0(void);
+static void DAC960_RequestFunction1(void);
+static void DAC960_RequestFunction2(void);
+static void DAC960_RequestFunction3(void);
+static void DAC960_RequestFunction4(void);
+static void DAC960_RequestFunction5(void);
+static void DAC960_RequestFunction6(void);
+static void DAC960_RequestFunction7(void);
+static void DAC960_InterruptHandler(int, void *, Registers_T *);
+static void DAC960_QueueMonitoringCommand(DAC960_Command_T *);
+static void DAC960_MonitoringTimerFunction(unsigned long);
+static int DAC960_Open(Inode_T *, File_T *);
+static void DAC960_Release(Inode_T *, File_T *);
+static int DAC960_Ioctl(Inode_T *, File_T *, unsigned int, unsigned long);
+static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *);
+static void DAC960_Message(DAC960_MessageLevel_T, char *,
+			   DAC960_Controller_T *, ...);
+static void DAC960_CreateProcEntries(void);
+static void DAC960_DestroyProcEntries(void);
+
+
+#endif /* DAC960_DriverVersion */
diff -urN linux-2.0.37-pre8/Makefile linux-2.0.37-pre9/Makefile
--- linux-2.0.37-pre8/Makefile	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/Makefile	2003-08-15 15:04:42.000000000 -0700
@@ -2,7 +2,7 @@
 PATCHLEVEL = 0
 SUBLEVEL = 37
 
-ARCH = i386
+ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
 #
 # For SMP kernels, set this. We don't want to have this in the config file
diff -urN linux-2.0.37-pre8/net/appletalk/ddp.c linux-2.0.37-pre9/net/appletalk/ddp.c
--- linux-2.0.37-pre8/net/appletalk/ddp.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/net/appletalk/ddp.c	2003-08-15 15:04:42.000000000 -0700
@@ -408,6 +408,7 @@
 	 *	Scan the networks.
 	 */
 	 
+	atif->status |= ATIF_PROBE;
 	for(netct=0;netct<=netrange;netct++)
 	{
 		/*
@@ -435,8 +436,10 @@
 					if(atif->status&ATIF_PROBE_FAIL)
 						break;
 				}
-				if(!(atif->status&ATIF_PROBE_FAIL))
+				if(!(atif->status&ATIF_PROBE_FAIL)) {
+					atif->status &= ~ATIF_PROBE;
 					return 0;
+				}
 			}
 			atif->status&=~ATIF_PROBE_FAIL;
 		}
@@ -444,6 +447,7 @@
 		if(probe_net>ntohs(atif->nets.nr_lastnet))
 			probe_net=ntohs(atif->nets.nr_firstnet);
 	}
+	atif->status &= ~ATIF_PROBE;
 	return -EADDRINUSE;	/* Network is full... */
 }
 
diff -urN linux-2.0.37-pre8/net/ipv4/ip_sockglue.c linux-2.0.37-pre9/net/ipv4/ip_sockglue.c
--- linux-2.0.37-pre8/net/ipv4/ip_sockglue.c	2003-08-15 15:04:41.000000000 -0700
+++ linux-2.0.37-pre9/net/ipv4/ip_sockglue.c	2003-08-15 15:04:42.000000000 -0700
@@ -549,12 +549,18 @@
 		  	if(err)
   				return err;
   			
-  			dev=dev_get(sk->ip_mc_name);
-  			/* Someone ran off with the interface, its probably
-  			   been downed. */
-  			if(dev==NULL)
-  				return -ENODEV;
-  			ia.s_addr = dev->pa_addr;
+  			if(*sk->ip_mc_name)
+  			{
+	  			dev=dev_get(sk->ip_mc_name);
+  				/* Someone ran off with the interface, its probably
+  				   been downed. */
+  				if(dev==NULL)
+  					return -ENODEV;
+	  			ia.s_addr = dev->pa_addr;
+	  		}
+	  		else
+	  			ia.s_addr = 0L;
+	 
   			put_user(sizeof(ia),(int *) optlen);
 			memcpy_tofs((void *)optval, &ia, sizeof(ia));
 			return 0;
diff -urN linux-2.0.37-pre8/net/netrom/af_netrom.c linux-2.0.37-pre9/net/netrom/af_netrom.c
--- linux-2.0.37-pre8/net/netrom/af_netrom.c	1998-11-15 10:33:22.000000000 -0800
+++ linux-2.0.37-pre9/net/netrom/af_netrom.c	2003-08-15 15:04:42.000000000 -0700
@@ -960,10 +960,18 @@
 	 */
 	if (frametype != NR_CONNREQ) {
 		/*
+		 * Here it would be nice to be able to send a reset but
+		 * NET/ROM doesn't have one. The following hack would
+		 * have been a way to extend the protocol but apparently
+		 * it kills BPQ boxes... :-(
+		 */
+#if 0
+		/*
 		 * Never reply to a CONNACK/CHOKE.
 		 */
 		if (frametype != NR_CONNACK || flags != NR_CHOKE_FLAG)
 			nr_transmit_dm(skb, 1);
+#endif
 		return 0;
 	}
 
diff -urN linux-2.0.37-pre8/net/netsyms.c linux-2.0.37-pre9/net/netsyms.c
--- linux-2.0.37-pre8/net/netsyms.c	1998-07-13 13:47:41.000000000 -0700
+++ linux-2.0.37-pre9/net/netsyms.c	2003-08-15 15:04:42.000000000 -0700
@@ -12,6 +12,7 @@
 #include <linux/net.h>
 #include <linux/netdevice.h>
 #include <linux/trdevice.h>
+#include <linux/fddidevice.h>
 #include <linux/ioport.h>
 #include <net/sock.h>
 
@@ -131,6 +132,11 @@
 	X(tr_type_trans),
 #endif
                           
+#ifdef CONFIG_FDDI
+	X(fddi_setup),
+	X(fddi_type_trans),
+#endif
+                          
 #ifdef CONFIG_NET_ALIAS
 #include <linux/net_alias.h>
 #endif
diff -urN linux-2.0.37-pre8/README linux-2.0.37-pre9/README
--- linux-2.0.37-pre8/README	1996-06-26 01:05:26.000000000 -0700
+++ linux-2.0.37-pre9/README	2003-08-15 15:04:42.000000000 -0700
@@ -76,15 +76,6 @@
    the current directory, but an alternative directory can be specified
    as the second argument.
 
- - make sure your /usr/include/asm, /usr/include/linux, and /usr/include/scsi
-   directories are just symlinks to the kernel sources:
-
-		cd /usr/include
-		rm -rf asm linux scsi
-		ln -s /usr/src/linux/include/asm-i386 asm
-		ln -s /usr/src/linux/include/linux linux
-		ln -s /usr/src/linux/include/scsi scsi
-
  - make sure you have no stale .o files and dependencies lying around:
 
 		cd /usr/src/linux
