patch-2.2.11 linux/drivers/block/DAC960.c

Next file: linux/drivers/block/DAC960.h
Previous file: linux/drivers/block/Config.in
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.10/linux/drivers/block/DAC960.c linux/drivers/block/DAC960.c
@@ -0,0 +1,3320 @@
+/*
+
+  Linux Driver for Mylex DAC960 and DAC1100 PCI RAID Controllers
+
+  Copyright 1998-1999 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+  This program is free software; you may redistribute and/or modify it under
+  the terms of the GNU General Public License Version 2 as published by the
+  Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY, without even the implied warranty of MERCHANTABILITY
+  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+  for complete details.
+
+  The author respectfully requests that any modifications to this software be
+  sent directly to him for evaluation and testing.
+
+*/
+
+
+#define DAC960_DriverVersion			"2.2.2"
+#define DAC960_DriverDate			"3 July 1999"
+
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/locks.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/reboot.h>
+#include <linux/timer.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/spinlock.h>
+#include <asm/uaccess.h>
+#include "DAC960.h"
+
+
+/*
+  DAC960_ControllerCount is the number of DAC960 Controllers detected.
+*/
+
+static int
+  DAC960_ControllerCount =			0;
+
+
+/*
+  DAC960_ActiveControllerCount is the number of Active DAC960 Controllers
+  detected.
+*/
+
+static int
+  DAC960_ActiveControllerCount =		0;
+
+
+/*
+  DAC960_Controllers is an array of pointers to the DAC960 Controller
+  structures.
+*/
+
+static DAC960_Controller_T
+  *DAC960_Controllers[DAC960_MaxControllers] =	{ NULL };
+
+
+/*
+  DAC960_FileOperations is the File Operations structure for DAC960 Logical
+  Disk Devices.
+*/
+
+static FileOperations_T
+  DAC960_FileOperations =
+    { llseek:		    NULL,
+      read:		    block_read,
+      write:		    block_write,
+      readdir:		    NULL,
+      poll:		    NULL,
+      ioctl:		    DAC960_IOCTL,
+      mmap:		    NULL,
+      open:		    DAC960_Open,
+      release:		    DAC960_Release,
+      fsync:		    block_fsync,
+      fasync:		    NULL,
+      check_media_change:   NULL,
+      revalidate:	    NULL };
+
+
+/*
+  DAC960_ProcDirectoryEntry is the DAC960 /proc/rd directory entry.
+*/
+
+static PROC_DirectoryEntry_T
+  DAC960_ProcDirectoryEntry;
+
+
+/*
+  DAC960_NotifierBlock is the Notifier Block structure for DAC960 Driver.
+*/
+
+static NotifierBlock_T
+  DAC960_NotifierBlock =    { DAC960_Finalize, NULL, 0 };
+
+
+/*
+  DAC960_AnnounceDriver announces the Driver Version and Date, Author's Name,
+  Copyright Notice, and Electronic Mail Address.
+*/
+
+static void DAC960_AnnounceDriver(DAC960_Controller_T *Controller)
+{
+  DAC960_Announce("***** DAC960 RAID Driver Version "
+		  DAC960_DriverVersion " of "
+		  DAC960_DriverDate " *****\n", Controller);
+  DAC960_Announce("Copyright 1998-1999 by Leonard N. Zubkoff "
+		  "<lnz@dandelion.com>\n", Controller);
+}
+
+
+/*
+  DAC960_Failure prints a standardized error message, and then returns false.
+*/
+
+static boolean DAC960_Failure(DAC960_Controller_T *Controller,
+			      char *ErrorMessage)
+{
+  DAC960_Error("While configuring DAC960 PCI RAID Controller at\n",
+	       Controller);
+  if (Controller->IO_Address == 0)
+    DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		 "PCI Address 0x%X\n", Controller,
+		 Controller->Bus, Controller->Device,
+		 Controller->Function, Controller->PCI_Address);
+  else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+		    "0x%X PCI Address 0x%X\n", Controller,
+		    Controller->Bus, Controller->Device,
+		    Controller->Function, Controller->IO_Address,
+		    Controller->PCI_Address);
+  DAC960_Error("%s FAILED - DETACHING\n", Controller, ErrorMessage);
+  return false;
+}
+
+
+/*
+  DAC960_ClearCommand clears critical fields of Command.
+*/
+
+static inline void DAC960_ClearCommand(DAC960_Command_T *Command)
+{
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  CommandMailbox->Words[0] = 0;
+  CommandMailbox->Words[1] = 0;
+  CommandMailbox->Words[2] = 0;
+  CommandMailbox->Words[3] = 0;
+  Command->CommandStatus = 0;
+}
+
+
+/*
+  DAC960_AllocateCommand allocates a Command structure from Controller's
+  free list.
+*/
+
+static inline DAC960_Command_T *DAC960_AllocateCommand(DAC960_Controller_T
+						       *Controller)
+{
+  DAC960_Command_T *Command = Controller->FreeCommands;
+  if (Command == NULL) return NULL;
+  Controller->FreeCommands = Command->Next;
+  Command->Next = NULL;
+  return Command;
+}
+
+
+/*
+  DAC960_DeallocateCommand deallocates Command, returning it to Controller's
+  free list.
+*/
+
+static inline void DAC960_DeallocateCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  Command->Next = Controller->FreeCommands;
+  Controller->FreeCommands = Command;
+}
+
+
+/*
+  DAC960_QueueCommand queues Command.
+*/
+
+static void DAC960_QueueCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_CommandMailbox_T *NextCommandMailbox;
+  CommandMailbox->Common.CommandIdentifier = Command - Controller->Commands;
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V5_Controller:
+      NextCommandMailbox = Controller->NextCommandMailbox;
+      DAC960_V5_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+      if (Controller->PreviousCommandMailbox1->Words[0] == 0 ||
+	  Controller->PreviousCommandMailbox2->Words[0] == 0)
+	{
+	  if (Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V5_MemoryMailboxNewCommand(ControllerBaseAddress);
+	  else DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress);
+	}
+      Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1;
+      Controller->PreviousCommandMailbox1 = NextCommandMailbox;
+      if (++NextCommandMailbox > Controller->LastCommandMailbox)
+	NextCommandMailbox = Controller->FirstCommandMailbox;
+      Controller->NextCommandMailbox = NextCommandMailbox;
+      break;
+    case DAC960_V4_Controller:
+      NextCommandMailbox = Controller->NextCommandMailbox;
+      DAC960_V4_WriteCommandMailbox(NextCommandMailbox, CommandMailbox);
+      if (Controller->PreviousCommandMailbox1->Words[0] == 0 ||
+	  Controller->PreviousCommandMailbox2->Words[0] == 0)
+	{
+	  if (Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V4_MemoryMailboxNewCommand(ControllerBaseAddress);
+	  else DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress);
+	}
+      Controller->PreviousCommandMailbox2 = Controller->PreviousCommandMailbox1;
+      Controller->PreviousCommandMailbox1 = NextCommandMailbox;
+      if (++NextCommandMailbox > Controller->LastCommandMailbox)
+	NextCommandMailbox = Controller->FirstCommandMailbox;
+      Controller->NextCommandMailbox = NextCommandMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_MailboxFullP(ControllerBaseAddress))
+	udelay(1);
+      DAC960_V3_WriteCommandMailbox(ControllerBaseAddress, CommandMailbox);
+      DAC960_V3_NewCommand(ControllerBaseAddress);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ExecuteCommand executes Command and waits for completion.  It
+  returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  Semaphore_T Semaphore = MUTEX_LOCKED;
+  unsigned long ProcessorFlags;
+  Command->Semaphore = &Semaphore;
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  DAC960_QueueCommand(Command);
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+  if (!in_interrupt())
+    down(&Semaphore);
+  return Command->CommandStatus == DAC960_NormalCompletion;
+}
+
+
+/*
+  DAC960_ExecuteType3 executes a DAC960 Type 3 Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3(DAC960_Controller_T *Controller,
+				   DAC960_CommandOpcode_T CommandOpcode,
+				   void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_ExecuteType3D executes a DAC960 Type 3D Command and waits for
+  completion.  It returns true on success and false on failure.
+*/
+
+static boolean DAC960_ExecuteType3D(DAC960_Controller_T *Controller,
+				    DAC960_CommandOpcode_T CommandOpcode,
+				    unsigned char Channel,
+				    unsigned char TargetID,
+				    void *DataPointer)
+{
+  DAC960_Command_T *Command = DAC960_AllocateCommand(Controller);
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  boolean Result;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox->Type3D.CommandOpcode = CommandOpcode;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.BusAddress = Virtual_to_Bus(DataPointer);
+  Result = DAC960_ExecuteCommand(Command);
+  DAC960_DeallocateCommand(Command);
+  return Result;
+}
+
+
+/*
+  DAC960_EnableMemoryMailboxInterface enables the Memory Mailbox Interface.
+*/
+
+static boolean DAC960_EnableMemoryMailboxInterface(DAC960_Controller_T
+						   *Controller)
+{
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_CommandMailbox_T *CommandMailboxesMemory;
+  DAC960_StatusMailbox_T *StatusMailboxesMemory;
+  DAC960_CommandMailbox_T CommandMailbox;
+  DAC960_CommandStatus_T CommandStatus;
+  void *SavedMemoryMailboxesAddress = NULL;
+  short NextCommandMailboxIndex = 0;
+  short NextStatusMailboxIndex = 0;
+  int TimeoutCounter = 1000000, i;
+  if (Controller->ControllerType == DAC960_V5_Controller)
+    DAC960_V5_RestoreMemoryMailboxInfo(Controller,
+				       &SavedMemoryMailboxesAddress,
+				       &NextCommandMailboxIndex,
+				       &NextStatusMailboxIndex);
+  else DAC960_V4_RestoreMemoryMailboxInfo(Controller,
+					  &SavedMemoryMailboxesAddress,
+					  &NextCommandMailboxIndex,
+					  &NextStatusMailboxIndex);
+  if (SavedMemoryMailboxesAddress == NULL)
+    CommandMailboxesMemory =
+      (DAC960_CommandMailbox_T *) __get_free_pages(GFP_KERNEL, 1);
+  else CommandMailboxesMemory = SavedMemoryMailboxesAddress;
+  memset(CommandMailboxesMemory, 0, PAGE_SIZE << 1);
+  Controller->FirstCommandMailbox = CommandMailboxesMemory;
+  CommandMailboxesMemory += DAC960_CommandMailboxCount - 1;
+  Controller->LastCommandMailbox = CommandMailboxesMemory;
+  Controller->NextCommandMailbox =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  if (--NextCommandMailboxIndex < 0)
+    NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1;
+  Controller->PreviousCommandMailbox1 =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  if (--NextCommandMailboxIndex < 0)
+    NextCommandMailboxIndex = DAC960_CommandMailboxCount - 1;
+  Controller->PreviousCommandMailbox2 =
+    &Controller->FirstCommandMailbox[NextCommandMailboxIndex];
+  StatusMailboxesMemory =
+    (DAC960_StatusMailbox_T *) (CommandMailboxesMemory + 1);
+  Controller->FirstStatusMailbox = StatusMailboxesMemory;
+  StatusMailboxesMemory += DAC960_StatusMailboxCount - 1;
+  Controller->LastStatusMailbox = StatusMailboxesMemory;
+  Controller->NextStatusMailbox =
+    &Controller->FirstStatusMailbox[NextStatusMailboxIndex];
+  if (SavedMemoryMailboxesAddress != NULL) return true;
+  /* Enable the Memory Mailbox Interface. */
+  Controller->DualModeMemoryMailboxInterface = true;
+  CommandMailbox.TypeX.CommandOpcode = 0x2B;
+  CommandMailbox.TypeX.CommandIdentifier = 0;
+  CommandMailbox.TypeX.CommandOpcode2 = 0x14;
+  CommandMailbox.TypeX.CommandMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstCommandMailbox);
+  CommandMailbox.TypeX.StatusMailboxesBusAddress =
+    Virtual_to_Bus(Controller->FirstStatusMailbox);
+  for (i = 0; i < 2; i++)
+    switch (Controller->ControllerType)
+      {
+      case DAC960_V5_Controller:
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V5_HardwareMailboxEmptyP(ControllerBaseAddress))
+	      break;
+	    udelay(10);
+	  }
+	if (TimeoutCounter < 0) return false;
+	DAC960_V5_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+	DAC960_V5_HardwareMailboxNewCommand(ControllerBaseAddress);
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V5_HardwareMailboxStatusAvailableP(
+		  ControllerBaseAddress))
+	      break;
+	    udelay(10);
+	  }
+	if (TimeoutCounter < 0) return false;
+	CommandStatus = DAC960_V5_ReadStatusRegister(ControllerBaseAddress);
+	DAC960_V5_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+	DAC960_V5_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+	if (CommandStatus == DAC960_NormalCompletion) return true;
+	Controller->DualModeMemoryMailboxInterface = false;
+	CommandMailbox.TypeX.CommandOpcode2 = 0x10;
+	break;
+      case DAC960_V4_Controller:
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (!DAC960_V4_HardwareMailboxFullP(ControllerBaseAddress))
+	      break;
+	    udelay(10);
+	  }
+	if (TimeoutCounter < 0) return false;
+	DAC960_V4_WriteHardwareMailbox(ControllerBaseAddress, &CommandMailbox);
+	DAC960_V4_HardwareMailboxNewCommand(ControllerBaseAddress);
+	while (--TimeoutCounter >= 0)
+	  {
+	    if (DAC960_V4_HardwareMailboxStatusAvailableP(
+		  ControllerBaseAddress))
+	      break;
+	    udelay(10);
+	  }
+	if (TimeoutCounter < 0) return false;
+	CommandStatus = DAC960_V4_ReadStatusRegister(ControllerBaseAddress);
+	DAC960_V4_AcknowledgeHardwareMailboxInterrupt(ControllerBaseAddress);
+	DAC960_V4_AcknowledgeHardwareMailboxStatus(ControllerBaseAddress);
+	if (CommandStatus == DAC960_NormalCompletion) return true;
+	Controller->DualModeMemoryMailboxInterface = false;
+	CommandMailbox.TypeX.CommandOpcode2 = 0x10;
+	break;
+      default:
+	break;
+      }
+  return false;
+}
+
+
+/*
+  DAC960_DetectControllers detects DAC960 PCI RAID Controllers by interrogating
+  the PCI Configuration Space for Controller Type.
+*/
+
+static void DAC960_DetectControllers(DAC960_ControllerType_T ControllerType)
+{
+  unsigned short VendorID = 0, DeviceID = 0;
+  unsigned int MemoryWindowSize = 0;
+  PCI_Device_T *PCI_Device = NULL;
+  switch (ControllerType)
+    {
+    case DAC960_V5_Controller:
+      VendorID = PCI_VENDOR_ID_DEC;
+      DeviceID = PCI_DEVICE_ID_DEC_21285;
+      MemoryWindowSize = DAC960_V5_RegisterWindowSize;
+      break;
+    case DAC960_V4_Controller:
+      VendorID = PCI_VENDOR_ID_MYLEX;
+      DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V4;
+      MemoryWindowSize = DAC960_V4_RegisterWindowSize;
+      break;
+    case DAC960_V3_Controller:
+      VendorID = PCI_VENDOR_ID_MYLEX;
+      DeviceID = PCI_DEVICE_ID_MYLEX_DAC960P_V3;
+      MemoryWindowSize = DAC960_V3_RegisterWindowSize;
+      break;
+    }
+  while ((PCI_Device = pci_find_device(VendorID, DeviceID, PCI_Device)) != NULL)
+    {
+      DAC960_Controller_T *Controller = (DAC960_Controller_T *)
+	kmalloc(sizeof(DAC960_Controller_T), GFP_ATOMIC);
+      DAC960_IO_Address_T IO_Address = 0;
+      DAC960_PCI_Address_T PCI_Address = 0;
+      unsigned char Bus = PCI_Device->bus->number;
+      unsigned char DeviceFunction = PCI_Device->devfn;
+      unsigned char Device = DeviceFunction >> 3;
+      unsigned char Function = DeviceFunction & 0x7;
+      unsigned int IRQ_Channel = PCI_Device->irq;
+      unsigned long BaseAddress0 = PCI_Device->base_address[0];
+      unsigned long BaseAddress1 = PCI_Device->base_address[1];
+      unsigned short SubsystemVendorID, SubsystemDeviceID;
+      pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_VENDOR_ID,
+			   &SubsystemVendorID);
+      pci_read_config_word(PCI_Device, PCI_SUBSYSTEM_ID,
+			   &SubsystemDeviceID);
+      switch (ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  if (!(SubsystemVendorID == PCI_VENDOR_ID_MYLEX &&
+		SubsystemDeviceID == PCI_DEVICE_ID_MYLEX_DAC960P_V5))
+	    goto Ignore;
+	  PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	case DAC960_V4_Controller:
+	  PCI_Address = BaseAddress0 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	case DAC960_V3_Controller:
+	  IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	  PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+	  break;
+	}
+      if (DAC960_ControllerCount == DAC960_MaxControllers)
+	{
+	  DAC960_Error("More than %d DAC960 Controllers detected - "
+		       "ignoring from Controller at\n",
+		       NULL, DAC960_MaxControllers);
+	  goto Ignore;
+	}
+      if (Controller == NULL)
+	{
+	  DAC960_Error("Unable to allocate Controller structure for "
+		       "Controller at\n", NULL);
+	  goto Ignore;
+	}
+      memset(Controller, 0, sizeof(DAC960_Controller_T));
+      Controller->ControllerNumber = DAC960_ControllerCount;
+      DAC960_Controllers[DAC960_ControllerCount++] = Controller;
+      DAC960_AnnounceDriver(Controller);
+      Controller->ControllerType = ControllerType;
+      Controller->IO_Address = IO_Address;
+      Controller->PCI_Address = PCI_Address;
+      Controller->Bus = Bus;
+      Controller->Device = Device;
+      Controller->Function = Function;
+      sprintf(Controller->ControllerName, "c%d", Controller->ControllerNumber);
+      /*
+	Acquire shared access to the IRQ Channel.
+      */
+      if (IRQ_Channel == 0)
+	{
+	  DAC960_Error("IRQ Channel %d illegal for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      strcpy(Controller->FullModelName, "DAC960");
+      if (request_irq(IRQ_Channel, DAC960_InterruptHandler,
+		      SA_SHIRQ, Controller->FullModelName, Controller) < 0)
+	{
+	  DAC960_Error("Unable to acquire IRQ Channel %d for Controller at\n",
+		       Controller, IRQ_Channel);
+	  goto Failure;
+	}
+      Controller->IRQ_Channel = IRQ_Channel;
+      /*
+	Map the Controller Register Window.
+      */
+      if (MemoryWindowSize < PAGE_SIZE)
+	MemoryWindowSize = PAGE_SIZE;
+      Controller->MemoryMappedAddress =
+	ioremap_nocache(PCI_Address & PAGE_MASK, MemoryWindowSize);
+      Controller->BaseAddress =
+	Controller->MemoryMappedAddress + (PCI_Address & ~PAGE_MASK);
+      if (Controller->MemoryMappedAddress == NULL)
+	{
+	  DAC960_Error("Unable to map Controller Register Window for "
+		       "Controller at\n", Controller);
+	  goto Failure;
+	}
+      switch (ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  DAC960_V5_DisableInterrupts(Controller->BaseAddress);
+	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
+	    {
+	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
+			   "for Controller at\n", Controller);
+	      goto Failure;
+	    }
+	  DAC960_V5_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	case DAC960_V4_Controller:
+	  DAC960_V4_DisableInterrupts(Controller->BaseAddress);
+	  if (!DAC960_EnableMemoryMailboxInterface(Controller))
+	    {
+	      DAC960_Error("Unable to Enable Memory Mailbox Interface "
+			   "for Controller at\n", Controller);
+	      goto Failure;
+	    }
+	  DAC960_V4_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	case DAC960_V3_Controller:
+	  request_region(Controller->IO_Address, 0x80,
+			 Controller->FullModelName);
+	  DAC960_V3_EnableInterrupts(Controller->BaseAddress);
+	  break;
+	}
+      DAC960_ActiveControllerCount++;
+      Controller->Commands[0].Controller = Controller;
+      Controller->Commands[0].Next = NULL;
+      Controller->FreeCommands = &Controller->Commands[0];
+      continue;
+    Failure:
+      if (IO_Address == 0)
+	DAC960_Error("PCI Bus %d Device %d Function %d I/O Address N/A "
+		     "PCI Address 0x%X\n", Controller,
+		     Bus, Device, Function, PCI_Address);
+      else DAC960_Error("PCI Bus %d Device %d Function %d I/O Address "
+			"0x%X PCI Address 0x%X\n", Controller,
+			Bus, Device, Function, IO_Address, PCI_Address);
+      if (Controller == NULL) break;
+      if (Controller->IRQ_Channel > 0)
+	free_irq(IRQ_Channel, Controller);
+      if (Controller->MemoryMappedAddress != NULL)
+	iounmap(Controller->MemoryMappedAddress);
+      DAC960_Controllers[Controller->ControllerNumber] = NULL;
+    Ignore:
+      kfree(Controller);
+    }
+}
+
+
+/*
+  DAC960_ReadControllerConfiguration reads the Configuration Information
+  from Controller and initializes the Controller structure.
+*/
+
+static boolean DAC960_ReadControllerConfiguration(DAC960_Controller_T
+						  *Controller)
+{
+  DAC960_Enquiry2_T Enquiry2;
+  DAC960_Config2_T Config2;
+  int LogicalDriveNumber, Channel, TargetID;
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry,
+			   &Controller->Enquiry[0]))
+    return DAC960_Failure(Controller, "ENQUIRY");
+  if (!DAC960_ExecuteType3(Controller, DAC960_Enquiry2, &Enquiry2))
+    return DAC960_Failure(Controller, "ENQUIRY2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_ReadConfig2, &Config2))
+    return DAC960_Failure(Controller, "READ CONFIG2");
+  if (!DAC960_ExecuteType3(Controller, DAC960_GetLogicalDriveInformation,
+			   &Controller->LogicalDriveInformation[0]))
+    return DAC960_Failure(Controller, "GET LOGICAL DRIVE INFORMATION");
+  for (Channel = 0; Channel < Enquiry2.ActualChannels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      if (!DAC960_ExecuteType3D(Controller, DAC960_GetDeviceState,
+				Channel, TargetID,
+				&Controller->DeviceState[0][Channel][TargetID]))
+	return DAC960_Failure(Controller, "GET DEVICE STATE");
+  /*
+    Initialize the Controller Model Name and Full Model Name fields.
+  */
+  switch (Enquiry2.HardwareID.SubModel)
+    {
+    case DAC960_P_PD_PU:
+      if (Enquiry2.SCSICapability.BusSpeed == DAC960_Ultra)
+	strcpy(Controller->ModelName, "DAC960PU");
+      else strcpy(Controller->ModelName, "DAC960PD");
+      break;
+    case DAC960_PL:
+      strcpy(Controller->ModelName, "DAC960PL");
+      break;
+    case DAC960_PG:
+      strcpy(Controller->ModelName, "DAC960PG");
+      break;
+    case DAC960_PJ:
+      strcpy(Controller->ModelName, "DAC960PJ");
+      break;
+    case DAC960_PR:
+      strcpy(Controller->ModelName, "DAC960PR");
+      break;
+    case DAC960_PT:
+      strcpy(Controller->ModelName, "DAC960PT");
+      break;
+    case DAC960_PTL0:
+      strcpy(Controller->ModelName, "DAC960PTL0");
+      break;
+    case DAC960_PRL:
+      strcpy(Controller->ModelName, "DAC960PRL");
+      break;
+    case DAC960_PTL1:
+      strcpy(Controller->ModelName, "DAC960PTL1");
+      break;
+    case DAC1164_P:
+      strcpy(Controller->ModelName, "DAC1164P");
+      break;
+    default:
+      return DAC960_Failure(Controller, "MODEL VERIFICATION");
+    }
+  strcpy(Controller->FullModelName, "Mylex ");
+  strcat(Controller->FullModelName, Controller->ModelName);
+  /*
+    Initialize the Controller Firmware Version field and verify that it
+    is a supported firmware version.  The supported firmware versions are:
+
+    DAC1164P		    5.06 and above
+    DAC960PTL/PRL/PJ/PG	    4.06 and above
+    DAC960PU/PD/PL	    3.51 and above
+  */
+  sprintf(Controller->FirmwareVersion, "%d.%02d-%c-%02d",
+	  Enquiry2.FirmwareID.MajorVersion, Enquiry2.FirmwareID.MinorVersion,
+	  Enquiry2.FirmwareID.FirmwareType, Enquiry2.FirmwareID.TurnID);
+  if (!((Controller->FirmwareVersion[0] == '5' &&
+	 strcmp(Controller->FirmwareVersion, "5.06") >= 0) ||
+	(Controller->FirmwareVersion[0] == '4' &&
+	 strcmp(Controller->FirmwareVersion, "4.06") >= 0) ||
+	(Controller->FirmwareVersion[0] == '3' &&
+	 strcmp(Controller->FirmwareVersion, "3.51") >= 0)))
+    {
+      DAC960_Failure(Controller, "FIRMWARE VERSION VERIFICATION");
+      DAC960_Error("Firmware Version = '%s'\n", Controller,
+		   Controller->FirmwareVersion);
+      return false;
+    }
+  /*
+    Initialize the Controller Channels, Memory Size, and SAF-TE Enclosure
+    Management Enabled fields.
+  */
+  Controller->Channels = Enquiry2.ActualChannels;
+  Controller->MemorySize = Enquiry2.MemorySize >> 20;
+  Controller->SAFTE_EnclosureManagementEnabled =
+    Enquiry2.FaultManagementType == DAC960_SAFTE;
+  /*
+    Initialize the Controller Queue Depth, Driver Queue Depth, Logical Drive
+    Count, Maximum Blocks per Command, and Maximum Scatter/Gather Segments.
+    The Driver Queue Depth must be at most one less than the Controller Queue
+    Depth to allow for an automatic drive rebuild operation.
+  */
+  Controller->ControllerQueueDepth = Controller->Enquiry[0].MaxCommands;
+  Controller->DriverQueueDepth = Controller->ControllerQueueDepth - 1;
+  Controller->LogicalDriveCount = Controller->Enquiry[0].NumberOfLogicalDrives;
+  Controller->MaxBlocksPerCommand = Enquiry2.MaxBlocksPerCommand;
+  Controller->MaxScatterGatherSegments = Enquiry2.MaxScatterGatherEntries;
+  /*
+    Initialize the Stripe Size, Segment Size, and Geometry Translation.
+  */
+  Controller->StripeSize = Config2.BlocksPerStripe * Config2.BlockFactor
+			   >> (10 - DAC960_BlockSizeBits);
+  Controller->SegmentSize = Config2.BlocksPerCacheLine * Config2.BlockFactor
+			    >> (10 - DAC960_BlockSizeBits);
+  switch (Config2.DriveGeometry)
+    {
+    case DAC960_Geometry_128_32:
+      Controller->GeometryTranslationHeads = 128;
+      Controller->GeometryTranslationSectors = 32;
+      break;
+    case DAC960_Geometry_255_63:
+      Controller->GeometryTranslationHeads = 255;
+      Controller->GeometryTranslationSectors = 63;
+      break;
+    default:
+      return DAC960_Failure(Controller, "CONFIG2 DRIVE GEOMETRY");
+    }
+  /*
+    Initialize the Logical Drive Initial State.
+  */
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    Controller->LogicalDriveInitialState[LogicalDriveNumber] =
+      Controller->LogicalDriveInformation[0]
+		  [LogicalDriveNumber].LogicalDriveState;
+  Controller->LastRebuildStatus = DAC960_NoRebuildOrCheckInProgress;
+  return true;
+}
+
+
+/*
+  DAC960_ReportControllerConfiguration reports the configuration of
+  Controller.
+*/
+
+static boolean DAC960_ReportControllerConfiguration(DAC960_Controller_T
+						    *Controller)
+{
+  int LogicalDriveNumber, Channel, TargetID;
+  DAC960_Info("Configuring Mylex %s PCI RAID Controller\n",
+	      Controller, Controller->ModelName);
+  DAC960_Info("  Firmware Version: %s, Channels: %d, Memory Size: %dMB\n",
+	      Controller, Controller->FirmwareVersion,
+	      Controller->Channels, Controller->MemorySize);
+  DAC960_Info("  PCI Bus: %d, Device: %d, Function: %d, I/O Address: ",
+	      Controller, Controller->Bus,
+	      Controller->Device, Controller->Function);
+  if (Controller->IO_Address == 0)
+    DAC960_Info("Unassigned\n", Controller);
+  else DAC960_Info("0x%X\n", Controller, Controller->IO_Address);
+  DAC960_Info("  PCI Address: 0x%X mapped at 0x%lX, IRQ Channel: %d\n",
+	      Controller, Controller->PCI_Address,
+	      (unsigned long) Controller->BaseAddress,
+	      Controller->IRQ_Channel);
+  DAC960_Info("  Controller Queue Depth: %d, "
+	      "Maximum Blocks per Command: %d\n",
+	      Controller, Controller->ControllerQueueDepth,
+	      Controller->MaxBlocksPerCommand);
+  DAC960_Info("  Driver Queue Depth: %d, "
+	      "Maximum Scatter/Gather Segments: %d\n",
+	      Controller, Controller->DriverQueueDepth,
+	      Controller->MaxScatterGatherSegments);
+  DAC960_Info("  Stripe Size: %dKB, Segment Size: %dKB, "
+	      "BIOS Geometry: %d/%d\n", Controller,
+	      Controller->StripeSize,
+	      Controller->SegmentSize,
+	      Controller->GeometryTranslationHeads,
+	      Controller->GeometryTranslationSectors);
+  if (Controller->SAFTE_EnclosureManagementEnabled)
+    DAC960_Info("  SAF-TE Enclosure Management Enabled\n", Controller);
+  DAC960_Info("  Physical Devices:\n", Controller);
+  for (Channel = 0; Channel < Controller->Channels; Channel++)
+    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+      {
+	DAC960_DeviceState_T *DeviceState =
+	  &Controller->DeviceState[Controller->DeviceStateIndex]
+				  [Channel][TargetID];
+	if (!DeviceState->Present) continue;
+	switch (DeviceState->DeviceType)
+	  {
+	  case DAC960_OtherType:
+	    DAC960_Info("    %d:%d - Other\n", Controller, Channel, TargetID);
+	    break;
+	  case DAC960_DiskType:
+	    DAC960_Info("    %d:%d - Disk: %s, %d blocks\n", Controller,
+			Channel, TargetID,
+			(DeviceState->DeviceState == DAC960_Device_Dead
+			 ? "Dead"
+			 : DeviceState->DeviceState == DAC960_Device_WriteOnly
+			   ? "Write-Only"
+			   : DeviceState->DeviceState == DAC960_Device_Online
+			     ? "Online" : "Standby"),
+			DeviceState->DiskSize);
+	    break;
+	  case DAC960_SequentialType:
+	    DAC960_Info("    %d:%d - Sequential\n", Controller,
+			Channel, TargetID);
+	    break;
+	  case DAC960_CDROM_or_WORM_Type:
+	    DAC960_Info("    %d:%d - CD-ROM or WORM\n", Controller,
+			Channel, TargetID);
+	    break;
+	  }
+
+      }
+  DAC960_Info("  Logical Drives:\n", Controller);
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    {
+      DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+	&Controller->LogicalDriveInformation
+	   [Controller->LogicalDriveInformationIndex][LogicalDriveNumber];
+      DAC960_Info("    /dev/rd/c%dd%d: RAID-%d, %s, %d blocks, %s\n",
+		  Controller, Controller->ControllerNumber, LogicalDriveNumber,
+		  LogicalDriveInformation->RAIDLevel,
+		  (LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Online
+		   ? "Online"
+		   : LogicalDriveInformation->LogicalDriveState ==
+		     DAC960_LogicalDrive_Critical
+		     ? "Critical" : "Offline"),
+		  LogicalDriveInformation->LogicalDriveSize,
+		  (LogicalDriveInformation->WriteBack
+		   ? "Write Back" : "Write Thru"));
+    }
+  return true;
+}
+
+
+/*
+  DAC960_RegisterBlockDevice registers the Block Device structures
+  associated with Controller.
+*/
+
+static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  static void (*RequestFunctions[DAC960_MaxControllers])(void) =
+    { DAC960_RequestFunction0, DAC960_RequestFunction1,
+      DAC960_RequestFunction2, DAC960_RequestFunction3,
+      DAC960_RequestFunction4, DAC960_RequestFunction5,
+      DAC960_RequestFunction6, DAC960_RequestFunction7 };
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  GenericDiskInfo_T *GenericDiskInfo;
+  int MinorNumber;
+  /*
+    Register the Block Device Major Number for this DAC960 Controller.
+  */
+  if (register_blkdev(MajorNumber, "rd", &DAC960_FileOperations) < 0)
+    {
+      DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n",
+		   Controller, MajorNumber);
+      return false;
+    }
+  /*
+    Initialize the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn =
+    RequestFunctions[Controller->ControllerNumber];
+  /*
+    Initialize the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++)
+    {
+      Controller->BlockSizes[MinorNumber] = BLOCK_SIZE;
+      Controller->MaxSectorsPerRequest[MinorNumber] =
+	Controller->MaxBlocksPerCommand;
+      Controller->MaxSegmentsPerRequest[MinorNumber] =
+	Controller->MaxScatterGatherSegments;
+    }
+  Controller->GenericDiskInfo.part = Controller->DiskPartitions;
+  Controller->GenericDiskInfo.sizes = Controller->PartitionSizes;
+  blksize_size[MajorNumber] = Controller->BlockSizes;
+  max_sectors[MajorNumber] = Controller->MaxSectorsPerRequest;
+  max_segments[MajorNumber] = Controller->MaxSegmentsPerRequest;
+  /*
+    Initialize Read Ahead to 128 sectors.
+  */
+  read_ahead[MajorNumber] = 128;
+  /*
+    Complete initialization of the Generic Disk Information structure.
+  */
+  Controller->GenericDiskInfo.major = MajorNumber;
+  Controller->GenericDiskInfo.major_name = "rd";
+  Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits;
+  Controller->GenericDiskInfo.max_p = DAC960_MaxPartitions;
+  Controller->GenericDiskInfo.max_nr = DAC960_MaxLogicalDrives;
+  Controller->GenericDiskInfo.init = DAC960_InitializeGenericDiskInfo;
+  Controller->GenericDiskInfo.nr_real = Controller->LogicalDriveCount;
+  Controller->GenericDiskInfo.real_devices = Controller;
+  Controller->GenericDiskInfo.next = NULL;
+  /*
+    Install the Generic Disk Information structure at the end of the list.
+  */
+  if ((GenericDiskInfo = gendisk_head) != NULL)
+    {
+      while (GenericDiskInfo->next != NULL)
+	GenericDiskInfo = GenericDiskInfo->next;
+      GenericDiskInfo->next = &Controller->GenericDiskInfo;
+    }
+  else gendisk_head = &Controller->GenericDiskInfo;
+  /*
+    Indicate the Block Device Registration completed successfully,
+  */
+  return true;
+}
+
+
+/*
+  DAC960_UnregisterBlockDevice unregisters the Block Device structures
+  associated with Controller.
+*/
+
+static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller)
+{
+  int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
+  /*
+    Unregister the Block Device Major Number for this DAC960 Controller.
+  */
+  unregister_blkdev(MajorNumber, "rd");
+  /*
+    Remove the I/O Request Function.
+  */
+  blk_dev[MajorNumber].request_fn = NULL;
+  /*
+    Remove the Disk Partitions array, Partition Sizes array, Block Sizes
+    array, Max Sectors per Request array, and Max Segments per Request array.
+  */
+  Controller->GenericDiskInfo.part = NULL;
+  Controller->GenericDiskInfo.sizes = NULL;
+  blk_size[MajorNumber] = NULL;
+  blksize_size[MajorNumber] = NULL;
+  max_sectors[MajorNumber] = NULL;
+  max_segments[MajorNumber] = NULL;
+  /*
+    Remove the Generic Disk Information structure from the list.
+  */
+  if (gendisk_head != &Controller->GenericDiskInfo)
+    {
+      GenericDiskInfo_T *GenericDiskInfo = gendisk_head;
+      while (GenericDiskInfo != NULL &&
+	     GenericDiskInfo->next != &Controller->GenericDiskInfo)
+	GenericDiskInfo = GenericDiskInfo->next;
+      if (GenericDiskInfo != NULL)
+	GenericDiskInfo->next = GenericDiskInfo->next->next;
+    }
+  else gendisk_head = Controller->GenericDiskInfo.next;
+}
+
+
+/*
+  DAC960_InitializeController initializes Controller.
+*/
+
+static void DAC960_InitializeController(DAC960_Controller_T *Controller)
+{
+  if (DAC960_ReadControllerConfiguration(Controller) &&
+      DAC960_ReportControllerConfiguration(Controller) &&
+      DAC960_RegisterBlockDevice(Controller))
+    {
+      /*
+	Initialize the Command structures.
+      */
+      DAC960_Command_T *Commands = Controller->Commands;
+      int CommandIdentifier;
+      Controller->FreeCommands = NULL;
+      for (CommandIdentifier = 0;
+	   CommandIdentifier < Controller->DriverQueueDepth;
+	   CommandIdentifier++)
+	{
+	  Commands[CommandIdentifier].Controller = Controller;
+	  Commands[CommandIdentifier].Next = Controller->FreeCommands;
+	  Controller->FreeCommands = &Commands[CommandIdentifier];
+	}
+      /*
+	Initialize the Monitoring Timer.
+      */
+      init_timer(&Controller->MonitoringTimer);
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      Controller->MonitoringTimer.data = (unsigned long) Controller;
+      Controller->MonitoringTimer.function = DAC960_MonitoringTimerFunction;
+      add_timer(&Controller->MonitoringTimer);
+      Controller->ControllerInitialized = true;
+    }
+  else DAC960_FinalizeController(Controller);
+}
+
+
+/*
+  DAC960_FinalizeController finalizes Controller.
+*/
+
+static void DAC960_FinalizeController(DAC960_Controller_T *Controller)
+{
+  if (Controller->ControllerInitialized)
+    {
+      del_timer(&Controller->MonitoringTimer);
+      DAC960_Notice("Flushing Cache...", Controller);
+      DAC960_ExecuteType3(Controller, DAC960_Flush, NULL);
+      DAC960_Notice("done\n", Controller);
+      switch (Controller->ControllerType)
+	{
+	case DAC960_V5_Controller:
+	  if (!Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V5_SaveMemoryMailboxInfo(Controller);
+	  break;
+	case DAC960_V4_Controller:
+	  if (!Controller->DualModeMemoryMailboxInterface)
+	    DAC960_V4_SaveMemoryMailboxInfo(Controller);
+	  break;
+	case DAC960_V3_Controller:
+	  break;
+	}
+    }
+  free_irq(Controller->IRQ_Channel, Controller);
+  iounmap(Controller->MemoryMappedAddress);
+  if (Controller->IO_Address > 0)
+    release_region(Controller->IO_Address, 0x80);
+  DAC960_UnregisterBlockDevice(Controller);
+  DAC960_Controllers[Controller->ControllerNumber] = NULL;
+  kfree(Controller);
+}
+
+
+/*
+  DAC960_Initialize initializes the DAC960 Driver.
+*/
+
+void DAC960_Initialize(void)
+{
+  int ControllerNumber;
+  DAC960_DetectControllers(DAC960_V5_Controller);
+  DAC960_DetectControllers(DAC960_V4_Controller);
+  DAC960_DetectControllers(DAC960_V3_Controller);
+  if (DAC960_ActiveControllerCount == 0) return;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    if (DAC960_Controllers[ControllerNumber] != NULL)
+      DAC960_InitializeController(DAC960_Controllers[ControllerNumber]);
+  DAC960_CreateProcEntries();
+  register_reboot_notifier(&DAC960_NotifierBlock);
+}
+
+
+/*
+  DAC960_Finalize finalizes the DAC960 Driver.
+*/
+
+static int DAC960_Finalize(NotifierBlock_T *NotifierBlock,
+			   unsigned long Event,
+			   void *Buffer)
+{
+  int ControllerNumber;
+  if (!(Event == SYS_RESTART || Event == SYS_HALT || Event == SYS_POWER_OFF))
+    return NOTIFY_DONE;
+  if (DAC960_ActiveControllerCount == 0) return NOTIFY_OK;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    if (DAC960_Controllers[ControllerNumber] != NULL)
+      DAC960_FinalizeController(DAC960_Controllers[ControllerNumber]);
+  DAC960_DestroyProcEntries();
+  unregister_reboot_notifier(&DAC960_NotifierBlock);
+  return NOTIFY_OK;
+}
+
+
+/*
+  DAC960_ProcessRequest attempts to remove one I/O Request from Controller's
+  I/O Request Queue and queues it to the Controller.  WaitForCommand is true if
+  this function should wait for a Command to become available if necessary.
+  This function returns true if an I/O Request was queued and false otherwise.
+*/
+
+static boolean DAC960_ProcessRequest(DAC960_Controller_T *Controller,
+				     boolean WaitForCommand)
+{
+  IO_Request_T **RequestQueuePointer =
+    &blk_dev[DAC960_MAJOR + Controller->ControllerNumber].current_request;
+  IO_Request_T *Request;
+  DAC960_Command_T *Command;
+  char *RequestBuffer;
+  while (true)
+    {
+      Request = *RequestQueuePointer;
+      if (Request == NULL || Request->rq_status == RQ_INACTIVE) return false;
+      Command = DAC960_AllocateCommand(Controller);
+      if (Command != NULL) break;
+      if (!WaitForCommand) return false;
+      spin_unlock(&io_request_lock);
+      sleep_on(&Controller->CommandWaitQueue);
+      spin_lock_irq(&io_request_lock);
+    }
+  DAC960_ClearCommand(Command);
+  if (Request->cmd == READ)
+    Command->CommandType = DAC960_ReadCommand;
+  else Command->CommandType = DAC960_WriteCommand;
+  Command->Semaphore = Request->sem;
+  Command->LogicalDriveNumber = DAC960_LogicalDriveNumber(Request->rq_dev);
+  Command->BlockNumber =
+    Request->sector
+    + Controller->GenericDiskInfo.part[MINOR(Request->rq_dev)].start_sect;
+  Command->BlockCount = Request->nr_sectors;
+  Command->SegmentCount = Request->nr_segments;
+  Command->BufferHeader = Request->bh;
+  RequestBuffer = Request->buffer;
+  Request->rq_status = RQ_INACTIVE;
+  *RequestQueuePointer = Request->next;
+  wake_up(&wait_for_request);
+  if (Command->SegmentCount == 1)
+    {
+      DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+      else CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(RequestBuffer);
+    }
+  else
+    {
+      DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+      DAC960_ScatterGatherSegment_T
+	*ScatterGatherList = Command->ScatterGatherList;
+      BufferHeader_T *BufferHeader = Command->BufferHeader;
+      char *LastDataEndPointer = NULL;
+      int SegmentNumber = 0;
+      if (Command->CommandType == DAC960_ReadCommand)
+	CommandMailbox->Type5.CommandOpcode = DAC960_ReadWithOldScatterGather;
+      else
+	CommandMailbox->Type5.CommandOpcode = DAC960_WriteWithOldScatterGather;
+      CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+      CommandMailbox->Type5.LD.LogicalDriveNumber = Command->LogicalDriveNumber;
+      CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+      CommandMailbox->Type5.BusAddress = Virtual_to_Bus(ScatterGatherList);
+      CommandMailbox->Type5.ScatterGatherCount = Command->SegmentCount;
+      while (BufferHeader != NULL)
+	{
+	  if (BufferHeader->b_data == LastDataEndPointer)
+	    {
+	      ScatterGatherList[SegmentNumber-1].SegmentByteCount +=
+		BufferHeader->b_size;
+	      LastDataEndPointer += BufferHeader->b_size;
+	    }
+	  else
+	    {
+	      ScatterGatherList[SegmentNumber].SegmentDataPointer =
+		Virtual_to_Bus(BufferHeader->b_data);
+	      ScatterGatherList[SegmentNumber].SegmentByteCount =
+		BufferHeader->b_size;
+	      LastDataEndPointer = BufferHeader->b_data + BufferHeader->b_size;
+	      if (SegmentNumber++ > Controller->MaxScatterGatherSegments)
+		panic("DAC960: Scatter/Gather Segment Overflow\n");
+	    }
+	  BufferHeader = BufferHeader->b_reqnext;
+	}
+      if (SegmentNumber != Command->SegmentCount)
+	panic("DAC960: SegmentNumber != SegmentCount\n");
+    }
+  DAC960_QueueCommand(Command);
+  return true;
+}
+
+
+/*
+  DAC960_ProcessRequests attempts to remove as many I/O Requests as possible
+  from Controller's I/O Request Queue and queue them to the Controller.
+*/
+
+static inline void DAC960_ProcessRequests(DAC960_Controller_T *Controller)
+{
+  int Counter = 0;
+  while (DAC960_ProcessRequest(Controller, Counter++ == 0)) ;
+}
+
+
+/*
+  DAC960_RequestFunction0 is the I/O Request Function for DAC960 Controller 0.
+*/
+
+static void DAC960_RequestFunction0(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[0];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction1 is the I/O Request Function for DAC960 Controller 1.
+*/
+
+static void DAC960_RequestFunction1(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[1];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction2 is the I/O Request Function for DAC960 Controller 2.
+*/
+
+static void DAC960_RequestFunction2(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[2];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction3 is the I/O Request Function for DAC960 Controller 3.
+*/
+
+static void DAC960_RequestFunction3(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[3];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction4 is the I/O Request Function for DAC960 Controller 4.
+*/
+
+static void DAC960_RequestFunction4(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[4];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction5 is the I/O Request Function for DAC960 Controller 5.
+*/
+
+static void DAC960_RequestFunction5(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[5];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction6 is the I/O Request Function for DAC960 Controller 6.
+*/
+
+static void DAC960_RequestFunction6(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[6];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_RequestFunction7 is the I/O Request Function for DAC960 Controller 7.
+*/
+
+static void DAC960_RequestFunction7(void)
+{
+  DAC960_Controller_T *Controller = DAC960_Controllers[7];
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockRF(Controller, &ProcessorFlags);
+  /*
+    Process I/O Requests for Controller.
+  */
+  DAC960_ProcessRequests(Controller);
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockRF(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_ReadWriteError prints an appropriate error message for Command when
+  an error occurs on a Read or Write operation.
+*/
+
+static void DAC960_ReadWriteError(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  char *CommandName = "UNKNOWN";
+  switch (Command->CommandType)
+    {
+    case DAC960_ReadCommand:
+    case DAC960_ReadRetryCommand:
+      CommandName = "READ";
+      break;
+    case DAC960_WriteCommand:
+    case DAC960_WriteRetryCommand:
+      CommandName = "WRITE";
+      break;
+    case DAC960_MonitoringCommand:
+    case DAC960_ImmediateCommand:
+    case DAC960_QueuedCommand:
+      break;
+    }
+  switch (Command->CommandStatus)
+    {
+    case DAC960_IrrecoverableDataError:
+      DAC960_Error("Irrecoverable Data Error on %s:\n",
+		   Controller, CommandName);
+      break;
+    case DAC960_LogicalDriveNonexistentOrOffline:
+      DAC960_Error("Logical Drive Nonexistent or Offline on %s:\n",
+		   Controller, CommandName);
+      break;
+    case DAC960_AccessBeyondEndOfLogicalDrive:
+      DAC960_Error("Attempt to Access Beyond End of Logical Drive "
+		   "on %s:\n", Controller, CommandName);
+      break;
+    case DAC960_BadDataEncountered:
+      DAC960_Error("Bad Data Encountered on %s:\n", Controller, CommandName);
+      break;
+    default:
+      DAC960_Error("Unexpected Error Status %04X on %s:\n",
+		   Controller, Command->CommandStatus, CommandName);
+      break;
+    }
+  DAC960_Error("  /dev/rd/c%dd%d:   absolute blocks %d..%d\n",
+	       Controller, Controller->ControllerNumber,
+	       Command->LogicalDriveNumber, Command->BlockNumber,
+	       Command->BlockNumber + Command->BlockCount - 1);
+  if (DAC960_PartitionNumber(Command->BufferHeader->b_rdev) > 0)
+    DAC960_Error("  /dev/rd/c%dd%dp%d: relative blocks %d..%d\n",
+		 Controller, Controller->ControllerNumber,
+		 Command->LogicalDriveNumber,
+		 DAC960_PartitionNumber(Command->BufferHeader->b_rdev),
+		 Command->BufferHeader->b_rsector,
+		 Command->BufferHeader->b_rsector + Command->BlockCount - 1);
+}
+
+
+/*
+  DAC960_ProcessCompletedBuffer performs completion processing for an
+  individual Buffer.
+*/
+
+static inline void DAC960_ProcessCompletedBuffer(BufferHeader_T *BufferHeader,
+						 boolean SuccessfulIO)
+{
+  BufferHeader->b_end_io(BufferHeader, SuccessfulIO);
+}
+
+
+/*
+  DAC960_ProcessCompletedCommand performs completion processing for Command.
+*/
+
+static void DAC960_ProcessCompletedCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandType_T CommandType = Command->CommandType;
+  DAC960_CommandOpcode_T CommandOpcode =
+    Command->CommandMailbox.Common.CommandOpcode;
+  DAC960_CommandStatus_T CommandStatus = Command->CommandStatus;
+  BufferHeader_T *BufferHeader = Command->BufferHeader;
+  if (CommandType == DAC960_ReadCommand ||
+      CommandType == DAC960_WriteCommand)
+    {
+      if (CommandStatus == DAC960_NormalCompletion)
+	{
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, true);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	  add_blkdev_randomness(DAC960_MAJOR + Controller->ControllerNumber);
+	}
+      else if ((CommandStatus == DAC960_IrrecoverableDataError ||
+		CommandStatus == DAC960_BadDataEncountered) &&
+	       BufferHeader != NULL &&
+	       BufferHeader->b_reqnext != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  if (CommandType == DAC960_ReadCommand)
+	    {
+	      Command->CommandType = DAC960_ReadRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Read;
+	    }
+	  else
+	    {
+	      Command->CommandType = DAC960_WriteRetryCommand;
+	      CommandMailbox->Type5.CommandOpcode = DAC960_Write;
+	    }
+	  Command->BlockCount = BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(BufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      else
+	{
+	  if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline)
+	    DAC960_ReadWriteError(Command);
+	  /*
+	    Perform completion processing for all buffers in this I/O Request.
+	  */
+	  while (BufferHeader != NULL)
+	    {
+	      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+	      BufferHeader->b_reqnext = NULL;
+	      DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	      BufferHeader = NextBufferHeader;
+	    }
+	  /*
+	    Wake up requestor for swap file paging requests.
+	  */
+	  if (Command->Semaphore != NULL)
+	    {
+	      up(Command->Semaphore);
+	      Command->Semaphore = NULL;
+	    }
+	}
+    }
+  else if (CommandType == DAC960_ReadRetryCommand ||
+	   CommandType == DAC960_WriteRetryCommand)
+    {
+      BufferHeader_T *NextBufferHeader = BufferHeader->b_reqnext;
+      BufferHeader->b_reqnext = NULL;
+      /*
+	Perform completion processing for this single buffer.
+      */
+      if (CommandStatus == DAC960_NormalCompletion)
+	DAC960_ProcessCompletedBuffer(BufferHeader, true);
+      else
+	{
+	  if (CommandStatus != DAC960_LogicalDriveNonexistentOrOffline)
+	    DAC960_ReadWriteError(Command);
+	  DAC960_ProcessCompletedBuffer(BufferHeader, false);
+	}
+      if (NextBufferHeader != NULL)
+	{
+	  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+	  Command->BlockNumber +=
+	    BufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BlockCount =
+	    NextBufferHeader->b_size >> DAC960_BlockSizeBits;
+	  Command->BufferHeader = NextBufferHeader;
+	  CommandMailbox->Type5.LD.TransferLength = Command->BlockCount;
+	  CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber;
+	  CommandMailbox->Type5.BusAddress =
+	    Virtual_to_Bus(NextBufferHeader->b_data);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+    }
+  else if (CommandType == DAC960_MonitoringCommand ||
+	   CommandOpcode == DAC960_Enquiry ||
+	   CommandOpcode == DAC960_GetRebuildProgress)
+    {
+      if (CommandType != DAC960_MonitoringCommand)
+	{
+	  if (CommandOpcode == DAC960_Enquiry)
+	    memcpy(&Controller->Enquiry[Controller->EnquiryIndex ^ 1],
+		   Bus_to_Virtual(Command->CommandMailbox.Type3.BusAddress),
+		   sizeof(DAC960_Enquiry_T));
+	  else if (CommandOpcode == DAC960_GetRebuildProgress)
+	    memcpy(&Controller->RebuildProgress,
+		   Bus_to_Virtual(Command->CommandMailbox.Type3.BusAddress),
+		   sizeof(DAC960_RebuildProgress_T));
+	}
+      if (CommandOpcode == DAC960_Enquiry)
+	{
+	  DAC960_Enquiry_T *OldEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex];
+	  DAC960_Enquiry_T *NewEnquiry =
+	    &Controller->Enquiry[Controller->EnquiryIndex ^= 1];
+	  unsigned int OldCriticalLogicalDriveCount =
+	    OldEnquiry->CriticalLogicalDriveCount;
+	  unsigned int NewCriticalLogicalDriveCount =
+	    NewEnquiry->CriticalLogicalDriveCount;
+	  if (NewEnquiry->StatusFlags.DeferredWriteError !=
+	      OldEnquiry->StatusFlags.DeferredWriteError)
+	    DAC960_Critical("Deferred Write Error Flag is now %s\n", Controller,
+			    (NewEnquiry->StatusFlags.DeferredWriteError
+			     ? "TRUE" : "FALSE"));
+	  if ((NewCriticalLogicalDriveCount > 0 ||
+	       NewCriticalLogicalDriveCount != OldCriticalLogicalDriveCount) ||
+	      (NewEnquiry->OfflineLogicalDriveCount > 0 ||
+	       NewEnquiry->OfflineLogicalDriveCount !=
+	       OldEnquiry->OfflineLogicalDriveCount) ||
+	      (NewEnquiry->DeadDriveCount > 0 ||
+	       NewEnquiry->DeadDriveCount !=
+	       OldEnquiry->DeadDriveCount) ||
+	      (NewEnquiry->EventLogSequenceNumber !=
+	       OldEnquiry->EventLogSequenceNumber) ||
+	      Controller->MonitoringTimerCount == 0 ||
+	      (jiffies - Controller->SecondaryMonitoringTime
+	       >= DAC960_SecondaryMonitoringInterval))
+	    {
+	      Controller->NeedLogicalDriveInformation = true;
+	      Controller->NewEventLogSequenceNumber =
+		NewEnquiry->EventLogSequenceNumber;
+	      Controller->NeedErrorTableInformation = true;
+	      Controller->NeedDeviceStateInformation = true;
+	      Controller->DeviceStateChannel = 0;
+	      Controller->DeviceStateTargetID = 0;
+	      Controller->SecondaryMonitoringTime = jiffies;
+	    }
+	  if (NewEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress ||
+	      NewEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress ||
+	      OldEnquiry->RebuildFlag == DAC960_StandbyRebuildInProgress ||
+	      OldEnquiry->RebuildFlag == DAC960_BackgroundRebuildInProgress)
+	    Controller->NeedRebuildProgress = true;
+	  if (OldEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress)
+	    switch (NewEnquiry->RebuildFlag)
+	      {
+	      case DAC960_NoStandbyRebuildOrCheckInProgress:
+		DAC960_Progress("Consistency Check Completed Successfully\n",
+				Controller);
+		break;
+	      case DAC960_StandbyRebuildInProgress:
+	      case DAC960_BackgroundRebuildInProgress:
+		break;
+	      case DAC960_BackgroundCheckInProgress:
+		Controller->NeedConsistencyCheckProgress = true;
+		break;
+	      case DAC960_StandbyRebuildCompletedWithError:
+		DAC960_Progress("Consistency Check Completed with Error\n",
+				Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_DriveFailed:
+		DAC960_Progress("Consistency Check Failed - "
+				"Physical Drive Failed\n", Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_LogicalDriveFailed:
+		DAC960_Progress("Consistency Check Failed - "
+				"Logical Drive Failed\n", Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckFailed_OtherCauses:
+		DAC960_Progress("Consistency Check Failed - Other Causes\n",
+				Controller);
+		break;
+	      case DAC960_BackgroundRebuildOrCheckSuccessfullyTerminated:
+		DAC960_Progress("Consistency Check Successfully Terminated\n",
+				Controller);
+		break;
+	      }
+	  else if (NewEnquiry->RebuildFlag == DAC960_BackgroundCheckInProgress)
+	    Controller->NeedConsistencyCheckProgress = true;
+	}
+      else if (CommandOpcode == DAC960_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_EventLogEntry_T *EventLogEntry = &Controller->EventLogEntry;
+	  if (EventLogEntry->SequenceNumber ==
+	      Controller->OldEventLogSequenceNumber)
+	    {
+	      unsigned char SenseKey = EventLogEntry->SenseKey;
+	      unsigned char AdditionalSenseCode =
+		EventLogEntry->AdditionalSenseCode;
+	      unsigned char AdditionalSenseCodeQualifier =
+		EventLogEntry->AdditionalSenseCodeQualifier;
+	      if (SenseKey == 9 &&
+		  AdditionalSenseCode == 0x80 &&
+		  AdditionalSenseCodeQualifier <
+		  sizeof(DAC960_EventMessages) / sizeof(char *))
+		DAC960_Critical("Physical Drive %d:%d %s\n", Controller,
+				EventLogEntry->Channel,
+				EventLogEntry->TargetID,
+				DAC960_EventMessages[
+				  AdditionalSenseCodeQualifier]);
+	      else if (!(SenseKey == 0 ||
+			 (SenseKey == 2 &&
+			  AdditionalSenseCode == 0x04 &&
+			  (AdditionalSenseCodeQualifier == 0x01 ||
+			   AdditionalSenseCodeQualifier == 0x02)) ||
+			 (SenseKey == 6 && AdditionalSenseCode == 0x29 &&
+			  Controller->MonitoringTimerCount == 0)))
+		{
+		  DAC960_Critical("Physical Drive %d:%d Error Log: "
+				  "Sense Key = %d, ASC = %02X, ASCQ = %02X\n",
+				  Controller,
+				  EventLogEntry->Channel,
+				  EventLogEntry->TargetID,
+				  SenseKey,
+				  AdditionalSenseCode,
+				  AdditionalSenseCodeQualifier);
+		  DAC960_Critical("Physical Drive %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->OldEventLogSequenceNumber++;
+	}
+      else if (CommandOpcode == DAC960_GetErrorTable)
+	{
+	  DAC960_ErrorTable_T *OldErrorTable =
+	    &Controller->ErrorTable[Controller->ErrorTableIndex];
+	  DAC960_ErrorTable_T *NewErrorTable =
+	    &Controller->ErrorTable[Controller->ErrorTableIndex ^= 1];
+	  int Channel, TargetID;
+	  for (Channel = 0; Channel < Controller->Channels; Channel++)
+	    for (TargetID = 0; TargetID < DAC960_MaxTargets; TargetID++)
+	      {
+		DAC960_ErrorTableEntry_T *NewErrorEntry =
+		  &NewErrorTable->ErrorTableEntries[Channel][TargetID];
+		DAC960_ErrorTableEntry_T *OldErrorEntry =
+		  &OldErrorTable->ErrorTableEntries[Channel][TargetID];
+		if ((NewErrorEntry->ParityErrorCount !=
+		     OldErrorEntry->ParityErrorCount) ||
+		    (NewErrorEntry->SoftErrorCount !=
+		     OldErrorEntry->SoftErrorCount) ||
+		    (NewErrorEntry->HardErrorCount !=
+		     OldErrorEntry->HardErrorCount) ||
+		    (NewErrorEntry->MiscErrorCount !=
+		     OldErrorEntry->MiscErrorCount))
+		  DAC960_Critical("Physical Drive %d:%d Errors: "
+				  "Parity = %d, Soft = %d, "
+				  "Hard = %d, Misc = %d\n",
+				  Controller, Channel, TargetID,
+				  NewErrorEntry->ParityErrorCount,
+				  NewErrorEntry->SoftErrorCount,
+				  NewErrorEntry->HardErrorCount,
+				  NewErrorEntry->MiscErrorCount);
+	      }
+	}
+      else if (CommandOpcode == DAC960_GetDeviceState)
+	{
+	  DAC960_DeviceState_T *OldDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  DAC960_DeviceState_T *NewDeviceState =
+	    &Controller->DeviceState[Controller->DeviceStateIndex ^ 1]
+				    [Controller->DeviceStateChannel]
+				    [Controller->DeviceStateTargetID];
+	  if (NewDeviceState->DeviceState != OldDeviceState->DeviceState)
+	    DAC960_Critical("Physical Drive %d:%d is now %s\n", Controller,
+			    Controller->DeviceStateChannel,
+			    Controller->DeviceStateTargetID,
+			    (NewDeviceState->DeviceState == DAC960_Device_Dead
+			     ? "DEAD"
+			     : NewDeviceState->DeviceState
+			       == DAC960_Device_WriteOnly
+			       ? "WRITE-ONLY"
+			       : NewDeviceState->DeviceState
+				 == DAC960_Device_Online
+				 ? "ONLINE" : "STANDBY"));
+	  if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+	    {
+	      Controller->DeviceStateChannel++;
+	      Controller->DeviceStateTargetID = 0;
+	    }
+	}
+      else if (CommandOpcode == DAC960_GetLogicalDriveInformation)
+	{
+	  int LogicalDriveNumber;
+	  for (LogicalDriveNumber = 0;
+	       LogicalDriveNumber < Controller->LogicalDriveCount;
+	       LogicalDriveNumber++)
+	    {
+	      DAC960_LogicalDriveInformation_T *OldLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex]
+			     [LogicalDriveNumber];
+	      DAC960_LogicalDriveInformation_T *NewLogicalDriveInformation =
+		&Controller->LogicalDriveInformation
+			     [Controller->LogicalDriveInformationIndex ^ 1]
+			     [LogicalDriveNumber];
+	      if (NewLogicalDriveInformation->LogicalDriveState !=
+		  OldLogicalDriveInformation->LogicalDriveState)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Online
+				 ? "ONLINE"
+				 : NewLogicalDriveInformation->LogicalDriveState
+				 == DAC960_LogicalDrive_Critical
+				 ? "CRITICAL" : "OFFLINE"));
+	      if (NewLogicalDriveInformation->WriteBack !=
+		  OldLogicalDriveInformation->WriteBack)
+		DAC960_Critical("Logical Drive %d (/dev/rd/c%dd%d) "
+				"is now %s\n", Controller,
+				LogicalDriveNumber,
+				Controller->ControllerNumber,
+				LogicalDriveNumber,
+				(NewLogicalDriveInformation->WriteBack
+				 ? "WRITE BACK" : "WRITE THRU"));
+	    }
+	  Controller->LogicalDriveInformationIndex ^= 1;
+	}
+      else if (CommandOpcode == DAC960_GetRebuildProgress)
+	{
+	  unsigned int LogicalDriveNumber =
+	    Controller->RebuildProgress.LogicalDriveNumber;
+	  unsigned int LogicalDriveSize =
+	    Controller->RebuildProgress.LogicalDriveSize;
+	  unsigned int BlocksCompleted =
+	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  switch (CommandStatus)
+	    {
+	    case DAC960_NormalCompletion:
+	      Controller->EphemeralProgressMessage = true;
+	      DAC960_Progress("Rebuild in Progress: "
+			      "Logical Drive %d (/dev/rd/c%dd%d) "
+			      "%d%% completed\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber,
+			      (100 * (BlocksCompleted >> 7))
+			      / (LogicalDriveSize >> 7));
+	      Controller->EphemeralProgressMessage = false;
+	      break;
+	    case DAC960_RebuildFailed_LogicalDriveFailure:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Logical Drive Failure\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_BadBlocksOnOther:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Bad Blocks on Other Drives\n", Controller);
+	      break;
+	    case DAC960_RebuildFailed_NewDriveFailed:
+	      DAC960_Progress("Rebuild Failed due to "
+			      "Failure of Drive Being Rebuilt\n", Controller);
+	      break;
+	    case DAC960_NoRebuildOrCheckInProgress:
+	      if (Controller->LastRebuildStatus != DAC960_NormalCompletion)
+		break;
+	    case DAC960_RebuildSuccessful:
+	      DAC960_Progress("Rebuild Completed Successfully\n", Controller);
+	      break;
+	    }
+	  Controller->LastRebuildStatus = CommandStatus;
+	}
+      else if (CommandOpcode == DAC960_RebuildStat)
+	{
+	  unsigned int LogicalDriveNumber =
+	    Controller->RebuildProgress.LogicalDriveNumber;
+	  unsigned int LogicalDriveSize =
+	    Controller->RebuildProgress.LogicalDriveSize;
+	  unsigned int BlocksCompleted =
+	    LogicalDriveSize - Controller->RebuildProgress.RemainingBlocks;
+	  if (CommandStatus == DAC960_NormalCompletion)
+	    {
+	      Controller->EphemeralProgressMessage = true;
+	      DAC960_Progress("Consistency Check in Progress: "
+			      "Logical Drive %d (/dev/rd/c%dd%d) "
+			      "%d%% completed\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber,
+			      (100 * (BlocksCompleted >> 7))
+			      / (LogicalDriveSize >> 7));
+	      Controller->EphemeralProgressMessage = false;
+	    }
+	}
+    }
+  if (CommandType == DAC960_MonitoringCommand)
+    {
+      if (Controller->NewEventLogSequenceNumber
+	  - Controller->OldEventLogSequenceNumber > 0)
+	{
+	  Command->CommandMailbox.Type3E.CommandOpcode =
+	    DAC960_PerformEventLogOperation;
+	  Command->CommandMailbox.Type3E.OperationType =
+	    DAC960_GetEventLogEntry;
+	  Command->CommandMailbox.Type3E.OperationQualifier = 1;
+	  Command->CommandMailbox.Type3E.SequenceNumber =
+	    Controller->OldEventLogSequenceNumber;
+	  Command->CommandMailbox.Type3E.BusAddress =
+	    Virtual_to_Bus(&Controller->EventLogEntry);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedErrorTableInformation)
+	{
+	  Controller->NeedErrorTableInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode = DAC960_GetErrorTable;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->ErrorTable[Controller->ErrorTableIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedRebuildProgress && 
+	  Controller->Enquiry[Controller->EnquiryIndex]
+		      .CriticalLogicalDriveCount <
+	  Controller->Enquiry[Controller->EnquiryIndex ^ 1]
+		      .CriticalLogicalDriveCount)
+	{
+	  Controller->NeedRebuildProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetRebuildProgress;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedDeviceStateInformation)
+	{
+	  while (Controller->DeviceStateChannel < Controller->Channels)
+	    {
+	      DAC960_DeviceState_T *OldDeviceState =
+		&Controller->DeviceState[Controller->DeviceStateIndex]
+					[Controller->DeviceStateChannel]
+					[Controller->DeviceStateTargetID];
+	      if (OldDeviceState->Present &&
+		  OldDeviceState->DeviceType == DAC960_DiskType)
+		{
+		  Command->CommandMailbox.Type3D.CommandOpcode =
+		    DAC960_GetDeviceState;
+		  Command->CommandMailbox.Type3D.Channel =
+		    Controller->DeviceStateChannel;
+		  Command->CommandMailbox.Type3D.TargetID =
+		    Controller->DeviceStateTargetID;
+		  Command->CommandMailbox.Type3D.BusAddress =
+		    Virtual_to_Bus(&Controller->DeviceState
+				      [Controller->DeviceStateIndex ^ 1]
+				      [Controller->DeviceStateChannel]
+				      [Controller->DeviceStateTargetID]);
+		  DAC960_QueueCommand(Command);
+		  return;
+		}
+	      if (++Controller->DeviceStateTargetID == DAC960_MaxTargets)
+		{
+		  Controller->DeviceStateChannel++;
+		  Controller->DeviceStateTargetID = 0;
+		}
+	    }
+	  Controller->NeedDeviceStateInformation = false;
+	  Controller->DeviceStateIndex ^= 1;
+	}
+      if (Controller->NeedLogicalDriveInformation)
+	{
+	  Controller->NeedLogicalDriveInformation = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetLogicalDriveInformation;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(
+	      &Controller->LogicalDriveInformation
+			   [Controller->LogicalDriveInformationIndex ^ 1]);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedRebuildProgress)
+	{
+	  Controller->NeedRebuildProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode =
+	    DAC960_GetRebuildProgress;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      if (Controller->NeedConsistencyCheckProgress)
+	{
+	  Controller->NeedConsistencyCheckProgress = false;
+	  Command->CommandMailbox.Type3.CommandOpcode = DAC960_RebuildStat;
+	  Command->CommandMailbox.Type3.BusAddress =
+	    Virtual_to_Bus(&Controller->RebuildProgress);
+	  DAC960_QueueCommand(Command);
+	  return;
+	}
+      Controller->MonitoringTimerCount++;
+      Controller->MonitoringTimer.expires =
+	jiffies + DAC960_MonitoringTimerInterval;
+      add_timer(&Controller->MonitoringTimer);
+    }
+  if (CommandType == DAC960_ImmediateCommand)
+    {
+      up(Command->Semaphore);
+      Command->Semaphore = NULL;
+      return;
+    }
+  if (CommandType == DAC960_QueuedCommand)
+    {
+      DAC960_KernelCommand_T *KernelCommand = Command->KernelCommand;
+      KernelCommand->CommandStatus = CommandStatus;
+      Command->KernelCommand = NULL;
+      if (CommandOpcode == DAC960_DCDB)
+	Controller->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_QueueMonitoringCommand(Command);
+      return;
+    }
+  /*
+    Deallocate the Command, and wake up any processes waiting on a free Command.
+  */
+  DAC960_DeallocateCommand(Command);
+  wake_up(&Controller->CommandWaitQueue);
+}
+
+
+/*
+  DAC960_InterruptHandler handles hardware interrupts from DAC960 Controllers.
+*/
+
+static void DAC960_InterruptHandler(int IRQ_Channel,
+				    void *DeviceIdentifier,
+				    Registers_T *InterruptRegisters)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) DeviceIdentifier;
+  void *ControllerBaseAddress = Controller->BaseAddress;
+  DAC960_StatusMailbox_T *NextStatusMailbox;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLockIH(Controller, &ProcessorFlags);
+  /*
+    Process Hardware Interrupts for Controller.
+  */
+  switch (Controller->ControllerType)
+    {
+    case DAC960_V5_Controller:
+      DAC960_V5_AcknowledgeInterrupt(ControllerBaseAddress);
+      NextStatusMailbox = Controller->NextStatusMailbox;
+      while (NextStatusMailbox->Fields.Valid)
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    NextStatusMailbox->Fields.CommandIdentifier;
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+	  NextStatusMailbox->Word = 0;
+	  if (++NextStatusMailbox > Controller->LastStatusMailbox)
+	    NextStatusMailbox = Controller->FirstStatusMailbox;
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      Controller->NextStatusMailbox = NextStatusMailbox;
+      break;
+    case DAC960_V4_Controller:
+      DAC960_V4_AcknowledgeInterrupt(ControllerBaseAddress);
+      NextStatusMailbox = Controller->NextStatusMailbox;
+      while (NextStatusMailbox->Fields.Valid)
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    NextStatusMailbox->Fields.CommandIdentifier;
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus = NextStatusMailbox->Fields.CommandStatus;
+	  NextStatusMailbox->Word = 0;
+	  if (++NextStatusMailbox > Controller->LastStatusMailbox)
+	    NextStatusMailbox = Controller->FirstStatusMailbox;
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      Controller->NextStatusMailbox = NextStatusMailbox;
+      break;
+    case DAC960_V3_Controller:
+      while (DAC960_V3_StatusAvailableP(ControllerBaseAddress))
+	{
+	  DAC960_CommandIdentifier_T CommandIdentifier =
+	    DAC960_V3_ReadStatusCommandIdentifier(ControllerBaseAddress);
+	  DAC960_Command_T *Command = &Controller->Commands[CommandIdentifier];
+	  Command->CommandStatus =
+	    DAC960_V3_ReadStatusRegister(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeInterrupt(ControllerBaseAddress);
+	  DAC960_V3_AcknowledgeStatus(ControllerBaseAddress);
+	  DAC960_ProcessCompletedCommand(Command);
+	}
+      break;
+    }
+  /*
+    Attempt to remove additional I/O Requests from the Controller's
+    I/O Request Queue and queue them to the Controller.
+  */
+  while (DAC960_ProcessRequest(Controller, false)) ;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLockIH(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_QueueMonitoringCommand queues a Monitoring Command to Controller.
+*/
+
+static void DAC960_QueueMonitoringCommand(DAC960_Command_T *Command)
+{
+  DAC960_Controller_T *Controller = Command->Controller;
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_MonitoringCommand;
+  CommandMailbox->Type3.CommandOpcode = DAC960_Enquiry;
+  CommandMailbox->Type3.BusAddress =
+    Virtual_to_Bus(&Controller->Enquiry[Controller->EnquiryIndex ^ 1]);
+  DAC960_QueueCommand(Command);
+}
+
+
+/*
+  DAC960_MonitoringTimerFunction is the timer function for monitoring
+  the status of DAC960 Controllers.
+*/
+
+static void DAC960_MonitoringTimerFunction(unsigned long TimerData)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) TimerData;
+  DAC960_Command_T *Command;
+  ProcessorFlags_T ProcessorFlags;
+  /*
+    Acquire exclusive access to Controller.
+  */
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  /*
+    Queue a Status Monitoring Command to Controller.
+  */
+  Command = DAC960_AllocateCommand(Controller);
+  if (Command != NULL)
+    DAC960_QueueMonitoringCommand(Command);
+  else Controller->MonitoringCommandDeferred = true;
+  /*
+    Release exclusive access to Controller.
+  */
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+}
+
+
+/*
+  DAC960_Open is the Device Open Function for the DAC960 Driver.
+*/
+
+static int DAC960_Open(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller;
+  if (ControllerNumber == 0 && LogicalDriveNumber == 0 &&
+      (File->f_flags & O_NONBLOCK))
+    goto ModuleOnly;
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  if (Controller->LogicalDriveInformation
+		  [Controller->LogicalDriveInformationIndex]
+		  [LogicalDriveNumber].LogicalDriveState
+      == DAC960_LogicalDrive_Offline)
+    return -ENXIO;
+  if (Controller->LogicalDriveInitialState[LogicalDriveNumber]
+      == DAC960_LogicalDrive_Offline)
+    {
+      Controller->LogicalDriveInitialState[LogicalDriveNumber] =
+	DAC960_LogicalDrive_Online;
+      DAC960_InitializeGenericDiskInfo(&Controller->GenericDiskInfo);
+      resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+    }
+  if (Controller->GenericDiskInfo.sizes[MINOR(Inode->i_rdev)] == 0)
+    return -ENXIO;
+  /*
+    Increment Controller and Logical Drive Usage Counts.
+  */
+  Controller->ControllerUsageCount++;
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]++;
+ ModuleOnly:
+  MOD_INC_USE_COUNT;
+  return 0;
+}
+
+
+/*
+  DAC960_Release is the Device Release Function for the DAC960 Driver.
+*/
+
+static int DAC960_Release(Inode_T *Inode, File_T *File)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+  if (ControllerNumber == 0 && LogicalDriveNumber == 0 &&
+      File != NULL && (File->f_flags & O_NONBLOCK))
+    goto ModuleOnly;
+  /*
+    Force any buffered data to be written.
+  */
+  fsync_dev(Inode->i_rdev);
+  /*
+    Decrement the Logical Drive and Controller Usage Counts.
+  */
+  Controller->LogicalDriveUsageCount[LogicalDriveNumber]--;
+  Controller->ControllerUsageCount--;
+ ModuleOnly:
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+
+/*
+  DAC960_IOCTL is the Device IOCTL Function for the DAC960 Driver.
+*/
+
+static int DAC960_IOCTL(Inode_T *Inode, File_T *File,
+			unsigned int Request, unsigned long Argument)
+{
+  int ControllerNumber = DAC960_ControllerNumber(Inode->i_rdev);
+  int LogicalDriveNumber = DAC960_LogicalDriveNumber(Inode->i_rdev);
+  DiskGeometry_T Geometry, *UserGeometry;
+  DAC960_Controller_T *Controller;
+  int PartitionNumber;
+  if (File->f_flags & O_NONBLOCK)
+    return DAC960_UserIOCTL(Inode, File, Request, Argument);
+  if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1)
+    return -ENXIO;
+  Controller = DAC960_Controllers[ControllerNumber];
+  if (Controller == NULL ||
+      LogicalDriveNumber > Controller->LogicalDriveCount - 1)
+    return -ENXIO;
+  switch (Request)
+    {
+    case HDIO_GETGEO:
+      /* Get BIOS Disk Geometry. */
+      UserGeometry = (DiskGeometry_T *) Argument;
+      if (UserGeometry == NULL) return -EINVAL;
+      Geometry.heads = Controller->GeometryTranslationHeads;
+      Geometry.sectors = Controller->GeometryTranslationSectors;
+      Geometry.cylinders =
+	Controller->LogicalDriveInformation
+		    [Controller->LogicalDriveInformationIndex]
+		    [LogicalDriveNumber].LogicalDriveSize
+	/ (Controller->GeometryTranslationHeads *
+	   Controller->GeometryTranslationSectors);
+      Geometry.start = Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)]
+						  .start_sect;
+      return copy_to_user(UserGeometry, &Geometry, sizeof(DiskGeometry_T));
+    case BLKGETSIZE:
+      /* Get Device Size. */
+      if ((long *) Argument == NULL) return -EINVAL;
+      return put_user(Controller->GenericDiskInfo.part[MINOR(Inode->i_rdev)]
+						 .nr_sects,
+		      (long *) Argument);
+    case BLKRAGET:
+      /* Get Read-Ahead. */
+      if ((int *) Argument == NULL) return -EINVAL;
+      return put_user(read_ahead[MAJOR(Inode->i_rdev)], (int *) Argument);
+    case BLKRASET:
+      /* Set Read-Ahead. */
+      if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+      if (Argument > 256) return -EINVAL;
+      read_ahead[MAJOR(Inode->i_rdev)] = Argument;
+      return 0;
+    case BLKFLSBUF:
+      /* Flush Buffers. */
+      if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+      fsync_dev(Inode->i_rdev);
+      invalidate_buffers(Inode->i_rdev);
+      return 0;
+    case BLKRRPART:
+      /* Re-Read Partition Table. */
+      if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+      if (Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 1)
+	return -EBUSY;
+      for (PartitionNumber = 0;
+	   PartitionNumber < DAC960_MaxPartitions;
+	   PartitionNumber++)
+	{
+	  KernelDevice_T Device = DAC960_KernelDevice(ControllerNumber,
+						      LogicalDriveNumber,
+						      PartitionNumber);
+	  int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber,
+					       PartitionNumber);
+	  SuperBlock_T *SuperBlock = get_super(Device);
+	  if (Controller->GenericDiskInfo.part[MinorNumber].nr_sects == 0)
+	    continue;
+	  /*
+	    Flush all changes and invalidate buffered state.
+	  */
+	  sync_dev(Device);
+	  if (SuperBlock != NULL)
+	    invalidate_inodes(SuperBlock);
+	  invalidate_buffers(Device);
+	  /*
+	    Clear existing partition sizes.
+	  */
+	  if (PartitionNumber > 0)
+	    {
+	      Controller->GenericDiskInfo.part[MinorNumber].start_sect = 0;
+	      Controller->GenericDiskInfo.part[MinorNumber].nr_sects = 0;
+	    }
+	  /*
+	    Reset the Block Size so that the partition table can be read.
+	  */
+	  set_blocksize(Device, BLOCK_SIZE);
+	}
+      resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+      return 0;
+    }
+  return -EINVAL;
+}
+
+
+/*
+  DAC960_UserIOCTL is the User IOCTL Function for the DAC960 Driver.
+*/
+
+static int DAC960_UserIOCTL(Inode_T *Inode, File_T *File,
+			    unsigned int Request, unsigned long Argument)
+{
+  int ErrorCode;
+  if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+  switch (Request)
+    {
+    case DAC960_IOCTL_GET_CONTROLLER_COUNT:
+      return DAC960_ControllerCount;
+    case DAC960_IOCTL_GET_CONTROLLER_INFO:
+      {
+	DAC960_ControllerInfo_T *UserSpaceControllerInfo =
+	  (DAC960_ControllerInfo_T *) Argument;
+	DAC960_ControllerInfo_T ControllerInfo;
+	DAC960_Controller_T *Controller;
+	int ControllerNumber;
+	if (UserSpaceControllerInfo == NULL) return -EINVAL;
+	ErrorCode = get_user(ControllerNumber,
+			     &UserSpaceControllerInfo->ControllerNumber);
+	if (ErrorCode != 0) return ErrorCode;
+	if (ControllerNumber < 0 ||
+	    ControllerNumber > DAC960_ControllerCount - 1)
+	  return -ENXIO;
+	Controller = DAC960_Controllers[ControllerNumber];
+	if (Controller == NULL)
+	  return -ENXIO;
+	memset(&ControllerInfo, 0, sizeof(DAC960_ControllerInfo_T));
+	ControllerInfo.ControllerNumber = ControllerNumber;
+	ControllerInfo.PCI_Bus = Controller->Bus;
+	ControllerInfo.PCI_Device = Controller->Device;
+	ControllerInfo.PCI_Function = Controller->Function;
+	ControllerInfo.IRQ_Channel = Controller->IRQ_Channel;
+	ControllerInfo.Channels = Controller->Channels;
+	ControllerInfo.PCI_Address = Controller->PCI_Address;
+	strcpy(ControllerInfo.ModelName, Controller->ModelName);
+	strcpy(ControllerInfo.FirmwareVersion, Controller->FirmwareVersion);
+	return copy_to_user(UserSpaceControllerInfo, &ControllerInfo,
+			    sizeof(DAC960_ControllerInfo_T));
+      }
+    case DAC960_IOCTL_EXECUTE_COMMAND:
+      {
+	DAC960_UserCommand_T *UserSpaceUserCommand =
+	  (DAC960_UserCommand_T *) Argument;
+	DAC960_UserCommand_T UserCommand;
+	DAC960_Controller_T *Controller;
+	DAC960_Command_T *Command = NULL;
+	DAC960_CommandOpcode_T CommandOpcode;
+	DAC960_CommandStatus_T CommandStatus;
+	DAC960_DCDB_T DCDB;
+	ProcessorFlags_T ProcessorFlags;
+	int ControllerNumber, DataTransferLength;
+	unsigned char *DataTransferBuffer = NULL;
+	if (UserSpaceUserCommand == NULL) return -EINVAL;
+	ErrorCode = copy_from_user(&UserCommand, UserSpaceUserCommand,
+				   sizeof(DAC960_UserCommand_T));
+	if (ErrorCode != 0) goto Failure;
+	ControllerNumber = UserCommand.ControllerNumber;
+	if (ControllerNumber < 0 ||
+	    ControllerNumber > DAC960_ControllerCount - 1)
+	  return -ENXIO;
+	Controller = DAC960_Controllers[ControllerNumber];
+	if (Controller == NULL)
+	  return -ENXIO;
+	CommandOpcode = UserCommand.CommandMailbox.Common.CommandOpcode;
+	DataTransferLength = UserCommand.DataTransferLength;
+	if (CommandOpcode & 0x80) return -EINVAL;
+	if (CommandOpcode == DAC960_DCDB)
+	  {
+	    ErrorCode =
+	      copy_from_user(&DCDB, UserCommand.DCDB, sizeof(DAC960_DCDB_T));
+	    if (ErrorCode != 0) goto Failure;
+	    if (!((DataTransferLength == 0 &&
+		   DCDB.Direction == DAC960_DCDB_NoDataTransfer) ||
+		  (DataTransferLength > 0 &&
+		   DCDB.Direction == DAC960_DCDB_DataTransferDeviceToSystem) ||
+		  (DataTransferLength < 0 &&
+		   DCDB.Direction == DAC960_DCDB_DataTransferSystemToDevice)))
+	      return -EINVAL;
+	    if (((DCDB.TransferLengthHigh4 << 16) | DCDB.TransferLength)
+		!= abs(DataTransferLength))
+	      return -EINVAL;
+	  }
+	if (DataTransferLength > 0)
+	  {
+	    DataTransferBuffer = kmalloc(DataTransferLength, GFP_KERNEL);
+	    if (DataTransferBuffer == NULL) return -ENOMEM;
+	    memset(DataTransferBuffer, 0, DataTransferLength);
+	  }
+	else if (DataTransferLength < 0)
+	  {
+	    DataTransferBuffer = kmalloc(-DataTransferLength, GFP_KERNEL);
+	    if (DataTransferBuffer == NULL) return -ENOMEM;
+	    ErrorCode = copy_from_user(DataTransferBuffer,
+				       UserCommand.DataTransferBuffer,
+				       -DataTransferLength);
+	    if (ErrorCode != 0) goto Failure;
+	  }
+	if (CommandOpcode == DAC960_DCDB)
+	  {
+	    while (true)
+	      {
+		DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+		if (!Controller->DirectCommandActive[DCDB.Channel]
+						    [DCDB.TargetID])
+		  Command = DAC960_AllocateCommand(Controller);
+		if (Command != NULL)
+		  Controller->DirectCommandActive[DCDB.Channel]
+						 [DCDB.TargetID] = true;
+		DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+		if (Command != NULL) break;
+		sleep_on(&Controller->CommandWaitQueue);
+	      }
+	    DAC960_ClearCommand(Command);
+	    Command->CommandType = DAC960_ImmediateCommand;
+	    memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox,
+		   sizeof(DAC960_CommandMailbox_T));
+	    Command->CommandMailbox.Type3.BusAddress = Virtual_to_Bus(&DCDB);
+	    DCDB.BusAddress = Virtual_to_Bus(DataTransferBuffer);
+	  }
+	else
+	  {
+	    while (true)
+	      {
+		DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+		Command = DAC960_AllocateCommand(Controller);
+		DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+		if (Command != NULL) break;
+		sleep_on(&Controller->CommandWaitQueue);
+	      }
+	    DAC960_ClearCommand(Command);
+	    Command->CommandType = DAC960_ImmediateCommand;
+	    memcpy(&Command->CommandMailbox, &UserCommand.CommandMailbox,
+		   sizeof(DAC960_CommandMailbox_T));
+	    if (DataTransferBuffer != NULL)
+	      Command->CommandMailbox.Type3.BusAddress =
+		Virtual_to_Bus(DataTransferBuffer);
+	  }
+	DAC960_ExecuteCommand(Command);
+	CommandStatus = Command->CommandStatus;
+	DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+	DAC960_DeallocateCommand(Command);
+	DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+	if (CommandStatus == DAC960_NormalCompletion &&
+	    DataTransferLength > 0)
+	  {
+	    ErrorCode = copy_to_user(UserCommand.DataTransferBuffer,
+				     DataTransferBuffer, DataTransferLength);
+	    if (ErrorCode != 0) goto Failure;
+	  }
+	if (CommandOpcode == DAC960_DCDB)
+	  {
+	    Controller->DirectCommandActive[DCDB.Channel]
+					   [DCDB.TargetID] = false;
+	    ErrorCode =
+	      copy_to_user(UserCommand.DCDB, &DCDB, sizeof(DAC960_DCDB_T));
+	    if (ErrorCode != 0) goto Failure;
+	  }
+	ErrorCode = CommandStatus;
+      Failure:
+	if (DataTransferBuffer != NULL)
+	  kfree(DataTransferBuffer);
+	return ErrorCode;
+      }
+    }
+  return -EINVAL;
+}
+
+
+/*
+  DAC960_KernelIOCTL is the Kernel IOCTL Function for the DAC960 Driver.
+*/
+
+int DAC960_KernelIOCTL(unsigned int Request, void *Argument)
+{
+  switch (Request)
+    {
+    case DAC960_IOCTL_GET_CONTROLLER_COUNT:
+      return DAC960_ControllerCount;
+    case DAC960_IOCTL_GET_CONTROLLER_INFO:
+      {
+	DAC960_ControllerInfo_T *ControllerInfo =
+	  (DAC960_ControllerInfo_T *) Argument;
+	DAC960_Controller_T *Controller;
+	int ControllerNumber;
+	if (ControllerInfo == NULL) return -EINVAL;
+	ControllerNumber = ControllerInfo->ControllerNumber;
+	if (ControllerNumber < 0 ||
+	    ControllerNumber > DAC960_ControllerCount - 1)
+	  return -ENXIO;
+	Controller = DAC960_Controllers[ControllerNumber];
+	if (Controller == NULL)
+	  return -ENXIO;
+	memset(ControllerInfo, 0, sizeof(DAC960_ControllerInfo_T));
+	ControllerInfo->ControllerNumber = ControllerNumber;
+	ControllerInfo->PCI_Bus = Controller->Bus;
+	ControllerInfo->PCI_Device = Controller->Device;
+	ControllerInfo->PCI_Function = Controller->Function;
+	ControllerInfo->IRQ_Channel = Controller->IRQ_Channel;
+	ControllerInfo->Channels = Controller->Channels;
+	ControllerInfo->PCI_Address = Controller->PCI_Address;
+	strcpy(ControllerInfo->ModelName, Controller->ModelName);
+	strcpy(ControllerInfo->FirmwareVersion, Controller->FirmwareVersion);
+	return 0;
+      }
+    case DAC960_IOCTL_EXECUTE_COMMAND:
+      {
+	DAC960_KernelCommand_T *KernelCommand =
+	  (DAC960_KernelCommand_T *) Argument;
+	DAC960_Controller_T *Controller;
+	DAC960_Command_T *Command = NULL;
+	DAC960_CommandOpcode_T CommandOpcode;
+	DAC960_DCDB_T *DCDB = NULL;
+	ProcessorFlags_T ProcessorFlags;
+	int ControllerNumber, DataTransferLength;
+	unsigned char *DataTransferBuffer = NULL;
+	if (KernelCommand == NULL) return -EINVAL;
+	ControllerNumber = KernelCommand->ControllerNumber;
+	if (ControllerNumber < 0 ||
+	    ControllerNumber > DAC960_ControllerCount - 1)
+	  return -ENXIO;
+	Controller = DAC960_Controllers[ControllerNumber];
+	if (Controller == NULL)
+	  return -ENXIO;
+	CommandOpcode = KernelCommand->CommandMailbox.Common.CommandOpcode;
+	DataTransferLength = KernelCommand->DataTransferLength;
+	DataTransferBuffer = KernelCommand->DataTransferBuffer;
+	if (CommandOpcode & 0x80) return -EINVAL;
+	if (CommandOpcode == DAC960_DCDB)
+	  {
+	    DCDB = KernelCommand->DCDB;
+	    if (!((DataTransferLength == 0 &&
+		   DCDB->Direction == DAC960_DCDB_NoDataTransfer) ||
+		  (DataTransferLength > 0 &&
+		   DCDB->Direction == DAC960_DCDB_DataTransferDeviceToSystem) ||
+		  (DataTransferLength < 0 &&
+		   DCDB->Direction == DAC960_DCDB_DataTransferSystemToDevice)))
+	      return -EINVAL;
+	    if (((DCDB->TransferLengthHigh4 << 16) | DCDB->TransferLength)
+		!= abs(DataTransferLength))
+	      return -EINVAL;
+	  }
+	if (DataTransferLength != 0 && DataTransferBuffer == NULL)
+	  return -EINVAL;
+	if (DataTransferLength > 0)
+	  memset(DataTransferBuffer, 0, DataTransferLength);
+	if (CommandOpcode == DAC960_DCDB)
+	  {
+	    DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+	    if (!Controller->DirectCommandActive[DCDB->Channel]
+						[DCDB->TargetID])
+	      Command = DAC960_AllocateCommand(Controller);
+	    if (Command == NULL)
+	      {
+		DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+		return -EBUSY;
+	      }
+	    else Controller->DirectCommandActive[DCDB->Channel]
+						[DCDB->TargetID] = true;
+	    DAC960_ClearCommand(Command);
+	    Command->CommandType = DAC960_QueuedCommand;
+	    memcpy(&Command->CommandMailbox, &KernelCommand->CommandMailbox,
+		   sizeof(DAC960_CommandMailbox_T));
+	    Command->CommandMailbox.Type3.BusAddress = Virtual_to_Bus(DCDB);
+	    Command->KernelCommand = KernelCommand;
+	    DCDB->BusAddress = Virtual_to_Bus(DataTransferBuffer);
+	    DAC960_QueueCommand(Command);
+	    DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+	  }
+	else
+	  {
+	    DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+	    Command = DAC960_AllocateCommand(Controller);
+	    if (Command == NULL)
+	      {
+		DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+		return -EBUSY;
+	      }
+	    DAC960_ClearCommand(Command);
+	    Command->CommandType = DAC960_QueuedCommand;
+	    memcpy(&Command->CommandMailbox, &KernelCommand->CommandMailbox,
+		   sizeof(DAC960_CommandMailbox_T));
+	    if (DataTransferBuffer != NULL)
+	      Command->CommandMailbox.Type3.BusAddress =
+		Virtual_to_Bus(DataTransferBuffer);
+	    Command->KernelCommand = KernelCommand;
+	    DAC960_QueueCommand(Command);
+	    DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+	  }
+	return 0;
+      }
+    }
+  return -EINVAL;
+}
+
+
+/*
+  DAC960_GenericDiskInit is the Generic Disk Information Initialization
+  Function for the DAC960 Driver.
+*/
+
+static void DAC960_InitializeGenericDiskInfo(GenericDiskInfo_T *GenericDiskInfo)
+{
+  DAC960_Controller_T *Controller =
+    (DAC960_Controller_T *) GenericDiskInfo->real_devices;
+  DAC960_LogicalDriveInformation_T *LogicalDriveInformation =
+    Controller->LogicalDriveInformation
+		[Controller->LogicalDriveInformationIndex];
+  int LogicalDriveNumber;
+  for (LogicalDriveNumber = 0;
+       LogicalDriveNumber < Controller->LogicalDriveCount;
+       LogicalDriveNumber++)
+    GenericDiskInfo->part[DAC960_MinorNumber(LogicalDriveNumber, 0)].nr_sects =
+      LogicalDriveInformation[LogicalDriveNumber].LogicalDriveSize;
+}
+
+
+/*
+  DAC960_Message prints Driver Messages.
+*/
+
+static void DAC960_Message(DAC960_MessageLevel_T MessageLevel,
+			   char *Format,
+			   DAC960_Controller_T *Controller,
+			   ...)
+{
+  static char Buffer[DAC960_LineBufferSize];
+  static boolean BeginningOfLine = true;
+  va_list Arguments;
+  int Length = 0;
+  va_start(Arguments, Controller);
+  Length = vsprintf(Buffer, Format, Arguments);
+  va_end(Arguments);
+  if (Controller == NULL)
+    printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	   DAC960_ControllerCount, Buffer);
+  else if (MessageLevel == DAC960_AnnounceLevel ||
+	   MessageLevel == DAC960_InfoLevel)
+    {
+      if (!Controller->ControllerInitialized)
+	{
+	  strcpy(&Controller->InitialStatusBuffer[
+		    Controller->InitialStatusLength], Buffer);
+	  Controller->InitialStatusLength += Length;
+	  if (MessageLevel == DAC960_AnnounceLevel)
+	    {
+	      static int AnnouncementLines = 0;
+	      if (++AnnouncementLines <= 2)
+		printk("%sDAC960: %s", DAC960_MessageLevelMap[MessageLevel],
+		       Buffer);
+	    }
+	  else
+	    {
+	      if (BeginningOfLine)
+		{
+		  if (Buffer[0] != '\n' || Length > 1)
+		    printk("%sDAC960#%d: %s",
+			   DAC960_MessageLevelMap[MessageLevel],
+			   Controller->ControllerNumber, Buffer);
+		}
+	      else printk("%s", Buffer);
+	    }
+	}
+      else
+	{
+	  strcpy(&Controller->CurrentStatusBuffer[
+		    Controller->CurrentStatusLength], Buffer);
+	  Controller->CurrentStatusLength += Length;
+	}
+    }
+  else if (MessageLevel == DAC960_ProgressLevel)
+    {
+      strcpy(Controller->RebuildProgressBuffer, Buffer);
+      Controller->RebuildProgressLength = Length;
+      if (Controller->EphemeralProgressMessage)
+	{
+	  if (jiffies - Controller->LastProgressReportTime
+	      >= DAC960_ProgressReportingInterval)
+	    {
+	      printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+		     Controller->ControllerNumber, Buffer);
+	      Controller->LastProgressReportTime = jiffies;
+	    }
+	}
+      else printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+		  Controller->ControllerNumber, Buffer);
+    }
+  else if (MessageLevel == DAC960_UserCriticalLevel)
+    {
+      strcpy(&Controller->UserStatusBuffer[Controller->UserStatusLength],
+	     Buffer);
+      Controller->UserStatusLength += Length;
+      if (Buffer[0] != '\n' || Length > 1)
+	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	       Controller->ControllerNumber, Buffer);
+    }
+  else
+    {
+      if (BeginningOfLine)
+	printk("%sDAC960#%d: %s", DAC960_MessageLevelMap[MessageLevel],
+	       Controller->ControllerNumber, Buffer);
+      else printk("%s", Buffer);
+    }
+  BeginningOfLine = (Buffer[Length-1] == '\n');
+}
+
+
+/*
+  DAC960_ParsePhysicalDrive parses spaces followed by a Physical Drive
+  Channel:TargetID specification from a User Command string.  It updates
+  Channel and TargetID and returns true on success and returns false otherwise.
+*/
+
+static boolean DAC960_ParsePhysicalDrive(DAC960_Controller_T *Controller,
+					 char *UserCommandString,
+					 unsigned char *Channel,
+					 unsigned char *TargetID)
+{
+  char *NewUserCommandString = UserCommandString;
+  unsigned long XChannel, XTargetID;
+  while (*UserCommandString == ' ') UserCommandString++;
+  if (UserCommandString == NewUserCommandString)
+    return false;
+  XChannel = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != ':' ||
+      XChannel >= Controller->Channels)
+    return false;
+  UserCommandString = ++NewUserCommandString;
+  XTargetID = simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != '\0' ||
+      XTargetID >= DAC960_MaxTargets)
+    return false;
+  *Channel = XChannel;
+  *TargetID = XTargetID;
+  return true;
+}
+
+
+/*
+  DAC960_ParseLogicalDrive parses spaces followed by a Logical Drive Number
+  specification from a User Command string.  It updates LogicalDriveNumber and
+  returns true on success and returns false otherwise.
+*/
+
+static boolean DAC960_ParseLogicalDrive(DAC960_Controller_T *Controller,
+					char *UserCommandString,
+					unsigned char *LogicalDriveNumber)
+{
+  char *NewUserCommandString = UserCommandString;
+  unsigned long XLogicalDriveNumber;
+  while (*UserCommandString == ' ') UserCommandString++;
+  if (UserCommandString == NewUserCommandString)
+    return false;
+  XLogicalDriveNumber =
+    simple_strtoul(UserCommandString, &NewUserCommandString, 10);
+  if (NewUserCommandString == UserCommandString ||
+      *NewUserCommandString != '\0' ||
+      XLogicalDriveNumber >= Controller->LogicalDriveCount)
+    return false;
+  *LogicalDriveNumber = XLogicalDriveNumber;
+  return true;
+}
+
+
+/*
+  DAC960_SetDeviceState sets the Device State for a Physical Drive.
+*/
+
+static void DAC960_SetDeviceState(DAC960_Controller_T *Controller,
+				  DAC960_Command_T *Command,
+				  unsigned char Channel,
+				  unsigned char TargetID,
+				  DAC960_PhysicalDeviceState_T DeviceState,
+				  const char *DeviceStateString)
+{
+  DAC960_CommandMailbox_T *CommandMailbox = &Command->CommandMailbox;
+  CommandMailbox->Type3D.CommandOpcode = DAC960_StartDevice;
+  CommandMailbox->Type3D.Channel = Channel;
+  CommandMailbox->Type3D.TargetID = TargetID;
+  CommandMailbox->Type3D.DeviceState = DeviceState;
+  CommandMailbox->Type3D.Modifier = 0;
+  DAC960_ExecuteCommand(Command);
+  switch (Command->CommandStatus)
+    {
+    case DAC960_NormalCompletion:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Succeeded\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_UnableToStartDevice:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Unable to Start Device\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_NoDeviceAtAddress:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "No Device at Address\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_InvalidChannelOrTargetOrModifier:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Invalid Channel or Target or Modifier\n",
+			  Controller, DeviceStateString, Channel, TargetID);
+      break;
+    case DAC960_ChannelBusy:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Channel Busy\n", Controller,
+			  DeviceStateString, Channel, TargetID);
+      break;
+    default:
+      DAC960_UserCritical("%s of Physical Drive %d:%d Failed - "
+			  "Unexpected Status %04X\n", Controller,
+			  DeviceStateString, Channel, TargetID,
+			  Command->CommandStatus);
+      break;
+    }
+}
+
+
+/*
+  DAC960_ExecuteUserCommand executes a User Command.
+*/
+
+static boolean DAC960_ExecuteUserCommand(DAC960_Controller_T *Controller,
+					 char *UserCommand)
+{
+  DAC960_Command_T *Command;
+  DAC960_CommandMailbox_T *CommandMailbox;
+  ProcessorFlags_T ProcessorFlags;
+  unsigned char Channel, TargetID, LogicalDriveNumber;
+  while (true)
+    {
+      DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+      Command = DAC960_AllocateCommand(Controller);
+      DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+      if (Command != NULL) break;
+      sleep_on(&Controller->CommandWaitQueue);
+    }
+  Controller->UserStatusLength = 0;
+  DAC960_ClearCommand(Command);
+  Command->CommandType = DAC960_ImmediateCommand;
+  CommandMailbox = &Command->CommandMailbox;
+  if (strcmp(UserCommand, "flush-cache") == 0)
+    {
+      CommandMailbox->Type3.CommandOpcode = DAC960_Flush;
+      DAC960_ExecuteCommand(Command);
+      DAC960_UserCritical("Cache Flush Completed\n", Controller);
+    }
+  else if (strncmp(UserCommand, "kill", 4) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[4],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState != DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Dead, "Kill");
+      else DAC960_UserCritical("Kill of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "make-online", 11) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[11],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState == DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Online, "Make Online");
+      else DAC960_UserCritical("Make Online of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+
+    }
+  else if (strncmp(UserCommand, "make-standby", 12) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[12],
+				     &Channel, &TargetID))
+    {
+      DAC960_DeviceState_T *DeviceState =
+	&Controller->DeviceState[Controller->DeviceStateIndex]
+				[Channel][TargetID];
+      if (DeviceState->Present &&
+	  DeviceState->DeviceType == DAC960_DiskType &&
+	  DeviceState->DeviceState == DAC960_Device_Dead)
+	DAC960_SetDeviceState(Controller, Command, Channel, TargetID,
+			      DAC960_Device_Standby, "Make Standby");
+      else DAC960_UserCritical("Make Standby of Physical Drive %d:%d Illegal\n",
+			       Controller, Channel, TargetID);
+    }
+  else if (strncmp(UserCommand, "rebuild", 7) == 0 &&
+	   DAC960_ParsePhysicalDrive(Controller, &UserCommand[7],
+				     &Channel, &TargetID))
+    {
+      CommandMailbox->Type3D.CommandOpcode = DAC960_RebuildAsync;
+      CommandMailbox->Type3D.Channel = Channel;
+      CommandMailbox->Type3D.TargetID = TargetID;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Initiated\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_AttemptToRebuildOnlineDrive:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Attempt to Rebuild Online or "
+			      "Unresponsive Drive\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_NewDiskFailedDuringRebuild:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "New Disk Failed During Rebuild\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_InvalidDeviceAddress:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Invalid Device Address\n",
+			      Controller, Channel, TargetID);
+	  break;
+	case DAC960_RebuildOrCheckAlreadyInProgress:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Rebuild or Consistency Check Already "
+			      "in Progress\n", Controller, Channel, TargetID);
+	  break;
+	default:
+	  DAC960_UserCritical("Rebuild of Physical Drive %d:%d Failed - "
+			      "Unexpected Status %04X\n", Controller,
+			      Channel, TargetID, Command->CommandStatus);
+	  break;
+	}
+    }
+  else if (strncmp(UserCommand, "check-consistency", 17) == 0 &&
+	   DAC960_ParseLogicalDrive(Controller, &UserCommand[17],
+				    &LogicalDriveNumber))
+    {
+      CommandMailbox->Type3C.CommandOpcode = DAC960_CheckConsistencyAsync;
+      CommandMailbox->Type3C.LogicalDriveNumber = LogicalDriveNumber;
+      CommandMailbox->Type3C.AutoRestore = true;
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Initiated\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_DependentDiskIsDead:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Dependent Physical Drive is DEAD\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_InvalidOrNonredundantLogicalDrive:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Invalid or Nonredundant Logical Drive\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	case DAC960_RebuildOrCheckAlreadyInProgress:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - Rebuild or "
+			      "Consistency Check Already in Progress\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber);
+	  break;
+	default:
+	  DAC960_UserCritical("Consistency Check of Logical Drive %d "
+			      "(/dev/rd/c%dd%d) Failed - "
+			      "Unexpected Status %04X\n",
+			      Controller, LogicalDriveNumber,
+			      Controller->ControllerNumber,
+			      LogicalDriveNumber, Command->CommandStatus);
+	  break;
+	}
+    }
+  else if (strcmp(UserCommand, "cancel-rebuild") == 0 ||
+	   strcmp(UserCommand, "cancel-consistency-check") == 0)
+    {
+      unsigned char OldRebuildRateConstant;
+      CommandMailbox->Type3R.CommandOpcode = DAC960_RebuildControl;
+      CommandMailbox->Type3R.RebuildRateConstant = 0xFF;
+      CommandMailbox->Type3R.BusAddress =
+	Virtual_to_Bus(&OldRebuildRateConstant);
+      DAC960_ExecuteCommand(Command);
+      switch (Command->CommandStatus)
+	{
+	case DAC960_NormalCompletion:
+	  DAC960_UserCritical("Rebuild or Consistency Check Cancelled\n",
+			      Controller);
+	  break;
+	default:
+	  DAC960_UserCritical("Cancellation of Rebuild or "
+			      "Consistency Check Failed - "
+			      "Unexpected Status %04X\n",
+			      Controller, Command->CommandStatus);
+	  break;
+	}
+    }
+  else DAC960_UserCritical("Illegal User Command: '%s'\n",
+			   Controller, UserCommand);
+  DAC960_AcquireControllerLock(Controller, &ProcessorFlags);
+  DAC960_DeallocateCommand(Command);
+  DAC960_ReleaseControllerLock(Controller, &ProcessorFlags);
+  return true;
+}
+
+
+/*
+  DAC960_ProcReadStatus implements reading /proc/rd/status.
+*/
+
+static ssize_t DAC960_ProcReadStatus(char *Page, char **Start,
+				     off_t Offset, int Count,
+				     int *EOF, void *Data)
+{
+  char *StatusMessage = "OK\n";
+  int ControllerNumber, BytesAvailable;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      DAC960_Enquiry_T *Enquiry;
+      if (Controller == NULL) continue;
+      Enquiry = &Controller->Enquiry[Controller->EnquiryIndex];
+      if (Enquiry->CriticalLogicalDriveCount > 0 ||
+	  Enquiry->OfflineLogicalDriveCount > 0 ||
+	  Enquiry->DeadDriveCount > 0)
+	{
+	  StatusMessage = "ALERT\n";
+	  break;
+	}
+    }
+  BytesAvailable = strlen(StatusMessage) - Offset;
+  if (Count >= BytesAvailable)
+    {
+      Count = BytesAvailable;
+      *EOF = true;
+    }
+  if (Count <= 0) return 0;
+  memcpy(Page, &StatusMessage[Offset], Count);
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadInitialStatus implements reading /proc/rd/cN/initial_status.
+*/
+
+static ssize_t DAC960_ProcReadInitialStatus(char *Page, char **Start,
+					    off_t Offset, int Count,
+					    int *EOF, void *Data)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
+  int BytesAvailable = Controller->InitialStatusLength - Offset;
+  if (Count >= BytesAvailable)
+    {
+      Count = BytesAvailable;
+      *EOF = true;
+    }
+  if (Count <= 0) return 0;
+  memcpy(Page, &Controller->InitialStatusBuffer[Offset], Count);
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadCurrentStatus implements reading /proc/rd/cN/current_status.
+*/
+
+static ssize_t DAC960_ProcReadCurrentStatus(char *Page, char **Start,
+					    off_t Offset, int Count,
+					    int *EOF, void *Data)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
+  int BytesAvailable;
+  Controller->CurrentStatusLength = 0;
+  DAC960_AnnounceDriver(Controller);
+  DAC960_ReportControllerConfiguration(Controller);
+  Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+  Controller->CurrentStatusBuffer[Controller->CurrentStatusLength++] = ' ';
+  if (Controller->RebuildProgressLength > 0)
+    {
+      strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength],
+	     Controller->RebuildProgressBuffer);
+      Controller->CurrentStatusLength += Controller->RebuildProgressLength;
+    }
+  else
+    {
+      char *StatusMessage = "No Rebuild or Consistency Check in Progress\n";
+      strcpy(&Controller->CurrentStatusBuffer[Controller->CurrentStatusLength],
+	     StatusMessage);
+      Controller->CurrentStatusLength += strlen(StatusMessage);
+    }
+  BytesAvailable = Controller->CurrentStatusLength - Offset;
+  if (Count >= BytesAvailable)
+    {
+      Count = BytesAvailable;
+      *EOF = true;
+    }
+  if (Count <= 0) return 0;
+  memcpy(Page, &Controller->CurrentStatusBuffer[Offset], Count);
+  return Count;
+}
+
+
+/*
+  DAC960_ProcReadUserCommand implements reading /proc/rd/cN/user_command.
+*/
+
+static ssize_t DAC960_ProcReadUserCommand(char *Page, char **Start,
+					  off_t Offset, int Count,
+					  int *EOF, void *Data)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
+  int BytesAvailable = Controller->UserStatusLength - Offset;
+  if (Count >= BytesAvailable)
+    {
+      Count = BytesAvailable;
+      *EOF = true;
+    }
+  if (Count <= 0) return 0;
+  memcpy(Page, &Controller->UserStatusBuffer[Offset], Count);
+  return Count;
+}
+
+
+/*
+  DAC960_ProcWriteUserCommand implements writing /proc/rd/cN/user_command.
+*/
+
+static ssize_t DAC960_ProcWriteUserCommand(File_T *File, const char *Buffer,
+					   unsigned long Count, void *Data)
+{
+  DAC960_Controller_T *Controller = (DAC960_Controller_T *) Data;
+  char CommandBuffer[80];
+  int Length;
+  if (Count > sizeof(CommandBuffer)-1) return -EINVAL;
+  copy_from_user(CommandBuffer, Buffer, Count);
+  CommandBuffer[Count] = '\0';
+  Length = strlen(CommandBuffer);
+  if (CommandBuffer[Length-1] == '\n')
+    CommandBuffer[--Length] = '\0';
+  return (DAC960_ExecuteUserCommand(Controller, CommandBuffer)
+	  ? Count : -EBUSY);
+}
+
+
+/*
+  DAC960_CreateProcEntries creates the /proc/rd/... entries for the DAC960
+  Driver.
+*/
+
+static void DAC960_CreateProcEntries(void)
+{
+  static PROC_DirectoryEntry_T StatusProcEntry;
+  int ControllerNumber;
+  DAC960_ProcDirectoryEntry.name = "rd";
+  DAC960_ProcDirectoryEntry.namelen = strlen(DAC960_ProcDirectoryEntry.name);
+  DAC960_ProcDirectoryEntry.mode = S_IFDIR | S_IRUGO | S_IXUGO;
+  proc_register(&proc_root, &DAC960_ProcDirectoryEntry);
+  StatusProcEntry.name = "status";
+  StatusProcEntry.namelen = strlen(StatusProcEntry.name);
+  StatusProcEntry.mode = S_IFREG | S_IRUGO;
+  StatusProcEntry.read_proc = DAC960_ProcReadStatus;
+  proc_register(&DAC960_ProcDirectoryEntry, &StatusProcEntry);
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      PROC_DirectoryEntry_T *ControllerProcEntry, *InitialStatusProcEntry;
+      PROC_DirectoryEntry_T *CurrentStatusProcEntry, *UserCommandProcEntry;
+      if (Controller == NULL) continue;
+      ControllerProcEntry = &Controller->ControllerProcEntry;
+      ControllerProcEntry->name = Controller->ControllerName;
+      ControllerProcEntry->namelen = strlen(ControllerProcEntry->name);
+      ControllerProcEntry->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+      proc_register(&DAC960_ProcDirectoryEntry, ControllerProcEntry);
+      InitialStatusProcEntry = &Controller->InitialStatusProcEntry;
+      InitialStatusProcEntry->name = "initial_status";
+      InitialStatusProcEntry->namelen = strlen(InitialStatusProcEntry->name);
+      InitialStatusProcEntry->mode = S_IFREG | S_IRUGO;
+      InitialStatusProcEntry->data = Controller;
+      InitialStatusProcEntry->read_proc = DAC960_ProcReadInitialStatus;
+      proc_register(ControllerProcEntry, InitialStatusProcEntry);
+      CurrentStatusProcEntry = &Controller->CurrentStatusProcEntry;
+      CurrentStatusProcEntry->name = "current_status";
+      CurrentStatusProcEntry->namelen = strlen(CurrentStatusProcEntry->name);
+      CurrentStatusProcEntry->mode = S_IFREG | S_IRUGO;
+      CurrentStatusProcEntry->data = Controller;
+      CurrentStatusProcEntry->read_proc = DAC960_ProcReadCurrentStatus;
+      proc_register(ControllerProcEntry, CurrentStatusProcEntry);
+      UserCommandProcEntry = &Controller->UserCommandProcEntry;
+      UserCommandProcEntry->name = "user_command";
+      UserCommandProcEntry->namelen = strlen(UserCommandProcEntry->name);
+      UserCommandProcEntry->mode = S_IFREG | S_IWUSR | S_IRUSR;
+      UserCommandProcEntry->data = Controller;
+      UserCommandProcEntry->read_proc = DAC960_ProcReadUserCommand;
+      UserCommandProcEntry->write_proc = DAC960_ProcWriteUserCommand;
+      proc_register(ControllerProcEntry, UserCommandProcEntry);
+    }
+}
+
+
+/*
+  DAC960_DestroyProcEntries destroys the /proc/rd/... entries for the DAC960
+  Driver.
+*/
+
+static void DAC960_DestroyProcEntries(void)
+{
+  proc_unregister(&proc_root, DAC960_ProcDirectoryEntry.low_ino);
+}
+
+
+/*
+  Include Module support if requested.
+*/
+
+#ifdef MODULE
+
+
+int init_module(void)
+{
+  int ControllerNumber, LogicalDriveNumber;
+  DAC960_Initialize();
+  if (DAC960_ActiveControllerCount == 0) return -1;
+  for (ControllerNumber = 0;
+       ControllerNumber < DAC960_ControllerCount;
+       ControllerNumber++)
+    {
+      DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
+      if (Controller == NULL) continue;
+      DAC960_InitializeGenericDiskInfo(&Controller->GenericDiskInfo);
+      for (LogicalDriveNumber = 0;
+	   LogicalDriveNumber < Controller->LogicalDriveCount;
+	   LogicalDriveNumber++)
+	resetup_one_dev(&Controller->GenericDiskInfo, LogicalDriveNumber);
+    }
+  return 0;
+}
+
+
+void cleanup_module(void)
+{
+  DAC960_Finalize(&DAC960_NotifierBlock, SYS_RESTART, NULL);
+}
+
+
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)