diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/miniport/spock/spock.c | 2150 |
1 files changed, 2150 insertions, 0 deletions
diff --git a/private/ntos/miniport/spock/spock.c b/private/ntos/miniport/spock/spock.c new file mode 100644 index 000000000..fbcad5932 --- /dev/null +++ b/private/ntos/miniport/spock/spock.c @@ -0,0 +1,2150 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + spock.c + +Abstract: + + This is the NT SCSI miniport driver for the IBM MCA SCSI adapter. + +Author: + + Mike Glass + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ +#include "miniport.h" +#include "mca.h" +#include "scsi.h" + +#define MAXIMUM_ERRORS 10 + +// +// The following table specifies the ports to be checked when searching for +// an adapter. A zero entry terminates the search. +// + +CONST ULONG AdapterAddresses[] = {0X3540, 0X3548, 0X3550, 0X3558, + 0X3560, 0X3568, 0x3570, 0x3578, 0}; + +// +// Device extension +// + +typedef struct _HW_DEVICE_EXTENSION { + + // + // Adapter parameters + // + + PMCA_REGISTERS Registers; + + // + // Disk activity light count + // + + ULONG ActiveRequests; + + ULONG ErrorCount; + + UCHAR HostTargetId; + +} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION; + +// +// Logical unit extension +// + +typedef struct _HW_LOGICAL_UNIT { + + PSCB Scb; + PSCSI_REQUEST_BLOCK AbortSrb; + PSCSI_REQUEST_BLOCK CurrentSrb; + +} HW_LOGICAL_UNIT, *PHW_LOGICAL_UNIT; + +// +// Noncached version extension. +// + +typedef struct _HW_ADAPTER_INFOMATION { + SCB Scb; + ADAPTER_INFORMATION AdapterInfo; +}HW_ADAPTER_INFOMATION, *PHW_ADAPTER_INFOMATION; + + +// +// Function declarations +// + +ULONG +DriverEntry ( + IN PVOID DriverObject, + IN PVOID Argument2 + ); + +ULONG +SpockConfiguration( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ); + +BOOLEAN +SpockInitialize( + IN PVOID HwDeviceExtension + ); + +BOOLEAN +SpockStartIo( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +SpockInterrupt( + IN PVOID HwDeviceExtension + ); + +BOOLEAN +SpockResetBus( + IN PVOID HwDeviceExtension, + IN ULONG PathId + ); + +BOOLEAN +SpockAbortIo( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +BuildScb( + IN PHW_DEVICE_EXTENSION HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +BuildSgl( + IN PVOID DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +BuildReadCapacity( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +ULONG +McaAdapterPresent( + IN PHW_DEVICE_EXTENSION HwDeviceExtension, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN OUT PULONG AdapterCount, + OUT PBOOLEAN Again + ); + +VOID +MapTsbError( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PTSB Tsb + ); + +BOOLEAN +IssueScbCommand( + IN PVOID DeviceExtension, + IN ULONG PhysicalScb, + IN UCHAR TargetId + ); + +BOOLEAN +IssueImmediateCommand( + IN PVOID HwDeviceExtension, + IN ULONG ImmediateCommand, + IN UCHAR TargetId + ); + + +// +// Routines start +// + +ULONG +DriverEntry ( + IN PVOID DriverObject, + IN PVOID Argument2 + ) + +/*++ + +Routine Description: + + Installable driver initialization entry point for system. + +Arguments: + + Driver Object is passed to ScsiPortInitialize() + +Return Value: + + Status from ScsiPortInitialize() + +--*/ + +{ + HW_INITIALIZATION_DATA hwInitializationData; + ULONG adapterCount; + ULONG i; + + DebugPrint((1,"\n\nMCA SCSI 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 = SpockInitialize; + hwInitializationData.HwFindAdapter = SpockConfiguration; + hwInitializationData.HwStartIo = SpockStartIo; + hwInitializationData.HwInterrupt = SpockInterrupt; + hwInitializationData.HwResetBus = SpockResetBus; + + hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION); + hwInitializationData.SpecificLuExtensionSize = sizeof(HW_LOGICAL_UNIT); + + // + // Set number of access ranges and bus type.. + // + + hwInitializationData.NumberOfAccessRanges = 1; + hwInitializationData.AdapterInterfaceType = MicroChannel; + + // + // Indicate no buffer mapping but will need physical addresses. + // + + hwInitializationData.NeedPhysicalAddresses = TRUE; + + // + // Ask for SRB extensions for SCBs. + // + + hwInitializationData.SrbExtensionSize = sizeof(SCB); + + // + // The adapter count is used by McaAdapterPresent routine to track + // which adapter addresses have been searched. + // + + adapterCount = 0; + + return ScsiPortInitialize(DriverObject, + Argument2, + &hwInitializationData, + &adapterCount); + +} // end DriverEntry() + + +ULONG +SpockConfiguration( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ) + +/*++ + +Routine Description: + + Called from ScsiPortInitialize to collect adapter configuration + and capability information. + +Arguments: + + HwDevice Extension + Context - Pointer to adapters initialized count + BusInformation + ArgumentString - Not used + ConfigInfo - Configuration information structure describing HBA + Again - Indicates init routine should be called again + +Return Value: + + ULONG + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PHW_ADAPTER_INFOMATION adapterInfo; + ULONG physicalAdapterInfo; + ULONG status; + UCHAR basicStatus; + + // + // Assume initialization will not need to be called again. + // + + *Again = FALSE; + + // + // Search for IBM SCSI adapters. + // + + status = McaAdapterPresent(HwDeviceExtension, + ConfigInfo, + Context, + Again); + + // + // If there are not adapter's found then return. + // + + if (status != SP_RETURN_FOUND) { + return(status); + } + + // + // Set IRQ to 14. + // + + ConfigInfo->BusInterruptLevel = 14; + ConfigInfo->NumberOfBuses = 1; + ConfigInfo->InterruptMode = LevelSensitive; + ConfigInfo->InitiatorBusId[0] = 7; + deviceExtension->HostTargetId = 7; + ConfigInfo->Master = TRUE; + ConfigInfo->ScatterGather = TRUE; + + // + // Indicate maximum transfer length is 16M. + // + + ConfigInfo->MaximumTransferLength = MAXIMUM_DATA_TRANSFER; + + // + // Maximum number of physical segments is 16. + // + + ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SDL_SIZE; + + // + // Get an noncached extension for the adapter information. + // + + adapterInfo = ScsiPortGetUncachedExtension(HwDeviceExtension, + ConfigInfo, + sizeof(HW_ADAPTER_INFOMATION)); + + if (adapterInfo == NULL) { + return SP_RETURN_BAD_CONFIG; + } + + physicalAdapterInfo = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress( + HwDeviceExtension, + NULL, + adapterInfo, + &status)); + + if (status == 0 || physicalAdapterInfo == SP_UNINITIALIZED_VALUE) { + return SP_RETURN_BAD_CONFIG; + } + + // + // Disable the adapter interrupt. The interrupts will be enabled + // when the adapter is initialized. + // + + basicStatus = ScsiPortReadPortUchar( + &deviceExtension->Registers->BaseControl); + + basicStatus &= ~INTERRUPT_ENABLE; + + ScsiPortWritePortUchar(&deviceExtension->Registers->BaseControl, basicStatus); + + // + // Build a get POS and adapter information command. + // + + adapterInfo->Scb.Command = SCB_COMMAND_GET_POS; + adapterInfo->Scb.EnableFlags = SCB_ENABLE_READ | SCB_ENABLE_TSB_ON_ERROR | + SCB_ENABLE_RETRY_ENABLE | SCB_ENABLE_BYPASS_BUFFER; + adapterInfo->Scb.CdbSize = 0; + adapterInfo->Scb.Reserved = 0; + adapterInfo->Scb.BufferAddress = physicalAdapterInfo + + offsetof(HW_ADAPTER_INFOMATION, AdapterInfo); + adapterInfo->Scb.BufferLength = sizeof(adapterInfo->AdapterInfo); + adapterInfo->Scb.StatusBlock = physicalAdapterInfo + + offsetof(HW_ADAPTER_INFOMATION, Scb.Tsb); + adapterInfo->Scb.NextScb = NULL; + + if (!IssueScbCommand(HwDeviceExtension, physicalAdapterInfo, 0x0f)) { + + DebugPrint((1, "SpockConfiguration: Could not issue get POS command.\n")); + + // + // Assume this is a bad adapter. Force no disconnects for all + // requests. + // + + deviceExtension->ErrorCount = MAXIMUM_ERRORS + 1; + + return SP_RETURN_FOUND; + } + + // + // Wait for the request to complete. + // + + for (status = 0; status < 1000; status++) { + + basicStatus = ScsiPortReadPortUchar(&deviceExtension->Registers->BasicStatus); + + if (basicStatus & BASIC_STATUS_INTERRUPT) { + break; + } + + ScsiPortStallExecution(10); + } + + + if (!(--deviceExtension->ActiveRequests)) { + + // + // Turn disk activity light off. + // + + DISK_ACTIVITY_LIGHT_OFF(); + } + + if (!(basicStatus & BASIC_STATUS_INTERRUPT)) { + + DebugPrint((1, "SpockConfiguration: Get POS command timed out.\n")); + + // + // Assume this is a bad adapter. Force no disconnects for all + // requests. + // + + deviceExtension->ErrorCount = MAXIMUM_ERRORS + 1; + return SP_RETURN_FOUND; + + } + + // + // Read interrupt status register to determine + // interrupting device and status. + // + + basicStatus = ScsiPortReadPortUchar( + &deviceExtension->Registers->InterruptStatus); + + // + // Acknowledge interrupt. + // + + status = 0; + while (ScsiPortReadPortUchar(&deviceExtension->Registers->BasicStatus) & + BASIC_STATUS_BUSY){ + + ScsiPortStallExecution(1); + + if (status++ > 10000) { + + DebugPrint((1, "SpockConfiguration: Wait for non-busy timed out.\n")); + + // + // Assume this is a bad adapter. Force no disconnects for all + // requests. + // + + deviceExtension->ErrorCount = MAXIMUM_ERRORS + 1; + return SP_RETURN_FOUND; + + } + } + + ScsiPortWritePortUchar(&deviceExtension->Registers->Attention, + (0x0f | END_OF_INTERRUPT)); + + // + // Bits 4-7 are interrupt status id. + // + + status = basicStatus >> 4; + + if (status != SCB_STATUS_SUCCESS && + status != SCB_STATUS_SUCCESS_WITH_RETRIES) { + + DebugPrint((1, "SpockConfiguration: Get POS command failed. Status = %hx\n", status)); + + // + // Assume this is a bad adapter. Force no disconnects for all + // requests. + // + + deviceExtension->ErrorCount = MAXIMUM_ERRORS + 1; + return SP_RETURN_FOUND; + + } + + DebugPrint((1, "SpockConfiguration: Retrived data is: %0.4hx\n",adapterInfo->AdapterInfo.RevisionLevel)); + + if (adapterInfo->AdapterInfo.RevisionLevel == 0xf) { + + DebugPrint((1, "SpockConfiguration: Found old firmware disabling disconnect!\n")); + deviceExtension->ErrorCount = MAXIMUM_ERRORS + 1; + + // + // Log nasty firmware. + // + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_BAD_FW_WARNING, + (10 << 16)); + } + + return SP_RETURN_FOUND; + +} // end SpockConfiguration() + + +BOOLEAN +SpockInitialize( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + Reset and inititialize Adapter. + +Arguments: + + DeviceExtension - Adapter object device extension. + +Return Value: + + TRUE + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + UCHAR basicStatus; + + // + // Issue feature control immediate command to disable + // adapter timing of SCBs. + // + + if (!IssueImmediateCommand(HwDeviceExtension, + (ULONG)SCB_COMMAND_FEATURE_CONTROL, + 0x0f)) { + + DebugPrint((1,"SpockInitialize: Set feature control failed\n")); + } + + // + // Enable the adapter interrupt. + // + + basicStatus = ScsiPortReadPortUchar(&deviceExtension->Registers->BaseControl); + + basicStatus |= INTERRUPT_ENABLE; + + ScsiPortWritePortUchar(&deviceExtension->Registers->BaseControl, basicStatus); + + return TRUE; + +} // end SpockInitialize() + +BOOLEAN +SpockStartIo( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Issue call to build SCB and SDL and write address and + command to adapter. + +Arguments: + + HwDeviceExtension + Srb + +Return Value: + + TRUE. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCB scb; + ULONG physicalScb; + ULONG length; + PHW_LOGICAL_UNIT logicalUnit; + + // + // Make sure that the request is for a valid SCSI bus and LUN as + // the IBM SCSI card does random things if address is wrong. + // + + if (Srb->PathId != 0 || Srb->Lun != 0) { + + // + // The spock card only supports logical unit zero and one bus. + // + + Srb->SrbStatus = SRB_STATUS_INVALID_LUN; + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + // + // Adapter ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + return TRUE; + } + + // + // Get logical unit extension. + // + + logicalUnit = ScsiPortGetLogicalUnit(HwDeviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + + switch (Srb->Function) { + + case SRB_FUNCTION_EXECUTE_SCSI: + + // + // Save SRB in logical unit extension. + // + + ASSERT(!logicalUnit->CurrentSrb); + logicalUnit->CurrentSrb = Srb; + + scb = Srb->SrbExtension; + + // + // Save SRB back pointer in SCB. + // + + scb->SrbAddress = Srb; + + // + // Get SCB physical address. + // + + physicalScb = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(deviceExtension, NULL, scb, &length)); + + // + // Assume physical address is contiguous for size of SCB. + // + + ASSERT(length >= sizeof(SCB)); + + // + // Save Scb in logical unit extension. + // + + logicalUnit->Scb = scb; + + // + // Build SCB. + // + + BuildScb(deviceExtension, Srb); + + // + // Issue send SCB command to adapter. + // + + DebugPrint((2, "BuildSCB: Function %0.4hx LDN %0.4hx \n", + Srb->Cdb[0], Srb->TargetId)); + + if (!IssueScbCommand(deviceExtension, + physicalScb, + Srb->TargetId)) { + + // + // Fail SRB. + // + + DebugPrint((1, "SpockStartIo: IssueScbCommand failed\n")); + + Srb->SrbStatus = SRB_STATUS_TIMEOUT; + + logicalUnit->CurrentSrb = NULL; + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + } + + break; + + case SRB_FUNCTION_ABORT_COMMAND: + + DebugPrint((3,"SpockStartIo: Abort command\n")); + + // + // Check to see if SRB to abort is still around. + // + + if (!logicalUnit->CurrentSrb) { + + // + // Request must of already completed. + // + + DebugPrint((1,"SpockStartIo: Srb to abort already complete\n")); + + // + // Complete ABORT SRB. + // + + Srb->SrbStatus = SRB_STATUS_ERROR; + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + + } else if (!SpockAbortIo(deviceExtension, Srb)) { + + DebugPrint((1,"SpockStartIo: Abort command failed\n")); + + Srb->SrbStatus = SRB_STATUS_ERROR; + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + } + + break; + + default: + + // + // Set error, complete request + // and signal ready for next request. + // + + DebugPrint((1,"SpockStartIo: Invalid SRB request\n")); + + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + + } // end switch + + // + // Adapter ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + return TRUE; + +} // end SpockStartIo() + + +BOOLEAN +SpockInterrupt( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + This is the interrupt handler for the IBM MCA SCSI adapter. + +Arguments: + + Device Object + +Return Value: + + Returns TRUE if interrupt expected. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PSCB scb; + PSCSI_REQUEST_BLOCK srb; + UCHAR srbStatus; + UCHAR scsiStatus; + PTSB tsb; + UCHAR status; + UCHAR targetId; + PHW_LOGICAL_UNIT logicalUnit; + ULONG logError = 0; + BOOLEAN srbValid = TRUE; + ULONG j; + + if (!(ScsiPortReadPortUchar(&deviceExtension->Registers->BasicStatus) & + BASIC_STATUS_INTERRUPT)) { + + // + // Spurious interrupt. + // + + return FALSE; + } + + // + // Read interrupt status register to determine + // interrupting device and status. + // + + status = ScsiPortReadPortUchar(&deviceExtension->Registers->InterruptStatus); + + // + // Bits 0-3 are device id and + // bits 4-7 are interrupt id. + // + + targetId = status & 0x0F; + status = status >> 4; + + // + // Acknowledge interrupt. + // + + j = 0; + while (ScsiPortReadPortUchar(&deviceExtension->Registers->BasicStatus) & + BASIC_STATUS_BUSY){ + + ScsiPortStallExecution(1); + + if (j++ > 10000) { + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + (9 << 16) | status + ); + + } + } + + ScsiPortWritePortUchar(&deviceExtension->Registers->Attention, + (UCHAR)(targetId | END_OF_INTERRUPT)); + + switch (status) { + + case SCB_STATUS_SUCCESS_WITH_RETRIES: + case SCB_STATUS_SUCCESS: + + srbStatus = SRB_STATUS_SUCCESS; + scsiStatus = SCSISTAT_GOOD; + DebugPrint((2, "Interupt Success: %0.4hx \n", + targetId)); + + break; + + case SCB_STATUS_IMMEDIATE_COMMAND_COMPLETE: + + if ((targetId & 7) != 7) { + + DebugPrint((1, "SpockInterrupt: Abort command complete\n")); + + // + // This is an ABORT command completion. + // + + + logicalUnit = ScsiPortGetLogicalUnit(HwDeviceExtension, + 0, + targetId, + 0); + + if (logicalUnit == NULL) { + break; + } + + if (logicalUnit->AbortSrb == NULL) { + logicalUnit = NULL; + break; + } + + // + // Get the SRB aborted. + // + + srb = logicalUnit->AbortSrb->NextSrb; + + srb->SrbStatus = SRB_STATUS_TIMEOUT; + + // + // Remove the aborted SRB from the logical unit. + // + + logicalUnit->CurrentSrb = NULL; + logicalUnit->Scb = NULL; + + // + // Call notification routine for the SRB. + // + + ScsiPortNotification(RequestComplete, + (PVOID)deviceExtension, + srb); + + // + // Complete the ABORT SRB. + // + + logicalUnit->AbortSrb->SrbStatus = SRB_STATUS_SUCCESS; + + ScsiPortNotification(RequestComplete, + (PVOID)deviceExtension, + logicalUnit->AbortSrb); + + } else { + + DebugPrint((1,"SpockInterrupt: Immediate command complete\n")); + } + + return TRUE; + + case SCB_STATUS_ADAPTER_FAILED: + case SCB_STATUS_COMMAND_ERROR: + case SCB_STATUS_SOFTWARE_SEQUENCING_ERROR: + + logError = SP_INTERNAL_ADAPTER_ERROR; + + case SCB_STATUS_COMMAND_COMPLETE_WITH_FAILURE: + + DebugPrint((2, "SpockInterrupt: Error\n")); + + srbStatus = SRB_STATUS_ERROR; + + break; + + default: + + srbValid = FALSE; + logError = SP_INTERNAL_ADAPTER_ERROR; + return TRUE; + + } // end switch() + + if (srbValid) { + + // + // Get SCB address from logical unit extension. + // + + logicalUnit = ScsiPortGetLogicalUnit(HwDeviceExtension, + 0, + targetId, + 0); + + if (logicalUnit == NULL || logicalUnit->Scb == NULL) { + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + (6 << 16) | status + ); + + return TRUE; + } + + scb = logicalUnit->Scb; + logicalUnit->Scb = NULL; + } + + if (logError != 0 ) { + + deviceExtension->ErrorCount++; + + // + // Log the error. + // + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 1 << 16 | status + ); + + if (!srbValid) { + + // + // If the srb is not valid for this type of interrupt the + // return. + // + + return TRUE; + } + } + + // + // Get virtual TSB address. + // + + tsb = ScsiPortGetVirtualAddress(deviceExtension, ScsiPortConvertUlongToPhysicalAddress(scb->StatusBlock)); + + if (tsb == NULL) { + + deviceExtension->ErrorCount++; + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + (5 << 16) | status + ); + + return TRUE; + } + + // + // Get SRB and update status. + // + + srb = scb->SrbAddress; + + if (status == SCB_STATUS_COMMAND_COMPLETE_WITH_FAILURE) { + + // + // Get statuses from TSB. + // + + MapTsbError(deviceExtension, srb, tsb); + + } else { + + srb->SrbStatus = srbStatus; + srb->ScsiStatus = scsiStatus; + } + + // + // Remove the SRB from the logical unit extension. + // + + logicalUnit->CurrentSrb = NULL; + + // + // Call notification routine for the SRB. + // + + ScsiPortNotification(RequestComplete, + (PVOID)deviceExtension, + srb); + + if (!(--deviceExtension->ActiveRequests)) { + + // + // Turn disk activity light off. + // + + DISK_ACTIVITY_LIGHT_OFF(); + } + + return TRUE; + +} // end SpockInterrupt() + +BOOLEAN +SpockResetBus( + IN PVOID HwDeviceExtension, + IN ULONG PathId + ) + +/*++ + +Routine Description: + + Reset adapter and SCSI bus. + +Arguments: + + DeviceExtension + Pathid - identifies which bus on adapter that supports multiple + SCSI buses. + +Return Value: + + TRUE if reset completed + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PMCA_REGISTERS mcaRegisters = deviceExtension->Registers; + ULONG i; + + UNREFERENCED_PARAMETER(PathId); + + deviceExtension->ErrorCount++; + + // + // Issue RESET command. + // + + if (!IssueImmediateCommand(HwDeviceExtension, + (ULONG)SCB_COMMAND_RESET, + 0x0f)) { + + DebugPrint((1,"SpockResetBus: Reset failed\n")); + return FALSE; + } + + // + // Wait 2 seconds for bus to quiet down. + // + + for (i=0; i<10; i++) { + + // + // Stall 200 milliseconds. + // + + ScsiPortStallExecution(200 * 1000); + } + + // + // Wait up to 3 more seconds for adapter to become ready. + // + + for (i=0; i<100; i++) { + + // + // Stall 3 milliseconds. + // + + ScsiPortStallExecution(30 * 1000); + + // + // If busy bit is set then reset adapter has not completed. + // + + if (ScsiPortReadPortUchar( + &mcaRegisters->BasicStatus) & BASIC_STATUS_BUSY) { + + continue; + + } else { + + break; + } + } + + if (i == 100) { + + DebugPrint((1,"SpockResetBus: Reset failed\n")); + ScsiPortLogError( + deviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 7 << 16 + ); + + return FALSE; + } + + // + // Issue feature control immediate command to disable + // adapter timing of SCBs. + // + + if (!IssueImmediateCommand(HwDeviceExtension, + (ULONG)SCB_COMMAND_FEATURE_CONTROL, + 0x0f)) { + + DebugPrint((1,"SpockResetBus: Set feature controls failed\n")); + } + + // + // Complete all outstanding requests with SRB_STATUS_BUS_RESET. + // + + ScsiPortCompleteRequest(deviceExtension, + (UCHAR)PathId, + 0xFF, + 0xFF, + (ULONG)SRB_STATUS_BUS_RESET); + + // + // Turn disk activity light off. + // + + DISK_ACTIVITY_LIGHT_OFF(); + + deviceExtension->ActiveRequests = 0; + + return TRUE; + +} // end SpockResetBus() + + +BOOLEAN +SpockAbortIo( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Abort command in progress. + +Arguments: + + DeviceExtension + SRB + +Return Value: + + True, if command aborted. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PMCA_REGISTERS mcaRegisters = deviceExtension->Registers; + PHW_LOGICAL_UNIT logicalUnit; + ULONG i; + + // + // Wait up to 10 milliseconds until adapter is not busy. + // + + for (i=0; i<1000; i++) { + + if (ScsiPortReadPortUchar( + &mcaRegisters->BasicStatus) & BASIC_STATUS_BUSY) { + + // + // Wait 10 microseconds. + // + + ScsiPortStallExecution(10); + + } else { + + // + // Busy bit clear. Exit loop. + // + + break; + } + } + + if (i < 1000) { + + // + // Save SRB in logical unit extension. + // + + logicalUnit = ScsiPortGetLogicalUnit(HwDeviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + + logicalUnit->AbortSrb = Srb; + + // + // Issue abort to the command interface register. + // + + ScsiPortWritePortUlong(&mcaRegisters->CommandInterface, SCB_COMMAND_ABORT); + + // + // Write immediate command code to attention register. + // + + ScsiPortWritePortUchar(&mcaRegisters->Attention, (UCHAR)(Srb->TargetId | IMMEDIATE_COMMAND)); + + return TRUE; + + } else { + + // + // Timed out waiting for adapter to be in state to accept + // immediate command. Return TRUE so that the abort command + // will appear to have been sent and will time out causing a + // SCSI bus reset to occur. + + DebugPrint((1,"SpockAbortIo: Timed out waiting on BUSY adapter\n")); + + ScsiPortLogError( + deviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 8 << 16 + ); + + return TRUE; + } + +} // end SpockAbortIo() + + +VOID +BuildScb( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Build SCB + +Arguments: + + DeviceExtension + SRB + +Return Value: + + Nothing. + +--*/ + +{ + + PSCB scb = Srb->SrbExtension; + ULONG length; + + // + // Check for read capacity CDB. IBM boot devices store IML + // code near the end of the boot device that must be preserved. + // Send the operation specific SCB instead of the generic + // SCSI CDB. + // + + if (Srb->Cdb[0] == SCSIOP_READ_CAPACITY) { + + // + // Call routine to build special SCB. + // + + BuildReadCapacity(DeviceExtension, + Srb); + return; + } + + scb->Command = SCB_COMMAND_SEND_SCSI; + + // + // Set SCB command flags. + // + + // + // Some the spock controllers do not work well with multiple devices. + // If too many errors are detected then disable disconnects. + // + + if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT || + DeviceExtension->ErrorCount > MAXIMUM_ERRORS) { + scb->Command |= SCB_NO_DISCONNECT; + } + + if (Srb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER) { + scb->Command |= SCB_NO_SYNCHRONOUS_TRANSFER; + } + + // + // Set SCB request control flags. + // + + if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + + // + // Write request. + // + + scb->EnableFlags = SCB_ENABLE_SG_LIST | + SCB_ENABLE_WRITE | + SCB_ENABLE_RETRY_ENABLE | + SCB_ENABLE_TSB_ON_ERROR; + + } else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + // + // Read request. + // + + scb->EnableFlags = SCB_ENABLE_SG_LIST | + SCB_ENABLE_READ | + SCB_ENABLE_SHORT_TRANSFER | + SCB_ENABLE_RETRY_ENABLE | + SCB_ENABLE_TSB_ON_ERROR; + } else { + + // + // No data transfer. + // + + scb->EnableFlags = SCB_ENABLE_TSB_ON_ERROR; + } + + // + // Set CDB length and copy to SCB. + // + + scb->CdbSize = Srb->CdbLength; + + ScsiPortMoveMemory(scb->Cdb, Srb->Cdb, Srb->CdbLength); + + // + // Build SDL in SCB if data transfer. + // + + if (Srb->DataTransferLength) { + BuildSgl(DeviceExtension, Srb); + } else { + scb->BufferAddress = 0; + scb->BufferLength = 0; + } + + // + // Put physical address of TSB in SCB. + // + + scb->StatusBlock = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, NULL, + &scb->Tsb, &length)); + + return; + +} // end BuildScb() + + +VOID +BuildSgl( + IN PVOID DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Build scatter/gather descriptor list in SCB. + +Arguments: + + DeviceExtension + SRB + +Return Value: + + Nothing. + +--*/ + +{ + PSCB scb = Srb->SrbExtension; + PVOID dataPointer = Srb->DataBuffer; + ULONG bytesLeft = Srb->DataTransferLength; + PSDL sdl = &scb->Sdl; + ULONG physicalSdl; + ULONG physicalAddress; + ULONG length; + ULONG descriptorCount = 0; + + DebugPrint((3,"BuildSgl: Enter routine\n")); + + // + // Zero first SDL descriptor. + // + + sdl->Descriptor[descriptorCount].Address = 0; + sdl->Descriptor[descriptorCount].Length = 0; + + // + // Get physical SDL address. + // + + physicalSdl = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, NULL, + sdl, &length)); + + // + // Assume physical memory contiguous for sizeof(SDL) bytes. + // + + ASSERT(length >= sizeof(SDL)); + + // + // Create SDL segment descriptors. + // + + do { + + DebugPrint((3, "BuildSgl: Data buffer %lx\n", dataPointer)); + + // + // Get physical address and length of contiguous + // physical buffer. + // + + physicalAddress = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, + Srb, + dataPointer, + &length)); + + DebugPrint((3, "BuildSgl: Physical address %lx\n", physicalAddress)); + DebugPrint((3, "BuildSgl: Data length %lx\n", length)); + DebugPrint((3, "BuildSgl: Bytes left %lx\n", bytesLeft)); + + // + // If length of physical memory is more + // than bytes left in transfer, use bytes + // left as final length. + // + + if (length > bytesLeft) { + length = bytesLeft; + } + + // + // Check for adjacent physical memory descriptors. + // + + if (descriptorCount && + ((sdl->Descriptor[descriptorCount-1].Address + + sdl->Descriptor[descriptorCount-1].Length) == physicalAddress)) { + + DebugPrint((3,"BuildSgl: Concatenate adjacent descriptors\n")); + + sdl->Descriptor[descriptorCount-1].Length += length; + + } else { + + sdl->Descriptor[descriptorCount].Address = physicalAddress; + sdl->Descriptor[descriptorCount].Length = length; + descriptorCount++; + } + + // + // Adjust counts. + // + + dataPointer = (PUCHAR)dataPointer + length; + bytesLeft -= length; + + } while (bytesLeft); + + // + // Write SDL length to SCB. + // + + scb->BufferLength = descriptorCount * sizeof(SG_DESCRIPTOR); + + DebugPrint((3,"BuildSgl: SDL length is %d\n", descriptorCount)); + + // + // Write SDL address to SCB. + // + + scb->BufferAddress = physicalSdl; + + DebugPrint((3,"BuildSgl: SDL address is %lx\n", sdl)); + + DebugPrint((3,"BuildSgl: SCB address is %lx\n", scb)); + + return; + +} // end BuildSgl() + +VOID +BuildReadCapacity( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Build SCB for read capacity command. + +Arguments: + + DeviceExtension + SRB + +Return Value: + + Nothing. + +--*/ + +{ + PSCB scb = Srb->SrbExtension; + ULONG length; + + DebugPrint((1, "Spock: BuildReadCapacity: Building spock read capacity\n")); + + // + // Set SCB command. + // + + scb->Command = SCB_COMMAND_READ_CAPACITY; + scb->Command |= SCB_NO_SYNCHRONOUS_TRANSFER | SCB_NO_DISCONNECT; + + scb->EnableFlags = SCB_ENABLE_TSB_ON_ERROR | + SCB_ENABLE_READ | + SCB_ENABLE_BYPASS_BUFFER | + SCB_ENABLE_RETRY_ENABLE; + + // + // Get physical buffer address. + // + + scb->BufferAddress = (ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, + Srb, + Srb->DataBuffer, + &length))); + + scb->BufferLength = Srb->DataTransferLength; + + // + // Put physical address of TSB in SCB. + // + + scb->StatusBlock = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, NULL, + &scb->Tsb, &length)); + + return; + +} // end BuildReadCapacity() + + +ULONG +McaAdapterPresent( + IN PHW_DEVICE_EXTENSION HwDeviceExtension, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN OUT PULONG AdapterCount, + OUT PBOOLEAN Again + ) + +/*++ + +Routine Description: + + Determine if Spock adapter present in sytem by reading + interrupt status register. + +Arguments: + + HwDeviceExtension - miniport device extension + + ConfigInfo - Supplies the known configuraiton information. + + AdapterCount - Supplies the count of adapter slots which have been tested. + + Again - Returns whether the OS-specific driver should call again. + +Return Value: + + Returns TRUE if adapter exists + +--*/ + +{ + PMCA_REGISTERS baseIoAddress; + PUCHAR ioSpace; + + // + // Get the system physical address for this card. The card uses I/O space. + // + + ioSpace = ScsiPortGetDeviceBase( + HwDeviceExtension, // HwDeviceExtension + ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType + ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber + ScsiPortConvertUlongToPhysicalAddress(0), + 0x400, // NumberOfBytes + TRUE // InIoSpace + ); + + // + // Scan though the adapter address looking for adapters. + // + + while (AdapterAddresses[*AdapterCount] != 0) { + + // + // Check to see if adapter present in system. + // + + baseIoAddress = (PMCA_REGISTERS)(ioSpace + + AdapterAddresses[*AdapterCount]); + + // + // Update the adapter count. + // + + (*AdapterCount)++; + + if (ScsiPortReadPortUchar((PUCHAR)baseIoAddress) != 0xFF) { + + DebugPrint((1,"Spock: Base IO address is %x\n", baseIoAddress)); + + // + // An adapter has been found. Set the base address in the device + // extension, and request another call. + // + + HwDeviceExtension->Registers = baseIoAddress; + *Again = TRUE; + + // + // Fill in the access array information. + // + + (*ConfigInfo->AccessRanges)[0].RangeStart = + ScsiPortConvertUlongToPhysicalAddress( + AdapterAddresses[*AdapterCount - 1]); + (*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(MCA_REGISTERS); + (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE; + + return(SP_RETURN_FOUND); + } + } + + // + // The entire table has been searched and no adapters have been found. + // There is no need to call again and the device base can now be freed. + // Clear the adapter count for the next bus. + // + + *Again = FALSE; + *(AdapterCount) = 0; + + ScsiPortFreeDeviceBase( + HwDeviceExtension, + ioSpace + ); + + return(SP_RETURN_NOT_FOUND); + +} // end McaAdapterPresent() + + +BOOLEAN +IssueScbCommand( + IN PVOID HwDeviceExtension, + IN ULONG PhysicalScb, + IN UCHAR TargetId + ) + +/*++ + +Routine Description: + + Send SCB to adapter. + +Arguments: + + DeviceExtension + Physical SCB + TargeId + +Return Value: + + TRUE if command sent. + FALSE if wait for BUSY bit timed out. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PMCA_REGISTERS mcaRegisters = deviceExtension->Registers; + ULONG i; + + // + // Wait up to 10 milliseconds until adapter is not busy. + // + + for (i=0; i<1000; i++) { + + if (ScsiPortReadPortUchar( + &mcaRegisters->BasicStatus) & BASIC_STATUS_BUSY) { + + // + // Wait 10 microseconds. + // + + ScsiPortStallExecution(10); + + } else { + + // + // Busy bit clear. Exit loop. + // + + break; + } + } + + if (i < 1000) { + + // + // Write physical SCB address to command interface register. + // + + ScsiPortWritePortUlong(&mcaRegisters->CommandInterface, PhysicalScb); + + // + // Write targetid and command code to attention register. + // + + ScsiPortWritePortUchar(&mcaRegisters->Attention, (UCHAR)(TargetId | START_SCB)); + + if (!deviceExtension->ActiveRequests++) { + + // + // Turn disk activity light on. + // + + DISK_ACTIVITY_LIGHT_ON(); + } + + return TRUE; + + } else { + + ScsiPortLogError( + deviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 4 << 16 + ); + + return FALSE; + } + +} // end IssueScbCommand() + + +BOOLEAN +IssueImmediateCommand( + IN PVOID HwDeviceExtension, + IN ULONG ImmediateCommand, + IN UCHAR TargetId + ) + +/*++ + +Routine Description: + + Send SCB to adapter. + +Arguments: + + DeviceExtension + ImmediateCommand + TargeId + +Return Value: + + TRUE if command sent. + FALSE if wait for BUSY bit timed out. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PMCA_REGISTERS mcaRegisters = deviceExtension->Registers; + ULONG i; + + // + // Wait up to 10 milliseconds until adapter is not busy. + // + + for (i=0; i<1000; i++) { + + if (ScsiPortReadPortUchar( + &mcaRegisters->BasicStatus) & BASIC_STATUS_BUSY) { + + // + // Wait 10 microseconds. + // + + ScsiPortStallExecution(10); + + } else { + + // + // Busy bit clear. Exit loop. + // + + break; + } + } + + if (i < 1000) { + + // + // Write immediate command to command interface register. + // + + ScsiPortWritePortUlong(&mcaRegisters->CommandInterface, ImmediateCommand); + + // + // Write targetid and command code to attention register. + // + + ScsiPortWritePortUchar(&mcaRegisters->Attention, + (UCHAR)(TargetId | IMMEDIATE_COMMAND)); + + return TRUE; + + } else { + + ScsiPortLogError( + deviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 4 << 16 + ); + + return FALSE; + } + +} // end IssueImmediateCommand() + + +VOID +MapTsbError( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PTSB Tsb + ) + +/*++ + +Routine Description: + +Arguments: + + TSB - Termination Status Block + +Return Value: + + SCSI error code + +--*/ + +{ + ULONG logError = 0; + + DebugPrint((2, "MapTsbError: TSB ending status %lx\n", Tsb->ScbStatus)); + + switch (Tsb->ScbStatus & 0x003F) { + + case TSB_STATUS_NO_ERROR: + + // + // Check if device is not assigned. + // + + if (Tsb->CommandError == TSB_COMMAND_ERROR_DEVICE_NOT_ASSIGNED) { + + // + // Check for check condition. + // + + if (Tsb->DeviceStatus == SCB_DEV_STATUS_CHECK_CONDITION) { + + // + // Adjust count of bytes transferred. + // + + Srb->DataTransferLength -= Tsb->ResidualByteCount; + } + + Srb->SrbStatus = SRB_STATUS_NO_DEVICE; + + } else { + + Srb->SrbStatus = SRB_STATUS_ERROR; + } + + break; + + case TSB_STATUS_SHORT_RECORD: + + DebugPrint((1, "MapTsbError: Short record exception\n")); + + Srb->DataTransferLength -= Tsb->ResidualByteCount; + + Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; + + break; + + case TSB_STATUS_INVALID_COMMAND: + + DebugPrint((1, "MapTsbError: Invalid command rejected\n")); + + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + logError = SP_INTERNAL_ADAPTER_ERROR; + + break; + + case TSB_STATUS_SCB_REJECTED: + + DebugPrint((1, "MapTsbError: SCB rejected\n")); + + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + logError = SP_INTERNAL_ADAPTER_ERROR; + + break; + + case TSB_STATUS_SCB_SPECIFIC_CHECK: + + DebugPrint((1, "MapTsbError: SCB speicific check\n")); + Srb->SrbStatus = SRB_STATUS_ERROR; + logError = SP_INTERNAL_ADAPTER_ERROR; + + break; + + case TSB_STATUS_LONG_RECORD: + + DebugPrint((1, "MapTsbError: Long record exception\n")); + + Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; + + break; + + default: + + DebugPrint((1, "MapTsbError: Unknown end status %lx\n",Tsb->ScbStatus)); + logError = SP_INTERNAL_ADAPTER_ERROR; + + Srb->SrbStatus = SRB_STATUS_ERROR; + + } // end switch + + DebugPrint((2, + "MapTsbError: Device status %x, DeviceError = %x\n", + Tsb->DeviceStatus, + Tsb->DeviceError)); + DebugPrint((2, + "MapTsbError: Command status %x, CommandError = %x\n", + Tsb->CommandStatus, + Tsb->CommandError)); + + if (logError != 0) { + + // + // Log error. + // + + ScsiPortLogError( + DeviceExtension, + Srb, + Srb->PathId, + Srb->TargetId, + Srb->Lun, + logError, + 2 << 16 | Tsb->ScbStatus + ); + + } + + Srb->ScsiStatus = Tsb->DeviceStatus; + + return; + +} // end MapTsbError() |