NT4/private/ntos/miniport/aha154x/aha154x.c
2020-09-30 17:12:29 +02:00

4422 lines
102 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
aha154x.c
Abstract:
This is the port driver for the Adaptec 1540B SCSI Adapter.
Author:
Mike Glass
Tuong Hoang (Adaptec)
Renato Maranon (Adaptec)
Bill Williams (Adaptec)
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "miniport.h"
#include "aha154x.h" // includes scsi.h
//
// This conditionally compiles in the code to force the DMA transfer speed
// to 5.0.
//
#define FORCE_DMA_SPEED 1
//
// The following structure is allocated
// from noncached memory as data will be DMA'd to
// and from it.
//
typedef struct _NONCACHED_EXTENSION {
//
// Physical base address of mailboxes
//
ULONG MailboxPA;
//
// Mailboxes
//
MBO Mbo[MB_COUNT];
MBI Mbi[MB_COUNT];
} NONCACHED_EXTENSION, *PNONCACHED_EXTENSION;
//
// Device extension
//
typedef struct _HW_DEVICE_EXTENSION {
//
// NonCached extension
//
PNONCACHED_EXTENSION NoncachedExtension;
//
// Adapter parameters
//
PBASE_REGISTER BaseIoAddress;
//
// Host Target id.
//
UCHAR HostTargetId;
//
// Old in\out box indexes.
//
UCHAR MboIndex;
UCHAR MbiIndex;
//
// Pending request.
//
BOOLEAN PendingRequest;
//
// Bus on time to use.
//
UCHAR BusOnTime;
//
// Scatter gather command
//
UCHAR CcbScatterGatherCommand;
//
// Non scatter gather command
//
UCHAR CcbInitiatorCommand;
//
// Don't send CDB's longer than this to any device on the bus
// Ignored if the value is 0
//
UCHAR MaxCdbLength;
//
// Real Mode adapter config info
//
RM_CFG RMSaveState;
#if defined(_SCAM_ENABLED)
//
// SCAM boolean, set to TRUE if miniport must control SCAM operation.
//
BOOLEAN PerformScam;
#endif
} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
//
// Logical unit extension
//
typedef struct _HW_LU_EXTENSION {
PSCSI_REQUEST_BLOCK CurrentSrb;
} HW_LU_EXTENSION, *PHW_LU_EXTENSION;
//
// Function declarations
//
// Functions that start with 'A154x' are entry points
// for the OS port driver.
//
ULONG
DriverEntry(
IN PVOID DriverObject,
IN PVOID Argument2
);
ULONG
A154xDetermineInstalled(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN OUT PSCAN_CONTEXT Context,
OUT PBOOLEAN Again
);
VOID
A154xClaimBIOSSpace(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN PBASE_REGISTER baseIoAddress,
IN PSCAN_CONTEXT Context,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo
);
ULONG
A154xFindAdapter(
IN PVOID HwDeviceExtension,
IN PSCAN_CONTEXT Context,
IN PVOID BusInformation,
IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again
);
BOOLEAN
A154xAdapterState(
IN PVOID HwDeviceExtension,
IN PVOID Context,
IN BOOLEAN SaveState
);
BOOLEAN
A154xHwInitialize(
IN PVOID DeviceExtension
);
#if defined(_SCAM_ENABLED)
//
// Issues SCAM command to HA
//
BOOLEAN
PerformScamProtocol(
IN PHW_DEVICE_EXTENSION DeviceExtension
);
#endif
BOOLEAN
A154xStartIo(
IN PVOID DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
BOOLEAN
A154xInterrupt(
IN PVOID DeviceExtension
);
BOOLEAN
A154xResetBus(
IN PVOID HwDeviceExtension,
IN ULONG PathId
);
VOID
GetHostAdapterBoardId (
IN PVOID HwDeviceExtension,
OUT PUCHAR BoardId
);
//
// This function is called from A154xStartIo.
//
VOID
BuildCcb(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
//
// This function is called from BuildCcb.
//
VOID
BuildSdl(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
//
// This function is called from A154xInitialize.
//
BOOLEAN
AdapterPresent(
IN PVOID HwDeviceExtension
);
//
// This function is called from A154xInterrupt.
//
UCHAR
MapError(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN PCCB Ccb
);
BOOLEAN
ReadCommandRegister(
IN PHW_DEVICE_EXTENSION DeviceExtension,
OUT PUCHAR DataByte,
IN BOOLEAN TimeOutFlag
);
BOOLEAN
WriteCommandRegister(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN UCHAR AdapterCommand,
IN BOOLEAN LogError
);
BOOLEAN
WriteDataRegister(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN UCHAR DataByte
);
BOOLEAN
ScatterGatherSupported (
IN PHW_DEVICE_EXTENSION HwDeviceExtension
);
BOOLEAN
SpinForInterrupt(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN BOOLEAN TimeOutFlag
);
BOOLEAN SendUnlockCommand(
IN PVOID HwDeviceExtension,
IN UCHAR locktype
);
BOOLEAN UnlockMailBoxes(
IN PVOID HwDeviceExtension
);
ULONG
AhaParseArgumentString(
IN PCHAR String,
IN PCHAR KeyWord
);
//
// This function determines whether adapter is an AMI
//
BOOLEAN
A4448IsAmi(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
ULONG portNumber
);
ULONG
DriverEntry(
IN PVOID DriverObject,
IN PVOID Argument2
)
/*++
Routine Description:
Installable driver initialization entry point for system.
Arguments:
Driver Object
Return Value:
Status from ScsiPortInitialize()
--*/
{
HW_INITIALIZATION_DATA hwInitializationData;
SCAN_CONTEXT context;
ULONG isaStatus;
ULONG mcaStatus;
ULONG i;
DebugPrint((1,"\n\nSCSI Adaptec 154X MiniPort Driver\n"));
//
// Zero out structure.
//
for (i=0; i<sizeof(HW_INITIALIZATION_DATA); i++) {
((PUCHAR)&hwInitializationData)[i] = 0;
}
//
// Set size of hwInitializationData.
//
hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
//
// Set entry points.
//
hwInitializationData.HwInitialize = A154xHwInitialize;
hwInitializationData.HwResetBus = A154xResetBus;
hwInitializationData.HwStartIo = A154xStartIo;
hwInitializationData.HwInterrupt = A154xInterrupt;
hwInitializationData.HwFindAdapter = A154xFindAdapter;
hwInitializationData.HwAdapterState = A154xAdapterState;
//
// Indicate no buffer mapping but will need physical addresses.
//
hwInitializationData.NeedPhysicalAddresses = TRUE;
//
// Specify size of extensions.
//
hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
hwInitializationData.SpecificLuExtensionSize = sizeof(HW_LU_EXTENSION);
//
// Specifiy the bus type.
//
hwInitializationData.AdapterInterfaceType = Isa;
hwInitializationData.NumberOfAccessRanges = 2;
//
// Ask for SRB extensions for CCBs.
//
hwInitializationData.SrbExtensionSize = sizeof(CCB);
//
// The adapter count is used by the find adapter routine to track how
// which adapter addresses have been tested.
//
context.adapterCount = 0;
context.biosScanStart = 0;
isaStatus = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &context);
//
// Now try to configure for the Mca bus.
// Specifiy the bus type.
//
hwInitializationData.AdapterInterfaceType = MicroChannel;
context.adapterCount = 0;
context.biosScanStart = 0;
mcaStatus = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &context);
//
// Return the smaller status.
//
return(mcaStatus < isaStatus ? mcaStatus : isaStatus);
} // end A154xEntry()
ULONG
A154xFindAdapter(
IN PVOID HwDeviceExtension,
IN PSCAN_CONTEXT Context,
IN PVOID BusInformation,
IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again
)
/*++
Routine Description:
This function is called by the OS-specific port driver after
the necessary storage has been allocated, to gather information
about the adapter's configuration.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Context - Register base address
ConfigInfo - Configuration information structure describing HBA
This structure is defined in PORT.H.
Return Value:
ULONG
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
ULONG length;
ULONG status;
UCHAR adapterTid;
UCHAR dmaChannel;
UCHAR irq;
UCHAR bit;
UCHAR hostAdapterId[4];
#if defined(_SCAM_ENABLED)
UCHAR temp, i;
UCHAR BoardID;
UCHAR EepromData;
#endif
//
// Determine if there are any adapters installed. Determine installed
// will initialize the BaseIoAddress if an adapter is found.
//
status = A154xDetermineInstalled(deviceExtension,
ConfigInfo,
Context,
Again);
//
// If there are no adapters found then return.
//
if (status != SP_RETURN_FOUND) {
return(status);
}
//
// Issue adapter command to get IRQ, DMA channel, and adapter SCSI ID.
// But first, check for PnP non-default values. If any of these values
// are default, then we do 'em all to save code space, since the same
// command is used.
//
// Returns 3 data bytes:
//
// Byte 0 Dma Channel
//
// Byte 1 Interrupt Channel
//
// Byte 2 Adapter SCSI ID
//
if (((ConfigInfo->DmaChannel+1) == 0) || // default DMA channel ?
(ConfigInfo->BusInterruptLevel == 0) || // default IRQ ?
((ConfigInfo->InitiatorBusId[0]+1) == 0) // default adapter ID ?
) {
if (!WriteCommandRegister(deviceExtension, AC_RET_CONFIGURATION_DATA, TRUE)) {
DebugPrint((1,"A154xFindAdapter: Get configuration data command failed\n"));
return SP_RETURN_ERROR;
}
//
// Determine DMA channel.
//
if (!ReadCommandRegister(deviceExtension,&dmaChannel,TRUE)) {
DebugPrint((1,"A154xFindAdapter: Can't read dma channel\n"));
return SP_RETURN_ERROR;
}
if (ConfigInfo->AdapterInterfaceType != MicroChannel) {
WHICH_BIT(dmaChannel,bit);
ConfigInfo->DmaChannel = bit;
DebugPrint((2,"A154xFindAdapter: DMA channel is %x\n",
ConfigInfo->DmaChannel));
} else {
ConfigInfo->InterruptMode = LevelSensitive;
}
//
// Determine hardware interrupt vector.
//
if (!ReadCommandRegister(deviceExtension,&irq,TRUE)) {
DebugPrint((1,"A154xFindAdapter: Can't read adapter irq\n"));
return SP_RETURN_ERROR;
}
WHICH_BIT(irq, bit);
ConfigInfo->BusInterruptLevel = (UCHAR) 9 + bit;
//
// Determine what SCSI bus id the adapter is on.
//
if (!ReadCommandRegister(deviceExtension,&adapterTid,TRUE)) {
DebugPrint((1,"A154xFindAdapter: Can't read adapter SCSI id\n"));
return SP_RETURN_ERROR;
}
//
// Wait for HACC interrupt.
//
SpinForInterrupt(deviceExtension,FALSE); // eddy
//
// Use PnP fields
//
} else {
adapterTid = ConfigInfo->InitiatorBusId[0];
}
//
// Set number of buses.
//
ConfigInfo->NumberOfBuses = 1;
ConfigInfo->InitiatorBusId[0] = adapterTid;
deviceExtension->HostTargetId = adapterTid;
//
// Set default CCB command to scatter/gather with residual counts.
// If the adapter rejects this command, then set the command
// to scatter/gather without residual.
//
deviceExtension->CcbScatterGatherCommand = SCATTER_GATHER_COMMAND;
if ((ConfigInfo->MaximumTransferLength+1) == 0)
ConfigInfo->MaximumTransferLength = MAX_TRANSFER_SIZE;
//
// NumberOfPhysicalBreaks incorrectly defined.
// Must be set to MAX_SG_DESCRIPTORS.
//
if ((ConfigInfo->NumberOfPhysicalBreaks+1) == 0)
ConfigInfo->NumberOfPhysicalBreaks = MAX_SG_DESCRIPTORS;
//ConfigInfo->NumberOfPhysicalBreaks = MAX_SG_DESCRIPTORS - 1;
if (!ConfigInfo->ScatterGather)
ConfigInfo->ScatterGather = ScatterGatherSupported(HwDeviceExtension);
if (!ConfigInfo->ScatterGather) {
//ConfigInfo->NumberOfPhysicalBreaks = 1;
DebugPrint((1,"Aha154x: Scatter/Gather not supported!\n"));
}
ConfigInfo->Master = TRUE;
//
// Allocate a Noncached Extension to use for mail boxes.
//
deviceExtension->NoncachedExtension =
ScsiPortGetUncachedExtension(deviceExtension,
ConfigInfo,
sizeof(NONCACHED_EXTENSION));
if (deviceExtension->NoncachedExtension == NULL) {
//
// Log error.
//
ScsiPortLogError(deviceExtension,
NULL,
0,
0,
0,
SP_INTERNAL_ADAPTER_ERROR,
7 << 8);
return(SP_RETURN_ERROR);
}
//
// Convert virtual to physical mailbox address.
//
deviceExtension->NoncachedExtension->MailboxPA =
ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(deviceExtension,
NULL,
deviceExtension->NoncachedExtension->Mbo,
&length));
//
// Set default bus on time. Then check for an override parameter.
//
deviceExtension->BusOnTime = 0x07;
if (ArgumentString != NULL) {
length = AhaParseArgumentString(ArgumentString, "BUSONTIME");
//
// Validate that the new bus on time is reasonable before attempting
// to set it.
//
if (length >= 2 && length <= 15) {
deviceExtension->BusOnTime = (UCHAR) length;
DebugPrint((1,"A154xFindAdapter: Setting bus on time: %ld\n", length));
}
}
//
// Set maximum cdb length to zero unless the user has overridden the value
//
if( ArgumentString != NULL) {
length = AhaParseArgumentString(ArgumentString, "MAXCDBLENGTH");
//
// Validate the maximum cdb length before attempting to set it
//
if (length >= 6 && length <= 20) {
deviceExtension->MaxCdbLength = (UCHAR) length;
DebugPrint((1, "A154xFindAdapter: Setting maximum cdb length: %ld\n", length));
}
} else {
GetHostAdapterBoardId(HwDeviceExtension,&hostAdapterId[0]);
if(hostAdapterId[BOARD_ID] < 'E') {
deviceExtension->MaxCdbLength = 10;
DebugPrint((1, "A154xFindAdapter: Old firmware - Setting maximum cdb length: %ld\n", length));
} else {
length = deviceExtension->MaxCdbLength = 0;
DebugPrint((1, "A154xFindAdapter: Setting maximum cdb length: %ld\n", length));
}
}
#if defined(_SCAM_ENABLED)
//
// Get info to determine if miniport must issues SCAM command.
//
DebugPrint((1,"A154x => Start SCAM enabled determination.", length));
deviceExtension->PerformScam = FALSE;
do {
//
// Fall through do loop if a command fails.
//
if (!WriteCommandRegister(deviceExtension,AC_ADAPTER_INQUIRY,FALSE)) {
break;
}
if ((ReadCommandRegister(deviceExtension,&BoardID,TRUE)) == FALSE) {
break;
}
//
// Don't care about three other bytes
//
for (i=0; i < 0x3; i++) {
if ((ReadCommandRegister(deviceExtension,&temp,TRUE)) == FALSE) {
break;
}
}
SpinForInterrupt(HwDeviceExtension,FALSE);
//
// Check to see that three 'extra bytes' were read.
//
if (i != 0x3)
break;
if (BoardID >= 'F') {
if (!WriteCommandRegister(deviceExtension,AC_RETURN_EEPROM,FALSE)) {
break;
}
//
// Flag Byte => set returns configured options
//
if (!WriteCommandRegister(deviceExtension,0x01,FALSE)) {
break;
}
//
// Data length => reading one byte.
//
if (!WriteCommandRegister(deviceExtension,0x01,FALSE)) {
break;
}
//
// Data offset => read SCSI_BUS_CONTROL_FLAG
//
if (!WriteCommandRegister(deviceExtension,SCSI_BUS_CONTROL_FLAG,FALSE)) {
break;
}
//
// Read it!
//
if ((ReadCommandRegister(deviceExtension,&EepromData,TRUE)) == FALSE) {
break;
}
SpinForInterrupt(HwDeviceExtension,FALSE);
//
// SCAM only if it's enabled in SCSISelect.
//
if (EepromData | SCAM_ENABLED) {
DebugPrint((1,"A154x => SCAM Enabled\n"));
deviceExtension->PerformScam = TRUE;
}
}
} while (FALSE);
#endif
DebugPrint((3,"A154xFindAdapter: Configuration completed\n"));
return SP_RETURN_FOUND;
} // end A154xFindAdapter()
BOOLEAN
A154xAdapterState(
IN PVOID HwDeviceExtension,
IN PVOID Context,
IN BOOLEAN SaveState
)
/*++
Routine Description:
This function is called after FindAdapter with SaveState set to TRUE,
inidicating that the adapter state should be saved. Before Chicago
exits, this function is again called with SaveState set to FALSE,
indicating the adapter should be restored to the same state it was
when this function was first called. By saving its real mode state
and restoring it during protected mode exit will give the adapter
a higher chance of working back in real mode.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Context - Register base address
SaveState - Flag to indicate whether to perform SAVE or RESTORE.
TRUE == SAVE, FALSE == RESTORE.
Return Value:
TRUE SAVE/RESTORE operation was successful.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
UCHAR idx;
UCHAR cfgsz = sizeof(RM_CFG);
PRM_CFG SaveCfg;
deviceExtension = HwDeviceExtension;
SaveCfg = &deviceExtension->RMSaveState;
//
// SAVE real mode state
//
if (SaveState) {
//
// Read off config data from AHA154X...
//
if (!WriteCommandRegister(deviceExtension, AC_RETURN_SETUP_DATA, TRUE))
return FALSE;
if (!WriteDataRegister(deviceExtension, cfgsz))
return FALSE;
for (idx=0;idx<cfgsz;idx++) {
if (!(ReadCommandRegister(HwDeviceExtension,(PUCHAR)(SaveCfg),TRUE)))
return FALSE;
((PUCHAR)SaveCfg)++;
}
//
// ...and wait for interrupt
//
if (!SpinForInterrupt(deviceExtension,TRUE))
return FALSE;
//
// RESTORE state to real mode
//
} else {
//
// If mailbox count was not zero, re-initialize mailbox addresses
// saved from real mode
//
if (SaveCfg->NumMailBoxes) {
if (!WriteCommandRegister(deviceExtension, AC_MAILBOX_INITIALIZATION, TRUE))
return FALSE;
if (!WriteDataRegister(deviceExtension, SaveCfg->NumMailBoxes))
return FALSE;
if (!WriteDataRegister(deviceExtension, SaveCfg->MBAddrHiByte))
return FALSE;
if (!WriteDataRegister(deviceExtension, SaveCfg->MBAddrMiByte))
return FALSE;
if (!WriteDataRegister(deviceExtension, SaveCfg->MBAddrLoByte))
return FALSE;
//
// ... and wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,TRUE))
return FALSE;
}
//
// Restore transfer speed gotten from real mode...
//
if (!WriteCommandRegister(deviceExtension, AC_SET_TRANSFER_SPEED, TRUE))
return FALSE;
if (!WriteDataRegister(deviceExtension, SaveCfg->TxSpeed))
return FALSE;
//
// ... and wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,TRUE))
return FALSE;
//
// Restore setting for bus on time from real mode...
//
if (!WriteCommandRegister(deviceExtension, AC_SET_BUS_ON_TIME, TRUE))
return FALSE;
if (!WriteDataRegister(deviceExtension, SaveCfg->BusOnTime))
return FALSE;
//
// ...and wait for interrupt
//
if (!SpinForInterrupt(deviceExtension,TRUE))
return FALSE;
//
// Restore setting for bus off time from real mode...
//
if (!WriteCommandRegister(deviceExtension, AC_SET_BUS_OFF_TIME, TRUE))
return FALSE;
if (!WriteDataRegister(deviceExtension, SaveCfg->BusOffTime))
return FALSE;
//
// ...and wait for interrupt
//
if (!SpinForInterrupt(deviceExtension,TRUE))
return FALSE;
//
// Reset any pending interrupts
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET);
}
return TRUE;
} // end A154xAdapterState()
BOOLEAN
AdaptecAdapter(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN ULONG IoPort,
IN BOOLEAN Mca
)
/*++
Routine Description:
This routine checks the Special Options byte of the Adapter Inquiry
command to see if it is one of the two values returned by Adaptec
Adapters. This avoids claiming adapters from BusLogic and DTC.
Arguments:
HwDeviceExtension - miniport driver's adapter extension.
Return Values:
TRUE if the adapter looks like an Adaptec.
FALSE if not.
--*/
{
UCHAR byte;
UCHAR specialOptions;
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
if (Mca == TRUE) {
INIT_DATA initData;
LONG slot;
LONG i;
for (slot = 0; slot < NUMBER_POS_SLOTS; slot++) {
i = ScsiPortGetBusData(HwDeviceExtension,
Pos,
0,
slot,
&initData.PosData[slot],
sizeof(POS_DATA));
if (i < (sizeof(POS_DATA))) {
initData.PosData[slot].AdapterId = 0xffff;
}
}
for (slot = 0; slot < NUMBER_POS_SLOTS; slot++) {
if (initData.PosData[slot].AdapterId == POS_IDENTIFIER) {
switch (initData.PosData[slot].IoPortInformation & POS_PORT_MASK) {
case POS_PORT_130:
if (IoPort == 0x0130) {
return TRUE;
}
break;
case POS_PORT_134:
if (IoPort == 0x0134) {
return TRUE;
}
break;
case POS_PORT_230:
if (IoPort == 0x0230) {
return TRUE;
}
break;
case POS_PORT_234:
if (IoPort == 0x234) {
return TRUE;
}
break;
case POS_PORT_330:
if (IoPort == 0x330) {
return TRUE;
}
break;
case POS_PORT_334:
if (IoPort == 0x334) {
return TRUE;
}
break;
}
}
}
return FALSE;
}
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
if (!WriteCommandRegister(HwDeviceExtension,AC_ADAPTER_INQUIRY,FALSE)) {
return FALSE;
}
//
// Byte 0.
//
if ((ReadCommandRegister(HwDeviceExtension,&byte,TRUE)) == FALSE) {
return FALSE;
}
//
// Get the special options byte.
//
if ((ReadCommandRegister(HwDeviceExtension,&specialOptions,TRUE)) == FALSE) {
return FALSE;
}
//
// Get the last two bytes and clear the interrupt.
//
if ((ReadCommandRegister(HwDeviceExtension,&byte,TRUE)) == FALSE) {
return FALSE;
}
if ((ReadCommandRegister(HwDeviceExtension,&byte,TRUE)) == FALSE) {
return FALSE;
}
//
// Wait for HACC interrupt.
//
SpinForInterrupt(HwDeviceExtension,FALSE); // eddy
if ((specialOptions == 0x30) || (specialOptions == 0x42)) {
return TRUE;
}
return FALSE;
}
ULONG
A154xDetermineInstalled(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN OUT PSCAN_CONTEXT Context,
OUT PBOOLEAN Again
)
/*++
Routine Description:
Determine if Adaptec 154X SCSI adapter is installed in system
by reading the status register as each base I/O address
and looking for a pattern. If an adapter is found, the BaseIoAddres is
initialized.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
ConfigInfo - Supplies the known configuraiton information.
AdapterCount - Supplies the count of adapter slots which have been tested.
Again - Returns whehter the OS specific driver should call again.
Return Value:
Returns a status indicating whether a driver is present or not.
--*/
{
PBASE_REGISTER baseIoAddress;
PUCHAR ioSpace;
UCHAR portValue;
ULONG ioPort;
//
// The following table specifies the ports to be checked when searching for
// an adapter. A zero entry terminates the search.
//
CONST ULONG AdapterAddresses[7] = {0X330, 0X334, 0X234, 0X134, 0X130, 0X230, 0};
//
// Get the system physical address for this card. The card uses I/O space.
//
ioSpace =
ScsiPortGetDeviceBase(HwDeviceExtension,
ConfigInfo->AdapterInterfaceType,
ConfigInfo->SystemIoBusNumber,
ScsiPortConvertUlongToPhysicalAddress(0),
0x400,
TRUE);
//
// Check for configuration information passed in from system.
//
if ((*ConfigInfo->AccessRanges)[0].RangeLength != 0) {
ioPort = ScsiPortConvertPhysicalAddressToUlong
((*ConfigInfo->AccessRanges)[0].RangeStart);
baseIoAddress = (PBASE_REGISTER)(ioSpace + ioPort);
HwDeviceExtension->BaseIoAddress = baseIoAddress;
*Again = FALSE;
return (ULONG)SP_RETURN_FOUND;
} else {
//
// Scan possible base addresses looking for adapters.
//
while (AdapterAddresses[Context->adapterCount] != 0) {
//
// Get next base address.
//
baseIoAddress = (PBASE_REGISTER)(ioSpace + AdapterAddresses[Context->adapterCount]);
HwDeviceExtension->BaseIoAddress = baseIoAddress;
ioPort = AdapterAddresses[Context->adapterCount];
//
// Update the Adapter count
//
(Context->adapterCount)++;
//
// Check to see if adapter present in system.
//
portValue = ScsiPortReadPortUchar((PUCHAR)baseIoAddress);
//
// Check for Adaptec adapter.
// The mask (0x29) are bits that may or may not be set.
// The bit 0x10 (IOP_SCSI_HBA_IDLE) should be set.
//
if ((portValue & ~0x29) == IOP_SCSI_HBA_IDLE) {
if (!AdaptecAdapter(HwDeviceExtension, ioPort,
(BOOLEAN)(ConfigInfo->AdapterInterfaceType == MicroChannel ? TRUE : FALSE))) {
DebugPrint((1,"A154xDetermineInstalled: Clone command completed successfully - \n not our board;"));
continue;
//
// Run AMI4448 detection code.
//
} else if (A4448IsAmi(HwDeviceExtension,
ConfigInfo,
AdapterAddresses[(Context->adapterCount) - 1])) {
DebugPrint ((1,
"A154xDetermineInstalled: Detected AMI4448\n"));
continue;
}
//
// An adapter has been found. Request another call.
//
*Again = TRUE;
//
// Fill in the access array information.
//
(*ConfigInfo->AccessRanges)[0].RangeStart =
ScsiPortConvertUlongToPhysicalAddress(
AdapterAddresses[Context->adapterCount - 1]);
(*ConfigInfo->AccessRanges)[0].RangeLength = 4;
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
//
// Check if BIOS is enabled and claim that memory range.
//
A154xClaimBIOSSpace(HwDeviceExtension,
baseIoAddress,
Context,
ConfigInfo);
return (ULONG)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;
Context->adapterCount = 0;
Context->biosScanStart = 0;
ScsiPortFreeDeviceBase(HwDeviceExtension,
ioSpace);
return SP_RETURN_NOT_FOUND;
} // end A154xDetermineInstalled()
VOID
A154xClaimBIOSSpace(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN PBASE_REGISTER BaseIoAddress,
IN OUT PSCAN_CONTEXT Context,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo
)
/*++
Routine Description:
This routine is called from A154xDetermineInstalled to find
and claim BIOS space.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
BaseIoAddress - IO address of adapter
ConfigInfo - Miniport configuration information
Return Value:
None.
--*/
{
UCHAR inboundData, byte;
ULONG baseBIOSAddress;
ULONG i, j;
PUCHAR biosSpace, biosPtr;
UCHAR aha154xBSignature[16] =
{ 0x06, 0x73, 0x01, 0xC3, 0x8A, 0xE7, 0xC6, 0x06,
0x42, 0x00, 0x00, 0xF9, 0xC3, 0x88, 0x26, 0x42 };
//
// Reset interrupt just in case.
//
ScsiPortWritePortUchar(&BaseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
//
// The Adapter Inquiry command will return 4 bytes describing
// the firmware revision level.
//
if (WriteCommandRegister(HwDeviceExtension,
AC_ADAPTER_INQUIRY,TRUE) == FALSE) {
return;
}
if ((ReadCommandRegister(HwDeviceExtension,
&inboundData,TRUE)) == FALSE) {
return;
}
if ((ReadCommandRegister(HwDeviceExtension,&byte,TRUE)) == FALSE) {
return;
}
if ((ReadCommandRegister(HwDeviceExtension,&byte,TRUE)) == FALSE) {
return;
}
if ((ReadCommandRegister(HwDeviceExtension,&byte,TRUE)) == FALSE) {
return;
}
//
// Wait for HACC by hand.
//
SpinForInterrupt(HwDeviceExtension, FALSE);
//
// If the 1st bytethe adapter inquiry command is 0x41,
// then the adapter is an AHA154XB; if 0x44 or 0x45 then
// it is an AHA154XC or CF respectively
//
// if we've already checked all the possible locations for
// an AHA154XB bios don't waste time mapping the ports
//
if ((inboundData == 0x41)&&(Context->biosScanStart < 6)) {
//
// Get the system physical address for this BIOS section.
//
biosSpace =
ScsiPortGetDeviceBase(HwDeviceExtension,
ConfigInfo->AdapterInterfaceType,
ConfigInfo->SystemIoBusNumber,
ScsiPortConvertUlongToPhysicalAddress(0xC8000),
0x18000,
FALSE);
//
// Loop through all BIOS base possibilities. Use the context information
// to pick up where we left off the last time around.
//
for (i = Context->biosScanStart; i < 6; i ++) {
biosPtr = biosSpace + i * 0x4000 + 16;
//
// Compare the second 16 bytes to BIOS header
for (j = 0; j < 16; j++) {
if (aha154xBSignature[j] != ScsiPortReadRegisterUchar(biosPtr)) {
break;
}
biosPtr++;
}
if (j == 16) {
//
// Found the BIOS. Set up ConfigInfo->AccessRanges
//
(*ConfigInfo->AccessRanges)[1].RangeStart =
ScsiPortConvertUlongToPhysicalAddress(0xC8000 + i * 0x4000);
(*ConfigInfo->AccessRanges)[1].RangeLength = 0x4000;
(*ConfigInfo->AccessRanges)[1].RangeInMemory = TRUE;
DebugPrint((1,
"A154xClaimBiosSpace: 154XB BIOS address = %lX\n",
0xC8000 + i * 0x4000 ));
break;
}
}
Context->biosScanStart = i + 1;
ScsiPortFreeDeviceBase(HwDeviceExtension, (PVOID)biosSpace);
} else {
if ((inboundData == 0x44) || (inboundData == 0x45)) {
//
// Fill in BIOS address information
//
ScsiPortWritePortUchar(&BaseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
if (WriteCommandRegister(HwDeviceExtension,
AC_RETURN_SETUP_DATA,TRUE) == FALSE) {
return;
}
//
// Send length of incoming transfer for the Return Setup Data
//
if (WriteDataRegister(HwDeviceExtension,0x27) == FALSE) {
return;
}
//
// Magic Adaptec C rev byte.
//
for (i = 0; i < 0x27; i++) {
if ((ReadCommandRegister(HwDeviceExtension,
&inboundData,TRUE)) == FALSE) {
return;
}
}
//
// Interrupt handler is not yet installed so wait for HACC by hand.
//
SpinForInterrupt(HwDeviceExtension, FALSE);
inboundData >>= 4;
inboundData &= 0x07; // Filter BIOS bits out
baseBIOSAddress = 0xC8000;
if (inboundData != 0x07 && inboundData != 0x06) {
baseBIOSAddress +=
(ULONG)((~inboundData & 0x07) - 2) * 0x4000;
(*ConfigInfo->AccessRanges)[1].RangeStart =
ScsiPortConvertUlongToPhysicalAddress(baseBIOSAddress);
(*ConfigInfo->AccessRanges)[1].RangeLength = 0x4000;
(*ConfigInfo->AccessRanges)[1].RangeInMemory = TRUE;
DebugPrint((1,
"A154xClaimBiosSpace: 154XC BIOS address = %lX\n",
baseBIOSAddress));
}
}
}
return;
}
BOOLEAN
A154xHwInitialize(
IN PVOID HwDeviceExtension
)
/*++
Routine Description:
This routine is called from ScsiPortInitialize
to set up the adapter so that it is ready to service requests.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
TRUE - if initialization successful.
FALSE - if initialization unsuccessful.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PNONCACHED_EXTENSION noncachedExtension =
deviceExtension->NoncachedExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
UCHAR status;
ULONG i;
DebugPrint((2,"A154xHwInitialize: Reset aha154X and SCSI bus\n"));
//
// Reset SCSI chip.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_HARD_RESET);
//
// Inform the port driver that the bus has been reset.
//
ScsiPortNotification(ResetDetected, HwDeviceExtension, 0);
ScsiPortStallExecution(500*1000);
//
// Wait up to 5000 microseconds for adapter to initialize.
//
for (i = 0; i < 5000; i++) {
ScsiPortStallExecution(1);
status = ScsiPortReadPortUchar(&deviceExtension->BaseIoAddress->StatusRegister);
if (status & IOP_SCSI_HBA_IDLE) {
break;
}
}
//
// Check if reset failed or succeeded.
//
if (!(status & IOP_SCSI_HBA_IDLE) || !(status & IOP_MAILBOX_INIT_REQUIRED)) {
DebugPrint((1,"A154xInitialize: Reset SCSI bus failed\n"));
return FALSE;
}
//
// Unlock mailboxes in case the adapter is a 1540B with 1Gb support
// or 1540C with extended translation enabled.
//
status = UnlockMailBoxes(deviceExtension);
(VOID) SpinForInterrupt(deviceExtension,FALSE); // eddy
//
// Zero out mailboxes.
//
for (i=0; i<MB_COUNT; i++) {
PMBO mailboxOut;
PMBI mailboxIn;
mailboxIn = &noncachedExtension->Mbi[i];
mailboxOut = &noncachedExtension->Mbo[i];
mailboxOut->Command = mailboxIn->Status = 0;
}
//
// Zero preivous indexes.
//
deviceExtension->MboIndex = 0;
deviceExtension->MbiIndex = 0;
DebugPrint((3,"A154xHwInitialize: Initialize mailbox\n"));
if (!WriteCommandRegister(deviceExtension,AC_MAILBOX_INITIALIZATION, TRUE)) {
DebugPrint((1,"A154xHwInitialize: Can't initialize mailboxes\n"));
return FALSE;
}
//
// Send Adapter number of mailbox locations.
//
if (!WriteDataRegister(deviceExtension, MB_COUNT)) {
return FALSE;
}
//
// Send the most significant byte of the mailbox physical address.
//
if (!WriteDataRegister(deviceExtension,
((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte2)) {
return FALSE;
}
//
// Send the middle byte of the mailbox physical address.
//
if (!WriteDataRegister(deviceExtension,
((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte1)) {
return FALSE;
}
//
// Send the least significant byte of the mailbox physical address.
//
if (!WriteDataRegister(deviceExtension,
((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte0)) {
return FALSE;
}
#ifdef FORCE_DMA_SPEED
//
// Set the DMA transfer speed to 5.0 MB/second. This is because
// faster transfer speeds cause data corruption on 486/33 machines.
// This overrides the card jumper setting.
//
if (!WriteCommandRegister(deviceExtension, AC_SET_TRANSFER_SPEED, TRUE)) {
DebugPrint((1,"Can't set dma transfer speed\n"));
} else if (!WriteDataRegister(deviceExtension, DMA_SPEED_50_MBS)) {
DebugPrint((1,"Can't set dma transfer speed\n"));
}
//
// Wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,TRUE)) {
DebugPrint((1,"Timed out waiting for adapter command to complete\n"));
return TRUE;
}
#endif
//
// Override default setting for bus on time. This makes floppy
// drives work better with this adapter.
//
if (!WriteCommandRegister(deviceExtension, AC_SET_BUS_ON_TIME, TRUE)) {
DebugPrint((1,"Can't set bus on time\n"));
} else if (!WriteDataRegister(deviceExtension, deviceExtension->BusOnTime)) {
DebugPrint((1,"Can't set bus on time\n"));
}
//
// Wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,TRUE)) {
DebugPrint((1,"Timed out waiting for adapter command to complete\n"));
return TRUE;
}
//
// Override the default CCB timeout of 250 mseconds to 500 (0x1F4).
//
if (!WriteCommandRegister(deviceExtension, AC_SET_SELECTION_TIMEOUT, TRUE)) {
DebugPrint((1,"A154xHwInitialize: Can't set CCB timeout\n"));
}
else {
if (!WriteDataRegister(deviceExtension,0x01)) {
DebugPrint((1,"A154xHwInitialize: Can't set timeout selection enable\n"));
}
if (!WriteDataRegister(deviceExtension,0x00)) {
DebugPrint((1,"A154xHwInitialize: Can't set second byte\n"));
}
if (!WriteDataRegister(deviceExtension,0x01)) {
DebugPrint((1,"A154xHwInitialize: Can't set MSB\n"));
}
if (!WriteDataRegister(deviceExtension,0xF4)) {
DebugPrint((1,"A154xHwInitialize: Can't set LSB\n"));
}
}
//
// Wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,TRUE)) {
DebugPrint((1,"Timed out waiting for adapter command to complete\n"));
return TRUE;
}
#if defined(_SCAM_ENABLED)
//
// SCAM because A154xHwInitialize reset's the SCSI bus.
//
PerformScamProtocol(deviceExtension);
#endif
return TRUE;
} // end A154xHwInitialize()
#if defined(_SCAM_ENABLED)
BOOLEAN
PerformScamProtocol(
IN PHW_DEVICE_EXTENSION deviceExtension
)
{
if (deviceExtension->PerformScam) {
DebugPrint((1,"AHA154x => Starting SCAM operation.\n"));
if (!WriteCommandRegister(deviceExtension, AC_PERFORM_SCAM, TRUE)) {
DebugPrint((0,"AHA154x => Adapter time out, SCAM command failure.\n"));
ScsiPortLogError(deviceExtension,
NULL,
0,
deviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
0xA << 8);
return FALSE;
} else {
DebugPrint((1,"AHA154x => SCAM Performed OK.\n"));
return TRUE;
}
} else {
DebugPrint((1,"AHA154x => SCAM not performed, non-SCAM adapter.\n"));
return FALSE;
}
} //End PerformScamProtocol
#endif
BOOLEAN
A154xStartIo(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine is called from the SCSI port driver synchronized
with the kernel. The mailboxes are scanned for an empty one and
the CCB is written to it. Then the doorbell is rung and the
OS port driver is notified that the adapter can take
another request, if any are available.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Srb - IO request packet
Return Value:
TRUE
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PNONCACHED_EXTENSION noncachedExtension =
deviceExtension->NoncachedExtension;
PMBO mailboxOut;
PCCB ccb;
PHW_LU_EXTENSION luExtension;
ULONG i = deviceExtension->MboIndex;
ULONG physicalCcb;
ULONG length;
DebugPrint((3,"A154xStartIo: Enter routine\n"));
//
// Check if command is an ABORT request.
//
if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
//
// Verify that SRB to abort is still outstanding.
//
luExtension =
ScsiPortGetLogicalUnit(deviceExtension,
Srb->PathId,
Srb->TargetId,
Srb->Lun);
if (!luExtension->CurrentSrb) {
DebugPrint((1, "A154xStartIo: SRB to abort already completed\n"));
//
// Complete abort SRB.
//
Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
ScsiPortNotification(RequestComplete,
deviceExtension,
Srb);
//
// Adapter ready for next request.
//
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
return TRUE;
}
//
// Get CCB to abort.
//
ccb = Srb->NextSrb->SrbExtension;
//
// Set abort SRB for completion.
//
ccb->AbortSrb = Srb;
} else {
ccb = Srb->SrbExtension;
//
// Save SRB back pointer in CCB.
//
ccb->SrbAddress = Srb;
}
//
// Make sure that this request isn't too long for the adapter. If so
// bounce it back as an invalid request
//
if ((deviceExtension->MaxCdbLength) &&
(deviceExtension->MaxCdbLength < Srb->CdbLength)) {
DebugPrint((1,"A154xStartIo: Srb->CdbLength [%d] > MaxCdbLength [%d]. Invalid request\n",
Srb->CdbLength,
deviceExtension->MaxCdbLength
));
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
ScsiPortNotification(RequestComplete,
deviceExtension,
Srb);
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
return TRUE;
}
//
// Get CCB physical address.
//
physicalCcb = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(deviceExtension, NULL, ccb, &length));
//
// Find free mailboxOut.
//
do {
mailboxOut = &noncachedExtension->Mbo[i % MB_COUNT];
i++;
} while (mailboxOut->Command != MBO_FREE);
//
// Save the next free location.
//
deviceExtension->MboIndex = (UCHAR) (i % MB_COUNT);
DebugPrint((3,"A154xStartIo: MBO address %lx, Loop count = %d\n", mailboxOut, i));
//
// Write CCB to mailbox.
//
FOUR_TO_THREE(&mailboxOut->Address,
(PFOUR_BYTE)&physicalCcb);
switch (Srb->Function) {
case SRB_FUNCTION_ABORT_COMMAND:
DebugPrint((1, "A154xStartIo: Abort request received\n"));
//
// Race condition (what if CCB to be aborted
// completes after setting new SrbAddress?)
//
mailboxOut->Command = MBO_ABORT;
break;
case SRB_FUNCTION_RESET_BUS:
//
// Reset aha154x and SCSI bus.
//
DebugPrint((1, "A154xStartIo: Reset bus request received\n"));
if (!A154xResetBus(
deviceExtension,
Srb->PathId
)) {
DebugPrint((1,"A154xStartIo: Reset bus failed\n"));
Srb->SrbStatus = SRB_STATUS_ERROR;
} else {
Srb->SrbStatus = SRB_STATUS_SUCCESS;
}
ScsiPortNotification(RequestComplete,
deviceExtension,
Srb);
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
return TRUE;
case SRB_FUNCTION_EXECUTE_SCSI:
//
// Get logical unit extension.
//
luExtension =
ScsiPortGetLogicalUnit(deviceExtension,
Srb->PathId,
Srb->TargetId,
Srb->Lun);
//
// Move SRB to logical unit extension.
//
luExtension->CurrentSrb = Srb;
//
// Build CCB.
//
BuildCcb(deviceExtension, Srb);
mailboxOut->Command = MBO_START;
break;
case SRB_FUNCTION_RESET_DEVICE:
DebugPrint((1,"A154xStartIo: Reset device not supported\n"));
//
// Drop through to default.
//
default:
//
// Set error, complete request
// and signal ready for next request.
//
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
ScsiPortNotification(RequestComplete,
deviceExtension,
Srb);
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
return TRUE;
} // end switch
//
// Tell 154xb a CCB is available now.
//
if (!WriteCommandRegister(deviceExtension,AC_START_SCSI_COMMAND, FALSE)) {
//
// Let request time out and fail.
//
DebugPrint((1,"A154xStartIo: Can't write command to adapter\n"));
deviceExtension->PendingRequest = TRUE;
} else {
//
// Command(s) submitted. Clear pending request flag.
//
deviceExtension->PendingRequest = FALSE;
//
// Adapter ready for next request.
//
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
}
return TRUE;
} // end A154xStartIo()
BOOLEAN
A154xInterrupt(
IN PVOID HwDeviceExtension
)
/*++
Routine Description:
This is the interrupt service routine for the adaptec 154x SCSI adapter.
It reads the interrupt register to determine if the adapter is indeed
the source of the interrupt and clears the interrupt at the device.
If the adapter is interrupting because a mailbox is full, the CCB is
retrieved to complete the request.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
TRUE if MailboxIn full
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PNONCACHED_EXTENSION noncachedExtension =
deviceExtension->NoncachedExtension;
PCCB ccb;
PSCSI_REQUEST_BLOCK srb;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
PMBI mailboxIn;
ULONG physicalCcb;
PHW_LU_EXTENSION luExtension;
ULONG residualBytes;
ULONG i;
UCHAR InterruptFlags;
InterruptFlags = ScsiPortReadPortUchar(&baseIoAddress->InterruptRegister);
//
// Determine cause of interrupt.
//
if ((InterruptFlags ^ IOP_ANY_INTERRUPT) == 0) {
DebugPrint((4,"A154xInterrupt: Spurious interrupt\n"));
return FALSE;
}
if (InterruptFlags & IOP_COMMAND_COMPLETE) {
//
// Adapter command completed.
//
DebugPrint((2,"A154xInterrupt: Adapter Command complete\n"));
DebugPrint((3,"A154xInterrupt: Interrupt flags %x\n", InterruptFlags));
DebugPrint((3,"A154xInterrupt: Status %x\n",
ScsiPortReadPortUchar(&baseIoAddress->StatusRegister)));
//
// Clear interrupt on adapter.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET);
return TRUE;
} else if (InterruptFlags & IOP_MBI_FULL) {
DebugPrint((3,"A154xInterrupt: MBI Full\n"));
//
// Clear interrupt on adapter.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET);
} else if (InterruptFlags & IOP_SCSI_RESET_DETECTED) {
DebugPrint((1,"A154xInterrupt: SCSI Reset detected\n"));
//
// Clear interrupt on adapter.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET);
//
// Notify of reset.
//
ScsiPortNotification(ResetDetected,
deviceExtension,
NULL);
#if defined(_SCAM_ENABLED)
//
// Interrupt handler where reset is detected
//
PerformScamProtocol(deviceExtension);
#endif
return TRUE;
}
//
// Determine which MailboxIn location contains the CCB.
//
for (i=0; i<MB_COUNT; i++) {
mailboxIn = &noncachedExtension->Mbi[deviceExtension->MbiIndex];
//
// Look for a mailbox entry with a legitimate status.
//
if (mailboxIn->Status != MBI_FREE) {
//
// Point to the next in box.
//
deviceExtension->MbiIndex = (deviceExtension->MbiIndex + 1) % MB_COUNT;
//
// MBI found. Convert CCB to big endian.
//
THREE_TO_FOUR((PFOUR_BYTE)&physicalCcb,
&mailboxIn->Address);
DebugPrint((3, "A154xInterrupt: Physical CCB %lx\n", physicalCcb));
//
// Check if physical CCB is zero.
// This is done to cover for hardware errors.
//
if (!physicalCcb) {
DebugPrint((1,"A154xInterrupt: Physical CCB address is 0\n"));
//
// Indicate MBI is available.
//
mailboxIn->Status = MBI_FREE;
continue;
}
//
// Convert Physical CCB to Virtual.
//
ccb = ScsiPortGetVirtualAddress(deviceExtension, ScsiPortConvertUlongToPhysicalAddress(physicalCcb));
DebugPrint((3, "A154xInterrupt: Virtual CCB %lx\n", ccb));
//
// Make sure the virtual address was found.
//
if (ccb == NULL) {
//
// A bad physcial address was return by the adapter.
// Log it as an error.
//
ScsiPortLogError(
HwDeviceExtension,
NULL,
0,
deviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
5 << 8
);
//
// Indicate MBI is available.
//
mailboxIn->Status = MBI_FREE;
continue;
}
//
// Get SRB from CCB.
//
srb = ccb->SrbAddress;
//
// Get logical unit extension.
//
luExtension =
ScsiPortGetLogicalUnit(deviceExtension,
srb->PathId,
srb->TargetId,
srb->Lun);
//
// Make sure the luExtension was found and it has a current request.
//
if (luExtension == NULL || (luExtension->CurrentSrb == NULL &&
mailboxIn->Status != MBI_NOT_FOUND)) {
//
// A bad physcial address was return by the adapter.
// Log it as an error.
//
ScsiPortLogError(
HwDeviceExtension,
NULL,
0,
deviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
(6 << 8) | mailboxIn->Status
);
//
// Indicate MBI is available.
//
mailboxIn->Status = MBI_FREE;
continue;
}
//
// Check MBI status.
//
switch (mailboxIn->Status) {
case MBI_SUCCESS:
srb->SrbStatus = SRB_STATUS_SUCCESS;
//
// Check for data underrun if using scatter/gather
// command with residual bytes.
//
if (deviceExtension->CcbScatterGatherCommand == SCATTER_GATHER_COMMAND) {
//
// Update SRB with number of bytes transferred.
//
THREE_TO_FOUR((PFOUR_BYTE)&residualBytes,
&ccb->DataLength);
if (residualBytes != 0) {
ULONG transferLength = srb->DataTransferLength;
DebugPrint((2,
"A154xInterrupt: Underrun occured. Request length = %lx, Residual length = %lx\n",
srb->DataTransferLength,
residualBytes));
//
// Update SRB with bytes transferred and
// underrun status.
//
srb->DataTransferLength -= residualBytes;
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
if ((LONG)(srb->DataTransferLength) < 0) {
DebugPrint((0,
"A154xInterrupt: Overrun occured. Request length = %lx, Residual length = %lx\n",
transferLength,
residualBytes));
//
// Seems to be a FW bug in some revs. where
// residual comes back as a negative number, yet the
// request is successful.
//
srb->DataTransferLength = 0;
srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
//
// Log the event and then the residual byte count.
//
ScsiPortLogError(HwDeviceExtension,
NULL,
0,
deviceExtension->HostTargetId,
0,
SP_PROTOCOL_ERROR,
0xb);
ScsiPortLogError(HwDeviceExtension,
NULL,
0,
deviceExtension->HostTargetId,
0,
SP_PROTOCOL_ERROR,
residualBytes);
}
}
}
luExtension->CurrentSrb = NULL;
break;
case MBI_NOT_FOUND:
DebugPrint((1, "A154xInterrupt: CCB abort failed %lx\n", ccb));
srb = ccb->AbortSrb;
srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
//
// Check if SRB still outstanding.
//
if (luExtension->CurrentSrb) {
//
// Complete this SRB.
//
luExtension->CurrentSrb->SrbStatus = SRB_STATUS_TIMEOUT;
ScsiPortNotification(RequestComplete,
deviceExtension,
luExtension->CurrentSrb);
luExtension->CurrentSrb = NULL;
}
break;
case MBI_ABORT:
DebugPrint((1, "A154xInterrupt: CCB aborted\n"));
//
// Update target status in aborted SRB.
//
srb->SrbStatus = SRB_STATUS_ABORTED;
//
// Call notification routine for the aborted SRB.
//
ScsiPortNotification(RequestComplete,
deviceExtension,
srb);
luExtension->CurrentSrb = NULL;
//
// Get the abort SRB from CCB.
//
srb = ccb->AbortSrb;
//
// Set status for completing abort request.
//
srb->SrbStatus = SRB_STATUS_SUCCESS;
break;
case MBI_ERROR:
DebugPrint((2, "A154xInterrupt: Error occurred\n"));
srb->SrbStatus = MapError(deviceExtension, srb, ccb);
//
// Check if ABORT command.
//
if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
//
// Check if SRB still outstanding.
//
if (luExtension->CurrentSrb) {
//
// Complete this SRB.
//
luExtension->CurrentSrb->SrbStatus = SRB_STATUS_TIMEOUT;
ScsiPortNotification(RequestComplete,
deviceExtension,
luExtension->CurrentSrb);
}
DebugPrint((1,"A154xInterrupt: Abort command failed\n"));
}
luExtension->CurrentSrb = NULL;
break;
default:
//
// Log the error.
//
ScsiPortLogError(
HwDeviceExtension,
NULL,
0,
deviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
(1 << 8) | mailboxIn->Status
);
DebugPrint((1, "A154xInterrupt: Unrecognized mailbox status\n"));
mailboxIn->Status = MBI_FREE;
continue;
} // end switch
//
// Indicate MBI is available.
//
mailboxIn->Status = MBI_FREE;
DebugPrint((2, "A154xInterrupt: SCSI Status %x\n", srb->ScsiStatus));
DebugPrint((2, "A154xInterrupt: Adapter Status %x\n", ccb->HostStatus));
//
// Update target status in SRB.
//
srb->ScsiStatus = ccb->TargetStatus;
//
// Signal request completion.
//
ScsiPortNotification(RequestComplete,
(PVOID)deviceExtension,
srb);
} else {
break;
} // end if ((mailboxIn->Status == MBI_SUCCESS ...
} // end for (i=0; i<MB_COUNT; i++) {
if (deviceExtension->PendingRequest) {
//
// The last write command to the adapter failed. Try and start it now.
//
deviceExtension->PendingRequest = FALSE;
//
// Tell 154xb a CCB is available now.
//
if (!WriteCommandRegister(deviceExtension,AC_START_SCSI_COMMAND, FALSE)) {
//
// Let request time out and fail.
//
DebugPrint((1,"A154xInterrupt: Can't write command to adapter\n"));
deviceExtension->PendingRequest = TRUE;
} else {
//
// Adapter ready for next request.
//
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
}
}
return TRUE;
} // end A154xInterrupt()
VOID
BuildCcb(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Build CCB for 154x.
Arguments:
DeviceExtenson
SRB
Return Value:
Nothing.
--*/
{
PCCB ccb = Srb->SrbExtension;
DebugPrint((3,"BuildCcb: Enter routine\n"));
//
// Set target id and LUN.
//
ccb->ControlByte = (UCHAR)(Srb->TargetId << 5) | Srb->Lun;
//
// Set CCB Operation Code.
//
ccb->OperationCode = DeviceExtension->CcbScatterGatherCommand;
//
// Set transfer direction bit.
//
if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
//
// Check if both direction bits are set. This is an
// indication that the direction has not been specified.
//
if (!(Srb->SrbFlags & SRB_FLAGS_DATA_IN)) {
ccb->ControlByte |= CCB_DATA_XFER_OUT;
}
} else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
ccb->ControlByte |= CCB_DATA_XFER_IN;
} else {
//
// if no data transfer, we must set ccb command to to INITIATOR
// instead of SCATTER_GATHER and zero ccb data pointer and length.
//
ccb->OperationCode = DeviceExtension->CcbInitiatorCommand;
ccb->DataPointer.Msb = 0;
ccb->DataPointer.Mid = 0;
ccb->DataPointer.Lsb = 0;
ccb->DataLength.Msb = 0;
ccb->DataLength.Mid = 0;
ccb->DataLength.Lsb = 0;
}
//
// 01h disables auto request sense.
//
ccb->RequestSenseLength = 1;
//
// Set CDB length and copy to CCB.
//
ccb->CdbLength = (UCHAR)Srb->CdbLength;
ScsiPortMoveMemory(ccb->Cdb, Srb->Cdb, ccb->CdbLength);
//
// Set reserved bytes to zero.
//
ccb->Reserved[0] = 0;
ccb->Reserved[1] = 0;
ccb->LinkIdentifier = 0;
//
// Zero link pointer.
//
ccb->LinkPointer.Msb = 0;
ccb->LinkPointer.Lsb = 0;
ccb->LinkPointer.Mid = 0;
//
// Build SDL in CCB if data transfer.
//
if (Srb->DataTransferLength > 0) {
BuildSdl(DeviceExtension, Srb);
}
//
// Move 0xff to Target Status to indicate
// CCB has not completed.
//
ccb->TargetStatus = 0xFF;
return;
} // end BuildCcb()
VOID
BuildSdl(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine builds a scatter/gather descriptor list for the CCB.
Arguments:
DeviceExtension
Srb
Return Value:
None
--*/
{
PVOID dataPointer = Srb->DataBuffer;
ULONG bytesLeft = Srb->DataTransferLength;
PCCB ccb = Srb->SrbExtension;
PSDL sdl = &ccb->Sdl;
ULONG physicalSdl;
ULONG physicalAddress;
ULONG length;
ULONG four;
PTHREE_BYTE three;
ULONG i = 0;
DebugPrint((3,"BuildSdl: Enter routine\n"));
//
// Get physical SDL address.
//
physicalSdl = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(DeviceExtension, NULL,
sdl, &length));
//
// Create SDL segment descriptors.
//
do {
DebugPrint((3, "BuildSdl: Data buffer %lx\n", dataPointer));
//
// Get physical address and length of contiguous
// physical buffer.
//
physicalAddress =
ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(DeviceExtension,
Srb,
dataPointer,
&length));
DebugPrint((3, "BuildSdl: Physical address %lx\n", physicalAddress));
DebugPrint((3, "BuildSdl: Data length %lx\n", length));
DebugPrint((3, "BuildSdl: 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;
}
//
// Convert length to 3-byte big endian format.
//
four = length;
three = &sdl->Sgd[i].Length;
FOUR_TO_THREE(three, (PFOUR_BYTE)&four);
//
// Convert physical address to 3-byte big endian format.
//
four = (ULONG)physicalAddress;
three = &sdl->Sgd[i].Address;
FOUR_TO_THREE(three, (PFOUR_BYTE)&four);
i++;
//
// Adjust counts.
//
dataPointer = (PUCHAR)dataPointer + length;
bytesLeft -= length;
} while (bytesLeft);
//##BW
//
// For data transfers that have less than one scatter gather element, convert
// CCB to one transfer without using SG element. This will clear up the data
// overrun/underrun problem with small transfers that reak havoc with scanners
// and CD-ROM's etc. This is the method employed in ASPI4DOS to avoid similar
// problems.
//
if (i == 0x1) {
//
// Only one element, so convert...
//
//
// The above Do..While loop performed all necessary conversions for the
// SRB buffer, so we copy over the length and address directly into the
// CCB
//
ccb->DataLength = sdl->Sgd[0x0].Length;
ccb->DataPointer = sdl->Sgd[0x0].Address;
//
// Change the OpCode from SG command to initiator command and we're
// done. Easy, huh?
//
ccb->OperationCode = SCSI_INITIATOR_COMMAND; //##BW _OLD_ command?
} else {
//
// Multiple SG elements, so continue as normal.
//
//
// Write SDL length to CCB.
//
four = i * sizeof(SGD);
three = &ccb->DataLength;
FOUR_TO_THREE(three, (PFOUR_BYTE)&four);
DebugPrint((3,"BuildSdl: SDL length is %d\n", four));
//
// Write SDL address to CCB.
//
FOUR_TO_THREE(&ccb->DataPointer,
(PFOUR_BYTE)&physicalSdl);
DebugPrint((3,"BuildSdl: SDL address is %lx\n", sdl));
DebugPrint((3,"BuildSdl: CCB address is %lx\n", ccb));
}
return;
} // end BuildSdl()
BOOLEAN
A154xResetBus(
IN PVOID HwDeviceExtension,
IN ULONG PathId
)
/*++
Routine Description:
Reset Adaptec 154X SCSI adapter and SCSI bus.
Initialize adapter mailbox.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
Nothing.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PNONCACHED_EXTENSION noncachedExtension =
deviceExtension->NoncachedExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
UCHAR status;
ULONG i;
DebugPrint((2,"ResetBus: Reset aha154X and SCSI bus\n"));
//
// Complete all outstanding requests with SRB_STATUS_BUS_RESET.
//
ScsiPortCompleteRequest(deviceExtension,
(UCHAR) PathId,
0xFF,
0xFF,
(ULONG) SRB_STATUS_BUS_RESET);
//
// Read status register.
//
status = ScsiPortReadPortUchar(&baseIoAddress->StatusRegister);
//
// If value is normal then reset device only.
//
if ((status & ~IOP_MAILBOX_INIT_REQUIRED) != IOP_SCSI_HBA_IDLE) {
//
// Reset SCSI chip.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_HARD_RESET);
ScsiPortStallExecution(500 * 1000);
//
// Wait up to 5000 microseconds for adapter to initialize.
//
for (i = 0; i < 5000; i++) {
ScsiPortStallExecution(1);
status = ScsiPortReadPortUchar(&deviceExtension->BaseIoAddress->StatusRegister);
if (status & IOP_SCSI_HBA_IDLE) {
break;
}
}
}
//
// Zero out mailboxes.
//
for (i=0; i<MB_COUNT; i++) {
PMBO mailboxOut;
PMBI mailboxIn;
mailboxIn = &noncachedExtension->Mbi[i];
mailboxOut = &noncachedExtension->Mbo[i];
mailboxOut->Command = mailboxIn->Status = 0;
}
//
// Zero previous indexes.
//
deviceExtension->MboIndex = 0;
deviceExtension->MbiIndex = 0;
if (deviceExtension->PendingRequest) {
deviceExtension->PendingRequest = FALSE;
//
// Adapter ready for next request.
//
ScsiPortNotification(NextRequest,
deviceExtension,
NULL);
}
if (!(status & IOP_SCSI_HBA_IDLE)) {
return(FALSE);
}
//
// Unlock mailboxes in case the adapter is a 1540B with 1Gb support
// or 1540C with extended translation enabled. Maiboxes cannot be
// initialized until unlock code is sent.
status = UnlockMailBoxes(deviceExtension);
if (!SpinForInterrupt(deviceExtension,FALSE)) {
DebugPrint((1,"A154xResetBus: Failed to unlock mailboxes\n"));
return FALSE;
}
DebugPrint((3,"ResetBus: Initialize mailbox\n"));
if (!WriteCommandRegister(deviceExtension,AC_MAILBOX_INITIALIZATION, TRUE)) {
DebugPrint((1,"A154xResetBus: Can't initialize mailboxes\n"));
return FALSE;
}
//
// Send Adapter number of mailbox locations.
//
if (!WriteDataRegister(deviceExtension,MB_COUNT)) {
return FALSE;
}
//
// Send the most significant byte of the mailbox physical address.
//
if (!WriteDataRegister(deviceExtension,
((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte2)) {
return FALSE;
}
//
// Send the middle byte of the mailbox physical address.
//
if (!WriteDataRegister(deviceExtension,
((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte1)) {
return FALSE;
}
//
// Send the least significant byte of the mailbox physical address.
//
if (!WriteDataRegister(deviceExtension,
((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte0)) {
return FALSE;
}
#ifdef FORCE_DMA_SPEED
//
// Set the DMA transfer speed to 5.0 MB/second. This is because
// faster transfer speeds cause data corruption on 486/33 machines.
// This overrides the card jumper setting.
//
if (!WriteCommandRegister(deviceExtension, AC_SET_TRANSFER_SPEED, TRUE)) {
DebugPrint((1,"Can't set dma transfer speed\n"));
} else if (!WriteDataRegister(deviceExtension, DMA_SPEED_50_MBS)) {
DebugPrint((1,"Can't set dma transfer speed\n"));
}
//
// Wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,TRUE)) {
DebugPrint((1,"Timed out waiting for adapter command to complete\n"));
}
#endif
//
// Override default setting for bus on time. This makes floppy
// drives work better with this adapter.
//
if (!WriteCommandRegister(deviceExtension, AC_SET_BUS_ON_TIME, TRUE)) {
DebugPrint((1,"Can't set bus on time\n"));
} else if (!WriteDataRegister(deviceExtension, 0x07)) {
DebugPrint((1,"Can't set bus on time\n"));
}
//
// Wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,TRUE)) {
DebugPrint((1,"Timed out waiting for adapter command to complete\n"));
}
#if defined(_SCAM_ENABLED)
//
// SCAM because we're a154xResetBus
//
PerformScamProtocol(deviceExtension);
#endif
return TRUE;
} // end A154xResetBus()
UCHAR
MapError(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb,
IN PCCB Ccb
)
/*++
Routine Description:
Translate A154x error to SRB error, and log an error if necessary.
Arguments:
HwDeviceExtension - The hardware device extension.
Srb - The failing Srb.
Ccb - Command Control Block contains error.
Return Value:
SRB Error
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
UCHAR status;
ULONG logError;
ULONG residualBytes;
switch (Ccb->HostStatus) {
case CCB_SELECTION_TIMEOUT:
return SRB_STATUS_SELECTION_TIMEOUT;
case CCB_COMPLETE:
if (Ccb->TargetStatus == SCSISTAT_CHECK_CONDITION) {
//
// Update SRB with number of bytes transferred.
//
THREE_TO_FOUR((PFOUR_BYTE)&residualBytes,
&Ccb->DataLength);
DebugPrint((2, "Aha154x MapError: Underrun occured. Request length = %lx, Residual length = %lx\n", Srb->DataTransferLength, residualBytes));
Srb->DataTransferLength -= residualBytes;
}
return SRB_STATUS_ERROR;
case CCB_DATA_OVER_UNDER_RUN:
//
// Check for data underrun if using scatter/gather
// command with residual bytes.
//
if (deviceExtension->CcbScatterGatherCommand == SCATTER_GATHER_COMMAND) {
THREE_TO_FOUR((PFOUR_BYTE)&residualBytes,
&Ccb->DataLength);
if (residualBytes) {
Srb->DataTransferLength -= residualBytes;
return SRB_STATUS_DATA_OVERRUN; //##BW this look good
} else {
logError = SP_PROTOCOL_ERROR;
}
}
//
// Return instead of posting DU/DO to the log file.
//
//status = SRB_STATUS_DATA_OVERRUN;
return SRB_STATUS_DATA_OVERRUN;
break;
case CCB_UNEXPECTED_BUS_FREE:
status = SRB_STATUS_UNEXPECTED_BUS_FREE;
logError = SP_UNEXPECTED_DISCONNECT;
break;
case CCB_PHASE_SEQUENCE_FAIL:
case CCB_INVALID_DIRECTION:
status = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
logError = SP_PROTOCOL_ERROR;
break;
case CCB_INVALID_OP_CODE:
//
// Try CCB commands without residual bytes.
//
deviceExtension->CcbScatterGatherCommand = SCATTER_GATHER_OLD_COMMAND;
deviceExtension->CcbInitiatorCommand = SCSI_INITIATOR_OLD_COMMAND;
status = SRB_STATUS_INVALID_REQUEST;
logError = SP_BAD_FW_WARNING;
break;
case CCB_INVALID_CCB:
case CCB_BAD_MBO_COMMAND:
case CCB_BAD_LINKED_LUN:
case CCB_DUPLICATE_CCB:
status = SRB_STATUS_INVALID_REQUEST;
logError = SP_INTERNAL_ADAPTER_ERROR;
break;
default:
status = SRB_STATUS_ERROR;
logError = SP_INTERNAL_ADAPTER_ERROR;
break;
}
ScsiPortLogError(
HwDeviceExtension,
Srb,
Srb->PathId,
Srb->TargetId,
Srb->Lun,
logError,
(2 << 8) | Ccb->HostStatus
);
return(status);
} // end MapError()
BOOLEAN
ReadCommandRegister(
IN PHW_DEVICE_EXTENSION DeviceExtension,
OUT PUCHAR DataByte,
IN BOOLEAN TimeOutFlag
)
/*++
Routine Description:
Read command register.
Arguments:
DeviceExtesion - Pointer to adapder extension
DataByte - Byte read from register
Return Value:
TRUE if command register read.
FALSE if timed out waiting for adapter.
--*/
{
PBASE_REGISTER baseIoAddress = DeviceExtension->BaseIoAddress;
ULONG i;
//
// Wait up to 5000 microseconds for adapter to be ready.
//
for (i=0; i<5000; i++) {
if (ScsiPortReadPortUchar(&baseIoAddress->StatusRegister) &
IOP_DATA_IN_PORT_FULL) {
//
// Adapter ready. Break out of loop.
//
break;
} else {
//
// Stall 1 microsecond before
// trying again.
//
ScsiPortStallExecution(1);
}
}
if ( (i==5000) && (TimeOutFlag == TRUE)) {
ScsiPortLogError(
DeviceExtension,
NULL,
0,
DeviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
3 << 8
);
DebugPrint((1, "Aha154x:ReadCommandRegister: Read command timed out\n"));
return FALSE;
}
*DataByte = ScsiPortReadPortUchar(&baseIoAddress->CommandRegister);
return TRUE;
} // end ReadCommandRegister()
BOOLEAN
WriteCommandRegister(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN UCHAR AdapterCommand,
IN BOOLEAN WaitForIdle
)
/*++
Routine Description:
Write operation code to command register.
Arguments:
DeviceExtension - Pointer to adapter extension
AdapterCommand - Value to be written to register
WaitForIdle - Indicates if the idle bit needs to be checked
Return Value:
TRUE if command sent.
FALSE if timed out waiting for adapter.
--*/
{
PBASE_REGISTER baseIoAddress = DeviceExtension->BaseIoAddress;
ULONG i;
UCHAR status;
//
// Wait up to 500 milliseconds for adapter to be ready.
//
for (i=0; i<5000; i++) {
status = ScsiPortReadPortUchar(&baseIoAddress->StatusRegister);
if ((status & IOP_COMMAND_DATA_OUT_FULL) ||
( WaitForIdle && !(status & IOP_SCSI_HBA_IDLE))) {
//
// Stall 100 microseconds before
// trying again.
//
ScsiPortStallExecution(100);
} else {
//
// Adapter ready. Break out of loop.
//
break;
}
}
if (i==5000) {
ScsiPortLogError(
DeviceExtension,
NULL,
0,
DeviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
(4 << 8) | status
);
DebugPrint((1, "Aha154x:WriteCommandRegister: Write command timed out\n"));
return FALSE;
}
ScsiPortWritePortUchar(&baseIoAddress->CommandRegister, AdapterCommand);
return TRUE;
} // end WriteCommandRegister()
BOOLEAN
WriteDataRegister(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN UCHAR DataByte
)
/*++
Routine Description:
Write data byte to data register.
Arguments:
DeviceExtension - Pointer to adapter extension
DataByte - Value to be written to register
Return Value:
TRUE if byte sent.
FALSE if timed out waiting for adapter.
--*/
{
PBASE_REGISTER baseIoAddress = DeviceExtension->BaseIoAddress;
ULONG i;
//
// Wait up to 500 microseconds for adapter to be idle
// and ready for next byte.
//
for (i=0; i<500; i++) {
if (ScsiPortReadPortUchar(&baseIoAddress->StatusRegister) &
IOP_COMMAND_DATA_OUT_FULL) {
//
// Stall 1 microsecond before
// trying again.
//
ScsiPortStallExecution(1);
} else {
//
// Adapter ready. Break out of loop.
//
break;
}
}
if (i==500) {
ScsiPortLogError(
DeviceExtension,
NULL,
0,
DeviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
8 << 8
);
DebugPrint((1, "Aha154x:WriteDataRegister: Write data timed out\n"));
return FALSE;
}
ScsiPortWritePortUchar(&baseIoAddress->CommandRegister, DataByte);
return TRUE;
} // end WriteDataRegister()
BOOLEAN
FirmwareBug (
IN PVOID HwDeviceExtension
)
/*++
Routine Description:
Check to see if the host adapter firmware has the scatter/gather
bug.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
Return FALSE if there is no firmware bug.
Return TRUE if firmware has scatter/gather bug.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
UCHAR ch;
int i;
//
// Issue a RETURN SETUP DATA command
// If timeout then return TRUE to indicate that there is a firmware bug.
//
if ((WriteCommandRegister(HwDeviceExtension,
AC_RETURN_SETUP_DATA,FALSE)) == FALSE) {
return TRUE;
}
//
// Tell the adapter we want to read in 0x11 bytes.
//
if (WriteDataRegister(HwDeviceExtension,0x11) == FALSE) {
return TRUE;
}
//
// Now try to read in 0x11 bytes.
//
for (i = 0; i< 0x11; i++) {
if (ReadCommandRegister(HwDeviceExtension,&ch,TRUE) == FALSE) {
return TRUE;
}
}
//
// Wait for HACC interrupt.
//
SpinForInterrupt(HwDeviceExtension,FALSE); // eddy
//
// Issue SET HA OPTION command.
//
if (WriteCommandRegister(HwDeviceExtension,
AC_SET_HA_OPTION,FALSE) == FALSE) {
return TRUE;
}
//
// Delay 500 microseconds.
//
ScsiPortStallExecution(500);
//
// Check for invalid command.
//
if ( (ScsiPortReadPortUchar(&baseIoAddress->StatusRegister) &
IOP_INVALID_COMMAND) ) {
//
// Clear adapter interrupt.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
return TRUE;
}
//
// send 01h
//
if (WriteDataRegister(HwDeviceExtension,0x01) == FALSE) {
return TRUE;
}
//
// Send same byte as was last received.
//
if (WriteDataRegister(HwDeviceExtension,ch) == FALSE) {
return TRUE;
}
//
// Clear adapter interrupt.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
return FALSE;
} // end of FirmwareBug ()
VOID
GetHostAdapterBoardId (
IN PVOID HwDeviceExtension,
OUT PUCHAR BoardId
)
/*++
Routine Description:
Get board id, firmware id and hardware id from the host adapter.
These info are used to determine if the host adapter supports
scatter/gather.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
Board id, hardware id and firmware id (in that order) by modyfing *BoardId
If there is any error, it will just return with *BoardId unmodified
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
UCHAR firmwareId;
UCHAR boardId;
UCHAR hardwareId;
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
if (!WriteCommandRegister(HwDeviceExtension,AC_ADAPTER_INQUIRY,FALSE)) {
return;
}
//
// Save byte 0 as board ID.
//
if ((ReadCommandRegister(HwDeviceExtension,&boardId,TRUE)) == FALSE) {
return;
}
//
// Ignore byte 1. Use hardwareId as scrap storage.
//
if ((ReadCommandRegister(HwDeviceExtension,&hardwareId,TRUE)) == FALSE) {
return;
}
//
// Save byte 2 as hardware revision in hardwareId.
//
if ((ReadCommandRegister(HwDeviceExtension,&hardwareId,TRUE)) == FALSE) {
return;
}
if ((ReadCommandRegister(HwDeviceExtension,&firmwareId,TRUE)) == FALSE) {
return;
}
//
// If timeout then return with *BoardId unmodified. This means that
// scatter/gather won't be supported.
//
if (!SpinForInterrupt(HwDeviceExtension, TRUE)) { // eddy
return;
}
//
// Clear adapter interrupt.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,IOP_INTERRUPT_RESET);
//
// Return with appropriate ID's.
//
*BoardId++ = boardId;
*BoardId++ = hardwareId;
*BoardId++ = firmwareId;
DebugPrint((2,"board id = %d, hardwareid = %d, firmware id = %d\n",
boardId,
hardwareId,
firmwareId));
} // end of GetHostAdapterBoardId ()
BOOLEAN
ScatterGatherSupported (
IN PHW_DEVICE_EXTENSION HwDeviceExtension
)
/*++
Routine Description:
Determine if the host adapter supports scatter/gather. On older
boards, scatter/gather is not supported. On some boards, there is
a bug that causes data corruption on multi-segment WRITE commands.
The algorithm to determine whether the board has the scatter/gather
bug is not "clean" but there is no other way since the firmware revision
levels returned by the host adapter are inconsistent with previous
releases.
Arguments:
HwDeviceExtension - HBA miniport driver's adapter data storage
Return Value:
Return TRUE if the algorithm determines that there is no scatter/gather
firmware bug.
Return FALSE if the algorithm determines that the adapter is an older
board or that the firmware contains the scatter gather bug
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
UCHAR HostAdapterId[3];
GetHostAdapterBoardId(HwDeviceExtension,&HostAdapterId[0]);
//
// If it's an older board then scatter/gather is not supported.
//
if ((HostAdapterId[BOARD_ID] == OLD_BOARD_ID1) ||
(HostAdapterId[BOARD_ID] == OLD_BOARD_ID2) ) {
return FALSE;
}
//
// If 1540A/B then check for firmware bug.
//
if (HostAdapterId[BOARD_ID] == A154X_BOARD) {
if (FirmwareBug(HwDeviceExtension)) {
return FALSE;
}
}
//
// Now check hardware ID and firmware ID.
//
if (HostAdapterId[HARDWARE_ID] != A154X_BAD_HARDWARE_ID) {
return TRUE;
}
if (HostAdapterId[FIRMWARE_ID] != A154X_BAD_FIRMWARE_ID) {
return TRUE;
}
//
// Host adapter has scatter/gather bug.
// Clear interrupt on adapter.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,IOP_INTERRUPT_RESET);
return FALSE;
} // end of ScatterGatherSupported ()
BOOLEAN
SpinForInterrupt(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN BOOLEAN TimeOutFlag
)
/*++
Routine Description:
Wait for interrupt.
Arguments:
DeviceExtension - Pointer to adapter extension
Return Value:
TRUE if interrupt occurred.
FALSE if timed out waiting for interrupt.
--*/
{
PBASE_REGISTER baseIoAddress = DeviceExtension->BaseIoAddress;
ULONG i;
//
// Wait up to 5 millisecond for interrupt to occur.
//
for (i=0; i<5000; i++) {
if (ScsiPortReadPortUchar(&baseIoAddress->InterruptRegister) & IOP_COMMAND_COMPLETE) {
//
// Interrupt occurred. Break out of wait loop.
//
break;
} else {
//
// Stall one microsecond.
//
ScsiPortStallExecution(1);
}
}
if ( (i==5000) && (TimeOutFlag == TRUE)) {
ScsiPortLogError(DeviceExtension,
NULL,
0,
DeviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
9 << 8
);
DebugPrint((1, "Aha154x:SpinForInterrupt: Timed out waiting for interrupt\n"));
return FALSE;
} else {
//
// Clear interrupt on adapter.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET);
return TRUE;
}
} // end SpinForInterrupt()
BOOLEAN UnlockMailBoxes (
IN PVOID HwDeviceExtension
)
/*++
Routine Description:
Unlock 1542B+ or 1542C mailboxes so that the driver
can zero out mailboxes when it's initializing the adapter.
The mailboxes are locked if:
1. >1Gb option is enabled (this option is available for 154xB+ and
154xC).
2. Dynamic scan lock option is enabled (154xC board only)
The reason the mailboxes are locked by the adapter's firmware is
because the BIOS is now reporting 255/63 translation instead of 64/32.
As such, if a user inadvertently enabled the >1Gb option (enabling
255/63 translation) and still uses an old driver, hard disk data
will be corrupted. Therefore, the firmware will not allow mailboxes
to be initialized unless the user knows what he is doing and updates
his driver so that his disk won't be trashed.
Arguments:
DeviceExtension - Pointer to adapter extension
Return Value:
TRUE if mailboxes are unlocked.
FALSE if mailboxes are not unlocked.
Note that if the adapter is just a 154xB board (without the >1Gb
option), this routine will return FALSE.
--*/
{
UCHAR locktype;
//
// Request information.
//
if (WriteCommandRegister(HwDeviceExtension, AC_GET_BIOS_INFO, TRUE) == FALSE) {
return FALSE;
}
//
// Retrieve first byte.
//
if (ReadCommandRegister(HwDeviceExtension,&locktype,FALSE) == FALSE) {
return FALSE;
}
//
// Check for extended bios translation enabled option on 1540C and
// 1540B with 1GB support.
//
if (locktype != TRANSLATION_ENABLED) {
//
// Extended translation is disabled. Retrieve lock status.
//
if (ReadCommandRegister(HwDeviceExtension,&locktype,FALSE) == FALSE) {
return FALSE;
}
//
// Wait for HACC interrupt.
//
SpinForInterrupt(HwDeviceExtension,FALSE); // eddy
if (locktype == DYNAMIC_SCAN_LOCK) {
return(SendUnlockCommand(HwDeviceExtension,locktype));
}
return FALSE;
}
//
// Extended BIOS translation (255/63) is enabled.
//
if (ReadCommandRegister(HwDeviceExtension,&locktype,FALSE) == FALSE) {
return FALSE;
}
//
// Wait for HACC interrupt.
//
SpinForInterrupt(HwDeviceExtension,FALSE); // eddy
if ((locktype == TRANSLATION_LOCK) || (locktype == DYNAMIC_SCAN_LOCK)) {
return(SendUnlockCommand(HwDeviceExtension,locktype));
}
return FALSE;
} // end of UnlockMailBoxes ()
BOOLEAN
SendUnlockCommand(
IN PVOID HwDeviceExtension,
IN UCHAR locktype
)
/*++
Routine Description:
Send unlock command to 1542B+ or 1542C board so that the driver
can zero out mailboxes when it's initializing the adapter.
Arguments:
DeviceExtension - Pointer to adapter extension
Return Value:
TRUE if commands are sent successfully.
FALSE if not.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress;
if (WriteCommandRegister(deviceExtension,
AC_SET_MAILBOX_INTERFACE,TRUE) == FALSE) {
return FALSE;
}
if (WriteDataRegister(deviceExtension,MAILBOX_UNLOCK) == FALSE) {
return FALSE;
}
if (WriteDataRegister(deviceExtension,locktype) == FALSE) {
return FALSE;
}
//
// Clear interrupt on adapter.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET);
return TRUE;
} // end of SendUnlockCommand ()
ULONG
AhaParseArgumentString(
IN PCHAR String,
IN PCHAR KeyWord
)
/*++
Routine Description:
This routine will parse the string for a match on the keyword, then
calculate the value for the keyword and return it to the caller.
Arguments:
String - The ASCII string to parse.
KeyWord - The keyword for the value desired.
Return Values:
Zero if value not found
Value converted from ASCII to binary.
--*/
{
PCHAR cptr;
PCHAR kptr;
ULONG value;
ULONG stringLength = 0;
ULONG keyWordLength = 0;
ULONG index;
//
// Calculate the string length and lower case all characters.
//
cptr = String;
while (*cptr) {
if (*cptr >= 'A' && *cptr <= 'Z') {
*cptr = *cptr + ('a' - 'A');
}
cptr++;
stringLength++;
}
//
// Calculate the keyword length and lower case all characters.
//
cptr = KeyWord;
while (*cptr) {
if (*cptr >= 'A' && *cptr <= 'Z') {
*cptr = *cptr + ('a' - 'A');
}
cptr++;
keyWordLength++;
}
if (keyWordLength > stringLength) {
//
// Can't possibly have a match.
//
return 0;
}
//
// Now setup and start the compare.
//
cptr = String;
ContinueSearch:
//
// The input string may start with white space. Skip it.
//
while (*cptr == ' ' || *cptr == '\t') {
cptr++;
}
if (*cptr == '\0') {
//
// end of string.
//
return 0;
}
kptr = KeyWord;
while (*cptr++ == *kptr++) {
if (*(cptr - 1) == '\0') {
//
// end of string
//
return 0;
}
}
if (*(kptr - 1) == '\0') {
//
// May have a match backup and check for blank or equals.
//
cptr--;
while (*cptr == ' ' || *cptr == '\t') {
cptr++;
}
//
// Found a match. Make sure there is an equals.
//
if (*cptr != '=') {
//
// Not a match so move to the next semicolon.
//
while (*cptr) {
if (*cptr++ == ';') {
goto ContinueSearch;
}
}
return 0;
}
//
// Skip the equals sign.
//
cptr++;
//
// Skip white space.
//
while ((*cptr == ' ') || (*cptr == '\t')) {
cptr++;
}
if (*cptr == '\0') {
//
// Early end of string, return not found
//
return 0;
}
if (*cptr == ';') {
//
// This isn't it either.
//
cptr++;
goto ContinueSearch;
}
value = 0;
if ((*cptr == '0') && (*(cptr + 1) == 'x')) {
//
// Value is in Hex. Skip the "0x"
//
cptr += 2;
for (index = 0; *(cptr + index); index++) {
if (*(cptr + index) == ' ' ||
*(cptr + index) == '\t' ||
*(cptr + index) == ';') {
break;
}
if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
value = (16 * value) + (*(cptr + index) - '0');
} else {
if ((*(cptr + index) >= 'a') && (*(cptr + index) <= 'f')) {
value = (16 * value) + (*(cptr + index) - 'a' + 10);
} else {
//
// Syntax error, return not found.
//
return 0;
}
}
}
} else {
//
// Value is in Decimal.
//
for (index = 0; *(cptr + index); index++) {
if (*(cptr + index) == ' ' ||
*(cptr + index) == '\t' ||
*(cptr + index) == ';') {
break;
}
if ((*(cptr + index) >= '0') && (*(cptr + index) <= '9')) {
value = (10 * value) + (*(cptr + index) - '0');
} else {
//
// Syntax error return not found.
//
return 0;
}
}
}
return value;
} else {
//
// Not a match check for ';' to continue search.
//
while (*cptr) {
if (*cptr++ == ';') {
goto ContinueSearch;
}
}
return 0;
}
}
BOOLEAN
A4448ReadString(
IN PHW_DEVICE_EXTENSION deviceExtension,
PUCHAR theString,
UCHAR stringLength,
UCHAR stringCommand
)
/*++
Routine Description:
Arguments:
Return Values:
True if read was OK.
False otherwise.
--*/
{
ULONG ii;
//
// Send in the string command
//
if (!WriteCommandRegister(deviceExtension, stringCommand, TRUE)) {
return FALSE;
}
//
// Send in the string length
//
if (!WriteCommandRegister(deviceExtension, stringLength, FALSE)) {
return FALSE;
}
//
// Read each byte of the string
//
for (ii = 0; ii < stringLength; ++ii) {
if (!ReadCommandRegister(deviceExtension, &theString[ii],FALSE)) {
return FALSE;
}
}
//
// Wait for interrupt.
//
if (!SpinForInterrupt(deviceExtension,FALSE)) {
return FALSE;
}
return TRUE;
} // End A4448ReadString
BOOLEAN
A4448IsAmi(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
ULONG portNumber
)
/*++
Routine Description:
This routine determines if the adapter this driver recognized is an
AMI4448. Eddy Quicksall of AMI provided MS with this detection code.
Arguments:
HwDeviceExtension - Pointer to driver device data area.
ConfigInfo - Structure describing this adapter's configuration.
portNumber - Indicates the ordinal of the card relative to this driver.
Return Values:
True if an AMI board.
False otherwise.
--*/
{
PUCHAR x330IoSpace; // mapped I/O for 330
ULONG x330Address; // unmapped 330
PX330_REGISTER x330IoBase; // mapped 330 for use with struct X330_REGISTER
//
// this string is only avalable if new BIOS
// you will get INVDCMD if an old BIOS or some other manufacturer
// if an old BIOS, there is nothing that can be done except to check
// the Manufacturers ID if you are on an EISA system
//
struct _CONFIG_STRING {
UCHAR companyString[4]; // AMI<0)
UCHAR modelString[6]; // <0>
UCHAR seriesString[6]; // 48<0>
UCHAR versionString[6]; // 1.00<0)
} configString;
//
// Get the system physical address for this card. The card uses I/O space.
// This actually just maps the I/O if necessary, it does not reserve it.
//
x330IoSpace = ScsiPortGetDeviceBase(
HwDeviceExtension, // HwDeviceExtension
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
ScsiPortConvertUlongToPhysicalAddress(portNumber),
4, // NumberOfBytes
TRUE // InIoSpace
);
//
// Intel port number
//
x330Address = portNumber;
//
// Check to see if the adapter is present in the system.
//
x330IoBase = (PX330_REGISTER)(x330IoSpace);
//
// Criteria is IDLE and not STST,DIAGF,INVDCMD
// but INIT, CDF, and DF are don't cares.
//
// Can't check for INIT because the driver may already be running if it
// is the boot device.
//
if (((ScsiPortReadPortUchar((PUCHAR)x330IoBase)) & (~0x2C)) == 0x10) {
if (A4448ReadString(HwDeviceExtension, (PUCHAR)&configString,
sizeof(configString), AC_AMI_INQUIRY ) &&
configString.companyString[0] == 'A' &&
configString.companyString[1] == 'M' &&
configString.companyString[2] == 'I') {
return TRUE;
}
}
return FALSE;
}