diff options
Diffstat (limited to 'private/ntos/miniport/aha154x')
-rw-r--r-- | private/ntos/miniport/aha154x/aha154x.c | 4421 | ||||
-rw-r--r-- | private/ntos/miniport/aha154x/aha154x.h | 472 | ||||
-rw-r--r-- | private/ntos/miniport/aha154x/aha154x.rc | 90 | ||||
-rw-r--r-- | private/ntos/miniport/aha154x/aha154x.sys | 0 | ||||
-rw-r--r-- | private/ntos/miniport/aha154x/makefile | 7 | ||||
-rw-r--r-- | private/ntos/miniport/aha154x/sources | 40 |
6 files changed, 5030 insertions, 0 deletions
diff --git a/private/ntos/miniport/aha154x/aha154x.c b/private/ntos/miniport/aha154x/aha154x.c new file mode 100644 index 000000000..952d5a104 --- /dev/null +++ b/private/ntos/miniport/aha154x/aha154x.c @@ -0,0 +1,4421 @@ +/*++ + +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; +} diff --git a/private/ntos/miniport/aha154x/aha154x.h b/private/ntos/miniport/aha154x/aha154x.h new file mode 100644 index 000000000..6339c4e09 --- /dev/null +++ b/private/ntos/miniport/aha154x/aha154x.h @@ -0,0 +1,472 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + aha154x.h + +Abstract: + + This module contains the structures, specific to the Adaptec aha154x + host bus adapter, used by the SCSI miniport driver. Data structures + that are part of standard ANSI SCSI will be defined in a header + file that will be available to all SCSI device drivers. + +Author: + + Mike Glass December 1990 + Bill Williams (Adaptec) + +Revision History: + +--*/ + +#include "scsi.h" + +// +// The following definitions are used to convert ULONG addresses +// to Adaptec's 3 byte address format. +// + +typedef struct _THREE_BYTE { + UCHAR Msb; + UCHAR Mid; + UCHAR Lsb; +} THREE_BYTE, *PTHREE_BYTE; + +// +// Convert four-byte Little Endian to three-byte Big Endian +// + +#define FOUR_TO_THREE(Three, Four) { \ + ASSERT(!((Four)->Byte3)); \ + (Three)->Lsb = (Four)->Byte0; \ + (Three)->Mid = (Four)->Byte1; \ + (Three)->Msb = (Four)->Byte2; \ +} + +#define THREE_TO_FOUR(Four, Three) { \ + (Four)->Byte0 = (Three)->Lsb; \ + (Four)->Byte1 = (Three)->Mid; \ + (Four)->Byte2 = (Three)->Msb; \ + (Four)->Byte3 = 0; \ +} + +// +// Context information for adapter scan/sniff +// + +typedef struct _SCAN_CONTEXT { + ULONG adapterCount; + ULONG biosScanStart; +} SCAN_CONTEXT, *PSCAN_CONTEXT; + +/////////////////////////////////////////////////////////////////////////////// +// +// CCB - Adaptec SCSI Command Control Block +// +// The CCB is a superset of the CDB (Command Descriptor Block) +// and specifies detailed information about a SCSI command. +// +/////////////////////////////////////////////////////////////////////////////// + +// +// Byte 0 Command Control Block Operation Code +// + +#define SCSI_INITIATOR_OLD_COMMAND 0x00 +#define TARGET_MODE_COMMAND 0x01 +#define SCATTER_GATHER_OLD_COMMAND 0x02 +#define SCSI_INITIATOR_COMMAND 0x03 +#define SCATTER_GATHER_COMMAND 0x04 + +// +// Byte 1 Address and Direction Control +// + +#define CCB_TARGET_ID_SHIFT 0x06 // CCB Op Code = 00, 02 +#define CCB_INITIATOR_ID_SHIFT 0x06 // CCB Op Code = 01 +#define CCB_DATA_XFER_OUT 0x10 // Write +#define CCB_DATA_XFER_IN 0x08 // Read +#define CCB_LUN_MASK 0x07 // Logical Unit Number + +// +// Byte 2 SCSI_Command_Length - Length of SCSI CDB +// +// Byte 3 Request Sense Allocation Length +// + +#define FOURTEEN_BYTES 0x00 // Request Sense Buffer size +#define NO_AUTO_REQUEST_SENSE 0x01 // No Request Sense Buffer + +// +// Bytes 4, 5 and 6 Data Length // Data transfer byte count +// +// Bytes 7, 8 and 9 Data Pointer // SGD List or Data Buffer +// +// Bytes 10, 11 and 12 Link Pointer // Next CCB in Linked List +// +// Byte 13 Command Link ID // TBD (I don't know yet) +// +// Byte 14 Host Status // Host Adapter status +// + +#define CCB_COMPLETE 0x00 // CCB completed without error +#define CCB_LINKED_COMPLETE 0x0A // Linked command completed +#define CCB_LINKED_COMPLETE_INT 0x0B // Linked complete with interrupt +#define CCB_SELECTION_TIMEOUT 0x11 // Set SCSI selection timed out +#define CCB_DATA_OVER_UNDER_RUN 0x12 +#define CCB_UNEXPECTED_BUS_FREE 0x13 // Target dropped SCSI BSY +#define CCB_PHASE_SEQUENCE_FAIL 0x14 // Target bus phase sequence failure +#define CCB_BAD_MBO_COMMAND 0x15 // MBO command not 0, 1 or 2 +#define CCB_INVALID_OP_CODE 0x16 // CCB invalid operation code +#define CCB_BAD_LINKED_LUN 0x17 // Linked CCB LUN different from first +#define CCB_INVALID_DIRECTION 0x18 // Invalid target direction +#define CCB_DUPLICATE_CCB 0x19 // Duplicate CCB +#define CCB_INVALID_CCB 0x1A // Invalid CCB - bad parameter + +// +// Byte 15 Target Status +// +// See SCSI.H files for these statuses. +// + +// +// Bytes 16 and 17 Reserved (must be 0) +// + +// +// Bytes 18 through 18+n-1, where n=size of CDB Command Descriptor Block +// + +// +// Bytes 18+n through 18+m-1, where m=buffer size Allocated for Sense Data +// + +#define REQUEST_SENSE_BUFFER_SIZE 18 + +/////////////////////////////////////////////////////////////////////////////// +// +// Scatter/Gather Segment List Definitions +// +/////////////////////////////////////////////////////////////////////////////// + +// +// Adapter limits +// + +#define MAX_SG_DESCRIPTORS 17 +#define MAX_TRANSFER_SIZE 64 * 1024 + +// +// Scatter/Gather Segment Descriptor Definition +// + +typedef struct _SGD { + THREE_BYTE Length; + THREE_BYTE Address; +} SGD, *PSGD; + +typedef struct _SDL { + SGD Sgd[MAX_SG_DESCRIPTORS]; +} SDL, *PSDL; + +#define SEGMENT_LIST_SIZE MAX_SG_DESCRIPTORS * sizeof(SGD) + +/////////////////////////////////////////////////////////////////////////////// +// +// CCB Typedef +// + +typedef struct _CCB { + UCHAR OperationCode; + UCHAR ControlByte; + UCHAR CdbLength; + UCHAR RequestSenseLength; + THREE_BYTE DataLength; + THREE_BYTE DataPointer; + THREE_BYTE LinkPointer; + UCHAR LinkIdentifier; + UCHAR HostStatus; + UCHAR TargetStatus; + UCHAR Reserved[2]; + UCHAR Cdb[MAXIMUM_CDB_SIZE]; + PVOID SrbAddress; + PVOID AbortSrb; + SDL Sdl; + UCHAR RequestSenseBuffer[REQUEST_SENSE_BUFFER_SIZE]; +} CCB, *PCCB; + +// +// CCB and request sense buffer +// + +#define CCB_SIZE sizeof(CCB) + +/////////////////////////////////////////////////////////////////////////////// +// +// Adapter Command Overview +// +// Adapter commands are issued by writing to the Command/Data Out port. +// They are used to initialize the host adapter and to establish control +// conditions within the host adapter. They may not be issued when there +// are outstanding SCSI commands. +// +// All adapter commands except Start SCSI(02) and Enable Mailbox-Out +// Interrupt(05) must be executed only when the IDLE bit (Status bit 4) +// is one. Many commands require additional parameter bytes which are +// then written to the Command/Data Out I/O port (base+1). Before each +// byte is written by the host to the host adapter, the host must verify +// that the CDF bit (Status bit 3) is zero, indicating that the command +// port is ready for another byte of information. The host adapter usually +// clears the Command/Data Out port within 100 microseconds. Some commands +// require information bytes to be returned from the host adapter to the +// host. In this case, the host monitors the DF bit (Status bit 2) to +// determine when the host adapter has placed a byte in the Data In I/O +// port for the host to read. The DF bit is reset automatically when the +// host reads the byte. The format of each adapter command is strictly +// defined, so the host adapter and host system can always agree upon the +// correct number of parameter bytes to be transferred during a command. +// +// +/////////////////////////////////////////////////////////////////////////////// + +// +// Host Adapter Command Operation Codes +// + +#define AC_NO_OPERATION 0x00 +#define AC_MAILBOX_INITIALIZATION 0x01 +#define AC_START_SCSI_COMMAND 0x02 +#define AC_START_BIOS_COMMAND 0x03 +#define AC_ADAPTER_INQUIRY 0x04 +#define AC_ENABLE_MBO_AVAIL_INT 0x05 +#define AC_SET_SELECTION_TIMEOUT 0x06 +#define AC_SET_BUS_ON_TIME 0x07 +#define AC_SET_BUS_OFF_TIME 0x08 +#define AC_SET_TRANSFER_SPEED 0x09 +#define AC_RET_INSTALLED_DEVICES 0x0A +#define AC_RET_CONFIGURATION_DATA 0x0B +#define AC_ENABLE_TARGET_MODE 0x0C +#define AC_RETURN_SETUP_DATA 0x0D +#define AC_WRITE_CHANNEL_2_BUFFER 0x1A +#define AC_READ_CHANNEL_2_BUFFER 0x1B +#define AC_WRITE_FIFO_BUFFER 0x1C +#define AC_READ_FIFO_BUFFER 0x1D +#define AC_ECHO_COMMAND_DATA 0x1F +#define AC_SET_HA_OPTION 0x21 +#define AC_RETURN_EEPROM 0x23 +#define AC_GET_BIOS_INFO 0x28 +#define AC_SET_MAILBOX_INTERFACE 0x29 +#define AC_EXTENDED_SETUP_INFO 0x8D + +// +//Adapter commands new to the AHA-154xCP are defined below. +// +#define AC_SET_DMS_BUS_SPEED 0x2B +#define AC_TERMINATION_AND_CABLE_STATUS 0x2C +#define AC_DEVICE_INQUIRY 0x2D +#define AC_SCSI_DEVICE_TABLE 0x2E +#define AC_PERFORM_SCAM 0x2F + +// +// EEPROM define for SCAM +// +#define SCSI_BUS_CONTROL_FLAG 0x06 +#define SCAM_ENABLED 0x40 + +// +// DMA Transfer Speeds +// + +#define DMA_SPEED_50_MBS 0x00 + +// +// I/O Port Interface +// + +typedef struct _BASE_REGISTER { + UCHAR StatusRegister; + UCHAR CommandRegister; + UCHAR InterruptRegister; +} BASE_REGISTER, *PBASE_REGISTER; + +// +// Base+0 Write: Control Register +// + +#define IOP_HARD_RESET 0x80 // bit 7 +#define IOP_SOFT_RESET 0x40 // bit 6 +#define IOP_INTERRUPT_RESET 0x20 // bit 5 +#define IOP_SCSI_BUS_RESET 0x10 // bit 4 + +// +// Base+0 Read: Status +// + +#define IOP_SELF_TEST 0x80 // bit 7 +#define IOP_INTERNAL_DIAG_FAILURE 0x40 // bit 6 +#define IOP_MAILBOX_INIT_REQUIRED 0x20 // bit 5 +#define IOP_SCSI_HBA_IDLE 0x10 // bit 4 +#define IOP_COMMAND_DATA_OUT_FULL 0x08 // bit 3 +#define IOP_DATA_IN_PORT_FULL 0x04 // bit 2 +#define IOP_INVALID_COMMAND 0X01 // bit 1 + +// +// Base+1 Write: Command/Data Out +// + +// +// Base+1 Read: Data In +// + +// +// Base+2 Read: Interrupt Flags +// + +#define IOP_ANY_INTERRUPT 0x80 // bit 7 +#define IOP_SCSI_RESET_DETECTED 0x08 // bit 3 +#define IOP_COMMAND_COMPLETE 0x04 // bit 2 +#define IOP_MBO_EMPTY 0x02 // bit 1 +#define IOP_MBI_FULL 0x01 // bit 0 + +/////////////////////////////////////////////////////////////////////////////// +// +// Mailbox Definitions +// +// +/////////////////////////////////////////////////////////////////////////////// + +// +// Mailbox Definition +// + +#define MB_COUNT 0x08 // number of mailboxes + +// +// Mailbox Out +// + +typedef struct _MBO { + UCHAR Command; + THREE_BYTE Address; +} MBO, *PMBO; + +// +// MBO Command Values +// + +#define MBO_FREE 0x00 +#define MBO_START 0x01 +#define MBO_ABORT 0x02 + +// +// Mailbox In +// + +typedef struct _MBI { + UCHAR Status; + THREE_BYTE Address; +} MBI, *PMBI; + +// +// MBI Status Values +// + +#define MBI_FREE 0x00 +#define MBI_SUCCESS 0x01 +#define MBI_ABORT 0x02 +#define MBI_NOT_FOUND 0x03 +#define MBI_ERROR 0x04 + +// +// Mailbox Initialization +// + +typedef struct _MAILBOX_INIT { + UCHAR Count; + THREE_BYTE Address; +} MAILBOX_INIT, *PMAILBOX_INIT; + +#define MAILBOX_UNLOCK 0x00 +#define TRANSLATION_LOCK 0x01 // mailbox locked for extended BIOS +#define DYNAMIC_SCAN_LOCK 0x02 // mailbox locked for 154xC +#define TRANSLATION_ENABLED 0x08 // extended BIOS translation (1023/64) + +// +// Scatter/Gather firmware bug detection +// + +#define BOARD_ID 0x00 +#define HARDWARE_ID 0x01 +#define FIRMWARE_ID 0x02 +#define OLD_BOARD_ID1 0x00 +#define OLD_BOARD_ID2 0x30 +#define A154X_BOARD 0x41 +#define A154X_BAD_HARDWARE_ID 0x30 +#define A154X_BAD_FIRMWARE_ID 0x33 + +// +// MCA specific definitions. +// + +#define NUMBER_POS_SLOTS 8 +#define POS_IDENTIFIER 0x0F1F +#define POS_PORT_MASK 0xC7 +#define POS_PORT_130 0x01 +#define POS_PORT_134 0x41 +#define POS_PORT_230 0x02 +#define POS_PORT_234 0x42 +#define POS_PORT_330 0x03 +#define POS_PORT_334 0x43 + +typedef struct _POS_DATA { + USHORT AdapterId; + UCHAR BiosEnabled; + UCHAR IoPortInformation; + UCHAR ScsiInformation; + UCHAR DmaInformation; +} POS_DATA, *PPOS_DATA; + +typedef struct _INIT_DATA { + + ULONG AdapterId; + ULONG CardSlot; + POS_DATA PosData[NUMBER_POS_SLOTS]; + +} INIT_DATA, *PINIT_DATA; + + +// +// Real Mode Adapter Config Info +// +typedef struct _RM_SAVRES { + UCHAR SDTPar; + UCHAR TxSpeed; + UCHAR BusOnTime; + UCHAR BusOffTime; + UCHAR NumMailBoxes; + UCHAR MBAddrHiByte; + UCHAR MBAddrMiByte; + UCHAR MBAddrLoByte; + UCHAR SyncNeg[8]; + UCHAR DisOpt; + +} RM_CFG, *PRM_CFG; + +// +//AMI Detect Code +// +#define AC_AMI_INQUIRY 0x41 // Get model number, ect. (ASCIIZ) + +// +// I/O Port Interface +// + +typedef struct _X330_REGISTER { + UCHAR StatusRegister; + UCHAR CommandRegister; + UCHAR InterruptRegister; + UCHAR DiagRegister; +} X330_REGISTER, *PX330_REGISTER; diff --git a/private/ntos/miniport/aha154x/aha154x.rc b/private/ntos/miniport/aha154x/aha154x.rc new file mode 100644 index 000000000..8c1fae3be --- /dev/null +++ b/private/ntos/miniport/aha154x/aha154x.rc @@ -0,0 +1,90 @@ +//*************************************************************************** +// +// Copyright 1992-1994 Adaptec, Inc. All rights reserved. This software +// contains the valuable trade secrets of Adaptec. The software is +// protected under copyright laws as an unpublished work of Adaptec. +// Notice is for informational purposes only and does not imply publication. +// The user of this software may make copies of the software for use with +// parts manufactured by Adaptec or under license from Adaptec and for no +// other use. +// +//*************************************************************************** + + +//=========================================================================== +// +// Module Name: +// +// aha154x.rc +// +// Abstract: +// +// This is the miniport resource file for the aha154x series miniport +// driver. +// +// Author: +// +// Bill Williams +// James Goodwin +// +// Environment: +// +// kernel mode only +// +// Notes: +// +// Revision History: +// +// Indentation: +// +// 4 spaces per tab +// +//=========================================================================== + +#include <windows.h> +#include <ntverp.h> + +// +// This is the DCS label for the version of the sources from which the driver +// was built. +// +#define SOURCE_VERSION "JSG003" + +// +// Declared miniport version number. Every unique binary released for any reason +// must have a unique version number. +// +#define MINIPORT_VERSION "v1.13" + +#if (defined _INCLUDE_DEV_BANNER) + // + // DEV_BANNER should always include a leading space before text. + // + #define DEV_BANNER " " +#else + #define DEV_BANNER +#endif + + +// +// This defines the copyright years for your driver. +// +#define VER_LEGALCOPYRIGHT_YEARS "1992-1995" + +// +// The defines below this line should not be changed. The defines listed above +// should be set for each driver release. +// +//**************************************************************************** + +//#define VER_COMPANYNAME_STR "Adaptec, Inc." +#define VER_INTERNALNAME_STR SOURCE_VERSION +#define VER_FILEVERSION_STR MINIPORT_VERSION DEV_BANNER +#define VER_FILEDESCRIPTION_STR "Adaptec AHA-154x series SCSI miniport" +#define VER_ORIGINALFILENAME_STR "aha154x.sys" +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) Adaptec, Inc. " VER_LEGALCOPYRIGHT_YEARS +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM + +#include "common.ver" + diff --git a/private/ntos/miniport/aha154x/aha154x.sys b/private/ntos/miniport/aha154x/aha154x.sys new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/private/ntos/miniport/aha154x/aha154x.sys diff --git a/private/ntos/miniport/aha154x/makefile b/private/ntos/miniport/aha154x/makefile new file mode 100644 index 000000000..58189757d --- /dev/null +++ b/private/ntos/miniport/aha154x/makefile @@ -0,0 +1,7 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the driver components of the Windows NT DDK +# + +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/ntos/miniport/aha154x/sources b/private/ntos/miniport/aha154x/sources new file mode 100644 index 000000000..e799e943a --- /dev/null +++ b/private/ntos/miniport/aha154x/sources @@ -0,0 +1,40 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=ntos +MINORCOMP=miniport + +TARGETNAME=aha154x +TARGETPATH=\nt\public\sdk\lib +TARGETTYPE=MINIPORT + +INCLUDES=..\..\inc + +TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\scsiport.lib \ + $(BASEDIR)\public\sdk\lib\*\ntoskrnl.lib + +#C_DEFINES=-D_SCAM_ENABLED + +SOURCES=aha154x.c \ + aha154x.rc |