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