summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/spock/spock.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/miniport/spock/spock.c2150
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()