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

2365 lines
54 KiB
C
Raw Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
oliesc1.c
Abstract:
This is the port driver for the Olivetti ESC-1 SCSI adapters.
Authors:
Bruno Sartirana (o-obruno) 13-Dec-1991
Environment:
kernel mode only
Notes:
Revision History:
12-Feb-1992: (o-obruno) Replaced calls to HAL and MM with calls to
ScsiPortGetDeviceBase and ScsiPortFreeDeviceBase.
8-Nov-1992: (o-obruno)
- Added error logging
- Removed adapter reset at initialization time
(it saves 2-3 secs.)
- Removed list of present peripherals
- Enhanced interrupt status check for better handling
of the ESC-2 interrupt status codes.
9-Apr-1993: (v-egidis)
- Removed the search for ESC-2 cards.
- Added call to ESC-2/EFP-2 driver init routine.
- Added code to claim the primary AT disk ctrl
if the AT mode is enabled.
- Now all the routine names start with the "OliEsc1".
- Removed all the "static" directives, this will
be very useful during the debugging sessions.
- Now if there is an error during the execution of
OliEsc1Configuration routine the SP_RETURN_ERROR
error code is returned instead of
SP_RETURN_NOT_FOUND.
- Now all the physical slots 1-15 are checked,
before only slots 1-7 were checked.
7-Jul-1993: (v-egidis)
- The reset has been changed to use the scsiport's
timer. This modification fixes the following
problem: the reset is taking too much DPC time.
--*/
#include "miniport.h"
#include "oliesc1.h" // includes scsi.h
//
// Function declarations
//
// Functions that start with 'OliEsc1' are entry points
// for the OS port driver.
//
VOID
OliEsc1BuildCcb(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
VOID
OliEsc1BuildSgl(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
ULONG
DriverEntry (
IN PVOID DriverObject,
IN PVOID Argument2
);
BOOLEAN
OliEsc1GetIrql(
IN PEISA_CONTROLLER eisaController,
PUCHAR Irql
);
BOOLEAN
OliEsc1CheckAtMode(
IN PEISA_CONTROLLER eisaController,
PBOOLEAN AtModeEnabled
);
ULONG
OliEsc1DriverEntry(
IN PVOID DriverObject,
IN PVOID Argument2
);
ULONG
OliEsc1Configuration(
IN PVOID HwDeviceExtension,
IN PVOID Context,
IN PVOID BusInformation,
IN PCHAR ArgumentString,
IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
OUT PBOOLEAN Again
);
BOOLEAN
OliEsc1Initialize(
IN PVOID HwDeviceExtension
);
BOOLEAN
OliEsc1Interrupt(
IN PVOID HwDeviceExtension
);
BOOLEAN
OliEsc1ResetBus(
IN PVOID HwDeviceExtension,
IN ULONG PathId
);
BOOLEAN
OliEsc1StartIo(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
);
BOOLEAN
OliEsc1SendCommand(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN UCHAR OperationCode,
IN PCCB ccb
);
BOOLEAN
OliEsc1SendCommandQuick(
IN PEISA_CONTROLLER EisaController,
IN UCHAR TaskId,
IN UCHAR OperationCode,
IN USHORT CommandLength,
IN ULONG Address
);
VOID
OliEsc1ResetAdapter(
IN PVOID Context
);
//
// External entry points
//
ULONG
OliEsc2DriverEntry(
IN PVOID DriverObject,
IN PVOID Argument2
);
VOID
OliEsc1BuildCcb(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
Build a Command Control Block for the ESC-1.
Arguments:
DeviceExtension Pointer to the device extension for this driver.
Srb Pointer to the Scsi Request Block to service
Return Value:
Nothing.
--*/
{
PCCB ccb; // Virtual address of the CCB
ULONG physicalAddress; // Physical address of the CCB
ULONG length; // Length of contiguous memory in the
// data buffer, starting at the
// beginning of the buffer
DebugPrint((3,"OliEsc1BuildCcb: Enter routine\n"));
//
// Get the CCB address
//
ccb = Srb->SrbExtension;
//
// Set LUN and Target ID
//
ccb->TaskId = Srb->Lun | (UCHAR) (Srb->TargetId << CCB_TARGET_ID_SHIFT);
//
// We distinguish between the Abort command and all the others, because
// we translate the Abort into a Target Reset, which does not require
// a CCB. Since a Terget Reset doesn't imply any data transfer, we skip
// some proceessing here below, but we use a CCB anyway, so as to allow
// the interrupt service routine to complete the Target Reset request
// like any others, without special case handling.
//
if (Srb->Function != SRB_FUNCTION_ABORT_COMMAND) {
//
// Set transfer direction bit.
//
if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
//
// Adapter to system transfer
//
ccb->TaskId |= CCB_DATA_XFER_OUT;
} else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
//
// System to adapter transfer
//
ccb->TaskId |= CCB_DATA_XFER_IN;
} else ccb->TaskId |= CCB_DATA_XFER_NONE;
//
// Set the LinkedCommandAddress to NULL. It is not used by the ESC-1,
// but, for safety, it's better to set it.
//
ccb->LinkedCommandAddress = (ULONG) NULL;
//
// Copy the Command Descriptor Block (CDB) into the CCB.
//
ScsiPortMoveMemory(ccb->Cdb, Srb->Cdb, Srb->CdbLength);
DebugPrint((3,"OliEsc1BuildCcb: CDB at %lx, length=%x\n",
ccb->Cdb, Srb->CdbLength));
//
// Set the CDB length and the data transfer length in the CCB
//
ccb->CdbLength = (UCHAR) Srb->CdbLength;
ccb->DataLength = Srb->DataTransferLength;
//
// Build a actter/gather list in the CCB if necessary
//
if (Srb->DataTransferLength > 0) {
physicalAddress = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(DeviceExtension,
Srb,
Srb->DataBuffer,
&length));
//
// length contains the length of contiguous memory starting
// at Srb->DataBuffer
//
if (length >= Srb->DataTransferLength) {
//
// The Srb->DataBuffer is contiguous: no need of
// scatter/gather descriptors
//
ccb->DataAddress = physicalAddress;
ccb->AdditionalRequestBlockLength = 0;
} else {
//
// The Srb->DataBuffer is not contiguous: we need
// scatter/gather descriptors
//
OliEsc1BuildSgl(DeviceExtension, Srb);
}
} else {
//
// No data transfer is requested
//
ccb->AdditionalRequestBlockLength = 0;
}
}
return;
} // end OliEsc1BuildCcb()
VOID
OliEsc1BuildSgl(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine builds a scatter/gather descriptor list for the Command
Control Block.
Arguments:
DeviceExtension Pointer to the device extension for this driver.
Srb Pointer to the Scsi Request Block to service
Return Value:
None
--*/
{
ULONG bytesLeft; // # of bytes left to be described
// in an SGL
PCCB ccb; // CCB address
PVOID dataPointer; // Pointer to the data buffer to send
ULONG descriptorCount; // # of scatter/gather descriptors
// built
ULONG length; // Length of contiguous memory in the
// data buffer, starting at a given
// offset
ULONG physicalAddress; // Physical address of the data buffer
ULONG physicalSgl; // Physical SGL address
PSGL sgl; // Virtual SGL address
DebugPrint((3,"OliEsc1BuildSgl: Enter routine\n"));
//
// Initialize some variables
//
dataPointer = Srb->DataBuffer;
bytesLeft = Srb->DataTransferLength;
ccb = Srb->SrbExtension;
sgl = &ccb->Sgl;
descriptorCount = 0;
//
// Get physical SGL address.
//
physicalSgl = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(DeviceExtension, NULL,
sgl, &length));
//
// Assume physical memory contiguous for sizeof(SGL) bytes.
//
ASSERT(length >= sizeof(SGL));
//
// Create SGL segment descriptors.
//
do {
DebugPrint((3,
"OliEsc1BuildSgl: Data buffer %lx\n", dataPointer));
//
// Get physical address and length of contiguous
// physical buffer.
//
physicalAddress = ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(DeviceExtension,
Srb,
dataPointer,
&length));
DebugPrint((3, "OliEsc1BuildSgl: Physical address %lx\n",
physicalAddress));
DebugPrint((3, "OliEsc1BuildSgl: Data length %lx\n", length));
DebugPrint((3, "OliEsc1BuildSgl: 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;
}
sgl->Descriptor[descriptorCount].Address = physicalAddress;
sgl->Descriptor[descriptorCount].Length = length;
//
// Adjust counts.
//
dataPointer = (PUCHAR)dataPointer + length;
bytesLeft -= length;
descriptorCount++;
} while (bytesLeft);
//
// Write SGL length to CCB.
//
ccb->AdditionalRequestBlockLength = descriptorCount * sizeof(SG_DESCRIPTOR);
DebugPrint((3,"OliEsc1BuildSgl: SGL length is %d\n", descriptorCount));
//
// Write SGL address to CCB.
//
ccb->DataAddress = physicalSgl;
DebugPrint((3,"OliEsc1BuildSgl: SGL address is %lx\n", sgl));
DebugPrint((3,"OliEsc1BuildSgl: CCB address is %lx\n", ccb));
return;
} // end OliEsc1BuildSgl()
ULONG
DriverEntry (
IN PVOID DriverObject,
IN PVOID Argument2
)
/*++
Routine Description:
Installable driver initialization entry point for the OS.
Arguments:
Driver Object Pointer to the driver object for this driver
Return Value:
Status from OliEsc1DriverEntry()
--*/
{
ULONG Esc1Status, Esc2Status;
//
// Search for any ESC-1
//
Esc1Status = OliEsc1DriverEntry(DriverObject, Argument2);
//
// Search for any ESC-2 and EFP-2
//
Esc2Status = OliEsc2DriverEntry(DriverObject, Argument2);
//
// The driver should return the lowest status
//
return MIN( Esc1Status, Esc2Status );
} // end DriverEntry()
BOOLEAN
OliEsc1GetIrql(
IN PEISA_CONTROLLER eisaController,
PUCHAR Irql
)
/*++
Routine Description:
It reads the ESC-1's IRQL. It is assumed to be called at system
initialization time only, since it uses polling.
Arguments:
Return Value:
TRUE Success
FALSE Failure
--*/
{
BOOLEAN Success; // Return value
UCHAR IntMask; // Current System Doorbell interrupt mask value
ULONG i; // Auxiliary variable
//
// Get the current System Doorbell Interrupt Mask
//
IntMask = ScsiPortReadPortUchar(&eisaController->SystemDoorBellMask);
//
// Disable ESC-1 interrupts
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_DISABLE);
//
// Get the ESC-1 Irql
//
Success = OliEsc1SendCommandQuick(eisaController,
CCB_DATA_XFER_NONE,
GET_CONFIGURATION,
IRQL_REGISTER,
(ULONG) NULL);
if (Success) {
i = 0;
//
// Poll interrupt pending bit
//
while (!(ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
INTERRUPT_PENDING) && i < INTERRUPT_POLLING_TIME) {
ScsiPortStallExecution(1);
i++;
}
DebugPrint((4, "OliEsc1GetIrql: got INT after %ld us\n", i));
if (i < INTERRUPT_POLLING_TIME) {
//
// Sensed the INTERRUPT_PENDING bit. Reset the interrupt pending.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
INTERRUPT_PENDING);
//
// Check to see whether the command completed correctly
//
if ((UCHAR)
((ScsiPortReadPortUshort(&eisaController->Status) >> 8)) ==
NO_ERROR) {
Success = TRUE;
//
// The IRQL value is available in the OutAddress mailbox
// register, in the low byte
//
switch((UCHAR) ScsiPortReadPortUlong(
&eisaController->OutAddress)) {
case 0: *Irql = (KIRQL) 11;
break;
case 1: *Irql = (KIRQL) 10;
break;
case 2: *Irql = (KIRQL) 5;
break;
case 3: *Irql = (KIRQL) 15;
break;
default: Success = FALSE;
}
//
// Unlock the Result Semaphore, so that the ESC-1 can load
// new values in the output mailboxes.
//
ScsiPortWritePortUchar(&eisaController->ResultSemaphore,
SEM_UNLOCK);
} else Success = FALSE;
} else Success = FALSE;
}
//
// Restore the original interrupt mask
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask, IntMask);
return Success;
} // end OliEsc1GetIrql
BOOLEAN
OliEsc1CheckAtMode(
IN PEISA_CONTROLLER eisaController,
PBOOLEAN AtModeEnabled
)
/*++
Routine Description:
This routine checks if this board has the AT compatible mode enabled.
Arguments:
eisaController I/O address of ESC-1 controller.
AtModeEnabled pointer to a variable. This variable is
set to FALSE if the AT mode is not enable,
and to TRUE otherwise.
Return Value:
TRUE Success
FALSE Failure
--*/
{
BOOLEAN Success; // Return value
UCHAR IntMask; // Current System Doorbell interrupt mask value
ULONG i; // Auxiliary variable
//
// Get the current System Doorbell Interrupt Mask
//
IntMask = ScsiPortReadPortUchar(&eisaController->SystemDoorBellMask);
//
// Disable ESC-1 interrupts
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_DISABLE);
//
// Get the ESC-1 Hard Disk Configuration value
//
Success = OliEsc1SendCommandQuick(eisaController,
CCB_DATA_XFER_NONE,
GET_CONFIGURATION,
ATCFG_REGISTER,
(ULONG) NULL);
if (Success) {
i = 0;
//
// Poll interrupt pending bit
//
while (!(ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
INTERRUPT_PENDING) && i < INTERRUPT_POLLING_TIME) {
ScsiPortStallExecution(1);
i++;
}
DebugPrint((4, "OliEsc1CheckAtMode: got INT after %ld us\n", i));
if (i < INTERRUPT_POLLING_TIME) {
//
// Sensed the INTERRUPT_PENDING bit. Reset the interrupt pending.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
INTERRUPT_PENDING);
//
// Check to see whether the command completed correctly
//
if ((UCHAR)
((ScsiPortReadPortUshort(&eisaController->Status) >> 8)) ==
NO_ERROR) {
Success = TRUE;
//
// The AT info is available in the OutAddress mailbox
// register, in the low byte
//
if (ScsiPortReadPortUlong(&eisaController->OutAddress)& 0x01) {
*AtModeEnabled = TRUE;
}
else {
*AtModeEnabled = FALSE;
}
//
// Unlock the Result Semaphore, so that the ESC-1 can load
// new values in the output mailboxes.
//
ScsiPortWritePortUchar(&eisaController->ResultSemaphore,
SEM_UNLOCK);
} else Success = FALSE;
} else Success = FALSE;
}
//
// Restore the original interrupt mask
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask, IntMask);
return Success;
} // end OliEsc1CheckAtMode
ULONG
OliEsc1DriverEntry(
IN PVOID DriverObject,
IN PVOID Argument2
)
/*++
Routine Description:
This routine is called from DriverEntry(). It initializes some fields
in a data structure of type HW_INITIALIZATION_DATA for use by the port
driver.
Arguments:
DriverObject Address of the context to pass to ScsiPortInitialize
Return Value:
Status from ScsiPortInitialize()
--*/
{
HW_INITIALIZATION_DATA hwInitializationData;
// Structure used to tell the upper
// layer the entry points of this
// driver
ULONG i; // Auxiliary variable
ULONG adapterCount = 0; // Indicates the slot which have been
// check for adapters.
DebugPrint((1,"\n\nSCSI ESC-1 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 = OliEsc1Initialize;
hwInitializationData.HwFindAdapter = OliEsc1Configuration;
hwInitializationData.HwStartIo = OliEsc1StartIo;
hwInitializationData.HwInterrupt = OliEsc1Interrupt;
hwInitializationData.HwResetBus = OliEsc1ResetBus;
//
// Set number of access ranges and the bus type.
//
hwInitializationData.NumberOfAccessRanges = 1;
hwInitializationData.AdapterInterfaceType = Eisa;
//
// The ESC-1 supports tagged queuing
//
hwInitializationData.TaggedQueuing = TRUE;
//
// 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(LU_EXTENSION);
//
// Ask for SRB extensions for CCBs.
//
hwInitializationData.SrbExtensionSize = sizeof(CCB);
DebugPrint((1,
"OliEsc1DriverEntry: hwInitializationData address %lx\n",
&hwInitializationData));
return(ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &adapterCount));
} // end OliEsc1DriverEntry()
ULONG
OliEsc1Configuration(
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.
The EISA bus is scanned in search for an ESC-1 or an ESC-2 board.
ES: The search for an ESC-2 has been removed.
Arguments:
HwDeviceExtension Device extension for this driver
Context ESC-1 registers' address space
ConfigInfo Configuration information structure describing
the board configuration
Return Value:
TRUE if adapter present in system
--*/
{
PHW_DEVICE_EXTENSION deviceExtension; // Pointer to the device extension
// for this driver
PEISA_CONTROLLER eisaController; // Base address of the ESC-1
// registers' address space
ULONG eisaSlotNumber; // Auxiliary variable
// in case of initialization failure
BOOLEAN Success = FALSE; // Indicates an adapter was found.
PULONG adapterCount = Context; // Indicates which slots have been
// checked.
deviceExtension = HwDeviceExtension;
//
// Check to see if an adapter is present in the system
//
for (eisaSlotNumber = *adapterCount + 1;
eisaSlotNumber < MAX_EISA_SLOTS_STD; eisaSlotNumber++) {
//
// Update the adpater count to indicate this slot has been checked.
//
(*adapterCount)++;
//
// Get the system physical address for this card.
// The card uses I/O space.
//
eisaController = ScsiPortGetDeviceBase(
deviceExtension,
ConfigInfo->AdapterInterfaceType,
ConfigInfo->SystemIoBusNumber,
ScsiPortConvertUlongToPhysicalAddress(0x1000 * eisaSlotNumber),
0x1000,
(BOOLEAN) TRUE);
eisaController =
(PEISA_CONTROLLER)((PUCHAR)eisaController + EISA_ADDRESS_BASE);
//
// Read the EISA board ID and check to see whether it identifies
// an ESC-1
//
if ((ScsiPortReadPortUchar(&eisaController->BoardId[0]) == 0x3D) &&
(ScsiPortReadPortUchar(&eisaController->BoardId[1]) == 0x89) &&
(ScsiPortReadPortUchar(&eisaController->BoardId[2]) == 0x10) &&
(ScsiPortReadPortUchar(&eisaController->BoardId[3]) == 0x21)) {
DebugPrint((1,"ESC-1 Adapter found at EISA slot %d\n",
eisaSlotNumber));
//
// Immediately disable system interrupts (bellinte).
// They will remain disabled (polling mode only) during
// the EFP initialization sequence.
//
ScsiPortWritePortUchar(&eisaController->SystemIntEnable,
INTERRUPTS_DISABLE);
//
// The adpater is not reset and assumed to be functioning.
// Resetting the adapter is particularly time consuming (2 secs.
// for the ESC-1, 3.1 secs. for the ESC-2). The BIOS on x86
// computers or the EISA Support F/W on the M700 computers have
// already reset the adapter and checked its status.
//
//
// Reset System Doorbell interrupts
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBell, 0x0FF);
//
// Enable the ESC-1 High Performance interrupt.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_ENABLE);
Success = TRUE;
break;
}
//
// In the current slot there isn't an ESC-1/ESC-2 card. Try next
// slot.
//
ScsiPortFreeDeviceBase(deviceExtension,
(PUCHAR)eisaController - EISA_ADDRESS_BASE);
} // end for (eisaSlotNumber ...
if (!Success) {
//
// No adapter was found. Clear the call again flag, reset the adapter
// count for the next bus and return.
//
*Again = FALSE;
*adapterCount = 0;
return(SP_RETURN_NOT_FOUND);
}
//
// There are more slots to search so call again.
//
*Again = TRUE;
//
// Store base address of EISA registers in device extension.
//
deviceExtension->EisaController = eisaController;
//
// Reset the "ResetInProgress" variable.
//
deviceExtension->ResetInProgress = 0;
//
// Indicate the SCSI ID of the ESC-x, that's always 7
//
ConfigInfo->InitiatorBusId[0] = ADAPTER_ID;
//
// Indicate the maximum transfer length in bytes.
//
ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_SIZE;
//
// Indicate the maximum number of physical segments
//
ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SGL_DESCRIPTORS;
ConfigInfo->ScatterGather = TRUE;
ConfigInfo->Master = TRUE;
ConfigInfo->NumberOfBuses = 1;
//
// Get the "AtdiskPrimaryClaimed" info
//
if (!OliEsc1CheckAtMode(deviceExtension->EisaController,
&ConfigInfo->AtdiskPrimaryClaimed)) {
DebugPrint((1,
"OliEsc1Configuration: Adapter initialization error.\n"));
ScsiPortLogError(
deviceExtension,
NULL,
0,
0,
0,
SP_INTERNAL_ADAPTER_ERROR,
ESCX_INIT_FAILED
);
return SP_RETURN_ERROR;
}
//
// Indicate which interrupt mode the adapter uses
//
if (ScsiPortReadPortUchar(&eisaController->GlobalConfiguration) &
EDGE_SENSITIVE) {
ConfigInfo->InterruptMode = Latched;
} else {
ConfigInfo->InterruptMode = LevelSensitive;
}
//
// Fill in the access array information.
//
(*ConfigInfo->AccessRanges)[0].RangeStart = ScsiPortConvertUlongToPhysicalAddress(0x1000 * eisaSlotNumber + EISA_ADDRESS_BASE);
(*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(EISA_CONTROLLER);
(*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
//
// Get the ESC-x IRQL.
//
if (OliEsc1GetIrql(deviceExtension->EisaController,
(UCHAR *) &ConfigInfo->BusInterruptLevel)) {
return SP_RETURN_FOUND;
} else {
DebugPrint((1,
"OliEsc1Configuration: Adapter initialization error.\n"));
ScsiPortLogError(
deviceExtension,
NULL,
0,
0,
0,
SP_INTERNAL_ADAPTER_ERROR,
ESCX_INIT_FAILED
);
return SP_RETURN_ERROR;
}
} // end OliEsc1Configuration()
BOOLEAN
OliEsc1Initialize(
IN PVOID HwDeviceExtension
)
/*++
Routine Description:
It does nothing (for now).
Arguments:
HwDeviceExtension Device extension for this driver
Return Value:
Always TRUE.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension; // Pointer to the device extension
// for this driver
PEISA_CONTROLLER eisaController; // Base address of the ESC-1
deviceExtension = HwDeviceExtension;
eisaController = deviceExtension->EisaController;
//
// Make sure that the ESC-1 High Performance interrupt is enabled.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_ENABLE);
//
// Enable system interrupts (bellinte).
//
ScsiPortWritePortUchar(&eisaController->SystemIntEnable,
SYSTEM_INTS_ENABLE);
return TRUE;
} // end OliEsc1Initialize()
BOOLEAN
OliEsc1Interrupt(
IN PVOID HwDeviceExtension
)
/*++
Routine Description:
This is the interrupt service routine for the ESC-1 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 an outuput mailbox is full,
the CCB is retrieved to complete the request.
NOTE: if the semaphore 1 is used, it must be released after resetting
the associated interrupt !
Arguments:
HwDeviceExtension Device extention for this driver
Return Value:
TRUE if the interrupt was expected
--*/
{
PCCB ccb;
PHW_DEVICE_EXTENSION deviceExtension;
PEISA_CONTROLLER eisaController;
USHORT interruptStatus;
ULONG physicalCcb;
PLU_EXTENSION LuExtension;
UCHAR Lun;
PSCSI_REQUEST_BLOCK srb;
deviceExtension = HwDeviceExtension;
eisaController = deviceExtension->EisaController;
//
// Disable interrupts to diminish the chance for other CPUs to "spin-lock"
// for the same interrupt vector (multi-processor environment with dynamic
// interrupt dispatching).
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_DISABLE);
//
// Check interrupt pending.
//
if (!(ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
INTERRUPT_PENDING)) {
//
// No interrupt is pending.
// Enable interrupts.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_ENABLE);
DebugPrint((2, "OliEsc1Interrupt: Unrecognized interrupt\n"));
return FALSE;
}
//
// Read interrupt status. The high byte is the adapter status, whereas
// the low byte is the device status. If the device status is not zero,
// an error will be returned, regardless of the adapter status.
//
interruptStatus = ScsiPortReadPortUshort(&eisaController->Status);
//
// Get physical address of CCB.
//
physicalCcb = ScsiPortReadPortUlong(&eisaController->OutAddress);
//
// Acknowledge interrupt.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBell, INTERRUPT_RESET);
//
// Unlock the Result Semaphore, so that the ESC-1 can load
// new values in the output mailboxes.
//
ScsiPortWritePortUchar(&eisaController->ResultSemaphore, SEM_UNLOCK);
//
// Enable interrupts
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_ENABLE);
//
// Get virtual CCB address.
//
ccb = ScsiPortGetVirtualAddress(deviceExtension, ScsiPortConvertUlongToPhysicalAddress(physicalCcb));
//
// 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(
deviceExtension,
NULL,
0,
ADAPTER_ID,
0,
SP_INTERNAL_ADAPTER_ERROR,
ESC1_BAD_PHYSICAL_ADDRESS | (ULONG) interruptStatus
);
return TRUE;
}
//
// Get SRB from CCB.
//
srb = ccb->SrbAddress;
DebugPrint((5,
"OliEsc1Interrupt: ccb = %lx, srb = %lx, Int Status = %x\n",
ccb, srb, interruptStatus));
//
// Check the adapter status.
//
switch (interruptStatus >> 8) {
case NO_ERROR:
//
// Check the device status.
//
if ((interruptStatus & 0xff) != NO_ERROR) {
//
// The device status is not ok: return an error. This allows
// the class driver to detect a media change on a removable disk
// unit.
//
DebugPrint((1, "OliEsc1Interrupt: Status = %x\n",
interruptStatus));
srb->SrbStatus = SRB_STATUS_ERROR;
srb->ScsiStatus = (UCHAR) (interruptStatus & 0xff);
} else {
srb->SrbStatus = SRB_STATUS_SUCCESS;
srb->ScsiStatus = SCSISTAT_GOOD;
}
break;
case SELECTION_TIMEOUT_EXPIRED:
srb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT;
break;
case DATA_OVERRUN_UNDERRUN:
srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
//
// On the ESC-1 it is not possible to distinguish the overrun error
// from the underrun error.
// We don't log the error because the underrun error can be very
// common on some devices (example: scanner).
//
// ScsiPortLogError(
// deviceExtension,
// NULL,
// 0,
// ADAPTER_ID,
// 0,
// SP_INTERNAL_ADAPTER_ERROR,
// DATA_OVERRUN_UNDERRUN
// );
//
break;
case UNEXPECTED_BUS_FREE:
srb->SrbStatus = SRB_STATUS_UNEXPECTED_BUS_FREE;
break;
case SCSI_PHASE_SEQUENCE_FAILURE:
srb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
break;
case QUEUE_FULL:
srb->SrbStatus = SRB_STATUS_BUSY;
break;
case PARITY_ERROR:
//
// This is a severe error. The controller is now shut down.
//
srb->SrbStatus = SRB_STATUS_PARITY_ERROR;
ScsiPortLogError(
deviceExtension,
NULL,
0,
ADAPTER_ID,
0,
SP_BUS_PARITY_ERROR,
0
);
break;
case PROTOCOL_ERROR:
//
// Return bus reset error, because the bus has been reset.
//
srb->SrbStatus = SRB_STATUS_BUS_RESET;
ScsiPortLogError(
deviceExtension,
NULL,
0,
ADAPTER_ID,
0,
SP_PROTOCOL_ERROR,
0
);
break;
case BUS_RESET_BY_TARGET:
//
// No error logging.
//
// Return bus reset error, because the bus has been reset.
//
srb->SrbStatus = SRB_STATUS_BUS_RESET;
break;
case UNEXPECTED_PHASE_CHANGE:
//
// This is a severe error. The controller is now shut down.
//
case PARITY_ERROR_DURING_DATA_PHASE:
case AUTO_REQUEST_SENSE_FAILURE:
case NO_REQUEST_SENSE_ISSUED:
case INVALID_CONFIGURATION_COMMAND:
case INVALID_CONFIGURATION_REGISTER:
case INVALID_COMMAND:
srb->SrbStatus = SRB_STATUS_ERROR;
srb->ScsiStatus = SCSISTAT_GOOD;
ScsiPortLogError(
deviceExtension,
NULL,
0,
ADAPTER_ID,
0,
SP_INTERNAL_ADAPTER_ERROR,
interruptStatus
);
break;
default:
DebugPrint((1,
"OliEsc1Interrupt: Unrecognized interrupt status %x\n",
interruptStatus));
srb->SrbStatus = SRB_STATUS_ERROR;
srb->ScsiStatus = SCSISTAT_GOOD;
ScsiPortLogError(
deviceExtension,
NULL,
0,
ADAPTER_ID,
0,
SP_INTERNAL_ADAPTER_ERROR,
interruptStatus
);
} // end switch
if (srb->Function == SRB_FUNCTION_ABORT_COMMAND ||
srb->Function == SRB_FUNCTION_RESET_DEVICE) {
if (srb->Function == SRB_FUNCTION_RESET_DEVICE) {
//
// Call notification routine for the SRB.
//
ScsiPortNotification(RequestComplete, (PVOID)deviceExtension, srb);
}
//
// The interrupt refers to a Target Reset command issued
// instead of the unsupported Abort command or to a real one.
// All the pending requests for the target have to be completed
// with status SRB_STATUS_BUS_RESET (any better idea?).
//
ScsiPortCompleteRequest(deviceExtension,
(UCHAR) 0,
srb->TargetId,
(UCHAR) ALL_LUNS,
(UCHAR) SRB_STATUS_BUS_RESET);
//
// Reset all the pending request counters for the target
//
for (Lun = 0; Lun < 8; Lun++) {
LuExtension = ScsiPortGetLogicalUnit(deviceExtension,
0,
srb->TargetId,
Lun);
if (LuExtension != NULL) {
LuExtension->NumberOfPendingRequests = 0;
}
}
} else {
//
// Decrement the pending requests counter for this (targetId, LUN)
// pair
//
LuExtension = ScsiPortGetLogicalUnit(deviceExtension,
0,
srb->TargetId,
srb->Lun);
ASSERT(LuExtension);
LuExtension->NumberOfPendingRequests--;
ASSERT (LuExtension->NumberOfPendingRequests >= 0);
//
// Call notification routine for the SRB.
//
ScsiPortNotification(RequestComplete, (PVOID)deviceExtension, srb);
}
return TRUE;
} // end OliEsc1Interrupt()
BOOLEAN
OliEsc1ResetBus(
IN PVOID HwDeviceExtension,
IN ULONG PathId
)
/*++
Routine Description:
Reset ESC-1 SCSI adapter and SCSI bus.
Arguments:
HwDeviceExtension Device extension for this driver
PathId SCSI Bus path ID (always 0 for the ESC-1)
Return Value:
Nothing.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension;
PEISA_CONTROLLER eisaController;
PLU_EXTENSION LuExtension;
UCHAR Lun;
UCHAR TargetId;
UNREFERENCED_PARAMETER(PathId);
DebugPrint((2,"OliEsc1ResetBus: Reset ESC-1 and SCSI bus\n"));
//
// Get the ESC-1 registers' base address
//
deviceExtension = HwDeviceExtension;
eisaController = deviceExtension->EisaController;
//
// If the reset is already in progress, return TRUE.
// This should never happen!
//
if (deviceExtension->ResetInProgress) {
DebugPrint((2,"OliEsc1ResetBus: The reset is already in progess.\n"));
return TRUE;
}
//
// Issue a board reset
//
OliEsc1ResetAdapter(deviceExtension);
//
// Complete all outstanding requests with SRB_STATUS_BUS_RESET
//
ScsiPortCompleteRequest(deviceExtension,
(UCHAR) 0,
(UCHAR) ALL_TARGET_IDS,
(UCHAR) ALL_LUNS,
(UCHAR) SRB_STATUS_BUS_RESET);
//
// Reset to zero all the pending request counters
//
for (TargetId = 0; TargetId < 8; TargetId++) {
for (Lun = 0; Lun < 8; Lun++) {
LuExtension = ScsiPortGetLogicalUnit(deviceExtension,
0,
TargetId,
Lun);
if (LuExtension != NULL) {
LuExtension->NumberOfPendingRequests = 0;
}
}
}
//
// Send a "reset detected" notification.
//
ScsiPortNotification(ResetDetected, deviceExtension, 0);
return TRUE;
} // end Oliesc1ResetBus
BOOLEAN
OliEsc1StartIo(
IN PVOID HwDeviceExtension,
IN PSCSI_REQUEST_BLOCK Srb
)
/*++
Routine Description:
This routine is called from the SCSI port driver synchronized
with the kernel to send a CCB or issue an immediate command.
Arguments:
HwDeviceExtension Device extension for this driver
Srb Pointer to the Scsi Request Block to service
Return Value:
Nothing
--*/
{
PHW_DEVICE_EXTENSION deviceExtension;
PEISA_CONTROLLER eisaController;
PLU_EXTENSION luExtension;
PCCB ccb;
UCHAR opCode;
BOOLEAN Send;
DebugPrint((3,"OliEsc1StartIo: Enter routine\n"));
//
// Get the base address of the ESC-1 registers' address space
//
deviceExtension = HwDeviceExtension;
eisaController = deviceExtension->EisaController;
//
// If the "ResetInProgress" flag is TRUE, no SRBs are allowed to go
// through because the SCSI controller needs more time to complete its
// initialization.
//
if (deviceExtension->ResetInProgress) {
DebugPrint((2,"OliEsc1StartIo: The reset is not completed yet.\n"));
//
// Complete the current request.
//
Srb->SrbStatus = SRB_STATUS_BUS_RESET;
ScsiPortNotification(RequestComplete, deviceExtension, Srb);
//
// Notify that a reset was detected on the SCSI bus.
//
ScsiPortNotification(ResetDetected, deviceExtension, 0);
//
// The controller is now ready for the next request.
//
ScsiPortNotification(NextRequest, deviceExtension, NULL);
return(TRUE);
}
//
// Assume we are going to send a command to the ESC-1
//
Send = TRUE;
//
// Get CCB from SRB
//
ccb = Srb->SrbExtension;
//
// Save SRB back pointer in CCB
//
ccb->SrbAddress = Srb;
//
// Get the pointer to the extension data area associated with the
// pair (Srb->TargetId, Srb->Lun)
//
luExtension = ScsiPortGetLogicalUnit(deviceExtension,
0,
Srb->TargetId,
Srb->Lun);
ASSERT(luExtension);
switch (Srb->Function) {
case SRB_FUNCTION_EXECUTE_SCSI:
if (Srb->TargetId == ADAPTER_ID) {
//
// No SCSI massages directed to the adatpter are let
// go through, because the adapter doesn't support any
//
DebugPrint((1,
"OliEsc1StartIo: SCSI command to adapter rejected\n"));
Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
ScsiPortNotification(RequestComplete, deviceExtension, Srb);
Send = FALSE;
} else {
//
// Increment the number of pending requests for this (targetId,
// LUN), so that we can process an abort request in case this
// command gets timed out
//
luExtension->NumberOfPendingRequests++;
//
// Build CCB.
//
OliEsc1BuildCcb(deviceExtension, Srb);
opCode = START_CCB;
}
break;
case SRB_FUNCTION_ABORT_COMMAND:
DebugPrint((1,
"OliEsc1StartIo: Abort Cmd Target ID %d\n", Srb->TargetId));
//
// The Abort command is not supported by the ESC-x. Here we do what
// we can.
//
if (luExtension->NumberOfPendingRequests) {
//
// A command sent to a device has to be aborted.
// All we can do is to reset the target.
//
OliEsc1BuildCcb(deviceExtension, Srb);
opCode = RESET_TARGET;
} else {
//
// The request to abort has already completed.
//
Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
ScsiPortNotification(RequestComplete, deviceExtension, Srb);
Send = FALSE;
}
break;
case SRB_FUNCTION_RESET_BUS:
//
// Reset ESC-1 and SCSI bus.
//
DebugPrint((1, "OliEsc1StartIo: Reset bus request received\n"));
//
// The following routine will ...
//
// a) reset the bus.
// b) complete all the active requests (including this one).
// c) notify that a reset was detected on the SCSI bus.
//
OliEsc1ResetBus(deviceExtension, (ULONG) NULL);
Send = FALSE;
break;
case SRB_FUNCTION_RESET_DEVICE:
if (Srb->TargetId == ADAPTER_ID) {
//
// No SCSI massages directed to the adatpter are let
// go through, because the adapter doesn't support any
//
DebugPrint((1,
"OliEsc1StartIo: Reset Device sent to the adapter rjected\n"));
Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
ScsiPortNotification(RequestComplete, deviceExtension, Srb);
Send = FALSE;
} else {
//
// Increment the number of pending requests for this (targetId,
// LUN), so that we can process an abort request in case this
// command gets timed out
//
DebugPrint((4,"OliEsc1StartIo: Reset device ID %d\n",
Srb->TargetId));
OliEsc1BuildCcb(deviceExtension, Srb);
opCode = RESET_TARGET;
}
break;
default:
//
// Set error and complete request
//
DebugPrint((1,"OliEsc1StartIo: Invalid Request\n"));
Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
ScsiPortNotification(RequestComplete, deviceExtension, Srb);
Send = FALSE;
} // end switch
if (Send) {
if (!OliEsc1SendCommand(deviceExtension, opCode, ccb)) {
DebugPrint((1,"OliEsc1StartIo: Send command timed out\n"));
//
// Let operating system time out SRB.
//
}
}
//
// Adapter ready for next request.
//
ScsiPortNotification(NextRequest, deviceExtension, NULL);
return TRUE;
} // end OliEsc1StartIo()
VOID
OliEsc1ResetAdapter(
IN PVOID Context
)
/*++
Routine Description:
The routine resets the SCSI controller.
Arguments:
Context Device adapter context pointer.
Return Value:
None.
--*/
{
PHW_DEVICE_EXTENSION deviceExtension;
PEISA_CONTROLLER eisaController;
ULONG Delay;
BOOLEAN Error = FALSE;
deviceExtension = Context;
eisaController = deviceExtension->EisaController;
//
// The routine releases the control of the CPU while waiting for some
// status/interrupt, this is required because the reset/re-initialization
// of the controller can take several seconds.
//
// Reset Controller:
//
// Phase 0: Reset the controller.
// Phase 1: Waiting for the controller to complete its initialization.
// Phase 2: Small delay.
//
switch(deviceExtension->ResetInProgress) {
//
// Phase 0: Reset the controller.
//
case 0:
//////////////////////////////////////////////////////////////////////
//
// Disable interrupts.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_DISABLE);
//////////////////////////////////////////////////////////////////////
//
// Reset controller.
//
DebugPrint((3,
"OliEsc1ResetAdapter: Phase 1 (reset adapter) max time = %ld us.\n",
ESC_RESET_DELAY + ESC_RESET_INTERVAL * ESC_RESET_LOOPS
));
//
// Initialize the output location to a known value.
//
ScsiPortWritePortUchar( (PUCHAR)&eisaController->Status,
(UCHAR)(~DIAGNOSTICS_OK_NO_CONFIG_RECEIVED));
//
// Reset ESC-1 and SCSI bus. Wait to allow the
// board diagnostics to complete
//
ScsiPortWritePortUchar(&eisaController->LocalDoorBell, ADAPTER_RESET);
//
// Request a timer call to complete the reset.
//
deviceExtension->ResetTimerCalls = ESC_RESET_LOOPS + 1;
Delay = ESC_RESET_DELAY;
//
// The "ResetNotification" variable is used to keep track of the
// time during the reset. If the reset is not completed before
// the next ESC1_RESET_NOTIFICATION usec. unit, we call the
// "ScsiPortNotification(ResetDetected...)" routine.
// After the call the ScsiPort stops the delivery of SRBs for a
// little bit (~4 sec.).
//
deviceExtension->ResetNotification = 0;
deviceExtension->ResetInProgress++;
break;
//
// Phase 1: Waiting for the controller to complete its initialization.
//
case 1:
//
// Note that after a reset the LOW byte of the Status register is
// loaded with the diagnostics result code. This should be the
// only case, since usually the high byte reports the adapter status.
//
if ( (UCHAR)ScsiPortReadPortUshort(&eisaController->Status) !=
DIAGNOSTICS_OK_NO_CONFIG_RECEIVED ) {
Delay = ESC_RESET_INTERVAL;
break;
}
//
// Reset completed!
//
DebugPrint((1,
"OliEsc1ResetAdapter: Reset bus succeeded after %ld us\n",
ESC_RESET_DELAY + ESC_RESET_INTERVAL *
(ESC_RESET_LOOPS - deviceExtension->ResetTimerCalls)
));
//
// The following delay is necessary because the adapter, immediately
// after a reset, is insensitive to interrupts through the Local
// Doorbell Register for almost 50ms. This shouldn't be and needs
// to be investigated further. After the adapter has accepted the very
// first command (see OliEsc1GetIrql()), it take 500ms to return the answer to
// the CPU (i.e., to generate an interrupt). The very first command sent
// to the adapter is a "Get Configuration" to read the IRQL register
// at system boot time
//
deviceExtension->ResetTimerCalls = 1;
Delay = POST_RESET_DELAY;
deviceExtension->ResetInProgress++;
break;
//
// Phase 2: Small delay.
//
case 2:
//////////////////////////////////////////////////////////////////////
//
// Remove any interrupt that was pending before issuing the reset.
// The controller doesn't reset these interrupts.
//
if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
INTERRUPT_PENDING) {
DebugPrint((3,
"OliEsc1ResetAdapter: The HP interrupt was pending.\n"));
//
// Reset the interrupt
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
INTERRUPT_PENDING);
}
//////////////////////////////////////////////////////////////////////
//
// Enable interrupts.
//
ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
INTERRUPTS_ENABLE);
//////////////////////////////////////////////////////////////////////
//
// All done !
//
deviceExtension->ResetInProgress = 0;
return;
default:
//
// Invalid reset phase number. This should never happen!
//
DebugPrint((1,
"OliEsc1ResetAdapter: Invalid reset phase number: %x hex.\n",
deviceExtension->ResetInProgress ));
ASSERT(0);
Error = TRUE;
break;
}
//
// If no error, request a timer call.
//
if (!Error) {
//
// Check if time-out.
//
if (deviceExtension->ResetTimerCalls--) {
//
// Request a timer call.
//
ScsiPortNotification(RequestTimerCall,
deviceExtension,
OliEsc1ResetAdapter,
Delay);
//
// The "ResetNotification" variable is used to keep track of the
// time during the reset. If the reset is not completed before
// the next ESC1_RESET_NOTIFICATION usec. unit, we call the
// "ScsiPortNotification(ResetDetected...)" routine.
// After the call the ScsiPort stops the delivery of SRBs for a
// little bit (~4 sec.).
//
if (deviceExtension->ResetNotification >= ESC1_RESET_NOTIFICATION) {
//
// Notify that a reset was detected on the SCSI bus.
//
ScsiPortNotification(ResetDetected, deviceExtension, 0);
//
// Reset the "reset notification timer".
//
deviceExtension->ResetNotification = 0;
}
//
// Update the "reset notification timer".
//
deviceExtension->ResetNotification += Delay;
}
else {
//
// Time-out !
//
DebugPrint((1,
"OliEsc1ResetAdapter: Time-out! Reset phase number: %x hex.\n",
deviceExtension->ResetInProgress ));
Error = TRUE;
}
}
//
// If error, log it.
//
if (Error) {
//
// Log an error.
//
ScsiPortLogError(
deviceExtension,
NULL,
0,
ADAPTER_ID,
0,
SP_INTERNAL_ADAPTER_ERROR,
ESCX_RESET_FAILED
);
//
// We clear the "ResetInProgress" variable to force another SCSI
// bus reset when the driver receives the first SRB request.
// Note that the interrupts are left disabled at the controller level.
//
deviceExtension->ResetInProgress = 0;
}
//
// Done for now.
//
return;
} // end OliEsc1ResetAdapter
BOOLEAN
OliEsc1SendCommand(
IN PHW_DEVICE_EXTENSION DeviceExtension,
IN UCHAR OperationCode,
IN PCCB ccb
)
/*++
Routine Description:
Send a Command Control Block to ESC-1.
Arguments:
DeviceExtension Device extension for this driver
OperationCode Command for the ESC-1
ccb Pointer to the CCB
Return Value:
True if command was sent.
False if the Command Semaphore was busy.
--*/
{
PEISA_CONTROLLER EisaController;
ULONG physicalCcb;
ULONG length;
//
// Get the base address of the ESC-1 registers' address space
//
EisaController = DeviceExtension->EisaController;
length = 0;
//
// Get the CCB physical address
//
physicalCcb =
ScsiPortConvertPhysicalAddressToUlong(
ScsiPortGetPhysicalAddress(DeviceExtension, NULL, ccb, &length));
ASSERT (length >= sizeof(CCB));
return(OliEsc1SendCommandQuick(EisaController, ccb->TaskId, OperationCode,
(USHORT) (CCB_FIXED_LENGTH + ccb->CdbLength),
physicalCcb));
}
BOOLEAN
OliEsc1SendCommandQuick(
IN PEISA_CONTROLLER EisaController,
IN UCHAR TaskId,
IN UCHAR OperationCode,
IN USHORT CommandLength,
IN ULONG Address
)
/*++
Routine Description:
Send CCB or immediate command to ESC-1.
Arguments:
EisaController Base address of the ESC-1 registers' address space
TaskId Task ID for the ESC-1
OperationCode Command code for the ESC-1
CommandLength Total CCB length
Address Physical address of the CCB
Return Value:
True if the command was sent.
False if the Command Semaphore was busy.
--*/
{
ULONG i;
BOOLEAN ReturnCode = FALSE;
//
// Try to send the command for up to 100 microsends
//
for (i = 0; i < 100; i++) {
ScsiPortWritePortUchar(&EisaController->CommandSemaphore, SEM_LOCK);
if ((ScsiPortReadPortUchar(&EisaController->CommandSemaphore) &
SEM_TAKEN_MASK) == SEM_TAKEN) {
//
// We can send a command to the ESC-1.
//
ScsiPortWritePortUchar(&EisaController->InTaskId, TaskId);
ScsiPortWritePortUchar(&EisaController->Command, OperationCode);
ScsiPortWritePortUshort(&EisaController->CommandLength,
CommandLength);
ScsiPortWritePortUlong(&EisaController->InAddress, Address);
//
// Send an attention interrupt to the adapter.
//
ScsiPortWritePortUchar(&EisaController->LocalDoorBell,
INTERRUPT_PENDING);
ReturnCode = TRUE;
break;
}
//
// Stall execution for 1 microsecond.
//
ScsiPortStallExecution(1);
}
return ReturnCode;
} // end OliEsc1SendCommand()