4422 lines
102 KiB
C
4422 lines
102 KiB
C
/*++
|
||
|
||
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;
|
||
}
|