mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2150 lines
45 KiB
2150 lines
45 KiB
/*++
|
|
|
|
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()
|