summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/compaq/cpqarray.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/miniport/compaq/cpqarray.c3819
1 files changed, 3819 insertions, 0 deletions
diff --git a/private/ntos/miniport/compaq/cpqarray.c b/private/ntos/miniport/compaq/cpqarray.c
new file mode 100644
index 000000000..b5a0219d3
--- /dev/null
+++ b/private/ntos/miniport/compaq/cpqarray.c
@@ -0,0 +1,3819 @@
+/*++
+
+Copyright (c) 1993-4 Microsoft Corporation
+
+Module Name:
+
+ cpqarray.c
+
+Abstract:
+
+ This is the device driver for the Compaq Intelligent Disk Array.
+
+Authors:
+
+ Mike Glass (mglass)
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+ Compaq Information Manager (CIM) support was developed by Tom Bonola and
+ Tom Woller, courtesy of Compaq Computer Corporation.
+
+Revision History:
+
+--*/
+
+#ifndef BYTE
+#define BYTE unsigned char
+#endif
+
+#ifndef WORD
+#define WORD unsigned short
+#endif
+
+#ifndef DWORD
+#define DWORD unsigned long
+#endif
+
+#ifndef INT
+#define INT int
+#endif
+
+#ifndef STATIC
+#if DBG
+#define STATIC
+#else
+#define STATIC static
+#endif
+#endif
+
+
+
+#include "miniport.h"
+#include "scsi.h"
+#include <ntddscsi.h>
+#include <scsireg.h>
+#include <cpqsczmp.h> // Compaq SCSI M&P definitions
+#include "cpqarray.h"
+#include "pcibios.h"
+
+
+//
+// Adapter storage area
+//
+
+typedef struct _DEVICE_EXTENSION {
+
+ //
+ // Requests needing restarts
+ //
+
+ PCOMMAND_LIST RestartRequests;
+
+ //
+ // IDA BMIC registers
+ //
+
+ PIDA_CONTROLLER Bmic;
+ PULONG CPFIFO;
+ PULONG CCFIFO;
+ PULONG InterruptMask;
+ PULONG InterruptStatus;
+ PULONG InterruptPending;
+ HBA_CONFIGURATION HBAConfiguration; //Memory mapped base
+ ULONG BaseIOAddress; // I/O space accessible, not used.
+ PEISAPCI_CONTROLLER eisapci;
+ ULONG PCIoff;
+
+ //
+ // Noncached extension for identify commands
+ //
+
+ PVOID IdentifyBuffer;
+
+ //
+ // Number of logical drives
+ //
+
+ ULONG NumberOfLogicalDrives;
+ UCHAR SectorShift; //setup to 9
+ ULONG EisaId;
+ UCHAR IrqLevel;
+ IDENTIFY_CONTROLLER IdentifyData; // permanent controller info storage
+
+} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
+
+//
+// Drive storage area
+//
+
+typedef struct _LOGICAL_UNIT_EXTENSION {
+
+ //
+ // Drive indentify data.
+ //
+
+ IDENTIFY_LOGICAL_DRIVE IdentifyData;
+ SENSE_CONFIGURATION SenseData; // sense data for logical drive
+
+} LOGICAL_UNIT_EXTENSION, *PLOGICAL_UNIT_EXTENSION;
+
+#include "cpqsmngr.h"
+
+VOID
+BuildFlushDisable(
+ IN PDEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+);
+
+ULONG
+IdaProcessIoctl(
+ IN PDEVICE_EXTENSION pIdaDeviceExtension,
+ PVOID pIoctlBuffer,
+ IN PSCSI_REQUEST_BLOCK Srb
+);
+ULONG
+BuildCIMList(
+ IN PDEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+);
+VOID
+IdaMoveMemory(
+ OUT PUCHAR pDestination,
+ IN PUCHAR pSource,
+ IN ULONG ulLength
+);
+
+BOOLEAN
+IdaStrCmp(
+ IN PUCHAR p1,
+ IN PUCHAR p2
+);
+
+VOID
+IdaEnableInts(IN PDEVICE_EXTENSION);
+
+
+VOID
+IdaDisableInts(IN PDEVICE_EXTENSION);
+
+
+BOOLEAN
+SearchEisaBus(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from IdaFindAdapter if the system fails to
+ pass in predetermined configuration data. It searches the EISA bus
+ data looking for information about controllers that this driver
+ supports.
+
+Arguments:
+
+ HwDeviceExtension - Address of adapter storage area.
+ Context - Used to track how many EISA slots have been searched.
+ ConfigInfo - System template for configuration information.
+
+Return Value:
+
+ TRUE - If Compaq IDA controller found.
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ ULONG length;
+ ULONG eisaSlotNumber;
+ PACCESS_RANGE accessRange;
+ PCM_EISA_SLOT_INFORMATION slotInformation;
+ PCM_EISA_FUNCTION_INFORMATION functionInformation;
+ ULONG numberOfFunctions;
+
+ //
+ // Get pointer to first configuration info structure access range.
+ //
+
+ accessRange = &((*(ConfigInfo->AccessRanges))[0]);
+
+ for (eisaSlotNumber=*((PULONG)Context);
+ eisaSlotNumber<16;
+ eisaSlotNumber++) {
+
+ //
+ // Get pointer to bus data for this EISA slot.
+ //
+
+ length = ScsiPortGetBusData(HwDeviceExtension,
+ EisaConfiguration,
+ ConfigInfo->SystemIoBusNumber,
+ eisaSlotNumber,
+ &slotInformation,
+ 0);
+
+ if (!length) {
+ continue;
+ }
+
+ //
+ // Check for Compaq IDA board id.
+ //
+
+ if ((slotInformation->CompressedId & 0x00FFFFFF) == 0x0040110E) {
+ break;
+ }
+ }
+
+ //
+ // Check if all slots searched.
+ //
+
+ if (eisaSlotNumber == 16) {
+ return FALSE;
+ }
+
+ //
+ // Set up default port address.
+ //
+
+ accessRange->RangeStart.LowPart =
+ (eisaSlotNumber * 0x1000) + 0x0C80;
+ accessRange->RangeLength = sizeof(IDA_CONTROLLER);
+
+ accessRange++;
+
+ ConfigInfo->SlotNumber = eisaSlotNumber;
+
+ //
+ // Get the number of EISA configuration functions returned in bus data.
+ //
+
+ numberOfFunctions = slotInformation->NumberFunctions;
+
+ //
+ // Get first configuration record.
+ //
+
+ functionInformation =
+ (PCM_EISA_FUNCTION_INFORMATION)(slotInformation + 1);
+
+ //
+ // Walk configuration records to find EISA IRQ.
+ //
+
+ for (; 0 < numberOfFunctions; numberOfFunctions--, functionInformation++) {
+
+ //
+ // Check for IRQ.
+ //
+
+ if (functionInformation->FunctionFlags & EISA_HAS_IRQ_ENTRY) {
+
+ ConfigInfo->BusInterruptLevel =
+ functionInformation->EisaIrq->ConfigurationByte.Interrupt;
+ ConfigInfo->InterruptMode = LevelSensitive;
+ }
+
+ //
+ // Check for IO ranges.
+ //
+
+ if (functionInformation->FunctionFlags & EISA_HAS_PORT_RANGE) {
+
+ PEISA_PORT_CONFIGURATION eisaPort =
+ functionInformation->EisaPort;
+
+ //
+ // Search for emulation ranges.
+ //
+
+ while (eisaPort->PortAddress) {
+
+ //
+ // Check range to determine length.
+ //
+
+ switch (eisaPort->PortAddress) {
+
+ case 0x000001f0:
+ case 0x00000170:
+
+ accessRange->RangeStart.LowPart = eisaPort->PortAddress;
+ accessRange->RangeLength = 0x0000000F;
+ break;
+
+ case 0x000003f6:
+ case 0x00000176:
+
+ accessRange->RangeStart.LowPart = eisaPort->PortAddress;
+ accessRange->RangeLength = 0x00000001;
+ break;
+ }
+
+ DebugPrint((1,
+ "CPQARRAY: SearchEisaBus: IO base %x\n",
+ eisaPort->PortAddress));
+
+ //
+ // Advance pointers to next IO range.
+ //
+
+ accessRange++;
+ eisaPort++;
+ }
+ }
+ }
+
+ //
+ // Indicate from which EISA slot to continue search.
+ //
+
+ *((PULONG)Context) = eisaSlotNumber + 1;
+
+ return TRUE;
+
+} // end SearchEisaBus()
+
+
+BOOLEAN
+IdaInitialize(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by the system during initialization to
+ prepare the controller to receive requests.
+
+Arguments:
+
+ HwDeviceExtension - Address of adapter storage area.
+
+Return Value:
+
+ TRUE
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+
+
+ if (deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER) {
+
+ {
+ ULONG tmp;
+ DebugPrint((3,"CPQARRAY: Initing DAZZLER\n"));
+ tmp = ScsiPortReadPortUlong(&deviceExtension->eisapci->CPFIFO);
+ DebugPrint((3,"IdaInitialize: Room for %x requests\n",tmp));
+ }
+
+ //
+ // Enable command completion interrupts and not channel clear
+ //
+ ScsiPortWritePortUlong(&deviceExtension->eisapci->InterruptMask,
+ IDA_PCI_FIFO_NOT_EMPTY_MASK);
+ } else {
+
+ //
+ // Enable completion interrupts.
+ //
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->InterruptControl,
+ IDA_COMPLETION_INTERRUPT_ENABLE);
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBellMask,
+ IDA_COMPLETION_INTERRUPT_ENABLE);
+ }
+
+ return TRUE;
+} // end IdaInitialize()
+
+
+BOOLEAN
+IdaInitializePCI(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This function is called by the system during initialization to
+ prepare the controller to receive requests.
+
+Arguments:
+
+ HwDeviceExtension - Address of adapter storage area.
+
+Return Value:
+
+ TRUE
+
+--*/
+
+{
+ PULONG ptmp;
+ PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+
+ DebugPrint((1,"CPQARRAY: Initing PCI DAZZLER at 0x%x\n",
+ deviceExtension->HBAConfiguration.ulBaseIOAddress));
+ //
+ // Enable command completion interrupts and not channel clear
+ //
+ ptmp = deviceExtension->InterruptMask;
+ ptmp[0] |= IDA_PCI_FIFO_NOT_EMPTY_MASK;
+
+ DebugPrint((1,"IdaInitializePCI: Room for %x requests\n",
+ *((PULONG)deviceExtension->CPFIFO)));
+
+ return TRUE;
+
+} // end IdaInitializePCI()
+
+
+
+BOOLEAN
+IdaResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine resets the controller and completes outstanding requests.
+
+Arguments:
+
+ HwDeviceExtension - Address of adapter storage area.
+ PathId - Indicates adapter to reset.
+
+Return Value:
+
+ TRUE
+
+--*/
+
+{
+
+#ifndef NCPQNO_TIMEOUT
+ //
+ // 10 second timeout is inappropriate for IDA-style controllers. The
+ // better approach is to never timeout any requests. Proper fix is
+ // to have a class driver for IDA and set the timeout for each type
+ // of controller (currently 3 minutes is a good timeout).
+ //
+/* ScsiPortLogError(HwDeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_REQUEST_TIMEOUT,
+ 1); */
+#else
+
+ //
+ // Complete all outstanding requests.
+ //
+
+ ScsiPortCompleteRequest(HwDeviceExtension,
+ (UCHAR)PathId,
+ 0xFF,
+ 0xFF,
+ SRB_STATUS_BUS_RESET);
+#endif
+
+ //
+ // Adapter ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ HwDeviceExtension,
+ NULL);
+
+ return TRUE;
+
+} // end IdaResetBus()
+
+
+VOID
+BuildCommandList(
+ IN PDEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a command list suitable for submission to the
+ Compaq IDA controller, from an SRB.
+
+Arguments:
+
+ DeviceExtension - Address of adapter storage area.
+ Srb - System request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PCOMMAND_LIST commandList = Srb->SrbExtension;
+ PVOID dataPointer;
+ ULONG physicalAddress;
+ ULONG bytesLeft;
+ ULONG descriptor;
+ ULONG length;
+
+ //
+ // Save SRB address for interrupt routine.
+ //
+
+ commandList->SrbAddress = Srb;
+
+ //
+ // Set up Command List Header.
+ //
+
+ commandList->CommandListHeader.LogicalDriveNumber = Srb->TargetId;
+
+ commandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
+
+ commandList->CommandListHeader.Flags =
+ CL_FLAGS_NOTIFY_LIST_COMPLETE + CL_FLAGS_NOTIFY_LIST_ERROR;
+
+ //
+ // Terminate request list.
+ //
+
+ commandList->RequestHeader.NextRequestOffset = 0;
+
+ //
+ // Clear request tracking flags.
+ //
+
+ commandList->Flags = 0;
+
+ //
+ // Determine command.
+ //
+
+ if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
+ commandList->RequestHeader.CommandByte = RH_COMMAND_READ;
+ } else {
+ commandList->RequestHeader.CommandByte = RH_COMMAND_WRITE;
+ }
+
+ //
+ // Reset error code.
+ //
+
+ commandList->RequestHeader.ErrorCode = 0;
+
+ //
+ // Clear reserved field.
+ //
+
+ commandList->RequestHeader.Reserved = 0;
+
+ //
+ // Determine number of blocks to transfer.
+ //
+
+ commandList->RequestHeader.BlockCount =
+ ((PCDB)Srb->Cdb)->CDB10.TransferBlocksLsb |
+ ((PCDB)Srb->Cdb)->CDB10.TransferBlocksMsb << 8;
+
+ //
+ // Determine number starting block.
+ //
+
+ commandList->RequestHeader.BlockNumber =
+ ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte3 |
+ ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte2 << 8 |
+ ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte1 << 16 |
+ ((PCDB)Srb->Cdb)->CDB10.LogicalBlockByte0 << 24;
+
+ //
+ // Build scatter/gather descriptor list.
+ //
+
+ descriptor = 0;
+ dataPointer = Srb->DataBuffer;
+ bytesLeft = Srb->DataTransferLength;
+
+ do {
+
+ //
+ // Get physical address and length of contiguous
+ // physical buffer.
+ //
+
+ physicalAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ Srb,
+ dataPointer,
+ &length));
+
+ //
+ // If length of physical memory is more
+ // than bytes left in transfer, use bytes
+ // left as final length.
+ //
+
+ if (length > bytesLeft) {
+ length = bytesLeft;
+ }
+
+ //
+ // Fill in descriptor.
+ //
+
+ commandList->SgDescriptor[descriptor].Address = physicalAddress;
+ commandList->SgDescriptor[descriptor].Length = length;
+
+ //
+ // Adjust counts.
+ //
+
+ dataPointer = (PUCHAR)dataPointer + length;
+ bytesLeft -= length;
+ descriptor++;
+
+ } while (bytesLeft);
+
+ //
+ // Calculate size of command list.
+ //
+
+ commandList->RequestHeader.ScatterGatherCount = (UCHAR) descriptor;
+ commandList->CommandListSize = sizeof(COMMAND_LIST_HEADER) +
+ sizeof(REQUEST_HEADER) +
+ sizeof(SG_DESCRIPTOR) *
+ descriptor;
+
+ return;
+
+} // end BuildCommandList()
+
+VOID
+SubmitCommandList(
+ IN PDEVICE_EXTENSION DeviceExtension,
+ IN PCOMMAND_LIST CommandList
+ )
+
+/*++
+
+Routine Description:
+
+ This routine attempts to submit a command list to the controller. If
+ the controller can't take it within a specified time interval, then the
+ request is queued to be retried after another request completes.
+
+Arguments:
+
+ DeviceExtension - Address of adapter storage area.
+ CommandList - Request to be submitted.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ULONG physicalAddress;
+ ULONG length;
+ ULONG i;
+ PULONG ptmp;
+ ULONG tmp;
+
+#ifdef DBG
+ DebugPrint((3,
+ "%x %x %x %x Cmd=%x %x Bln=%x Blc=%x SGc=%x %x SG0l=%x SG0a=%x\n",
+ CommandList->CommandListHeader.LogicalDriveNumber,
+ CommandList->CommandListHeader.RequestPriority,
+ CommandList->CommandListHeader.Flags,
+ CommandList->RequestHeader.NextRequestOffset,
+ CommandList->RequestHeader.CommandByte,
+ CommandList->RequestHeader.ErrorCode,
+ CommandList->RequestHeader.BlockNumber,
+ CommandList->RequestHeader.BlockCount,
+ CommandList->RequestHeader.ScatterGatherCount,
+ CommandList->RequestHeader.Reserved,
+ CommandList->SgDescriptor[0].Length,
+ CommandList->SgDescriptor[0].Address
+ ));
+
+ if (CommandList->RequestHeader.ScatterGatherCount > 1) {
+
+ for (i=1;i<CommandList->RequestHeader.ScatterGatherCount;i++) {
+ DebugPrint((1,
+ "%d-l=%x a=%x ",
+ i,CommandList->SgDescriptor[i].Length,
+ CommandList->SgDescriptor[i].Address
+ ));
+ }
+
+ DebugPrint((3,"\n"));
+ }
+#endif
+
+ //
+ // Check for double submission.
+ //
+
+ if (CommandList->Flags & CL_FLAGS_REQUEST_STARTED) {
+
+ DebugPrint((0,
+ "CPQARRAY: SubmitCommandList: Double submission %x\n"));
+
+ //
+ // Log this error.
+ //
+
+ ScsiPortLogError(DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 1);
+
+ return;
+ }
+
+ //
+ // Get physical address of command list.
+ //
+
+ physicalAddress =
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL,
+ CommandList,
+ &length).LowPart;
+
+ // Handle slightly different Command Header in case of SMART-2
+ // controllers. Probably should introduce union to clearify
+ // Command Header structure instead of setting .Flags to List
+ // size. Size is in dwords not bytes as was the case in SMART
+ // and previous ida controllers.
+
+ if (DeviceExtension->HBAConfiguration.bHBAModel == IDA_PCI_DAZZLER) {
+
+ DebugPrint((9,"SubmitCommandList: DAZZLER PCI card\n"));
+
+ CommandList->CommandListHeader.RequestPriority = 0;
+
+ CommandList->CommandListHeader.Flags =
+ ((CommandList->CommandListSize % 4) ? 1 : 0) +
+ (CommandList->CommandListSize >> 2);
+
+ ptmp = DeviceExtension->CPFIFO;
+
+ do {
+ tmp = ptmp[0];
+ ptmp[0] = physicalAddress;
+ } while (tmp == 0);
+
+ DebugPrint((3,
+ "SubmitCommandList: ptmp=0x%x physicalAddress=0x%x\n",
+ ptmp,physicalAddress));
+ CommandList->Flags |= CL_FLAGS_REQUEST_STARTED;
+
+ } else if ((DeviceExtension->HBAConfiguration.bHBAModel ==
+ IDA_EISA_DAZZLER) && !DeviceExtension->PCIoff ) {
+ DebugPrint((9,"SubmitCommandList: DAZZLER EISA PCI interface\n"));
+ CommandList->CommandListHeader.RequestPriority = 0;
+ CommandList->CommandListHeader.Flags =
+ ((CommandList->CommandListSize % 4) ? 1 : 0) +
+ (CommandList->CommandListSize/4);
+
+ //
+ // loop on CPFIFO until we have room to submit
+ //
+ do {
+ tmp = ScsiPortReadPortUlong(&DeviceExtension->eisapci->CPFIFO);
+ ScsiPortWritePortUlong(&DeviceExtension->eisapci->CPFIFO,
+ physicalAddress);
+ } while (tmp == 0);
+
+ CommandList->Flags |= CL_FLAGS_REQUEST_STARTED;
+ } else {
+ DebugPrint((9, "SubmitCommandList: DAZZLER EISA compatible interface\n"));
+
+ //
+ // Wait up to 100 microseconds for submission channel to clear.
+ //
+
+ for (i=0; i<100; i++) {
+
+ if (!(ScsiPortReadPortUchar(&DeviceExtension->Bmic->SystemDoorBell) &
+ SYSTEM_DOORBELL_SUBMIT_CHANNEL_CLEAR)) {
+
+ //
+ // Stall for a microsecond.
+ //
+
+ ScsiPortStallExecution(1);
+
+ } else {
+ break;
+ }
+
+ }
+
+ //
+ // Check for timeout.
+ //
+
+ if (i == 100) {
+
+ //
+ // Queue request for restart in completion routine.
+ //
+
+ DebugPrint((1,
+ "CPQARRAY: SubmitRequest: Queueing %x\n",
+ CommandList));
+
+ CommandList->Flags |= CL_FLAGS_REQUEST_QUEUED;
+ CommandList->NextEntry = DeviceExtension->RestartRequests;
+ DeviceExtension->RestartRequests = CommandList;
+
+ } else {
+
+ CommandList->Flags |= CL_FLAGS_REQUEST_STARTED;
+
+ //
+ // Reset channel clear bit to claim channel.
+ //
+
+ ScsiPortWritePortUchar(&DeviceExtension->Bmic->SystemDoorBell,
+ SYSTEM_DOORBELL_SUBMIT_CHANNEL_CLEAR);
+
+ //
+ // Write Command List physical address to BMIC mailbox.
+ //
+
+ ScsiPortWritePortUlong(&DeviceExtension->Bmic->CommandListSubmit.Address,
+ physicalAddress);
+
+ //
+ // Write Command List length to BMIC mailbox.
+ //
+
+ ScsiPortWritePortUshort(&DeviceExtension->Bmic->CommandListSubmit.Length,
+ CommandList->CommandListSize);
+
+ //
+ // Set channel busy bit to signal new Command List is available.
+ //
+
+ ScsiPortWritePortUchar(&DeviceExtension->Bmic->LocalDoorBell,
+ LOCAL_DOORBELL_COMMAND_LIST_SUBMIT);
+ }
+
+ }
+
+} // end SubmitCommandList()
+
+
+
+BOOLEAN
+IdaInterrupt(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This interrupt service routine is called by the system to process an
+ adapter interrupt. The Compaq IDA controller interrupts to signal
+ completion of a request.
+
+Arguments:
+
+ HwDeviceExtension - Address of adapter storage area.
+
+Return Value:
+
+ TRUE if adapter is interrupting.
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ ULONG physicalAddress;
+ PCOMMAND_LIST commandList;
+ PCOMMAND_LIST nextCommand;
+ PSCSI_REQUEST_BLOCK srb;
+ UCHAR status;
+ PSRB_IO_CONTROL pSrb;
+ PIDA_ERROR_BITS dataPointer;
+ PUCHAR ReturnPointer;
+ PUCHAR MovePointer;
+ UCHAR CmdListStatus;
+ PULONG ptmp;
+
+ //
+ // Verify that interrupt is from one of our controllers.
+ //
+
+ if ((deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER) &&
+ !deviceExtension->PCIoff ) {
+ DebugPrint((3,"IdaInterrupt: DAZZLER PCI mode\n"));
+
+ //
+ // The PCI interface specification calls for us to check the
+ // InterruptPending register to verify that an interrupt has
+ // been asserted at our controller. We are instead looking
+ // at the InterruptStatus masked with 0x01 to acheive the same
+ // result. We cannot use the spec's method, because at init and
+ // during a rescan at runtime, we have disabled controller
+ // interrupts and thus InterruptPending will be set to zero.
+ //
+
+ if (!(ScsiPortReadPortUlong(&deviceExtension->eisapci->InterruptStatus)
+ & IDA_PCI_COMPLETION_STATUS_ACTIVE)) {
+
+ //
+ // Interrupt is not for this controller.
+ //
+
+ return FALSE;
+ }
+
+ //
+ // Read the physical address
+ //
+
+ physicalAddress = ScsiPortReadPortUlong(&deviceExtension->
+ eisapci->CCFIFO);
+
+ DebugPrint((1,"CCFIFO=%x\n",physicalAddress));
+
+ CmdListStatus = (UCHAR)physicalAddress &
+ IDA_PCI_COMPLETION_STATUS_MASK;
+
+ if (CmdListStatus & IDA_PCI_COMPLETION_ERROR) {
+
+ DebugPrint((1,"IdaInterrupt: DAZZLER ERROR bit 0 set\n"));
+
+ //
+ // for compatibility change the status to old style error code.
+ //
+
+ CmdListStatus = RH_BAD_COMMAND_LIST;
+ }
+
+ physicalAddress &= IDA_PCI_PHYS_ADDR_MASK;
+
+ } else if (deviceExtension->HBAConfiguration.bHBAModel
+ == IDA_BASE_CONTROLLER) {
+
+ //
+ // Check if interrupt is expected.
+ //
+
+ if (!(ScsiPortReadPortUchar(&deviceExtension->Bmic->SystemDoorBell) &
+ SYSTEM_DOORBELL_COMMAND_LIST_COMPLETE)) {
+
+ //
+ // Interrupt is spurious.
+ //
+
+ return FALSE;
+ }
+
+ //
+ // Get physical command list address from mailbox.
+ //
+
+ physicalAddress =
+ ScsiPortReadPortUlong(&deviceExtension->Bmic->
+ CommandListComplete.Address);
+
+ CmdListStatus =
+ ScsiPortReadPortUchar(&deviceExtension->Bmic->
+ CommandListComplete.Status);
+
+ //
+ // Dismiss interrupt at device by clearing command complete
+ // bit in system doorbell.
+ //
+
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBell,
+ SYSTEM_DOORBELL_COMMAND_LIST_COMPLETE);
+
+ //
+ // Free command completion channel.
+ //
+
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->LocalDoorBell,
+ LOCAL_DOORBELL_COMPLETE_CHANNEL_CLEAR);
+
+ } else if (deviceExtension->HBAConfiguration.bHBAModel
+ == IDA_PCI_DAZZLER) {
+
+ // Flags gets the list size and then write the phys addr out
+ // to the port
+ DebugPrint((3,"IdaInterrupt: DAZZLER PCI card\n"));
+
+ //
+ // The PCI interface specification calls for us to check the
+ // InterruptPending register to verify that an interrupt has
+ // been asserted at our controller. We are instead looking
+ // at the InterruptStatus masked with 0x01 to acheive the same
+ // result. We cannot use the spec's method, because at init and
+ // during a rescan at runtime, we have disabled controller
+ // interrupts and thus InterruptPending will be set to zero.
+ //
+
+ if (!(*((PULONG)deviceExtension->InterruptStatus) &
+ IDA_PCI_COMPLETION_STATUS_ACTIVE)) {
+
+ //
+ // Interrupt is not for this controller.
+ //
+
+ return FALSE;
+ }
+
+ //
+ // Read the physical address
+ //
+
+ ptmp = deviceExtension->CCFIFO;
+ physicalAddress = ptmp[0];
+
+ DebugPrint((9,"CCFIFO=%x\n",physicalAddress));
+
+ CmdListStatus = (UCHAR)physicalAddress & IDA_PCI_COMPLETION_STATUS_MASK;
+
+ if (CmdListStatus & IDA_PCI_COMPLETION_ERROR) {
+
+ DebugPrint((1,"IdaInterrupt: DAZZLER ERROR bit 0 set\n"));
+
+ CmdListStatus = RH_BAD_COMMAND_LIST;
+ }
+
+ physicalAddress &= IDA_PCI_PHYS_ADDR_MASK;
+
+ } else {
+
+ DebugPrint((1,"IdaInterrupt: ERROR - unknown HBA\n"));
+
+ }
+
+ //
+ // As a sanity check make sure physical address is not zero.
+ //
+
+ if (!physicalAddress) {
+
+ DebugPrint((1,
+ "IdaInterrupt: Physical address is zero\n"));
+
+ //
+ // Log this error.
+ //
+
+ ScsiPortLogError(HwDeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 2);
+
+ return TRUE;
+ }
+
+ //
+ // Get the virtual command list address.
+ //
+
+ commandList =
+ ScsiPortGetVirtualAddress(deviceExtension,
+ ScsiPortConvertUlongToPhysicalAddress(
+ physicalAddress));
+
+ DebugPrint((9, "Phys=%x %x\n",physicalAddress,commandList));
+
+ //
+ // As a sanity check make sure command list is not zero.
+ //
+
+ if (!commandList) {
+
+ DebugPrint((1, "IdaInterrupt: Command list is zero\n"));
+
+ //
+ // Log this error.
+ //
+
+ ScsiPortLogError(HwDeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 3);
+
+ return TRUE;
+ }
+
+ //
+ // Check for double completion.
+ //
+
+ if (commandList->Flags & CL_FLAGS_REQUEST_COMPLETED) {
+
+ DebugPrint((1, "IdaInterrupt: Double completion %x\n",
+ commandList));
+
+ //
+ // Log this error.
+ //
+
+ ScsiPortLogError(HwDeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 4);
+
+ return TRUE;
+
+ } else {
+
+ commandList->Flags |= CL_FLAGS_REQUEST_COMPLETED;
+ }
+
+ DebugPrint((3,"ErrorCode=%x\n", commandList->RequestHeader.ErrorCode));
+
+ if (CmdListStatus & RH_BAD_COMMAND_LIST)
+ commandList->RequestHeader.ErrorCode |= RH_BAD_COMMAND_LIST;
+
+ //
+ // Check request block error code.
+ //
+
+ DebugPrint((3,"ErrorCode=%x\n", commandList->RequestHeader.ErrorCode));
+
+ switch (commandList->RequestHeader.ErrorCode & ~RH_WARNING) {
+
+ case RH_SUCCESS:
+
+ status = SRB_STATUS_SUCCESS;
+ break;
+
+ case RH_FATAL_ERROR:
+
+ status = SRB_STATUS_ERROR;
+ break;
+
+ case RH_RECOVERABLE_ERROR:
+
+ status = SRB_STATUS_SUCCESS;
+ break;
+
+ case RH_INVALID_REQUEST:
+
+ status = SRB_STATUS_INVALID_REQUEST;
+ break;
+
+ case RH_REQUEST_ABORTED:
+
+ status = SRB_STATUS_ABORTED;
+ break;
+
+ default:
+
+ status = SRB_STATUS_ERROR;
+ break;
+ }
+
+ //
+ // Get SRB.
+ //
+
+ srb = commandList->SrbAddress;
+
+ //
+ // As a sanity check make sure SRB is not zero.
+ //
+
+ if (!srb) {
+
+ if (!commandList->Flags & CL_FLAGS_IDENTIFY_REQUEST) {
+
+ DebugPrint((1, "IdaInterrupt: SRB is zero\n"));
+
+ //
+ // Log this error.
+ //
+
+ ScsiPortLogError(HwDeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ 5);
+ }
+
+ return TRUE;
+ }
+
+ if (srb->Function == SRB_FUNCTION_IO_CONTROL) {
+
+ pSrb = (PSRB_IO_CONTROL)srb->DataBuffer;
+
+ switch (pSrb->ControlCode) {
+ case CPQ_IOCTL_PASSTHROUGH:
+ {
+ dataPointer = (PIDA_ERROR_BITS)((PUCHAR)srb->DataBuffer
+ + srb->DataTransferLength
+ - sizeof(IDA_ERROR_BITS));
+
+ if (CmdListStatus & RH_BAD_COMMAND_LIST) {
+ DebugPrint((1,
+ "IdaInterrupt: BAD_COMMAND_LIST error for PASSTHRU to %x\n",
+ deviceExtension));
+
+ dataPointer->ControllerError = RH_BAD_COMMAND_LIST |
+ (ULONG)commandList->RequestHeader.ErrorCode;
+ } else {
+ dataPointer->ControllerError =
+ (ULONG)commandList->RequestHeader.ErrorCode;
+ }
+
+ break;
+ }
+
+ case CPQ_IOCTL_SCSIPASSTHROUGH:
+ {
+
+ PSCSI_BUFFER_HEADER dataPacket;
+ ULONG bufferOffset;
+
+ if (commandList->RequestHeader.BlockNumber == 1) {
+ DebugPrint((3,
+ "IdaInterrupt: SCSIPASSTHRU intermediate copy needed.\n"));
+
+ //
+ // if BlockNumber == 1 then need to copy the data at the end of the
+ // commandList into the user buffer.
+ //
+
+ ReturnPointer = (PUCHAR)srb->DataBuffer
+ + sizeof(SRB_IO_CONTROL)
+ + sizeof(MAP_PARAMETER_PACKET);
+
+ MovePointer = (PUCHAR)commandList + sizeof(SG_DESCRIPTOR)
+ + sizeof(COMMAND_LIST_HEADER)
+ + sizeof(REQUEST_HEADER)
+ + sizeof(SCSI_PASSTHRU);
+
+ IdaMoveMemory(ReturnPointer, MovePointer,
+ commandList->SgDescriptor[0].Length);
+ }
+
+ //
+ // setup the return fields in the return data area.
+ //
+
+ bufferOffset = sizeof(SRB_IO_CONTROL) + sizeof(SCSI_PASSTHRU);
+
+ dataPacket = (PSCSI_BUFFER_HEADER)((PUCHAR)srb->DataBuffer
+ + bufferOffset);
+
+ dataPacket->CmdError = (UCHAR)commandList->RequestHeader.ErrorCode;
+
+ dataPacket->device_status =
+ ((PSCSI_PASSTHRU) (&commandList->
+ SgDescriptor[
+ commandList->
+ RequestHeader.ScatterGatherCount
+ ].Length))->scsi_header.device_status;
+
+ dataPacket->machine_error =
+ ((PSCSI_PASSTHRU) (&commandList->
+ SgDescriptor[commandList->RequestHeader.
+ ScatterGatherCount
+ ].
+ Length))->scsi_header.machine_error;
+
+ if (CmdListStatus & RH_BAD_COMMAND_LIST) {
+ DebugPrint((1,
+ "IdaInterrupt: BAD_COMMAND_LIST error for SCSI PASSTHRU to %x\n",
+ deviceExtension));
+
+ dataPacket->CmdError = (UCHAR)(RH_BAD_COMMAND_LIST |
+ commandList->RequestHeader.ErrorCode);
+ }
+
+ }
+
+ default:
+ break;
+
+ } // end switch
+
+ }
+
+ srb->SrbStatus = status;
+
+ //
+ // Inform system that this request is complete.
+ //
+
+ ScsiPortNotification(RequestComplete, deviceExtension, srb);
+
+ //
+ // Check if any requests need restarting.
+ //
+
+ if (deviceExtension->RestartRequests) {
+
+ //
+ // Get pointer to head of list.
+ //
+
+ nextCommand = deviceExtension->RestartRequests;
+ deviceExtension->RestartRequests = NULL;
+
+ //
+ // Try to restart each request in the list.
+ //
+
+ while (nextCommand) {
+
+ commandList = nextCommand;
+ nextCommand = nextCommand->NextEntry;
+
+ DebugPrint((1, "IdaInterrupt: Restarting request %x\n",
+ commandList));
+
+ //
+ // Submit command list to controller.
+ //
+
+ SubmitCommandList(deviceExtension, commandList);
+ }
+
+ }
+
+ return TRUE;
+
+} // IdaInterrupt();
+
+BOOLEAN
+GetDiskIdentifyData(
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN PVOID HwDeviceExtension,
+ IN PCOMMAND_LIST CommandList,
+ IN ULONG DriveNumber,
+ IN UCHAR Command
+ )
+
+/*++
+
+Routine Description:
+
+ Issue request to get identify data for this drive. This
+ routine has been modified for SMART-2 controller support.
+ Specifically, we are not accepting rescans for added logical
+ drives at run-time. Two functions have been added to
+ disable/enable controller interrupts while requesting details
+ from the firmware.
+
+Arguments:
+
+ HwDeviceExtension - Address of adapter storage area.
+ CommandList - Buffer for building request to controller.
+ DriveNumber - Identifies drive on controller.
+ Command - IDA command code.
+
+Return Value:
+
+ TRUE if successful.
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ ULONG length;
+ ULONG i;
+ BOOLEAN intrtn;
+
+ //
+ // Disable controller interrupts. This action was added with support
+ // for the SMART-2 controllers and online logical drive configuration.
+ // Following the addition of one or more logical drives, a rescan will
+ // request information about the new drives. Since this routine will
+ // return requested information, we cannot simply submit the command
+ // and exit. To reduce the number of outstanding requests we find at
+ // the controller while searching for ours, interrupts are temporarily
+ // disabled at all array controllers. The impact of this should not be
+ // too significant, as we should not be rescanning often.
+ //
+
+ IdaDisableInts(deviceExtension);
+
+ //
+ // load srb for interrupt routine...
+ //
+
+ CommandList->SrbAddress = Srb;
+
+ //
+ // Set up Command List Header.
+ //
+
+ CommandList->CommandListHeader.LogicalDriveNumber = (UCHAR)DriveNumber;
+ CommandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
+
+ //
+ // Indicate no notification required.
+ //
+
+ CommandList->CommandListHeader.Flags = 0;
+
+ //
+ // Zero out unused fields.
+ //
+
+ CommandList->RequestHeader.NextRequestOffset = 0;
+ CommandList->RequestHeader.ErrorCode = RH_SUCCESS;
+ CommandList->RequestHeader.Reserved = 0;
+
+ //
+ // Determine command.
+ //
+
+ CommandList->RequestHeader.CommandByte = Command;
+
+ //
+ // Set up request control fields.
+ //
+
+ CommandList->RequestHeader.BlockCount = 1;
+ CommandList->RequestHeader.BlockNumber = 0;
+ CommandList->Flags = CL_FLAGS_IDENTIFY_REQUEST;
+
+ //
+ // Fill in scatter/gather entry.
+ //
+
+ CommandList->SgDescriptor[0].Length = 512;
+
+ CommandList->SgDescriptor[0].Address =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(HwDeviceExtension,
+ NULL,
+ deviceExtension->
+ IdentifyBuffer,
+ &length));
+
+ //
+ // Calculate size of command list.
+ //
+
+ CommandList->RequestHeader.ScatterGatherCount=1;
+ CommandList->CommandListSize = sizeof(COMMAND_LIST_HEADER) +
+ sizeof(REQUEST_HEADER) +
+ sizeof(SG_DESCRIPTOR);
+
+ //
+ // Submit command list to controller.
+ //
+
+ SubmitCommandList(deviceExtension, CommandList);
+
+ DebugPrint((1, "GetDiskIdentifyData: Command Submitted:\n"));
+
+ //
+ // Poll interrupt routine. We are planning to poll for quite
+ // some time. It appears that the initial request made to the
+ // Dazzler/P board takes sometime and if we don't wait long
+ // enough here, we will have problems at init and probably
+ // anytime a rescan is requested. Current setting is 6 minutes
+ // which is probably too long, but.....
+ //
+
+ for (i=0; i < 360000; i++) {
+
+ //
+ // Call interrupt routine directly.
+ //
+
+ IdaInterrupt(HwDeviceExtension);
+
+ DebugPrint((1, "GetDiskIdentifyData: IdaInterrupt called:\n"));
+
+ // check for my completion...
+ if (CommandList->Flags & CL_FLAGS_REQUEST_COMPLETED) {
+
+ //
+ // Check status of completed request.
+ //
+
+ if ((CommandList->RequestHeader.ErrorCode & ~RH_WARNING) ==
+ RH_SUCCESS) {
+ IdaEnableInts(deviceExtension);
+ return TRUE;
+ } else {
+ DebugPrint((1, "GetDiskIdentifyData: Command failed: %x\n",
+ CommandList->RequestHeader.ErrorCode));
+
+ //
+ // Command failed.
+ //
+
+ IdaEnableInts(deviceExtension);
+ return FALSE;
+ }
+
+ }
+
+ ScsiPortStallExecution(1000);
+
+ }
+
+ IdaEnableInts(deviceExtension);
+ return FALSE;
+
+} // end GetDiskIdentifyData()
+
+
+BOOLEAN
+IdaStartIo(
+ IN PVOID HwDeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This is routine is called by the system to start a request on the adapter.
+
+Arguments:
+
+ HwDeviceExtension - Address of adapter storage area.
+ Srb - Address of the request to be started.
+
+Return Value:
+
+ TRUE - The request has been started.
+ FALSE - The controller was busy.
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PLOGICAL_UNIT_EXTENSION luExtension;
+ ULONG i;
+ UCHAR status;
+
+ switch (Srb->Function) {
+
+ case SRB_FUNCTION_RESET_BUS:
+
+ if (!IdaResetBus(deviceExtension, Srb->PathId)) {
+ status = SRB_STATUS_ERROR;
+ } else {
+ status = SRB_STATUS_SUCCESS;
+ }
+
+ break;
+
+ case SRB_FUNCTION_EXECUTE_SCSI:
+
+ switch (Srb->Cdb[0]) {
+
+ case SCSIOP_WRITE:
+ case SCSIOP_READ:
+
+ //
+ // Build command list from SRB.
+ //
+
+ BuildCommandList(deviceExtension,
+ Srb);
+
+ //
+ // Submit command list to controller.
+ //
+
+ SubmitCommandList(deviceExtension,
+ (PCOMMAND_LIST)Srb->SrbExtension);
+
+ status = SRB_STATUS_PENDING;
+ break;
+
+ case SCSIOP_TEST_UNIT_READY:
+
+ status = SRB_STATUS_SUCCESS;
+ break;
+
+ case SCSIOP_READ_CAPACITY:
+
+ //
+ // Get logical unit extension.
+ //
+ luExtension =
+ ScsiPortGetLogicalUnit(deviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+
+ if (luExtension) {
+ ULONG blockSize = luExtension->IdentifyData.BlockLength;
+
+ //
+ // Get blocksize and number of blocks from identify
+ // data.
+ //
+ REVERSE_BYTES
+ (&((PREAD_CAPACITY_DATA)Srb->DataBuffer)->BytesPerBlock,
+ &blockSize);
+
+ REVERSE_BYTES
+ (&((PREAD_CAPACITY_DATA)Srb->DataBuffer)->LogicalBlockAddress,
+ &luExtension->IdentifyData.NumberOfBlocks);
+
+ DebugPrint((1, "IdaStartIo: Block size %x\n",
+ luExtension->IdentifyData.BlockLength));
+
+ DebugPrint((1, "IdaStartIo: Number of blocks %x\n",
+ luExtension->IdentifyData.NumberOfBlocks));
+
+ status = SRB_STATUS_SUCCESS;
+
+ } else {
+ status = SRB_STATUS_ERROR;
+ }
+
+ break;
+
+ case SCSIOP_INQUIRY:
+ //
+ // Only respond at logical unit 0;
+ //
+
+ if (Srb->Lun != 0) {
+ //
+ // Indicate no device found at this address.
+ //
+ status = SRB_STATUS_SELECTION_TIMEOUT;
+ break;
+ }
+
+ //
+ // Get number of logical drives.
+ //
+
+ if (GetDiskIdentifyData(Srb, HwDeviceExtension,
+ (PCOMMAND_LIST)Srb->SrbExtension,
+ 0, RH_COMMAND_IDENTIFY_CONTROLLER)) {
+ deviceExtension->NumberOfLogicalDrives = (ULONG)
+ ((PIDENTIFY_CONTROLLER)
+ deviceExtension->IdentifyBuffer)->NumberLogicalDrives;
+
+ DebugPrint((1,
+ "IdaStartIo: Number of logical drives %x\n",
+ deviceExtension->NumberOfLogicalDrives));
+
+ //
+ // save off the identify controller buffer to the
+ // extension area
+ //
+
+ ScsiPortMoveMemory(&deviceExtension->IdentifyData,
+ deviceExtension->IdentifyBuffer,
+ sizeof(IDENTIFY_CONTROLLER));
+
+ } else {
+ DebugPrint((1,
+ "IdaFindAdapters: Get controller information failed\n"));
+ status = SRB_STATUS_ERROR;
+ break;
+ }
+
+ //
+ // Check if this is for one of the reported logical drives.
+ //
+
+ if (Srb->TargetId >=
+ deviceExtension->NumberOfLogicalDrives) {
+ status = SRB_STATUS_SELECTION_TIMEOUT;
+ break;
+ }
+
+ //
+ // Issue identify command.
+ //
+
+ if (!GetDiskIdentifyData(Srb, HwDeviceExtension,
+ (PCOMMAND_LIST)Srb->SrbExtension,
+ Srb->TargetId,
+ RH_COMMAND_IDENTIFY_LOGICAL_DRIVES)) {
+ status = SRB_STATUS_SELECTION_TIMEOUT;
+ break;
+ }
+
+ //
+ // Get logical unit extension.
+ //
+
+ luExtension = ScsiPortGetLogicalUnit(deviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+
+ //
+ // Copy data from buffer to logical unit extension.
+ //
+
+ ScsiPortMoveMemory(&luExtension->IdentifyData,
+ deviceExtension->IdentifyBuffer,
+ sizeof(IDENTIFY_LOGICAL_DRIVE));
+ //
+ // Issue sense configuration command.
+ //
+
+ if (!GetDiskIdentifyData(Srb, HwDeviceExtension,
+ (PCOMMAND_LIST)Srb->SrbExtension,
+ Srb->TargetId,
+ RH_COMMAND_SENSE_CONFIGURATION)) {
+
+ status = SRB_STATUS_SELECTION_TIMEOUT;
+ break;
+ }
+
+ //
+ // Copy data from buffer to logical unit extension.
+ //
+
+ ScsiPortMoveMemory(&luExtension->SenseData,
+ deviceExtension->IdentifyBuffer,
+ sizeof(SENSE_CONFIGURATION));
+
+ //
+ // Zero INQUIRY data structure.
+ //
+
+ for (i = 0; i < Srb->DataTransferLength; i++) {
+ ((PUCHAR)Srb->DataBuffer)[i] = 0;
+ }
+
+ //
+ // Compaq IDA only supports disks.
+ //
+
+ ((PINQUIRYDATA)Srb->DataBuffer)->DeviceType = DIRECT_ACCESS_DEVICE;
+
+ //
+ // Fill in vendor identification fields.
+ //
+
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[0] = 'C';
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[1] = 'o';
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[2] = 'm';
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[3] = 'p';
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[4] = 'a';
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[5] = 'q';
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[6] = ' ';
+ ((PINQUIRYDATA)Srb->DataBuffer)->VendorId[7] = ' ';
+
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[0] = 'D';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[1] = 'i';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[2] = 's';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[3] = 'k';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[4] = ' ';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[5] = 'A';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[6] = 'r';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[7] = 'r';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[8] = 'a';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[9] = 'y';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[10] = ' ';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[11] = ' ';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[12] = ' ';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[13] = ' ';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[14] = ' ';
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductId[15] = ' ';
+
+ //
+ // Move firmware revision from IDENTIFY data to
+ // product revision in INQUIRY data.
+ //
+
+ for (i = 0; i < 4; i++) {
+ ((PINQUIRYDATA)Srb->DataBuffer)->ProductRevisionLevel[i] =
+ deviceExtension->IdentifyData.FirmwareRevision[i];
+ }
+
+ status = SRB_STATUS_SUCCESS;
+ break;
+
+ case SCSIOP_VERIFY:
+
+ //
+ // Compaq array controllers hotfix bad sectors as they are
+ // encountered. A sector verify in unnecessary.
+ //
+
+ status = SRB_STATUS_SUCCESS;
+ break;
+
+ default:
+
+ status = SRB_STATUS_INVALID_REQUEST;
+ break;
+
+ } // end switch (Srb->Cdb[0])
+
+ break;
+
+ //
+ // Issue FLUSH/DISABLE if shutdown command.
+ //
+
+ case SRB_FUNCTION_SHUTDOWN:
+
+ BuildFlushDisable(deviceExtension,Srb);
+ SubmitCommandList(deviceExtension,
+ (PCOMMAND_LIST)Srb->SrbExtension);
+
+ status = SRB_STATUS_PENDING;
+
+ break;
+
+ //
+ // Do not need the flush command since all controllers have
+ // memory that is battery backed up. Just return success.
+ //
+
+ case SRB_FUNCTION_FLUSH:
+ status = SRB_STATUS_SUCCESS;
+ break;
+
+ case SRB_FUNCTION_IO_CONTROL: {
+ PCPQ_IDA_IDENTIFY pIoctlBuffer;
+
+ pIoctlBuffer = (PCPQ_IDA_IDENTIFY)Srb->DataBuffer;
+
+ //
+ // Status is returned mainly in 2 fields to the calling thread.
+ // These 2 fields determine if other status fields are valid to
+ // check. If the request is not a valid request for this driver
+ // then the Header.ReturnCode is not modified and the
+ // Srb->SrbStatus is set to SRB_STATUS_INVALID_REQUEST. If
+ // the request is valid for this driver then Srb->SrbStatus
+ // is always returned as SRB_STATUS_SUCCESS and the
+ // Header.ReturnCode contains information concerning the
+ // status of the particular request.
+ //
+
+ if (!IdaStrCmp(pIoctlBuffer->Header.Signature, IDA_SIGNATURE)) {
+
+ if (IdaProcessIoctl(deviceExtension,
+ pIoctlBuffer,
+ Srb) == CPQ_CIM_ISSUED) {
+
+ status = SRB_STATUS_PENDING;
+
+ } else {
+ status = SRB_STATUS_SUCCESS;
+ }
+
+ } else {
+ status = SRB_STATUS_INVALID_REQUEST;
+ }
+
+ break;
+ }
+
+ default:
+
+ status = SRB_STATUS_INVALID_REQUEST;
+
+ } // end switch
+
+ //
+ // Check if SRB should be completed.
+ //
+
+ if (status != SRB_STATUS_PENDING) {
+
+ //
+ // Set status in SRB.
+ //
+
+ Srb->SrbStatus = status;
+
+ //
+ // Inform system that this request is complete.
+ //
+
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ }
+
+ //
+ // Indicate to system that the controller can take another request
+ // for this device.
+ //
+
+ ScsiPortNotification(NextLuRequest,
+ deviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+
+ return TRUE;
+
+} // end IdaStartIo()
+
+ULONG
+IdaProcessIoctl(
+ IN PDEVICE_EXTENSION deviceExtension,
+ PVOID pIoctlBuffer,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ ULONG currentId;
+ ULONG numberOfLuns;
+ ULONG status;
+ PCPQ_IDA_IDENTIFY pCPQ = pIoctlBuffer;
+
+ //
+ // Build command list from SRB.
+ //
+
+ status = CPQ_CIM_COMPLETED;
+
+ DebugPrint((3,
+ "IdaProcessIoctl(): parsing request %d for PathId=%d TargetId=%d Lun=%d\n",
+ pCPQ->Header.ControlCode,Srb->PathId,Srb->TargetId,Srb->Lun));
+
+ switch(pCPQ->Header.ControlCode) {
+ case CPQ_IOCTL_IDENTIFY_DRIVER: {
+ PLOGICAL_UNIT_EXTENSION luExtension;
+
+ PMAP_HEADER header = (PMAP_HEADER)((PUCHAR)Srb->DataBuffer +
+ sizeof(SRB_IO_CONTROL));
+
+ IdaMoveMemory(header->DriverName, IDA_DRIVER_NAME,
+ sizeof(header->DriverName));
+
+ header->DriverMajorVersion = IDA_MAJOR_VERSION;
+ header->DriverMinorVersion = IDA_MINOR_VERSION;
+
+ header->ControllerCount = 1;
+
+ //
+ // We need to give back the number of LUNs not the actual
+ // number of LUNs available because LU extensions are not
+ // discarded when a drive has been removed or taken off-line.
+ //
+
+ currentId = 0;
+ numberOfLuns = 0;
+
+ luExtension = ScsiPortGetLogicalUnit(deviceExtension,
+ Srb->PathId,
+ (UCHAR)currentId,
+ Srb->Lun);
+ while (luExtension) {
+ numberOfLuns++;
+ currentId++;
+ luExtension = ScsiPortGetLogicalUnit(deviceExtension,
+ Srb->PathId,
+ (UCHAR)currentId,
+ Srb->Lun);
+ }
+
+ header->LogicalDiskCount = numberOfLuns;
+
+ header->RequiredMemory = sizeof(MAP_CONTROLLER_DATA) +
+ (sizeof(MAP_LOGICALDRIVE_DATA) * numberOfLuns);
+
+ status = CPQ_CIM_COMPLETED;
+ break;
+ }
+
+ case CPQ_IOCTL_IDENTIFY_CONTROLLERS: {
+ ULONG i;
+ PLOGICAL_UNIT_EXTENSION luExtension;
+ PMAP_LOGICALDRIVE_DATA LdriveData;
+ PMAP_CONTROLLER_DATA controllerData;
+
+ //
+ // Take care of the controller struct first
+ //
+
+ controllerData = (PMAP_CONTROLLER_DATA)
+ ((PUCHAR)Srb->DataBuffer + sizeof(SRB_IO_CONTROL));
+
+ controllerData->NextController = NULL;
+
+ //
+ // calculate offset from the beginning of the controller data area.
+ //
+
+ controllerData->LogicalDriveList =
+ (PMAP_LOGICALDRIVE_DATA)(controllerData + 1);
+ controllerData->EisaId = deviceExtension->EisaId;
+ controllerData->BmicIoAddress = (ULONG)deviceExtension->Bmic;
+ controllerData->IrqLevel = deviceExtension->IrqLevel;
+
+ IdaMoveMemory((PUCHAR)&controllerData->ControllerInfo,
+ (PUCHAR)&deviceExtension->IdentifyData,
+ sizeof(IDENTIFY_CONTROLLER));
+
+ //
+ // Now look for logical units until one is not found. In the future
+ // support non-consecutive logical units, for now, stop searching.
+ //
+
+ currentId = 0;
+ luExtension = ScsiPortGetLogicalUnit(deviceExtension,
+ Srb->PathId,
+ (UCHAR)currentId,
+ Srb->Lun);
+
+ LdriveData = controllerData->LogicalDriveList;
+
+ while (luExtension) {
+
+ //
+ // Set the DeviceLengthXX sizes to 0, removed from CIM interface.
+ //
+
+ LdriveData->NextLogicalDrive = LdriveData + 1;
+ LdriveData->Controller = controllerData;
+ LdriveData->LogicalDriveNumber = currentId;
+ LdriveData->SystemDriveNumber = 0;
+ LdriveData->DeviceLengthLo = 0;
+ LdriveData->DeviceLengthHi = 0;
+ LdriveData->SectorSize = (ULONG)(1 << deviceExtension->SectorShift);
+ IdaMoveMemory((PUCHAR)&LdriveData->Configuration,
+ (PUCHAR)&luExtension->SenseData,
+ sizeof(SENSE_CONFIGURATION));
+
+ IdaMoveMemory((PUCHAR)&LdriveData->LogicalDriveInfo,
+ (PUCHAR)&luExtension->IdentifyData,
+ sizeof(IDENTIFY_LOGICAL_DRIVE));
+
+ currentId++;
+ luExtension =
+ ScsiPortGetLogicalUnit(deviceExtension, Srb->PathId,
+ (UCHAR)currentId, Srb->Lun);
+
+ if (!luExtension) {
+ break;
+ }
+
+ LdriveData = LdriveData + 1;
+ }
+
+ LdriveData->NextLogicalDrive = NULL;
+
+ //
+ // Need to convert NextLogicalDrive fields to offsets from virtual
+ // addresses. currentId is the last ID that was found.
+ //
+
+ if (currentId) {
+ controllerData->LogicalDriveList =
+ (PMAP_LOGICALDRIVE_DATA)sizeof(MAP_CONTROLLER_DATA);
+ LdriveData = (PMAP_LOGICALDRIVE_DATA)(controllerData + 1);
+ LdriveData->NextLogicalDrive = NULL;
+
+ for (i=0;i<(currentId-1);i++,LdriveData++) {
+ LdriveData->NextLogicalDrive =
+ (PMAP_LOGICALDRIVE_DATA)(sizeof(MAP_CONTROLLER_DATA)
+ + ((i+1)*sizeof(MAP_LOGICALDRIVE_DATA)));
+ }
+
+ } else {
+ controllerData->LogicalDriveList = NULL;
+ }
+
+ status = CPQ_CIM_COMPLETED;
+ break;
+ }
+
+ case CPQ_IOCTL_PASSTHROUGH:
+ case CPQ_IOCTL_SCSIPASSTHROUGH: {
+
+ if (!(BuildCIMList(deviceExtension, Srb) == CPQ_CIM_CMDBUILT)) {
+ status = CPQ_CIM_COMPLETED;
+ } else {
+
+ //
+ // Submit command list to controller.
+ //
+
+ DebugPrint((3,
+ "IdaProcessIoctl(): Submitting PASSTHRU request to %x\n",
+ deviceExtension));
+
+ SubmitCommandList(deviceExtension,
+ (PCOMMAND_LIST)Srb->SrbExtension);
+ status = CPQ_CIM_ISSUED;
+ }
+ break;
+ }
+
+ case CPQ_IOCTL_CONFIGURATION_INFO: {
+ PIDA_CONFIGURATION pConfigData;
+
+ //
+ // Setup pointer to the Config Data area
+ //
+
+ pConfigData = (PIDA_CONFIGURATION)
+ ((PUCHAR)Srb->DataBuffer + sizeof(SRB_IO_CONTROL));
+
+ pConfigData->ulBaseMemoryAddress =
+ deviceExtension->HBAConfiguration.ulBaseIOAddress;
+ pConfigData->bIoBusType =
+ deviceExtension->HBAConfiguration.bHBAIoBusType;
+ pConfigData->ulBaseIOAddress = deviceExtension->BaseIOAddress;
+ pConfigData->ulControllerID = deviceExtension->EisaId;
+ IdaMoveMemory((PUCHAR)&pConfigData->IoBusData,
+ (PUCHAR)&deviceExtension->HBAConfiguration.HBAIoBusData,
+ sizeof(union _IO_BUS_DATA));
+ status = CPQ_CIM_COMPLETED;
+ break;
+ }
+
+ default:
+ pCPQ->Header.ReturnCode = CPQ_SCSI_ERR_BAD_CNTL_CODE;
+ status = CPQ_CIM_COMPLETED;
+ break;
+ }
+
+ return(status);
+}
+
+
+VOID
+BuildFlushDisable(
+ IN PDEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a shutdown command list suitable for submission to the
+ Compaq IDA controller, from an SRB.
+
+Arguments:
+
+ DeviceExtension - Address of adapter storage area.
+ Srb - System request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PCOMMAND_LIST commandList = Srb->SrbExtension;
+ ULONG length,i;
+ PFLUSH_DISABLE pFlushDisable;
+ PSG_DESCRIPTOR sgList;
+
+ sgList = commandList->SgDescriptor;
+
+ // clear out reserved area
+
+ for (i=0;i<MAXIMUM_SG_DESCRIPTORS;i++) {
+ sgList[i].Address = 0;
+ sgList[i].Length = 0;
+ }
+
+ //
+ // Save SRB address for interrupt routine.
+ //
+
+ commandList->SrbAddress = Srb;
+
+ //
+ // Set up Command List Header.
+ //
+
+ commandList->CommandListHeader.LogicalDriveNumber = 0;
+
+ commandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
+
+ commandList->CommandListHeader.Flags =
+ CL_FLAGS_NOTIFY_LIST_COMPLETE + CL_FLAGS_NOTIFY_LIST_ERROR;
+
+ //
+ // Set up Request Header.
+ //
+ // Terminate request list.
+ //
+
+ commandList->RequestHeader.NextRequestOffset = 0;
+
+ commandList->Flags = 0;
+
+ //
+ // Reset error code.
+ //
+
+ commandList->RequestHeader.ErrorCode = 0;
+
+ //
+ // Clear reserved field.
+ //
+
+ commandList->RequestHeader.Reserved = 0;
+
+ //
+ // Check for special Compaq passthrough command.
+ //
+
+ commandList->RequestHeader.BlockCount = (USHORT)1;
+ commandList->RequestHeader.BlockNumber = (ULONG)0;
+ commandList->RequestHeader.CommandByte = RH_COMMAND_FLUSH_DISABLE_CACHE;
+
+ pFlushDisable = (PFLUSH_DISABLE)&(sgList[1].Length);
+ pFlushDisable->disable_flag = 1; //disable cache also
+
+ sgList[0].Address =
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL,
+ commandList,
+ &length).LowPart;
+
+ //
+ // ScsiPortGetPhysicalAddress only accepts certain virtual addresses,
+ // so use the commandList and then increment over to the second s/g
+ // descriptor where the structure for the flush/disable command is
+ // located.
+ //
+ // Note that since it is difficult to allocate nonpaged memory at this
+ // level of the driver, and the command has 510 bytes of reserved
+ // area in the structure then memory will be retrieved by the controller
+ // that is past the end of the defined commandlist allocated memory.
+ // This will not be a problem unless the memory extends beyond the
+ // actual physical end of memory in the machine.
+ //
+ // The IDA-2 controller requires a multiple of 512 for the length so
+ // to avoid code that is controller dependent just use 512 that is
+ // accepted by all controllers. This command returns an BAD REQUEST
+ // when issued to IDA controllers since those controllers have no
+ // memory on the board.
+ //
+
+ sgList[0].Address += (sizeof(COMMAND_LIST_HEADER) +
+ sizeof(REQUEST_HEADER) +
+ sizeof(SG_DESCRIPTOR));
+
+ sgList[0].Length = 512;
+ commandList->RequestHeader.BlockNumber = 0;
+ commandList->RequestHeader.ScatterGatherCount=1;
+
+ //
+ // Build physical address translation list entry.
+ //
+
+ commandList->CommandListSize = (sizeof(COMMAND_LIST_HEADER) +
+ sizeof(REQUEST_HEADER) +
+ sizeof(SG_DESCRIPTOR));
+}
+
+
+ULONG
+IdaFindAdapter(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ )
+
+/*++
+
+Routine Description:
+
+ This function fills in the configuration information structure
+
+Arguments:
+
+ HwDeviceExtension - Supplies a pointer to the device extension.
+ Context - Supplies adapter initialization structure.
+ BusInformation - Unused.
+ ArgumentString - Unused.
+ ConfigInfo - Pointer to the configuration information structure.
+ Again - Indicates that system should continue search for adapters.
+
+Return Value:
+
+ SP_RETURN_FOUND - Indicates adapter found.
+ SP_RETURN_NOT_FOUND - Indicates adapter not found.
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PACCESS_RANGE accessRange;
+ DebugPrint((9,"&deviceExtension=%x\n",&deviceExtension));
+ DebugPrint((9,"&deviceExtension->PCIoff=%x\n",&deviceExtension->PCIoff));
+
+ //
+ // Get access range.
+ //
+
+ accessRange = &((*(ConfigInfo->AccessRanges))[0]);
+
+ if (accessRange->RangeLength == 0) {
+
+ if (!SearchEisaBus(HwDeviceExtension, Context, ConfigInfo)) {
+
+ //
+ // Tell system nothing was found and not to call again.
+ //
+
+ *Again = FALSE;
+ return SP_RETURN_NOT_FOUND;
+ }
+
+ }
+
+ //
+ // Get system-mapped controller address.
+ //
+
+ deviceExtension->Bmic =
+ ScsiPortGetDeviceBase(HwDeviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ accessRange->RangeStart,
+ accessRange->RangeLength,
+ (BOOLEAN) !accessRange->RangeInMemory);
+
+ //
+ // Complete description of controller.
+ //
+
+ ConfigInfo->MaximumTransferLength = (ULONG)-1;
+ ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SG_DESCRIPTORS;
+ ConfigInfo->NumberOfBuses = 1;
+ ConfigInfo->ScatterGather = TRUE;
+ ConfigInfo->Master = TRUE;
+ ConfigInfo->Dma32BitAddresses = TRUE;
+
+ //
+ // Get noncached extension for identify requests.
+ //
+
+ deviceExtension->EisaId =
+ ScsiPortReadPortUlong(&deviceExtension->Bmic->BoardId);
+
+ if ((deviceExtension->EisaId & IDA_EISA_ID_MASKID_LOW) >=
+ (IDA_EISA_ID_DAZZLER & IDA_EISA_ID_MASKID_LOW)) {
+
+ deviceExtension->HBAConfiguration.bHBAModel = IDA_EISA_DAZZLER;
+
+ deviceExtension->eisapci =
+ (PEISAPCI_CONTROLLER)(((ULONG)deviceExtension->Bmic & 0xf000));
+
+ DebugPrint((9,"Found EISA DAZZLER: deviceExtension->eisapci=%x\n",
+ deviceExtension->eisapci));
+
+ ConfigInfo->MaximumNumberOfTargets = 32;
+ } else {
+ deviceExtension->HBAConfiguration.bHBAModel = IDA_BASE_CONTROLLER;
+ }
+
+ //
+ // Setup some vars needed for the IDENTIFY commands
+ //
+
+ deviceExtension->IrqLevel = (UCHAR)ConfigInfo->BusInterruptLevel;
+ deviceExtension->SectorShift = 9;
+
+ deviceExtension->IdentifyBuffer =
+ ScsiPortGetUncachedExtension(deviceExtension,
+ ConfigInfo,
+ 512);
+ ConfigInfo->CachesData = TRUE;
+
+ deviceExtension->HBAConfiguration.HBAIoBusData.usEisaSlot =
+ (USHORT)ConfigInfo->SlotNumber;
+
+ deviceExtension->BaseIOAddress = ConfigInfo->SlotNumber * 0x1000;
+ deviceExtension->HBAConfiguration.bHBAIoBusType = EISA_BUS;
+
+ //
+ // Tell system to look for more adapters.
+ //
+
+ *Again = TRUE;
+
+ return SP_RETURN_FOUND;
+
+} // end IdaFindAdapter()
+
+
+ULONG
+BuildCIMList(
+ IN PDEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a command list suitable for submission to the
+ Compaq IDA controller, from an SRB.
+
+Arguments:
+
+ DeviceExtension - Address of adapter storage area.
+ Srb - System request.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PCOMMAND_LIST commandList = Srb->SrbExtension;
+ PUCHAR dataPointer;
+ ULONG physicalAddress;
+ ULONG bytesLeft;
+ ULONG descriptor;
+ ULONG length;
+ ULONG bufferOffset;
+ ULONG status;
+ PSRB_IO_CONTROL pSrb;
+ PSCSI_PASSTHRU scsipass;
+
+ //
+ // Save SRB address for interrupt routine.
+ //
+
+ commandList->SrbAddress = Srb;
+
+ //
+ // Set up Command List Header.
+ //
+
+ commandList->CommandListHeader.LogicalDriveNumber = Srb->TargetId;
+ commandList->CommandListHeader.RequestPriority = CL_NORMAL_PRIORITY;
+
+ commandList->CommandListHeader.Flags =
+ CL_FLAGS_NOTIFY_LIST_COMPLETE + CL_FLAGS_NOTIFY_LIST_ERROR;
+
+ commandList->RequestHeader.NextRequestOffset = 0;
+ commandList->Flags = 0;
+ commandList->RequestHeader.ErrorCode = 0;
+ commandList->RequestHeader.Reserved = 0;
+ commandList->RequestHeader.BlockCount = 0;
+ commandList->RequestHeader.BlockNumber = 0;
+
+ status = CPQ_CIM_ERROR;
+ pSrb = (PSRB_IO_CONTROL)Srb->DataBuffer;
+
+ switch (pSrb->ControlCode) {
+ case CPQ_IOCTL_PASSTHROUGH:
+ {
+ PMAP_PARAMETER_PACKET pParmPkt = (PMAP_PARAMETER_PACKET)
+ (((PUCHAR)Srb->DataBuffer) + sizeof(SRB_IO_CONTROL));
+
+ commandList->CommandListHeader.LogicalDriveNumber = pParmPkt->TargetId;
+ commandList->RequestHeader.BlockCount = pParmPkt->BlockCount;
+ commandList->RequestHeader.BlockNumber = pParmPkt->BlockNumber;
+ commandList->RequestHeader.CommandByte = pParmPkt->IdaLogicalCommand;
+
+ //
+ // Build scatter/gather descriptor list.
+ //
+
+ descriptor = 0;
+ bufferOffset = sizeof(SRB_IO_CONTROL) + sizeof(MAP_PARAMETER_PACKET);
+ dataPointer = (PUCHAR)Srb->DataBuffer + bufferOffset;
+ bytesLeft = Srb->DataTransferLength - bufferOffset -
+ sizeof(IDA_ERROR_BITS);
+
+ do {
+
+ //
+ // Get physical address and length of contiguous
+ // physical buffer.
+ //
+
+ physicalAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ Srb,
+ dataPointer,
+ &length)
+ );
+
+ //
+ // If length of physical memory is more
+ // than bytes left in transfer, use bytes
+ // left as final length.
+ //
+
+ if (length > bytesLeft) {
+ length = bytesLeft;
+ }
+
+ //
+ // Fill in descriptor.
+ //
+
+ commandList->SgDescriptor[descriptor].Address = physicalAddress;
+ commandList->SgDescriptor[descriptor].Length = length;
+
+ //
+ // Adjust counts.
+ //
+
+ dataPointer = dataPointer + length;
+ bytesLeft -= length;
+ descriptor++;
+
+ } while (bytesLeft);
+
+ //
+ // Calculate size of command list.
+ //
+
+ commandList->RequestHeader.ScatterGatherCount=(UCHAR)descriptor;
+ commandList->CommandListSize = (sizeof(COMMAND_LIST_HEADER) +
+ sizeof(REQUEST_HEADER) +
+ sizeof(SG_DESCRIPTOR) *
+ descriptor);
+
+ status = CPQ_CIM_CMDBUILT;
+ break;
+ }
+
+ case CPQ_IOCTL_SCSIPASSTHROUGH:
+
+ //
+ // Build scatter/gather descriptor list.
+ //
+
+ descriptor = 0;
+
+ bufferOffset = (sizeof(SRB_IO_CONTROL) +
+ sizeof(SCSI_PASSTHRU) +
+ sizeof(SCSI_BUFFER_HEADER));
+
+ dataPointer = (PUCHAR)Srb->DataBuffer + bufferOffset;
+ bytesLeft = Srb->DataTransferLength - bufferOffset;
+
+ //
+ // Get physical address and length of contiguous
+ // physical buffer.
+ //
+
+ physicalAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ Srb,
+ dataPointer,
+ &length)
+ );
+ //
+ // get to the scsi cdb area, and then copy to the end of the cmdlist
+ // which is after the first s/g descriptor.
+ // modify to move after the last USED s/g area when more than 1 s/g
+ // functions in the controller f/w.
+ //
+
+ scsipass = (PSCSI_PASSTHRU)(((PUCHAR)Srb->DataBuffer +
+ sizeof(SRB_IO_CONTROL)));
+ IdaMoveMemory((PUCHAR)&commandList->SgDescriptor[1].Length,
+ (PUCHAR)scsipass,
+ sizeof(SCSI_PASSTHRU_HEADER) +
+ scsipass->scsi_header.cdb_length
+ );
+
+ bufferOffset = (sizeof(SG_DESCRIPTOR) +
+ sizeof(COMMAND_LIST_HEADER) +
+ sizeof(REQUEST_HEADER) +
+ sizeof(SCSI_PASSTHRU_HEADER) +
+ scsipass->scsi_header.cdb_length);
+
+ //
+ // If length of physical memory is less than needed space
+ // attempt to use nonpaged memory left in the command list
+ // else return error since allocating memory is not possible
+ // under the miniport design.
+ //
+
+ if (length < bytesLeft) {
+
+ if (( (MAXIMUM_SG_DESCRIPTORS * sizeof(SG_DESCRIPTOR)) -
+ sizeof(SG_DESCRIPTOR) -
+ sizeof(SCSI_PASSTHRU)) < bytesLeft) {
+
+ DebugPrint((3,
+ "BuildCIMList(): Returning CPQ_SCSI_ERR_NONCONTIGUOUS\n"));
+
+ pSrb->ReturnCode = CPQ_SCSI_ERR_NONCONTIGUOUS;
+ return(CPQ_CIM_NONCONTIGUOUS);
+ }
+
+ //
+ // Get the physical address of the start of the command list and then
+ // increment to the first non-used byte in the s/g descriptor list.
+ // There are limitations on what physical addresses can be obtained
+ // from the ScsiPort calls so use what we know is nonpaged memory.
+ //
+
+ physicalAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ Srb,
+ commandList,
+ &length)
+ );
+
+ physicalAddress += bufferOffset;
+
+ //
+ // set BlockNumber to flag that a copy from end of commandList is needed
+ // when the request is completed.
+ //
+
+ commandList->RequestHeader.BlockNumber = 1;
+ }
+
+ commandList->RequestHeader.CommandByte = RH_COMMAND_SCSI_PASS_THRU;
+ commandList->CommandListSize = (USHORT)bufferOffset;
+ commandList->SgDescriptor[descriptor].Address = physicalAddress;
+ commandList->SgDescriptor[descriptor].Length = bytesLeft;
+ commandList->RequestHeader.ScatterGatherCount = 1;
+ commandList->CommandListHeader.LogicalDriveNumber = 0;
+
+ status = CPQ_CIM_CMDBUILT;
+ break;
+
+ default:
+
+ DebugPrint((1,
+ "BuildCIMList(): Returning CPQ_SCSI_ERR_BAD_CNTL_CODE\n"));
+ pSrb->ReturnCode = CPQ_SCSI_ERR_BAD_CNTL_CODE;
+ status = CPQ_CIM_ERROR;
+ break;
+ }
+
+ return(status);
+
+} // end BuildCIMList()
+
+VOID
+IdaMoveMemory(
+ OUT PUCHAR pDestination,
+ IN PUCHAR pSource,
+ IN ULONG ulLength
+ )
+
+{
+ while (ulLength--)
+ *pDestination++ = *pSource++;
+ return;
+}
+
+BOOLEAN
+IdaStrCmp(
+ IN PUCHAR p1,
+ IN PUCHAR p2
+ )
+{
+ ULONG count=0;
+ ULONG p1count=0;
+ ULONG p2count=0;
+
+ //
+ // Get count of number of bytes in first
+ // Get count for second
+ // Perform while loop until out of greater number of bytes.
+ //
+
+ while ((p1[count] < 0x7f) && (p1[count] > 0x1f))
+ count++;
+
+ p1count = count;
+
+ while ((p2[count] < 0x7f) && (p2[count] > 0x1f))
+ count++;
+
+ p2count = count;
+
+ if (p1count != p2count)
+ return(TRUE);
+
+ count = p2count;
+
+ while (count) {
+ if (p1[count-1] != p2[count-1])
+ return(TRUE);
+ count--;
+ }
+
+ return(FALSE);
+
+}
+
+
+// Device extension global variable. This variable is needed for the
+// pci_bios function since there is no way to pass in this value. The
+// ScsiPortGetBusData and ScsiPortSetBusDataByOffset require a pointer
+// to the device extension as an argument to the function.
+
+STATIC PVOID gpDeviceExtension = NULL;
+
+//
+// Internal module function prototypes
+//
+
+STATIC
+ULONG
+GetPciSpecifics(
+ IN OUT PVOID pDeviceExtension,
+ IN OUT PIDA_CONTEXT pIDAContext,
+ IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo,
+ IN PPCI_SLOT_NUMBER pPciSlotNumber
+ );
+
+STATIC
+ULONG
+GetPciResources(
+ IN PVOID pDeviceExtension,
+ IN PPCI_COMMON_CONFIG pPciConfigHeader,
+ IN ULONG ulPciSlotNumber,
+ IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo
+ );
+
+
+
+ULONG
+IDAFindPci(
+ IN OUT PVOID pDeviceExtension,
+ IN OUT PVOID pContext,
+ IN PVOID pBusInformation,
+ IN PCHAR pArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo,
+ OUT PBOOLEAN pAgain
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called by the SCSI port driver to find SMART-2/P
+ controllers on the system's PCI buses. This routine searches only
+ the input PCI bus number in the port configuration information. If
+ a controller is found, the function fills out the controller's resource
+ requirements in the port configuration information and begins the
+ initialization process for the controller.
+
+
+Arguments:
+
+ pDeviceExtension - pointer to the miniport driver's per-controller
+ storage area
+ pContext - pointer to the context value passed to ScsiPortInitialize()
+ pBusInformation - pointer to bus type specific information
+ pArgumentString - pointer to null-terminated ASCII string
+ pConfigInfo - pointer to SCSI port configuration information
+
+
+Return Values:
+
+ pDeviceExtension - Minport driver's per-controller storage area
+ pContext - Context value passed to ScsiPortInitialize()
+ pConfigInfo - pointer to SCSI port configuration information
+ pAgain - Indicates to call function again to find more controllers.
+
+
+ Function Return Values:
+
+ SP_RETURN_FOUND - Indicates a host adapter was found and the configuration
+ information was successfully determined.
+
+ SP_RETURN_ERROR - Indicates a host adapter was found but an error occurred
+ obtaining the configuration information.
+
+ SP_RETURN_NOT_FOUND - Indicates no host adapter was found for the supplied
+ configuration information.
+
+ SP_RETURN_BAD_CONFIG - Indicates the supplied configuration information
+ was invalid.
+
+-- */
+
+{
+ BYTE bDeviceNumber;
+ BYTE bFunctionNumber;
+ BYTE bStartDeviceNumber;
+ BYTE bStartFunctionNumber;
+ PCI_SLOT_NUMBER PciSlotNumber;
+ PIDA_CONTEXT pIDAContext = pContext;
+ PCI_COMMON_CONFIG PciConfigHeader;
+ ULONG ulBytes;
+ ULONG ulInitStatus;
+ ULONG ulTmp1;
+ ULONG ulTmp2;
+
+
+ UNREFERENCED_PARAMETER(pBusInformation);
+ UNREFERENCED_PARAMETER(pArgumentString);
+
+
+ DebugPrint((4, "\nDAZZLER: Enter function IDAFindPci.\n"));
+
+ //
+ // Set the input pAgain argument to TRUE. This ensures that the function
+ // will be called for every PCI bus in the system.
+ //
+
+ *pAgain = TRUE;
+
+ // Clear the slot number.
+
+ PciSlotNumber.u.AsULONG = 0;
+
+
+ // Set the initial search starting numbers.
+
+ bStartDeviceNumber = pIDAContext->PciAddress.bDeviceNumber;
+ bStartFunctionNumber = pIDAContext->PciAddress.bFunctionNumber;
+
+ DebugPrint((4, "DAZZLER: Beginning search on system PCI bus %u.\n",
+ pConfigInfo->SystemIoBusNumber));
+
+
+ // Look at each device.
+
+ for (bDeviceNumber = bStartDeviceNumber;
+ bDeviceNumber < PCI_MAX_DEVICES;
+ bDeviceNumber++ ) {
+
+ // Set the device number in the PCI slot number.
+
+ PciSlotNumber.u.bits.DeviceNumber = bDeviceNumber;
+
+ // Look at each function of the device.
+
+ for (bFunctionNumber = bStartFunctionNumber;
+ bFunctionNumber < PCI_MAX_FUNCTION;
+ bFunctionNumber++) {
+ // Set the function number in the PCI slot number.
+
+ PciSlotNumber.u.bits.FunctionNumber = bFunctionNumber;
+
+ // Get the PCI configuration data for the slot.
+
+ DebugPrint( (4, "DAZZLER: Searching device %#x, function %x.\n",
+ bDeviceNumber, bFunctionNumber) );
+
+ ulBytes = ScsiPortGetBusData(pDeviceExtension,
+ PCIConfiguration,
+ pConfigInfo->SystemIoBusNumber,
+ PciSlotNumber.u.AsULONG,
+ &PciConfigHeader,
+ PCI_COMMON_HDR_LENGTH);
+
+ if (ulBytes == 0) {
+ // Out of PCI data for this bus.
+
+ DebugPrint((4, "DAZZLER: No more PCI devices on bus!\n"));
+
+ pIDAContext->PciAddress.bDeviceNumber = 0;
+ pIDAContext->PciAddress.bFunctionNumber = 0;
+
+ return (SP_RETURN_NOT_FOUND);
+ }
+
+ // Check for a valid vendor ID.
+
+#ifdef DBG
+ if (PciConfigHeader.VendorID != PCI_INVALID_VENDORID) {
+
+ // print out the PciConfigHeader
+
+ DebugPrint((4,
+ "PciConfigHeader: VendorId=%x DeviceId=%x Command=%x Status=%x\n"
+ ,PciConfigHeader.VendorID,
+ PciConfigHeader.DeviceID,
+ PciConfigHeader.Command) );
+
+ DebugPrint((4,
+ "RevisionID=%x ProgIf=%x SubClass=%x BaseClass=%x CacheLineSize=%x\n",
+ PciConfigHeader.RevisionID,
+ PciConfigHeader.ProgIf,
+ PciConfigHeader.SubClass,
+ PciConfigHeader.BaseClass,
+ PciConfigHeader.CacheLineSize) );
+
+ DebugPrint((4,"LatencyTimer=%x HeaderType=%x BIST=%x\n",
+ PciConfigHeader.LatencyTimer,
+ PciConfigHeader.HeaderType,
+ PciConfigHeader.BIST) );
+ }
+#endif
+
+ if (PciConfigHeader.VendorID == PCI_INVALID_VENDORID) {
+ // No PCI device or no more functions on the current device.
+ // Go to the next device.
+
+ break;
+ }
+
+
+ // PCI controller found. Next check to see if it is one of the
+ // controllers being searched for.
+
+ if ((PciConfigHeader.VendorID ==
+ pIDAContext->PciIdentifier.usVendorID) &&
+ (PciConfigHeader.DeviceID ==
+ pIDAContext->PciIdentifier.usDeviceID)) {
+ DebugPrint( (4, "DAZZLER: Found PCI controller.\n") );
+
+ // Check if the controller is enabled.
+
+ if ((PciConfigHeader.Command & PCI_ENABLE_IO_SPACE) &&
+ (PciConfigHeader.Command & PCI_ENABLE_MEMORY_SPACE)) {
+ DebugPrint( (4, "DAZZLER: Controller is enabled.\n") );
+
+ //
+ // Get PCI Id placed in offset 0x2c and place into the
+ // device extension EisaId.
+ //
+
+// ulTmp1 = ulTmp2 = PciConfigHeader.u.type0.Reserved1[1];
+ ulTmp1 = ulTmp2 = ((((ULONG) PciConfigHeader.u.type0.SubSystemID) << 16) |
+ PciConfigHeader.u.type0.SubVendorID);
+
+ // Fix it because the bytes are swapped
+
+ ulTmp1 &= 0xff00ff00;
+ ulTmp2 &= 0x00ff00ff;
+
+ ulTmp1 = ulTmp1 >> 8;
+ ulTmp2 = ulTmp2 << 8;
+
+ ((PDEVICE_EXTENSION)pDeviceExtension)->EisaId = ulTmp1 | ulTmp2;
+
+ // Set starting address for the next search.
+
+ pIDAContext->PciAddress.bDeviceNumber = bDeviceNumber;
+ pIDAContext->PciAddress.bFunctionNumber = bFunctionNumber + 1;
+
+
+ // Get the PCI resource requirements for the controller.
+
+ ulInitStatus = GetPciResources(pDeviceExtension,
+ &PciConfigHeader,
+ PciSlotNumber.u.AsULONG,
+ pConfigInfo);
+
+ if (ulInitStatus != SP_RETURN_FOUND) {
+
+ DebugPrint((0,
+ "DAZZLER: Could not get PCI resources for controller!\n"));
+
+ return (ulInitStatus);
+ }
+
+ // Get the PCI specifics for the controller.
+
+ ulInitStatus = GetPciSpecifics(pDeviceExtension,
+ pIDAContext,
+ pConfigInfo,
+ &PciSlotNumber);
+
+ if (ulInitStatus != SP_RETURN_FOUND) {
+ DebugPrint((0, "DAZZLER: Could not get PCI specifics for controller!\n") );
+ }
+
+ return (ulInitStatus);
+
+ } else {
+ DebugPrint( (4, "DAZZLER: Controller is disabled.\n") );
+ continue;
+ }
+
+ } // end if ((PciConfigHeader.VendorID == ...) &&
+
+
+ } // end for (bFunctionNumber = bStartFunctionNumber; ...)
+
+
+ // Reset the initial starting function number.
+
+ bStartFunctionNumber = 0;
+
+ } // end for (bDeviceNumber = bStartDeviceNumber; ...)
+
+
+ // A controller was not found.
+
+ DebugPrint( (4,
+ "DAZZLER: Failed to find any PCI controllers this pass.\n") );
+
+ pIDAContext->PciAddress.bDeviceNumber = 0;
+ pIDAContext->PciAddress.bFunctionNumber = 0;
+
+ return (SP_RETURN_NOT_FOUND);
+
+} // end IDAFindPci()
+
+
+
+STATIC
+ULONG
+GetPciResources(
+ IN PVOID pDeviceExtension,
+ IN PPCI_COMMON_CONFIG pPciConfigHeader,
+ IN ULONG ulPciSlotNumber,
+ IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo
+ )
+
+
+/*++
+
+Routine Description:
+
+ This routine gets the resources required by the input PCI controller.
+
+
+Arguments:
+
+ pDeviceExtension - pointer to the miniport driver's per-controller
+ storage area
+ pPciConfigHeader - pointer to the controller's PCI configuration header
+ ulPciSlotNumber - the PCI controller's address represented as a ULONG
+ pConfigInfo - pointer to SCSI port configuration information
+
+
+Return Values:
+
+ pConfigInfo - pointer to SCSI port configuration information. The access
+ range elements of the structure are filled in with the resources
+ required by the controller.
+
+ Function Return Values:
+
+ SP_RETURN_FOUND - Used to indicate that the HBA was successfully
+ initialized.
+
+ SP_RETURN_ERROR - Used to indicate that the HBA could not be properly
+ initilaized.
+
+-- */
+
+{
+ PACCESS_RANGE pAccessRange;
+ PCI_COMMON_CONFIG PciTmpCfgHdr;
+ ULONG ulAddressSpaceMask = 0xFFFFFFFF;
+ ULONG ulAddressSpace, ulAddressLength, ulBytes;
+ USHORT i;
+
+
+ DebugPrint( (2, "\nDAZZLER: Enter function GetPciResources.\n") );
+
+
+ // Verify the number of available access ranges.
+
+ if (pConfigInfo->NumberOfAccessRanges > IDA_PCI_NUM_ACCESS_RANGES) {
+
+ DebugPrint((0,
+ "DAZZLER: # of access ranges invalid for PCI controller.\n"));
+
+ return (SP_RETURN_ERROR);
+ }
+
+
+ // Get the resources required for each PCI base address.
+
+ for (i = 0; i < IDA_PCI_NUM_ACCESS_RANGES; i++) {
+ // Get pointer to the access range element to fill out.
+
+ pAccessRange = &((*(pConfigInfo->AccessRanges))[i]);
+
+
+ // Check to see if the PCI base address is memory or I/O.
+
+ if (pPciConfigHeader->u.type0.BaseAddresses[i] & PCI_ADDRESS_IO_SPACE) {
+
+ // Address is an I/O address.
+
+ pAccessRange->RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(pPciConfigHeader->
+ u.type0.BaseAddresses[i] &
+ ~PCI_ADDRESS_IO_SPACE);
+
+ pAccessRange->RangeInMemory = FALSE;
+ } else {
+ // Address is a memory address.
+
+ ASSERT((pPciConfigHeader->u.type0.BaseAddresses[i] &
+ PCI_ADDRESS_MEMORY_TYPE_MASK) & PCI_TYPE_32BIT);
+
+ pAccessRange->RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(pPciConfigHeader->
+ u.type0.BaseAddresses[i] &
+ 0xfffffff0);
+
+ pAccessRange->RangeInMemory = TRUE;
+
+ DebugPrint((4, "pAccessRange->RangeStart.Hi/Low=%x %x ->RangeLength=%x\n",
+ pAccessRange->RangeStart.HighPart,pAccessRange->
+ RangeStart.LowPart,
+ pAccessRange->RangeLength));
+ }
+
+
+ // Get the amount of address space required. This is done by writing all
+ // 1's to the register and then reading the value back. The device will
+ // return 0's in all don't care bits. The first signficant bit set beyond
+ // those used to indicate memory or I/O determines the address space
+ // required. Finally, the register is restored with the original address.
+
+ ulBytes = ScsiPortSetBusDataByOffset(pDeviceExtension, PCIConfiguration,
+ pConfigInfo->SystemIoBusNumber,
+ ulPciSlotNumber,
+ (PVOID) &ulAddressSpaceMask,
+ FIELD_OFFSET(PCI_COMMON_CONFIG,
+ u.type0.BaseAddresses[i]),
+ sizeof(ULONG)
+ );
+
+ if (ulBytes == 0) {
+ DebugPrint((0,
+ "DAZZLER: Could not set PCI slot information for slot %u.\n",
+ ulPciSlotNumber));
+
+ return (SP_RETURN_ERROR);
+ }
+
+
+ // Read the value back.
+
+ ulBytes = ScsiPortGetBusData( pDeviceExtension,
+ PCIConfiguration,
+ pConfigInfo->SystemIoBusNumber,
+ ulPciSlotNumber,
+ &PciTmpCfgHdr,
+ PCI_COMMON_HDR_LENGTH );
+
+ if (ulBytes == 0) {
+
+ DebugPrint((0,
+ "DAZZLER: Could not get PCI information for slot %u.\n",
+ ulPciSlotNumber)
+ );
+
+ return(SP_RETURN_ERROR);
+ }
+
+
+ // Determine the space required by finding the first bit set.
+
+ ulAddressSpace = PciTmpCfgHdr.u.type0.BaseAddresses[i];
+ ulAddressLength = 1 << ((ulAddressSpace & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
+
+ while (!(ulAddressSpace & ulAddressLength) && ulAddressLength) {
+ ulAddressLength <<= 1;
+ }
+
+ // Set the access range length.
+
+ pAccessRange->RangeLength = ulAddressLength;
+
+ DebugPrint((4,
+ "pAccessRange->RangeStart.Hi/Low=%x %x ->RangeLength=%x\n",
+ pAccessRange->RangeStart.HighPart,pAccessRange->
+ RangeStart.LowPart,
+ pAccessRange->RangeLength)
+ );
+
+ // Reset the base address register to its original value.
+
+ ulBytes = ScsiPortSetBusDataByOffset(pDeviceExtension,
+ PCIConfiguration,
+ pConfigInfo->SystemIoBusNumber,
+ ulPciSlotNumber,
+ (PVOID) &pPciConfigHeader->
+ u.type0.BaseAddresses[i],
+ FIELD_OFFSET(PCI_COMMON_CONFIG,
+ u.type0.BaseAddresses[i]),
+ sizeof(ULONG)
+ );
+
+ if (ulBytes == 0) {
+
+ DebugPrint((0, "DAZZLER: Could not set PCI information for slot %u.\n",
+ ulPciSlotNumber));
+
+ return(SP_RETURN_ERROR);
+ }
+
+ } // end for (i = 0;...)
+
+
+ // Set the IRQ information in the port configuration data structure.
+
+ pConfigInfo->BusInterruptLevel = pPciConfigHeader->u.type0.InterruptLine;
+ pConfigInfo->InterruptMode = LevelSensitive;
+
+ // Return success.
+
+ return(SP_RETURN_FOUND);
+
+} // end GetPciResources
+
+
+
+
+STATIC
+ULONG
+GetPciSpecifics(
+ IN OUT PVOID pDeviceExtension,
+ IN OUT PIDA_CONTEXT pIDAContext,
+ IN OUT PPORT_CONFIGURATION_INFORMATION pConfigInfo,
+ IN PPCI_SLOT_NUMBER pPciSlotNumber
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called to complete initialization of the port configuration
+ information for the input controller. This function also begins the
+ configuration of the SMGR for the controller. Finally, the function makes
+ the call to initialize the controller.
+
+
+Arguments:
+
+ pDeviceExtension - Miniport driver's per-controller storage area
+ pIDAContext - Context value passed to ScsiPortInitialize()
+ pConfigInfo - pointer to SCSI port configuration information
+
+
+Return Values:
+
+ pDeviceExtension - Minport driver's per-controller storage area
+ pIDAContext - Context value passed to ScsiPortInitialize()
+ pConfigInfo - pointer to SCSI port configuration information
+
+
+ Function Return Values:
+
+ SP_RETURN_FOUND - Used to indicate that the HBA was successfully
+ initialized.
+
+ SP_RETURN_ERROR - Used to indicate that the HBA could not be properly
+ initilaized.
+
+-- */
+
+{
+ PACCESS_RANGE pAccessRange;
+ PDEVICE_EXTENSION pIDADeviceExtension = pDeviceExtension;
+ PVOID pBaseAddress, pIoAddress = NULL, pMemoryAddress = NULL;
+ ULONG ulInitStatus, ulPhysicalMemoryAddress;
+ USHORT i;
+
+
+ DebugPrint( (6, "Enter function GetPciSpecifics.\n") );
+
+
+ // Get and verify the access ranges and its length.
+
+ for (i = 0; i < IDA_PCI_NUM_ACCESS_RANGES - 1; i++) {
+ pAccessRange = &((*(pConfigInfo->AccessRanges))[i]);
+ ASSERT(pAccessRange->RangeLength != 0);
+
+ // Get the mapped system address.
+
+ pBaseAddress = ScsiPortGetDeviceBase(pDeviceExtension,
+ pConfigInfo->AdapterInterfaceType,
+ pConfigInfo->SystemIoBusNumber,
+ pAccessRange->RangeStart,
+ pAccessRange->RangeLength,
+ (BOOLEAN)!pAccessRange->RangeInMemory
+ );
+
+ if (pBaseAddress == NULL) {
+
+ DebugPrint( (0,
+ "DAZZLER: Error getting base addr. for PCI controller.\n"));
+
+ return (SP_RETURN_ERROR);
+ }
+
+
+ // Set the appropriate pointer to the mapped address.
+
+ if (pAccessRange->RangeInMemory) {
+ pMemoryAddress = pBaseAddress;
+
+ ulPhysicalMemoryAddress =
+ ScsiPortConvertPhysicalAddressToUlong( pAccessRange->RangeStart );
+
+ } else {
+ pIoAddress = pBaseAddress;
+ }
+
+ } // end for (i = 0;...)
+
+
+ // Debug checks
+
+ ASSERT(pIoAddress);
+ ASSERT(pMemoryAddress);
+
+ DebugPrint((4,
+ "DAZZLER: PCI controller I/O base address = %0#10x\n",
+ pIoAddress)
+ );
+
+ DebugPrint((4,
+ "DAZZLER: PCI controller memory base address = %0#10x\n",
+ pMemoryAddress)
+ );
+
+ DebugPrint((4,
+ "DAZZLER: PCI controller physical memory address = %0#10x\n",
+ ulPhysicalMemoryAddress)
+ );
+
+ DebugPrint((4,
+ "DAZZLER: PCI controller bus number = %#x\n",
+ pConfigInfo->SystemIoBusNumber)
+ );
+
+ DebugPrint((4,
+ "DAZZLER: PCI controller device number = %#x\n",
+ pPciSlotNumber->u.bits.DeviceNumber)
+ );
+
+ DebugPrint((4,
+ "DAZZLER: PCI controller function number = %#x\n",
+ pPciSlotNumber->u.bits.FunctionNumber)
+ );
+
+ // Finish initalizing the port configuration information
+
+ pConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_SIZE;
+ pConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SG_DESCRIPTORS;
+ pConfigInfo->ScatterGather = TRUE;
+ pConfigInfo->Master = TRUE;
+ pConfigInfo->NumberOfBuses = 1;
+ pConfigInfo->Dma32BitAddresses = TRUE;
+ pConfigInfo->MaximumNumberOfTargets = 32;
+
+ //
+ // Setup some vars needed for the IDENTIFY commands
+ //
+
+ pConfigInfo->CachesData = TRUE;
+ pIDADeviceExtension->SectorShift = 9;
+ pIDADeviceExtension->IdentifyBuffer =
+ ScsiPortGetUncachedExtension(pDeviceExtension,
+ pConfigInfo,
+ 512);
+
+ // Fill in the HBA configuration data in the device extension.
+
+ if (pMemoryAddress) {
+
+ pIDADeviceExtension->HBAConfiguration.ulBaseIOAddress =
+ (ULONG)pMemoryAddress;
+
+ pIDADeviceExtension->CPFIFO =
+ (PULONG)((PUCHAR)pMemoryAddress+IDA_PCI_CPFIFO_OFFSET);
+
+ pIDADeviceExtension->CCFIFO =
+ (PULONG)((PUCHAR)pMemoryAddress+IDA_PCI_CCFIFO_OFFSET);
+
+ pIDADeviceExtension->InterruptMask =
+ (PULONG)((PUCHAR)pMemoryAddress+IDA_PCI_MASK_OFFSET);
+
+ pIDADeviceExtension->InterruptStatus = (PULONG)((PUCHAR)pMemoryAddress+
+ IDA_PCI_STATUS_OFFSET);
+
+ pIDADeviceExtension->InterruptPending = (PULONG)((PUCHAR)pMemoryAddress+
+ IDA_PCI_PENDING_OFFSET);
+
+ ulInitStatus = SP_RETURN_FOUND;
+ }
+
+ if (pIoAddress) {
+ pIDADeviceExtension->BaseIOAddress = (ULONG)pIoAddress;
+ }
+
+ pIDADeviceExtension->HBAConfiguration.bHBAModel = pIDAContext->bHBAModel;
+ pIDADeviceExtension->HBAConfiguration.bHBAIoBusType = PCI_BUS;
+
+ pIDADeviceExtension->HBAConfiguration.HBAIoBusData.PciAddress.
+ bPCIBusNumber = (BYTE)pConfigInfo->SystemIoBusNumber;
+
+ pIDADeviceExtension->HBAConfiguration.HBAIoBusData.PciAddress.
+ bDeviceNumber = (BYTE)pPciSlotNumber->u.bits.DeviceNumber;
+
+ pIDADeviceExtension->HBAConfiguration.HBAIoBusData.PciAddress.
+ bFunctionNumber = (BYTE)pPciSlotNumber->u.bits.FunctionNumber;
+
+ pIDADeviceExtension->HBAConfiguration.bNumScsiBuses =
+ pConfigInfo->NumberOfBuses;
+
+ DebugPrint((1,
+ "CCFIFO=0x%x CPFIFO=0x%x InterruptMask=0x%x InterruptStatus=0x%x InterruptPending=0x%x BaseIo=0x%x\n",
+ pIDADeviceExtension->CCFIFO,
+ pIDADeviceExtension->CPFIFO,
+ pIDADeviceExtension->InterruptMask,
+ pIDADeviceExtension->InterruptStatus,
+ pIDADeviceExtension->InterruptPending,
+ pIDADeviceExtension->BaseIOAddress )
+ );
+
+
+ if (ulInitStatus != SP_RETURN_FOUND) {
+
+ // Free the device base for this controller.
+
+ ScsiPortFreeDeviceBase(pDeviceExtension, pIoAddress);
+ ScsiPortFreeDeviceBase(pDeviceExtension, pMemoryAddress);
+
+ } // end if (ulInitStatus != SP_RETURN_FOUND)
+
+ return (ulInitStatus);
+
+} // end GetPciSpecifics()
+
+
+ULONG
+DriverEntry(
+ IN PVOID pDriverObject,
+ IN PVOID pArgument2
+ )
+
+/*++
+
+Routine Description:
+
+ Installable driver initialization entry point. This function initializes
+ the hardware initialization data structure and begins the process of
+ finding controllers that the driver supports.
+
+
+Arguments:
+
+ pDriverObject - Pointer to the driver's driver object.
+ pArgument2 - Pointer to driver's entry in the Registry.
+
+
+Return Value:
+
+ Status from ScsiPortInitialize()
+
+-- */
+
+{
+ IDA_CONTEXT IDAContext;
+ HW_INITIALIZATION_DATA hwInitializationData;
+ ULONG i, ulStatus, ulReturnStatus=0;
+ ULONG eisaSlotNumber;
+ UCHAR deviceId[8] = {'0', '0', '4', '0', '1', '1', '0', 'E'};
+
+ DebugPrint((0,"\n\nCompaq Disk Array Miniport Driver\n"));
+
+ //
+ // Zero out structure.
+ //
+
+ for (i=0; i<sizeof(HW_INITIALIZATION_DATA); i++) {
+ ((PUCHAR)&hwInitializationData)[i] = 0;
+ }
+
+ //
+ // Set size of hwInitializationData.
+ //
+
+ hwInitializationData.HwInitializationDataSize =
+ sizeof(HW_INITIALIZATION_DATA);
+
+ //
+ // Set entry points.
+ //
+
+ hwInitializationData.HwInitialize = IdaInitialize;
+ hwInitializationData.HwResetBus = IdaResetBus;
+ hwInitializationData.HwStartIo = IdaStartIo;
+ hwInitializationData.HwInterrupt = IdaInterrupt;
+ hwInitializationData.HwFindAdapter = IdaFindAdapter;
+
+ //
+ // Indicate no buffer mapping but will need physical addresses.
+ //
+
+ hwInitializationData.NeedPhysicalAddresses = TRUE;
+
+ //
+ // Specify size of extensions.
+ //
+
+ hwInitializationData.DeviceExtensionSize =
+ sizeof(DEVICE_EXTENSION);
+ hwInitializationData.SpecificLuExtensionSize =
+ sizeof(LOGICAL_UNIT_EXTENSION);
+
+ //
+ // Specifiy the bus type.
+ //
+
+ hwInitializationData.AdapterInterfaceType = Eisa;
+ hwInitializationData.NumberOfAccessRanges = 3;
+
+ //
+ // Ask for SRB extensions for command lists.
+ //
+
+ hwInitializationData.SrbExtensionSize = sizeof(COMMAND_LIST);
+
+ //
+ // Indicate that this controller supports multiple outstand
+ // requests to its devices.
+ //
+
+ hwInitializationData.MultipleRequestPerLu = TRUE;
+ hwInitializationData.AutoRequestSense = TRUE;
+
+ //
+ // Set the context parameter to indicate that the search for controllers
+ // should start at the first EISA slot. This is only for a manual search
+ // by the miniport driver, if the system does not pass in predetermined
+ // configuration.
+ //
+
+ eisaSlotNumber = 0;
+
+ //
+ // Indicate EISA id.
+ //
+
+ hwInitializationData.DeviceId = &deviceId;
+ hwInitializationData.DeviceIdLength = 8;
+
+ //
+ // Call the system to search for this adapter.
+ //
+
+ ulReturnStatus = ScsiPortInitialize(pDriverObject, pArgument2,
+ &hwInitializationData,
+ &eisaSlotNumber
+ );
+
+ // Initialize hardware initialization data structure to zeroes.
+
+ for (i = 0; i < sizeof(HW_INITIALIZATION_DATA); i++) {
+ ((PUCHAR)&hwInitializationData)[i] = 0;
+ }
+
+ // Fill in the hardware initialization data structure.
+
+ hwInitializationData.HwInitializationDataSize =
+ sizeof(HW_INITIALIZATION_DATA);
+
+ // Set driver entry points.
+
+ hwInitializationData.HwInitialize = IdaInitializePCI;
+ hwInitializationData.HwStartIo = IdaStartIo;
+ hwInitializationData.HwInterrupt = IdaInterrupt;
+ hwInitializationData.HwResetBus = IdaResetBus;
+ hwInitializationData.HwDmaStarted = NULL;
+ hwInitializationData.HwAdapterState = NULL;
+
+
+ // Specify size of extensions.
+
+ hwInitializationData.DeviceExtensionSize = sizeof(DEVICE_EXTENSION);
+
+ hwInitializationData.SpecificLuExtensionSize =
+ sizeof(LOGICAL_UNIT_EXTENSION);
+
+ hwInitializationData.SrbExtensionSize = sizeof(COMMAND_LIST);
+
+
+ // Initialize other data.
+
+ hwInitializationData.MapBuffers = FALSE;
+ hwInitializationData.NeedPhysicalAddresses = TRUE;
+ hwInitializationData.TaggedQueuing = TRUE;
+ hwInitializationData.AutoRequestSense = TRUE;
+ hwInitializationData.MultipleRequestPerLu = TRUE;
+ hwInitializationData.ReceiveEvent = FALSE;
+
+ //
+ // Indicate that this controller supports multiple outstanding
+ // requests to its devices.
+ //
+
+ hwInitializationData.MultipleRequestPerLu = TRUE;
+ hwInitializationData.AutoRequestSense = TRUE;
+
+ // Setup required values to find PCI Compaq 32-Bit Array controllers.
+
+ hwInitializationData.AdapterInterfaceType = PCIBus;
+ hwInitializationData.NumberOfAccessRanges = IDA_PCI_NUM_ACCESS_RANGES;
+ hwInitializationData.HwFindAdapter = IDAFindPci;
+
+ IDAContext.bHBAModel = IDA_PCI_DAZZLER;
+ IDAContext.PciAddress.bPCIBusNumber = 0;
+ IDAContext.PciAddress.bDeviceNumber = 0;
+ IDAContext.PciAddress.bFunctionNumber = 0;
+ IDAContext.PciIdentifier.usVendorID = IDA_PCI_COMPAQ_ID;
+ IDAContext.PciIdentifier.usDeviceID = IDA_PCI_DAZZLER_DEVICE_ID;
+
+
+ ulStatus = ScsiPortInitialize(pDriverObject,
+ pArgument2,
+ &hwInitializationData,
+ &IDAContext
+ );
+
+ DebugPrint((0, "DAZZLER: PCI search status = %0#10x\n", ulStatus));
+
+ ulReturnStatus = (ulReturnStatus < ulStatus) ? ulReturnStatus : ulStatus;
+
+ // Return the final status value.
+
+ DebugPrint( (4, "DAZZLER: Final status = %0#10x\n", ulReturnStatus) );
+
+ return (ulReturnStatus);
+
+} // end DriverEntry()
+
+VOID
+IdaDisableInts(
+ IN PDEVICE_EXTENSION pDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Called for SCSI inquiry processing (GetDiskIdentifyData) to eliminate
+ interupts generated at the controller. This was added to support
+ Online Blazer and the ability to recognize Logical Volumes that may
+ be added while the system is running. GetDiskIdentifyData submits
+ the inquiry packet and returns to Startio when this packet has been
+ processed by the controller. Interrupts at the adapter are temporarily
+ disabled because we must wait for completion and we want to get back
+ in a timely fashion.
+
+Arguments:
+
+ pDeviceExtension - pointer to adapter's device extension.
+
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = pDeviceExtension;
+ PULONG pIntMask;
+
+ if (deviceExtension->HBAConfiguration.bHBAModel == IDA_PCI_DAZZLER) {
+
+ //
+ // we are looking at the Dazzler/P...use memapped io
+ //
+
+ pIntMask = deviceExtension->InterruptMask;
+ *pIntMask = 0L;
+ } else
+
+ if (deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER
+ && !deviceExtension->PCIoff)
+
+ //
+ // we are dealing with Dazzler/E use mapped portio
+ //
+
+ ScsiPortWritePortUlong(&deviceExtension->eisapci->InterruptMask, 0L);
+ else {
+
+ //
+ // ida and prior adapter....BMIC interface
+ //
+
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->InterruptControl, 0);
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBellMask, 0);
+ }
+
+} // end IdaDisableInts
+
+VOID
+IdaEnableInts(
+ IN PDEVICE_EXTENSION pDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Called for SCSI inquiry processing (GetDiskIdentifyData) to enable
+ interupts generated at the controller. This was added to support
+ Online Blazer and the ability to recognize Logical Volumes that may
+ be added while the system is running. GetDiskIdentifyData submits
+ the inquiry packet and returns to Startio when this packet has been
+ processed by the controller. Interrupts at the adapter are temporarily
+ disabled because we must wait for completion and we want to get back
+ in a timely fashion.
+
+Arguments:
+
+ pDeviceExtension - pointer to adapter's device extension.
+
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PDEVICE_EXTENSION deviceExtension = pDeviceExtension;
+ PULONG pIntMask;
+
+ if (deviceExtension->HBAConfiguration.bHBAModel == IDA_PCI_DAZZLER) {
+
+ //
+ // Dazzler/P...use memmapped io
+ //
+
+ pIntMask = deviceExtension->InterruptMask;
+ *pIntMask = 1L;
+ } else
+
+ if (deviceExtension->HBAConfiguration.bHBAModel == IDA_EISA_DAZZLER
+ && !deviceExtension->PCIoff)
+
+ //
+ // Dazzler/E...use 32bit port io
+ //
+
+ ScsiPortWritePortUlong(&deviceExtension->eisapci->InterruptMask, 1L);
+ else {
+
+ //
+ // ida and prior...use BMIC interface
+ //
+
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->InterruptControl, 1);
+ ScsiPortWritePortUchar(&deviceExtension->Bmic->SystemDoorBellMask, 1);
+ }
+
+} // end IdaEnableInts