Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / block / DAC960.c
diff --git a/kernel/drivers/block/DAC960.c b/kernel/drivers/block/DAC960.c
new file mode 100644 (file)
index 0000000..811e11c
--- /dev/null
@@ -0,0 +1,7241 @@
+/*
+
+  Linux Driver for Mylex DAC960/AcceleRAID/eXtremeRAID PCI RAID Controllers
+
+  Copyright 1998-2001 by Leonard N. Zubkoff <lnz@dandelion.com>
+  Portions Copyright 2002 by Mylex (An IBM Business Unit)
+
+  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.
+
+*/
+
+
+#define DAC960_DriverVersion                   "2.5.49"
+#define DAC960_DriverDate                      "21 Aug 2007"
+
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/blkpg.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/reboot.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/random.h>
+#include <linux/scatterlist.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "DAC960.h"
+
+#define DAC960_GAM_MINOR       252
+
+
+static DEFINE_MUTEX(DAC960_mutex);
+static DAC960_Controller_T *DAC960_Controllers[DAC960_MaxControllers];
+static int DAC960_ControllerCount;
+static struct proc_dir_entry *DAC960_ProcDirectoryEntry;
+
+static long disk_size(DAC960_Controller_T *p, int drive_nr)
+{
+       if (p->FirmwareType == DAC960_V1_Controller) {
+               if (drive_nr >= p->LogicalDriveCount)
+                       return 0;
+               return p->V1.LogicalDriveInformation[drive_nr].
+                       LogicalDriveSize;
+       } else {
+               DAC960_V2_LogicalDeviceInfo_T *i =
+                       p->V2.LogicalDeviceInformation[drive_nr];
+               if (i == NULL)
+                       return 0;
+               return i->ConfigurableDeviceSize;
+       }
+}
+
+static int DAC960_open(struct block_device *bdev, fmode_t mode)
+{
+       struct gendisk *disk = bdev->bd_disk;
+       DAC960_Controller_T *p = disk->queue->queuedata;
+       int drive_nr = (long)disk->private_data;
+       int ret = -ENXIO;
+
+       mutex_lock(&DAC960_mutex);
+       if (p->FirmwareType == DAC960_V1_Controller) {
+               if (p->V1.LogicalDriveInformation[drive_nr].
+                   LogicalDriveState == DAC960_V1_LogicalDrive_Offline)
+                       goto out;
+       } else {
+               DAC960_V2_LogicalDeviceInfo_T *i =
+                       p->V2.LogicalDeviceInformation[drive_nr];
+               if (!i || i->LogicalDeviceState == DAC960_V2_LogicalDevice_Offline)
+                       goto out;
+       }
+
+       check_disk_change(bdev);
+
+       if (!get_capacity(p->disks[drive_nr]))
+               goto out;
+       ret = 0;
+out:
+       mutex_unlock(&DAC960_mutex);
+       return ret;
+}
+
+static int DAC960_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+       struct gendisk *disk = bdev->bd_disk;
+       DAC960_Controller_T *p = disk->queue->queuedata;
+       int drive_nr = (long)disk->private_data;
+
+       if (p->FirmwareType == DAC960_V1_Controller) {
+               geo->heads = p->V1.GeometryTranslationHeads;
+               geo->sectors = p->V1.GeometryTranslationSectors;
+               geo->cylinders = p->V1.LogicalDriveInformation[drive_nr].
+                       LogicalDriveSize / (geo->heads * geo->sectors);
+       } else {
+               DAC960_V2_LogicalDeviceInfo_T *i =
+                       p->V2.LogicalDeviceInformation[drive_nr];
+               switch (i->DriveGeometry) {
+               case DAC960_V2_Geometry_128_32:
+                       geo->heads = 128;
+                       geo->sectors = 32;
+                       break;
+               case DAC960_V2_Geometry_255_63:
+                       geo->heads = 255;
+                       geo->sectors = 63;
+                       break;
+               default:
+                       DAC960_Error("Illegal Logical Device Geometry %d\n",
+                                       p, i->DriveGeometry);
+                       return -EINVAL;
+               }
+
+               geo->cylinders = i->ConfigurableDeviceSize /
+                       (geo->heads * geo->sectors);
+       }
+       
+       return 0;
+}
+
+static unsigned int DAC960_check_events(struct gendisk *disk,
+                                       unsigned int clearing)
+{
+       DAC960_Controller_T *p = disk->queue->queuedata;
+       int drive_nr = (long)disk->private_data;
+
+       if (!p->LogicalDriveInitiallyAccessible[drive_nr])
+               return DISK_EVENT_MEDIA_CHANGE;
+       return 0;
+}
+
+static int DAC960_revalidate_disk(struct gendisk *disk)
+{
+       DAC960_Controller_T *p = disk->queue->queuedata;
+       int unit = (long)disk->private_data;
+
+       set_capacity(disk, disk_size(p, unit));
+       return 0;
+}
+
+static const struct block_device_operations DAC960_BlockDeviceOperations = {
+       .owner                  = THIS_MODULE,
+       .open                   = DAC960_open,
+       .getgeo                 = DAC960_getgeo,
+       .check_events           = DAC960_check_events,
+       .revalidate_disk        = DAC960_revalidate_disk,
+};
+
+
+/*
+  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-2001 by Leonard N. Zubkoff "
+                 "<lnz@dandelion.com>\n", Controller);
+}
+
+
+/*
+  DAC960_Failure prints a standardized error message, and then returns false.
+*/
+
+static bool DAC960_Failure(DAC960_Controller_T *Controller,
+                             unsigned 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;
+}
+
+/*
+  init_dma_loaf() and slice_dma_loaf() are helper functions for
+  aggregating the dma-mapped memory for a well-known collection of
+  data structures that are of different lengths.
+
+  These routines don't guarantee any alignment.  The caller must
+  include any space needed for alignment in the sizes of the structures
+  that are passed in.
+ */
+
+static bool init_dma_loaf(struct pci_dev *dev, struct dma_loaf *loaf,
+                                                                size_t len)
+{
+       void *cpu_addr;
+       dma_addr_t dma_handle;
+
+       cpu_addr = pci_alloc_consistent(dev, len, &dma_handle);
+       if (cpu_addr == NULL)
+               return false;
+       
+       loaf->cpu_free = loaf->cpu_base = cpu_addr;
+       loaf->dma_free =loaf->dma_base = dma_handle;
+       loaf->length = len;
+       memset(cpu_addr, 0, len);
+       return true;
+}
+
+static void *slice_dma_loaf(struct dma_loaf *loaf, size_t len,
+                                       dma_addr_t *dma_handle)
+{
+       void *cpu_end = loaf->cpu_free + len;
+       void *cpu_addr = loaf->cpu_free;
+
+       BUG_ON(cpu_end > loaf->cpu_base + loaf->length);
+       *dma_handle = loaf->dma_free;
+       loaf->cpu_free = cpu_end;
+       loaf->dma_free += len;
+       return cpu_addr;
+}
+
+static void free_dma_loaf(struct pci_dev *dev, struct dma_loaf *loaf_handle)
+{
+       if (loaf_handle->cpu_base != NULL)
+               pci_free_consistent(dev, loaf_handle->length,
+                       loaf_handle->cpu_base, loaf_handle->dma_base);
+}
+
+
+/*
+  DAC960_CreateAuxiliaryStructures allocates and initializes the auxiliary
+  data structures for Controller.  It returns true on success and false on
+  failure.
+*/
+
+static bool DAC960_CreateAuxiliaryStructures(DAC960_Controller_T *Controller)
+{
+  int CommandAllocationLength, CommandAllocationGroupSize;
+  int CommandsRemaining = 0, CommandIdentifier, CommandGroupByteCount;
+  void *AllocationPointer = NULL;
+  void *ScatterGatherCPU = NULL;
+  dma_addr_t ScatterGatherDMA;
+  struct pci_pool *ScatterGatherPool;
+  void *RequestSenseCPU = NULL;
+  dma_addr_t RequestSenseDMA;
+  struct pci_pool *RequestSensePool = NULL;
+
+  if (Controller->FirmwareType == DAC960_V1_Controller)
+    {
+      CommandAllocationLength = offsetof(DAC960_Command_T, V1.EndMarker);
+      CommandAllocationGroupSize = DAC960_V1_CommandAllocationGroupSize;
+      ScatterGatherPool = pci_pool_create("DAC960_V1_ScatterGather",
+               Controller->PCIDevice,
+       DAC960_V1_ScatterGatherLimit * sizeof(DAC960_V1_ScatterGatherSegment_T),
+       sizeof(DAC960_V1_ScatterGatherSegment_T), 0);
+      if (ScatterGatherPool == NULL)
+           return DAC960_Failure(Controller,
+                       "AUXILIARY STRUCTURE CREATION (SG)");
+      Controller->ScatterGatherPool = ScatterGatherPool;
+    }
+  else
+    {
+      CommandAllocationLength = offsetof(DAC960_Command_T, V2.EndMarker);
+      CommandAllocationGroupSize = DAC960_V2_CommandAllocationGroupSize;
+      ScatterGatherPool = pci_pool_create("DAC960_V2_ScatterGather",
+               Controller->PCIDevice,
+       DAC960_V2_ScatterGatherLimit * sizeof(DAC960_V2_ScatterGatherSegment_T),
+       sizeof(DAC960_V2_ScatterGatherSegment_T), 0);
+      if (ScatterGatherPool == NULL)
+           return DAC960_Failure(Controller,
+                       "AUXILIARY STRUCTURE CREATION (SG)");
+      RequestSensePool = pci_pool_create("DAC960_V2_RequestSense",
+               Controller->PCIDevice, sizeof(DAC960_SCSI_RequestSense_T),
+               sizeof(int), 0);
+      if (RequestSensePool == NULL) {
+           pci_pool_destroy(ScatterGatherPool);
+           return DAC960_Failure(Controller,
+                       "AUXILIARY STRUCTURE CREATION (SG)");
+      }
+      Controller->ScatterGatherPool = ScatterGatherPool;
+      Controller->V2.RequestSensePool = RequestSensePool;
+    }
+  Controller->CommandAllocationGroupSize = CommandAllocationGroupSize;
+  Controller->FreeCommands = NULL;
+  for (CommandIdentifier = 1;
+       CommandIdentifier <= Controller->DriverQueueDepth;
+       CommandIdentifier++)
+    {
+      DAC960_Command_T *Command;
+      if (--CommandsRemaining <= 0)
+       {
+         CommandsRemaining =
+               Controller->DriverQueueDepth - CommandIdentifier + 1;
+         if (CommandsRemaining > CommandAllocationGroupSize)
+               CommandsRemaining = CommandAllocationGroupSize;
+         CommandGroupByteCount =
+               CommandsRemaining * CommandAllocationLength;
+         AllocationPointer = kzalloc(CommandGroupByteCount, GFP_ATOMIC);
+         if (AllocationPointer == NULL)
+               return DAC960_Failure(Controller,
+                                       "AUXILIARY STRUCTURE CREATION");
+        }
+      Command = (DAC960_Command_T *) AllocationPointer;
+      AllocationPointer += CommandAllocationLength;
+      Command->CommandIdentifier = CommandIdentifier;
+      Command->Controller = Controller;
+      Command->Next = Controller->FreeCommands;
+      Controller->FreeCommands = Command;
+      Controller->Commands[CommandIdentifier-1] = Command;
+      ScatterGatherCPU = pci_pool_alloc(ScatterGatherPool, GFP_ATOMIC,
+                                                       &ScatterGatherDMA);
+      if (ScatterGatherCPU == NULL)
+         return DAC960_Failure(Controller, "AUXILIARY STRUCTURE CREATION");
+
+      if (RequestSensePool != NULL) {
+         RequestSenseCPU = pci_pool_alloc(RequestSensePool, GFP_ATOMIC,
+                                               &RequestSenseDMA);
+         if (RequestSenseCPU == NULL) {
+                pci_pool_free(ScatterGatherPool, ScatterGatherCPU,
+                                ScatterGatherDMA);
+               return DAC960_Failure(Controller,
+                                       "AUXILIARY STRUCTURE CREATION");
+         }
+        }
+     if (Controller->FirmwareType == DAC960_V1_Controller) {
+        Command->cmd_sglist = Command->V1.ScatterList;
+       Command->V1.ScatterGatherList =
+               (DAC960_V1_ScatterGatherSegment_T *)ScatterGatherCPU;
+       Command->V1.ScatterGatherListDMA = ScatterGatherDMA;
+       sg_init_table(Command->cmd_sglist, DAC960_V1_ScatterGatherLimit);
+      } else {
+        Command->cmd_sglist = Command->V2.ScatterList;
+       Command->V2.ScatterGatherList =
+               (DAC960_V2_ScatterGatherSegment_T *)ScatterGatherCPU;
+       Command->V2.ScatterGatherListDMA = ScatterGatherDMA;
+       Command->V2.RequestSense =
+                               (DAC960_SCSI_RequestSense_T *)RequestSenseCPU;
+       Command->V2.RequestSenseDMA = RequestSenseDMA;
+       sg_init_table(Command->cmd_sglist, DAC960_V2_ScatterGatherLimit);
+      }
+    }
+  return true;
+}
+
+
+/*
+  DAC960_DestroyAuxiliaryStructures deallocates the auxiliary data
+  structures for Controller.
+*/
+
+static void DAC960_DestroyAuxiliaryStructures(DAC960_Controller_T *Controller)
+{
+  int i;
+  struct pci_pool *ScatterGatherPool = Controller->ScatterGatherPool;
+  struct pci_pool *RequestSensePool = NULL;
+  void *ScatterGatherCPU;
+  dma_addr_t ScatterGatherDMA;
+  void *RequestSenseCPU;
+  dma_addr_t RequestSenseDMA;
+  DAC960_Command_T *CommandGroup = NULL;
+  
+
+  if (Controller->FirmwareType == DAC960_V2_Controller)
+        RequestSensePool = Controller->V2.RequestSensePool;
+
+  Controller->FreeCommands = NULL;
+  for (i = 0; i < Controller->DriverQueueDepth; i++)
+    {
+      DAC960_Command_T *Command = Controller->Commands[i];
+
+      if (Command == NULL)
+         continue;
+
+      if (Controller->FirmwareType == DAC960_V1_Controller) {
+         ScatterGatherCPU = (void *)Command->V1.ScatterGatherList;
+         ScatterGatherDMA = Command->V1.ScatterGatherListDMA;
+         RequestSenseCPU = NULL;
+         RequestSenseDMA = (dma_addr_t)0;
+      } else {
+          ScatterGatherCPU = (void *)Command->V2.ScatterGatherList;
+         ScatterGatherDMA = Command->V2.ScatterGatherListDMA;
+         RequestSenseCPU = (void *)Command->V2.RequestSense;
+         RequestSenseDMA = Command->V2.RequestSenseDMA;
+      }
+      if (ScatterGatherCPU != NULL)
+          pci_pool_free(ScatterGatherPool, ScatterGatherCPU, ScatterGatherDMA);
+      if (RequestSenseCPU != NULL)
+          pci_pool_free(RequestSensePool, RequestSenseCPU, RequestSenseDMA);
+
+      if ((Command->CommandIdentifier
+          % Controller->CommandAllocationGroupSize) == 1) {
+          /*
+           * We can't free the group of commands until all of the
+           * request sense and scatter gather dma structures are free.
+            * Remember the beginning of the group, but don't free it
+           * until we've reached the beginning of the next group.
+           */
+          kfree(CommandGroup);
+          CommandGroup = Command;
+      }
+      Controller->Commands[i] = NULL;
+    }
+  kfree(CommandGroup);
+
+  if (Controller->CombinedStatusBuffer != NULL)
+    {
+      kfree(Controller->CombinedStatusBuffer);
+      Controller->CombinedStatusBuffer = NULL;
+      Controller->CurrentStatusBuffer = NULL;
+    }
+
+  if (ScatterGatherPool != NULL)
+       pci_pool_destroy(ScatterGatherPool);
+  if (Controller->FirmwareType == DAC960_V1_Controller)
+       return;
+
+  if (RequestSensePool != NULL)
+       pci_pool_destroy(RequestSensePool);
+
+  for (i = 0; i < DAC960_MaxLogicalDrives; i++) {
+       kfree(Controller->V2.LogicalDeviceInformation[i]);
+       Controller->V2.LogicalDeviceInformation[i] = NULL;
+  }
+
+  for (i = 0; i < DAC960_V2_MaxPhysicalDevices; i++)
+    {
+      kfree(Controller->V2.PhysicalDeviceInformation[i]);
+      Controller->V2.PhysicalDeviceInformation[i] = NULL;
+      kfree(Controller->V2.InquiryUnitSerialNumber[i]);
+      Controller->V2.InquiryUnitSerialNumber[i] = NULL;
+    }
+}
+
+
+/*
+  DAC960_V1_ClearCommand clears critical fields of Command for DAC960 V1
+  Firmware Controllers.
+*/
+
+static inline void DAC960_V1_ClearCommand(DAC960_Command_T *Command)
+{
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  memset(CommandMailbox, 0, sizeof(DAC960_V1_CommandMailbox_T));
+  Command->V1.CommandStatus = 0;
+}
+
+
+/*
+  DAC960_V2_ClearCommand clears critical fields of Command for DAC960 V2
+  Firmware Controllers.
+*/
+
+static inline void DAC960_V2_ClearCommand(DAC960_Command_T *Command)
+{
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  memset(CommandMailbox, 0, sizeof(DAC960_V2_CommandMailbox_T));
+  Command->V2.CommandStatus = 0;
+}
+
+
+/*
+  DAC960_AllocateCommand allocates a Command structure from Controller's
+  free list.  During driver initialization, a special initialization command
+  has been placed on the free list to guarantee that command allocation can
+  never fail.
+*/
+
+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->Request = NULL;
+  Command->Next = Controller->FreeCommands;
+  Controller->FreeCommands = Command;
+}
+
+
+/*
+  DAC960_WaitForCommand waits for a wake_up on Controller's Command Wait Queue.
+*/
+
+static void DAC960_WaitForCommand(DAC960_Controller_T *Controller)
+{
+  spin_unlock_irq(&Controller->queue_lock);
+  __wait_event(Controller->CommandWaitQueue, Controller->FreeCommands);
+  spin_lock_irq(&Controller->queue_lock);
+}
+
+/*
+  DAC960_GEM_QueueCommand queues Command for DAC960 GEM Series Controllers.
+*/
+
+static void DAC960_GEM_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandMailbox_T *NextCommandMailbox =
+      Controller->V2.NextCommandMailbox;
+
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  DAC960_GEM_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+
+  if (Controller->V2.PreviousCommandMailbox1->Words[0] == 0 ||
+      Controller->V2.PreviousCommandMailbox2->Words[0] == 0)
+      DAC960_GEM_MemoryMailboxNewCommand(ControllerBaseAddress);
+
+  Controller->V2.PreviousCommandMailbox2 =
+      Controller->V2.PreviousCommandMailbox1;
+  Controller->V2.PreviousCommandMailbox1 = NextCommandMailbox;
+
+  if (++NextCommandMailbox > Controller->V2.LastCommandMailbox)
+      NextCommandMailbox = Controller->V2.FirstCommandMailbox;
+
+  Controller->V2.NextCommandMailbox = NextCommandMailbox;
+}
+
+/*
+  DAC960_BA_QueueCommand queues Command for DAC960 BA Series Controllers.
+*/
+
+static void DAC960_BA_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandMailbox_T *NextCommandMailbox =
+    Controller->V2.NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  DAC960_BA_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+  if (Controller->V2.PreviousCommandMailbox1->Words[0] == 0 ||
+      Controller->V2.PreviousCommandMailbox2->Words[0] == 0)
+    DAC960_BA_MemoryMailboxNewCommand(ControllerBaseAddress);
+  Controller->V2.PreviousCommandMailbox2 =
+    Controller->V2.PreviousCommandMailbox1;
+  Controller->V2.PreviousCommandMailbox1 = NextCommandMailbox;
+  if (++NextCommandMailbox > Controller->V2.LastCommandMailbox)
+    NextCommandMailbox = Controller->V2.FirstCommandMailbox;
+  Controller->V2.NextCommandMailbox = NextCommandMailbox;
+}
+
+
+/*
+  DAC960_LP_QueueCommand queues Command for DAC960 LP Series Controllers.
+*/
+
+static void DAC960_LP_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandMailbox_T *NextCommandMailbox =
+    Controller->V2.NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  DAC960_LP_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+  if (Controller->V2.PreviousCommandMailbox1->Words[0] == 0 ||
+      Controller->V2.PreviousCommandMailbox2->Words[0] == 0)
+    DAC960_LP_MemoryMailboxNewCommand(ControllerBaseAddress);
+  Controller->V2.PreviousCommandMailbox2 =
+    Controller->V2.PreviousCommandMailbox1;
+  Controller->V2.PreviousCommandMailbox1 = NextCommandMailbox;
+  if (++NextCommandMailbox > Controller->V2.LastCommandMailbox)
+    NextCommandMailbox = Controller->V2.FirstCommandMailbox;
+  Controller->V2.NextCommandMailbox = NextCommandMailbox;
+}
+
+
+/*
+  DAC960_LA_QueueCommandDualMode queues Command for DAC960 LA Series
+  Controllers with Dual Mode Firmware.
+*/
+
+static void DAC960_LA_QueueCommandDualMode(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
+    Controller->V1.NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  DAC960_LA_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
+      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
+    DAC960_LA_MemoryMailboxNewCommand(ControllerBaseAddress);
+  Controller->V1.PreviousCommandMailbox2 =
+    Controller->V1.PreviousCommandMailbox1;
+  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
+  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
+    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
+  Controller->V1.NextCommandMailbox = NextCommandMailbox;
+}
+
+
+/*
+  DAC960_LA_QueueCommandSingleMode queues Command for DAC960 LA Series
+  Controllers with Single Mode Firmware.
+*/
+
+static void DAC960_LA_QueueCommandSingleMode(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
+    Controller->V1.NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  DAC960_LA_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
+      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
+    DAC960_LA_HardwareMailboxNewCommand(ControllerBaseAddress);
+  Controller->V1.PreviousCommandMailbox2 =
+    Controller->V1.PreviousCommandMailbox1;
+  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
+  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
+    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
+  Controller->V1.NextCommandMailbox = NextCommandMailbox;
+}
+
+
+/*
+  DAC960_PG_QueueCommandDualMode queues Command for DAC960 PG Series
+  Controllers with Dual Mode Firmware.
+*/
+
+static void DAC960_PG_QueueCommandDualMode(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
+    Controller->V1.NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  DAC960_PG_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
+      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
+    DAC960_PG_MemoryMailboxNewCommand(ControllerBaseAddress);
+  Controller->V1.PreviousCommandMailbox2 =
+    Controller->V1.PreviousCommandMailbox1;
+  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
+  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
+    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
+  Controller->V1.NextCommandMailbox = NextCommandMailbox;
+}
+
+
+/*
+  DAC960_PG_QueueCommandSingleMode queues Command for DAC960 PG Series
+  Controllers with Single Mode Firmware.
+*/
+
+static void DAC960_PG_QueueCommandSingleMode(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_CommandMailbox_T *NextCommandMailbox =
+    Controller->V1.NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  DAC960_PG_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+  if (Controller->V1.PreviousCommandMailbox1->Words[0] == 0 ||
+      Controller->V1.PreviousCommandMailbox2->Words[0] == 0)
+    DAC960_PG_HardwareMailboxNewCommand(ControllerBaseAddress);
+  Controller->V1.PreviousCommandMailbox2 =
+    Controller->V1.PreviousCommandMailbox1;
+  Controller->V1.PreviousCommandMailbox1 = NextCommandMailbox;
+  if (++NextCommandMailbox > Controller->V1.LastCommandMailbox)
+    NextCommandMailbox = Controller->V1.FirstCommandMailbox;
+  Controller->V1.NextCommandMailbox = NextCommandMailbox;
+}
+
+
+/*
+  DAC960_PD_QueueCommand queues Command for DAC960 PD Series Controllers.
+*/
+
+static void DAC960_PD_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  while (DAC960_PD_MailboxFullP(ControllerBaseAddress))
+    udelay(1);
+  DAC960_PD_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
+  DAC960_PD_NewCommand(ControllerBaseAddress);
+}
+
+
+/*
+  DAC960_P_QueueCommand queues Command for DAC960 P Series Controllers.
+*/
+
+static void DAC960_P_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command->CommandIdentifier;
+  switch (CommandMailbox->Common.CommandOpcode)
+    {
+    case DAC960_V1_Enquiry:
+      CommandMailbox->Common.CommandOpcode = DAC960_V1_Enquiry_Old;
+      break;
+    case DAC960_V1_GetDeviceState:
+      CommandMailbox->Common.CommandOpcode = DAC960_V1_GetDeviceState_Old;
+      break;
+    case DAC960_V1_Read:
+      CommandMailbox->Common.CommandOpcode = DAC960_V1_Read_Old;
+      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+      break;
+    case DAC960_V1_Write:
+      CommandMailbox->Common.CommandOpcode = DAC960_V1_Write_Old;
+      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+      break;
+    case DAC960_V1_ReadWithScatterGather:
+      CommandMailbox->Common.CommandOpcode =
+       DAC960_V1_ReadWithScatterGather_Old;
+      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+      break;
+    case DAC960_V1_WriteWithScatterGather:
+      CommandMailbox->Common.CommandOpcode =
+       DAC960_V1_WriteWithScatterGather_Old;
+      DAC960_PD_To_P_TranslateReadWriteCommand(CommandMailbox);
+      break;
+    default:
+      break;
+    }
+  while (DAC960_PD_MailboxFullP(ControllerBaseAddress))
+    udelay(1);
+  DAC960_PD_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
+  DAC960_PD_NewCommand(ControllerBaseAddress);
+}
+
+
+/*
+  DAC960_ExecuteCommand executes Command and waits for completion.
+*/
+
+static void DAC960_ExecuteCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DECLARE_COMPLETION_ONSTACK(Completion);
+  unsigned long flags;
+  Command->Completion = &Completion;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_QueueCommand(Command);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  if (in_interrupt())
+         return;
+  wait_for_completion(&Completion);
+}
+
+
+/*
+  DAC960_V1_ExecuteType3 executes a DAC960 V1 Firmware Controller Type 3
+  Command and waits for completion.  It returns true on success and false
+  on failure.
+*/
+
+static bool DAC960_V1_ExecuteType3(DAC960_Controller_T *Controller,
+                                     DAC960_V1_CommandOpcode_T CommandOpcode,
+                                     dma_addr_t DataDMA)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_CommandStatus_T CommandStatus;
+  DAC960_V1_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3.BusAddress = DataDMA;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V1.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V1_NormalCompletion);
+}
+
+
+/*
+  DAC960_V1_ExecuteTypeB executes a DAC960 V1 Firmware Controller Type 3B
+  Command and waits for completion.  It returns true on success and false
+  on failure.
+*/
+
+static bool DAC960_V1_ExecuteType3B(DAC960_Controller_T *Controller,
+                                      DAC960_V1_CommandOpcode_T CommandOpcode,
+                                      unsigned char CommandOpcode2,
+                                      dma_addr_t DataDMA)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_CommandStatus_T CommandStatus;
+  DAC960_V1_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3B.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3B.CommandOpcode2 = CommandOpcode2;
+  CommandMailbox->Type3B.BusAddress = DataDMA;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V1.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V1_NormalCompletion);
+}
+
+
+/*
+  DAC960_V1_ExecuteType3D executes a DAC960 V1 Firmware Controller Type 3D
+  Command and waits for completion.  It returns true on success and false
+  on failure.
+*/
+
+static bool DAC960_V1_ExecuteType3D(DAC960_Controller_T *Controller,
+                                      DAC960_V1_CommandOpcode_T CommandOpcode,
+                                      unsigned char Channel,
+                                      unsigned char TargetID,
+                                      dma_addr_t DataDMA)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_CommandStatus_T CommandStatus;
+  DAC960_V1_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3D.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.BusAddress = DataDMA;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V1.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V1_NormalCompletion);
+}
+
+
+/*
+  DAC960_V2_GeneralInfo executes a DAC960 V2 Firmware General Information
+  Reading IOCTL Command and waits for completion.  It returns true on success
+  and false on failure.
+
+  Return data in The controller's HealthStatusBuffer, which is dma-able memory
+*/
+
+static bool DAC960_V2_GeneralInfo(DAC960_Controller_T *Controller)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandStatus_T CommandStatus;
+  DAC960_V2_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->Common.CommandControlBits
+                       .DataTransferControllerToHost = true;
+  CommandMailbox->Common.CommandControlBits
+                       .NoAutoRequestSense = true;
+  CommandMailbox->Common.DataTransferSize = sizeof(DAC960_V2_HealthStatusBuffer_T);
+  CommandMailbox->Common.IOCTL_Opcode = DAC960_V2_GetHealthStatus;
+  CommandMailbox->Common.DataTransferMemoryAddress
+                       .ScatterGatherSegments[0]
+                       .SegmentDataPointer =
+    Controller->V2.HealthStatusBufferDMA;
+  CommandMailbox->Common.DataTransferMemoryAddress
+                       .ScatterGatherSegments[0]
+                       .SegmentByteCount =
+    CommandMailbox->Common.DataTransferSize;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V2.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+/*
+  DAC960_V2_ControllerInfo executes a DAC960 V2 Firmware Controller
+  Information Reading IOCTL Command and waits for completion.  It returns
+  true on success and false on failure.
+
+  Data is returned in the controller's V2.NewControllerInformation dma-able
+  memory buffer.
+*/
+
+static bool DAC960_V2_NewControllerInfo(DAC960_Controller_T *Controller)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandStatus_T CommandStatus;
+  DAC960_V2_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->ControllerInfo.CommandControlBits
+                               .DataTransferControllerToHost = true;
+  CommandMailbox->ControllerInfo.CommandControlBits
+                               .NoAutoRequestSense = true;
+  CommandMailbox->ControllerInfo.DataTransferSize = sizeof(DAC960_V2_ControllerInfo_T);
+  CommandMailbox->ControllerInfo.ControllerNumber = 0;
+  CommandMailbox->ControllerInfo.IOCTL_Opcode = DAC960_V2_GetControllerInfo;
+  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+                               .ScatterGatherSegments[0]
+                               .SegmentDataPointer =
+       Controller->V2.NewControllerInformationDMA;
+  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+                               .ScatterGatherSegments[0]
+                               .SegmentByteCount =
+    CommandMailbox->ControllerInfo.DataTransferSize;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V2.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+/*
+  DAC960_V2_LogicalDeviceInfo executes a DAC960 V2 Firmware Controller Logical
+  Device Information Reading IOCTL Command and waits for completion.  It
+  returns true on success and false on failure.
+
+  Data is returned in the controller's V2.NewLogicalDeviceInformation
+*/
+
+static bool DAC960_V2_NewLogicalDeviceInfo(DAC960_Controller_T *Controller,
+                                          unsigned short LogicalDeviceNumber)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandStatus_T CommandStatus;
+
+  DAC960_V2_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->LogicalDeviceInfo.CommandOpcode =
+                               DAC960_V2_IOCTL;
+  CommandMailbox->LogicalDeviceInfo.CommandControlBits
+                                  .DataTransferControllerToHost = true;
+  CommandMailbox->LogicalDeviceInfo.CommandControlBits
+                                  .NoAutoRequestSense = true;
+  CommandMailbox->LogicalDeviceInfo.DataTransferSize = 
+                               sizeof(DAC960_V2_LogicalDeviceInfo_T);
+  CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
+    LogicalDeviceNumber;
+  CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode = DAC960_V2_GetLogicalDeviceInfoValid;
+  CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
+                                  .ScatterGatherSegments[0]
+                                  .SegmentDataPointer =
+       Controller->V2.NewLogicalDeviceInformationDMA;
+  CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
+                                  .ScatterGatherSegments[0]
+                                  .SegmentByteCount =
+    CommandMailbox->LogicalDeviceInfo.DataTransferSize;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V2.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+/*
+  DAC960_V2_PhysicalDeviceInfo executes a DAC960 V2 Firmware Controller "Read
+  Physical Device Information" IOCTL Command and waits for completion.  It
+  returns true on success and false on failure.
+
+  The Channel, TargetID, LogicalUnit arguments should be 0 the first time
+  this function is called for a given controller.  This will return data
+  for the "first" device on that controller.  The returned data includes a
+  Channel, TargetID, LogicalUnit that can be passed in to this routine to
+  get data for the NEXT device on that controller.
+
+  Data is stored in the controller's V2.NewPhysicalDeviceInfo dma-able
+  memory buffer.
+
+*/
+
+static bool DAC960_V2_NewPhysicalDeviceInfo(DAC960_Controller_T *Controller,
+                                           unsigned char Channel,
+                                           unsigned char TargetID,
+                                           unsigned char LogicalUnit)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandStatus_T CommandStatus;
+
+  DAC960_V2_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
+                                   .DataTransferControllerToHost = true;
+  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
+                                   .NoAutoRequestSense = true;
+  CommandMailbox->PhysicalDeviceInfo.DataTransferSize =
+                               sizeof(DAC960_V2_PhysicalDeviceInfo_T);
+  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit = LogicalUnit;
+  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID;
+  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel;
+  CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode =
+                                       DAC960_V2_GetPhysicalDeviceInfoValid;
+  CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
+                                   .ScatterGatherSegments[0]
+                                   .SegmentDataPointer =
+                                       Controller->V2.NewPhysicalDeviceInformationDMA;
+  CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
+                                   .ScatterGatherSegments[0]
+                                   .SegmentByteCount =
+    CommandMailbox->PhysicalDeviceInfo.DataTransferSize;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V2.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+static void DAC960_V2_ConstructNewUnitSerialNumber(
+       DAC960_Controller_T *Controller,
+       DAC960_V2_CommandMailbox_T *CommandMailbox, int Channel, int TargetID,
+       int LogicalUnit)
+{
+      CommandMailbox->SCSI_10.CommandOpcode = DAC960_V2_SCSI_10_Passthru;
+      CommandMailbox->SCSI_10.CommandControlBits
+                            .DataTransferControllerToHost = true;
+      CommandMailbox->SCSI_10.CommandControlBits
+                            .NoAutoRequestSense = true;
+      CommandMailbox->SCSI_10.DataTransferSize =
+       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
+      CommandMailbox->SCSI_10.PhysicalDevice.LogicalUnit = LogicalUnit;
+      CommandMailbox->SCSI_10.PhysicalDevice.TargetID = TargetID;
+      CommandMailbox->SCSI_10.PhysicalDevice.Channel = Channel;
+      CommandMailbox->SCSI_10.CDBLength = 6;
+      CommandMailbox->SCSI_10.SCSI_CDB[0] = 0x12; /* INQUIRY */
+      CommandMailbox->SCSI_10.SCSI_CDB[1] = 1; /* EVPD = 1 */
+      CommandMailbox->SCSI_10.SCSI_CDB[2] = 0x80; /* Page Code */
+      CommandMailbox->SCSI_10.SCSI_CDB[3] = 0; /* Reserved */
+      CommandMailbox->SCSI_10.SCSI_CDB[4] =
+       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
+      CommandMailbox->SCSI_10.SCSI_CDB[5] = 0; /* Control */
+      CommandMailbox->SCSI_10.DataTransferMemoryAddress
+                            .ScatterGatherSegments[0]
+                            .SegmentDataPointer =
+               Controller->V2.NewInquiryUnitSerialNumberDMA;
+      CommandMailbox->SCSI_10.DataTransferMemoryAddress
+                            .ScatterGatherSegments[0]
+                            .SegmentByteCount =
+               CommandMailbox->SCSI_10.DataTransferSize;
+}
+
+
+/*
+  DAC960_V2_NewUnitSerialNumber executes an SCSI pass-through
+  Inquiry command to a SCSI device identified by Channel number,
+  Target id, Logical Unit Number.  This function Waits for completion
+  of the command.
+
+  The return data includes Unit Serial Number information for the
+  specified device.
+
+  Data is stored in the controller's V2.NewPhysicalDeviceInfo dma-able
+  memory buffer.
+*/
+
+static bool DAC960_V2_NewInquiryUnitSerialNumber(DAC960_Controller_T *Controller,
+                       int Channel, int TargetID, int LogicalUnit)
+{
+      DAC960_Command_T *Command;
+      DAC960_V2_CommandMailbox_T *CommandMailbox;
+      DAC960_V2_CommandStatus_T CommandStatus;
+
+      Command = DAC960_AllocateCommand(Controller);
+      CommandMailbox = &Command->V2.CommandMailbox;
+      DAC960_V2_ClearCommand(Command);
+      Command->CommandType = DAC960_ImmediateCommand;
+
+      DAC960_V2_ConstructNewUnitSerialNumber(Controller, CommandMailbox,
+                       Channel, TargetID, LogicalUnit);
+
+      DAC960_ExecuteCommand(Command);
+      CommandStatus = Command->V2.CommandStatus;
+      DAC960_DeallocateCommand(Command);
+      return (CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+/*
+  DAC960_V2_DeviceOperation executes a DAC960 V2 Firmware Controller Device
+  Operation IOCTL Command and waits for completion.  It returns true on
+  success and false on failure.
+*/
+
+static bool DAC960_V2_DeviceOperation(DAC960_Controller_T *Controller,
+                                        DAC960_V2_IOCTL_Opcode_T IOCTL_Opcode,
+                                        DAC960_V2_OperationDevice_T
+                                          OperationDevice)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_CommandStatus_T CommandStatus;
+  DAC960_V2_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->DeviceOperation.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->DeviceOperation.CommandControlBits
+                                .DataTransferControllerToHost = true;
+  CommandMailbox->DeviceOperation.CommandControlBits
+                                .NoAutoRequestSense = true;
+  CommandMailbox->DeviceOperation.IOCTL_Opcode = IOCTL_Opcode;
+  CommandMailbox->DeviceOperation.OperationDevice = OperationDevice;
+  DAC960_ExecuteCommand(Command);
+  CommandStatus = Command->V2.CommandStatus;
+  DAC960_DeallocateCommand(Command);
+  return (CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+/*
+  DAC960_V1_EnableMemoryMailboxInterface enables the Memory Mailbox Interface
+  for DAC960 V1 Firmware Controllers.
+
+  PD and P controller types have no memory mailbox, but still need the
+  other dma mapped memory.
+*/
+
+static bool DAC960_V1_EnableMemoryMailboxInterface(DAC960_Controller_T
+                                                     *Controller)
+{
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_HardwareType_T hw_type = Controller->HardwareType;
+  struct pci_dev *PCI_Device = Controller->PCIDevice;
+  struct dma_loaf *DmaPages = &Controller->DmaPages;
+  size_t DmaPagesSize;
+  size_t CommandMailboxesSize;
+  size_t StatusMailboxesSize;
+
+  DAC960_V1_CommandMailbox_T *CommandMailboxesMemory;
+  dma_addr_t CommandMailboxesMemoryDMA;
+
+  DAC960_V1_StatusMailbox_T *StatusMailboxesMemory;
+  dma_addr_t StatusMailboxesMemoryDMA;
+
+  DAC960_V1_CommandMailbox_T CommandMailbox;
+  DAC960_V1_CommandStatus_T CommandStatus;
+  int TimeoutCounter;
+  int i;
+
+  memset(&CommandMailbox, 0, sizeof(DAC960_V1_CommandMailbox_T));
+
+  if (pci_set_dma_mask(Controller->PCIDevice, DMA_BIT_MASK(32)))
+       return DAC960_Failure(Controller, "DMA mask out of range");
+  Controller->BounceBufferLimit = DMA_BIT_MASK(32);
+
+  if ((hw_type == DAC960_PD_Controller) || (hw_type == DAC960_P_Controller)) {
+    CommandMailboxesSize =  0;
+    StatusMailboxesSize = 0;
+  } else {
+    CommandMailboxesSize =  DAC960_V1_CommandMailboxCount * sizeof(DAC960_V1_CommandMailbox_T);
+    StatusMailboxesSize = DAC960_V1_StatusMailboxCount * sizeof(DAC960_V1_StatusMailbox_T);
+  }
+  DmaPagesSize = CommandMailboxesSize + StatusMailboxesSize + 
+       sizeof(DAC960_V1_DCDB_T) + sizeof(DAC960_V1_Enquiry_T) +
+       sizeof(DAC960_V1_ErrorTable_T) + sizeof(DAC960_V1_EventLogEntry_T) +
+       sizeof(DAC960_V1_RebuildProgress_T) +
+       sizeof(DAC960_V1_LogicalDriveInformationArray_T) +
+       sizeof(DAC960_V1_BackgroundInitializationStatus_T) +
+       sizeof(DAC960_V1_DeviceState_T) + sizeof(DAC960_SCSI_Inquiry_T) +
+       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
+
+  if (!init_dma_loaf(PCI_Device, DmaPages, DmaPagesSize))
+       return false;
+
+
+  if ((hw_type == DAC960_PD_Controller) || (hw_type == DAC960_P_Controller)) 
+       goto skip_mailboxes;
+
+  CommandMailboxesMemory = slice_dma_loaf(DmaPages,
+                CommandMailboxesSize, &CommandMailboxesMemoryDMA);
+  
+  /* These are the base addresses for the command memory mailbox array */
+  Controller->V1.FirstCommandMailbox = CommandMailboxesMemory;
+  Controller->V1.FirstCommandMailboxDMA = CommandMailboxesMemoryDMA;
+
+  CommandMailboxesMemory += DAC960_V1_CommandMailboxCount - 1;
+  Controller->V1.LastCommandMailbox = CommandMailboxesMemory;
+  Controller->V1.NextCommandMailbox = Controller->V1.FirstCommandMailbox;
+  Controller->V1.PreviousCommandMailbox1 = Controller->V1.LastCommandMailbox;
+  Controller->V1.PreviousCommandMailbox2 =
+                                       Controller->V1.LastCommandMailbox - 1;
+
+  /* These are the base addresses for the status memory mailbox array */
+  StatusMailboxesMemory = slice_dma_loaf(DmaPages,
+                StatusMailboxesSize, &StatusMailboxesMemoryDMA);
+
+  Controller->V1.FirstStatusMailbox = StatusMailboxesMemory;
+  Controller->V1.FirstStatusMailboxDMA = StatusMailboxesMemoryDMA;
+  StatusMailboxesMemory += DAC960_V1_StatusMailboxCount - 1;
+  Controller->V1.LastStatusMailbox = StatusMailboxesMemory;
+  Controller->V1.NextStatusMailbox = Controller->V1.FirstStatusMailbox;
+
+skip_mailboxes:
+  Controller->V1.MonitoringDCDB = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_DCDB_T),
+                &Controller->V1.MonitoringDCDB_DMA);
+
+  Controller->V1.NewEnquiry = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_Enquiry_T),
+                &Controller->V1.NewEnquiryDMA);
+
+  Controller->V1.NewErrorTable = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_ErrorTable_T),
+                &Controller->V1.NewErrorTableDMA);
+
+  Controller->V1.EventLogEntry = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_EventLogEntry_T),
+                &Controller->V1.EventLogEntryDMA);
+
+  Controller->V1.RebuildProgress = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_RebuildProgress_T),
+                &Controller->V1.RebuildProgressDMA);
+
+  Controller->V1.NewLogicalDriveInformation = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_LogicalDriveInformationArray_T),
+                &Controller->V1.NewLogicalDriveInformationDMA);
+
+  Controller->V1.BackgroundInitializationStatus = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_BackgroundInitializationStatus_T),
+                &Controller->V1.BackgroundInitializationStatusDMA);
+
+  Controller->V1.NewDeviceState = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V1_DeviceState_T),
+                &Controller->V1.NewDeviceStateDMA);
+
+  Controller->V1.NewInquiryStandardData = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_SCSI_Inquiry_T),
+                &Controller->V1.NewInquiryStandardDataDMA);
+
+  Controller->V1.NewInquiryUnitSerialNumber = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
+                &Controller->V1.NewInquiryUnitSerialNumberDMA);
+
+  if ((hw_type == DAC960_PD_Controller) || (hw_type == DAC960_P_Controller))
+       return true;
+  /* Enable the Memory Mailbox Interface. */
+  Controller->V1.DualModeMemoryMailboxInterface = true;
+  CommandMailbox.TypeX.CommandOpcode = 0x2B;
+  CommandMailbox.TypeX.CommandIdentifier = 0;
+  CommandMailbox.TypeX.CommandOpcode2 = 0x14;
+  CommandMailbox.TypeX.CommandMailboxesBusAddress =
+                               Controller->V1.FirstCommandMailboxDMA;
+  CommandMailbox.TypeX.StatusMailboxesBusAddress =
+                               Controller->V1.FirstStatusMailboxDMA;
+#define TIMEOUT_COUNT 1000000
+
+  for (i = 0; i < 2; i++)
+    switch (Controller->HardwareType)
+      {
+      case DAC960_LA_Controller:
+       TimeoutCounter = TIMEOUT_COUNT;
+       while (--TimeoutCounter >= 0)
+         {
+           if (!DAC960_LA_HardwareMailboxFullP(ControllerBaseAddress))
+             break;
+           udelay(10);
+         }
+       if (TimeoutCounter < 0) return false;
+       DAC960_LA_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+       DAC960_LA_HardwareMailboxNewCommand(ControllerBaseAddress);
+       TimeoutCounter = TIMEOUT_COUNT;
+       while (--TimeoutCounter >= 0)
+         {
+           if (DAC960_LA_HardwareMailboxStatusAvailableP(
+                 ControllerBaseAddress))
+             break;
+           udelay(10);
+         }
+       if (TimeoutCounter < 0) return false;
+       CommandStatus = DAC960_LA_ReadStatusRegister(ControllerBaseAddress);
+       DAC960_LA_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+       DAC960_LA_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+       if (CommandStatus == DAC960_V1_NormalCompletion) return true;
+       Controller->V1.DualModeMemoryMailboxInterface = false;
+       CommandMailbox.TypeX.CommandOpcode2 = 0x10;
+       break;
+      case DAC960_PG_Controller:
+       TimeoutCounter = TIMEOUT_COUNT;
+       while (--TimeoutCounter >= 0)
+         {
+           if (!DAC960_PG_HardwareMailboxFullP(ControllerBaseAddress))
+             break;
+           udelay(10);
+         }
+       if (TimeoutCounter < 0) return false;
+       DAC960_PG_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+       DAC960_PG_HardwareMailboxNewCommand(ControllerBaseAddress);
+
+       TimeoutCounter = TIMEOUT_COUNT;
+       while (--TimeoutCounter >= 0)
+         {
+           if (DAC960_PG_HardwareMailboxStatusAvailableP(
+                 ControllerBaseAddress))
+             break;
+           udelay(10);
+         }
+       if (TimeoutCounter < 0) return false;
+       CommandStatus = DAC960_PG_ReadStatusRegister(ControllerBaseAddress);
+       DAC960_PG_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+       DAC960_PG_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+       if (CommandStatus == DAC960_V1_NormalCompletion) return true;
+       Controller->V1.DualModeMemoryMailboxInterface = false;
+       CommandMailbox.TypeX.CommandOpcode2 = 0x10;
+       break;
+      default:
+        DAC960_Failure(Controller, "Unknown Controller Type\n");
+       break;
+      }
+  return false;
+}
+
+
+/*
+  DAC960_V2_EnableMemoryMailboxInterface enables the Memory Mailbox Interface
+  for DAC960 V2 Firmware Controllers.
+
+  Aggregate the space needed for the controller's memory mailbox and
+  the other data structures that will be targets of dma transfers with
+  the controller.  Allocate a dma-mapped region of memory to hold these
+  structures.  Then, save CPU pointers and dma_addr_t values to reference
+  the structures that are contained in that region.
+*/
+
+static bool DAC960_V2_EnableMemoryMailboxInterface(DAC960_Controller_T
+                                                     *Controller)
+{
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  struct pci_dev *PCI_Device = Controller->PCIDevice;
+  struct dma_loaf *DmaPages = &Controller->DmaPages;
+  size_t DmaPagesSize;
+  size_t CommandMailboxesSize;
+  size_t StatusMailboxesSize;
+
+  DAC960_V2_CommandMailbox_T *CommandMailboxesMemory;
+  dma_addr_t CommandMailboxesMemoryDMA;
+
+  DAC960_V2_StatusMailbox_T *StatusMailboxesMemory;
+  dma_addr_t StatusMailboxesMemoryDMA;
+
+  DAC960_V2_CommandMailbox_T *CommandMailbox;
+  dma_addr_t   CommandMailboxDMA;
+  DAC960_V2_CommandStatus_T CommandStatus;
+
+       if (!pci_set_dma_mask(Controller->PCIDevice, DMA_BIT_MASK(64)))
+               Controller->BounceBufferLimit = DMA_BIT_MASK(64);
+       else if (!pci_set_dma_mask(Controller->PCIDevice, DMA_BIT_MASK(32)))
+               Controller->BounceBufferLimit = DMA_BIT_MASK(32);
+       else
+               return DAC960_Failure(Controller, "DMA mask out of range");
+
+  /* This is a temporary dma mapping, used only in the scope of this function */
+  CommandMailbox = pci_alloc_consistent(PCI_Device,
+               sizeof(DAC960_V2_CommandMailbox_T), &CommandMailboxDMA);
+  if (CommandMailbox == NULL)
+         return false;
+
+  CommandMailboxesSize = DAC960_V2_CommandMailboxCount * sizeof(DAC960_V2_CommandMailbox_T);
+  StatusMailboxesSize = DAC960_V2_StatusMailboxCount * sizeof(DAC960_V2_StatusMailbox_T);
+  DmaPagesSize =
+    CommandMailboxesSize + StatusMailboxesSize +
+    sizeof(DAC960_V2_HealthStatusBuffer_T) +
+    sizeof(DAC960_V2_ControllerInfo_T) +
+    sizeof(DAC960_V2_LogicalDeviceInfo_T) +
+    sizeof(DAC960_V2_PhysicalDeviceInfo_T) +
+    sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T) +
+    sizeof(DAC960_V2_Event_T) +
+    sizeof(DAC960_V2_PhysicalToLogicalDevice_T);
+
+  if (!init_dma_loaf(PCI_Device, DmaPages, DmaPagesSize)) {
+       pci_free_consistent(PCI_Device, sizeof(DAC960_V2_CommandMailbox_T),
+                                       CommandMailbox, CommandMailboxDMA);
+       return false;
+  }
+
+  CommandMailboxesMemory = slice_dma_loaf(DmaPages,
+               CommandMailboxesSize, &CommandMailboxesMemoryDMA);
+
+  /* These are the base addresses for the command memory mailbox array */
+  Controller->V2.FirstCommandMailbox = CommandMailboxesMemory;
+  Controller->V2.FirstCommandMailboxDMA = CommandMailboxesMemoryDMA;
+
+  CommandMailboxesMemory += DAC960_V2_CommandMailboxCount - 1;
+  Controller->V2.LastCommandMailbox = CommandMailboxesMemory;
+  Controller->V2.NextCommandMailbox = Controller->V2.FirstCommandMailbox;
+  Controller->V2.PreviousCommandMailbox1 = Controller->V2.LastCommandMailbox;
+  Controller->V2.PreviousCommandMailbox2 =
+                                       Controller->V2.LastCommandMailbox - 1;
+
+  /* These are the base addresses for the status memory mailbox array */
+  StatusMailboxesMemory = slice_dma_loaf(DmaPages,
+               StatusMailboxesSize, &StatusMailboxesMemoryDMA);
+
+  Controller->V2.FirstStatusMailbox = StatusMailboxesMemory;
+  Controller->V2.FirstStatusMailboxDMA = StatusMailboxesMemoryDMA;
+  StatusMailboxesMemory += DAC960_V2_StatusMailboxCount - 1;
+  Controller->V2.LastStatusMailbox = StatusMailboxesMemory;
+  Controller->V2.NextStatusMailbox = Controller->V2.FirstStatusMailbox;
+
+  Controller->V2.HealthStatusBuffer = slice_dma_loaf(DmaPages,
+               sizeof(DAC960_V2_HealthStatusBuffer_T),
+               &Controller->V2.HealthStatusBufferDMA);
+
+  Controller->V2.NewControllerInformation = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V2_ControllerInfo_T), 
+                &Controller->V2.NewControllerInformationDMA);
+
+  Controller->V2.NewLogicalDeviceInformation =  slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V2_LogicalDeviceInfo_T),
+                &Controller->V2.NewLogicalDeviceInformationDMA);
+
+  Controller->V2.NewPhysicalDeviceInformation = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V2_PhysicalDeviceInfo_T),
+                &Controller->V2.NewPhysicalDeviceInformationDMA);
+
+  Controller->V2.NewInquiryUnitSerialNumber = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
+                &Controller->V2.NewInquiryUnitSerialNumberDMA);
+
+  Controller->V2.Event = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V2_Event_T),
+                &Controller->V2.EventDMA);
+
+  Controller->V2.PhysicalToLogicalDevice = slice_dma_loaf(DmaPages,
+                sizeof(DAC960_V2_PhysicalToLogicalDevice_T),
+                &Controller->V2.PhysicalToLogicalDeviceDMA);
+
+  /*
+    Enable the Memory Mailbox Interface.
+    
+    I don't know why we can't just use one of the memory mailboxes
+    we just allocated to do this, instead of using this temporary one.
+    Try this change later.
+  */
+  memset(CommandMailbox, 0, sizeof(DAC960_V2_CommandMailbox_T));
+  CommandMailbox->SetMemoryMailbox.CommandIdentifier = 1;
+  CommandMailbox->SetMemoryMailbox.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->SetMemoryMailbox.CommandControlBits.NoAutoRequestSense = true;
+  CommandMailbox->SetMemoryMailbox.FirstCommandMailboxSizeKB =
+    (DAC960_V2_CommandMailboxCount * sizeof(DAC960_V2_CommandMailbox_T)) >> 10;
+  CommandMailbox->SetMemoryMailbox.FirstStatusMailboxSizeKB =
+    (DAC960_V2_StatusMailboxCount * sizeof(DAC960_V2_StatusMailbox_T)) >> 10;
+  CommandMailbox->SetMemoryMailbox.SecondCommandMailboxSizeKB = 0;
+  CommandMailbox->SetMemoryMailbox.SecondStatusMailboxSizeKB = 0;
+  CommandMailbox->SetMemoryMailbox.RequestSenseSize = 0;
+  CommandMailbox->SetMemoryMailbox.IOCTL_Opcode = DAC960_V2_SetMemoryMailbox;
+  CommandMailbox->SetMemoryMailbox.HealthStatusBufferSizeKB = 1;
+  CommandMailbox->SetMemoryMailbox.HealthStatusBufferBusAddress =
+                                       Controller->V2.HealthStatusBufferDMA;
+  CommandMailbox->SetMemoryMailbox.FirstCommandMailboxBusAddress =
+                                       Controller->V2.FirstCommandMailboxDMA;
+  CommandMailbox->SetMemoryMailbox.FirstStatusMailboxBusAddress =
+                                       Controller->V2.FirstStatusMailboxDMA;
+  switch (Controller->HardwareType)
+    {
+    case DAC960_GEM_Controller:
+      while (DAC960_GEM_HardwareMailboxFullP(ControllerBaseAddress))
+       udelay(1);
+      DAC960_GEM_WriteHardwareMailbox(ControllerBaseAddress, CommandMailboxDMA);
+      DAC960_GEM_HardwareMailboxNewCommand(ControllerBaseAddress);
+      while (!DAC960_GEM_HardwareMailboxStatusAvailableP(ControllerBaseAddress))
+       udelay(1);
+      CommandStatus = DAC960_GEM_ReadCommandStatus(ControllerBaseAddress);
+      DAC960_GEM_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+      DAC960_GEM_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+      break;
+    case DAC960_BA_Controller:
+      while (DAC960_BA_HardwareMailboxFullP(ControllerBaseAddress))
+       udelay(1);
+      DAC960_BA_WriteHardwareMailbox(ControllerBaseAddress, CommandMailboxDMA);
+      DAC960_BA_HardwareMailboxNewCommand(ControllerBaseAddress);
+      while (!DAC960_BA_HardwareMailboxStatusAvailableP(ControllerBaseAddress))
+       udelay(1);
+      CommandStatus = DAC960_BA_ReadCommandStatus(ControllerBaseAddress);
+      DAC960_BA_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+      DAC960_BA_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+      break;
+    case DAC960_LP_Controller:
+      while (DAC960_LP_HardwareMailboxFullP(ControllerBaseAddress))
+       udelay(1);
+      DAC960_LP_WriteHardwareMailbox(ControllerBaseAddress, CommandMailboxDMA);
+      DAC960_LP_HardwareMailboxNewCommand(ControllerBaseAddress);
+      while (!DAC960_LP_HardwareMailboxStatusAvailableP(ControllerBaseAddress))
+       udelay(1);
+      CommandStatus = DAC960_LP_ReadCommandStatus(ControllerBaseAddress);
+      DAC960_LP_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+      DAC960_LP_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+      break;
+    default:
+      DAC960_Failure(Controller, "Unknown Controller Type\n");
+      CommandStatus = DAC960_V2_AbormalCompletion;
+      break;
+    }
+  pci_free_consistent(PCI_Device, sizeof(DAC960_V2_CommandMailbox_T),
+                                       CommandMailbox, CommandMailboxDMA);
+  return (CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+/*
+  DAC960_V1_ReadControllerConfiguration reads the Configuration Information
+  from DAC960 V1 Firmware Controllers and initializes the Controller structure.
+*/
+
+static bool DAC960_V1_ReadControllerConfiguration(DAC960_Controller_T
+                                                    *Controller)
+{
+  DAC960_V1_Enquiry2_T *Enquiry2;
+  dma_addr_t Enquiry2DMA;
+  DAC960_V1_Config2_T *Config2;
+  dma_addr_t Config2DMA;
+  int LogicalDriveNumber, Channel, TargetID;
+  struct dma_loaf local_dma;
+
+  if (!init_dma_loaf(Controller->PCIDevice, &local_dma,
+               sizeof(DAC960_V1_Enquiry2_T) + sizeof(DAC960_V1_Config2_T)))
+       return DAC960_Failure(Controller, "LOGICAL DEVICE ALLOCATION");
+
+  Enquiry2 = slice_dma_loaf(&local_dma, sizeof(DAC960_V1_Enquiry2_T), &Enquiry2DMA);
+  Config2 = slice_dma_loaf(&local_dma, sizeof(DAC960_V1_Config2_T), &Config2DMA);
+
+  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_Enquiry,
+                             Controller->V1.NewEnquiryDMA)) {
+    free_dma_loaf(Controller->PCIDevice, &local_dma);
+    return DAC960_Failure(Controller, "ENQUIRY");
+  }
+  memcpy(&Controller->V1.Enquiry, Controller->V1.NewEnquiry,
+                                               sizeof(DAC960_V1_Enquiry_T));
+
+  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_Enquiry2, Enquiry2DMA)) {
+    free_dma_loaf(Controller->PCIDevice, &local_dma);
+    return DAC960_Failure(Controller, "ENQUIRY2");
+  }
+
+  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_ReadConfig2, Config2DMA)) {
+    free_dma_loaf(Controller->PCIDevice, &local_dma);
+    return DAC960_Failure(Controller, "READ CONFIG2");
+  }
+
+  if (!DAC960_V1_ExecuteType3(Controller, DAC960_V1_GetLogicalDriveInformation,
+                             Controller->V1.NewLogicalDriveInformationDMA)) {
+    free_dma_loaf(Controller->PCIDevice, &local_dma);
+    return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION");
+  }
+  memcpy(&Controller->V1.LogicalDriveInformation,
+               Controller->V1.NewLogicalDriveInformation,
+               sizeof(DAC960_V1_LogicalDriveInformationArray_T));
+
+  for (Channel = 0; Channel < Enquiry2->ActualChannels; Channel++)
+    for (TargetID = 0; TargetID < Enquiry2->MaxTargets; TargetID++) {
+      if (!DAC960_V1_ExecuteType3D(Controller, DAC960_V1_GetDeviceState,
+                                  Channel, TargetID,
+                                  Controller->V1.NewDeviceStateDMA)) {
+               free_dma_loaf(Controller->PCIDevice, &local_dma);
+               return DAC960_Failure(Controller, "GET DEVICE STATE");
+       }
+       memcpy(&Controller->V1.DeviceState[Channel][TargetID],
+               Controller->V1.NewDeviceState, sizeof(DAC960_V1_DeviceState_T));
+     }
+  /*
+    Initialize the Controller Model Name and Full Model Name fields.
+  */
+  switch (Enquiry2->HardwareID.SubModel)
+    {
+    case DAC960_V1_P_PD_PU:
+      if (Enquiry2->SCSICapability.BusSpeed == DAC960_V1_Ultra)
+       strcpy(Controller->ModelName, "DAC960PU");
+      else strcpy(Controller->ModelName, "DAC960PD");
+      break;
+    case DAC960_V1_PL:
+      strcpy(Controller->ModelName, "DAC960PL");
+      break;
+    case DAC960_V1_PG:
+      strcpy(Controller->ModelName, "DAC960PG");
+      break;
+    case DAC960_V1_PJ:
+      strcpy(Controller->ModelName, "DAC960PJ");
+      break;
+    case DAC960_V1_PR:
+      strcpy(Controller->ModelName, "DAC960PR");
+      break;
+    case DAC960_V1_PT:
+      strcpy(Controller->ModelName, "DAC960PT");
+      break;
+    case DAC960_V1_PTL0:
+      strcpy(Controller->ModelName, "DAC960PTL0");
+      break;
+    case DAC960_V1_PRL:
+      strcpy(Controller->ModelName, "DAC960PRL");
+      break;
+    case DAC960_V1_PTL1:
+      strcpy(Controller->ModelName, "DAC960PTL1");
+      break;
+    case DAC960_V1_1164P:
+      strcpy(Controller->ModelName, "DAC1164P");
+      break;
+    default:
+      free_dma_loaf(Controller->PCIDevice, &local_dma);
+      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/PRL/PJ/PG            4.06 and above
+    DAC960PU/PD/PL         3.51 and above
+    DAC960PU/PD/PL/P       2.73 and above
+  */
+#if defined(CONFIG_ALPHA)
+  /*
+    DEC Alpha machines were often equipped with DAC960 cards that were
+    OEMed from Mylex, and had their own custom firmware. Version 2.70,
+    the last custom FW revision to be released by DEC for these older
+    controllers, appears to work quite well with this driver.
+
+    Cards tested successfully were several versions each of the PD and
+    PU, called by DEC the KZPSC and KZPAC, respectively, and having
+    the Manufacturer Numbers (from Mylex), usually on a sticker on the
+    back of the board, of:
+
+    KZPSC:  D040347 (1-channel) or D040348 (2-channel) or D040349 (3-channel)
+    KZPAC:  D040395 (1-channel) or D040396 (2-channel) or D040397 (3-channel)
+  */
+# define FIRMWARE_27X  "2.70"
+#else
+# define FIRMWARE_27X  "2.73"
+#endif
+
+  if (Enquiry2->FirmwareID.MajorVersion == 0)
+    {
+      Enquiry2->FirmwareID.MajorVersion =
+       Controller->V1.Enquiry.MajorFirmwareVersion;
+      Enquiry2->FirmwareID.MinorVersion =
+       Controller->V1.Enquiry.MinorFirmwareVersion;
+      Enquiry2->FirmwareID.FirmwareType = '0';
+      Enquiry2->FirmwareID.TurnID = 0;
+    }
+  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) ||
+       (Controller->FirmwareVersion[0] == '2' &&
+        strcmp(Controller->FirmwareVersion, FIRMWARE_27X) >= 0)))
+    {
+      DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION");
+      DAC960_Error("Firmware Version = '%s'\n", Controller,
+                  Controller->FirmwareVersion);
+      free_dma_loaf(Controller->PCIDevice, &local_dma);
+      return false;
+    }
+  /*
+    Initialize the Controller Channels, Targets, Memory Size, and SAF-TE
+    Enclosure Management Enabled fields.
+  */
+  Controller->Channels = Enquiry2->ActualChannels;
+  Controller->Targets = Enquiry2->MaxTargets;
+  Controller->MemorySize = Enquiry2->MemorySize >> 20;
+  Controller->V1.SAFTE_EnclosureManagementEnabled =
+    (Enquiry2->FaultManagementType == DAC960_V1_SAFTE);
+  /*
+    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
+    Count, Maximum Blocks per Command, Controller Scatter/Gather Limit, and
+    Driver Scatter/Gather Limit.  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->V1.Enquiry.MaxCommands;
+  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
+  if (Controller->DriverQueueDepth > DAC960_MaxDriverQueueDepth)
+    Controller->DriverQueueDepth = DAC960_MaxDriverQueueDepth;
+  Controller->LogicalDriveCount =
+    Controller->V1.Enquiry.NumberOfLogicalDrives;
+  Controller->MaxBlocksPerCommand = Enquiry2->MaxBlocksPerCommand;
+  Controller->ControllerScatterGatherLimit = Enquiry2->MaxScatterGatherEntries;
+  Controller->DriverScatterGatherLimit =
+    Controller->ControllerScatterGatherLimit;
+  if (Controller->DriverScatterGatherLimit > DAC960_V1_ScatterGatherLimit)
+    Controller->DriverScatterGatherLimit = DAC960_V1_ScatterGatherLimit;
+  /*
+    Initialize the Stripe Size, Segment Size, and Geometry Translation.
+  */
+  Controller->V1.StripeSize = Config2->BlocksPerStripe * Config2->BlockFactor
+                             >> (10 - DAC960_BlockSizeBits);
+  Controller->V1.SegmentSize = Config2->BlocksPerCacheLine * Config2->BlockFactor
+                              >> (10 - DAC960_BlockSizeBits);
+  switch (Config2->DriveGeometry)
+    {
+    case DAC960_V1_Geometry_128_32:
+      Controller->V1.GeometryTranslationHeads = 128;
+      Controller->V1.GeometryTranslationSectors = 32;
+      break;
+    case DAC960_V1_Geometry_255_63:
+      Controller->V1.GeometryTranslationHeads = 255;
+      Controller->V1.GeometryTranslationSectors = 63;
+      break;
+    default:
+      free_dma_loaf(Controller->PCIDevice, &local_dma);
+      return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY");
+    }
+  /*
+    Initialize the Background Initialization Status.
+  */
+  if ((Controller->FirmwareVersion[0] == '4' &&
+      strcmp(Controller->FirmwareVersion, "4.08") >= 0) ||
+      (Controller->FirmwareVersion[0] == '5' &&
+       strcmp(Controller->FirmwareVersion, "5.08") >= 0))
+    {
+      Controller->V1.BackgroundInitializationStatusSupported = true;
+      DAC960_V1_ExecuteType3B(Controller,
+                             DAC960_V1_BackgroundInitializationControl, 0x20,
+                             Controller->
+                              V1.BackgroundInitializationStatusDMA);
+      memcpy(&Controller->V1.LastBackgroundInitializationStatus,
+               Controller->V1.BackgroundInitializationStatus,
+               sizeof(DAC960_V1_BackgroundInitializationStatus_T));
+    }
+  /*
+    Initialize the Logical Drive Initially Accessible flag.
+  */
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    if (Controller->V1.LogicalDriveInformation
+                      [LogicalDriveNumber].LogicalDriveState !=
+       DAC960_V1_LogicalDrive_Offline)
+      Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber] = true;
+  Controller->V1.LastRebuildStatus = DAC960_V1_NoRebuildOrCheckInProgress;
+  free_dma_loaf(Controller->PCIDevice, &local_dma);
+  return true;
+}
+
+
+/*
+  DAC960_V2_ReadControllerConfiguration reads the Configuration Information
+  from DAC960 V2 Firmware Controllers and initializes the Controller structure.
+*/
+
+static bool DAC960_V2_ReadControllerConfiguration(DAC960_Controller_T
+                                                    *Controller)
+{
+  DAC960_V2_ControllerInfo_T *ControllerInfo =
+               &Controller->V2.ControllerInformation;
+  unsigned short LogicalDeviceNumber = 0;
+  int ModelNameLength;
+
+  /* Get data into dma-able area, then copy into permanent location */
+  if (!DAC960_V2_NewControllerInfo(Controller))
+    return DAC960_Failure(Controller, "GET CONTROLLER INFO");
+  memcpy(ControllerInfo, Controller->V2.NewControllerInformation,
+                       sizeof(DAC960_V2_ControllerInfo_T));
+        
+  
+  if (!DAC960_V2_GeneralInfo(Controller))
+    return DAC960_Failure(Controller, "GET HEALTH STATUS");
+
+  /*
+    Initialize the Controller Model Name and Full Model Name fields.
+  */
+  ModelNameLength = sizeof(ControllerInfo->ControllerName);
+  if (ModelNameLength > sizeof(Controller->ModelName)-1)
+    ModelNameLength = sizeof(Controller->ModelName)-1;
+  memcpy(Controller->ModelName, ControllerInfo->ControllerName,
+        ModelNameLength);
+  ModelNameLength--;
+  while (Controller->ModelName[ModelNameLength] == ' ' ||
+        Controller->ModelName[ModelNameLength] == '\0')
+    ModelNameLength--;
+  Controller->ModelName[++ModelNameLength] = '\0';
+  strcpy(Controller->FullModelName, "Mylex ");
+  strcat(Controller->FullModelName, Controller->ModelName);
+  /*
+    Initialize the Controller Firmware Version field.
+  */
+  sprintf(Controller->FirmwareVersion, "%d.%02d-%02d",
+         ControllerInfo->FirmwareMajorVersion,
+         ControllerInfo->FirmwareMinorVersion,
+         ControllerInfo->FirmwareTurnNumber);
+  if (ControllerInfo->FirmwareMajorVersion == 6 &&
+      ControllerInfo->FirmwareMinorVersion == 0 &&
+      ControllerInfo->FirmwareTurnNumber < 1)
+    {
+      DAC960_Info("FIRMWARE VERSION %s DOES NOT PROVIDE THE CONTROLLER\n",
+                 Controller, Controller->FirmwareVersion);
+      DAC960_Info("STATUS MONITORING FUNCTIONALITY NEEDED BY THIS DRIVER.\n",
+                 Controller);
+      DAC960_Info("PLEASE UPGRADE TO VERSION 6.00-01 OR ABOVE.\n",
+                 Controller);
+    }
+  /*
+    Initialize the Controller Channels, Targets, and Memory Size.
+  */
+  Controller->Channels = ControllerInfo->NumberOfPhysicalChannelsPresent;
+  Controller->Targets =
+    ControllerInfo->MaximumTargetsPerChannel
+                   [ControllerInfo->NumberOfPhysicalChannelsPresent-1];
+  Controller->MemorySize = ControllerInfo->MemorySizeMB;
+  /*
+    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
+    Count, Maximum Blocks per Command, Controller Scatter/Gather Limit, and
+    Driver Scatter/Gather Limit.  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 = ControllerInfo->MaximumParallelCommands;
+  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
+  if (Controller->DriverQueueDepth > DAC960_MaxDriverQueueDepth)
+    Controller->DriverQueueDepth = DAC960_MaxDriverQueueDepth;
+  Controller->LogicalDriveCount = ControllerInfo->LogicalDevicesPresent;
+  Controller->MaxBlocksPerCommand =
+    ControllerInfo->MaximumDataTransferSizeInBlocks;
+  Controller->ControllerScatterGatherLimit =
+    ControllerInfo->MaximumScatterGatherEntries;
+  Controller->DriverScatterGatherLimit =
+    Controller->ControllerScatterGatherLimit;
+  if (Controller->DriverScatterGatherLimit > DAC960_V2_ScatterGatherLimit)
+    Controller->DriverScatterGatherLimit = DAC960_V2_ScatterGatherLimit;
+  /*
+    Initialize the Logical Device Information.
+  */
+  while (true)
+    {
+      DAC960_V2_LogicalDeviceInfo_T *NewLogicalDeviceInfo =
+       Controller->V2.NewLogicalDeviceInformation;
+      DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo;
+      DAC960_V2_PhysicalDevice_T PhysicalDevice;
+
+      if (!DAC960_V2_NewLogicalDeviceInfo(Controller, LogicalDeviceNumber))
+       break;
+      LogicalDeviceNumber = NewLogicalDeviceInfo->LogicalDeviceNumber;
+      if (LogicalDeviceNumber >= DAC960_MaxLogicalDrives) {
+       DAC960_Error("DAC960: Logical Drive Number %d not supported\n",
+                      Controller, LogicalDeviceNumber);
+               break;
+      }
+      if (NewLogicalDeviceInfo->DeviceBlockSizeInBytes != DAC960_BlockSize) {
+       DAC960_Error("DAC960: Logical Drive Block Size %d not supported\n",
+             Controller, NewLogicalDeviceInfo->DeviceBlockSizeInBytes);
+        LogicalDeviceNumber++;
+        continue;
+      }
+      PhysicalDevice.Controller = 0;
+      PhysicalDevice.Channel = NewLogicalDeviceInfo->Channel;
+      PhysicalDevice.TargetID = NewLogicalDeviceInfo->TargetID;
+      PhysicalDevice.LogicalUnit = NewLogicalDeviceInfo->LogicalUnit;
+      Controller->V2.LogicalDriveToVirtualDevice[LogicalDeviceNumber] =
+       PhysicalDevice;
+      if (NewLogicalDeviceInfo->LogicalDeviceState !=
+         DAC960_V2_LogicalDevice_Offline)
+       Controller->LogicalDriveInitiallyAccessible[LogicalDeviceNumber] = true;
+      LogicalDeviceInfo = kmalloc(sizeof(DAC960_V2_LogicalDeviceInfo_T),
+                                  GFP_ATOMIC);
+      if (LogicalDeviceInfo == NULL)
+       return DAC960_Failure(Controller, "LOGICAL DEVICE ALLOCATION");
+      Controller->V2.LogicalDeviceInformation[LogicalDeviceNumber] =
+       LogicalDeviceInfo;
+      memcpy(LogicalDeviceInfo, NewLogicalDeviceInfo,
+            sizeof(DAC960_V2_LogicalDeviceInfo_T));
+      LogicalDeviceNumber++;
+    }
+  return true;
+}
+
+
+/*
+  DAC960_ReportControllerConfiguration reports the Configuration Information
+  for Controller.
+*/
+
+static bool DAC960_ReportControllerConfiguration(DAC960_Controller_T
+                                                   *Controller)
+{
+  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, "
+             "Scatter/Gather Limit: %d of %d Segments\n",
+             Controller, Controller->DriverQueueDepth,
+             Controller->DriverScatterGatherLimit,
+             Controller->ControllerScatterGatherLimit);
+  if (Controller->FirmwareType == DAC960_V1_Controller)
+    {
+      DAC960_Info("  Stripe Size: %dKB, Segment Size: %dKB, "
+                 "BIOS Geometry: %d/%d\n", Controller,
+                 Controller->V1.StripeSize,
+                 Controller->V1.SegmentSize,
+                 Controller->V1.GeometryTranslationHeads,
+                 Controller->V1.GeometryTranslationSectors);
+      if (Controller->V1.SAFTE_EnclosureManagementEnabled)
+       DAC960_Info("  SAF-TE Enclosure Management Enabled\n", Controller);
+    }
+  return true;
+}
+
+
+/*
+  DAC960_V1_ReadDeviceConfiguration reads the Device Configuration Information
+  for DAC960 V1 Firmware Controllers by requesting the SCSI Inquiry and SCSI
+  Inquiry Unit Serial Number information for each device connected to
+  Controller.
+*/
+
+static bool DAC960_V1_ReadDeviceConfiguration(DAC960_Controller_T
+                                                *Controller)
+{
+  struct dma_loaf local_dma;
+
+  dma_addr_t DCDBs_dma[DAC960_V1_MaxChannels];
+  DAC960_V1_DCDB_T *DCDBs_cpu[DAC960_V1_MaxChannels];
+
+  dma_addr_t SCSI_Inquiry_dma[DAC960_V1_MaxChannels];
+  DAC960_SCSI_Inquiry_T *SCSI_Inquiry_cpu[DAC960_V1_MaxChannels];
+
+  dma_addr_t SCSI_NewInquiryUnitSerialNumberDMA[DAC960_V1_MaxChannels];
+  DAC960_SCSI_Inquiry_UnitSerialNumber_T *SCSI_NewInquiryUnitSerialNumberCPU[DAC960_V1_MaxChannels];
+
+  struct completion Completions[DAC960_V1_MaxChannels];
+  unsigned long flags;
+  int Channel, TargetID;
+
+  if (!init_dma_loaf(Controller->PCIDevice, &local_dma, 
+               DAC960_V1_MaxChannels*(sizeof(DAC960_V1_DCDB_T) +
+                       sizeof(DAC960_SCSI_Inquiry_T) +
+                       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T))))
+     return DAC960_Failure(Controller,
+                        "DMA ALLOCATION FAILED IN ReadDeviceConfiguration"); 
+   
+  for (Channel = 0; Channel < Controller->Channels; Channel++) {
+       DCDBs_cpu[Channel] = slice_dma_loaf(&local_dma,
+                       sizeof(DAC960_V1_DCDB_T), DCDBs_dma + Channel);
+       SCSI_Inquiry_cpu[Channel] = slice_dma_loaf(&local_dma,
+                       sizeof(DAC960_SCSI_Inquiry_T),
+                       SCSI_Inquiry_dma + Channel);
+       SCSI_NewInquiryUnitSerialNumberCPU[Channel] = slice_dma_loaf(&local_dma,
+                       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
+                       SCSI_NewInquiryUnitSerialNumberDMA + Channel);
+  }
+               
+  for (TargetID = 0; TargetID < Controller->Targets; TargetID++)
+    {
+      /*
+       * For each channel, submit a probe for a device on that channel.
+       * The timeout interval for a device that is present is 10 seconds.
+       * With this approach, the timeout periods can elapse in parallel
+       * on each channel.
+       */
+      for (Channel = 0; Channel < Controller->Channels; Channel++)
+       {
+         dma_addr_t NewInquiryStandardDataDMA = SCSI_Inquiry_dma[Channel];
+         DAC960_V1_DCDB_T *DCDB = DCDBs_cpu[Channel];
+         dma_addr_t DCDB_dma = DCDBs_dma[Channel];
+         DAC960_Command_T *Command = Controller->Commands[Channel];
+          struct completion *Completion = &Completions[Channel];
+
+         init_completion(Completion);
+         DAC960_V1_ClearCommand(Command);
+         Command->CommandType = DAC960_ImmediateCommand;
+         Command->Completion = Completion;
+         Command->V1.CommandMailbox.Type3.CommandOpcode = DAC960_V1_DCDB;
+         Command->V1.CommandMailbox.Type3.BusAddress = DCDB_dma;
+         DCDB->Channel = Channel;
+         DCDB->TargetID = TargetID;
+         DCDB->Direction = DAC960_V1_DCDB_DataTransferDeviceToSystem;
+         DCDB->EarlyStatus = false;
+         DCDB->Timeout = DAC960_V1_DCDB_Timeout_10_seconds;
+         DCDB->NoAutomaticRequestSense = false;
+         DCDB->DisconnectPermitted = true;
+         DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_T);
+         DCDB->BusAddress = NewInquiryStandardDataDMA;
+         DCDB->CDBLength = 6;
+         DCDB->TransferLengthHigh4 = 0;
+         DCDB->SenseLength = sizeof(DCDB->SenseData);
+         DCDB->CDB[0] = 0x12; /* INQUIRY */
+         DCDB->CDB[1] = 0; /* EVPD = 0 */
+         DCDB->CDB[2] = 0; /* Page Code */
+         DCDB->CDB[3] = 0; /* Reserved */
+         DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_T);
+         DCDB->CDB[5] = 0; /* Control */
+
+         spin_lock_irqsave(&Controller->queue_lock, flags);
+         DAC960_QueueCommand(Command);
+         spin_unlock_irqrestore(&Controller->queue_lock, flags);
+       }
+      /*
+       * Wait for the problems submitted in the previous loop
+       * to complete.  On the probes that are successful, 
+       * get the serial number of the device that was found.
+       */
+      for (Channel = 0; Channel < Controller->Channels; Channel++)
+       {
+         DAC960_SCSI_Inquiry_T *InquiryStandardData =
+           &Controller->V1.InquiryStandardData[Channel][TargetID];
+         DAC960_SCSI_Inquiry_T *NewInquiryStandardData = SCSI_Inquiry_cpu[Channel];
+         dma_addr_t NewInquiryUnitSerialNumberDMA =
+                       SCSI_NewInquiryUnitSerialNumberDMA[Channel];
+         DAC960_SCSI_Inquiry_UnitSerialNumber_T *NewInquiryUnitSerialNumber =
+                       SCSI_NewInquiryUnitSerialNumberCPU[Channel];
+         DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+           &Controller->V1.InquiryUnitSerialNumber[Channel][TargetID];
+         DAC960_Command_T *Command = Controller->Commands[Channel];
+         DAC960_V1_DCDB_T *DCDB = DCDBs_cpu[Channel];
+          struct completion *Completion = &Completions[Channel];
+
+         wait_for_completion(Completion);
+
+         if (Command->V1.CommandStatus != DAC960_V1_NormalCompletion) {
+           memset(InquiryStandardData, 0, sizeof(DAC960_SCSI_Inquiry_T));
+           InquiryStandardData->PeripheralDeviceType = 0x1F;
+           continue;
+         } else
+           memcpy(InquiryStandardData, NewInquiryStandardData, sizeof(DAC960_SCSI_Inquiry_T));
+       
+         /* Preserve Channel and TargetID values from the previous loop */
+         Command->Completion = Completion;
+         DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
+         DCDB->BusAddress = NewInquiryUnitSerialNumberDMA;
+         DCDB->SenseLength = sizeof(DCDB->SenseData);
+         DCDB->CDB[0] = 0x12; /* INQUIRY */
+         DCDB->CDB[1] = 1; /* EVPD = 1 */
+         DCDB->CDB[2] = 0x80; /* Page Code */
+         DCDB->CDB[3] = 0; /* Reserved */
+         DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
+         DCDB->CDB[5] = 0; /* Control */
+
+         spin_lock_irqsave(&Controller->queue_lock, flags);
+         DAC960_QueueCommand(Command);
+         spin_unlock_irqrestore(&Controller->queue_lock, flags);
+         wait_for_completion(Completion);
+
+         if (Command->V1.CommandStatus != DAC960_V1_NormalCompletion) {
+               memset(InquiryUnitSerialNumber, 0,
+                       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+               InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
+         } else
+               memcpy(InquiryUnitSerialNumber, NewInquiryUnitSerialNumber,
+                       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+       }
+    }
+    free_dma_loaf(Controller->PCIDevice, &local_dma);
+  return true;
+}
+
+
+/*
+  DAC960_V2_ReadDeviceConfiguration reads the Device Configuration Information
+  for DAC960 V2 Firmware Controllers by requesting the Physical Device
+  Information and SCSI Inquiry Unit Serial Number information for each
+  device connected to Controller.
+*/
+
+static bool DAC960_V2_ReadDeviceConfiguration(DAC960_Controller_T
+                                                *Controller)
+{
+  unsigned char Channel = 0, TargetID = 0, LogicalUnit = 0;
+  unsigned short PhysicalDeviceIndex = 0;
+
+  while (true)
+    {
+      DAC960_V2_PhysicalDeviceInfo_T *NewPhysicalDeviceInfo =
+               Controller->V2.NewPhysicalDeviceInformation;
+      DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo;
+      DAC960_SCSI_Inquiry_UnitSerialNumber_T *NewInquiryUnitSerialNumber =
+               Controller->V2.NewInquiryUnitSerialNumber;
+      DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber;
+
+      if (!DAC960_V2_NewPhysicalDeviceInfo(Controller, Channel, TargetID, LogicalUnit))
+         break;
+
+      PhysicalDeviceInfo = kmalloc(sizeof(DAC960_V2_PhysicalDeviceInfo_T),
+                                   GFP_ATOMIC);
+      if (PhysicalDeviceInfo == NULL)
+               return DAC960_Failure(Controller, "PHYSICAL DEVICE ALLOCATION");
+      Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex] =
+               PhysicalDeviceInfo;
+      memcpy(PhysicalDeviceInfo, NewPhysicalDeviceInfo,
+               sizeof(DAC960_V2_PhysicalDeviceInfo_T));
+
+      InquiryUnitSerialNumber = kmalloc(
+             sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T), GFP_ATOMIC);
+      if (InquiryUnitSerialNumber == NULL) {
+       kfree(PhysicalDeviceInfo);
+       return DAC960_Failure(Controller, "SERIAL NUMBER ALLOCATION");
+      }
+      Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex] =
+               InquiryUnitSerialNumber;
+
+      Channel = NewPhysicalDeviceInfo->Channel;
+      TargetID = NewPhysicalDeviceInfo->TargetID;
+      LogicalUnit = NewPhysicalDeviceInfo->LogicalUnit;
+
+      /*
+        Some devices do NOT have Unit Serial Numbers.
+        This command fails for them.  But, we still want to
+        remember those devices are there.  Construct a
+        UnitSerialNumber structure for the failure case.
+      */
+      if (!DAC960_V2_NewInquiryUnitSerialNumber(Controller, Channel, TargetID, LogicalUnit)) {
+       memset(InquiryUnitSerialNumber, 0,
+             sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+       InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
+      } else
+       memcpy(InquiryUnitSerialNumber, NewInquiryUnitSerialNumber,
+               sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+
+      PhysicalDeviceIndex++;
+      LogicalUnit++;
+    }
+  return true;
+}
+
+
+/*
+  DAC960_SanitizeInquiryData sanitizes the Vendor, Model, Revision, and
+  Product Serial Number fields of the Inquiry Standard Data and Inquiry
+  Unit Serial Number structures.
+*/
+
+static void DAC960_SanitizeInquiryData(DAC960_SCSI_Inquiry_T
+                                        *InquiryStandardData,
+                                      DAC960_SCSI_Inquiry_UnitSerialNumber_T
+                                        *InquiryUnitSerialNumber,
+                                      unsigned char *Vendor,
+                                      unsigned char *Model,
+                                      unsigned char *Revision,
+                                      unsigned char *SerialNumber)
+{
+  int SerialNumberLength, i;
+  if (InquiryStandardData->PeripheralDeviceType == 0x1F) return;
+  for (i = 0; i < sizeof(InquiryStandardData->VendorIdentification); i++)
+    {
+      unsigned char VendorCharacter =
+       InquiryStandardData->VendorIdentification[i];
+      Vendor[i] = (VendorCharacter >= ' ' && VendorCharacter <= '~'
+                  ? VendorCharacter : ' ');
+    }
+  Vendor[sizeof(InquiryStandardData->VendorIdentification)] = '\0';
+  for (i = 0; i < sizeof(InquiryStandardData->ProductIdentification); i++)
+    {
+      unsigned char ModelCharacter =
+       InquiryStandardData->ProductIdentification[i];
+      Model[i] = (ModelCharacter >= ' ' && ModelCharacter <= '~'
+                 ? ModelCharacter : ' ');
+    }
+  Model[sizeof(InquiryStandardData->ProductIdentification)] = '\0';
+  for (i = 0; i < sizeof(InquiryStandardData->ProductRevisionLevel); i++)
+    {
+      unsigned char RevisionCharacter =
+       InquiryStandardData->ProductRevisionLevel[i];
+      Revision[i] = (RevisionCharacter >= ' ' && RevisionCharacter <= '~'
+                    ? RevisionCharacter : ' ');
+    }
+  Revision[sizeof(InquiryStandardData->ProductRevisionLevel)] = '\0';
+  if (InquiryUnitSerialNumber->PeripheralDeviceType == 0x1F) return;
+  SerialNumberLength = InquiryUnitSerialNumber->PageLength;
+  if (SerialNumberLength >
+      sizeof(InquiryUnitSerialNumber->ProductSerialNumber))
+    SerialNumberLength = sizeof(InquiryUnitSerialNumber->ProductSerialNumber);
+  for (i = 0; i < SerialNumberLength; i++)
+    {
+      unsigned char SerialNumberCharacter =
+       InquiryUnitSerialNumber->ProductSerialNumber[i];
+      SerialNumber[i] =
+       (SerialNumberCharacter >= ' ' && SerialNumberCharacter <= '~'
+        ? SerialNumberCharacter : ' ');
+    }
+  SerialNumber[SerialNumberLength] = '\0';
+}
+
+
+/*
+  DAC960_V1_ReportDeviceConfiguration reports the Device Configuration
+  Information for DAC960 V1 Firmware Controllers.
+*/
+
+static bool DAC960_V1_ReportDeviceConfiguration(DAC960_Controller_T
+                                                  *Controller)
+{
+  int LogicalDriveNumber, Channel, TargetID;
+  DAC960_Info("  Physical Devices:\n", Controller);
+  for (Channel = 0; Channel < Controller->Channels; Channel++)
+    for (TargetID = 0; TargetID < Controller->Targets; TargetID++)
+      {
+       DAC960_SCSI_Inquiry_T *InquiryStandardData =
+         &Controller->V1.InquiryStandardData[Channel][TargetID];
+       DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+         &Controller->V1.InquiryUnitSerialNumber[Channel][TargetID];
+       DAC960_V1_DeviceState_T *DeviceState =
+         &Controller->V1.DeviceState[Channel][TargetID];
+       DAC960_V1_ErrorTableEntry_T *ErrorEntry =
+         &Controller->V1.ErrorTable.ErrorTableEntries[Channel][TargetID];
+       char Vendor[1+sizeof(InquiryStandardData->VendorIdentification)];
+       char Model[1+sizeof(InquiryStandardData->ProductIdentification)];
+       char Revision[1+sizeof(InquiryStandardData->ProductRevisionLevel)];
+       char SerialNumber[1+sizeof(InquiryUnitSerialNumber
+                                  ->ProductSerialNumber)];
+       if (InquiryStandardData->PeripheralDeviceType == 0x1F) continue;
+       DAC960_SanitizeInquiryData(InquiryStandardData, InquiryUnitSerialNumber,
+                                  Vendor, Model, Revision, SerialNumber);
+       DAC960_Info("    %d:%d%s Vendor: %s  Model: %s  Revision: %s\n",
+                   Controller, Channel, TargetID, (TargetID < 10 ? " " : ""),
+                   Vendor, Model, Revision);
+       if (InquiryUnitSerialNumber->PeripheralDeviceType != 0x1F)
+         DAC960_Info("         Serial Number: %s\n", Controller, SerialNumber);
+       if (DeviceState->Present &&
+           DeviceState->DeviceType == DAC960_V1_DiskType)
+         {
+           if (Controller->V1.DeviceResetCount[Channel][TargetID] > 0)
+             DAC960_Info("         Disk Status: %s, %u blocks, %d resets\n",
+                         Controller,
+                         (DeviceState->DeviceState == DAC960_V1_Device_Dead
+                          ? "Dead"
+                          : DeviceState->DeviceState
+                            == DAC960_V1_Device_WriteOnly
+                            ? "Write-Only"
+                            : DeviceState->DeviceState
+                              == DAC960_V1_Device_Online
+                              ? "Online" : "Standby"),
+                         DeviceState->DiskSize,
+                         Controller->V1.DeviceResetCount[Channel][TargetID]);
+           else
+             DAC960_Info("         Disk Status: %s, %u blocks\n", Controller,
+                         (DeviceState->DeviceState == DAC960_V1_Device_Dead
+                          ? "Dead"
+                          : DeviceState->DeviceState
+                            == DAC960_V1_Device_WriteOnly
+                            ? "Write-Only"
+                            : DeviceState->DeviceState
+                              == DAC960_V1_Device_Online
+                              ? "Online" : "Standby"),
+                         DeviceState->DiskSize);
+         }
+       if (ErrorEntry->ParityErrorCount > 0 ||
+           ErrorEntry->SoftErrorCount > 0 ||
+           ErrorEntry->HardErrorCount > 0 ||
+           ErrorEntry->MiscErrorCount > 0)
+         DAC960_Info("         Errors - Parity: %d, Soft: %d, "
+                     "Hard: %d, Misc: %d\n", Controller,
+                     ErrorEntry->ParityErrorCount,
+                     ErrorEntry->SoftErrorCount,
+                     ErrorEntry->HardErrorCount,
+                     ErrorEntry->MiscErrorCount);
+      }
+  DAC960_Info("  Logical Drives:\n", Controller);
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    {
+      DAC960_V1_LogicalDriveInformation_T *LogicalDriveInformation =
+       &Controller->V1.LogicalDriveInformation[LogicalDriveNumber];
+      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %u blocks, %s\n",
+                 Controller, Controller->ControllerNumber, LogicalDriveNumber,
+                 LogicalDriveInformation->RAIDLevel,
+                 (LogicalDriveInformation->LogicalDriveState
+                  == DAC960_V1_LogicalDrive_Online
+                  ? "Online"
+                  : LogicalDriveInformation->LogicalDriveState
+                    == DAC960_V1_LogicalDrive_Critical
+                    ? "Critical" : "Offline"),
+                 LogicalDriveInformation->LogicalDriveSize,
+                 (LogicalDriveInformation->WriteBack
+                  ? "Write Back" : "Write Thru"));
+    }
+  return true;
+}
+
+
+/*
+  DAC960_V2_ReportDeviceConfiguration reports the Device Configuration
+  Information for DAC960 V2 Firmware Controllers.
+*/
+
+static bool DAC960_V2_ReportDeviceConfiguration(DAC960_Controller_T
+                                                  *Controller)
+{
+  int PhysicalDeviceIndex, LogicalDriveNumber;
+  DAC960_Info("  Physical Devices:\n", Controller);
+  for (PhysicalDeviceIndex = 0;
+       PhysicalDeviceIndex < DAC960_V2_MaxPhysicalDevices;
+       PhysicalDeviceIndex++)
+    {
+      DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo =
+       Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex];
+      DAC960_SCSI_Inquiry_T *InquiryStandardData =
+       (DAC960_SCSI_Inquiry_T *) &PhysicalDeviceInfo->SCSI_InquiryData;
+      DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+       Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex];
+      char Vendor[1+sizeof(InquiryStandardData->VendorIdentification)];
+      char Model[1+sizeof(InquiryStandardData->ProductIdentification)];
+      char Revision[1+sizeof(InquiryStandardData->ProductRevisionLevel)];
+      char SerialNumber[1+sizeof(InquiryUnitSerialNumber->ProductSerialNumber)];
+      if (PhysicalDeviceInfo == NULL) break;
+      DAC960_SanitizeInquiryData(InquiryStandardData, InquiryUnitSerialNumber,
+                                Vendor, Model, Revision, SerialNumber);
+      DAC960_Info("    %d:%d%s Vendor: %s  Model: %s  Revision: %s\n",
+                 Controller,
+                 PhysicalDeviceInfo->Channel,
+                 PhysicalDeviceInfo->TargetID,
+                 (PhysicalDeviceInfo->TargetID < 10 ? " " : ""),
+                 Vendor, Model, Revision);
+      if (PhysicalDeviceInfo->NegotiatedSynchronousMegaTransfers == 0)
+       DAC960_Info("         %sAsynchronous\n", Controller,
+                   (PhysicalDeviceInfo->NegotiatedDataWidthBits == 16
+                    ? "Wide " :""));
+      else
+       DAC960_Info("         %sSynchronous at %d MB/sec\n", Controller,
+                   (PhysicalDeviceInfo->NegotiatedDataWidthBits == 16
+                    ? "Wide " :""),
+                   (PhysicalDeviceInfo->NegotiatedSynchronousMegaTransfers
+                    * PhysicalDeviceInfo->NegotiatedDataWidthBits/8));
+      if (InquiryUnitSerialNumber->PeripheralDeviceType != 0x1F)
+       DAC960_Info("         Serial Number: %s\n", Controller, SerialNumber);
+      if (PhysicalDeviceInfo->PhysicalDeviceState ==
+         DAC960_V2_Device_Unconfigured)
+       continue;
+      DAC960_Info("         Disk Status: %s, %u blocks\n", Controller,
+                 (PhysicalDeviceInfo->PhysicalDeviceState
+                  == DAC960_V2_Device_Online
+                  ? "Online"
+                  : PhysicalDeviceInfo->PhysicalDeviceState
+                    == DAC960_V2_Device_Rebuild
+                    ? "Rebuild"
+                    : PhysicalDeviceInfo->PhysicalDeviceState
+                      == DAC960_V2_Device_Missing
+                      ? "Missing"
+                      : PhysicalDeviceInfo->PhysicalDeviceState
+                        == DAC960_V2_Device_Critical
+                        ? "Critical"
+                        : PhysicalDeviceInfo->PhysicalDeviceState
+                          == DAC960_V2_Device_Dead
+                          ? "Dead"
+                          : PhysicalDeviceInfo->PhysicalDeviceState
+                            == DAC960_V2_Device_SuspectedDead
+                            ? "Suspected-Dead"
+                            : PhysicalDeviceInfo->PhysicalDeviceState
+                              == DAC960_V2_Device_CommandedOffline
+                              ? "Commanded-Offline"
+                              : PhysicalDeviceInfo->PhysicalDeviceState
+                                == DAC960_V2_Device_Standby
+                                ? "Standby" : "Unknown"),
+                 PhysicalDeviceInfo->ConfigurableDeviceSize);
+      if (PhysicalDeviceInfo->ParityErrors == 0 &&
+         PhysicalDeviceInfo->SoftErrors == 0 &&
+         PhysicalDeviceInfo->HardErrors == 0 &&
+         PhysicalDeviceInfo->MiscellaneousErrors == 0 &&
+         PhysicalDeviceInfo->CommandTimeouts == 0 &&
+         PhysicalDeviceInfo->Retries == 0 &&
+         PhysicalDeviceInfo->Aborts == 0 &&
+         PhysicalDeviceInfo->PredictedFailuresDetected == 0)
+       continue;
+      DAC960_Info("         Errors - Parity: %d, Soft: %d, "
+                 "Hard: %d, Misc: %d\n", Controller,
+                 PhysicalDeviceInfo->ParityErrors,
+                 PhysicalDeviceInfo->SoftErrors,
+                 PhysicalDeviceInfo->HardErrors,
+                 PhysicalDeviceInfo->MiscellaneousErrors);
+      DAC960_Info("                  Timeouts: %d, Retries: %d, "
+                 "Aborts: %d, Predicted: %d\n", Controller,
+                 PhysicalDeviceInfo->CommandTimeouts,
+                 PhysicalDeviceInfo->Retries,
+                 PhysicalDeviceInfo->Aborts,
+                 PhysicalDeviceInfo->PredictedFailuresDetected);
+    }
+  DAC960_Info("  Logical Drives:\n", Controller);
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < DAC960_MaxLogicalDrives;
+       LogicalDriveNumber++)
+    {
+      DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
+       Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
+      unsigned char *ReadCacheStatus[] = { "Read Cache Disabled",
+                                          "Read Cache Enabled",
+                                          "Read Ahead Enabled",
+                                          "Intelligent Read Ahead Enabled",
+                                          "-", "-", "-", "-" };
+      unsigned char *WriteCacheStatus[] = { "Write Cache Disabled",
+                                           "Logical Device Read Only",
+                                           "Write Cache Enabled",
+                                           "Intelligent Write Cache Enabled",
+                                           "-", "-", "-", "-" };
+      unsigned char *GeometryTranslation;
+      if (LogicalDeviceInfo == NULL) continue;
+      switch (LogicalDeviceInfo->DriveGeometry)
+       {
+       case DAC960_V2_Geometry_128_32:
+         GeometryTranslation = "128/32";
+         break;
+       case DAC960_V2_Geometry_255_63:
+         GeometryTranslation = "255/63";
+         break;
+       default:
+         GeometryTranslation = "Invalid";
+         DAC960_Error("Illegal Logical Device Geometry %d\n",
+                      Controller, LogicalDeviceInfo->DriveGeometry);
+         break;
+       }
+      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %u blocks\n",
+                 Controller, Controller->ControllerNumber, LogicalDriveNumber,
+                 LogicalDeviceInfo->RAIDLevel,
+                 (LogicalDeviceInfo->LogicalDeviceState
+                  == DAC960_V2_LogicalDevice_Online
+                  ? "Online"
+                  : LogicalDeviceInfo->LogicalDeviceState
+                    == DAC960_V2_LogicalDevice_Critical
+                    ? "Critical" : "Offline"),
+                 LogicalDeviceInfo->ConfigurableDeviceSize);
+      DAC960_Info("                  Logical Device %s, BIOS Geometry: %s\n",
+                 Controller,
+                 (LogicalDeviceInfo->LogicalDeviceControl
+                                    .LogicalDeviceInitialized
+                  ? "Initialized" : "Uninitialized"),
+                 GeometryTranslation);
+      if (LogicalDeviceInfo->StripeSize == 0)
+       {
+         if (LogicalDeviceInfo->CacheLineSize == 0)
+           DAC960_Info("                  Stripe Size: N/A, "
+                       "Segment Size: N/A\n", Controller);
+         else
+           DAC960_Info("                  Stripe Size: N/A, "
+                       "Segment Size: %dKB\n", Controller,
+                       1 << (LogicalDeviceInfo->CacheLineSize - 2));
+       }
+      else
+       {
+         if (LogicalDeviceInfo->CacheLineSize == 0)
+           DAC960_Info("                  Stripe Size: %dKB, "
+                       "Segment Size: N/A\n", Controller,
+                       1 << (LogicalDeviceInfo->StripeSize - 2));
+         else
+           DAC960_Info("                  Stripe Size: %dKB, "
+                       "Segment Size: %dKB\n", Controller,
+                       1 << (LogicalDeviceInfo->StripeSize - 2),
+                       1 << (LogicalDeviceInfo->CacheLineSize - 2));
+       }
+      DAC960_Info("                  %s, %s\n", Controller,
+                 ReadCacheStatus[
+                   LogicalDeviceInfo->LogicalDeviceControl.ReadCache],
+                 WriteCacheStatus[
+                   LogicalDeviceInfo->LogicalDeviceControl.WriteCache]);
+      if (LogicalDeviceInfo->SoftErrors > 0 ||
+         LogicalDeviceInfo->CommandsFailed > 0 ||
+         LogicalDeviceInfo->DeferredWriteErrors)
+       DAC960_Info("                  Errors - Soft: %d, Failed: %d, "
+                   "Deferred Write: %d\n", Controller,
+                   LogicalDeviceInfo->SoftErrors,
+                   LogicalDeviceInfo->CommandsFailed,
+                   LogicalDeviceInfo->DeferredWriteErrors);
+
+    }
+  return true;
+}
+
+/*
+  DAC960_RegisterBlockDevice registers the Block Device structures
+  associated with Controller.
+*/
+
+static bool DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  int n;
+
+  /*
+    Register the Block Device Major Number for this DAC960 Controller.
+  */
+  if (register_blkdev(MajorNumber, "dac960") < 0)
+      return false;
+
+  for (n = 0; n < DAC960_MaxLogicalDrives; n++) {
+       struct gendisk *disk = Controller->disks[n];
+       struct request_queue *RequestQueue;
+
+       /* for now, let all request queues share controller's lock */
+       RequestQueue = blk_init_queue(DAC960_RequestFunction,&Controller->queue_lock);
+       if (!RequestQueue) {
+               printk("DAC960: failure to allocate request queue\n");
+               continue;
+       }
+       Controller->RequestQueue[n] = RequestQueue;
+       blk_queue_bounce_limit(RequestQueue, Controller->BounceBufferLimit);
+       RequestQueue->queuedata = Controller;
+       blk_queue_max_segments(RequestQueue, Controller->DriverScatterGatherLimit);
+       blk_queue_max_hw_sectors(RequestQueue, Controller->MaxBlocksPerCommand);
+       disk->queue = RequestQueue;
+       sprintf(disk->disk_name, "rd/c%dd%d", Controller->ControllerNumber, n);
+       disk->major = MajorNumber;
+       disk->first_minor = n << DAC960_MaxPartitionsBits;
+       disk->fops = &DAC960_BlockDeviceOperations;
+   }
+  /*
+    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;
+  int disk;
+
+  /* does order matter when deleting gendisk and cleanup in request queue? */
+  for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) {
+       del_gendisk(Controller->disks[disk]);
+       blk_cleanup_queue(Controller->RequestQueue[disk]);
+       Controller->RequestQueue[disk] = NULL;
+  }
+
+  /*
+    Unregister the Block Device Major Number for this DAC960 Controller.
+  */
+  unregister_blkdev(MajorNumber, "dac960");
+}
+
+/*
+  DAC960_ComputeGenericDiskInfo computes the values for the Generic Disk
+  Information Partition Sector Counts and Block Sizes.
+*/
+
+static void DAC960_ComputeGenericDiskInfo(DAC960_Controller_T *Controller)
+{
+       int disk;
+       for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++)
+               set_capacity(Controller->disks[disk], disk_size(Controller, disk));
+}
+
+/*
+  DAC960_ReportErrorStatus reports Controller BIOS Messages passed through
+  the Error Status Register when the driver performs the BIOS handshaking.
+  It returns true for fatal errors and false otherwise.
+*/
+
+static bool DAC960_ReportErrorStatus(DAC960_Controller_T *Controller,
+                                       unsigned char ErrorStatus,
+                                       unsigned char Parameter0,
+                                       unsigned char Parameter1)
+{
+  switch (ErrorStatus)
+    {
+    case 0x00:
+      DAC960_Notice("Physical Device %d:%d Not Responding\n",
+                   Controller, Parameter1, Parameter0);
+      break;
+    case 0x08:
+      if (Controller->DriveSpinUpMessageDisplayed) break;
+      DAC960_Notice("Spinning Up Drives\n", Controller);
+      Controller->DriveSpinUpMessageDisplayed = true;
+      break;
+    case 0x30:
+      DAC960_Notice("Configuration Checksum Error\n", Controller);
+      break;
+    case 0x60:
+      DAC960_Notice("Mirror Race Recovery Failed\n", Controller);
+      break;
+    case 0x70:
+      DAC960_Notice("Mirror Race Recovery In Progress\n", Controller);
+      break;
+    case 0x90:
+      DAC960_Notice("Physical Device %d:%d COD Mismatch\n",
+                   Controller, Parameter1, Parameter0);
+      break;
+    case 0xA0:
+      DAC960_Notice("Logical Drive Installation Aborted\n", Controller);
+      break;
+    case 0xB0:
+      DAC960_Notice("Mirror Race On A Critical Logical Drive\n", Controller);
+      break;
+    case 0xD0:
+      DAC960_Notice("New Controller Configuration Found\n", Controller);
+      break;
+    case 0xF0:
+      DAC960_Error("Fatal Memory Parity Error for Controller at\n", Controller);
+      return true;
+    default:
+      DAC960_Error("Unknown Initialization Error %02X for Controller at\n",
+                  Controller, ErrorStatus);
+      return true;
+    }
+  return false;
+}
+
+
+/*
+ * DAC960_DetectCleanup releases the resources that were allocated
+ * during DAC960_DetectController().  DAC960_DetectController can
+ * has several internal failure points, so not ALL resources may 
+ * have been allocated.  It's important to free only
+ * resources that HAVE been allocated.  The code below always
+ * tests that the resource has been allocated before attempting to
+ * free it.
+ */
+static void DAC960_DetectCleanup(DAC960_Controller_T *Controller)
+{
+  int i;
+
+  /* Free the memory mailbox, status, and related structures */
+  free_dma_loaf(Controller->PCIDevice, &Controller->DmaPages);
+  if (Controller->MemoryMappedAddress) {
+       switch(Controller->HardwareType)
+       {
+               case DAC960_GEM_Controller:
+                       DAC960_GEM_DisableInterrupts(Controller->BaseAddress);
+                       break;
+               case DAC960_BA_Controller:
+                       DAC960_BA_DisableInterrupts(Controller->BaseAddress);
+                       break;
+               case DAC960_LP_Controller:
+                       DAC960_LP_DisableInterrupts(Controller->BaseAddress);
+                       break;
+               case DAC960_LA_Controller:
+                       DAC960_LA_DisableInterrupts(Controller->BaseAddress);
+                       break;
+               case DAC960_PG_Controller:
+                       DAC960_PG_DisableInterrupts(Controller->BaseAddress);
+                       break;
+               case DAC960_PD_Controller:
+                       DAC960_PD_DisableInterrupts(Controller->BaseAddress);
+                       break;
+               case DAC960_P_Controller:
+                       DAC960_PD_DisableInterrupts(Controller->BaseAddress);
+                       break;
+       }
+       iounmap(Controller->MemoryMappedAddress);
+  }
+  if (Controller->IRQ_Channel)
+       free_irq(Controller->IRQ_Channel, Controller);
+  if (Controller->IO_Address)
+       release_region(Controller->IO_Address, 0x80);
+  pci_disable_device(Controller->PCIDevice);
+  for (i = 0; (i < DAC960_MaxLogicalDrives) && Controller->disks[i]; i++)
+       put_disk(Controller->disks[i]);
+  DAC960_Controllers[Controller->ControllerNumber] = NULL;
+  kfree(Controller);
+}
+
+
+/*
+  DAC960_DetectController detects Mylex DAC960/AcceleRAID/eXtremeRAID
+  PCI RAID Controllers by interrogating the PCI Configuration Space for
+  Controller Type.
+*/
+
+static DAC960_Controller_T * 
+DAC960_DetectController(struct pci_dev *PCI_Device,
+                       const struct pci_device_id *entry)
+{
+  struct DAC960_privdata *privdata =
+               (struct DAC960_privdata *)entry->driver_data;
+  irq_handler_t InterruptHandler = privdata->InterruptHandler;
+  unsigned int MemoryWindowSize = privdata->MemoryWindowSize;
+  DAC960_Controller_T *Controller = NULL;
+  unsigned char DeviceFunction = PCI_Device->devfn;
+  unsigned char ErrorStatus, Parameter0, Parameter1;
+  unsigned int IRQ_Channel;
+  void __iomem *BaseAddress;
+  int i;
+
+  Controller = kzalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC);
+  if (Controller == NULL) {
+       DAC960_Error("Unable to allocate Controller structure for "
+                       "Controller at\n", NULL);
+       return NULL;
+  }
+  Controller->ControllerNumber = DAC960_ControllerCount;
+  DAC960_Controllers[DAC960_ControllerCount++] = Controller;
+  Controller->Bus = PCI_Device->bus->number;
+  Controller->FirmwareType = privdata->FirmwareType;
+  Controller->HardwareType = privdata->HardwareType;
+  Controller->Device = DeviceFunction >> 3;
+  Controller->Function = DeviceFunction & 0x7;
+  Controller->PCIDevice = PCI_Device;
+  strcpy(Controller->FullModelName, "DAC960");
+
+  if (pci_enable_device(PCI_Device))
+       goto Failure;
+
+  switch (Controller->HardwareType)
+  {
+       case DAC960_GEM_Controller:
+         Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
+         break;
+       case DAC960_BA_Controller:
+         Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
+         break;
+       case DAC960_LP_Controller:
+         Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
+         break;
+       case DAC960_LA_Controller:
+         Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
+         break;
+       case DAC960_PG_Controller:
+         Controller->PCI_Address = pci_resource_start(PCI_Device, 0);
+         break;
+       case DAC960_PD_Controller:
+         Controller->IO_Address = pci_resource_start(PCI_Device, 0);
+         Controller->PCI_Address = pci_resource_start(PCI_Device, 1);
+         break;
+       case DAC960_P_Controller:
+         Controller->IO_Address = pci_resource_start(PCI_Device, 0);
+         Controller->PCI_Address = pci_resource_start(PCI_Device, 1);
+         break;
+  }
+
+  pci_set_drvdata(PCI_Device, (void *)((long)Controller->ControllerNumber));
+  for (i = 0; i < DAC960_MaxLogicalDrives; i++) {
+       Controller->disks[i] = alloc_disk(1<<DAC960_MaxPartitionsBits);
+       if (!Controller->disks[i])
+               goto Failure;
+       Controller->disks[i]->private_data = (void *)((long)i);
+  }
+  init_waitqueue_head(&Controller->CommandWaitQueue);
+  init_waitqueue_head(&Controller->HealthStatusWaitQueue);
+  spin_lock_init(&Controller->queue_lock);
+  DAC960_AnnounceDriver(Controller);
+  /*
+    Map the Controller Register Window.
+  */
+ if (MemoryWindowSize < PAGE_SIZE)
+       MemoryWindowSize = PAGE_SIZE;
+  Controller->MemoryMappedAddress =
+       ioremap_nocache(Controller->PCI_Address & PAGE_MASK, MemoryWindowSize);
+  Controller->BaseAddress =
+       Controller->MemoryMappedAddress + (Controller->PCI_Address & ~PAGE_MASK);
+  if (Controller->MemoryMappedAddress == NULL)
+  {
+         DAC960_Error("Unable to map Controller Register Window for "
+                      "Controller at\n", Controller);
+         goto Failure;
+  }
+  BaseAddress = Controller->BaseAddress;
+  switch (Controller->HardwareType)
+  {
+       case DAC960_GEM_Controller:
+         DAC960_GEM_DisableInterrupts(BaseAddress);
+         DAC960_GEM_AcknowledgeHardwareMailboxStatus(BaseAddress);
+         udelay(1000);
+         while (DAC960_GEM_InitializationInProgressP(BaseAddress))
+           {
+             if (DAC960_GEM_ReadErrorStatus(BaseAddress, &ErrorStatus,
+                                           &Parameter0, &Parameter1) &&
+                 DAC960_ReportErrorStatus(Controller, ErrorStatus,
+                                          Parameter0, Parameter1))
+               goto Failure;
+             udelay(10);
+           }
+         if (!DAC960_V2_EnableMemoryMailboxInterface(Controller))
+           {
+             DAC960_Error("Unable to Enable Memory Mailbox Interface "
+                          "for Controller at\n", Controller);
+             goto Failure;
+           }
+         DAC960_GEM_EnableInterrupts(BaseAddress);
+         Controller->QueueCommand = DAC960_GEM_QueueCommand;
+         Controller->ReadControllerConfiguration =
+           DAC960_V2_ReadControllerConfiguration;
+         Controller->ReadDeviceConfiguration =
+           DAC960_V2_ReadDeviceConfiguration;
+         Controller->ReportDeviceConfiguration =
+           DAC960_V2_ReportDeviceConfiguration;
+         Controller->QueueReadWriteCommand =
+           DAC960_V2_QueueReadWriteCommand;
+         break;
+       case DAC960_BA_Controller:
+         DAC960_BA_DisableInterrupts(BaseAddress);
+         DAC960_BA_AcknowledgeHardwareMailboxStatus(BaseAddress);
+         udelay(1000);
+         while (DAC960_BA_InitializationInProgressP(BaseAddress))
+           {
+             if (DAC960_BA_ReadErrorStatus(BaseAddress, &ErrorStatus,
+                                           &Parameter0, &Parameter1) &&
+                 DAC960_ReportErrorStatus(Controller, ErrorStatus,
+                                          Parameter0, Parameter1))
+               goto Failure;
+             udelay(10);
+           }
+         if (!DAC960_V2_EnableMemoryMailboxInterface(Controller))
+           {
+             DAC960_Error("Unable to Enable Memory Mailbox Interface "
+                          "for Controller at\n", Controller);
+             goto Failure;
+           }
+         DAC960_BA_EnableInterrupts(BaseAddress);
+         Controller->QueueCommand = DAC960_BA_QueueCommand;
+         Controller->ReadControllerConfiguration =
+           DAC960_V2_ReadControllerConfiguration;
+         Controller->ReadDeviceConfiguration =
+           DAC960_V2_ReadDeviceConfiguration;
+         Controller->ReportDeviceConfiguration =
+           DAC960_V2_ReportDeviceConfiguration;
+         Controller->QueueReadWriteCommand =
+           DAC960_V2_QueueReadWriteCommand;
+         break;
+       case DAC960_LP_Controller:
+         DAC960_LP_DisableInterrupts(BaseAddress);
+         DAC960_LP_AcknowledgeHardwareMailboxStatus(BaseAddress);
+         udelay(1000);
+         while (DAC960_LP_InitializationInProgressP(BaseAddress))
+           {
+             if (DAC960_LP_ReadErrorStatus(BaseAddress, &ErrorStatus,
+                                           &Parameter0, &Parameter1) &&
+                 DAC960_ReportErrorStatus(Controller, ErrorStatus,
+                                          Parameter0, Parameter1))
+               goto Failure;
+             udelay(10);
+           }
+         if (!DAC960_V2_EnableMemoryMailboxInterface(Controller))
+           {
+             DAC960_Error("Unable to Enable Memory Mailbox Interface "
+                          "for Controller at\n", Controller);
+             goto Failure;
+           }
+         DAC960_LP_EnableInterrupts(BaseAddress);
+         Controller->QueueCommand = DAC960_LP_QueueCommand;
+         Controller->ReadControllerConfiguration =
+           DAC960_V2_ReadControllerConfiguration;
+         Controller->ReadDeviceConfiguration =
+           DAC960_V2_ReadDeviceConfiguration;
+         Controller->ReportDeviceConfiguration =
+           DAC960_V2_ReportDeviceConfiguration;
+         Controller->QueueReadWriteCommand =
+           DAC960_V2_QueueReadWriteCommand;
+         break;
+       case DAC960_LA_Controller:
+         DAC960_LA_DisableInterrupts(BaseAddress);
+         DAC960_LA_AcknowledgeHardwareMailboxStatus(BaseAddress);
+         udelay(1000);
+         while (DAC960_LA_InitializationInProgressP(BaseAddress))
+           {
+             if (DAC960_LA_ReadErrorStatus(BaseAddress, &ErrorStatus,
+                                           &Parameter0, &Parameter1) &&
+                 DAC960_ReportErrorStatus(Controller, ErrorStatus,
+                                          Parameter0, Parameter1))
+               goto Failure;
+             udelay(10);
+           }
+         if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
+           {
+             DAC960_Error("Unable to Enable Memory Mailbox Interface "
+                          "for Controller at\n", Controller);
+             goto Failure;
+           }
+         DAC960_LA_EnableInterrupts(BaseAddress);
+         if (Controller->V1.DualModeMemoryMailboxInterface)
+           Controller->QueueCommand = DAC960_LA_QueueCommandDualMode;
+         else Controller->QueueCommand = DAC960_LA_QueueCommandSingleMode;
+         Controller->ReadControllerConfiguration =
+           DAC960_V1_ReadControllerConfiguration;
+         Controller->ReadDeviceConfiguration =
+           DAC960_V1_ReadDeviceConfiguration;
+         Controller->ReportDeviceConfiguration =
+           DAC960_V1_ReportDeviceConfiguration;
+         Controller->QueueReadWriteCommand =
+           DAC960_V1_QueueReadWriteCommand;
+         break;
+       case DAC960_PG_Controller:
+         DAC960_PG_DisableInterrupts(BaseAddress);
+         DAC960_PG_AcknowledgeHardwareMailboxStatus(BaseAddress);
+         udelay(1000);
+         while (DAC960_PG_InitializationInProgressP(BaseAddress))
+           {
+             if (DAC960_PG_ReadErrorStatus(BaseAddress, &ErrorStatus,
+                                           &Parameter0, &Parameter1) &&
+                 DAC960_ReportErrorStatus(Controller, ErrorStatus,
+                                          Parameter0, Parameter1))
+               goto Failure;
+             udelay(10);
+           }
+         if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
+           {
+             DAC960_Error("Unable to Enable Memory Mailbox Interface "
+                          "for Controller at\n", Controller);
+             goto Failure;
+           }
+         DAC960_PG_EnableInterrupts(BaseAddress);
+         if (Controller->V1.DualModeMemoryMailboxInterface)
+           Controller->QueueCommand = DAC960_PG_QueueCommandDualMode;
+         else Controller->QueueCommand = DAC960_PG_QueueCommandSingleMode;
+         Controller->ReadControllerConfiguration =
+           DAC960_V1_ReadControllerConfiguration;
+         Controller->ReadDeviceConfiguration =
+           DAC960_V1_ReadDeviceConfiguration;
+         Controller->ReportDeviceConfiguration =
+           DAC960_V1_ReportDeviceConfiguration;
+         Controller->QueueReadWriteCommand =
+           DAC960_V1_QueueReadWriteCommand;
+         break;
+       case DAC960_PD_Controller:
+         if (!request_region(Controller->IO_Address, 0x80,
+                             Controller->FullModelName)) {
+               DAC960_Error("IO port 0x%d busy for Controller at\n",
+                            Controller, Controller->IO_Address);
+               goto Failure;
+         }
+         DAC960_PD_DisableInterrupts(BaseAddress);
+         DAC960_PD_AcknowledgeStatus(BaseAddress);
+         udelay(1000);
+         while (DAC960_PD_InitializationInProgressP(BaseAddress))
+           {
+             if (DAC960_PD_ReadErrorStatus(BaseAddress, &ErrorStatus,
+                                           &Parameter0, &Parameter1) &&
+                 DAC960_ReportErrorStatus(Controller, ErrorStatus,
+                                          Parameter0, Parameter1))
+               goto Failure;
+             udelay(10);
+           }
+         if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
+           {
+             DAC960_Error("Unable to allocate DMA mapped memory "
+                          "for Controller at\n", Controller);
+             goto Failure;
+           }
+         DAC960_PD_EnableInterrupts(BaseAddress);
+         Controller->QueueCommand = DAC960_PD_QueueCommand;
+         Controller->ReadControllerConfiguration =
+           DAC960_V1_ReadControllerConfiguration;
+         Controller->ReadDeviceConfiguration =
+           DAC960_V1_ReadDeviceConfiguration;
+         Controller->ReportDeviceConfiguration =
+           DAC960_V1_ReportDeviceConfiguration;
+         Controller->QueueReadWriteCommand =
+           DAC960_V1_QueueReadWriteCommand;
+         break;
+       case DAC960_P_Controller:
+         if (!request_region(Controller->IO_Address, 0x80,
+                             Controller->FullModelName)){
+               DAC960_Error("IO port 0x%d busy for Controller at\n",
+                            Controller, Controller->IO_Address);
+               goto Failure;
+         }
+         DAC960_PD_DisableInterrupts(BaseAddress);
+         DAC960_PD_AcknowledgeStatus(BaseAddress);
+         udelay(1000);
+         while (DAC960_PD_InitializationInProgressP(BaseAddress))
+           {
+             if (DAC960_PD_ReadErrorStatus(BaseAddress, &ErrorStatus,
+                                           &Parameter0, &Parameter1) &&
+                 DAC960_ReportErrorStatus(Controller, ErrorStatus,
+                                          Parameter0, Parameter1))
+               goto Failure;
+             udelay(10);
+           }
+         if (!DAC960_V1_EnableMemoryMailboxInterface(Controller))
+           {
+             DAC960_Error("Unable to allocate DMA mapped memory"
+                          "for Controller at\n", Controller);
+             goto Failure;
+           }
+         DAC960_PD_EnableInterrupts(BaseAddress);
+         Controller->QueueCommand = DAC960_P_QueueCommand;
+         Controller->ReadControllerConfiguration =
+           DAC960_V1_ReadControllerConfiguration;
+         Controller->ReadDeviceConfiguration =
+           DAC960_V1_ReadDeviceConfiguration;
+         Controller->ReportDeviceConfiguration =
+           DAC960_V1_ReportDeviceConfiguration;
+         Controller->QueueReadWriteCommand =
+           DAC960_V1_QueueReadWriteCommand;
+         break;
+  }
+  /*
+     Acquire shared access to the IRQ Channel.
+  */
+  IRQ_Channel = PCI_Device->irq;
+  if (request_irq(IRQ_Channel, InterruptHandler, IRQF_SHARED,
+                     Controller->FullModelName, Controller) < 0)
+  {
+       DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
+                      Controller, Controller->IRQ_Channel);
+       goto Failure;
+  }
+  Controller->IRQ_Channel = IRQ_Channel;
+  Controller->InitialCommand.CommandIdentifier = 1;
+  Controller->InitialCommand.Controller = Controller;
+  Controller->Commands[0] = &Controller->InitialCommand;
+  Controller->FreeCommands = &Controller->InitialCommand;
+  return Controller;
+      
+Failure:
+  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_DetectCleanup(Controller);
+  DAC960_ControllerCount--;
+  return NULL;
+}
+
+/*
+  DAC960_InitializeController initializes Controller.
+*/
+
+static bool 
+DAC960_InitializeController(DAC960_Controller_T *Controller)
+{
+  if (DAC960_ReadControllerConfiguration(Controller) &&
+      DAC960_ReportControllerConfiguration(Controller) &&
+      DAC960_CreateAuxiliaryStructures(Controller) &&
+      DAC960_ReadDeviceConfiguration(Controller) &&
+      DAC960_ReportDeviceConfiguration(Controller) &&
+      DAC960_RegisterBlockDevice(Controller))
+    {
+      /*
+       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;
+      return true;
+    }
+  return false;
+}
+
+
+/*
+  DAC960_FinalizeController finalizes Controller.
+*/
+
+static void DAC960_FinalizeController(DAC960_Controller_T *Controller)
+{
+  if (Controller->ControllerInitialized)
+    {
+      unsigned long flags;
+
+      /*
+       * Acquiring and releasing lock here eliminates
+       * a very low probability race.
+       *
+       * The code below allocates controller command structures
+       * from the free list without holding the controller lock.
+       * This is safe assuming there is no other activity on
+       * the controller at the time.
+       * 
+       * But, there might be a monitoring command still
+       * in progress.  Setting the Shutdown flag while holding
+       * the lock ensures that there is no monitoring command
+       * in the interrupt handler currently, and any monitoring
+       * commands that complete from this time on will NOT return
+       * their command structure to the free list.
+       */
+
+      spin_lock_irqsave(&Controller->queue_lock, flags);
+      Controller->ShutdownMonitoringTimer = 1;
+      spin_unlock_irqrestore(&Controller->queue_lock, flags);
+
+      del_timer_sync(&Controller->MonitoringTimer);
+      if (Controller->FirmwareType == DAC960_V1_Controller)
+       {
+         DAC960_Notice("Flushing Cache...", Controller);
+         DAC960_V1_ExecuteType3(Controller, DAC960_V1_Flush, 0);
+         DAC960_Notice("done\n", Controller);
+
+         if (Controller->HardwareType == DAC960_PD_Controller)
+             release_region(Controller->IO_Address, 0x80);
+       }
+      else
+       {
+         DAC960_Notice("Flushing Cache...", Controller);
+         DAC960_V2_DeviceOperation(Controller, DAC960_V2_PauseDevice,
+                                   DAC960_V2_RAID_Controller);
+         DAC960_Notice("done\n", Controller);
+       }
+    }
+  DAC960_UnregisterBlockDevice(Controller);
+  DAC960_DestroyAuxiliaryStructures(Controller);
+  DAC960_DestroyProcEntries(Controller);
+  DAC960_DetectCleanup(Controller);
+}
+
+
+/*
+  DAC960_Probe verifies controller's existence and
+  initializes the DAC960 Driver for that controller.
+*/
+
+static int 
+DAC960_Probe(struct pci_dev *dev, const struct pci_device_id *entry)
+{
+  int disk;
+  DAC960_Controller_T *Controller;
+
+  if (DAC960_ControllerCount == DAC960_MaxControllers)
+  {
+       DAC960_Error("More than %d DAC960 Controllers detected - "
+                       "ignoring from Controller at\n",
+                       NULL, DAC960_MaxControllers);
+       return -ENODEV;
+  }
+
+  Controller = DAC960_DetectController(dev, entry);
+  if (!Controller)
+       return -ENODEV;
+
+  if (!DAC960_InitializeController(Controller)) {
+       DAC960_FinalizeController(Controller);
+       return -ENODEV;
+  }
+
+  for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) {
+        set_capacity(Controller->disks[disk], disk_size(Controller, disk));
+        add_disk(Controller->disks[disk]);
+  }
+  DAC960_CreateProcEntries(Controller);
+  return 0;
+}
+
+
+/*
+  DAC960_Finalize finalizes the DAC960 Driver.
+*/
+
+static void DAC960_Remove(struct pci_dev *PCI_Device)
+{
+  int Controller_Number = (long)pci_get_drvdata(PCI_Device);
+  DAC960_Controller_T *Controller = DAC960_Controllers[Controller_Number];
+  if (Controller != NULL)
+      DAC960_FinalizeController(Controller);
+}
+
+
+/*
+  DAC960_V1_QueueReadWriteCommand prepares and queues a Read/Write Command for
+  DAC960 V1 Firmware Controllers.
+*/
+
+static void DAC960_V1_QueueReadWriteCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_ScatterGatherSegment_T *ScatterGatherList =
+                                       Command->V1.ScatterGatherList;
+  struct scatterlist *ScatterList = Command->V1.ScatterList;
+
+  DAC960_V1_ClearCommand(Command);
+
+  if (Command->SegmentCount == 1)
+    {
+      if (Command->DmaDirection == PCI_DMA_FROMDEVICE)
+       CommandMailbox->Type5.CommandOpcode = DAC960_V1_Read;
+      else 
+        CommandMailbox->Type5.CommandOpcode = DAC960_V1_Write;
+
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress =
+                       (DAC960_BusAddress32_T)sg_dma_address(ScatterList);     
+    }
+  else
+    {
+      int i;
+
+      if (Command->DmaDirection == PCI_DMA_FROMDEVICE)
+       CommandMailbox->Type5.CommandOpcode = DAC960_V1_ReadWithScatterGather;
+      else
+       CommandMailbox->Type5.CommandOpcode = DAC960_V1_WriteWithScatterGather;
+
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Command->V1.ScatterGatherListDMA;
+
+      CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount;
+
+      for (i = 0; i < Command->SegmentCount; i++, ScatterList++, ScatterGatherList++) {
+               ScatterGatherList->SegmentDataPointer =
+                       (DAC960_BusAddress32_T)sg_dma_address(ScatterList);
+               ScatterGatherList->SegmentByteCount =
+                       (DAC960_ByteCount32_T)sg_dma_len(ScatterList);
+      }
+    }
+  DAC960_QueueCommand(Command);
+}
+
+
+/*
+  DAC960_V2_QueueReadWriteCommand prepares and queues a Read/Write Command for
+  DAC960 V2 Firmware Controllers.
+*/
+
+static void DAC960_V2_QueueReadWriteCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  struct scatterlist *ScatterList = Command->V2.ScatterList;
+
+  DAC960_V2_ClearCommand(Command);
+
+  CommandMailbox->SCSI_10.CommandOpcode = DAC960_V2_SCSI_10;
+  CommandMailbox->SCSI_10.CommandControlBits.DataTransferControllerToHost =
+    (Command->DmaDirection == PCI_DMA_FROMDEVICE);
+  CommandMailbox->SCSI_10.DataTransferSize =
+    Command->BlockCount << DAC960_BlockSizeBits;
+  CommandMailbox->SCSI_10.RequestSenseBusAddress = Command->V2.RequestSenseDMA;
+  CommandMailbox->SCSI_10.PhysicalDevice =
+    Controller->V2.LogicalDriveToVirtualDevice[Command->LogicalDriveNumber];
+  CommandMailbox->SCSI_10.RequestSenseSize = sizeof(DAC960_SCSI_RequestSense_T);
+  CommandMailbox->SCSI_10.CDBLength = 10;
+  CommandMailbox->SCSI_10.SCSI_CDB[0] =
+    (Command->DmaDirection == PCI_DMA_FROMDEVICE ? 0x28 : 0x2A);
+  CommandMailbox->SCSI_10.SCSI_CDB[2] = Command->BlockNumber >> 24;
+  CommandMailbox->SCSI_10.SCSI_CDB[3] = Command->BlockNumber >> 16;
+  CommandMailbox->SCSI_10.SCSI_CDB[4] = Command->BlockNumber >> 8;
+  CommandMailbox->SCSI_10.SCSI_CDB[5] = Command->BlockNumber;
+  CommandMailbox->SCSI_10.SCSI_CDB[7] = Command->BlockCount >> 8;
+  CommandMailbox->SCSI_10.SCSI_CDB[8] = Command->BlockCount;
+
+  if (Command->SegmentCount == 1)
+    {
+      CommandMailbox->SCSI_10.DataTransferMemoryAddress
+                            .ScatterGatherSegments[0]
+                            .SegmentDataPointer =
+       (DAC960_BusAddress64_T)sg_dma_address(ScatterList);
+      CommandMailbox->SCSI_10.DataTransferMemoryAddress
+                            .ScatterGatherSegments[0]
+                            .SegmentByteCount =
+       CommandMailbox->SCSI_10.DataTransferSize;
+    }
+  else
+    {
+      DAC960_V2_ScatterGatherSegment_T *ScatterGatherList;
+      int i;
+
+      if (Command->SegmentCount > 2)
+       {
+          ScatterGatherList = Command->V2.ScatterGatherList;
+         CommandMailbox->SCSI_10.CommandControlBits
+                        .AdditionalScatterGatherListMemory = true;
+         CommandMailbox->SCSI_10.DataTransferMemoryAddress
+               .ExtendedScatterGather.ScatterGatherList0Length = Command->SegmentCount;
+         CommandMailbox->SCSI_10.DataTransferMemoryAddress
+                        .ExtendedScatterGather.ScatterGatherList0Address =
+           Command->V2.ScatterGatherListDMA;
+       }
+      else
+       ScatterGatherList = CommandMailbox->SCSI_10.DataTransferMemoryAddress
+                                .ScatterGatherSegments;
+
+      for (i = 0; i < Command->SegmentCount; i++, ScatterList++, ScatterGatherList++) {
+               ScatterGatherList->SegmentDataPointer =
+                       (DAC960_BusAddress64_T)sg_dma_address(ScatterList);
+               ScatterGatherList->SegmentByteCount =
+                       (DAC960_ByteCount64_T)sg_dma_len(ScatterList);
+      }
+    }
+  DAC960_QueueCommand(Command);
+}
+
+
+static int DAC960_process_queue(DAC960_Controller_T *Controller, struct request_queue *req_q)
+{
+       struct request *Request;
+       DAC960_Command_T *Command;
+
+   while(1) {
+       Request = blk_peek_request(req_q);
+       if (!Request)
+               return 1;
+
+       Command = DAC960_AllocateCommand(Controller);
+       if (Command == NULL)
+               return 0;
+
+       if (rq_data_dir(Request) == READ) {
+               Command->DmaDirection = PCI_DMA_FROMDEVICE;
+               Command->CommandType = DAC960_ReadCommand;
+       } else {
+               Command->DmaDirection = PCI_DMA_TODEVICE;
+               Command->CommandType = DAC960_WriteCommand;
+       }
+       Command->Completion = Request->end_io_data;
+       Command->LogicalDriveNumber = (long)Request->rq_disk->private_data;
+       Command->BlockNumber = blk_rq_pos(Request);
+       Command->BlockCount = blk_rq_sectors(Request);
+       Command->Request = Request;
+       blk_start_request(Request);
+       Command->SegmentCount = blk_rq_map_sg(req_q,
+                 Command->Request, Command->cmd_sglist);
+       /* pci_map_sg MAY change the value of SegCount */
+       Command->SegmentCount = pci_map_sg(Controller->PCIDevice, Command->cmd_sglist,
+                Command->SegmentCount, Command->DmaDirection);
+
+       DAC960_QueueReadWriteCommand(Command);
+  }
+}
+
+/*
+  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 void DAC960_ProcessRequest(DAC960_Controller_T *controller)
+{
+       int i;
+
+       if (!controller->ControllerInitialized)
+               return;
+
+       /* Do this better later! */
+       for (i = controller->req_q_index; i < DAC960_MaxLogicalDrives; i++) {
+               struct request_queue *req_q = controller->RequestQueue[i];
+
+               if (req_q == NULL)
+                       continue;
+
+               if (!DAC960_process_queue(controller, req_q)) {
+                       controller->req_q_index = i;
+                       return;
+               }
+       }
+
+       if (controller->req_q_index == 0)
+               return;
+
+       for (i = 0; i < controller->req_q_index; i++) {
+               struct request_queue *req_q = controller->RequestQueue[i];
+
+               if (req_q == NULL)
+                       continue;
+
+               if (!DAC960_process_queue(controller, req_q)) {
+                       controller->req_q_index = i;
+                       return;
+               }
+       }
+}
+
+
+/*
+  DAC960_queue_partial_rw extracts one bio from the request already
+  associated with argument command, and construct a new command block to retry I/O
+  only on that bio.  Queue that command to the controller.
+
+  This function re-uses a previously-allocated Command,
+       there is no failure mode from trying to allocate a command.
+*/
+
+static void DAC960_queue_partial_rw(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  struct request *Request = Command->Request;
+  struct request_queue *req_q = Controller->RequestQueue[Command->LogicalDriveNumber];
+
+  if (Command->DmaDirection == PCI_DMA_FROMDEVICE)
+    Command->CommandType = DAC960_ReadRetryCommand;
+  else
+    Command->CommandType = DAC960_WriteRetryCommand;
+
+  /*
+   * We could be more efficient with these mapping requests
+   * and map only the portions that we need.  But since this
+   * code should almost never be called, just go with a
+   * simple coding.
+   */
+  (void)blk_rq_map_sg(req_q, Command->Request, Command->cmd_sglist);
+
+  (void)pci_map_sg(Controller->PCIDevice, Command->cmd_sglist, 1, Command->DmaDirection);
+  /*
+   * Resubmitting the request sector at a time is really tedious.
+   * But, this should almost never happen.  So, we're willing to pay
+   * this price so that in the end, as much of the transfer is completed
+   * successfully as possible.
+   */
+  Command->SegmentCount = 1;
+  Command->BlockNumber = blk_rq_pos(Request);
+  Command->BlockCount = 1;
+  DAC960_QueueReadWriteCommand(Command);
+  return;
+}
+
+/*
+  DAC960_RequestFunction is the I/O Request Function for DAC960 Controllers.
+*/
+
+static void DAC960_RequestFunction(struct request_queue *RequestQueue)
+{
+       DAC960_ProcessRequest(RequestQueue->queuedata);
+}
+
+/*
+  DAC960_ProcessCompletedBuffer performs completion processing for an
+  individual Buffer.
+*/
+
+static inline bool DAC960_ProcessCompletedRequest(DAC960_Command_T *Command,
+                                                bool SuccessfulIO)
+{
+       struct request *Request = Command->Request;
+       int Error = SuccessfulIO ? 0 : -EIO;
+
+       pci_unmap_sg(Command->Controller->PCIDevice, Command->cmd_sglist,
+               Command->SegmentCount, Command->DmaDirection);
+
+        if (!__blk_end_request(Request, Error, Command->BlockCount << 9)) {
+               if (Command->Completion) {
+                       complete(Command->Completion);
+                       Command->Completion = NULL;
+               }
+               return true;
+       }
+       return false;
+}
+
+/*
+  DAC960_V1_ReadWriteError prints an appropriate error message for Command
+  when an error occurs on a Read or Write operation.
+*/
+
+static void DAC960_V1_ReadWriteError(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  unsigned 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:
+    case DAC960_QueuedCommand:
+      break;
+    }
+  switch (Command->V1.CommandStatus)
+    {
+    case DAC960_V1_IrrecoverableDataError:
+      DAC960_Error("Irrecoverable Data Error on %s:\n",
+                  Controller, CommandName);
+      break;
+    case DAC960_V1_LogicalDriveNonexistentOrOffline:
+      DAC960_Error("Logical Drive Nonexistent or Offline on %s:\n",
+                  Controller, CommandName);
+      break;
+    case DAC960_V1_AccessBeyondEndOfLogicalDrive:
+      DAC960_Error("Attempt to Access Beyond End of Logical Drive "
+                  "on %s:\n", Controller, CommandName);
+      break;
+    case DAC960_V1_BadDataEncountered:
+      DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName);
+      break;
+    default:
+      DAC960_Error("Unexpected Error Status %04X on %s:\n",
+                  Controller, Command->V1.CommandStatus, CommandName);
+      break;
+    }
+  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %u..%u\n",
+              Controller, Controller->ControllerNumber,
+              Command->LogicalDriveNumber, Command->BlockNumber,
+              Command->BlockNumber + Command->BlockCount - 1);
+}
+
+
+/*
+  DAC960_V1_ProcessCompletedCommand performs completion processing for Command
+  for DAC960 V1 Firmware Controllers.
+*/
+
+static void DAC960_V1_ProcessCompletedCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandType_T CommandType = Command->CommandType;
+  DAC960_V1_CommandOpcode_T CommandOpcode =
+    Command->V1.CommandMailbox.Common.CommandOpcode;
+  DAC960_V1_CommandStatus_T CommandStatus = Command->V1.CommandStatus;
+
+  if (CommandType == DAC960_ReadCommand ||
+      CommandType == DAC960_WriteCommand)
+    {
+
+#ifdef FORCE_RETRY_DEBUG
+      CommandStatus = DAC960_V1_IrrecoverableDataError;
+#endif
+
+      if (CommandStatus == DAC960_V1_NormalCompletion) {
+
+               if (!DAC960_ProcessCompletedRequest(Command, true))
+                       BUG();
+
+      } else if (CommandStatus == DAC960_V1_IrrecoverableDataError ||
+               CommandStatus == DAC960_V1_BadDataEncountered)
+       {
+         /*
+          * break the command down into pieces and resubmit each
+          * piece, hoping that some of them will succeed.
+          */
+          DAC960_queue_partial_rw(Command);
+          return;
+       }
+      else
+       {
+         if (CommandStatus != DAC960_V1_LogicalDriveNonexistentOrOffline)
+           DAC960_V1_ReadWriteError(Command);
+
+        if (!DAC960_ProcessCompletedRequest(Command, false))
+               BUG();
+       }
+    }
+  else if (CommandType == DAC960_ReadRetryCommand ||
+          CommandType == DAC960_WriteRetryCommand)
+    {
+      bool normal_completion;
+#ifdef FORCE_RETRY_FAILURE_DEBUG
+      static int retry_count = 1;
+#endif
+      /*
+        Perform completion processing for the portion that was
+        retried, and submit the next portion, if any.
+      */
+      normal_completion = true;
+      if (CommandStatus != DAC960_V1_NormalCompletion) {
+        normal_completion = false;
+        if (CommandStatus != DAC960_V1_LogicalDriveNonexistentOrOffline)
+            DAC960_V1_ReadWriteError(Command);
+      }
+
+#ifdef FORCE_RETRY_FAILURE_DEBUG
+      if (!(++retry_count % 10000)) {
+             printk("V1 error retry failure test\n");
+             normal_completion = false;
+              DAC960_V1_ReadWriteError(Command);
+      }
+#endif
+
+      if (!DAC960_ProcessCompletedRequest(Command, normal_completion)) {
+        DAC960_queue_partial_rw(Command);
+        return;
+      }
+    }
+
+  else if (CommandType == DAC960_MonitoringCommand)
+    {
+      if (Controller->ShutdownMonitoringTimer)
+             return;
+      if (CommandOpcode == DAC960_V1_Enquiry)
+       {
+         DAC960_V1_Enquiry_T *OldEnquiry = &Controller->V1.Enquiry;
+         DAC960_V1_Enquiry_T *NewEnquiry = Controller->V1.NewEnquiry;
+         unsigned int OldCriticalLogicalDriveCount =
+           OldEnquiry->CriticalLogicalDriveCount;
+         unsigned int NewCriticalLogicalDriveCount =
+           NewEnquiry->CriticalLogicalDriveCount;
+         if (NewEnquiry->NumberOfLogicalDrives > Controller->LogicalDriveCount)
+           {
+             int LogicalDriveNumber = Controller->LogicalDriveCount - 1;
+             while (++LogicalDriveNumber < NewEnquiry->NumberOfLogicalDrives)
+               DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+                               "Now Exists\n", Controller,
+                               LogicalDriveNumber,
+                               Controller->ControllerNumber,
+                               LogicalDriveNumber);
+             Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
+             DAC960_ComputeGenericDiskInfo(Controller);
+           }
+         if (NewEnquiry->NumberOfLogicalDrives < Controller->LogicalDriveCount)
+           {
+             int LogicalDriveNumber = NewEnquiry->NumberOfLogicalDrives - 1;
+             while (++LogicalDriveNumber < Controller->LogicalDriveCount)
+               DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+                               "No Longer Exists\n", Controller,
+                               LogicalDriveNumber,
+                               Controller->ControllerNumber,
+                               LogicalDriveNumber);
+             Controller->LogicalDriveCount = NewEnquiry->NumberOfLogicalDrives;
+             DAC960_ComputeGenericDiskInfo(Controller);
+           }
+         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 ||
+             time_after_eq(jiffies, Controller->SecondaryMonitoringTime
+              + DAC960_SecondaryMonitoringInterval))
+           {
+             Controller->V1.NeedLogicalDriveInformation = true;
+             Controller->V1.NewEventLogSequenceNumber =
+               NewEnquiry->EventLogSequenceNumber;
+             Controller->V1.NeedErrorTableInformation = true;
+             Controller->V1.NeedDeviceStateInformation = true;
+             Controller->V1.StartDeviceStateScan = true;
+             Controller->V1.NeedBackgroundInitializationStatus =
+               Controller->V1.BackgroundInitializationStatusSupported;
+             Controller->SecondaryMonitoringTime = jiffies;
+           }
+         if (NewEnquiry->RebuildFlag == DAC960_V1_StandbyRebuildInProgress ||
+             NewEnquiry->RebuildFlag
+             == DAC960_V1_BackgroundRebuildInProgress ||
+             OldEnquiry->RebuildFlag == DAC960_V1_StandbyRebuildInProgress ||
+             OldEnquiry->RebuildFlag == DAC960_V1_BackgroundRebuildInProgress)
+           {
+             Controller->V1.NeedRebuildProgress = true;
+             Controller->V1.RebuildProgressFirst =
+               (NewEnquiry->CriticalLogicalDriveCount <
+                OldEnquiry->CriticalLogicalDriveCount);
+           }
+         if (OldEnquiry->RebuildFlag == DAC960_V1_BackgroundCheckInProgress)
+           switch (NewEnquiry->RebuildFlag)
+             {
+             case DAC960_V1_NoStandbyRebuildOrCheckInProgress:
+               DAC960_Progress("Consistency Check Completed Successfully\n",
+                               Controller);
+               break;
+             case DAC960_V1_StandbyRebuildInProgress:
+             case DAC960_V1_BackgroundRebuildInProgress:
+               break;
+             case DAC960_V1_BackgroundCheckInProgress:
+               Controller->V1.NeedConsistencyCheckProgress = true;
+               break;
+             case DAC960_V1_StandbyRebuildCompletedWithError:
+               DAC960_Progress("Consistency Check Completed with Error\n",
+                               Controller);
+               break;
+             case DAC960_V1_BackgroundRebuildOrCheckFailed_DriveFailed:
+               DAC960_Progress("Consistency Check Failed - "
+                               "Physical Device Failed\n", Controller);
+               break;
+             case DAC960_V1_BackgroundRebuildOrCheckFailed_LogicalDriveFailed:
+               DAC960_Progress("Consistency Check Failed - "
+                               "Logical Drive Failed\n", Controller);
+               break;
+             case DAC960_V1_BackgroundRebuildOrCheckFailed_OtherCauses:
+               DAC960_Progress("Consistency Check Failed - Other Causes\n",
+                               Controller);
+               break;
+             case DAC960_V1_BackgroundRebuildOrCheckSuccessfullyTerminated:
+               DAC960_Progress("Consistency Check Successfully Terminated\n",
+                               Controller);
+               break;
+             }
+         else if (NewEnquiry->RebuildFlag
+                  == DAC960_V1_BackgroundCheckInProgress)
+           Controller->V1.NeedConsistencyCheckProgress = true;
+         Controller->MonitoringAlertMode =
+           (NewEnquiry->CriticalLogicalDriveCount > 0 ||
+            NewEnquiry->OfflineLogicalDriveCount > 0 ||
+            NewEnquiry->DeadDriveCount > 0);
+         if (NewEnquiry->RebuildFlag > DAC960_V1_BackgroundCheckInProgress)
+           {
+             Controller->V1.PendingRebuildFlag = NewEnquiry->RebuildFlag;
+             Controller->V1.RebuildFlagPending = true;
+           }
+         memcpy(&Controller->V1.Enquiry, &Controller->V1.NewEnquiry,
+                sizeof(DAC960_V1_Enquiry_T));
+       }
+      else if (CommandOpcode == DAC960_V1_PerformEventLogOperation)
+       {
+         static char
+           *DAC960_EventMessages[] =
+              { "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" };
+         DAC960_V1_EventLogEntry_T *EventLogEntry =
+               Controller->V1.EventLogEntry;
+         if (EventLogEntry->SequenceNumber ==
+             Controller->V1.OldEventLogSequenceNumber)
+           {
+             unsigned char SenseKey = EventLogEntry->SenseKey;
+             unsigned char AdditionalSenseCode =
+               EventLogEntry->AdditionalSenseCode;
+             unsigned char AdditionalSenseCodeQualifier =
+               EventLogEntry->AdditionalSenseCodeQualifier;
+             if (SenseKey == DAC960_SenseKey_VendorSpecific &&
+                 AdditionalSenseCode == 0x80 &&
+                 AdditionalSenseCodeQualifier <
+                 ARRAY_SIZE(DAC960_EventMessages))
+               DAC960_Critical("Physical Device %d:%d %s\n", Controller,
+                               EventLogEntry->Channel,
+                               EventLogEntry->TargetID,
+                               DAC960_EventMessages[
+                                 AdditionalSenseCodeQualifier]);
+             else if (SenseKey == DAC960_SenseKey_UnitAttention &&
+                      AdditionalSenseCode == 0x29)
+               {
+                 if (Controller->MonitoringTimerCount > 0)
+                   Controller->V1.DeviceResetCount[EventLogEntry->Channel]
+                                                  [EventLogEntry->TargetID]++;
+               }
+             else if (!(SenseKey == DAC960_SenseKey_NoSense ||
+                        (SenseKey == DAC960_SenseKey_NotReady &&
+                         AdditionalSenseCode == 0x04 &&
+                         (AdditionalSenseCodeQualifier == 0x01 ||
+                          AdditionalSenseCodeQualifier == 0x02))))
+               {
+                 DAC960_Critical("Physical Device %d:%d Error Log: "
+                                 "Sense Key = %X, ASC = %02X, ASCQ = %02X\n",
+                                 Controller,
+                                 EventLogEntry->Channel,
+                                 EventLogEntry->TargetID,
+                                 SenseKey,
+                                 AdditionalSenseCode,
+                                 AdditionalSenseCodeQualifier);
+                 DAC960_Critical("Physical Device %d:%d Error Log: "
+                                 "Information = %02X%02X%02X%02X "
+                                 "%02X%02X%02X%02X\n",
+                                 Controller,
+                                 EventLogEntry->Channel,
+                                 EventLogEntry->TargetID,
+                                 EventLogEntry->Information[0],
+                                 EventLogEntry->Information[1],
+                                 EventLogEntry->Information[2],
+                                 EventLogEntry->Information[3],
+                                 EventLogEntry->CommandSpecificInformation[0],
+                                 EventLogEntry->CommandSpecificInformation[1],
+                                 EventLogEntry->CommandSpecificInformation[2],
+                                 EventLogEntry->CommandSpecificInformation[3]);
+               }
+           }
+         Controller->V1.OldEventLogSequenceNumber++;
+       }
+      else if (CommandOpcode == DAC960_V1_GetErrorTable)
+       {
+         DAC960_V1_ErrorTable_T *OldErrorTable = &Controller->V1.ErrorTable;
+         DAC960_V1_ErrorTable_T *NewErrorTable = Controller->V1.NewErrorTable;
+         int Channel, TargetID;
+         for (Channel = 0; Channel < Controller->Channels; Channel++)
+           for (TargetID = 0; TargetID < Controller->Targets; TargetID++)
+             {
+               DAC960_V1_ErrorTableEntry_T *NewErrorEntry =
+                 &NewErrorTable->ErrorTableEntries[Channel][TargetID];
+               DAC960_V1_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 Device %d:%d Errors: "
+                                 "Parity = %d, Soft = %d, "
+                                 "Hard = %d, Misc = %d\n",
+                                 Controller, Channel, TargetID,
+                                 NewErrorEntry->ParityErrorCount,
+                                 NewErrorEntry->SoftErrorCount,
+                                 NewErrorEntry->HardErrorCount,
+                                 NewErrorEntry->MiscErrorCount);
+             }
+         memcpy(&Controller->V1.ErrorTable, Controller->V1.NewErrorTable,
+                sizeof(DAC960_V1_ErrorTable_T));
+       }
+      else if (CommandOpcode == DAC960_V1_GetDeviceState)
+       {
+         DAC960_V1_DeviceState_T *OldDeviceState =
+           &Controller->V1.DeviceState[Controller->V1.DeviceStateChannel]
+                                      [Controller->V1.DeviceStateTargetID];
+         DAC960_V1_DeviceState_T *NewDeviceState =
+           Controller->V1.NewDeviceState;
+         if (NewDeviceState->DeviceState != OldDeviceState->DeviceState)
+           DAC960_Critical("Physical Device %d:%d is now %s\n", Controller,
+                           Controller->V1.DeviceStateChannel,
+                           Controller->V1.DeviceStateTargetID,
+                           (NewDeviceState->DeviceState
+                            == DAC960_V1_Device_Dead
+                            ? "DEAD"
+                            : NewDeviceState->DeviceState
+                              == DAC960_V1_Device_WriteOnly
+                              ? "WRITE-ONLY"
+                              : NewDeviceState->DeviceState
+                                == DAC960_V1_Device_Online
+                                ? "ONLINE" : "STANDBY"));
+         if (OldDeviceState->DeviceState == DAC960_V1_Device_Dead &&
+             NewDeviceState->DeviceState != DAC960_V1_Device_Dead)
+           {
+             Controller->V1.NeedDeviceInquiryInformation = true;
+             Controller->V1.NeedDeviceSerialNumberInformation = true;
+             Controller->V1.DeviceResetCount
+                            [Controller->V1.DeviceStateChannel]
+                            [Controller->V1.DeviceStateTargetID] = 0;
+           }
+         memcpy(OldDeviceState, NewDeviceState,
+                sizeof(DAC960_V1_DeviceState_T));
+       }
+      else if (CommandOpcode == DAC960_V1_GetLogicalDriveInformation)
+       {
+         int LogicalDriveNumber;
+         for (LogicalDriveNumber = 0;
+              LogicalDriveNumber < Controller->LogicalDriveCount;
+              LogicalDriveNumber++)
+           {
+             DAC960_V1_LogicalDriveInformation_T *OldLogicalDriveInformation =
+               &Controller->V1.LogicalDriveInformation[LogicalDriveNumber];
+             DAC960_V1_LogicalDriveInformation_T *NewLogicalDriveInformation =
+               &(*Controller->V1.NewLogicalDriveInformation)[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_V1_LogicalDrive_Online
+                                ? "ONLINE"
+                                : NewLogicalDriveInformation->LogicalDriveState
+                                  == DAC960_V1_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"));
+           }
+         memcpy(&Controller->V1.LogicalDriveInformation,
+                Controller->V1.NewLogicalDriveInformation,
+                sizeof(DAC960_V1_LogicalDriveInformationArray_T));
+       }
+      else if (CommandOpcode == DAC960_V1_GetRebuildProgress)
+       {
+         unsigned int LogicalDriveNumber =
+           Controller->V1.RebuildProgress->LogicalDriveNumber;
+         unsigned int LogicalDriveSize =
+           Controller->V1.RebuildProgress->LogicalDriveSize;
+         unsigned int BlocksCompleted =
+           LogicalDriveSize - Controller->V1.RebuildProgress->RemainingBlocks;
+         if (CommandStatus == DAC960_V1_NoRebuildOrCheckInProgress &&
+             Controller->V1.LastRebuildStatus == DAC960_V1_NormalCompletion)
+           CommandStatus = DAC960_V1_RebuildSuccessful;
+         switch (CommandStatus)
+           {
+           case DAC960_V1_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_V1_RebuildFailed_LogicalDriveFailure:
+             DAC960_Progress("Rebuild Failed due to "
+                             "Logical Drive Failure\n", Controller);
+             break;
+           case DAC960_V1_RebuildFailed_BadBlocksOnOther:
+             DAC960_Progress("Rebuild Failed due to "
+                             "Bad Blocks on Other Drives\n", Controller);
+             break;
+           case DAC960_V1_RebuildFailed_NewDriveFailed:
+             DAC960_Progress("Rebuild Failed due to "
+                             "Failure of Drive Being Rebuilt\n", Controller);
+             break;
+           case DAC960_V1_NoRebuildOrCheckInProgress:
+             break;
+           case DAC960_V1_RebuildSuccessful:
+             DAC960_Progress("Rebuild Completed Successfully\n", Controller);
+             break;
+           case DAC960_V1_RebuildSuccessfullyTerminated:
+             DAC960_Progress("Rebuild Successfully Terminated\n", Controller);
+             break;
+           }
+         Controller->V1.LastRebuildStatus = CommandStatus;
+         if (CommandType != DAC960_MonitoringCommand &&
+             Controller->V1.RebuildStatusPending)
+           {
+             Command->V1.CommandStatus = Controller->V1.PendingRebuildStatus;
+             Controller->V1.RebuildStatusPending = false;
+           }
+         else if (CommandType == DAC960_MonitoringCommand &&
+                  CommandStatus != DAC960_V1_NormalCompletion &&
+                  CommandStatus != DAC960_V1_NoRebuildOrCheckInProgress)
+           {
+             Controller->V1.PendingRebuildStatus = CommandStatus;
+             Controller->V1.RebuildStatusPending = true;
+           }
+       }
+      else if (CommandOpcode == DAC960_V1_RebuildStat)
+       {
+         unsigned int LogicalDriveNumber =
+           Controller->V1.RebuildProgress->LogicalDriveNumber;
+         unsigned int LogicalDriveSize =
+           Controller->V1.RebuildProgress->LogicalDriveSize;
+         unsigned int BlocksCompleted =
+           LogicalDriveSize - Controller->V1.RebuildProgress->RemainingBlocks;
+         if (CommandStatus == DAC960_V1_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;
+           }
+       }
+      else if (CommandOpcode == DAC960_V1_BackgroundInitializationControl)
+       {
+         unsigned int LogicalDriveNumber =
+           Controller->V1.BackgroundInitializationStatus->LogicalDriveNumber;
+         unsigned int LogicalDriveSize =
+           Controller->V1.BackgroundInitializationStatus->LogicalDriveSize;
+         unsigned int BlocksCompleted =
+           Controller->V1.BackgroundInitializationStatus->BlocksCompleted;
+         switch (CommandStatus)
+           {
+           case DAC960_V1_NormalCompletion:
+             switch (Controller->V1.BackgroundInitializationStatus->Status)
+               {
+               case DAC960_V1_BackgroundInitializationInvalid:
+                 break;
+               case DAC960_V1_BackgroundInitializationStarted:
+                 DAC960_Progress("Background Initialization Started\n",
+                                 Controller);
+                 break;
+               case DAC960_V1_BackgroundInitializationInProgress:
+                 if (BlocksCompleted ==
+                     Controller->V1.LastBackgroundInitializationStatus.
+                               BlocksCompleted &&
+                     LogicalDriveNumber ==
+                     Controller->V1.LastBackgroundInitializationStatus.
+                               LogicalDriveNumber)
+                   break;
+                 Controller->EphemeralProgressMessage = true;
+                 DAC960_Progress("Background Initialization 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_V1_BackgroundInitializationSuspended:
+                 DAC960_Progress("Background Initialization Suspended\n",
+                                 Controller);
+                 break;
+               case DAC960_V1_BackgroundInitializationCancelled:
+                 DAC960_Progress("Background Initialization Cancelled\n",
+                                 Controller);
+                 break;
+               }
+             memcpy(&Controller->V1.LastBackgroundInitializationStatus,
+                    Controller->V1.BackgroundInitializationStatus,
+                    sizeof(DAC960_V1_BackgroundInitializationStatus_T));
+             break;
+           case DAC960_V1_BackgroundInitSuccessful:
+             if (Controller->V1.BackgroundInitializationStatus->Status ==
+                 DAC960_V1_BackgroundInitializationInProgress)
+               DAC960_Progress("Background Initialization "
+                               "Completed Successfully\n", Controller);
+             Controller->V1.BackgroundInitializationStatus->Status =
+               DAC960_V1_BackgroundInitializationInvalid;
+             break;
+           case DAC960_V1_BackgroundInitAborted:
+             if (Controller->V1.BackgroundInitializationStatus->Status ==
+                 DAC960_V1_BackgroundInitializationInProgress)
+               DAC960_Progress("Background Initialization Aborted\n",
+                               Controller);
+             Controller->V1.BackgroundInitializationStatus->Status =
+               DAC960_V1_BackgroundInitializationInvalid;
+             break;
+           case DAC960_V1_NoBackgroundInitInProgress:
+             break;
+           }
+       } 
+      else if (CommandOpcode == DAC960_V1_DCDB)
+       {
+          /*
+            This is a bit ugly.
+
+            The InquiryStandardData and 
+            the InquiryUntitSerialNumber information
+            retrieval operations BOTH use the DAC960_V1_DCDB
+            commands.  the test above can't distinguish between
+            these two cases.
+
+            Instead, we rely on the order of code later in this
+             function to ensure that DeviceInquiryInformation commands
+             are submitted before DeviceSerialNumber commands.
+          */
+          if (Controller->V1.NeedDeviceInquiryInformation)
+            {
+               DAC960_SCSI_Inquiry_T *InquiryStandardData =
+                       &Controller->V1.InquiryStandardData
+                               [Controller->V1.DeviceStateChannel]
+                               [Controller->V1.DeviceStateTargetID];
+               if (CommandStatus != DAC960_V1_NormalCompletion)
+                  {
+                       memset(InquiryStandardData, 0,
+                               sizeof(DAC960_SCSI_Inquiry_T));
+                       InquiryStandardData->PeripheralDeviceType = 0x1F;
+                   }
+                else
+                       memcpy(InquiryStandardData, 
+                               Controller->V1.NewInquiryStandardData,
+                               sizeof(DAC960_SCSI_Inquiry_T));
+                Controller->V1.NeedDeviceInquiryInformation = false;
+              }
+          else if (Controller->V1.NeedDeviceSerialNumberInformation) 
+              {
+               DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+                 &Controller->V1.InquiryUnitSerialNumber
+                               [Controller->V1.DeviceStateChannel]
+                               [Controller->V1.DeviceStateTargetID];
+                if (CommandStatus != DAC960_V1_NormalCompletion)
+                  {
+                       memset(InquiryUnitSerialNumber, 0,
+                               sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+                       InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
+                   }
+                 else
+                       memcpy(InquiryUnitSerialNumber, 
+                               Controller->V1.NewInquiryUnitSerialNumber,
+                               sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+             Controller->V1.NeedDeviceSerialNumberInformation = false;
+            }
+       }
+      /*
+        Begin submitting new monitoring commands.
+       */
+      if (Controller->V1.NewEventLogSequenceNumber
+         - Controller->V1.OldEventLogSequenceNumber > 0)
+       {
+         Command->V1.CommandMailbox.Type3E.CommandOpcode =
+           DAC960_V1_PerformEventLogOperation;
+         Command->V1.CommandMailbox.Type3E.OperationType =
+           DAC960_V1_GetEventLogEntry;
+         Command->V1.CommandMailbox.Type3E.OperationQualifier = 1;
+         Command->V1.CommandMailbox.Type3E.SequenceNumber =
+           Controller->V1.OldEventLogSequenceNumber;
+         Command->V1.CommandMailbox.Type3E.BusAddress =
+               Controller->V1.EventLogEntryDMA;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V1.NeedErrorTableInformation)
+       {
+         Controller->V1.NeedErrorTableInformation = false;
+         Command->V1.CommandMailbox.Type3.CommandOpcode =
+           DAC960_V1_GetErrorTable;
+         Command->V1.CommandMailbox.Type3.BusAddress =
+               Controller->V1.NewErrorTableDMA;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V1.NeedRebuildProgress &&
+         Controller->V1.RebuildProgressFirst)
+       {
+         Controller->V1.NeedRebuildProgress = false;
+         Command->V1.CommandMailbox.Type3.CommandOpcode =
+           DAC960_V1_GetRebuildProgress;
+         Command->V1.CommandMailbox.Type3.BusAddress =
+           Controller->V1.RebuildProgressDMA;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V1.NeedDeviceStateInformation)
+       {
+         if (Controller->V1.NeedDeviceInquiryInformation)
+           {
+             DAC960_V1_DCDB_T *DCDB = Controller->V1.MonitoringDCDB;
+             dma_addr_t DCDB_DMA = Controller->V1.MonitoringDCDB_DMA;
+
+             dma_addr_t NewInquiryStandardDataDMA =
+               Controller->V1.NewInquiryStandardDataDMA;
+
+             Command->V1.CommandMailbox.Type3.CommandOpcode = DAC960_V1_DCDB;
+             Command->V1.CommandMailbox.Type3.BusAddress = DCDB_DMA;
+             DCDB->Channel = Controller->V1.DeviceStateChannel;
+             DCDB->TargetID = Controller->V1.DeviceStateTargetID;
+             DCDB->Direction = DAC960_V1_DCDB_DataTransferDeviceToSystem;
+             DCDB->EarlyStatus = false;
+             DCDB->Timeout = DAC960_V1_DCDB_Timeout_10_seconds;
+             DCDB->NoAutomaticRequestSense = false;
+             DCDB->DisconnectPermitted = true;
+             DCDB->TransferLength = sizeof(DAC960_SCSI_Inquiry_T);
+             DCDB->BusAddress = NewInquiryStandardDataDMA;
+             DCDB->CDBLength = 6;
+             DCDB->TransferLengthHigh4 = 0;
+             DCDB->SenseLength = sizeof(DCDB->SenseData);
+             DCDB->CDB[0] = 0x12; /* INQUIRY */
+             DCDB->CDB[1] = 0; /* EVPD = 0 */
+             DCDB->CDB[2] = 0; /* Page Code */
+             DCDB->CDB[3] = 0; /* Reserved */
+             DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_T);
+             DCDB->CDB[5] = 0; /* Control */
+             DAC960_QueueCommand(Command);
+             return;
+           }
+         if (Controller->V1.NeedDeviceSerialNumberInformation)
+           {
+             DAC960_V1_DCDB_T *DCDB = Controller->V1.MonitoringDCDB;
+             dma_addr_t DCDB_DMA = Controller->V1.MonitoringDCDB_DMA;
+             dma_addr_t NewInquiryUnitSerialNumberDMA = 
+                       Controller->V1.NewInquiryUnitSerialNumberDMA;
+
+             Command->V1.CommandMailbox.Type3.CommandOpcode = DAC960_V1_DCDB;
+             Command->V1.CommandMailbox.Type3.BusAddress = DCDB_DMA;
+             DCDB->Channel = Controller->V1.DeviceStateChannel;
+             DCDB->TargetID = Controller->V1.DeviceStateTargetID;
+             DCDB->Direction = DAC960_V1_DCDB_DataTransferDeviceToSystem;
+             DCDB->EarlyStatus = false;
+             DCDB->Timeout = DAC960_V1_DCDB_Timeout_10_seconds;
+             DCDB->NoAutomaticRequestSense = false;
+             DCDB->DisconnectPermitted = true;
+             DCDB->TransferLength =
+               sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
+             DCDB->BusAddress = NewInquiryUnitSerialNumberDMA;
+             DCDB->CDBLength = 6;
+             DCDB->TransferLengthHigh4 = 0;
+             DCDB->SenseLength = sizeof(DCDB->SenseData);
+             DCDB->CDB[0] = 0x12; /* INQUIRY */
+             DCDB->CDB[1] = 1; /* EVPD = 1 */
+             DCDB->CDB[2] = 0x80; /* Page Code */
+             DCDB->CDB[3] = 0; /* Reserved */
+             DCDB->CDB[4] = sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T);
+             DCDB->CDB[5] = 0; /* Control */
+             DAC960_QueueCommand(Command);
+             return;
+           }
+         if (Controller->V1.StartDeviceStateScan)
+           {
+             Controller->V1.DeviceStateChannel = 0;
+             Controller->V1.DeviceStateTargetID = 0;
+             Controller->V1.StartDeviceStateScan = false;
+           }
+         else if (++Controller->V1.DeviceStateTargetID == Controller->Targets)
+           {
+             Controller->V1.DeviceStateChannel++;
+             Controller->V1.DeviceStateTargetID = 0;
+           }
+         if (Controller->V1.DeviceStateChannel < Controller->Channels)
+           {
+             Controller->V1.NewDeviceState->DeviceState =
+               DAC960_V1_Device_Dead;
+             Command->V1.CommandMailbox.Type3D.CommandOpcode =
+               DAC960_V1_GetDeviceState;
+             Command->V1.CommandMailbox.Type3D.Channel =
+               Controller->V1.DeviceStateChannel;
+             Command->V1.CommandMailbox.Type3D.TargetID =
+               Controller->V1.DeviceStateTargetID;
+             Command->V1.CommandMailbox.Type3D.BusAddress =
+               Controller->V1.NewDeviceStateDMA;
+             DAC960_QueueCommand(Command);
+             return;
+           }
+         Controller->V1.NeedDeviceStateInformation = false;
+       }
+      if (Controller->V1.NeedLogicalDriveInformation)
+       {
+         Controller->V1.NeedLogicalDriveInformation = false;
+         Command->V1.CommandMailbox.Type3.CommandOpcode =
+           DAC960_V1_GetLogicalDriveInformation;
+         Command->V1.CommandMailbox.Type3.BusAddress =
+           Controller->V1.NewLogicalDriveInformationDMA;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V1.NeedRebuildProgress)
+       {
+         Controller->V1.NeedRebuildProgress = false;
+         Command->V1.CommandMailbox.Type3.CommandOpcode =
+           DAC960_V1_GetRebuildProgress;
+         Command->V1.CommandMailbox.Type3.BusAddress =
+               Controller->V1.RebuildProgressDMA;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V1.NeedConsistencyCheckProgress)
+       {
+         Controller->V1.NeedConsistencyCheckProgress = false;
+         Command->V1.CommandMailbox.Type3.CommandOpcode =
+           DAC960_V1_RebuildStat;
+         Command->V1.CommandMailbox.Type3.BusAddress =
+           Controller->V1.RebuildProgressDMA;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V1.NeedBackgroundInitializationStatus)
+       {
+         Controller->V1.NeedBackgroundInitializationStatus = false;
+         Command->V1.CommandMailbox.Type3B.CommandOpcode =
+           DAC960_V1_BackgroundInitializationControl;
+         Command->V1.CommandMailbox.Type3B.CommandOpcode2 = 0x20;
+         Command->V1.CommandMailbox.Type3B.BusAddress =
+           Controller->V1.BackgroundInitializationStatusDMA;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      Controller->MonitoringTimerCount++;
+      Controller->MonitoringTimer.expires =
+       jiffies + DAC960_MonitoringTimerInterval;
+       add_timer(&Controller->MonitoringTimer);
+    }
+  if (CommandType == DAC960_ImmediateCommand)
+    {
+      complete(Command->Completion);
+      Command->Completion = NULL;
+      return;
+    }
+  if (CommandType == DAC960_QueuedCommand)
+    {
+      DAC960_V1_KernelCommand_T *KernelCommand = Command->V1.KernelCommand;
+      KernelCommand->CommandStatus = Command->V1.CommandStatus;
+      Command->V1.KernelCommand = NULL;
+      if (CommandOpcode == DAC960_V1_DCDB)
+       Controller->V1.DirectCommandActive[KernelCommand->DCDB->Channel]
+                                         [KernelCommand->DCDB->TargetID] =
+         false;
+      DAC960_DeallocateCommand(Command);
+      KernelCommand->CompletionFunction(KernelCommand);
+      return;
+    }
+  /*
+    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_V1_QueueMonitoringCommand(Command);
+      return;
+    }
+  /*
+    Deallocate the Command.
+  */
+  DAC960_DeallocateCommand(Command);
+  /*
+    Wake up any processes waiting on a free Command.
+  */
+  wake_up(&Controller->CommandWaitQueue);
+}
+
+
+/*
+  DAC960_V2_ReadWriteError prints an appropriate error message for Command
+  when an error occurs on a Read or Write operation.
+*/
+
+static void DAC960_V2_ReadWriteError(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  unsigned char *SenseErrors[] = { "NO SENSE", "RECOVERED ERROR",
+                                  "NOT READY", "MEDIUM ERROR",
+                                  "HARDWARE ERROR", "ILLEGAL REQUEST",
+                                  "UNIT ATTENTION", "DATA PROTECT",
+                                  "BLANK CHECK", "VENDOR-SPECIFIC",
+                                  "COPY ABORTED", "ABORTED COMMAND",
+                                  "EQUAL", "VOLUME OVERFLOW",
+                                  "MISCOMPARE", "RESERVED" };
+  unsigned 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:
+    case DAC960_QueuedCommand:
+      break;
+    }
+  DAC960_Error("Error Condition %s on %s:\n", Controller,
+              SenseErrors[Command->V2.RequestSense->SenseKey], CommandName);
+  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %u..%u\n",
+              Controller, Controller->ControllerNumber,
+              Command->LogicalDriveNumber, Command->BlockNumber,
+              Command->BlockNumber + Command->BlockCount - 1);
+}
+
+
+/*
+  DAC960_V2_ReportEvent prints an appropriate message when a Controller Event
+  occurs.
+*/
+
+static void DAC960_V2_ReportEvent(DAC960_Controller_T *Controller,
+                                 DAC960_V2_Event_T *Event)
+{
+  DAC960_SCSI_RequestSense_T *RequestSense =
+    (DAC960_SCSI_RequestSense_T *) &Event->RequestSenseData;
+  unsigned char MessageBuffer[DAC960_LineBufferSize];
+  static struct { int EventCode; unsigned char *EventMessage; } EventList[] =
+    { /* Physical Device Events (0x0000 - 0x007F) */
+      { 0x0001, "P Online" },
+      { 0x0002, "P Standby" },
+      { 0x0005, "P Automatic Rebuild Started" },
+      { 0x0006, "P Manual Rebuild Started" },
+      { 0x0007, "P Rebuild Completed" },
+      { 0x0008, "P Rebuild Cancelled" },
+      { 0x0009, "P Rebuild Failed for Unknown Reasons" },
+      { 0x000A, "P Rebuild Failed due to New Physical Device" },
+      { 0x000B, "P Rebuild Failed due to Logical Drive Failure" },
+      { 0x000C, "S Offline" },
+      { 0x000D, "P Found" },
+      { 0x000E, "P Removed" },
+      { 0x000F, "P Unconfigured" },
+      { 0x0010, "P Expand Capacity Started" },
+      { 0x0011, "P Expand Capacity Completed" },
+      { 0x0012, "P Expand Capacity Failed" },
+      { 0x0013, "P Command Timed Out" },
+      { 0x0014, "P Command Aborted" },
+      { 0x0015, "P Command Retried" },
+      { 0x0016, "P Parity Error" },
+      { 0x0017, "P Soft Error" },
+      { 0x0018, "P Miscellaneous Error" },
+      { 0x0019, "P Reset" },
+      { 0x001A, "P Active Spare Found" },
+      { 0x001B, "P Warm Spare Found" },
+      { 0x001C, "S Sense Data Received" },
+      { 0x001D, "P Initialization Started" },
+      { 0x001E, "P Initialization Completed" },
+      { 0x001F, "P Initialization Failed" },
+      { 0x0020, "P Initialization Cancelled" },
+      { 0x0021, "P Failed because Write Recovery Failed" },
+      { 0x0022, "P Failed because SCSI Bus Reset Failed" },
+      { 0x0023, "P Failed because of Double Check Condition" },
+      { 0x0024, "P Failed because Device Cannot Be Accessed" },
+      { 0x0025, "P Failed because of Gross Error on SCSI Processor" },
+      { 0x0026, "P Failed because of Bad Tag from Device" },
+      { 0x0027, "P Failed because of Command Timeout" },
+      { 0x0028, "P Failed because of System Reset" },
+      { 0x0029, "P Failed because of Busy Status or Parity Error" },
+      { 0x002A, "P Failed because Host Set Device to Failed State" },
+      { 0x002B, "P Failed because of Selection Timeout" },
+      { 0x002C, "P Failed because of SCSI Bus Phase Error" },
+      { 0x002D, "P Failed because Device Returned Unknown Status" },
+      { 0x002E, "P Failed because Device Not Ready" },
+      { 0x002F, "P Failed because Device Not Found at Startup" },
+      { 0x0030, "P Failed because COD Write Operation Failed" },
+      { 0x0031, "P Failed because BDT Write Operation Failed" },
+      { 0x0039, "P Missing at Startup" },
+      { 0x003A, "P Start Rebuild Failed due to Physical Drive Too Small" },
+      { 0x003C, "P Temporarily Offline Device Automatically Made Online" },
+      { 0x003D, "P Standby Rebuild Started" },
+      /* Logical Device Events (0x0080 - 0x00FF) */
+      { 0x0080, "M Consistency Check Started" },
+      { 0x0081, "M Consistency Check Completed" },
+      { 0x0082, "M Consistency Check Cancelled" },
+      { 0x0083, "M Consistency Check Completed With Errors" },
+      { 0x0084, "M Consistency Check Failed due to Logical Drive Failure" },
+      { 0x0085, "M Consistency Check Failed due to Physical Device Failure" },
+      { 0x0086, "L Offline" },
+      { 0x0087, "L Critical" },
+      { 0x0088, "L Online" },
+      { 0x0089, "M Automatic Rebuild Started" },
+      { 0x008A, "M Manual Rebuild Started" },
+      { 0x008B, "M Rebuild Completed" },
+      { 0x008C, "M Rebuild Cancelled" },
+      { 0x008D, "M Rebuild Failed for Unknown Reasons" },
+      { 0x008E, "M Rebuild Failed due to New Physical Device" },
+      { 0x008F, "M Rebuild Failed due to Logical Drive Failure" },
+      { 0x0090, "M Initialization Started" },
+      { 0x0091, "M Initialization Completed" },
+      { 0x0092, "M Initialization Cancelled" },
+      { 0x0093, "M Initialization Failed" },
+      { 0x0094, "L Found" },
+      { 0x0095, "L Deleted" },
+      { 0x0096, "M Expand Capacity Started" },
+      { 0x0097, "M Expand Capacity Completed" },
+      { 0x0098, "M Expand Capacity Failed" },
+      { 0x0099, "L Bad Block Found" },
+      { 0x009A, "L Size Changed" },
+      { 0x009B, "L Type Changed" },
+      { 0x009C, "L Bad Data Block Found" },
+      { 0x009E, "L Read of Data Block in BDT" },
+      { 0x009F, "L Write Back Data for Disk Block Lost" },
+      { 0x00A0, "L Temporarily Offline RAID-5/3 Drive Made Online" },
+      { 0x00A1, "L Temporarily Offline RAID-6/1/0/7 Drive Made Online" },
+      { 0x00A2, "L Standby Rebuild Started" },
+      /* Fault Management Events (0x0100 - 0x017F) */
+      { 0x0140, "E Fan %d Failed" },
+      { 0x0141, "E Fan %d OK" },
+      { 0x0142, "E Fan %d Not Present" },
+      { 0x0143, "E Power Supply %d Failed" },
+      { 0x0144, "E Power Supply %d OK" },
+      { 0x0145, "E Power Supply %d Not Present" },
+      { 0x0146, "E Temperature Sensor %d Temperature Exceeds Safe Limit" },
+      { 0x0147, "E Temperature Sensor %d Temperature Exceeds Working Limit" },
+      { 0x0148, "E Temperature Sensor %d Temperature Normal" },
+      { 0x0149, "E Temperature Sensor %d Not Present" },
+      { 0x014A, "E Enclosure Management Unit %d Access Critical" },
+      { 0x014B, "E Enclosure Management Unit %d Access OK" },
+      { 0x014C, "E Enclosure Management Unit %d Access Offline" },
+      /* Controller Events (0x0180 - 0x01FF) */
+      { 0x0181, "C Cache Write Back Error" },
+      { 0x0188, "C Battery Backup Unit Found" },
+      { 0x0189, "C Battery Backup Unit Charge Level Low" },
+      { 0x018A, "C Battery Backup Unit Charge Level OK" },
+      { 0x0193, "C Installation Aborted" },
+      { 0x0195, "C Battery Backup Unit Physically Removed" },
+      { 0x0196, "C Memory Error During Warm Boot" },
+      { 0x019E, "C Memory Soft ECC Error Corrected" },
+      { 0x019F, "C Memory Hard ECC Error Corrected" },
+      { 0x01A2, "C Battery Backup Unit Failed" },
+      { 0x01AB, "C Mirror Race Recovery Failed" },
+      { 0x01AC, "C Mirror Race on Critical Drive" },
+      /* Controller Internal Processor Events */
+      { 0x0380, "C Internal Controller Hung" },
+      { 0x0381, "C Internal Controller Firmware Breakpoint" },
+      { 0x0390, "C Internal Controller i960 Processor Specific Error" },
+      { 0x03A0, "C Internal Controller StrongARM Processor Specific Error" },
+      { 0, "" } };
+  int EventListIndex = 0, EventCode;
+  unsigned char EventType, *EventMessage;
+  if (Event->EventCode == 0x1C &&
+      RequestSense->SenseKey == DAC960_SenseKey_VendorSpecific &&
+      (RequestSense->AdditionalSenseCode == 0x80 ||
+       RequestSense->AdditionalSenseCode == 0x81))
+    Event->EventCode = ((RequestSense->AdditionalSenseCode - 0x80) << 8) |
+                      RequestSense->AdditionalSenseCodeQualifier;
+  while (true)
+    {
+      EventCode = EventList[EventListIndex].EventCode;
+      if (EventCode == Event->EventCode || EventCode == 0) break;
+      EventListIndex++;
+    }
+  EventType = EventList[EventListIndex].EventMessage[0];
+  EventMessage = &EventList[EventListIndex].EventMessage[2];
+  if (EventCode == 0)
+    {
+      DAC960_Critical("Unknown Controller Event Code %04X\n",
+                     Controller, Event->EventCode);
+      return;
+    }
+  switch (EventType)
+    {
+    case 'P':
+      DAC960_Critical("Physical Device %d:%d %s\n", Controller,
+                     Event->Channel, Event->TargetID, EventMessage);
+      break;
+    case 'L':
+      DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) %s\n", Controller,
+                     Event->LogicalUnit, Controller->ControllerNumber,
+                     Event->LogicalUnit, EventMessage);
+      break;
+    case 'M':
+      DAC960_Progress("Logical Drive %d (/dev/rd/c%dd%d) %s\n", Controller,
+                     Event->LogicalUnit, Controller->ControllerNumber,
+                     Event->LogicalUnit, EventMessage);
+      break;
+    case 'S':
+      if (RequestSense->SenseKey == DAC960_SenseKey_NoSense ||
+         (RequestSense->SenseKey == DAC960_SenseKey_NotReady &&
+          RequestSense->AdditionalSenseCode == 0x04 &&
+          (RequestSense->AdditionalSenseCodeQualifier == 0x01 ||
+           RequestSense->AdditionalSenseCodeQualifier == 0x02)))
+       break;
+      DAC960_Critical("Physical Device %d:%d %s\n", Controller,
+                     Event->Channel, Event->TargetID, EventMessage);
+      DAC960_Critical("Physical Device %d:%d Request Sense: "
+                     "Sense Key = %X, ASC = %02X, ASCQ = %02X\n",
+                     Controller,
+                     Event->Channel,
+                     Event->TargetID,
+                     RequestSense->SenseKey,
+                     RequestSense->AdditionalSenseCode,
+                     RequestSense->AdditionalSenseCodeQualifier);
+      DAC960_Critical("Physical Device %d:%d Request Sense: "
+                     "Information = %02X%02X%02X%02X "
+                     "%02X%02X%02X%02X\n",
+                     Controller,
+                     Event->Channel,
+                     Event->TargetID,
+                     RequestSense->Information[0],
+                     RequestSense->Information[1],
+                     RequestSense->Information[2],
+                     RequestSense->Information[3],
+                     RequestSense->CommandSpecificInformation[0],
+                     RequestSense->CommandSpecificInformation[1],
+                     RequestSense->CommandSpecificInformation[2],
+                     RequestSense->CommandSpecificInformation[3]);
+      break;
+    case 'E':
+      if (Controller->SuppressEnclosureMessages) break;
+      sprintf(MessageBuffer, EventMessage, Event->LogicalUnit);
+      DAC960_Critical("Enclosure %d %s\n", Controller,
+                     Event->TargetID, MessageBuffer);
+      break;
+    case 'C':
+      DAC960_Critical("Controller %s\n", Controller, EventMessage);
+      break;
+    default:
+      DAC960_Critical("Unknown Controller Event Code %04X\n",
+                     Controller, Event->EventCode);
+      break;
+    }
+}
+
+
+/*
+  DAC960_V2_ReportProgress prints an appropriate progress message for
+  Logical Device Long Operations.
+*/
+
+static void DAC960_V2_ReportProgress(DAC960_Controller_T *Controller,
+                                    unsigned char *MessageString,
+                                    unsigned int LogicalDeviceNumber,
+                                    unsigned long BlocksCompleted,
+                                    unsigned long LogicalDeviceSize)
+{
+  Controller->EphemeralProgressMessage = true;
+  DAC960_Progress("%s in Progress: Logical Drive %d (/dev/rd/c%dd%d) "
+                 "%d%% completed\n", Controller,
+                 MessageString,
+                 LogicalDeviceNumber,
+                 Controller->ControllerNumber,
+                 LogicalDeviceNumber,
+                 (100 * (BlocksCompleted >> 7)) / (LogicalDeviceSize >> 7));
+  Controller->EphemeralProgressMessage = false;
+}
+
+
+/*
+  DAC960_V2_ProcessCompletedCommand performs completion processing for Command
+  for DAC960 V2 Firmware Controllers.
+*/
+
+static void DAC960_V2_ProcessCompletedCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandType_T CommandType = Command->CommandType;
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_IOCTL_Opcode_T IOCTLOpcode = CommandMailbox->Common.IOCTL_Opcode;
+  DAC960_V2_CommandOpcode_T CommandOpcode = CommandMailbox->SCSI_10.CommandOpcode;
+  DAC960_V2_CommandStatus_T CommandStatus = Command->V2.CommandStatus;
+
+  if (CommandType == DAC960_ReadCommand ||
+      CommandType == DAC960_WriteCommand)
+    {
+
+#ifdef FORCE_RETRY_DEBUG
+      CommandStatus = DAC960_V2_AbormalCompletion;
+#endif
+      Command->V2.RequestSense->SenseKey = DAC960_SenseKey_MediumError;
+
+      if (CommandStatus == DAC960_V2_NormalCompletion) {
+
+               if (!DAC960_ProcessCompletedRequest(Command, true))
+                       BUG();
+
+      } else if (Command->V2.RequestSense->SenseKey == DAC960_SenseKey_MediumError)
+       {
+         /*
+          * break the command down into pieces and resubmit each
+          * piece, hoping that some of them will succeed.
+          */
+          DAC960_queue_partial_rw(Command);
+          return;
+       }
+      else
+       {
+         if (Command->V2.RequestSense->SenseKey != DAC960_SenseKey_NotReady)
+           DAC960_V2_ReadWriteError(Command);
+         /*
+           Perform completion processing for all buffers in this I/O Request.
+         */
+          (void)DAC960_ProcessCompletedRequest(Command, false);
+       }
+    }
+  else if (CommandType == DAC960_ReadRetryCommand ||
+          CommandType == DAC960_WriteRetryCommand)
+    {
+      bool normal_completion;
+
+#ifdef FORCE_RETRY_FAILURE_DEBUG
+      static int retry_count = 1;
+#endif
+      /*
+        Perform completion processing for the portion that was
+       retried, and submit the next portion, if any.
+      */
+      normal_completion = true;
+      if (CommandStatus != DAC960_V2_NormalCompletion) {
+       normal_completion = false;
+       if (Command->V2.RequestSense->SenseKey != DAC960_SenseKey_NotReady)
+           DAC960_V2_ReadWriteError(Command);
+      }
+
+#ifdef FORCE_RETRY_FAILURE_DEBUG
+      if (!(++retry_count % 10000)) {
+             printk("V2 error retry failure test\n");
+             normal_completion = false;
+             DAC960_V2_ReadWriteError(Command);
+      }
+#endif
+
+      if (!DAC960_ProcessCompletedRequest(Command, normal_completion)) {
+               DAC960_queue_partial_rw(Command);
+               return;
+      }
+    }
+  else if (CommandType == DAC960_MonitoringCommand)
+    {
+      if (Controller->ShutdownMonitoringTimer)
+             return;
+      if (IOCTLOpcode == DAC960_V2_GetControllerInfo)
+       {
+         DAC960_V2_ControllerInfo_T *NewControllerInfo =
+           Controller->V2.NewControllerInformation;
+         DAC960_V2_ControllerInfo_T *ControllerInfo =
+           &Controller->V2.ControllerInformation;
+         Controller->LogicalDriveCount =
+           NewControllerInfo->LogicalDevicesPresent;
+         Controller->V2.NeedLogicalDeviceInformation = true;
+         Controller->V2.NeedPhysicalDeviceInformation = true;
+         Controller->V2.StartLogicalDeviceInformationScan = true;
+         Controller->V2.StartPhysicalDeviceInformationScan = true;
+         Controller->MonitoringAlertMode =
+           (NewControllerInfo->LogicalDevicesCritical > 0 ||
+            NewControllerInfo->LogicalDevicesOffline > 0 ||
+            NewControllerInfo->PhysicalDisksCritical > 0 ||
+            NewControllerInfo->PhysicalDisksOffline > 0);
+         memcpy(ControllerInfo, NewControllerInfo,
+                sizeof(DAC960_V2_ControllerInfo_T));
+       }
+      else if (IOCTLOpcode == DAC960_V2_GetEvent)
+       {
+         if (CommandStatus == DAC960_V2_NormalCompletion) {
+           DAC960_V2_ReportEvent(Controller, Controller->V2.Event);
+         }
+         Controller->V2.NextEventSequenceNumber++;
+       }
+      else if (IOCTLOpcode == DAC960_V2_GetPhysicalDeviceInfoValid &&
+              CommandStatus == DAC960_V2_NormalCompletion)
+       {
+         DAC960_V2_PhysicalDeviceInfo_T *NewPhysicalDeviceInfo =
+           Controller->V2.NewPhysicalDeviceInformation;
+         unsigned int PhysicalDeviceIndex = Controller->V2.PhysicalDeviceIndex;
+         DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo =
+           Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex];
+         DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+           Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex];
+         unsigned int DeviceIndex;
+         while (PhysicalDeviceInfo != NULL &&
+                (NewPhysicalDeviceInfo->Channel >
+                 PhysicalDeviceInfo->Channel ||
+                 (NewPhysicalDeviceInfo->Channel ==
+                  PhysicalDeviceInfo->Channel &&
+                  (NewPhysicalDeviceInfo->TargetID >
+                   PhysicalDeviceInfo->TargetID ||
+                  (NewPhysicalDeviceInfo->TargetID ==
+                   PhysicalDeviceInfo->TargetID &&
+                   NewPhysicalDeviceInfo->LogicalUnit >
+                   PhysicalDeviceInfo->LogicalUnit)))))
+           {
+             DAC960_Critical("Physical Device %d:%d No Longer Exists\n",
+                             Controller,
+                             PhysicalDeviceInfo->Channel,
+                             PhysicalDeviceInfo->TargetID);
+             Controller->V2.PhysicalDeviceInformation
+                            [PhysicalDeviceIndex] = NULL;
+             Controller->V2.InquiryUnitSerialNumber
+                            [PhysicalDeviceIndex] = NULL;
+             kfree(PhysicalDeviceInfo);
+             kfree(InquiryUnitSerialNumber);
+             for (DeviceIndex = PhysicalDeviceIndex;
+                  DeviceIndex < DAC960_V2_MaxPhysicalDevices - 1;
+                  DeviceIndex++)
+               {
+                 Controller->V2.PhysicalDeviceInformation[DeviceIndex] =
+                   Controller->V2.PhysicalDeviceInformation[DeviceIndex+1];
+                 Controller->V2.InquiryUnitSerialNumber[DeviceIndex] =
+                   Controller->V2.InquiryUnitSerialNumber[DeviceIndex+1];
+               }
+             Controller->V2.PhysicalDeviceInformation
+                            [DAC960_V2_MaxPhysicalDevices-1] = NULL;
+             Controller->V2.InquiryUnitSerialNumber
+                            [DAC960_V2_MaxPhysicalDevices-1] = NULL;
+             PhysicalDeviceInfo =
+               Controller->V2.PhysicalDeviceInformation[PhysicalDeviceIndex];
+             InquiryUnitSerialNumber =
+               Controller->V2.InquiryUnitSerialNumber[PhysicalDeviceIndex];
+           }
+         if (PhysicalDeviceInfo == NULL ||
+             (NewPhysicalDeviceInfo->Channel !=
+              PhysicalDeviceInfo->Channel) ||
+             (NewPhysicalDeviceInfo->TargetID !=
+              PhysicalDeviceInfo->TargetID) ||
+             (NewPhysicalDeviceInfo->LogicalUnit !=
+              PhysicalDeviceInfo->LogicalUnit))
+           {
+             PhysicalDeviceInfo =
+               kmalloc(sizeof(DAC960_V2_PhysicalDeviceInfo_T), GFP_ATOMIC);
+             InquiryUnitSerialNumber =
+                 kmalloc(sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T),
+                         GFP_ATOMIC);
+             if (InquiryUnitSerialNumber == NULL ||
+                 PhysicalDeviceInfo == NULL)
+               {
+                 kfree(InquiryUnitSerialNumber);
+                 InquiryUnitSerialNumber = NULL;
+                 kfree(PhysicalDeviceInfo);
+                 PhysicalDeviceInfo = NULL;
+               }
+             DAC960_Critical("Physical Device %d:%d Now Exists%s\n",
+                             Controller,
+                             NewPhysicalDeviceInfo->Channel,
+                             NewPhysicalDeviceInfo->TargetID,
+                             (PhysicalDeviceInfo != NULL
+                              ? "" : " - Allocation Failed"));
+             if (PhysicalDeviceInfo != NULL)
+               {
+                 memset(PhysicalDeviceInfo, 0,
+                        sizeof(DAC960_V2_PhysicalDeviceInfo_T));
+                 PhysicalDeviceInfo->PhysicalDeviceState =
+                   DAC960_V2_Device_InvalidState;
+                 memset(InquiryUnitSerialNumber, 0,
+                        sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+                 InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
+                 for (DeviceIndex = DAC960_V2_MaxPhysicalDevices - 1;
+                      DeviceIndex > PhysicalDeviceIndex;
+                      DeviceIndex--)
+                   {
+                     Controller->V2.PhysicalDeviceInformation[DeviceIndex] =
+                       Controller->V2.PhysicalDeviceInformation[DeviceIndex-1];
+                     Controller->V2.InquiryUnitSerialNumber[DeviceIndex] =
+                       Controller->V2.InquiryUnitSerialNumber[DeviceIndex-1];
+                   }
+                 Controller->V2.PhysicalDeviceInformation
+                                [PhysicalDeviceIndex] =
+                   PhysicalDeviceInfo;
+                 Controller->V2.InquiryUnitSerialNumber
+                                [PhysicalDeviceIndex] =
+                   InquiryUnitSerialNumber;
+                 Controller->V2.NeedDeviceSerialNumberInformation = true;
+               }
+           }
+         if (PhysicalDeviceInfo != NULL)
+           {
+             if (NewPhysicalDeviceInfo->PhysicalDeviceState !=
+                 PhysicalDeviceInfo->PhysicalDeviceState)
+               DAC960_Critical(
+                 "Physical Device %d:%d is now %s\n", Controller,
+                 NewPhysicalDeviceInfo->Channel,
+                 NewPhysicalDeviceInfo->TargetID,
+                 (NewPhysicalDeviceInfo->PhysicalDeviceState
+                  == DAC960_V2_Device_Online
+                  ? "ONLINE"
+                  : NewPhysicalDeviceInfo->PhysicalDeviceState
+                    == DAC960_V2_Device_Rebuild
+                    ? "REBUILD"
+                    : NewPhysicalDeviceInfo->PhysicalDeviceState
+                      == DAC960_V2_Device_Missing
+                      ? "MISSING"
+                      : NewPhysicalDeviceInfo->PhysicalDeviceState
+                        == DAC960_V2_Device_Critical
+                        ? "CRITICAL"
+                        : NewPhysicalDeviceInfo->PhysicalDeviceState
+                          == DAC960_V2_Device_Dead
+                          ? "DEAD"
+                          : NewPhysicalDeviceInfo->PhysicalDeviceState
+                            == DAC960_V2_Device_SuspectedDead
+                            ? "SUSPECTED-DEAD"
+                            : NewPhysicalDeviceInfo->PhysicalDeviceState
+                              == DAC960_V2_Device_CommandedOffline
+                              ? "COMMANDED-OFFLINE"
+                              : NewPhysicalDeviceInfo->PhysicalDeviceState
+                                == DAC960_V2_Device_Standby
+                                ? "STANDBY" : "UNKNOWN"));
+             if ((NewPhysicalDeviceInfo->ParityErrors !=
+                  PhysicalDeviceInfo->ParityErrors) ||
+                 (NewPhysicalDeviceInfo->SoftErrors !=
+                  PhysicalDeviceInfo->SoftErrors) ||
+                 (NewPhysicalDeviceInfo->HardErrors !=
+                  PhysicalDeviceInfo->HardErrors) ||
+                 (NewPhysicalDeviceInfo->MiscellaneousErrors !=
+                  PhysicalDeviceInfo->MiscellaneousErrors) ||
+                 (NewPhysicalDeviceInfo->CommandTimeouts !=
+                  PhysicalDeviceInfo->CommandTimeouts) ||
+                 (NewPhysicalDeviceInfo->Retries !=
+                  PhysicalDeviceInfo->Retries) ||
+                 (NewPhysicalDeviceInfo->Aborts !=
+                  PhysicalDeviceInfo->Aborts) ||
+                 (NewPhysicalDeviceInfo->PredictedFailuresDetected !=
+                  PhysicalDeviceInfo->PredictedFailuresDetected))
+               {
+                 DAC960_Critical("Physical Device %d:%d Errors: "
+                                 "Parity = %d, Soft = %d, "
+                                 "Hard = %d, Misc = %d\n",
+                                 Controller,
+                                 NewPhysicalDeviceInfo->Channel,
+                                 NewPhysicalDeviceInfo->TargetID,
+                                 NewPhysicalDeviceInfo->ParityErrors,
+                                 NewPhysicalDeviceInfo->SoftErrors,
+                                 NewPhysicalDeviceInfo->HardErrors,
+                                 NewPhysicalDeviceInfo->MiscellaneousErrors);
+                 DAC960_Critical("Physical Device %d:%d Errors: "
+                                 "Timeouts = %d, Retries = %d, "
+                                 "Aborts = %d, Predicted = %d\n",
+                                 Controller,
+                                 NewPhysicalDeviceInfo->Channel,
+                                 NewPhysicalDeviceInfo->TargetID,
+                                 NewPhysicalDeviceInfo->CommandTimeouts,
+                                 NewPhysicalDeviceInfo->Retries,
+                                 NewPhysicalDeviceInfo->Aborts,
+                                 NewPhysicalDeviceInfo
+                                 ->PredictedFailuresDetected);
+               }
+             if ((PhysicalDeviceInfo->PhysicalDeviceState
+                  == DAC960_V2_Device_Dead ||
+                  PhysicalDeviceInfo->PhysicalDeviceState
+                  == DAC960_V2_Device_InvalidState) &&
+                 NewPhysicalDeviceInfo->PhysicalDeviceState
+                 != DAC960_V2_Device_Dead)
+               Controller->V2.NeedDeviceSerialNumberInformation = true;
+             memcpy(PhysicalDeviceInfo, NewPhysicalDeviceInfo,
+                    sizeof(DAC960_V2_PhysicalDeviceInfo_T));
+           }
+         NewPhysicalDeviceInfo->LogicalUnit++;
+         Controller->V2.PhysicalDeviceIndex++;
+       }
+      else if (IOCTLOpcode == DAC960_V2_GetPhysicalDeviceInfoValid)
+       {
+         unsigned int DeviceIndex;
+         for (DeviceIndex = Controller->V2.PhysicalDeviceIndex;
+              DeviceIndex < DAC960_V2_MaxPhysicalDevices;
+              DeviceIndex++)
+           {
+             DAC960_V2_PhysicalDeviceInfo_T *PhysicalDeviceInfo =
+               Controller->V2.PhysicalDeviceInformation[DeviceIndex];
+             DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+               Controller->V2.InquiryUnitSerialNumber[DeviceIndex];
+             if (PhysicalDeviceInfo == NULL) break;
+             DAC960_Critical("Physical Device %d:%d No Longer Exists\n",
+                             Controller,
+                             PhysicalDeviceInfo->Channel,
+                             PhysicalDeviceInfo->TargetID);
+             Controller->V2.PhysicalDeviceInformation[DeviceIndex] = NULL;
+             Controller->V2.InquiryUnitSerialNumber[DeviceIndex] = NULL;
+             kfree(PhysicalDeviceInfo);
+             kfree(InquiryUnitSerialNumber);
+           }
+         Controller->V2.NeedPhysicalDeviceInformation = false;
+       }
+      else if (IOCTLOpcode == DAC960_V2_GetLogicalDeviceInfoValid &&
+              CommandStatus == DAC960_V2_NormalCompletion)
+       {
+         DAC960_V2_LogicalDeviceInfo_T *NewLogicalDeviceInfo =
+           Controller->V2.NewLogicalDeviceInformation;
+         unsigned short LogicalDeviceNumber =
+           NewLogicalDeviceInfo->LogicalDeviceNumber;
+         DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
+           Controller->V2.LogicalDeviceInformation[LogicalDeviceNumber];
+         if (LogicalDeviceInfo == NULL)
+           {
+             DAC960_V2_PhysicalDevice_T PhysicalDevice;
+             PhysicalDevice.Controller = 0;
+             PhysicalDevice.Channel = NewLogicalDeviceInfo->Channel;
+             PhysicalDevice.TargetID = NewLogicalDeviceInfo->TargetID;
+             PhysicalDevice.LogicalUnit = NewLogicalDeviceInfo->LogicalUnit;
+             Controller->V2.LogicalDriveToVirtualDevice[LogicalDeviceNumber] =
+               PhysicalDevice;
+             LogicalDeviceInfo = kmalloc(sizeof(DAC960_V2_LogicalDeviceInfo_T),
+                                         GFP_ATOMIC);
+             Controller->V2.LogicalDeviceInformation[LogicalDeviceNumber] =
+               LogicalDeviceInfo;
+             DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+                             "Now Exists%s\n", Controller,
+                             LogicalDeviceNumber,
+                             Controller->ControllerNumber,
+                             LogicalDeviceNumber,
+                             (LogicalDeviceInfo != NULL
+                              ? "" : " - Allocation Failed"));
+             if (LogicalDeviceInfo != NULL)
+               {
+                 memset(LogicalDeviceInfo, 0,
+                        sizeof(DAC960_V2_LogicalDeviceInfo_T));
+                 DAC960_ComputeGenericDiskInfo(Controller);
+               }
+           }
+         if (LogicalDeviceInfo != NULL)
+           {
+             unsigned long LogicalDeviceSize =
+               NewLogicalDeviceInfo->ConfigurableDeviceSize;
+             if (NewLogicalDeviceInfo->LogicalDeviceState !=
+                 LogicalDeviceInfo->LogicalDeviceState)
+               DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+                               "is now %s\n", Controller,
+                               LogicalDeviceNumber,
+                               Controller->ControllerNumber,
+                               LogicalDeviceNumber,
+                               (NewLogicalDeviceInfo->LogicalDeviceState
+                                == DAC960_V2_LogicalDevice_Online
+                                ? "ONLINE"
+                                : NewLogicalDeviceInfo->LogicalDeviceState
+                                  == DAC960_V2_LogicalDevice_Critical
+                                  ? "CRITICAL" : "OFFLINE"));
+             if ((NewLogicalDeviceInfo->SoftErrors !=
+                  LogicalDeviceInfo->SoftErrors) ||
+                 (NewLogicalDeviceInfo->CommandsFailed !=
+                  LogicalDeviceInfo->CommandsFailed) ||
+                 (NewLogicalDeviceInfo->DeferredWriteErrors !=
+                  LogicalDeviceInfo->DeferredWriteErrors))
+               DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) Errors: "
+                               "Soft = %d, Failed = %d, Deferred Write = %d\n",
+                               Controller, LogicalDeviceNumber,
+                               Controller->ControllerNumber,
+                               LogicalDeviceNumber,
+                               NewLogicalDeviceInfo->SoftErrors,
+                               NewLogicalDeviceInfo->CommandsFailed,
+                               NewLogicalDeviceInfo->DeferredWriteErrors);
+             if (NewLogicalDeviceInfo->ConsistencyCheckInProgress)
+               DAC960_V2_ReportProgress(Controller,
+                                        "Consistency Check",
+                                        LogicalDeviceNumber,
+                                        NewLogicalDeviceInfo
+                                        ->ConsistencyCheckBlockNumber,
+                                        LogicalDeviceSize);
+             else if (NewLogicalDeviceInfo->RebuildInProgress)
+               DAC960_V2_ReportProgress(Controller,
+                                        "Rebuild",
+                                        LogicalDeviceNumber,
+                                        NewLogicalDeviceInfo
+                                        ->RebuildBlockNumber,
+                                        LogicalDeviceSize);
+             else if (NewLogicalDeviceInfo->BackgroundInitializationInProgress)
+               DAC960_V2_ReportProgress(Controller,
+                                        "Background Initialization",
+                                        LogicalDeviceNumber,
+                                        NewLogicalDeviceInfo
+                                        ->BackgroundInitializationBlockNumber,
+                                        LogicalDeviceSize);
+             else if (NewLogicalDeviceInfo->ForegroundInitializationInProgress)
+               DAC960_V2_ReportProgress(Controller,
+                                        "Foreground Initialization",
+                                        LogicalDeviceNumber,
+                                        NewLogicalDeviceInfo
+                                        ->ForegroundInitializationBlockNumber,
+                                        LogicalDeviceSize);
+             else if (NewLogicalDeviceInfo->DataMigrationInProgress)
+               DAC960_V2_ReportProgress(Controller,
+                                        "Data Migration",
+                                        LogicalDeviceNumber,
+                                        NewLogicalDeviceInfo
+                                        ->DataMigrationBlockNumber,
+                                        LogicalDeviceSize);
+             else if (NewLogicalDeviceInfo->PatrolOperationInProgress)
+               DAC960_V2_ReportProgress(Controller,
+                                        "Patrol Operation",
+                                        LogicalDeviceNumber,
+                                        NewLogicalDeviceInfo
+                                        ->PatrolOperationBlockNumber,
+                                        LogicalDeviceSize);
+             if (LogicalDeviceInfo->BackgroundInitializationInProgress &&
+                 !NewLogicalDeviceInfo->BackgroundInitializationInProgress)
+               DAC960_Progress("Logical Drive %d (/dev/rd/c%dd%d) "
+                               "Background Initialization %s\n",
+                               Controller,
+                               LogicalDeviceNumber,
+                               Controller->ControllerNumber,
+                               LogicalDeviceNumber,
+                               (NewLogicalDeviceInfo->LogicalDeviceControl
+                                                     .LogicalDeviceInitialized
+                                ? "Completed" : "Failed"));
+             memcpy(LogicalDeviceInfo, NewLogicalDeviceInfo,
+                    sizeof(DAC960_V2_LogicalDeviceInfo_T));
+           }
+         Controller->V2.LogicalDriveFoundDuringScan
+                        [LogicalDeviceNumber] = true;
+         NewLogicalDeviceInfo->LogicalDeviceNumber++;
+       }
+      else if (IOCTLOpcode == DAC960_V2_GetLogicalDeviceInfoValid)
+       {
+         int LogicalDriveNumber;
+         for (LogicalDriveNumber = 0;
+              LogicalDriveNumber < DAC960_MaxLogicalDrives;
+              LogicalDriveNumber++)
+           {
+             DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
+               Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
+             if (LogicalDeviceInfo == NULL ||
+                 Controller->V2.LogicalDriveFoundDuringScan
+                                [LogicalDriveNumber])
+               continue;
+             DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+                             "No Longer Exists\n", Controller,
+                             LogicalDriveNumber,
+                             Controller->ControllerNumber,
+                             LogicalDriveNumber);
+             Controller->V2.LogicalDeviceInformation
+                            [LogicalDriveNumber] = NULL;
+             kfree(LogicalDeviceInfo);
+             Controller->LogicalDriveInitiallyAccessible
+                         [LogicalDriveNumber] = false;
+             DAC960_ComputeGenericDiskInfo(Controller);
+           }
+         Controller->V2.NeedLogicalDeviceInformation = false;
+       }
+      else if (CommandOpcode == DAC960_V2_SCSI_10_Passthru)
+        {
+           DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+               Controller->V2.InquiryUnitSerialNumber[Controller->V2.PhysicalDeviceIndex - 1];
+
+           if (CommandStatus != DAC960_V2_NormalCompletion) {
+               memset(InquiryUnitSerialNumber,
+                       0, sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+               InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
+           } else
+               memcpy(InquiryUnitSerialNumber,
+                       Controller->V2.NewInquiryUnitSerialNumber,
+                       sizeof(DAC960_SCSI_Inquiry_UnitSerialNumber_T));
+
+            Controller->V2.NeedDeviceSerialNumberInformation = false;
+        }
+
+      if (Controller->V2.HealthStatusBuffer->NextEventSequenceNumber
+         - Controller->V2.NextEventSequenceNumber > 0)
+       {
+         CommandMailbox->GetEvent.CommandOpcode = DAC960_V2_IOCTL;
+         CommandMailbox->GetEvent.DataTransferSize = sizeof(DAC960_V2_Event_T);
+         CommandMailbox->GetEvent.EventSequenceNumberHigh16 =
+           Controller->V2.NextEventSequenceNumber >> 16;
+         CommandMailbox->GetEvent.ControllerNumber = 0;
+         CommandMailbox->GetEvent.IOCTL_Opcode =
+           DAC960_V2_GetEvent;
+         CommandMailbox->GetEvent.EventSequenceNumberLow16 =
+           Controller->V2.NextEventSequenceNumber & 0xFFFF;
+         CommandMailbox->GetEvent.DataTransferMemoryAddress
+                                 .ScatterGatherSegments[0]
+                                 .SegmentDataPointer =
+           Controller->V2.EventDMA;
+         CommandMailbox->GetEvent.DataTransferMemoryAddress
+                                 .ScatterGatherSegments[0]
+                                 .SegmentByteCount =
+           CommandMailbox->GetEvent.DataTransferSize;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V2.NeedPhysicalDeviceInformation)
+       {
+         if (Controller->V2.NeedDeviceSerialNumberInformation)
+           {
+             DAC960_SCSI_Inquiry_UnitSerialNumber_T *InquiryUnitSerialNumber =
+                Controller->V2.NewInquiryUnitSerialNumber;
+             InquiryUnitSerialNumber->PeripheralDeviceType = 0x1F;
+
+             DAC960_V2_ConstructNewUnitSerialNumber(Controller, CommandMailbox,
+                       Controller->V2.NewPhysicalDeviceInformation->Channel,
+                       Controller->V2.NewPhysicalDeviceInformation->TargetID,
+               Controller->V2.NewPhysicalDeviceInformation->LogicalUnit - 1);
+
+
+             DAC960_QueueCommand(Command);
+             return;
+           }
+         if (Controller->V2.StartPhysicalDeviceInformationScan)
+           {
+             Controller->V2.PhysicalDeviceIndex = 0;
+             Controller->V2.NewPhysicalDeviceInformation->Channel = 0;
+             Controller->V2.NewPhysicalDeviceInformation->TargetID = 0;
+             Controller->V2.NewPhysicalDeviceInformation->LogicalUnit = 0;
+             Controller->V2.StartPhysicalDeviceInformationScan = false;
+           }
+         CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
+         CommandMailbox->PhysicalDeviceInfo.DataTransferSize =
+           sizeof(DAC960_V2_PhysicalDeviceInfo_T);
+         CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.LogicalUnit =
+           Controller->V2.NewPhysicalDeviceInformation->LogicalUnit;
+         CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID =
+           Controller->V2.NewPhysicalDeviceInformation->TargetID;
+         CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel =
+           Controller->V2.NewPhysicalDeviceInformation->Channel;
+         CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode =
+           DAC960_V2_GetPhysicalDeviceInfoValid;
+         CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
+                                           .ScatterGatherSegments[0]
+                                           .SegmentDataPointer =
+           Controller->V2.NewPhysicalDeviceInformationDMA;
+         CommandMailbox->PhysicalDeviceInfo.DataTransferMemoryAddress
+                                           .ScatterGatherSegments[0]
+                                           .SegmentByteCount =
+           CommandMailbox->PhysicalDeviceInfo.DataTransferSize;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      if (Controller->V2.NeedLogicalDeviceInformation)
+       {
+         if (Controller->V2.StartLogicalDeviceInformationScan)
+           {
+             int LogicalDriveNumber;
+             for (LogicalDriveNumber = 0;
+                  LogicalDriveNumber < DAC960_MaxLogicalDrives;
+                  LogicalDriveNumber++)
+               Controller->V2.LogicalDriveFoundDuringScan
+                              [LogicalDriveNumber] = false;
+             Controller->V2.NewLogicalDeviceInformation->LogicalDeviceNumber = 0;
+             Controller->V2.StartLogicalDeviceInformationScan = false;
+           }
+         CommandMailbox->LogicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
+         CommandMailbox->LogicalDeviceInfo.DataTransferSize =
+           sizeof(DAC960_V2_LogicalDeviceInfo_T);
+         CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
+           Controller->V2.NewLogicalDeviceInformation->LogicalDeviceNumber;
+         CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode =
+           DAC960_V2_GetLogicalDeviceInfoValid;
+         CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
+                                          .ScatterGatherSegments[0]
+                                          .SegmentDataPointer =
+           Controller->V2.NewLogicalDeviceInformationDMA;
+         CommandMailbox->LogicalDeviceInfo.DataTransferMemoryAddress
+                                          .ScatterGatherSegments[0]
+                                          .SegmentByteCount =
+           CommandMailbox->LogicalDeviceInfo.DataTransferSize;
+         DAC960_QueueCommand(Command);
+         return;
+       }
+      Controller->MonitoringTimerCount++;
+      Controller->MonitoringTimer.expires =
+       jiffies + DAC960_HealthStatusMonitoringInterval;
+       add_timer(&Controller->MonitoringTimer);
+    }
+  if (CommandType == DAC960_ImmediateCommand)
+    {
+      complete(Command->Completion);
+      Command->Completion = NULL;
+      return;
+    }
+  if (CommandType == DAC960_QueuedCommand)
+    {
+      DAC960_V2_KernelCommand_T *KernelCommand = Command->V2.KernelCommand;
+      KernelCommand->CommandStatus = CommandStatus;
+      KernelCommand->RequestSenseLength = Command->V2.RequestSenseLength;
+      KernelCommand->DataTransferLength = Command->V2.DataTransferResidue;
+      Command->V2.KernelCommand = NULL;
+      DAC960_DeallocateCommand(Command);
+      KernelCommand->CompletionFunction(KernelCommand);
+      return;
+    }
+  /*
+    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_V2_QueueMonitoringCommand(Command);
+      return;
+    }
+  /*
+    Deallocate the Command.
+  */
+  DAC960_DeallocateCommand(Command);
+  /*
+    Wake up any processes waiting on a free Command.
+  */
+  wake_up(&Controller->CommandWaitQueue);
+}
+
+/*
+  DAC960_GEM_InterruptHandler handles hardware interrupts from DAC960 GEM Series
+  Controllers.
+*/
+
+static irqreturn_t DAC960_GEM_InterruptHandler(int IRQ_Channel,
+                                      void *DeviceIdentifier)
+{
+  DAC960_Controller_T *Controller = DeviceIdentifier;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V2_StatusMailbox_T *NextStatusMailbox;
+  unsigned long flags;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_GEM_AcknowledgeInterrupt(ControllerBaseAddress);
+  NextStatusMailbox = Controller->V2.NextStatusMailbox;
+  while (NextStatusMailbox->Fields.CommandIdentifier > 0)
+    {
+       DAC960_V2_CommandIdentifier_T CommandIdentifier =
+           NextStatusMailbox->Fields.CommandIdentifier;
+       DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+       Command->V2.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+       Command->V2.RequestSenseLength =
+           NextStatusMailbox->Fields.RequestSenseLength;
+       Command->V2.DataTransferResidue =
+           NextStatusMailbox->Fields.DataTransferResidue;
+       NextStatusMailbox->Words[0] = 0;
+       if (++NextStatusMailbox > Controller->V2.LastStatusMailbox)
+           NextStatusMailbox = Controller->V2.FirstStatusMailbox;
+       DAC960_V2_ProcessCompletedCommand(Command);
+    }
+  Controller->V2.NextStatusMailbox = NextStatusMailbox;
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequest(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return IRQ_HANDLED;
+}
+
+/*
+  DAC960_BA_InterruptHandler handles hardware interrupts from DAC960 BA Series
+  Controllers.
+*/
+
+static irqreturn_t DAC960_BA_InterruptHandler(int IRQ_Channel,
+                                      void *DeviceIdentifier)
+{
+  DAC960_Controller_T *Controller = DeviceIdentifier;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V2_StatusMailbox_T *NextStatusMailbox;
+  unsigned long flags;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_BA_AcknowledgeInterrupt(ControllerBaseAddress);
+  NextStatusMailbox = Controller->V2.NextStatusMailbox;
+  while (NextStatusMailbox->Fields.CommandIdentifier > 0)
+    {
+      DAC960_V2_CommandIdentifier_T CommandIdentifier =
+       NextStatusMailbox->Fields.CommandIdentifier;
+      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+      Command->V2.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+      Command->V2.RequestSenseLength =
+       NextStatusMailbox->Fields.RequestSenseLength;
+      Command->V2.DataTransferResidue =
+       NextStatusMailbox->Fields.DataTransferResidue;
+      NextStatusMailbox->Words[0] = 0;
+      if (++NextStatusMailbox > Controller->V2.LastStatusMailbox)
+       NextStatusMailbox = Controller->V2.FirstStatusMailbox;
+      DAC960_V2_ProcessCompletedCommand(Command);
+    }
+  Controller->V2.NextStatusMailbox = NextStatusMailbox;
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequest(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return IRQ_HANDLED;
+}
+
+
+/*
+  DAC960_LP_InterruptHandler handles hardware interrupts from DAC960 LP Series
+  Controllers.
+*/
+
+static irqreturn_t DAC960_LP_InterruptHandler(int IRQ_Channel,
+                                      void *DeviceIdentifier)
+{
+  DAC960_Controller_T *Controller = DeviceIdentifier;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V2_StatusMailbox_T *NextStatusMailbox;
+  unsigned long flags;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_LP_AcknowledgeInterrupt(ControllerBaseAddress);
+  NextStatusMailbox = Controller->V2.NextStatusMailbox;
+  while (NextStatusMailbox->Fields.CommandIdentifier > 0)
+    {
+      DAC960_V2_CommandIdentifier_T CommandIdentifier =
+       NextStatusMailbox->Fields.CommandIdentifier;
+      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+      Command->V2.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+      Command->V2.RequestSenseLength =
+       NextStatusMailbox->Fields.RequestSenseLength;
+      Command->V2.DataTransferResidue =
+       NextStatusMailbox->Fields.DataTransferResidue;
+      NextStatusMailbox->Words[0] = 0;
+      if (++NextStatusMailbox > Controller->V2.LastStatusMailbox)
+       NextStatusMailbox = Controller->V2.FirstStatusMailbox;
+      DAC960_V2_ProcessCompletedCommand(Command);
+    }
+  Controller->V2.NextStatusMailbox = NextStatusMailbox;
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequest(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return IRQ_HANDLED;
+}
+
+
+/*
+  DAC960_LA_InterruptHandler handles hardware interrupts from DAC960 LA Series
+  Controllers.
+*/
+
+static irqreturn_t DAC960_LA_InterruptHandler(int IRQ_Channel,
+                                      void *DeviceIdentifier)
+{
+  DAC960_Controller_T *Controller = DeviceIdentifier;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_StatusMailbox_T *NextStatusMailbox;
+  unsigned long flags;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_LA_AcknowledgeInterrupt(ControllerBaseAddress);
+  NextStatusMailbox = Controller->V1.NextStatusMailbox;
+  while (NextStatusMailbox->Fields.Valid)
+    {
+      DAC960_V1_CommandIdentifier_T CommandIdentifier =
+       NextStatusMailbox->Fields.CommandIdentifier;
+      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+      Command->V1.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+      NextStatusMailbox->Word = 0;
+      if (++NextStatusMailbox > Controller->V1.LastStatusMailbox)
+       NextStatusMailbox = Controller->V1.FirstStatusMailbox;
+      DAC960_V1_ProcessCompletedCommand(Command);
+    }
+  Controller->V1.NextStatusMailbox = NextStatusMailbox;
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequest(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return IRQ_HANDLED;
+}
+
+
+/*
+  DAC960_PG_InterruptHandler handles hardware interrupts from DAC960 PG Series
+  Controllers.
+*/
+
+static irqreturn_t DAC960_PG_InterruptHandler(int IRQ_Channel,
+                                      void *DeviceIdentifier)
+{
+  DAC960_Controller_T *Controller = DeviceIdentifier;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_V1_StatusMailbox_T *NextStatusMailbox;
+  unsigned long flags;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_PG_AcknowledgeInterrupt(ControllerBaseAddress);
+  NextStatusMailbox = Controller->V1.NextStatusMailbox;
+  while (NextStatusMailbox->Fields.Valid)
+    {
+      DAC960_V1_CommandIdentifier_T CommandIdentifier =
+       NextStatusMailbox->Fields.CommandIdentifier;
+      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+      Command->V1.CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+      NextStatusMailbox->Word = 0;
+      if (++NextStatusMailbox > Controller->V1.LastStatusMailbox)
+       NextStatusMailbox = Controller->V1.FirstStatusMailbox;
+      DAC960_V1_ProcessCompletedCommand(Command);
+    }
+  Controller->V1.NextStatusMailbox = NextStatusMailbox;
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequest(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return IRQ_HANDLED;
+}
+
+
+/*
+  DAC960_PD_InterruptHandler handles hardware interrupts from DAC960 PD Series
+  Controllers.
+*/
+
+static irqreturn_t DAC960_PD_InterruptHandler(int IRQ_Channel,
+                                      void *DeviceIdentifier)
+{
+  DAC960_Controller_T *Controller = DeviceIdentifier;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  unsigned long flags;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  while (DAC960_PD_StatusAvailableP(ControllerBaseAddress))
+    {
+      DAC960_V1_CommandIdentifier_T CommandIdentifier =
+       DAC960_PD_ReadStatusCommandIdentifier(ControllerBaseAddress);
+      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+      Command->V1.CommandStatus =
+       DAC960_PD_ReadStatusRegister(ControllerBaseAddress);
+      DAC960_PD_AcknowledgeInterrupt(ControllerBaseAddress);
+      DAC960_PD_AcknowledgeStatus(ControllerBaseAddress);
+      DAC960_V1_ProcessCompletedCommand(Command);
+    }
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequest(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return IRQ_HANDLED;
+}
+
+
+/*
+  DAC960_P_InterruptHandler handles hardware interrupts from DAC960 P Series
+  Controllers.
+
+  Translations of DAC960_V1_Enquiry and DAC960_V1_GetDeviceState rely
+  on the data having been placed into DAC960_Controller_T, rather than
+  an arbitrary buffer.
+*/
+
+static irqreturn_t DAC960_P_InterruptHandler(int IRQ_Channel,
+                                     void *DeviceIdentifier)
+{
+  DAC960_Controller_T *Controller = DeviceIdentifier;
+  void __iomem *ControllerBaseAddress = Controller->BaseAddress;
+  unsigned long flags;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  while (DAC960_PD_StatusAvailableP(ControllerBaseAddress))
+    {
+      DAC960_V1_CommandIdentifier_T CommandIdentifier =
+       DAC960_PD_ReadStatusCommandIdentifier(ControllerBaseAddress);
+      DAC960_Command_T *Command = Controller->Commands[CommandIdentifier-1];
+      DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+      DAC960_V1_CommandOpcode_T CommandOpcode =
+       CommandMailbox->Common.CommandOpcode;
+      Command->V1.CommandStatus =
+       DAC960_PD_ReadStatusRegister(ControllerBaseAddress);
+      DAC960_PD_AcknowledgeInterrupt(ControllerBaseAddress);
+      DAC960_PD_AcknowledgeStatus(ControllerBaseAddress);
+      switch (CommandOpcode)
+       {
+       case DAC960_V1_Enquiry_Old:
+         Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Enquiry;
+         DAC960_P_To_PD_TranslateEnquiry(Controller->V1.NewEnquiry);
+         break;
+       case DAC960_V1_GetDeviceState_Old:
+         Command->V1.CommandMailbox.Common.CommandOpcode =
+                                               DAC960_V1_GetDeviceState;
+         DAC960_P_To_PD_TranslateDeviceState(Controller->V1.NewDeviceState);
+         break;
+       case DAC960_V1_Read_Old:
+         Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Read;
+         DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+         break;
+       case DAC960_V1_Write_Old:
+         Command->V1.CommandMailbox.Common.CommandOpcode = DAC960_V1_Write;
+         DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+         break;
+       case DAC960_V1_ReadWithScatterGather_Old:
+         Command->V1.CommandMailbox.Common.CommandOpcode =
+           DAC960_V1_ReadWithScatterGather;
+         DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+         break;
+       case DAC960_V1_WriteWithScatterGather_Old:
+         Command->V1.CommandMailbox.Common.CommandOpcode =
+           DAC960_V1_WriteWithScatterGather;
+         DAC960_P_To_PD_TranslateReadWriteCommand(CommandMailbox);
+         break;
+       default:
+         break;
+       }
+      DAC960_V1_ProcessCompletedCommand(Command);
+    }
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  DAC960_ProcessRequest(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return IRQ_HANDLED;
+}
+
+
+/*
+  DAC960_V1_QueueMonitoringCommand queues a Monitoring Command to DAC960 V1
+  Firmware Controllers.
+*/
+
+static void DAC960_V1_QueueMonitoringCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  DAC960_V1_ClearCommand(Command);
+  Command->CommandType = DAC960_MonitoringCommand;
+  CommandMailbox->Type3.CommandOpcode = DAC960_V1_Enquiry;
+  CommandMailbox->Type3.BusAddress = Controller->V1.NewEnquiryDMA;
+  DAC960_QueueCommand(Command);
+}
+
+
+/*
+  DAC960_V2_QueueMonitoringCommand queues a Monitoring Command to DAC960 V2
+  Firmware Controllers.
+*/
+
+static void DAC960_V2_QueueMonitoringCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_V2_CommandMailbox_T *CommandMailbox = &Command->V2.CommandMailbox;
+  DAC960_V2_ClearCommand(Command);
+  Command->CommandType = DAC960_MonitoringCommand;
+  CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->ControllerInfo.CommandControlBits
+                               .DataTransferControllerToHost = true;
+  CommandMailbox->ControllerInfo.CommandControlBits
+                               .NoAutoRequestSense = true;
+  CommandMailbox->ControllerInfo.DataTransferSize =
+    sizeof(DAC960_V2_ControllerInfo_T);
+  CommandMailbox->ControllerInfo.ControllerNumber = 0;
+  CommandMailbox->ControllerInfo.IOCTL_Opcode = DAC960_V2_GetControllerInfo;
+  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+                               .ScatterGatherSegments[0]
+                               .SegmentDataPointer =
+    Controller->V2.NewControllerInformationDMA;
+  CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+                               .ScatterGatherSegments[0]
+                               .SegmentByteCount =
+    CommandMailbox->ControllerInfo.DataTransferSize;
+  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;
+  unsigned long flags;
+
+  if (Controller->FirmwareType == DAC960_V1_Controller)
+    {
+      spin_lock_irqsave(&Controller->queue_lock, flags);
+      /*
+       Queue a Status Monitoring Command to Controller.
+      */
+      Command = DAC960_AllocateCommand(Controller);
+      if (Command != NULL)
+       DAC960_V1_QueueMonitoringCommand(Command);
+      else Controller->MonitoringCommandDeferred = true;
+      spin_unlock_irqrestore(&Controller->queue_lock, flags);
+    }
+  else
+    {
+      DAC960_V2_ControllerInfo_T *ControllerInfo =
+       &Controller->V2.ControllerInformation;
+      unsigned int StatusChangeCounter =
+       Controller->V2.HealthStatusBuffer->StatusChangeCounter;
+      bool ForceMonitoringCommand = false;
+      if (time_after(jiffies, Controller->SecondaryMonitoringTime
+         + DAC960_SecondaryMonitoringInterval))
+       {
+         int LogicalDriveNumber;
+         for (LogicalDriveNumber = 0;
+              LogicalDriveNumber < DAC960_MaxLogicalDrives;
+              LogicalDriveNumber++)
+           {
+             DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo =
+               Controller->V2.LogicalDeviceInformation[LogicalDriveNumber];
+             if (LogicalDeviceInfo == NULL) continue;
+             if (!LogicalDeviceInfo->LogicalDeviceControl
+                                    .LogicalDeviceInitialized)
+               {
+                 ForceMonitoringCommand = true;
+                 break;
+               }
+           }
+         Controller->SecondaryMonitoringTime = jiffies;
+       }
+      if (StatusChangeCounter == Controller->V2.StatusChangeCounter &&
+         Controller->V2.HealthStatusBuffer->NextEventSequenceNumber
+         == Controller->V2.NextEventSequenceNumber &&
+         (ControllerInfo->BackgroundInitializationsActive +
+          ControllerInfo->LogicalDeviceInitializationsActive +
+          ControllerInfo->PhysicalDeviceInitializationsActive +
+          ControllerInfo->ConsistencyChecksActive +
+          ControllerInfo->RebuildsActive +
+          ControllerInfo->OnlineExpansionsActive == 0 ||
+          time_before(jiffies, Controller->PrimaryMonitoringTime
+          + DAC960_MonitoringTimerInterval)) &&
+         !ForceMonitoringCommand)
+       {
+         Controller->MonitoringTimer.expires =
+           jiffies + DAC960_HealthStatusMonitoringInterval;
+           add_timer(&Controller->MonitoringTimer);
+         return;
+       }
+      Controller->V2.StatusChangeCounter = StatusChangeCounter;
+      Controller->PrimaryMonitoringTime = jiffies;
+
+      spin_lock_irqsave(&Controller->queue_lock, flags);
+      /*
+       Queue a Status Monitoring Command to Controller.
+      */
+      Command = DAC960_AllocateCommand(Controller);
+      if (Command != NULL)
+       DAC960_V2_QueueMonitoringCommand(Command);
+      else Controller->MonitoringCommandDeferred = true;
+      spin_unlock_irqrestore(&Controller->queue_lock, flags);
+      /*
+       Wake up any processes waiting on a Health Status Buffer change.
+      */
+      wake_up(&Controller->HealthStatusWaitQueue);
+    }
+}
+
+/*
+  DAC960_CheckStatusBuffer verifies that there is room to hold ByteCount
+  additional bytes in the Combined Status Buffer and grows the buffer if
+  necessary.  It returns true if there is enough room and false otherwise.
+*/
+
+static bool DAC960_CheckStatusBuffer(DAC960_Controller_T *Controller,
+                                       unsigned int ByteCount)
+{
+  unsigned char *NewStatusBuffer;
+  if (Controller->InitialStatusLength + 1 +
+      Controller->CurrentStatusLength + ByteCount + 1 <=
+      Controller->CombinedStatusBufferLength)
+    return true;
+  if (Controller->CombinedStatusBufferLength == 0)
+    {
+      unsigned int NewStatusBufferLength = DAC960_InitialStatusBufferSize;
+      while (NewStatusBufferLength < ByteCount)
+       NewStatusBufferLength *= 2;
+      Controller->CombinedStatusBuffer = kmalloc(NewStatusBufferLength,
+                                                 GFP_ATOMIC);
+      if (Controller->CombinedStatusBuffer == NULL) return false;
+      Controller->CombinedStatusBufferLength = NewStatusBufferLength;
+      return true;
+    }
+  NewStatusBuffer = kmalloc(2 * Controller->CombinedStatusBufferLength,
+                            GFP_ATOMIC);
+  if (NewStatusBuffer == NULL)
+    {
+      DAC960_Warning("Unable to expand Combined Status Buffer - Truncating\n",
+                    Controller);
+      return false;
+    }
+  memcpy(NewStatusBuffer, Controller->CombinedStatusBuffer,
+        Controller->CombinedStatusBufferLength);
+  kfree(Controller->CombinedStatusBuffer);
+  Controller->CombinedStatusBuffer = NewStatusBuffer;
+  Controller->CombinedStatusBufferLength *= 2;
+  Controller->CurrentStatusBuffer =
+    &NewStatusBuffer[Controller->InitialStatusLength + 1];
+  return true;
+}
+
+
+/*
+  DAC960_Message prints Driver Messages.
+*/
+
+static void DAC960_Message(DAC960_MessageLevel_T MessageLevel,
+                          unsigned char *Format,
+                          DAC960_Controller_T *Controller,
+                          ...)
+{
+  static unsigned char Buffer[DAC960_LineBufferSize];
+  static bool 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)
+       {
+         if (DAC960_CheckStatusBuffer(Controller, Length))
+           {
+             strcpy(&Controller->CombinedStatusBuffer
+                                 [Controller->InitialStatusLength],
+                    Buffer);
+             Controller->InitialStatusLength += Length;
+             Controller->CurrentStatusBuffer =
+               &Controller->CombinedStatusBuffer
+                            [Controller->InitialStatusLength + 1];
+           }
+         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 if (DAC960_CheckStatusBuffer(Controller, Length))
+       {
+         strcpy(&Controller->CurrentStatusBuffer[
+                   Controller->CurrentStatusLength], Buffer);
+         Controller->CurrentStatusLength += Length;
+       }
+    }
+  else if (MessageLevel == DAC960_ProgressLevel)
+    {
+      strcpy(Controller->ProgressBuffer, Buffer);
+      Controller->ProgressBufferLength = Length;
+      if (Controller->EphemeralProgressMessage)
+       {
+         if (time_after_eq(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_ParsePhysicalDevice parses spaces followed by a Physical Device
+  Channel:TargetID specification from a User Command string.  It updates
+  Channel and TargetID and returns true on success and false on failure.
+*/
+
+static bool DAC960_ParsePhysicalDevice(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 >= Controller->Targets)
+    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 false on failure.
+*/
+
+static bool 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 > DAC960_MaxLogicalDrives - 1)
+    return false;
+  *LogicalDriveNumber = XLogicalDriveNumber;
+  return true;
+}
+
+
+/*
+  DAC960_V1_SetDeviceState sets the Device State for a Physical Device for
+  DAC960 V1 Firmware Controllers.
+*/
+
+static void DAC960_V1_SetDeviceState(DAC960_Controller_T *Controller,
+                                    DAC960_Command_T *Command,
+                                    unsigned char Channel,
+                                    unsigned char TargetID,
+                                    DAC960_V1_PhysicalDeviceState_T
+                                      DeviceState,
+                                    const unsigned char *DeviceStateString)
+{
+  DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox;
+  CommandMailbox->Type3D.CommandOpcode = DAC960_V1_StartDevice;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.DeviceState = DeviceState;
+  CommandMailbox->Type3D.Modifier = 0;
+  DAC960_ExecuteCommand(Command);
+  switch (Command->V1.CommandStatus)
+    {
+    case DAC960_V1_NormalCompletion:
+      DAC960_UserCritical("%s of Physical Device %d:%d Succeeded\n", Controller,
+                         DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_V1_UnableToStartDevice:
+      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
+                         "Unable to Start Device\n", Controller,
+                         DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_V1_NoDeviceAtAddress:
+      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
+                         "No Device at Address\n", Controller,
+                         DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_V1_InvalidChannelOrTargetOrModifier:
+      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
+                         "Invalid Channel or Target or Modifier\n",
+                         Controller, DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_V1_ChannelBusy:
+      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
+                         "Channel Busy\n", Controller,
+                         DeviceStateString, Channel, TargetID);
+      break;
+    default:
+      DAC960_UserCritical("%s of Physical Device %d:%d Failed - "
+                         "Unexpected Status %04X\n", Controller,
+                         DeviceStateString, Channel, TargetID,
+                         Command->V1.CommandStatus);
+      break;
+    }
+}
+
+
+/*
+  DAC960_V1_ExecuteUserCommand executes a User Command for DAC960 V1 Firmware
+  Controllers.
+*/
+
+static bool DAC960_V1_ExecuteUserCommand(DAC960_Controller_T *Controller,
+                                           unsigned char *UserCommand)
+{
+  DAC960_Command_T *Command;
+  DAC960_V1_CommandMailbox_T *CommandMailbox;
+  unsigned long flags;
+  unsigned char Channel, TargetID, LogicalDriveNumber;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
+    DAC960_WaitForCommand(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  Controller->UserStatusLength = 0;
+  DAC960_V1_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox = &Command->V1.CommandMailbox;
+  if (strcmp(UserCommand, "flush-cache") == 0)
+    {
+      CommandMailbox->Type3.CommandOpcode = DAC960_V1_Flush;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Cache Flush Completed\n", Controller);
+    }
+  else if (strncmp(UserCommand, "kill", 4) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[4],
+                                     &Channel, &TargetID))
+    {
+      DAC960_V1_DeviceState_T *DeviceState =
+       &Controller->V1.DeviceState[Channel][TargetID];
+      if (DeviceState->Present &&
+         DeviceState->DeviceType == DAC960_V1_DiskType &&
+         DeviceState->DeviceState != DAC960_V1_Device_Dead)
+       DAC960_V1_SetDeviceState(Controller, Command, Channel, TargetID,
+                                DAC960_V1_Device_Dead, "Kill");
+      else DAC960_UserCritical("Kill of Physical Device %d:%d Illegal\n",
+                              Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "make-online", 11) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[11],
+                                     &Channel, &TargetID))
+    {
+      DAC960_V1_DeviceState_T *DeviceState =
+       &Controller->V1.DeviceState[Channel][TargetID];
+      if (DeviceState->Present &&
+         DeviceState->DeviceType == DAC960_V1_DiskType &&
+         DeviceState->DeviceState == DAC960_V1_Device_Dead)
+       DAC960_V1_SetDeviceState(Controller, Command, Channel, TargetID,
+                                DAC960_V1_Device_Online, "Make Online");
+      else DAC960_UserCritical("Make Online of Physical Device %d:%d Illegal\n",
+                              Controller, Channel, TargetID);
+
+    }
+  else if (strncmp(UserCommand, "make-standby", 12) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[12],
+                                     &Channel, &TargetID))
+    {
+      DAC960_V1_DeviceState_T *DeviceState =
+       &Controller->V1.DeviceState[Channel][TargetID];
+      if (DeviceState->Present &&
+         DeviceState->DeviceType == DAC960_V1_DiskType &&
+         DeviceState->DeviceState == DAC960_V1_Device_Dead)
+       DAC960_V1_SetDeviceState(Controller, Command, Channel, TargetID,
+                                DAC960_V1_Device_Standby, "Make Standby");
+      else DAC960_UserCritical("Make Standby of Physical "
+                              "Device %d:%d Illegal\n",
+                              Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "rebuild", 7) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[7],
+                                     &Channel, &TargetID))
+    {
+      CommandMailbox->Type3D.CommandOpcode = DAC960_V1_RebuildAsync;
+      CommandMailbox->Type3D.Channel = Channel;
+      CommandMailbox->Type3D.TargetID = TargetID;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->V1.CommandStatus)
+       {
+       case DAC960_V1_NormalCompletion:
+         DAC960_UserCritical("Rebuild of Physical Device %d:%d Initiated\n",
+                             Controller, Channel, TargetID);
+         break;
+       case DAC960_V1_AttemptToRebuildOnlineDrive:
+         DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
+                             "Attempt to Rebuild Online or "
+                             "Unresponsive Drive\n",
+                             Controller, Channel, TargetID);
+         break;
+       case DAC960_V1_NewDiskFailedDuringRebuild:
+         DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
+                             "New Disk Failed During Rebuild\n",
+                             Controller, Channel, TargetID);
+         break;
+       case DAC960_V1_InvalidDeviceAddress:
+         DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
+                             "Invalid Device Address\n",
+                             Controller, Channel, TargetID);
+         break;
+       case DAC960_V1_RebuildOrCheckAlreadyInProgress:
+         DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
+                             "Rebuild or Consistency Check Already "
+                             "in Progress\n", Controller, Channel, TargetID);
+         break;
+       default:
+         DAC960_UserCritical("Rebuild of Physical Device %d:%d Failed - "
+                             "Unexpected Status %04X\n", Controller,
+                             Channel, TargetID, Command->V1.CommandStatus);
+         break;
+       }
+    }
+  else if (strncmp(UserCommand, "check-consistency", 17) == 0 &&
+          DAC960_ParseLogicalDrive(Controller, &UserCommand[17],
+                                   &LogicalDriveNumber))
+    {
+      CommandMailbox->Type3C.CommandOpcode = DAC960_V1_CheckConsistencyAsync;
+      CommandMailbox->Type3C.LogicalDriveNumber = LogicalDriveNumber;
+      CommandMailbox->Type3C.AutoRestore = true;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->V1.CommandStatus)
+       {
+       case DAC960_V1_NormalCompletion:
+         DAC960_UserCritical("Consistency Check of Logical Drive %d "
+                             "(/dev/rd/c%dd%d) Initiated\n",
+                             Controller, LogicalDriveNumber,
+                             Controller->ControllerNumber,
+                             LogicalDriveNumber);
+         break;
+       case DAC960_V1_DependentDiskIsDead:
+         DAC960_UserCritical("Consistency Check of Logical Drive %d "
+                             "(/dev/rd/c%dd%d) Failed - "
+                             "Dependent Physical Device is DEAD\n",
+                             Controller, LogicalDriveNumber,
+                             Controller->ControllerNumber,
+                             LogicalDriveNumber);
+         break;
+       case DAC960_V1_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_V1_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->V1.CommandStatus);
+         break;
+       }
+    }
+  else if (strcmp(UserCommand, "cancel-rebuild") == 0 ||
+          strcmp(UserCommand, "cancel-consistency-check") == 0)
+    {
+      /*
+        the OldRebuildRateConstant is never actually used
+        once its value is retrieved from the controller.
+       */
+      unsigned char *OldRebuildRateConstant;
+      dma_addr_t OldRebuildRateConstantDMA;
+
+      OldRebuildRateConstant = pci_alloc_consistent( Controller->PCIDevice,
+               sizeof(char), &OldRebuildRateConstantDMA);
+      if (OldRebuildRateConstant == NULL) {
+         DAC960_UserCritical("Cancellation of Rebuild or "
+                            "Consistency Check Failed - "
+                            "Out of Memory",
+                             Controller);
+        goto failure;
+      }
+      CommandMailbox->Type3R.CommandOpcode = DAC960_V1_RebuildControl;
+      CommandMailbox->Type3R.RebuildRateConstant = 0xFF;
+      CommandMailbox->Type3R.BusAddress = OldRebuildRateConstantDMA;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->V1.CommandStatus)
+       {
+       case DAC960_V1_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->V1.CommandStatus);
+         break;
+       }
+failure:
+       pci_free_consistent(Controller->PCIDevice, sizeof(char),
+               OldRebuildRateConstant, OldRebuildRateConstantDMA);
+    }
+  else DAC960_UserCritical("Illegal User Command: '%s'\n",
+                          Controller, UserCommand);
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_DeallocateCommand(Command);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return true;
+}
+
+
+/*
+  DAC960_V2_TranslatePhysicalDevice translates a Physical Device Channel and
+  TargetID into a Logical Device.  It returns true on success and false
+  on failure.
+*/
+
+static bool DAC960_V2_TranslatePhysicalDevice(DAC960_Command_T *Command,
+                                                unsigned char Channel,
+                                                unsigned char TargetID,
+                                                unsigned short
+                                                  *LogicalDeviceNumber)
+{
+  DAC960_V2_CommandMailbox_T SavedCommandMailbox, *CommandMailbox;
+  DAC960_Controller_T *Controller =  Command->Controller;
+
+  CommandMailbox = &Command->V2.CommandMailbox;
+  memcpy(&SavedCommandMailbox, CommandMailbox,
+        sizeof(DAC960_V2_CommandMailbox_T));
+
+  CommandMailbox->PhysicalDeviceInfo.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
+                                   .DataTransferControllerToHost = true;
+  CommandMailbox->PhysicalDeviceInfo.CommandControlBits
+                                   .NoAutoRequestSense = true;
+  CommandMailbox->PhysicalDeviceInfo.DataTransferSize =
+    sizeof(DAC960_V2_PhysicalToLogicalDevice_T);
+  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.TargetID = TargetID;
+  CommandMailbox->PhysicalDeviceInfo.PhysicalDevice.Channel = Channel;
+  CommandMailbox->PhysicalDeviceInfo.IOCTL_Opcode =
+    DAC960_V2_TranslatePhysicalToLogicalDevice;
+  CommandMailbox->Common.DataTransferMemoryAddress
+                       .ScatterGatherSegments[0]
+                       .SegmentDataPointer =
+               Controller->V2.PhysicalToLogicalDeviceDMA;
+  CommandMailbox->Common.DataTransferMemoryAddress
+                       .ScatterGatherSegments[0]
+                       .SegmentByteCount =
+               CommandMailbox->Common.DataTransferSize;
+
+  DAC960_ExecuteCommand(Command);
+  *LogicalDeviceNumber = Controller->V2.PhysicalToLogicalDevice->LogicalDeviceNumber;
+
+  memcpy(CommandMailbox, &SavedCommandMailbox,
+        sizeof(DAC960_V2_CommandMailbox_T));
+  return (Command->V2.CommandStatus == DAC960_V2_NormalCompletion);
+}
+
+
+/*
+  DAC960_V2_ExecuteUserCommand executes a User Command for DAC960 V2 Firmware
+  Controllers.
+*/
+
+static bool DAC960_V2_ExecuteUserCommand(DAC960_Controller_T *Controller,
+                                           unsigned char *UserCommand)
+{
+  DAC960_Command_T *Command;
+  DAC960_V2_CommandMailbox_T *CommandMailbox;
+  unsigned long flags;
+  unsigned char Channel, TargetID, LogicalDriveNumber;
+  unsigned short LogicalDeviceNumber;
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
+    DAC960_WaitForCommand(Controller);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  Controller->UserStatusLength = 0;
+  DAC960_V2_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox = &Command->V2.CommandMailbox;
+  CommandMailbox->Common.CommandOpcode = DAC960_V2_IOCTL;
+  CommandMailbox->Common.CommandControlBits.DataTransferControllerToHost = true;
+  CommandMailbox->Common.CommandControlBits.NoAutoRequestSense = true;
+  if (strcmp(UserCommand, "flush-cache") == 0)
+    {
+      CommandMailbox->DeviceOperation.IOCTL_Opcode = DAC960_V2_PauseDevice;
+      CommandMailbox->DeviceOperation.OperationDevice =
+       DAC960_V2_RAID_Controller;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Cache Flush Completed\n", Controller);
+    }
+  else if (strncmp(UserCommand, "kill", 4) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[4],
+                                     &Channel, &TargetID) &&
+          DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
+                                            &LogicalDeviceNumber))
+    {
+      CommandMailbox->SetDeviceState.LogicalDevice.LogicalDeviceNumber =
+       LogicalDeviceNumber;
+      CommandMailbox->SetDeviceState.IOCTL_Opcode =
+       DAC960_V2_SetDeviceState;
+      CommandMailbox->SetDeviceState.DeviceState.PhysicalDeviceState =
+       DAC960_V2_Device_Dead;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Kill of Physical Device %d:%d %s\n",
+                         Controller, Channel, TargetID,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Succeeded" : "Failed"));
+    }
+  else if (strncmp(UserCommand, "make-online", 11) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[11],
+                                     &Channel, &TargetID) &&
+          DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
+                                            &LogicalDeviceNumber))
+    {
+      CommandMailbox->SetDeviceState.LogicalDevice.LogicalDeviceNumber =
+       LogicalDeviceNumber;
+      CommandMailbox->SetDeviceState.IOCTL_Opcode =
+       DAC960_V2_SetDeviceState;
+      CommandMailbox->SetDeviceState.DeviceState.PhysicalDeviceState =
+       DAC960_V2_Device_Online;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Make Online of Physical Device %d:%d %s\n",
+                         Controller, Channel, TargetID,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Succeeded" : "Failed"));
+    }
+  else if (strncmp(UserCommand, "make-standby", 12) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[12],
+                                     &Channel, &TargetID) &&
+          DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
+                                            &LogicalDeviceNumber))
+    {
+      CommandMailbox->SetDeviceState.LogicalDevice.LogicalDeviceNumber =
+       LogicalDeviceNumber;
+      CommandMailbox->SetDeviceState.IOCTL_Opcode =
+       DAC960_V2_SetDeviceState;
+      CommandMailbox->SetDeviceState.DeviceState.PhysicalDeviceState =
+       DAC960_V2_Device_Standby;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Make Standby of Physical Device %d:%d %s\n",
+                         Controller, Channel, TargetID,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Succeeded" : "Failed"));
+    }
+  else if (strncmp(UserCommand, "rebuild", 7) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[7],
+                                     &Channel, &TargetID) &&
+          DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
+                                            &LogicalDeviceNumber))
+    {
+      CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
+       LogicalDeviceNumber;
+      CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode =
+       DAC960_V2_RebuildDeviceStart;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Rebuild of Physical Device %d:%d %s\n",
+                         Controller, Channel, TargetID,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Initiated" : "Not Initiated"));
+    }
+  else if (strncmp(UserCommand, "cancel-rebuild", 14) == 0 &&
+          DAC960_ParsePhysicalDevice(Controller, &UserCommand[14],
+                                     &Channel, &TargetID) &&
+          DAC960_V2_TranslatePhysicalDevice(Command, Channel, TargetID,
+                                            &LogicalDeviceNumber))
+    {
+      CommandMailbox->LogicalDeviceInfo.LogicalDevice.LogicalDeviceNumber =
+       LogicalDeviceNumber;
+      CommandMailbox->LogicalDeviceInfo.IOCTL_Opcode =
+       DAC960_V2_RebuildDeviceStop;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Rebuild of Physical Device %d:%d %s\n",
+                         Controller, Channel, TargetID,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Cancelled" : "Not Cancelled"));
+    }
+  else if (strncmp(UserCommand, "check-consistency", 17) == 0 &&
+          DAC960_ParseLogicalDrive(Controller, &UserCommand[17],
+                                   &LogicalDriveNumber))
+    {
+      CommandMailbox->ConsistencyCheck.LogicalDevice.LogicalDeviceNumber =
+       LogicalDriveNumber;
+      CommandMailbox->ConsistencyCheck.IOCTL_Opcode =
+       DAC960_V2_ConsistencyCheckStart;
+      CommandMailbox->ConsistencyCheck.RestoreConsistency = true;
+      CommandMailbox->ConsistencyCheck.InitializedAreaOnly = false;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Consistency Check of Logical Drive %d "
+                         "(/dev/rd/c%dd%d) %s\n",
+                         Controller, LogicalDriveNumber,
+                         Controller->ControllerNumber,
+                         LogicalDriveNumber,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Initiated" : "Not Initiated"));
+    }
+  else if (strncmp(UserCommand, "cancel-consistency-check", 24) == 0 &&
+          DAC960_ParseLogicalDrive(Controller, &UserCommand[24],
+                                   &LogicalDriveNumber))
+    {
+      CommandMailbox->ConsistencyCheck.LogicalDevice.LogicalDeviceNumber =
+       LogicalDriveNumber;
+      CommandMailbox->ConsistencyCheck.IOCTL_Opcode =
+       DAC960_V2_ConsistencyCheckStop;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Consistency Check of Logical Drive %d "
+                         "(/dev/rd/c%dd%d) %s\n",
+                         Controller, LogicalDriveNumber,
+                         Controller->ControllerNumber,
+                         LogicalDriveNumber,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Cancelled" : "Not Cancelled"));
+    }
+  else if (strcmp(UserCommand, "perform-discovery") == 0)
+    {
+      CommandMailbox->Common.IOCTL_Opcode = DAC960_V2_StartDiscovery;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Discovery %s\n", Controller,
+                         (Command->V2.CommandStatus
+                          == DAC960_V2_NormalCompletion
+                          ? "Initiated" : "Not Initiated"));
+      if (Command->V2.CommandStatus == DAC960_V2_NormalCompletion)
+       {
+         CommandMailbox->ControllerInfo.CommandOpcode = DAC960_V2_IOCTL;
+         CommandMailbox->ControllerInfo.CommandControlBits
+                                       .DataTransferControllerToHost = true;
+         CommandMailbox->ControllerInfo.CommandControlBits
+                                       .NoAutoRequestSense = true;
+         CommandMailbox->ControllerInfo.DataTransferSize =
+           sizeof(DAC960_V2_ControllerInfo_T);
+         CommandMailbox->ControllerInfo.ControllerNumber = 0;
+         CommandMailbox->ControllerInfo.IOCTL_Opcode =
+           DAC960_V2_GetControllerInfo;
+         /*
+          * How does this NOT race with the queued Monitoring
+          * usage of this structure?
+          */
+         CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+                                       .ScatterGatherSegments[0]
+                                       .SegmentDataPointer =
+           Controller->V2.NewControllerInformationDMA;
+         CommandMailbox->ControllerInfo.DataTransferMemoryAddress
+                                       .ScatterGatherSegments[0]
+                                       .SegmentByteCount =
+           CommandMailbox->ControllerInfo.DataTransferSize;
+         while (1) {
+           DAC960_ExecuteCommand(Command);
+           if (!Controller->V2.NewControllerInformation->PhysicalScanActive)
+               break;
+           msleep(1000);
+         }
+         DAC960_UserCritical("Discovery Completed\n", Controller);
+       }
+    }
+  else if (strcmp(UserCommand, "suppress-enclosure-messages") == 0)
+    Controller->SuppressEnclosureMessages = true;
+  else DAC960_UserCritical("Illegal User Command: '%s'\n",
+                          Controller, UserCommand);
+
+  spin_lock_irqsave(&Controller->queue_lock, flags);
+  DAC960_DeallocateCommand(Command);
+  spin_unlock_irqrestore(&Controller->queue_lock, flags);
+  return true;
+}
+
+static int dac960_proc_show(struct seq_file *m, void *v)
+{
+  unsigned char *StatusMessage = "OK\n";
+  int ControllerNumber;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      if (Controller->MonitoringAlertMode)
+       {
+         StatusMessage = "ALERT\n";
+         break;
+       }
+    }
+  seq_puts(m, StatusMessage);
+  return 0;
+}
+
+static int dac960_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dac960_proc_show, NULL);
+}
+
+static const struct file_operations dac960_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = dac960_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int dac960_initial_status_proc_show(struct seq_file *m, void *v)
+{
+       DAC960_Controller_T *Controller = (DAC960_Controller_T *)m->private;
+       seq_printf(m, "%.*s", Controller->InitialStatusLength, Controller->CombinedStatusBuffer);
+       return 0;
+}
+
+static int dac960_initial_status_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dac960_initial_status_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations dac960_initial_status_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = dac960_initial_status_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int dac960_current_status_proc_show(struct seq_file *m, void *v)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) m->private;
+  unsigned char *StatusMessage =
+    "No Rebuild or Consistency Check in Progress\n";
+  int ProgressMessageLength = strlen(StatusMessage);
+  if (jiffies != Controller->LastCurrentStatusTime)
+    {
+      Controller->CurrentStatusLength = 0;
+      DAC960_AnnounceDriver(Controller);
+      DAC960_ReportControllerConfiguration(Controller);
+      DAC960_ReportDeviceConfiguration(Controller);
+      if (Controller->ProgressBufferLength > 0)
+       ProgressMessageLength = Controller->ProgressBufferLength;
+      if (DAC960_CheckStatusBuffer(Controller, 2 + ProgressMessageLength))
+       {
+         unsigned char *CurrentStatusBuffer = Controller->CurrentStatusBuffer;
+         CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+         CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+         if (Controller->ProgressBufferLength > 0)
+           strcpy(&CurrentStatusBuffer[Controller->CurrentStatusLength],
+                  Controller->ProgressBuffer);
+         else
+           strcpy(&CurrentStatusBuffer[Controller->CurrentStatusLength],
+                  StatusMessage);
+         Controller->CurrentStatusLength += ProgressMessageLength;
+       }
+      Controller->LastCurrentStatusTime = jiffies;
+    }
+       seq_printf(m, "%.*s", Controller->CurrentStatusLength, Controller->CurrentStatusBuffer);
+       return 0;
+}
+
+static int dac960_current_status_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dac960_current_status_proc_show, PDE_DATA(inode));
+}
+
+static const struct file_operations dac960_current_status_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = dac960_current_status_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int dac960_user_command_proc_show(struct seq_file *m, void *v)
+{
+       DAC960_Controller_T *Controller = (DAC960_Controller_T *)m->private;
+
+       seq_printf(m, "%.*s", Controller->UserStatusLength, Controller->UserStatusBuffer);
+       return 0;
+}
+
+static int dac960_user_command_proc_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dac960_user_command_proc_show, PDE_DATA(inode));
+}
+
+static ssize_t dac960_user_command_proc_write(struct file *file,
+                                      const char __user *Buffer,
+                                      size_t Count, loff_t *pos)
+{
+  DAC960_Controller_T *Controller = PDE_DATA(file_inode(file));
+  unsigned char CommandBuffer[80];
+  int Length;
+  if (Count > sizeof(CommandBuffer)-1) return -EINVAL;
+  if (copy_from_user(CommandBuffer, Buffer, Count)) return -EFAULT;
+  CommandBuffer[Count] = '\0';
+  Length = strlen(CommandBuffer);
+  if (Length > 0 && CommandBuffer[Length-1] == '\n')
+    CommandBuffer[--Length] = '\0';
+  if (Controller->FirmwareType == DAC960_V1_Controller)
+    return (DAC960_V1_ExecuteUserCommand(Controller, CommandBuffer)
+           ? Count : -EBUSY);
+  else
+    return (DAC960_V2_ExecuteUserCommand(Controller, CommandBuffer)
+           ? Count : -EBUSY);
+}
+
+static const struct file_operations dac960_user_command_proc_fops = {
+       .owner          = THIS_MODULE,
+       .open           = dac960_user_command_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+       .write          = dac960_user_command_proc_write,
+};
+
+/*
+  DAC960_CreateProcEntries creates the /proc/rd/... entries for the
+  DAC960 Driver.
+*/
+
+static void DAC960_CreateProcEntries(DAC960_Controller_T *Controller)
+{
+       struct proc_dir_entry *ControllerProcEntry;
+
+       if (DAC960_ProcDirectoryEntry == NULL) {
+               DAC960_ProcDirectoryEntry = proc_mkdir("rd", NULL);
+               proc_create("status", 0, DAC960_ProcDirectoryEntry,
+                           &dac960_proc_fops);
+       }
+
+       sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber);
+       ControllerProcEntry = proc_mkdir(Controller->ControllerName,
+                                        DAC960_ProcDirectoryEntry);
+       proc_create_data("initial_status", 0, ControllerProcEntry, &dac960_initial_status_proc_fops, Controller);
+       proc_create_data("current_status", 0, ControllerProcEntry, &dac960_current_status_proc_fops, Controller);
+       proc_create_data("user_command", S_IWUSR | S_IRUSR, ControllerProcEntry, &dac960_user_command_proc_fops, Controller);
+       Controller->ControllerProcEntry = ControllerProcEntry;
+}
+
+
+/*
+  DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the
+  DAC960 Driver.
+*/
+
+static void DAC960_DestroyProcEntries(DAC960_Controller_T *Controller)
+{
+      if (Controller->ControllerProcEntry == NULL)
+             return;
+      remove_proc_entry("initial_status", Controller->ControllerProcEntry);
+      remove_proc_entry("current_status", Controller->ControllerProcEntry);
+      remove_proc_entry("user_command", Controller->ControllerProcEntry);
+      remove_proc_entry(Controller->ControllerName, DAC960_ProcDirectoryEntry);
+      Controller->ControllerProcEntry = NULL;
+}
+
+#ifdef DAC960_GAM_MINOR
+
+/*
+ * DAC960_gam_ioctl is the ioctl function for performing RAID operations.
+*/
+
+static long DAC960_gam_ioctl(struct file *file, unsigned int Request,
+                                               unsigned long Argument)
+{
+  long ErrorCode = 0;
+  if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+
+  mutex_lock(&DAC960_mutex);
+  switch (Request)
+    {
+    case DAC960_IOCTL_GET_CONTROLLER_COUNT:
+      ErrorCode = DAC960_ControllerCount;
+      break;
+    case DAC960_IOCTL_GET_CONTROLLER_INFO:
+      {
+       DAC960_ControllerInfo_T __user *UserSpaceControllerInfo =
+         (DAC960_ControllerInfo_T __user *) Argument;
+       DAC960_ControllerInfo_T ControllerInfo;
+       DAC960_Controller_T *Controller;
+       int ControllerNumber;
+       if (UserSpaceControllerInfo == NULL)
+               ErrorCode = -EINVAL;
+       else ErrorCode = get_user(ControllerNumber,
+                            &UserSpaceControllerInfo->ControllerNumber);
+       if (ErrorCode != 0)
+               break;
+       ErrorCode = -ENXIO;
+       if (ControllerNumber < 0 ||
+           ControllerNumber > DAC960_ControllerCount - 1) {
+         break;
+       }
+       Controller = DAC960_Controllers[ControllerNumber];
+       if (Controller == NULL)
+               break;
+       memset(&ControllerInfo, 0, sizeof(DAC960_ControllerInfo_T));
+       ControllerInfo.ControllerNumber = ControllerNumber;
+       ControllerInfo.FirmwareType = Controller->FirmwareType;
+       ControllerInfo.Channels = Controller->Channels;
+       ControllerInfo.Targets = Controller->Targets;
+       ControllerInfo.PCI_Bus = Controller->Bus;
+       ControllerInfo.PCI_Device = Controller->Device;
+       ControllerInfo.PCI_Function = Controller->Function;
+       ControllerInfo.IRQ_Channel = Controller->IRQ_Channel;
+       ControllerInfo.PCI_Address = Controller->PCI_Address;
+       strcpy(ControllerInfo.ModelName, Controller->ModelName);
+       strcpy(ControllerInfo.FirmwareVersion, Controller->FirmwareVersion);
+       ErrorCode = (copy_to_user(UserSpaceControllerInfo, &ControllerInfo,
+                            sizeof(DAC960_ControllerInfo_T)) ? -EFAULT : 0);
+       break;
+      }
+    case DAC960_IOCTL_V1_EXECUTE_COMMAND:
+      {
+       DAC960_V1_UserCommand_T __user *UserSpaceUserCommand =
+         (DAC960_V1_UserCommand_T __user *) Argument;
+       DAC960_V1_UserCommand_T UserCommand;
+       DAC960_Controller_T *Controller;
+       DAC960_Command_T *Command = NULL;
+       DAC960_V1_CommandOpcode_T CommandOpcode;
+       DAC960_V1_CommandStatus_T CommandStatus;
+       DAC960_V1_DCDB_T DCDB;
+       DAC960_V1_DCDB_T *DCDB_IOBUF = NULL;
+       dma_addr_t      DCDB_IOBUFDMA;
+       unsigned long flags;
+       int ControllerNumber, DataTransferLength;
+       unsigned char *DataTransferBuffer = NULL;
+       dma_addr_t DataTransferBufferDMA;
+       if (UserSpaceUserCommand == NULL) {
+               ErrorCode = -EINVAL;
+               break;
+       }
+       if (copy_from_user(&UserCommand, UserSpaceUserCommand,
+                                  sizeof(DAC960_V1_UserCommand_T))) {
+               ErrorCode = -EFAULT;
+               break;
+       }
+       ControllerNumber = UserCommand.ControllerNumber;
+       ErrorCode = -ENXIO;
+       if (ControllerNumber < 0 ||
+           ControllerNumber > DAC960_ControllerCount - 1)
+               break;
+       Controller = DAC960_Controllers[ControllerNumber];
+       if (Controller == NULL)
+               break;
+       ErrorCode = -EINVAL;
+       if (Controller->FirmwareType != DAC960_V1_Controller)
+               break;
+       CommandOpcode = UserCommand.CommandMailbox.Common.CommandOpcode;
+       DataTransferLength = UserCommand.DataTransferLength;
+       if (CommandOpcode & 0x80)
+               break;
+       if (CommandOpcode == DAC960_V1_DCDB)
+         {
+           if (copy_from_user(&DCDB, UserCommand.DCDB,
+                              sizeof(DAC960_V1_DCDB_T))) {
+               ErrorCode = -EFAULT;
+               break;
+           }
+           if (DCDB.Channel >= DAC960_V1_MaxChannels)
+                       break;
+           if (!((DataTransferLength == 0 &&
+                  DCDB.Direction
+                  == DAC960_V1_DCDB_NoDataTransfer) ||
+                 (DataTransferLength > 0 &&
+                  DCDB.Direction
+                  == DAC960_V1_DCDB_DataTransferDeviceToSystem) ||
+                 (DataTransferLength < 0 &&
+                  DCDB.Direction
+                  == DAC960_V1_DCDB_DataTransferSystemToDevice)))
+                       break;
+           if (((DCDB.TransferLengthHigh4 << 16) | DCDB.TransferLength)
+               != abs(DataTransferLength))
+                       break;
+           DCDB_IOBUF = pci_alloc_consistent(Controller->PCIDevice,
+                       sizeof(DAC960_V1_DCDB_T), &DCDB_IOBUFDMA);
+           if (DCDB_IOBUF == NULL) {
+                       ErrorCode = -ENOMEM;
+                       break;
+               }
+         }
+       ErrorCode = -ENOMEM;
+       if (DataTransferLength > 0)
+         {
+           DataTransferBuffer = pci_zalloc_consistent(Controller->PCIDevice,
+                                                       DataTransferLength,
+                                                       &DataTransferBufferDMA);
+           if (DataTransferBuffer == NULL)
+               break;
+         }
+       else if (DataTransferLength < 0)
+         {
+           DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
+                               -DataTransferLength, &DataTransferBufferDMA);
+           if (DataTransferBuffer == NULL)
+               break;
+           if (copy_from_user(DataTransferBuffer,
+                              UserCommand.DataTransferBuffer,
+                              -DataTransferLength)) {
+               ErrorCode = -EFAULT;
+               break;
+           }
+         }
+       if (CommandOpcode == DAC960_V1_DCDB)
+         {
+           spin_lock_irqsave(&Controller->queue_lock, flags);
+           while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
+             DAC960_WaitForCommand(Controller);
+           while (Controller->V1.DirectCommandActive[DCDB.Channel]
+                                                    [DCDB.TargetID])
+             {
+               spin_unlock_irq(&Controller->queue_lock);
+               __wait_event(Controller->CommandWaitQueue,
+                            !Controller->V1.DirectCommandActive
+                                            [DCDB.Channel][DCDB.TargetID]);
+               spin_lock_irq(&Controller->queue_lock);
+             }
+           Controller->V1.DirectCommandActive[DCDB.Channel]
+                                             [DCDB.TargetID] = true;
+           spin_unlock_irqrestore(&Controller->queue_lock, flags);
+           DAC960_V1_ClearCommand(Command);
+           Command->CommandType = DAC960_ImmediateCommand;
+           memcpy(&Command->V1.CommandMailbox, &UserCommand.CommandMailbox,
+                  sizeof(DAC960_V1_CommandMailbox_T));
+           Command->V1.CommandMailbox.Type3.BusAddress = DCDB_IOBUFDMA;
+           DCDB.BusAddress = DataTransferBufferDMA;
+           memcpy(DCDB_IOBUF, &DCDB, sizeof(DAC960_V1_DCDB_T));
+         }
+       else
+         {
+           spin_lock_irqsave(&Controller->queue_lock, flags);
+           while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
+             DAC960_WaitForCommand(Controller);
+           spin_unlock_irqrestore(&Controller->queue_lock, flags);
+           DAC960_V1_ClearCommand(Command);
+           Command->CommandType = DAC960_ImmediateCommand;
+           memcpy(&Command->V1.CommandMailbox, &UserCommand.CommandMailbox,
+                  sizeof(DAC960_V1_CommandMailbox_T));
+           if (DataTransferBuffer != NULL)
+             Command->V1.CommandMailbox.Type3.BusAddress =
+               DataTransferBufferDMA;
+         }
+       DAC960_ExecuteCommand(Command);
+       CommandStatus = Command->V1.CommandStatus;
+       spin_lock_irqsave(&Controller->queue_lock, flags);
+       DAC960_DeallocateCommand(Command);
+       spin_unlock_irqrestore(&Controller->queue_lock, flags);
+       if (DataTransferLength > 0)
+         {
+           if (copy_to_user(UserCommand.DataTransferBuffer,
+                            DataTransferBuffer, DataTransferLength)) {
+               ErrorCode = -EFAULT;
+               goto Failure1;
+            }
+         }
+       if (CommandOpcode == DAC960_V1_DCDB)
+         {
+           /*
+             I don't believe Target or Channel in the DCDB_IOBUF
+             should be any different from the contents of DCDB.
+            */
+           Controller->V1.DirectCommandActive[DCDB.Channel]
+                                             [DCDB.TargetID] = false;
+           if (copy_to_user(UserCommand.DCDB, DCDB_IOBUF,
+                            sizeof(DAC960_V1_DCDB_T))) {
+               ErrorCode = -EFAULT;
+               goto Failure1;
+           }
+         }
+       ErrorCode = CommandStatus;
+      Failure1:
+       if (DataTransferBuffer != NULL)
+         pci_free_consistent(Controller->PCIDevice, abs(DataTransferLength),
+                       DataTransferBuffer, DataTransferBufferDMA);
+       if (DCDB_IOBUF != NULL)
+         pci_free_consistent(Controller->PCIDevice, sizeof(DAC960_V1_DCDB_T),
+                       DCDB_IOBUF, DCDB_IOBUFDMA);
+       break;
+      }
+    case DAC960_IOCTL_V2_EXECUTE_COMMAND:
+      {
+       DAC960_V2_UserCommand_T __user *UserSpaceUserCommand =
+         (DAC960_V2_UserCommand_T __user *) Argument;
+       DAC960_V2_UserCommand_T UserCommand;
+       DAC960_Controller_T *Controller;
+       DAC960_Command_T *Command = NULL;
+       DAC960_V2_CommandMailbox_T *CommandMailbox;
+       DAC960_V2_CommandStatus_T CommandStatus;
+       unsigned long flags;
+       int ControllerNumber, DataTransferLength;
+       int DataTransferResidue, RequestSenseLength;
+       unsigned char *DataTransferBuffer = NULL;
+       dma_addr_t DataTransferBufferDMA;
+       unsigned char *RequestSenseBuffer = NULL;
+       dma_addr_t RequestSenseBufferDMA;
+
+       ErrorCode = -EINVAL;
+       if (UserSpaceUserCommand == NULL)
+               break;
+       if (copy_from_user(&UserCommand, UserSpaceUserCommand,
+                          sizeof(DAC960_V2_UserCommand_T))) {
+               ErrorCode = -EFAULT;
+               break;
+       }
+       ErrorCode = -ENXIO;
+       ControllerNumber = UserCommand.ControllerNumber;
+       if (ControllerNumber < 0 ||
+           ControllerNumber > DAC960_ControllerCount - 1)
+               break;
+       Controller = DAC960_Controllers[ControllerNumber];
+       if (Controller == NULL)
+               break;
+       if (Controller->FirmwareType != DAC960_V2_Controller){
+               ErrorCode = -EINVAL;
+               break;
+       }
+       DataTransferLength = UserCommand.DataTransferLength;
+       ErrorCode = -ENOMEM;
+       if (DataTransferLength > 0)
+         {
+           DataTransferBuffer = pci_zalloc_consistent(Controller->PCIDevice,
+                                                       DataTransferLength,
+                                                       &DataTransferBufferDMA);
+           if (DataTransferBuffer == NULL)
+               break;
+         }
+       else if (DataTransferLength < 0)
+         {
+           DataTransferBuffer = pci_alloc_consistent(Controller->PCIDevice,
+                               -DataTransferLength, &DataTransferBufferDMA);
+           if (DataTransferBuffer == NULL)
+               break;
+           if (copy_from_user(DataTransferBuffer,
+                              UserCommand.DataTransferBuffer,
+                              -DataTransferLength)) {
+               ErrorCode = -EFAULT;
+               goto Failure2;
+           }
+         }
+       RequestSenseLength = UserCommand.RequestSenseLength;
+       if (RequestSenseLength > 0)
+         {
+           RequestSenseBuffer = pci_zalloc_consistent(Controller->PCIDevice,
+                                                       RequestSenseLength,
+                                                       &RequestSenseBufferDMA);
+           if (RequestSenseBuffer == NULL)
+             {
+               ErrorCode = -ENOMEM;
+               goto Failure2;
+             }
+         }
+       spin_lock_irqsave(&Controller->queue_lock, flags);
+       while ((Command = DAC960_AllocateCommand(Controller)) == NULL)
+         DAC960_WaitForCommand(Controller);
+       spin_unlock_irqrestore(&Controller->queue_lock, flags);
+       DAC960_V2_ClearCommand(Command);
+       Command->CommandType = DAC960_ImmediateCommand;
+       CommandMailbox = &Command->V2.CommandMailbox;
+       memcpy(CommandMailbox, &UserCommand.CommandMailbox,
+              sizeof(DAC960_V2_CommandMailbox_T));
+       CommandMailbox->Common.CommandControlBits
+                             .AdditionalScatterGatherListMemory = false;
+       CommandMailbox->Common.CommandControlBits
+                             .NoAutoRequestSense = true;
+       CommandMailbox->Common.DataTransferSize = 0;
+       CommandMailbox->Common.DataTransferPageNumber = 0;
+       memset(&CommandMailbox->Common.DataTransferMemoryAddress, 0,
+              sizeof(DAC960_V2_DataTransferMemoryAddress_T));
+       if (DataTransferLength != 0)
+         {
+           if (DataTransferLength > 0)
+             {
+               CommandMailbox->Common.CommandControlBits
+                                     .DataTransferControllerToHost = true;
+               CommandMailbox->Common.DataTransferSize = DataTransferLength;
+             }
+           else
+             {
+               CommandMailbox->Common.CommandControlBits
+                                     .DataTransferControllerToHost = false;
+               CommandMailbox->Common.DataTransferSize = -DataTransferLength;
+             }
+           CommandMailbox->Common.DataTransferMemoryAddress
+                                 .ScatterGatherSegments[0]
+                                 .SegmentDataPointer = DataTransferBufferDMA;
+           CommandMailbox->Common.DataTransferMemoryAddress
+                                 .ScatterGatherSegments[0]
+                                 .SegmentByteCount =
+             CommandMailbox->Common.DataTransferSize;
+         }
+       if (RequestSenseLength > 0)
+         {
+           CommandMailbox->Common.CommandControlBits
+                                 .NoAutoRequestSense = false;
+           CommandMailbox->Common.RequestSenseSize = RequestSenseLength;
+           CommandMailbox->Common.RequestSenseBusAddress =
+                                                       RequestSenseBufferDMA;
+         }
+       DAC960_ExecuteCommand(Command);
+       CommandStatus = Command->V2.CommandStatus;
+       RequestSenseLength = Command->V2.RequestSenseLength;
+       DataTransferResidue = Command->V2.DataTransferResidue;
+       spin_lock_irqsave(&Controller->queue_lock, flags);
+       DAC960_DeallocateCommand(Command);
+       spin_unlock_irqrestore(&Controller->queue_lock, flags);
+       if (RequestSenseLength > UserCommand.RequestSenseLength)
+         RequestSenseLength = UserCommand.RequestSenseLength;
+       if (copy_to_user(&UserSpaceUserCommand->DataTransferLength,
+                                &DataTransferResidue,
+                                sizeof(DataTransferResidue))) {
+               ErrorCode = -EFAULT;
+               goto Failure2;
+       }
+       if (copy_to_user(&UserSpaceUserCommand->RequestSenseLength,
+                        &RequestSenseLength, sizeof(RequestSenseLength))) {
+               ErrorCode = -EFAULT;
+               goto Failure2;
+       }
+       if (DataTransferLength > 0)
+         {
+           if (copy_to_user(UserCommand.DataTransferBuffer,
+                            DataTransferBuffer, DataTransferLength)) {
+               ErrorCode = -EFAULT;
+               goto Failure2;
+           }
+         }
+       if (RequestSenseLength > 0)
+         {
+           if (copy_to_user(UserCommand.RequestSenseBuffer,
+                            RequestSenseBuffer, RequestSenseLength)) {
+               ErrorCode = -EFAULT;
+               goto Failure2;
+           }
+         }
+       ErrorCode = CommandStatus;
+      Failure2:
+         pci_free_consistent(Controller->PCIDevice, abs(DataTransferLength),
+               DataTransferBuffer, DataTransferBufferDMA);
+       if (RequestSenseBuffer != NULL)
+         pci_free_consistent(Controller->PCIDevice, RequestSenseLength,
+               RequestSenseBuffer, RequestSenseBufferDMA);
+        break;
+      }
+    case DAC960_IOCTL_V2_GET_HEALTH_STATUS:
+      {
+       DAC960_V2_GetHealthStatus_T __user *UserSpaceGetHealthStatus =
+         (DAC960_V2_GetHealthStatus_T __user *) Argument;
+       DAC960_V2_GetHealthStatus_T GetHealthStatus;
+       DAC960_V2_HealthStatusBuffer_T HealthStatusBuffer;
+       DAC960_Controller_T *Controller;
+       int ControllerNumber;
+       if (UserSpaceGetHealthStatus == NULL) {
+               ErrorCode = -EINVAL;
+               break;
+       }
+       if (copy_from_user(&GetHealthStatus, UserSpaceGetHealthStatus,
+                          sizeof(DAC960_V2_GetHealthStatus_T))) {
+               ErrorCode = -EFAULT;
+               break;
+       }
+       ErrorCode = -ENXIO;
+       ControllerNumber = GetHealthStatus.ControllerNumber;
+       if (ControllerNumber < 0 ||
+           ControllerNumber > DAC960_ControllerCount - 1)
+                   break;
+       Controller = DAC960_Controllers[ControllerNumber];
+       if (Controller == NULL)
+               break;
+       if (Controller->FirmwareType != DAC960_V2_Controller) {
+               ErrorCode = -EINVAL;
+               break;
+       }
+       if (copy_from_user(&HealthStatusBuffer,
+                          GetHealthStatus.HealthStatusBuffer,
+                          sizeof(DAC960_V2_HealthStatusBuffer_T))) {
+               ErrorCode = -EFAULT;
+               break;
+       }
+       ErrorCode = wait_event_interruptible_timeout(Controller->HealthStatusWaitQueue,
+                       !(Controller->V2.HealthStatusBuffer->StatusChangeCounter
+                           == HealthStatusBuffer.StatusChangeCounter &&
+                         Controller->V2.HealthStatusBuffer->NextEventSequenceNumber
+                           == HealthStatusBuffer.NextEventSequenceNumber),
+                       DAC960_MonitoringTimerInterval);
+       if (ErrorCode == -ERESTARTSYS) {
+               ErrorCode = -EINTR;
+               break;
+       }
+       if (copy_to_user(GetHealthStatus.HealthStatusBuffer,
+                        Controller->V2.HealthStatusBuffer,
+                        sizeof(DAC960_V2_HealthStatusBuffer_T)))
+               ErrorCode = -EFAULT;
+       else
+               ErrorCode =  0;
+      }
+      break;
+      default:
+       ErrorCode = -ENOTTY;
+    }
+  mutex_unlock(&DAC960_mutex);
+  return ErrorCode;
+}
+
+static const struct file_operations DAC960_gam_fops = {
+       .owner          = THIS_MODULE,
+       .unlocked_ioctl = DAC960_gam_ioctl,
+       .llseek         = noop_llseek,
+};
+
+static struct miscdevice DAC960_gam_dev = {
+       DAC960_GAM_MINOR,
+       "dac960_gam",
+       &DAC960_gam_fops
+};
+
+static int DAC960_gam_init(void)
+{
+       int ret;
+
+       ret = misc_register(&DAC960_gam_dev);
+       if (ret)
+               printk(KERN_ERR "DAC960_gam: can't misc_register on minor %d\n", DAC960_GAM_MINOR);
+       return ret;
+}
+
+static void DAC960_gam_cleanup(void)
+{
+       misc_deregister(&DAC960_gam_dev);
+}
+
+#endif /* DAC960_GAM_MINOR */
+
+static struct DAC960_privdata DAC960_GEM_privdata = {
+       .HardwareType =         DAC960_GEM_Controller,
+       .FirmwareType   =       DAC960_V2_Controller,
+       .InterruptHandler =     DAC960_GEM_InterruptHandler,
+       .MemoryWindowSize =     DAC960_GEM_RegisterWindowSize,
+};
+
+
+static struct DAC960_privdata DAC960_BA_privdata = {
+       .HardwareType =         DAC960_BA_Controller,
+       .FirmwareType   =       DAC960_V2_Controller,
+       .InterruptHandler =     DAC960_BA_InterruptHandler,
+       .MemoryWindowSize =     DAC960_BA_RegisterWindowSize,
+};
+
+static struct DAC960_privdata DAC960_LP_privdata = {
+       .HardwareType =         DAC960_LP_Controller,
+       .FirmwareType   =       DAC960_V2_Controller,
+       .InterruptHandler =     DAC960_LP_InterruptHandler,
+       .MemoryWindowSize =     DAC960_LP_RegisterWindowSize,
+};
+
+static struct DAC960_privdata DAC960_LA_privdata = {
+       .HardwareType =         DAC960_LA_Controller,
+       .FirmwareType   =       DAC960_V1_Controller,
+       .InterruptHandler =     DAC960_LA_InterruptHandler,
+       .MemoryWindowSize =     DAC960_LA_RegisterWindowSize,
+};
+
+static struct DAC960_privdata DAC960_PG_privdata = {
+       .HardwareType =         DAC960_PG_Controller,
+       .FirmwareType   =       DAC960_V1_Controller,
+       .InterruptHandler =     DAC960_PG_InterruptHandler,
+       .MemoryWindowSize =     DAC960_PG_RegisterWindowSize,
+};
+
+static struct DAC960_privdata DAC960_PD_privdata = {
+       .HardwareType =         DAC960_PD_Controller,
+       .FirmwareType   =       DAC960_V1_Controller,
+       .InterruptHandler =     DAC960_PD_InterruptHandler,
+       .MemoryWindowSize =     DAC960_PD_RegisterWindowSize,
+};
+
+static struct DAC960_privdata DAC960_P_privdata = {
+       .HardwareType =         DAC960_P_Controller,
+       .FirmwareType   =       DAC960_V1_Controller,
+       .InterruptHandler =     DAC960_P_InterruptHandler,
+       .MemoryWindowSize =     DAC960_PD_RegisterWindowSize,
+};
+
+static const struct pci_device_id DAC960_id_table[] = {
+       {
+               .vendor         = PCI_VENDOR_ID_MYLEX,
+               .device         = PCI_DEVICE_ID_MYLEX_DAC960_GEM,
+               .subvendor      = PCI_VENDOR_ID_MYLEX,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (unsigned long) &DAC960_GEM_privdata,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_MYLEX,
+               .device         = PCI_DEVICE_ID_MYLEX_DAC960_BA,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (unsigned long) &DAC960_BA_privdata,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_MYLEX,
+               .device         = PCI_DEVICE_ID_MYLEX_DAC960_LP,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (unsigned long) &DAC960_LP_privdata,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_DEC,
+               .device         = PCI_DEVICE_ID_DEC_21285,
+               .subvendor      = PCI_VENDOR_ID_MYLEX,
+               .subdevice      = PCI_DEVICE_ID_MYLEX_DAC960_LA,
+               .driver_data    = (unsigned long) &DAC960_LA_privdata,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_MYLEX,
+               .device         = PCI_DEVICE_ID_MYLEX_DAC960_PG,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (unsigned long) &DAC960_PG_privdata,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_MYLEX,
+               .device         = PCI_DEVICE_ID_MYLEX_DAC960_PD,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (unsigned long) &DAC960_PD_privdata,
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_MYLEX,
+               .device         = PCI_DEVICE_ID_MYLEX_DAC960_P,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = (unsigned long) &DAC960_P_privdata,
+       },
+       {0, },
+};
+
+MODULE_DEVICE_TABLE(pci, DAC960_id_table);
+
+static struct pci_driver DAC960_pci_driver = {
+       .name           = "DAC960",
+       .id_table       = DAC960_id_table,
+       .probe          = DAC960_Probe,
+       .remove         = DAC960_Remove,
+};
+
+static int __init DAC960_init_module(void)
+{
+       int ret;
+
+       ret =  pci_register_driver(&DAC960_pci_driver);
+#ifdef DAC960_GAM_MINOR
+       if (!ret)
+               DAC960_gam_init();
+#endif
+       return ret;
+}
+
+static void __exit DAC960_cleanup_module(void)
+{
+       int i;
+
+#ifdef DAC960_GAM_MINOR
+       DAC960_gam_cleanup();
+#endif
+
+       for (i = 0; i < DAC960_ControllerCount; i++) {
+               DAC960_Controller_T *Controller = DAC960_Controllers[i];
+               if (Controller == NULL)
+                       continue;
+               DAC960_FinalizeController(Controller);
+       }
+       if (DAC960_ProcDirectoryEntry != NULL) {
+               remove_proc_entry("rd/status", NULL);
+               remove_proc_entry("rd", NULL);
+       }
+       DAC960_ControllerCount = 0;
+       pci_unregister_driver(&DAC960_pci_driver);
+}
+
+module_init(DAC960_init_module);
+module_exit(DAC960_cleanup_module);
+
+MODULE_LICENSE("GPL");