|
|
Microsoft Windows NT Build 511 (DDK SDK) 11-01-1993
/*++
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)
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;
} 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 PULONG AdapterCount,
OUT PBOOLEAN Again
);
ULONG
A154xFindAdapter(
IN PVOID HwDeviceExtension,
IN PVOID Context,
IN PVOID BusInformation,
IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again
);
BOOLEAN
A154xHwInitialize(
IN PVOID DeviceExtension
);
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
FinishHBACmd(
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
);
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;
ULONG adapterCount;
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;
//
// 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 = 1;
//
// 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.
//
adapterCount = 0;
isaStatus = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &adapterCount);
//
// Now try to configure for the Mca bus.
// Specifiy the bus type.
//
hwInitializationData.AdapterInterfaceType = MicroChannel;
adapterCount = 0;
mcaStatus = ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &adapterCount);
//
// Return the smaller status.
//
return(mcaStatus < isaStatus ? mcaStatus : isaStatus);
} // end A154xEntry()
ULONG
A154xFindAdapter(
IN PVOID HwDeviceExtension,
IN PVOID 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;
//
// 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 not adapter's found then return.
//
if (status != SP_RETURN_FOUND) {
return(status);
}
//
// Issue adapter command to get IRQ.
//
// Returns 3 data bytes:
//
// Byte 0 Dma Channel
//
// Byte 1 Interrupt Channel
//
// Byte 2 Adapter SCSI 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: Couldn'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: Couldn'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: Couldn't read adapter SCSI id\n"));
return SP_RETURN_ERROR;
}
//
// Set number of buses.
//
ConfigInfo->NumberOfBuses = 1;
ConfigInfo->InitiatorBusId[0] = adapterTid;
deviceExtension->HostTargetId = adapterTid;
ConfigInfo->MaximumTransferLength = MAX_TRANSFER_SIZE;
ConfigInfo->NumberOfPhysicalBreaks = MAX_SG_DESCRIPTORS - 1;
ConfigInfo->ScatterGather = ScatterGatherSupported(HwDeviceExtension);
if (!ConfigInfo->ScatterGather) {
//ConfigInfo->NumberOfPhysicalBreaks = 1;
DebugPrint((1,"Aha154x: Scatter/Gather not supported!"));
}
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));
}
}
DebugPrint((3,"A154xFindAdapter: Configuration completed\n"));
return SP_RETURN_FOUND;
} // end A154xFindAdapter()
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.
--*/
{
ULONG i;
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;
}
//
// Interrupt handler is not yet installed so wait for HACC by hand.
//
for (i = 0; i<5000; i++) {
if (ScsiPortReadPortUchar(&baseIoAddress->InterruptRegister) ==
(IOP_ANY_INTERRUPT | IOP_COMMAND_COMPLETE) ) {
break;
}
ScsiPortStallExecution(1);
}
if ((specialOptions == 0x30) || (specialOptions == 0x42)) {
return TRUE;
}
return FALSE;
}
ULONG
A154xDetermineInstalled(
IN PHW_DEVICE_EXTENSION HwDeviceExtension,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
IN OUT PULONG AdapterCount,
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;
//
// 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, // HwDeviceExtension
ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
ScsiPortConvertUlongToPhysicalAddress(0),
0x400, // NumberOfBytes
TRUE // InIoSpace
);
//
// Scan though the adapter address looking for adapters.
//
while (AdapterAddresses[*AdapterCount] != 0) {
//
// Check to see if adapter present in system.
//
baseIoAddress = (PBASE_REGISTER)(ioSpace + AdapterAddresses[*AdapterCount]);
HwDeviceExtension->BaseIoAddress = baseIoAddress;
//
// Update the adapter count.
//
(*AdapterCount)++;
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, AdapterAddresses[(*AdapterCount) - 1],
(BOOLEAN)(ConfigInfo->AdapterInterfaceType == MicroChannel ? TRUE : FALSE))) {
DebugPrint((1,"A154xDetermineInstalled: Clone command completed successfully - \n not our board;"));
continue;
}
//
// An adapter has been found. Set the base address in the device
// extension, and request another call.
//
HwDeviceExtension->BaseIoAddress = baseIoAddress;
*Again = TRUE;
//
// Fill in the access array information.
//
(*ConfigInfo->AccessRanges)[0].RangeStart =
ScsiPortConvertUlongToPhysicalAddress(
AdapterAddresses[*AdapterCount - 1]);
(*ConfigInfo->AccessRanges)[0].RangeLength = 4;
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
return(SP_RETURN_FOUND);
}
}
//
// The entire table has been searched and no adapters have been found.
// There is no need to call again and the device base can now be freed.
// Clear the adapter count for the next bus.
//
*Again = FALSE;
*(AdapterCount) = 0;
ScsiPortFreeDeviceBase(
HwDeviceExtension,
ioSpace
);
return(SP_RETURN_NOT_FOUND);
} // end A154xDetermineInstalled()
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);
//
// 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: Couldn'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;
}
return TRUE;
} // end A154xHwInitialize()
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;
}
//
// 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"));
//
// BUGBUG: 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_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);
return TRUE;
} else {
DebugPrint((4,"A154xInterrupt: Spurious interrupt\n"));
return FALSE;
}
//
// 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:
//
// Update SRB with number of bytes transferred.
//
THREE_TO_FOUR((PFOUR_BYTE)&residualBytes,
&ccb->DataLength);
if (residualBytes != 0) {
DebugPrint((1, "A154xInterrupt: Underrun occured. Request length = %lx, Residual length = %lx\n", srb->DataTransferLength, residualBytes));
srb->DataTransferLength -= residualBytes;
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
} else {
srb->SrbStatus = SRB_STATUS_SUCCESS;
}
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 = SCATTER_GATHER_COMMAND;
//
// 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 = SCSI_INITIATOR_COMMAND;
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);
//
// 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);
//
// 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: Couldn'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"));
}
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
--*/
{
UCHAR status;
ULONG logError;
ULONG residualBytes;
switch (Ccb->HostStatus) {
case CCB_COMPLETE:
case CCB_SELECTION_TIMEOUT:
if (Ccb->TargetStatus == SCSISTAT_CHECK_CONDITION) {
//
// Update SRB with number of bytes transferred.
//
THREE_TO_FOUR((PFOUR_BYTE)&residualBytes,
&Ccb->DataLength);
DebugPrint((1, "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:
THREE_TO_FOUR((PFOUR_BYTE)&residualBytes,
&Ccb->DataLength);
if (residualBytes) {
Srb->DataTransferLength -= residualBytes;
} else {
logError = SP_PROTOCOL_ERROR;
}
status = 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_BAD_MBO_COMMAND:
case CCB_INVALID_OP_CODE:
case CCB_BAD_LINKED_LUN:
case CCB_DUPLICATE_CCB:
case CCB_INVALID_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 microseconds 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 1 microsecond before
// trying again.
//
ScsiPortStallExecution(1);
} 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;
}
}
//
// Clear adapter interrupt.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
//
// 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 board_Id;
UCHAR hardwareId;
ULONG i;
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,
IOP_INTERRUPT_RESET);
if (!WriteCommandRegister(HwDeviceExtension,AC_ADAPTER_INQUIRY,FALSE)) {
return;
}
//
// Save byte 0 as board ID.
//
if ((ReadCommandRegister(HwDeviceExtension,&board_Id,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;
}
//
// Interrupt handler is not yet installed so wait for HACC by hand.
//
for (i = 0; i<5000; i++) {
if (ScsiPortReadPortUchar(&baseIoAddress->InterruptRegister) ==
(IOP_ANY_INTERRUPT | IOP_COMMAND_COMPLETE) ) {
break;
}
ScsiPortStallExecution(1);
}
//
// If timeout then return with *BoardId unmodified. This means that
// scatter/gather won't be supported.
//
if (i == 5000) {
return;
}
//
// Clear adapter interrupt.
//
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,IOP_INTERRUPT_RESET);
//
// Return with appropriate ID's.
//
*BoardId++ = board_Id;
*BoardId++ = hardwareId;
*BoardId++ = firmwareId;
DebugPrint((1,"board_id = %d\n",board_Id));
DebugPrint((1,"hardwareid = %d\n",hardwareId));
DebugPrint((1,"fid = %d\n",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 1 millisecond for interrupt to occur.
//
for (i=0; i<1000; i++) {
if (ScsiPortReadPortUchar(&baseIoAddress->InterruptRegister) & IOP_COMMAND_COMPLETE) {
//
// Interrupt occurred. Break out of wait loop.
//
break;
} else {
//
// Stall one microsecond.
//
ScsiPortStallExecution(1);
}
}
if ( (i==1000) && (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;
}
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;
}
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 ()
BOOLEAN
FinishHBACmd(
IN PHW_DEVICE_EXTENSION HwDeviceExtension
)
/*++
Routine Description:
Wait for command complete interrupt, clear interrupt, chk cmd status.
This is done because the driver has not registered our interrupt handler
(which would normally do this work).
Arguments:
HwDeviceExtension - Pointer to adapter extension
Return Value:
TRUE if command completed successfully.
FALSE if timed out waiting for adapter or invalid command.
--*/
{
PBASE_REGISTER baseIoAddress = HwDeviceExtension->BaseIoAddress;
ULONG i;
for (i=0; i<500; i++) {
if (!((ScsiPortReadPortUchar(&baseIoAddress->InterruptRegister) &
(IOP_COMMAND_COMPLETE | IOP_ANY_INTERRUPT))==
(IOP_COMMAND_COMPLETE | IOP_ANY_INTERRUPT))) {
//
// Stall 1 microsecond before
// trying again.
//
ScsiPortStallExecution(1);
} else {
//
// Adapter ready. Break out of loop.
//
break;
}
}
if (i==500) {
ScsiPortLogError(HwDeviceExtension,
NULL,
0,
HwDeviceExtension->HostTargetId,
0,
SP_INTERNAL_ADAPTER_ERROR,
4 << 8);
DebugPrint((1, "A154x:FinishHBACmd: Wait for CmdCmplt & AnyIntr failed\n"));
return FALSE;
}
ScsiPortWritePortUchar(&baseIoAddress->StatusRegister,IOP_INTERRUPT_RESET);
if (ScsiPortReadPortUchar(&baseIoAddress->StatusRegister) & (IOP_INVALID_COMMAND)) {
return FALSE;
}
return TRUE;
} // end FinishHBACmd()
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;
}
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.