]> git.neil.brown.name Git - history.git/commitdiff
Linux 2.0.37pre9 2.0.37pre9
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:12:14 +0000 (15:12 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:12:14 +0000 (15:12 -0500)
o Appletalk probe bug (Jens-Uwe Mager)
o Further multicast fixes (Alan Cox)
| We should now pass the JDK mcast tests
o Export fddi symbols (Christoph Goos)
o Autoset ARCH, Makefile fix (Paul Slootman)
o PCI handling fix (Thomas Bogendoerfer)
o PCI2000, PCI2200, PSI240 drivers (Perceptive Solutions)
o Our netrom code crashed BPQ (Tomi Manninen)
| BPQ doesn't appear to have been fixed in most
| places. If you run BPQ, you should worry about this 8)
o Updated DAC960 driver (Leonard Zubkoff)
o Updated Adaptec 29xx driver (Doug Ledford)
o Assorted minor tweaks/cleanups (various)

41 files changed:
CREDITS
Documentation/Configure.help
Makefile
README
arch/alpha/Makefile
arch/i386/kernel/bios32.c
arch/i386/mm/init.c
drivers/block/genhd.c
drivers/block/ll_rw_blk.c
drivers/pci/pci.c
drivers/scsi/Config.in
drivers/scsi/Makefile
drivers/scsi/README.aic7xxx
drivers/scsi/aic7xxx.c
drivers/scsi/aic7xxx_proc.c
drivers/scsi/hosts.c
drivers/scsi/pci2000.c [new file with mode: 0644]
drivers/scsi/pci2000.h [new file with mode: 0644]
drivers/scsi/pci2220i.c [new file with mode: 0644]
drivers/scsi/pci2220i.h [new file with mode: 0644]
drivers/scsi/psi240i.c [new file with mode: 0644]
drivers/scsi/psi240i.h [new file with mode: 0644]
drivers/scsi/psi_chip.h [new file with mode: 0644]
drivers/scsi/psi_dale.h [new file with mode: 0644]
drivers/scsi/psi_roy.h [new file with mode: 0644]
drivers/scsi/scsi.c
drivers/scsi/scsi_syms.c
drivers/scsi/scsicam.c
drivers/scsi/seagate.c
drivers/scsi/tripace.c
include/linux/pci.h
include/linux/proc_fs.h
include/scsi/scsicam.h
kernel/sys.c
linux/Documentation/README.DAC960 [new file with mode: 0644]
linux/drivers/block/DAC960.c [new file with mode: 0644]
linux/drivers/block/DAC960.h [new file with mode: 0644]
net/appletalk/ddp.c
net/ipv4/ip_sockglue.c
net/netrom/af_netrom.c
net/netsyms.c

diff --git a/CREDITS b/CREDITS
index 097961ccdf89df05e22f03609c5d5a007d36d82a..df57041342f5f74e8ca1ac21a595376e01d32e5f 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1646,6 +1646,7 @@ N: Leonard N. Zubkoff
 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
index 3546fc8e510965de5eddee836f2f0fc0bea0a03a..88e8f7df55920fb7503e7d6cdf5bf86a43c9a529 100644 (file)
@@ -337,6 +337,12 @@ CONFIG_BLK_DEV_XD
   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 @@ CONFIG_SCSI_AHA1740
 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.
-
-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
+  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.
+
+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
index f767527d80211e5453c03a0f20fbfd7816135049..94d174739b101f3ebb88030bf3956324328cdb4d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ VERSION = 2
 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 --git a/README b/README
index 1414fd7229ad17870f3b9e8661fcfd89ce399910..e33ff6cda7cb8fb6ee01fc0fade88582f49aac65 100644 (file)
--- a/README
+++ b/README
@@ -76,15 +76,6 @@ INSTALLING the kernel:
    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
index 2467ba8ebb908e6d1caa7755ea779ef3d5347789..0fc8db2410d55e7128a5fd2f7521f3399a653fb0 100644 (file)
@@ -27,9 +27,9 @@ endif
 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)
index d4dbe3bcf45f1411fb6f38a495e5ccdb0ca9cc11..a5adda85435e3fb9c2330b38fad83a223e61d431 100644 (file)
@@ -730,7 +730,7 @@ static struct pci_access *check_direct_pci(void)
     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;
index 61388fa54c292b06ca1bfc2cac65cfbff0623c59..d7c7a8c0cc80eec1e2fda0ff66756a4317940c8d 100644 (file)
@@ -140,7 +140,7 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem)
                 */
                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
index 3c256718f800ca6f3982f5e4da07e0c7ce9d4ff8..3337424f670f8d39e0f760aa1f14240299db955c 100644 (file)
@@ -86,7 +86,6 @@ char *disk_name (struct gendisk *hd, int minor, char *buf)
                        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 @@ char *disk_name (struct gendisk *hd, int minor, char *buf)
                         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 @@ static void add_partition (struct gendisk *hd, int minor, int start, int size)
        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)));
index 664b7f6b2da98ac87dde3e31670e320059d74043..7be11b300acdaf53ab017db2ba240a0f1dc62cf0 100644 (file)
@@ -263,6 +263,11 @@ void add_request(struct blk_dev_struct * dev, struct request * req)
        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 @@ void make_request(int major,int rw, struct buffer_head * bh)
        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.,
index 6b022cfb6cbbcf91e7d5ff2049baa6170b2c6c79..1943a8a6bb03c45a660eee8780d53920ad0914d1 100644 (file)
@@ -527,8 +527,10 @@ struct pci_dev_info dev_info[] = {
        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 @@ struct pci_dev_info dev_info[] = {
        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"),
index 8d7c9be4adfc54576ac8291ff77a5aad872e7e1b..60b4b675cd270b6ba50b7d82457d412962f3ff66 100644 (file)
@@ -21,10 +21,8 @@ dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 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 @@ if [ "$CONFIG_SCSI_PPA" != "n" ]; then
     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
index 7b8d439f87c8561c42eb96f7740244b2e08a3920..f2ae232e6affd9386d8f13df886125f5c292c72c 100644 (file)
@@ -412,6 +412,30 @@ else
   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
index b350d9d5812f67bfce4f9e9c4b24a3490a58f83d..0497bbb10f115f6af2f70bbc193059302d7aa75f 100644 (file)
@@ -17,6 +17,10 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     AHA-274xT               
     AHA-2842
     AHA-2910B               
+    AHA-2920C
+    AHA-2930
+    AHA-2930U
+    AHA-2930U2
     AHA-2940               
     AHA-2940W              
     AHA-2940U              
@@ -37,8 +41,10 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     AHA-3940U
     AHA-3940W
     AHA-3940UW
+    AHA-3940AUW
     AHA-3940U2W
     AHA-3950U2B
+    AHA-3950U2D
     AHA-3985
     AHA-3985U
     AHA-3985W
@@ -52,6 +58,7 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     AIC-787x
     AIC-788x
     AIC-789x
+    AIC-3860
 
     Bus Types
     ----------------------------
@@ -69,8 +76,30 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     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 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     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 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
        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 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
           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 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     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 @@ linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
     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
index 4268df0fb202d26f1c1c5d6bb642ee1410737b21..1c1bc810ce36f5f1b1a549e94f7fc755533c7709 100644 (file)
  *
  * 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
 #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 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
     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 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  * 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 @@ struct proc_dir_entry proc_scsi_aic7xxx = {
  *
  * *** 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 @@ typedef struct
  * 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 @@ adapter_tag_info_t aic7xxx_tag_info[] =
 };
 */
 
+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 @@ static const char *board_names[] = {
   "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 @@ typedef enum {
   *  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 @@ typedef enum {
   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 @@ typedef enum {
   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 @@ typedef enum {
                          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 @@ struct aic7xxx_host {
   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 @@ struct aic7xxx_host {
   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 @@ struct aic7xxx_host {
 
   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 @@ struct aic7xxx_host {
   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 aic7xxx_host {
   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 @@ struct aic7xxx_host {
    *
    * 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 @@ static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
 
 #endif
 
-/*
- * See the comments earlier in the file for what this item is all about
- * If you have more than 4 controllers, you will need to increase the
- * the number of items in the array below.  Additionally, if you don't
- * want to have lilo pass a humongous config line to the aic7xxx driver,
- * then you can get in and manually adjust these instead of leaving them
- * at the default.  Pay attention to the comments earlier in this file
- * concerning this array if you are going to hand modify these values.
- */
-static adapter_tag_info_t aic7xxx_tag_info[] =
-{
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS}
-};
-
 #define VERBOSE_NORMAL         0x0000
 #define VERBOSE_NEGOTIATION    0x0001
 #define VERBOSE_SEQINT         0x0002
@@ -1510,6 +1514,8 @@ timer_pending(struct timer_list *timer)
   return( timer->prev != NULL );
 }
 
+#define PCI_DEVICE_ID_ADAPTEC_1480A 0x6075
+
 #endif
 
 static inline unsigned char
@@ -2796,6 +2802,7 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
   Scsi_Cmnd *cmd;
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
   unsigned int cpu_flags = 0;
+#endif
   
   DRIVER_LOCK
   while (p->completeq.head != NULL)
@@ -2803,20 +2810,9 @@ aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
     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 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
       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_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
           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_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
           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 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
             "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 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
   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 @@ aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
    * 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_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
    */
   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 @@ aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
     }
     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 @@ aic7xxx_timer(struct aic7xxx_host *p)
 #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 @@ aic7xxx_timer(struct aic7xxx_host *p)
     }
     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 @@ aic7xxx_timer(struct aic7xxx_host *p)
       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 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
                 {
                   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 @@ do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
  *   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 @@ aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
   {
     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 @@ read_seeprom(struct aic7xxx_host *p, int offset,
   }
   printk("\n");
 #endif
-  if (checksum != scarray[len - 1])
+  if ( (checksum != scarray[len - 1]) || (checksum == 0) )
   {
     return (0);
   }
@@ -7446,7 +7438,6 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
   }
 
   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 @@ aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
   }
   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_register(Scsi_Host_Template *template, struct aic7xxx_host *p,
    */
   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 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
         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 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
     {
       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 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
     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 @@ aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
     }
     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 @@ aic7xxx_detect(Scsi_Host_Template *template)
       {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 @@ aic7xxx_detect(Scsi_Host_Template *template)
       {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 @@ aic7xxx_detect(Scsi_Host_Template *template)
        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 @@ aic7xxx_detect(Scsi_Host_Template *template)
           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 @@ aic7xxx_detect(Scsi_Host_Template *template)
           }
 
           /*
-           * 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 @@ aic7xxx_detect(Scsi_Host_Template *template)
               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 @@ aic7xxx_detect(Scsi_Host_Template *template)
               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 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
     }
     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 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
     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 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
        * 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 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
 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);
index 8d75085f67da86c3410fa671a043238b6104c057..e0ff63aea6e3e26a52d1d4ac6ad91c4454124379 100644 (file)
@@ -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 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   int    size = 0;
   unsigned char i;
   struct aic7xxx_xferstats *sp;
-  unsigned char target, lun;
+  unsigned char target;
 
   HBAptr = NULL;
 
@@ -130,16 +130,12 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   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 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   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 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   }
   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 @@ aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length,
   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)
index 54d085f24f8fba92c568d56d19fd4c15efb243fd..5c4b867b10568685f7b4d92c6d6e712e730e12d1 100644 (file)
 #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 @@ static Scsi_Host_Template builtin_scsi_hosts[] =
 #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 --git a/drivers/scsi/pci2000.c b/drivers/scsi/pci2000.c
new file mode 100644 (file)
index 0000000..c6a13d8
--- /dev/null
@@ -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 --git a/drivers/scsi/pci2000.h b/drivers/scsi/pci2000.h
new file mode 100644 (file)
index 0000000..ded9939
--- /dev/null
@@ -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 --git a/drivers/scsi/pci2220i.c b/drivers/scsi/pci2220i.c
new file mode 100644 (file)
index 0000000..a45bafb
--- /dev/null
@@ -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 --git a/drivers/scsi/pci2220i.h b/drivers/scsi/pci2220i.h
new file mode 100644 (file)
index 0000000..0fafc26
--- /dev/null
@@ -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 --git a/drivers/scsi/psi240i.c b/drivers/scsi/psi240i.c
new file mode 100644 (file)
index 0000000..2fc31ca
--- /dev/null
@@ -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 --git a/drivers/scsi/psi240i.h b/drivers/scsi/psi240i.h
new file mode 100644 (file)
index 0000000..282ee1b
--- /dev/null
@@ -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 --git a/drivers/scsi/psi_chip.h b/drivers/scsi/psi_chip.h
new file mode 100644 (file)
index 0000000..644c42f
--- /dev/null
@@ -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 --git a/drivers/scsi/psi_dale.h b/drivers/scsi/psi_dale.h
new file mode 100644 (file)
index 0000000..d8a407f
--- /dev/null
@@ -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 --git a/drivers/scsi/psi_roy.h b/drivers/scsi/psi_roy.h
new file mode 100644 (file)
index 0000000..0b7f337
--- /dev/null
@@ -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
+
index b21272cd56aa6d204948b042dae1a523ed996691..6a3d7d4d7c0e3daf0af3bb71dd56442d316e2352 100644 (file)
@@ -295,6 +295,7 @@ static struct dev_info device_list[] =
 {"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...
  */
index 985b3eaaa39c5448d28df6ac78bcd64258f52b5a..6b3a7c9a89a7a87e496ce83de1451636b1bb69ad 100644 (file)
 #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 @@ struct symbol_table scsi_symbol_table = {
     X(scsi_register),
     X(scsi_unregister),
     X(scsicam_bios_param),
+    X(scsi_partsize),
     X(allocate_device),
     X(scsi_do_cmd),
     X(scsi_command_size),
index 2c6f63772b82f0077373d4c4ef55e581e1c4818d..c3fb8792514c1b7078233bd378cbfb42d3547c74 100644 (file)
@@ -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 @@ int scsicam_bios_param (Disk *disk, /* SCSI disk */
        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 @@ int scsicam_bios_param (Disk *disk, /* SCSI disk */
 }
 
 /*
- * 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 @@ int scsicam_bios_param (Disk *disk, /* SCSI disk */
  *
  */
 
-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;
index 9e94274644e14278b79d8cbe3b5d4bac1ef33e78..14b7e363fc70c0e72485b0fa971160e154b50d2f 100644 (file)
@@ -164,7 +164,7 @@ typedef struct {
 } 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},
 
index 8aba9c2e222734b871a49fb8e137a9b1e75e43a5..09d00feec2b9820616e6d1c0e6b43169d57db42e 100644 (file)
@@ -620,7 +620,7 @@ static PRISC_SRB search(PAdapter);
 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 @@ const char *tc2550_info(struct Scsi_Host *ignore)
        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;
 
index fe6f35fc303a89f0f2c24ec311d6faf4b3911a36..95d1dfa50f0d084bae89838fbf52df20a9c5f795 100644 (file)
 
 #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
 #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
index c055c43ce5c8aacc12def570cd3321618f29665b..b8711043d9045a4ff5397565dd41b605e8424bb3 100644 (file)
@@ -153,6 +153,9 @@ enum scsi_directory_inos {
        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,
index 954e1407ee549635e66eb8260fd2fcc6417cd37e..a9f00e229fddcaff65807dafd3aa428ccc48ebfa 100644 (file)
@@ -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 */
index 0de552de1e6645c29e39f815561cce9bdb957988..749f32c7f8bf1ef0926651d822a6cacfccd28379 100644 (file)
@@ -226,6 +226,9 @@ asmlinkage int sys_reboot(int magic, int magic_too, int flag)
 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 --git a/linux/Documentation/README.DAC960 b/linux/Documentation/README.DAC960
new file mode 100644 (file)
index 0000000..446d001
--- /dev/null
@@ -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 --git a/linux/drivers/block/DAC960.c b/linux/drivers/block/DAC960.c
new file mode 100644 (file)
index 0000000..32c0faa
--- /dev/null
@@ -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 --git a/linux/drivers/block/DAC960.h b/linux/drivers/block/DAC960.h
new file mode 100644 (file)
index 0000000..203d5d8
--- /dev/null
@@ -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 */
index 202203b3cc0871caf38d8c65df283ef7ea12f9dc..015459d951eb85f8b368c27d2e460fa959011ab4 100644 (file)
@@ -408,6 +408,7 @@ static int atif_probe_device(struct atalk_iface *atif)
         *      Scan the networks.
         */
         
+       atif->status |= ATIF_PROBE;
        for(netct=0;netct<=netrange;netct++)
        {
                /*
@@ -435,8 +436,10 @@ static int atif_probe_device(struct atalk_iface *atif)
                                        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 @@ static int atif_probe_device(struct atalk_iface *atif)
                if(probe_net>ntohs(atif->nets.nr_lastnet))
                        probe_net=ntohs(atif->nets.nr_firstnet);
        }
+       atif->status &= ~ATIF_PROBE;
        return -EADDRINUSE;     /* Network is full... */
 }
 
index aba62f3728d616d318dee86b0d35c70fedfac17c..323fc424071cec9e8729865c480ebe6a35845913 100644 (file)
@@ -549,12 +549,18 @@ int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *op
                        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;
index 04e8224ab0988447c671986b7f690b4a80a4ab64..94493fa18ad3a4e8057fdcddabb4ce042d695d6b 100644 (file)
@@ -959,11 +959,19 @@ int nr_rx_frame(struct sk_buff *skb, struct device *dev)
         * Now it should be a CONNREQ.
         */
        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;
        }
 
index 6729ff135fe4c3d6f89b74643b4b8f282a8aa5da..b4ef9684234fe6a42b5fee162da2f5e5e3418850 100644 (file)
@@ -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 @@ static struct symbol_table net_syms = {
        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