summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/oliscsi
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/miniport/oliscsi')
-rw-r--r--private/ntos/miniport/oliscsi/makefile6
-rw-r--r--private/ntos/miniport/oliscsi/oliesc1.c2364
-rw-r--r--private/ntos/miniport/oliscsi/oliesc1.h363
-rw-r--r--private/ntos/miniport/oliscsi/oliesc2.c6108
-rw-r--r--private/ntos/miniport/oliscsi/oliesc2.h1400
-rw-r--r--private/ntos/miniport/oliscsi/oliscsi.rc12
-rw-r--r--private/ntos/miniport/oliscsi/sources35
7 files changed, 10288 insertions, 0 deletions
diff --git a/private/ntos/miniport/oliscsi/makefile b/private/ntos/miniport/oliscsi/makefile
new file mode 100644
index 000000000..b4338519e
--- /dev/null
+++ b/private/ntos/miniport/oliscsi/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT WINDOWS
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/ntos/miniport/oliscsi/oliesc1.c b/private/ntos/miniport/oliscsi/oliesc1.c
new file mode 100644
index 000000000..a3ae32d8f
--- /dev/null
+++ b/private/ntos/miniport/oliscsi/oliesc1.c
@@ -0,0 +1,2364 @@
+/*++
+
+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()
diff --git a/private/ntos/miniport/oliscsi/oliesc1.h b/private/ntos/miniport/oliscsi/oliesc1.h
new file mode 100644
index 000000000..99ffb5a55
--- /dev/null
+++ b/private/ntos/miniport/oliscsi/oliesc1.h
@@ -0,0 +1,363 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ oliesc1.h
+
+Abstract:
+
+ This module contains the structures, specific to the Olivetti ESC-1
+ and ESC-2 host bus adapter, used by the SCSI port driver. Data
+ structures that are part of standard ANSI SCSI will be defined
+ in a header file that will be available to all SCSI device drivers.
+
+Author:
+
+ Bruno Sartirana (o-obruno) 13-Dec-1991
+
+
+Revision History:
+
+ Bruno Sartirana (o-obruno) 8-Nov-1992
+ Added error codes of the ESC-2 adapter.
+ Increased the board reset timeout to 6 secs.
+
+--*/
+
+#include <scsi.h>
+
+//
+// Minimum define
+//
+
+#define MIN(x,y) ((x) > (y) ? (y) : (x))
+
+//
+// Maximun number of EISA slots in the system
+//
+
+#define MAX_EISA_SLOTS_STD 16 // # of EISA slots possible (per EISA std)
+#define MAX_EISA_SLOTS 8 // max # that Oli machines support
+
+//
+// Base of the EISA address space
+//
+
+#define EISA_ADDRESS_BASE 0x0C80
+
+//
+// Define constants for request completion in case of bus reset
+//
+
+#define ALL_TARGET_IDS -1
+#define ALL_LUNS -1
+
+//
+// Maximum number of scatter/gather descriptors (the ESC-1 has no limit)
+//
+
+#define MAXIMUM_SGL_DESCRIPTORS 20
+
+//
+// Maximum data transfer length
+//
+
+#define MAXIMUM_TRANSFER_SIZE 0xffffffff
+
+//
+// The ESC-1 SCSI ID is fixed to 7
+//
+
+#define ADAPTER_ID 7
+
+//
+// ESC-1 8-bit command codes (for CCB)
+//
+
+#define START_CCB 0x01
+#define SEND_CONF_INFO 0x02
+#define RESET_TARGET 0x04
+#define SET_CONFIGURATION 0x40
+#define GET_CONFIGURATION 0x41
+#define GET_FW_VERSION 0x42
+#define CHECK_DEVICE_PRESENT 0x43
+
+//
+// ESC-1 configuration registers
+//
+
+#define IRQL_REGISTER 0x2
+#define ATCFG_REGISTER 0X1
+
+//
+// First byte of the Command Control Block:
+//
+// Drive Number / Transfer Direction
+//
+// --------------------------------------
+// | XFER Dir | Target ID | LU Number |
+// --------------------------------------
+// 7 6 5 4 3 2 1 0
+//
+//
+// Subfield constants:
+
+#define CCB_DATA_XFER_ANY_DIR 0 // The adapter decides
+#define CCB_DATA_XFER_IN 0x40 // XFER Dir = 01
+#define CCB_DATA_XFER_OUT 0x80 // XFER Dir = 10
+#define CCB_DATA_XFER_NONE 0xC0 // XFER Dir = 11
+#define CCB_TARGET_ID_SHIFT 3
+
+//
+// Status Register: bit 15-8: adapter status, bits 7-0: target status
+//
+// Adapter status after a power cycle:
+
+#define DIAGNOSTICS_RUNNING 0x53
+#define DIAGNOSTICS_OK_CONFIG_RECEIVED 0x01
+#define DIAGNOSTICS_OK_NO_CONFIG_RECEIVED 0x02
+
+// Adapter status after a CCB command:
+
+#define NO_ERROR 0x00
+#define INVALID_COMMAND 0x01
+#define SELECTION_TIMEOUT_EXPIRED 0x11
+#define DATA_OVERRUN_UNDERRUN 0x12
+#define UNEXPECTED_BUS_FREE 0x13
+#define SCSI_PHASE_SEQUENCE_FAILURE 0x14
+#define COMMAND_ABORTED 0x15
+#define COMMAND_TO_BE_ABORTED_NOT_FOUND 0x16
+#define QUEUE_FULL 0x1F
+#define INVALID_CONFIGURATION_COMMAND 0x20
+#define INVALID_CONFIGURATION_REGISTER 0x21
+#define NO_REQUEST_SENSE_ISSUED 0x3B
+#define AUTO_REQUEST_SENSE_FAILURE 0x80
+#define PARITY_ERROR 0x81
+#define UNEXPECTED_PHASE_CHANGE 0x82
+#define BUS_RESET_BY_TARGET 0x83
+#define PARITY_ERROR_DURING_DATA_PHASE 0x84
+#define PROTOCOL_ERROR 0x85
+
+// Codes to identify logged errors related to H/W malfunction.
+// These codes must be shifted left by 8 bits, to distinguish them from
+// the adapter status after a CCB command.
+//
+// For use with ScsiPortLogError().
+
+#define ESC1_BAD_PHYSICAL_ADDRESS (0x01 << 16)
+#define ESCX_RESET_FAILED (0x06 << 16)
+#define ESCX_INIT_FAILED (0x07 << 16)
+
+
+//
+// Define various timeouts:
+//
+// RESET_REACTION_TIME number of microseconds the adapter takes to
+// change the status register on the reset command.
+//
+// ESC_RESET_DELAY number of microseconds the driver waits for after
+// a ESC-1 reset command. The minimum value for
+// this define is RESET_REACTION_TIME.
+//
+// ESC_RESET_LOOPS maximum number of attempts made by the driver to
+// get the diagnostics result from the status
+// register after a ESC-1 reset command.
+//
+// ESC_RESET_INTERVAL number of microseconds the driver waits for after
+// each read of the status register (on the reset
+// command).
+//
+// INTERRUPT_POLLING_TIME maximum time (in microseconds) spent by driver
+// polling the adapter's interrupt register on a
+// synchronous get configuration command.
+//
+// POST_RESET_DELAY number of microseconds the adpater needs (!) after
+// a successful reset in order to accept the first
+// command (this should not happen and needs to be
+// investigated).
+//
+//
+
+#define RESET_REACTION_TIME 80
+#define ESC_RESET_DELAY 100000 // 100 msec.
+#define ESC_RESET_LOOPS 140 // 14 sec.
+#define ESC_RESET_INTERVAL 100000 // 100 msec.
+#define INTERRUPT_POLLING_TIME 1000000
+#define POST_RESET_DELAY 50000
+
+//
+// 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.). The value of this define is lower than 4 sec. because:
+// a) it's more implementation indipendent.
+// b) we want really really to make sure that the SRBs are held at the ScsiPort
+// level during the reset phase.
+//
+
+#define ESC1_RESET_NOTIFICATION 1000000 // 1 sec. (in usec).
+
+//
+// System/Local Interrupt Mask Register constants
+//
+
+#define INTERRUPTS_DISABLE 0x00
+#define INTERRUPTS_ENABLE 0x80
+
+//
+// SystemIntEnable register bit definition(s) (bellinte)
+//
+
+#define SYSTEM_INTS_ENABLE 0x01
+
+//
+// System/Local Interrupt register
+//
+// bit 3: Adpater reset w/out reconfiguration (Local Interrupt Register only)
+// bit 4: Adapter reset w/ reconfiguration (Local Interrupt Register only)
+// bit 7: Interrupt pending (read), reset (write)
+//
+
+#define ADAPTER_RESET 0x08
+#define INTERRUPT_PENDING 0x80
+#define INTERRUPT_RESET 0x80
+
+
+//
+// Semaphore constants
+//
+
+#define SEM_LOCK 1
+#define SEM_TAKEN 1
+#define SEM_TAKEN_MASK 0x03
+#define SEM_UNLOCK 0
+
+//
+// Global Configuration Register bits
+//
+// Bit 3: 1 = edge-triggered interrupts
+// 0 = level-triggered interrupts
+//
+
+#define EDGE_SENSITIVE 8
+
+//
+// Command Control Block length (includes only the fields meaningful to the
+// ESC-1, SCSI Command Descriptor Block excluded)
+//
+
+#define CCB_FIXED_LENGTH 18
+
+//
+// ESC-1 registers model
+//
+
+typedef struct _EISA_CONTROLLER {
+ UCHAR BoardId[4]; // xC80
+ UCHAR Unused[4];
+ UCHAR GlobalConfiguration; // xC88 - Indicates level- or edge-triggered
+ // interrupts
+ UCHAR SystemIntEnable; // xC89 - system int enab/ctrl reg (bellinte)
+ UCHAR CommandSemaphore; // xC8A - Semaphore port 0 for the Incoming
+ // Mailbox Registers
+ UCHAR ResultSemaphore; // xC8B - Semaphore port 1 for the Outgoing
+ // Mailbox Registers
+ UCHAR LocalDoorBellMask; // xC8C - Interrupt mask register for the
+ // Local Doorbell register
+ UCHAR LocalDoorBell; // xC8D - Local Doorbell register
+ UCHAR SystemDoorBellMask; // xC8E - Interrupt mask register for the
+ // System Doorbel register
+ UCHAR SystemDoorBell; // xC8F - System Doorbell register
+ UCHAR InTaskId; // xC90 - 8-bit Incoming Mailbox Register
+ UCHAR Command; // xC91 - 8-bit Incoming Mailbox Register
+ USHORT CommandLength; // xC92 - 16-bit Incoming Mailbox Register
+ ULONG InAddress; // xC94 - 32-bit Incoming Mailbox Register
+ UCHAR OutTaskId; // xC98 - 8-bit Outgoing Mailbox register
+ UCHAR Reserved; // xC99
+ USHORT Status; // xC9A - 16-bit Outgoing Mailbox register
+ ULONG OutAddress; // xC9C - 32-bit Outgoing Mailbox register
+ // Other use: XC9C: Target ID
+ // XC9D: Device Present/
+ // not Present
+
+ } EISA_CONTROLLER, *PEISA_CONTROLLER;
+
+//
+// Scatter Gather descriptor
+//
+
+typedef struct _SG_DESCRIPTOR {
+ ULONG Length;
+ ULONG Address;
+} SG_DESCRIPTOR, *PSG_DESCRIPTOR;
+
+//
+// Scatter Gather descriptor list (SGL)
+//
+
+typedef struct _SGL {
+ SG_DESCRIPTOR Descriptor[MAXIMUM_SGL_DESCRIPTORS];
+} SGL, *PSGL;
+
+//
+// ESC-1 Command Control Block (byte-aligned)
+//
+
+#pragma pack(1)
+
+typedef struct _CCB {
+
+ //
+ // This first portion is the structure expected by the ESC-1.
+ // Its size is CCB_FIXED_LENGTH and does NOT include the variable
+ // length field Cdb (which can be 6, 10 or 12 bytes long).
+ //
+
+ UCHAR TaskId; // CCB byte 0 (bits 7-6: xfer dir;
+ // bits 5-3: target ID;
+ // bits 2-0: LUN)
+ UCHAR CdbLength; // CCB byte 1 SCSI Command Descriptor
+ // Block length
+ ULONG DataLength; // CCB bytes 2-5 Total data transfer
+ // length
+ ULONG DataAddress; // CCB bytes 6-9 Data segment address
+ ULONG AdditionalRequestBlockLength; // CCB bytes 10-13 Length of the
+ // scatter/gather list
+ ULONG LinkedCommandAddress; // CCB bytes 14-17 Not used
+ UCHAR Cdb[12]; // CCB bytes 18-29 SCSI Command
+ // Descriptor Block
+ //
+ // The following portion is for the miniport driver use only
+ //
+
+ PVOID SrbAddress; // Address of the related SRB
+ SGL Sgl; // Scatter/gather data segment list
+} CCB, *PCCB;
+
+#pragma pack()
+
+//
+// This structure is allocated on a per logical unit basis. It is necessary
+// for the Abort request handling.
+//
+
+typedef struct _LU_EXTENSION {
+ SHORT NumberOfPendingRequests; // Number of SRBs for a logical unit
+ // not completed yet
+} LU_EXTENSION, *PLU_EXTENSION;
+
+//
+// Device extension
+//
+
+typedef struct _HW_DEVICE_EXTENSION {
+
+ PEISA_CONTROLLER EisaController;
+
+ ULONG ResetInProgress; // >0 if reset is in progress.
+ ULONG ResetTimerCalls; // # of timer calls before time-out.
+ ULONG ResetNotification; // Reset notification trigger.
+
+} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
diff --git a/private/ntos/miniport/oliscsi/oliesc2.c b/private/ntos/miniport/oliscsi/oliesc2.c
new file mode 100644
index 000000000..cb4c223b8
--- /dev/null
+++ b/private/ntos/miniport/oliscsi/oliesc2.c
@@ -0,0 +1,6108 @@
+/*++
+
+Copyright (c) 1992 Ing. C. Olivetti & C., S.p.A.
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ oliesc2.c
+
+Abstract:
+
+ This is the miniport driver for the Olivetti ESC-2 SCSI adapter.
+
+Authors:
+
+ Kris Karnos 1-Nov-1992
+ Young-Chi Tan 1-Nov-1992
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+ Founded on 8-Nov-1992 version of Bruno Sartirana's NT miniport for the ESC-1
+
+ 9-Apr-1993: (v-egidis)
+ - The "FindAdapter" process has been subdivided in
+ two phases:
+ #0, gather the # of devices on each SCSI controller.
+ #1, allocate the right quantity of uncached memory.
+ - Now if there is an error during the FindAdapter
+ process (phase #1) 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.
+ - Only the 1st, the 2nd, the 3rd and the upper nibble
+ of the 4th ID byte are used to identify the EFP
+ boards. This will allow future EFP controllers to be
+ supported by this driver.
+ - The StartIo routine has been modified to behave
+ correctly when the device's command queue is full.
+ - The enqueue command routine has been modified to
+ return different error codes.
+ - The dequeue reply routine has been modified to
+ return a different error code.
+ - The interrupt routine has been modified to handle
+ more efficiently critical situations.
+ - Implemented the EFP reset.
+ CHECK BACK WITH RUFFONI THE FOLLOWING:
+ From Ruffoni's answer to one of my e-mails,
+ the EFP-2 controller re-sends all the commands
+ interrupted by a SCSI reset (this is the reason
+ why I didn't abort any pending SRBs during a SCSI
+ bus reset).
+ - ScsiPortLogError: removed the PathId, TargetId and
+ Lun (substituted with zeros) when the error is
+ global to the whole adapter.
+
+ 28-May-1993: (v-egidis)
+ - The EFP reset is now done resetting the EFP board.
+ - The "Reset Device" commands has been changed to
+ behave like the "Reset Bus" command.
+ - The "Abort" routine now verifies that the SRB to
+ abort is still outstanding before resetting the
+ SCSI bus.
+
+ 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.
+ - The EISA bus number is now checked during the
+ OliEsc2FindAdapter routine because this miniport
+ supports only one EISA bus.
+ (see MAX_EISA_BUSES for more info).
+
+ 14-Sep-1993: (v-egidis)
+ - Added support for the EFP-2 mirroring mode.
+
+--*/
+
+#include "miniport.h"
+#include "oliesc2.h" // includes scsi.h
+
+
+//
+// Function declarations
+//
+// Functions that start with 'OliEsc2' are entry points for this
+// NT miniport driver.
+//
+
+
+ULONG
+OliEsc2DriverEntry (
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ );
+
+ULONG
+OliEsc2FindAdapter(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ );
+
+ULONG
+OliEsc2FindAdapterPhase0(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ );
+
+ULONG
+OliEsc2FindAdapterPhase1(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ );
+
+BOOLEAN
+OliEsc2Initialize(
+ IN PVOID HwDeviceExtension
+ );
+
+BOOLEAN
+OliEsc2StartIo(
+ IN PVOID HwDeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+OliEsc2Interrupt(
+ IN PVOID HwDeviceExtension
+ );
+
+BOOLEAN
+OliEsc2ResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+ );
+
+BOOLEAN
+ReadEsc2ConfigReg(
+ IN PEISA_CONTROLLER EisaController,
+ IN UCHAR ConfigReg,
+ OUT PUCHAR ConfigByteInfo
+ );
+
+
+BOOLEAN
+OliEsc2IrqRegToIrql(
+ IN UCHAR IrqReg,
+ OUT PUCHAR pIrql
+ );
+
+BOOLEAN
+RegisterEfpQueues(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+VOID
+BuildSgl(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+OliEsc2ResetAdapter(
+ IN PVOID Context
+ );
+
+BOOLEAN
+GainSemaphore0(
+ PEISA_CONTROLLER EisaController
+ );
+
+VOID
+BuildEfpCmd(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+EfpGetDevinfo(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+BOOLEAN
+OliEsc2DisableEfp(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+BOOLEAN
+EnqueueEfpCmd (
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PQ_ENTRY pEfpCmd,
+ IN PCOMMAND_QUEUE qPtr,
+ IN UCHAR TarLun,
+ OUT PUCHAR pSignal
+ );
+
+BOOLEAN
+DequeueEfpReply (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+BOOLEAN
+EfpReplyQNotFull (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+BOOLEAN
+EfpCommand (
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN UCHAR TarLun
+ );
+
+BOOLEAN
+EfpGetInformation (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+BOOLEAN
+EfpGetConfiguration (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+
+#if EFP_MIRRORING_ENABLED
+
+VOID
+OliEsc2MirrorInitialize(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN BOOLEAN InitTime
+ );
+
+#endif // EFP_MIRRORING_ENABLED
+
+
+// Function definitions
+
+
+ULONG
+OliEsc2DriverEntry (
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ )
+
+/*++
+
+Routine Description:
+
+ Installable driver initialization entry point for the OS.
+
+ This routine initializes the fields of the HW_INITIALIZATION_DATA
+ structure (see SRB.H), reporting this miniport driver's entry point
+ and storage requirements, and describing the features supported by
+ this HBA. The routine then calls the Port driver's ScsiPortInitialize.
+
+Arguments:
+
+ Driver Object Pointer to the driver object for this driver
+
+Return Value:
+
+ Status from ScsiPortInitialize()
+
+--*/
+
+{
+
+ // This structure tells the upper layer the entry points of this driver.
+ HW_INITIALIZATION_DATA hwInitializationData;
+
+ ULONG i; // Auxiliary variable
+ ESC2_CONTEXT Context; // Used to keep track of the
+ // slots that have been checked
+ // and to pass info between
+ // phase #0 and phase #1.
+
+ DebugPrint((1,"\n\nSCSI Olivetti ESC-2 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 adapter bus type
+ //
+
+ hwInitializationData.AdapterInterfaceType = Eisa;
+
+ //
+ // Set entry points.
+ //
+
+ hwInitializationData.HwFindAdapter = OliEsc2FindAdapter;
+ hwInitializationData.HwInitialize = OliEsc2Initialize;
+ hwInitializationData.HwStartIo = OliEsc2StartIo;
+ hwInitializationData.HwInterrupt = OliEsc2Interrupt;
+ hwInitializationData.HwResetBus = OliEsc2ResetBus;
+
+ //
+ // Specify size of extensions.
+ //
+
+ hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
+ hwInitializationData.SpecificLuExtensionSize = sizeof(LU_EXTENSION);
+
+ //
+ // Ask for SRB extensions for SGL.
+ //
+
+ hwInitializationData.SrbExtensionSize = sizeof(EFP_SGL);
+
+ //
+ // Set number of access ranges.
+ //
+
+ hwInitializationData.NumberOfAccessRanges = 1;
+
+ //
+ // Indicate no buffer mapping but will need physical addresses.
+ //
+
+ hwInitializationData.NeedPhysicalAddresses = TRUE;
+
+ //
+ // The ESC-2 supports tagged queueing, but it's not currently enabled.
+ //
+ // KMK EFP queueing is done in the controller, not at the target.
+ // Although the ESC-2 can support tagged queueing, the feature is
+ // currently (and for the foreseeable future) disabled.
+ //
+ hwInitializationData.TaggedQueuing = FALSE;
+
+ //
+ // We will enable the ESC-2's Automatic Request Sense (ARS) capability
+ //
+ // Note: Other parts of the code assume that ARS is enabled; if this
+ // capability is ever disabled, that code will need to be changed too.
+ //
+
+ hwInitializationData.AutoRequestSense = TRUE;
+
+ //
+ // The ESC-2 supports multiple requests per logical unit.
+ //
+
+ hwInitializationData.MultipleRequestPerLu = TRUE;
+
+ // KMK Do we support ReceiveEvent function?
+
+ DebugPrint((4,
+ "OliEsc2DriverEntry: hwInitializationData address %lx.\n",
+ &hwInitializationData));
+
+ //
+ // Zero out the context structure used to pass info between phases.
+ //
+
+ for (i = 0; i < sizeof(ESC2_CONTEXT); i++) {
+ ((PUCHAR)&Context)[i] = 0;
+ }
+
+ //
+ // Phase #0, gather all the info about the devices.
+ //
+
+ Context.Phase = 0;
+
+ ScsiPortInitialize(DriverObject,
+ Argument2,
+ &hwInitializationData,
+ &Context);
+
+ //
+ // Phase #1, perform the real driver initialization.
+ //
+
+ Context.Phase = 1;
+
+ Context.CheckedSlot = 0; // Start from the beginning.
+
+ return(ScsiPortInitialize(DriverObject,
+ Argument2,
+ &hwInitializationData,
+ &Context));
+
+} // end OliEsc2DriverEntry()
+
+
+ULONG
+OliEsc2FindAdapter(
+ 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. This routine checks each slot
+ on the EISA bus, checking for ESC-2 adapters. This routine will
+ act on two phases. The first one (see the 1st call to the
+ ScsiPortInitialize routine in the OliEsc2DriverEntry routine)
+ fills only the ESC2_CONTEXT structure (with the number of devices
+ attached to each EFP-2/ESC-2 SCSI controllers). The second
+ phase will use the info gathered within the ESC2_CONTEXT to
+ allocate the right amount of "non cached" memory.
+
+Arguments:
+
+ HwDeviceExtension Device extension for this driver
+ Context ESC-2 registers' address space
+ ConfigInfo Configuration information structure describing
+ the board configuration
+
+Return Value:
+
+ SP_RETURN_NOT_FOUND Adapter not found
+ SP_RETURN_FOUND Adapter found
+ SP_RETURN_ERROR General error
+ SP_RETURN_BAD_CONFIG Configuration error
+
+--*/
+
+{
+ PESC2_CONTEXT pContext = Context;
+
+ //
+ // This miniport supports only one EISA bus.
+ //
+
+ if (ConfigInfo->SystemIoBusNumber >= MAX_EISA_BUSES) {
+
+ *Again = FALSE;
+ return(SP_RETURN_NOT_FOUND);
+ }
+
+ //
+ // Call the routine associated to the current phase number
+ //
+
+ if (pContext->Phase == 0) {
+
+ //
+ // phase #0
+ //
+
+ return OliEsc2FindAdapterPhase0( HwDeviceExtension,
+ Context,
+ BusInformation,
+ ArgumentString,
+ ConfigInfo,
+ Again );
+
+ }
+
+ //
+ // phase #1
+ //
+
+ return OliEsc2FindAdapterPhase1( HwDeviceExtension,
+ Context,
+ BusInformation,
+ ArgumentString,
+ ConfigInfo,
+ Again );
+
+} // end OliEsc2FindAdapter()
+
+
+
+ULONG
+OliEsc2FindAdapterPhase0(
+ 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. This routine checks each slot
+ on the EISA bus, checking for ESC-2 adapters. The routine will
+ store all the devices info within the ESC2_CONTEXT structure
+ (used during the 2nd phase).
+
+Arguments:
+
+ HwDeviceExtension Device extension for this driver
+ Context ESC-2 registers' address space
+ ConfigInfo Configuration information structure describing
+ the board configuration
+
+Return Value:
+
+ SP_RETURN_FOUND Adapter found
+ SP_RETURN_ERROR General error
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION DeviceExtension; // Pointer to device extension
+ PEISA_CONTROLLER eisaController; // Base address of the ESC-2
+ // registers' address space
+ PESC2_CONTEXT pContext = Context; // Used to pass info between
+ // phase #0 and phase #1.
+ BOOLEAN ExtensionAllocated = FALSE; // Non cached extension is
+ // not yet allocated.
+ UCHAR Slot; // EISA slot #
+ UCHAR DataByte;
+
+ DebugPrint((4,"OliEsc2FindAdapterPhase0: Phase #0 started.\n"));
+
+ //
+ // Initialize pointers.
+ //
+
+ DeviceExtension = HwDeviceExtension;
+
+ //
+ // Search for any ESC2/EFP2 adapters
+ //
+
+ for (Slot = 1; Slot < MAX_EISA_SLOTS_STD; Slot++) {
+
+ //
+ // Get the system physical address for this card.
+ // The card uses I/O space.
+ //
+
+ eisaController = ScsiPortGetDeviceBase(
+ DeviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ ScsiPortConvertUlongToPhysicalAddress(0x1000 * Slot),
+ 0x1000,
+ (BOOLEAN) TRUE);
+
+ //
+ // eisaController stores all our HBA's registers
+ //
+
+ eisaController =
+ (PEISA_CONTROLLER)((PUCHAR)eisaController + EISA_ADDRESS_BASE);
+
+ //
+ // Read the EISA board ID and check to see if it identifies
+ // an ESC-2 (ID 2x10893D where x = any digit greater than 1,
+ // because 2110893D is the ID of the ESC-1, which does not support
+ // the EFP interface).
+ //
+
+ DataByte = ScsiPortReadPortUchar(&eisaController->BoardId[3]);
+
+ if ((ScsiPortReadPortUchar(&eisaController->BoardId[0]) == 0x3D) &&
+ (ScsiPortReadPortUchar(&eisaController->BoardId[1]) == 0x89) &&
+ (ScsiPortReadPortUchar(&eisaController->BoardId[2]) == 0x10) &&
+ ( ((DataByte > 0x21) && ((DataByte & 0xF0) == 0x20))
+ || ((DataByte & 0xF0) == 0x50) )) {
+
+ DebugPrint((2,
+ "OliEsc2FindAdapterPhase0: ESC-2 Adapter found at EISA slot %d.\n",
+ Slot));
+
+ //
+ // Immediately disable system interrupts (bellinte).
+ // They will remain disabled (polling mode only) during
+ // the EFP initialization sequence.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemIntEnable,
+ INTERRUPTS_DISABLE);
+
+
+ // The adapter is not reset and is assumed to be functioning.
+ // Resetting the adapter is particularly time consuming
+ // (approximately 3 seconds for the ESC-2). The BIOS on x86
+ // computers or the EISA Support F/W on the M700 computers
+ // has already reset the adapter and checked its status.
+ //
+
+ // ...
+
+ //
+ // Check if we need to allocate some "non cached" memory.
+ //
+
+ if (!ExtensionAllocated) {
+
+ //
+ // Fill-in the minimum info to make the call.
+ //
+
+ ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_SIZE;
+ ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SGL_DESCRIPTORS;
+ ConfigInfo->ScatterGather = TRUE;
+ ConfigInfo->Master = TRUE;
+
+ //
+ // Allocate some "non cached" memory.
+ //
+
+ DeviceExtension->NoncachedExt = (PNONCACHED_EXTENSION)
+ ScsiPortGetUncachedExtension(
+ DeviceExtension,
+ ConfigInfo,
+ sizeof(NONCACHED_EXTENSION));
+
+ //
+ // Make sure that the call succeeded.
+ //
+
+ if (DeviceExtension->NoncachedExt == NULL) {
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase0: UncachedExtesion error.\n"));
+
+ ScsiPortLogError( DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_INIT_FAILED );
+
+ return(SP_RETURN_ERROR);
+ }
+
+ //
+ // No need to make this call again.
+ //
+
+ ExtensionAllocated = TRUE;
+
+ } // end if (no extension) ....
+
+ //
+ // Store base address of EISA registers in device extension.
+ //
+
+ DeviceExtension->EisaController = eisaController;
+
+ //
+ // Store the controller type
+ //
+
+ DataByte = ScsiPortReadPortUchar(&eisaController->BoardId[3]);
+
+ DeviceExtension->Esc2 = (DataByte & 0xF0) != 0x50 ? TRUE : FALSE;
+
+ //
+ // Reset hardware interrupts.
+ //
+ // This process ensures that we can communicate with the
+ // controller in polling mode during initialization time.
+ //
+
+ //
+ // Reset System Doorbell interrupts
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, 0x0FF);
+
+ //
+ // Enable System Doorbell interrupts
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
+ INTERRUPTS_ENABLE);
+
+ //
+ // Get the ESC-2 IRQL,
+ // get the SCSI device info,
+ // and finally disable the EFP mode.
+ //
+
+ if ( ReadEsc2ConfigReg(DeviceExtension->EisaController,
+ IRQL_REGISTER,
+ &DeviceExtension->IRQ_In_Use)
+ &&
+
+ OliEsc2IrqRegToIrql( DeviceExtension->IRQ_In_Use,
+ &DeviceExtension->IRQ_In_Use )
+
+ &&
+
+ EfpGetDevinfo(DeviceExtension)
+
+ &&
+
+ OliEsc2DisableEfp(DeviceExtension) ) {
+
+ //----------------------------------------
+ // Fill-in the ESC2_CONTEXT structure
+ //----------------------------------------
+
+ pContext->ScsiInfo[Slot - 1].AdapterPresent = 1;
+
+ pContext->ScsiInfo[Slot - 1].NumberOfDevices =
+ DeviceExtension->TotalAttachedDevices;
+
+ }
+ else {
+
+ //
+ // Log errror and continue the search.
+ //
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase0: Adapter initialization error.\n"));
+
+ ScsiPortLogError( DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_INIT_FAILED );
+
+ } // end if (Adapter configuration corret)
+
+ } // end if (ESC-2/EFP-2)
+
+ //
+ // Deallocate this I/O address and go check the next slot.
+ //
+
+ ScsiPortFreeDeviceBase(DeviceExtension,
+ (PUCHAR)eisaController - EISA_ADDRESS_BASE);
+
+ } // end for (Slot=1 to 15)
+
+ //
+ // End of phase #0.
+ //
+
+ DebugPrint((4,"OliEsc2FindAdapterPhase0: Phase #0 completed.\n"));
+
+ *Again = FALSE;
+
+ return(SP_RETURN_NOT_FOUND);
+
+} // end OliEsc2FindAdapterPhase0()
+
+
+
+
+ULONG
+OliEsc2FindAdapterPhase1(
+ 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. This routine checks each slot
+ on the EISA bus, checking for ESC-2 adapters. The routine will
+ store all the necessary info within the ESC2_CONTEXT structure
+ (used during the 2nd phase).
+
+Arguments:
+
+ HwDeviceExtension Device extension for this driver
+ Context ESC-2 registers' address space
+ ConfigInfo Configuration information structure describing
+ the board configuration
+
+Return Value:
+
+ SP_RETURN_NOT_FOUND Adapter not found
+ SP_RETURN_FOUND Adapter found
+ SP_RETURN_ERROR General error
+ SP_RETURN_BAD_CONFIG Configuration error
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION DeviceExtension; // Pointer to device extension
+ PEISA_CONTROLLER eisaController; // Base address of the ESC-2
+ // registers' address space
+ UCHAR Slot, BusCount;
+ ULONG i, TotalQueueSize;
+ UCHAR DataByte;
+ BOOLEAN Success = FALSE; // Indicates an adapter was found.
+ PESC2_CONTEXT pContext = Context; // Context pointer.
+ PUCHAR pCheckedSlot; // Indicates which slots have
+ // been checked.
+
+ DebugPrint((4,"OliEsc2FindAdapterPhase1: Phase #1 started.\n"));
+
+ //
+ // Initialize pointers.
+ //
+
+ DeviceExtension = HwDeviceExtension;
+ pCheckedSlot = &pContext->CheckedSlot;
+
+ //
+ // Check to see if an adapter is present in the system
+ //
+
+ for (Slot = *pCheckedSlot + 1; Slot < MAX_EISA_SLOTS_STD; Slot++) {
+
+ //
+ // Update the adapter count to indicate this slot has been checked.
+ //
+
+ (*pCheckedSlot)++;
+
+ //
+ // Check next slot if this is empty.
+ //
+
+ if (pContext->ScsiInfo[Slot - 1].AdapterPresent == 1) {
+
+ //
+ // Get the system physical address for this card.
+ // The card uses I/O space.
+ //
+
+ eisaController = ScsiPortGetDeviceBase(
+ DeviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ ScsiPortConvertUlongToPhysicalAddress(0x1000 * Slot),
+ 0x1000,
+ (BOOLEAN) TRUE);
+
+
+ // eisaController stores all our HBA's registers
+
+ eisaController =
+ (PEISA_CONTROLLER)((PUCHAR)eisaController + EISA_ADDRESS_BASE);
+
+ //
+ // We found one!
+ //
+
+ Success = TRUE;
+ break;
+ }
+
+ } // end for (Slot 1 to 15)
+
+ if (!Success) {
+
+ //
+ // End of phase #1.
+ //
+
+ DebugPrint((4,"OliEsc2FindAdapterPhase1: Phase #1 completed.\n"));
+
+
+ //
+ // No adapter was found. Clear the call again flag, reset the
+ // adapter count for the next bus and return.
+ //
+
+ *Again = FALSE;
+ *pCheckedSlot = 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;
+
+ //
+ // Store the controller type
+ //
+
+ DataByte = ScsiPortReadPortUchar(&eisaController->BoardId[3]);
+
+ DeviceExtension->Esc2 = (DataByte & 0xF0) != 0x50 ? TRUE : FALSE;
+
+ //
+ // Reset the "ResetInProgress" variable.
+ //
+
+ DeviceExtension->ResetInProgress = 0;
+
+ //
+ // Indicate the maximum transfer length in bytes.
+ //
+
+ ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_SIZE; // KMK Check..
+
+ //
+ // Indicate the maximum number of physical segments
+ //
+
+ ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SGL_DESCRIPTORS; // KMK yes?
+
+ // no DMA, so we use the default values (no DMA) for DMA parameters.
+
+ ConfigInfo->ScatterGather = TRUE;
+ ConfigInfo->Master = TRUE;
+
+ //
+ // Indicate which interrupt mode the ESC-2 uses
+ //
+
+ if (ScsiPortReadPortUchar(&eisaController->GlobalConfiguration) &
+ EDGE_SENSITIVE) {
+
+ ConfigInfo->InterruptMode = Latched;
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: EDGE_SENSITIVE interrupts.\n"));
+
+ } else {
+
+ ConfigInfo->InterruptMode = LevelSensitive;
+ DebugPrint((4,
+ "OliEsc2FindAdapterPhase1: LEVEL_SENSITIVE interrupts.\n"));
+
+ }
+
+ //
+ // Fill in the access array information.
+ //
+
+ (*ConfigInfo->AccessRanges)[0].RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(0x1000 * Slot + EISA_ADDRESS_BASE);
+ (*ConfigInfo->AccessRanges)[0].RangeLength = sizeof(EISA_CONTROLLER);
+ (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
+
+ //
+ // Reset hardware interrupts.
+ //
+ // This process ensures that we can communicate with the controller
+ // in polling mode during initialization time.
+ //
+
+ //
+ // Reset System Doorbell interrupts
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, 0x0FF);
+
+ //
+ // Enable System Doorbell interrupts
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
+ INTERRUPTS_ENABLE);
+
+ //
+ // Read the ESC-2's / EFP-2's configuration registers.
+ //
+
+ for (i=0; i < CFG_REGS_NUMBER; i++) {
+
+ if (ReadEsc2ConfigReg(DeviceExtension->EisaController,
+ (UCHAR)i,
+ &DeviceExtension->CfgRegs[i])) {
+
+ //
+ // Got it!
+ //
+
+ DeviceExtension->CfgRegsPresent[i] = TRUE;
+
+ }
+ else {
+
+ //
+ // Error reading the config register or
+ // config register not present.
+ //
+
+ DeviceExtension->CfgRegsPresent[i] = FALSE;
+ DeviceExtension->CfgRegs[i] = 0;
+ }
+ }
+
+
+ //
+ // Read the ESC-2's ATCFG register, to see if this adapter is in
+ // AT-enabled mode. If so, report that this controller is acting
+ // like an AT primary controller, to prevent access by the "AT"
+ // controller driver. An ESC-2 cannot be an AT secondary controller.
+ //
+
+ if (DeviceExtension->CfgRegsPresent[ATCFG_REGISTER] &&
+ DeviceExtension->CfgRegs[ATCFG_REGISTER] & AT_ENABLED) {
+
+ ConfigInfo->AtdiskPrimaryClaimed = TRUE;
+ }
+
+ //
+ // Get the ESC-2 IRQL.
+ //
+
+ if (!DeviceExtension->CfgRegsPresent[IRQL_REGISTER] ||
+ !OliEsc2IrqRegToIrql( DeviceExtension->CfgRegs[IRQL_REGISTER],
+ &DeviceExtension->IRQ_In_Use )) {
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: Adapter initialization error.\n"));
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_INIT_FAILED
+ );
+
+ return SP_RETURN_ERROR;
+ }
+
+ //
+ // Save the IRQL value in the ConfigInfo structure.
+ //
+
+ ConfigInfo->BusInterruptLevel = DeviceExtension->IRQ_In_Use;
+
+ //
+ // Allocate queues here.
+ //
+
+ TotalQueueSize = (pContext->ScsiInfo[Slot - 1].NumberOfDevices) *
+ sizeof(EFP_COMMAND_QUEUE) +
+ sizeof(NONCACHED_EXTENSION);
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: TotalQueueSize = %d.\n",TotalQueueSize));
+
+ DeviceExtension->NoncachedExt = (PNONCACHED_EXTENSION)
+ ScsiPortGetUncachedExtension(
+ DeviceExtension,
+ ConfigInfo,
+ TotalQueueSize);
+ //
+ // Make sure that the call succeeded.
+ //
+
+ if (DeviceExtension->NoncachedExt == NULL) {
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: UncachedExtesion error.\n"));
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_INIT_FAILED
+ );
+
+ return(SP_RETURN_ERROR);
+ }
+
+ //
+ // Get the SCSI devices info
+ //
+
+ if ( !EfpGetDevinfo(DeviceExtension) ) {
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: Adapter initialization error.\n"));
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_INIT_FAILED
+ );
+
+ return SP_RETURN_ERROR;
+ };
+
+ //
+ // Make sure that the two phases got the same number of devices.
+ //
+
+ if (pContext->ScsiInfo[Slot - 1].NumberOfDevices !=
+ DeviceExtension->TotalAttachedDevices) {
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: The number of devices is different:\n"
+ " phase #0: %d, phase #1: %d.\n",
+ pContext->ScsiInfo[Slot - 1].NumberOfDevices,
+ DeviceExtension->TotalAttachedDevices));
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_INIT_FAILED
+ );
+
+ return SP_RETURN_ERROR;
+ }
+
+ //
+ // Count the number of buses
+ //
+
+ for ( i=0,BusCount=0 ; i < MAX_HAIDS ; i++ ) {
+ if ( DeviceExtension->Adapter_ID[i] != NO_BUS_ID ) {
+ ConfigInfo->InitiatorBusId[BusCount] =
+ DeviceExtension->Adapter_ID[i];
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: InitiatorBusId[%d] = %d.\n",
+ BusCount,
+ ConfigInfo->InitiatorBusId[BusCount]));
+ BusCount++;
+ };
+ };
+
+ ConfigInfo->NumberOfBuses = BusCount;
+ DeviceExtension->NumberOfBuses = BusCount;
+
+ //
+ // End of phase #1.
+ //
+
+ DebugPrint((4,"OliEsc2FindAdapterPhase1: Phase #1 multi-stage.\n"));
+
+ return SP_RETURN_FOUND;
+
+} // end OliEsc2FindAdapterPhase1()
+
+
+
+
+BOOLEAN
+OliEsc2Initialize(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Initializes the EFP interface on the adapter. As directed by the NT
+ miniport specification, this routine avoids resetting the SCSI bus.
+
+Arguments:
+
+ HwDeviceExtension Device extension for this driver
+
+Return Value:
+
+ Returns TRUE if initialization completed successfully.
+ Returns FALSE and logs an error if adapter initialization fails.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION DeviceExtension;
+ PEISA_CONTROLLER eisaController;
+
+ DeviceExtension = HwDeviceExtension;
+
+ eisaController = DeviceExtension->EisaController;
+
+ //
+ // At this point, we know how many devices are attached; therefore,
+ // we know how many device queues are required. We call another
+ // routine which will use the Get Configuration information stored
+ // in the uncached extension to build a new queues descriptor,
+ // allocate mailbox and device queues, and resend the EFP_SET and
+ // EFP_START commands to register this new revised information with
+ // the EFP-2 controller.
+ //
+
+ if ( !RegisterEfpQueues(DeviceExtension) ) {
+
+ DebugPrint((1,
+ "OliEsc2FindAdapterPhase1: Adapter initialization error.\n"));
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_INIT_FAILED
+ );
+
+ return(FALSE);
+ }
+
+#if EFP_MIRRORING_ENABLED
+
+ //
+ // We need to initialize all the mirroring structures.
+ //
+
+ OliEsc2MirrorInitialize(DeviceExtension, TRUE);
+
+#endif // EFP_MIRRORING_ENABLED
+
+ //
+ // The EFP-2 interface is now ready for use, so now we can
+ // enable interrupts.
+ //
+
+ //
+ // Ensure that EFP interrupts (0 and 1) are enabled.
+ // Enable also the ESC-1 High Performance interrupt, which is
+ // need for implementation of the Reset_Bus command.
+ //
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
+ INTERRUPTS_ENABLE);
+
+ //
+ // Enable system interrupts (bellinte).
+ //
+ ScsiPortWritePortUchar(&eisaController->SystemIntEnable,
+ SYSTEM_INTS_ENABLE);
+
+ //
+ // Initialization completed successfully.
+ //
+
+ return(TRUE);
+} // end OliEsc2Initialize()
+
+
+BOOLEAN
+EfpGetDevinfo(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Initializes the EFP interface on the adapter. As directed by the NT
+ miniport specification, this routine avoids resetting the SCSI bus.
+
+Arguments:
+
+ DeviceExtension Device extension for this driver
+
+Return Value:
+
+ Returns TRUE if initialization completed successfully.
+ Returns FALSE and logs an error if adapter initialization fails.
+
+--*/
+
+{
+ PEISA_CONTROLLER eisaController;
+ PNONCACHED_EXTENSION pNoncachedExt;
+ ULONG i, length;
+ BOOLEAN GotInt = FALSE;
+ UCHAR EfpMsg;
+
+ eisaController = DeviceExtension->EisaController;
+ pNoncachedExt = DeviceExtension->NoncachedExt;
+
+ //
+ // Interrupts have already been disabled on entry (see OliEsc2FindAdapter).
+ // Polling is possible through enabling the ESC-1 HP and EFP interface
+ // in the System Doorbell Mask.
+ //
+
+ //
+ // 1. Issue EFP_SET command via I/O instruction 'TYPE SERVICE' to the
+ // BMIC mailbox registers.
+ //
+
+ if (!GainSemaphore0(eisaController)) {
+ return(FALSE);
+ }
+
+ // if got the semaphore, place IRQ parameter in mailbox register 1
+ ScsiPortWritePortUchar(&eisaController->InParm1,
+ DeviceExtension->IRQ_In_Use);
+
+ // output a TYPE SERVICE request: 'efp_set'
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_SET);
+
+ // set bit 1 of local doorbell register to generate interrupt request
+ // (EFP_ATTENTION)
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ // Wait for controller to respond
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell)
+ & EFP_TYPE_MSG) {
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ DebugPrint((1, "EfpGetDevinfo: No interrupt after EFP_SET.\n"));
+ return(FALSE);
+ }
+ else {
+ DebugPrint((4, "EfpGetDevinfo: Set, interrupt after %ld us.\n",
+ i*WAIT_INT_INTERVAL));
+
+ EfpMsg = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+ if (EfpMsg != M_INIT_DIAG_OK) {
+ DebugPrint((1,
+ "EfpGetDevinfo: INIT_DIAG_OK not received after EFP_SET.\n"));
+ if (EfpMsg == M_ERR_INIT) {
+ DebugPrint((1,
+ "EfpGetDevinfo: M_ERR_INIT received after EFP_SET.\n"));
+ }
+ return(FALSE);
+ }
+ }
+
+ // reset bit 1 of the system doorbell register to clear request
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ // clear semaphore 1 after reading TYPE_MSG
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, 0);
+
+ //
+ // 2. Build queues descriptor. This initial QD defines only the mailbox
+ // command queue and the reply queue.
+ //
+
+ //
+ // First, initialize queue-related fields in HW_DEVICE_EXTENSION
+ // and NONCACHED_EXTENSION.
+ //
+
+ DeviceExtension->Reply_Q_Full_Flag = 0;
+ DeviceExtension->Reply_Q_Get = 0;
+ DeviceExtension->RQ_In_Process = 0;
+ pNoncachedExt->Command_Qs[0].Cmd_Q_Get = 0;
+ pNoncachedExt->Command_Qs[0].Cmd_Q_Put = 0;
+
+ for (i = 0; i < HA_QUEUES; i++) {
+ DeviceExtension->Q_Full_Map[i] = 0;
+ DeviceExtension->DevicesPresent[i].present = FALSE;
+ }
+
+ for (i = 0; i < REPLY_Q_ENTRIES; i++) {
+ pNoncachedExt->Reply_Q[i].qnrply.nrply_flag = 0;
+ }
+
+ // set up Queues Descriptor header
+
+ pNoncachedExt->QD_Head.qdh_maint = 0; // normal environment
+ pNoncachedExt->QD_Head.qdh_n_cmd_q = 1; // # of queues
+ pNoncachedExt->QD_Head.qdh_type_reply = 0; // int after 1+
+ pNoncachedExt->QD_Head.qdh_reserved1 = 0;
+ pNoncachedExt->QD_Head.qdh_reply_q_addr =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL, // No SRB
+ &pNoncachedExt->Reply_Q,
+ &length));
+ pNoncachedExt->QD_Head.qdh_n_ent_reply = REPLY_Q_ENTRIES;
+ pNoncachedExt->QD_Head.qdh_reserved2 = 0;
+
+ // set up Queues Descriptor body for mailbox queue
+
+ pNoncachedExt->QD_Bodies[0].qdb_scsi_level = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_channel = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_ID = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_LUN = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_n_entry_cmd = COMMAND_Q_ENTRIES;
+ pNoncachedExt->QD_Bodies[0].qdb_notfull_int = 1;
+ pNoncachedExt->QD_Bodies[0].qdb_no_ars = 0; // ESC2/EFP2
+ pNoncachedExt->QD_Bodies[0].qdb_timeout = 0;
+ pNoncachedExt->QD_Bodies[0].qdb_cmd_q_addr =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL, // no SRB
+ &pNoncachedExt->Command_Qs[0],
+ &length));
+ pNoncachedExt->QD_Bodies[0].qdb_reserved = 0;
+
+ // store the controller mailbox info in common area
+
+ DeviceExtension->DevicesPresent[0].present = TRUE;
+ DeviceExtension->DevicesPresent[0].qnumber = 0;
+ DeviceExtension->DevicesPresent[0].qPtr = &pNoncachedExt->Command_Qs[0];
+
+ //
+ // 3. Issue EFP_START command to BMIC mailbox registers.
+ //
+
+ if (!GainSemaphore0(eisaController)) {
+ return(FALSE);
+ }
+
+ //
+ // Get the physical address of the queues descriptor (and store it,
+ // as we'll need it in RegisterEfpQueues).
+ //
+
+ DeviceExtension->QueuesDescriptor_PA =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL, // no SRB
+ &pNoncachedExt->QD_Head,
+ &length));
+
+ // if got the semaphore, output physical address of the queues descriptor
+ // to mailbox registers 1 - 4.
+ ScsiPortWritePortUlong((PULONG)&eisaController->InParm1,
+ DeviceExtension->QueuesDescriptor_PA);
+
+ // output a TYPE SERVICE request to 'efp_start'
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_START);
+
+ // set bit 1 of local doorbell register to generate an interrupt request
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ // Wait for controller to respond.
+ GotInt = FALSE; // re-initialize GotInt
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
+ EFP_TYPE_MSG) { // was EFP_MSG_INT()
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ DebugPrint((1, "EfpGetDevinfo: No interrupt after EFP_START.\n"));
+ return(FALSE);
+ }
+ else {
+ DebugPrint((4, "EfpGetDevinfo: Start, interrupt after %ld us.\n",
+ i*WAIT_INT_INTERVAL));
+
+ EfpMsg = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+ if (EfpMsg != M_INIT_DIAG_OK) {
+ DebugPrint((1,
+ "EfpGetDevinfo: INIT_DIAG_OK not received after EFP_START.\n"));
+ if (EfpMsg == M_ERR_INIT) {
+ DebugPrint((1,
+ "EfpGetDevinfo: M_ERR_INIT received after EFP_START.\n"));
+ }
+ return(FALSE);
+ }
+ }
+
+ // reset bit 1 of the system doorbell register to clear request
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ // clear semaphore 1 after reading TYPE_MSG (RELEASE_SEM1)
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, 0);
+
+ //
+ // 4. Issue Get_Information command.
+ //
+
+ if (!EfpGetInformation(DeviceExtension)) {
+ DebugPrint((1, "EfpGetDevinfo: EFP Get_Information failed.\n"));
+ return(FALSE);
+ }
+
+ if (DeviceExtension->Max_CmdQ_ents < COMMAND_Q_ENTRIES) {
+ DebugPrint((1,
+ "EfpGetDevinfo: Controller's max command q size too small.\n"));
+ return(FALSE);
+ }
+
+ //
+ // 5. Issue Get_Configuration command.
+ //
+
+ if (!EfpGetConfiguration(DeviceExtension)) {
+ DebugPrint((1, "EfpGetDevinfo: EfpGetConfiguration failed.\n"));
+ return(FALSE);
+ }
+
+ DeviceExtension->TotalAttachedDevices =
+ (USHORT)(DeviceExtension->Q_Buf.qmbr.mbr_length / sizeof(GET_CONF));
+ DebugPrint((1,"EfpGetDevinfo: TotalAttachedDevices = %d.\n",
+ DeviceExtension->TotalAttachedDevices));
+ return(TRUE);
+
+
+} // end EfpGetDevinfo()
+
+
+BOOLEAN
+OliEsc2DisableEfp(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Disables the EFP interface on the adapter sending the SET command.
+
+Arguments:
+
+ DeviceExtension Device extension for this driver
+
+Return Value:
+
+ Returns TRUE if initialization completed successfully.
+ Returns FALSE and logs an error if adapter initialization fails.
+
+--*/
+
+{
+ PEISA_CONTROLLER eisaController;
+ ULONG i;
+ BOOLEAN GotInt = FALSE;
+ UCHAR EfpMsg;
+
+ eisaController = DeviceExtension->EisaController;
+
+ //
+ // Interrupts have already been disabled on entry (see OliEsc2FindAdapter).
+ // Polling is possible through enabling the ESC-1 HP and EFP interface
+ // in the System Doorbell Mask.
+ //
+
+ //
+ // Issue EFP_SET command via I/O instruction 'TYPE SERVICE' to the
+ // BMIC mailbox registers.
+ //
+
+ if (!GainSemaphore0(eisaController)) {
+ return(FALSE);
+ }
+
+ // if got the semaphore, place IRQ parameter in mailbox register 1
+ ScsiPortWritePortUchar(&eisaController->InParm1,
+ DeviceExtension->IRQ_In_Use);
+
+ // output a TYPE SERVICE request: 'efp_set'
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_SET);
+
+ // set bit 1 of local doorbell register to generate interrupt request
+ // (EFP_ATTENTION)
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ // Wait for controller to respond
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell)
+ & EFP_TYPE_MSG) {
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ DebugPrint((1, "OliEsc2Disable: No interrupt after EFP_SET.\n"));
+ return(FALSE);
+ }
+ else {
+ DebugPrint((4, "OliEsc2Disable: Set, interrupt after %ld us.\n",
+ i*WAIT_INT_INTERVAL));
+
+ EfpMsg = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+ if (EfpMsg != M_INIT_DIAG_OK) {
+ DebugPrint((1,
+ "OliEsc2Disable: INIT_DIAG_OK not received after EFP_SET.\n"));
+ if (EfpMsg == M_ERR_INIT) {
+ DebugPrint((1,
+ "OliEsc2Disable: M_ERR_INIT received after EFP_SET.\n"));
+ }
+ return(FALSE);
+ }
+ }
+
+ // reset bit 1 of the system doorbell register to clear request
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ // clear semaphore 1 after reading TYPE_MSG
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, 0);
+
+ return(TRUE);
+}
+
+
+
+BOOLEAN
+OliEsc2StartIo(
+ 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 an SRB 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;
+ PEFP_SGL pEFP;
+ UCHAR opCode = 0;
+ UCHAR qnumber, TarLun, SignalFlag;
+ PSCSI_REQUEST_BLOCK abortedSrb;
+ UCHAR Bus;
+
+ DebugPrint((3,"OliEsc2StartIo: Enter routine.\n"));
+
+ //
+ // Get the base address of the ESC-2 registers' address space
+ //
+
+ DeviceExtension = HwDeviceExtension;
+ eisaController = DeviceExtension->EisaController;
+
+ //
+ // Get EFP command extension from SRB
+ //
+
+ pEFP = Srb->SrbExtension;
+
+
+ DebugPrint((3,"OliEsc2StartIo: Srb->Function = %x,\n", Srb->Function));
+ DebugPrint((3,"OliEsc2StartIo: PathId=%d TargetId=%d Lun=%d.\n",
+ Srb->PathId,Srb->TargetId,Srb->Lun));
+
+ //
+ // 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,"OliEsc2StartIo: 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.
+ //
+
+ for (Bus=0; Bus < DeviceExtension->NumberOfBuses; Bus++) {
+ ScsiPortNotification(ResetDetected, DeviceExtension, Bus);
+ }
+
+ //
+ // The controller is now ready for the next request.
+ //
+
+ ScsiPortNotification(NextRequest, DeviceExtension, NULL);
+
+ return(TRUE);
+ }
+
+ //
+ // Perform the requested function.
+ //
+
+ switch (Srb->Function) {
+
+
+ case SRB_FUNCTION_EXECUTE_SCSI:
+
+ TarLun = GET_QINDEX(Srb->PathId, Srb->TargetId, Srb->Lun);
+
+ //
+ // Do not process requests to non-existent devices.
+ //
+
+ if (Srb->TargetId == DeviceExtension->Adapter_ID[Srb->PathId] ||
+ !DeviceExtension->DevicesPresent[TarLun].present) {
+
+ //
+ // We are filtering SCSI messages directed to not present
+ // devices and messages to the host adapter itself.
+ //
+
+ DebugPrint((2,
+ "OliEsc2StartIo: Rejected SCSI cmd to TID %d LUN %d.\n",
+ Srb->TargetId, Srb->Lun));
+
+ Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+
+ ScsiPortNotification(RequestComplete, DeviceExtension, Srb);
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+ return(TRUE);
+
+ }
+ break;
+
+ //
+ // ... if the request is for a valid device, go at the end of this
+ // switch body.
+ //
+
+
+
+ case SRB_FUNCTION_ABORT_COMMAND:
+
+ DebugPrint((2, "OliEsc2StartIo: Abort Cmd Target ID %d.\n",
+ Srb->TargetId));
+
+ //
+ // Verify that SRB to abort is still outstanding.
+ //
+
+ abortedSrb = ScsiPortGetSrb(DeviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun,
+ Srb->QueueTag);
+
+ if (abortedSrb != Srb->NextSrb ||
+ abortedSrb->SrbStatus != SRB_STATUS_PENDING) {
+
+ DebugPrint((1,
+ "OliEsc2StartIo: SRB to abort already completed.\n"));
+
+ //
+ // Complete abort SRB.
+ //
+
+ Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
+
+ ScsiPortNotification(RequestComplete,
+ DeviceExtension,
+ Srb);
+
+ }
+ else {
+
+ //
+ // Only Reset Bus, not Abort Request or Reset Device, is supported
+ // on the ESC-2 in EFP mode, so we will send a Reset Bus command.
+ //
+
+ //
+ // 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.
+ //
+
+ OliEsc2ResetBus(DeviceExtension, Srb->PathId);
+
+ }
+
+ //
+ // The controller is now ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+ return(TRUE);
+
+
+
+ case SRB_FUNCTION_RESET_BUS:
+
+ //
+ // Reset ESC-2 and SCSI bus.
+ //
+
+ DebugPrint((2, "OliEsc2StartIo: 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.
+ //
+
+ OliEsc2ResetBus(DeviceExtension, Srb->PathId);
+
+ //
+ // The controller is now ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+ return(TRUE);
+
+
+
+ case SRB_FUNCTION_RESET_DEVICE:
+
+ //
+ // Only Reset Bus, not Reset Device, is supported on the ESC-2
+ // in EFP mode, so we will send a Reset Bus command.
+ //
+
+ DebugPrint((2, "OliEsc2StartIo: Reset Device Target ID %d.\n",
+ Srb->TargetId));
+
+ //
+ // 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.
+ //
+
+ OliEsc2ResetBus(DeviceExtension, Srb->PathId);
+
+ //
+ // The controller is now ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+ return(TRUE);
+
+
+
+ default:
+
+ //
+ // Set error and complete request
+ //
+
+ DebugPrint((1, "OliEsc2StartIo: Invalid Request.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+
+ ScsiPortNotification(RequestComplete, DeviceExtension, Srb);
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+
+
+ return(TRUE);
+
+ } // end switch (function to perform)
+
+
+ //-----------------------------------------------------------------
+ // The following code can be executed only if we are coming from
+ // the SRB_FUNCTION_EXECUTE_SCSI function.
+ //-----------------------------------------------------------------
+
+ //
+ // Get the pointer to the extension data area associated with
+ // the pair (Srb->TargetId, Srb->Lun)
+ //
+
+ luExtension = ScsiPortGetLogicalUnit(DeviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+ ASSERT(luExtension);
+
+ //
+ // 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 EFP command.
+ //
+
+ BuildEfpCmd(DeviceExtension, Srb);
+
+ //
+ // Enqueue the command
+ //
+
+ if (!EnqueueEfpCmd(DeviceExtension,
+ &pEFP->EfpCmd,
+ DeviceExtension->DevicesPresent[TarLun].qPtr,
+ TarLun,
+ &SignalFlag)) {
+
+ //
+ // Error enqueuing the command.
+ //
+
+ if (SignalFlag) { // send command time out
+
+ //
+ // Let the operating system time-out this SRB.
+ //
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ Srb->PathId,
+ DeviceExtension->Adapter_ID[Srb->PathId],
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ SEND_COMMAND_TIMED_OUT
+ );
+
+ DebugPrint((1,"OliEsc2StartIo: Send command timed out.\n"));
+
+ //
+ // Ready for the next command but not for the same queue.
+ //
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+
+ //
+ // Note: the StartIo return code is not checked.
+ //
+
+ return (FALSE);
+
+ }
+ else { // command not enqueued, queue was full
+
+ //
+ // Note: this error should never happen !
+ //
+
+ qnumber = DeviceExtension->DevicesPresent[TarLun].qnumber;
+
+ DebugPrint((1, "OliEsc2StartIo: Command queue %d was full.\n",
+ qnumber));
+
+ Srb->SrbStatus = SRB_STATUS_BUSY;
+
+ ScsiPortNotification(RequestComplete, DeviceExtension, Srb);
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+
+ return (TRUE);
+ }
+
+ } // end if (command not enqueued)
+
+ //
+ // The command has been enqueued,
+ // the adapter is ready for the next command.
+ //
+
+ if (SignalFlag) { // command enqueued, queue not full
+
+ //
+ // ESC-2 supports multiple requests and
+ // the queue for this device is not full.
+ //
+
+ ScsiPortNotification(NextLuRequest,
+ DeviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+
+ return(TRUE);
+
+ }
+ else { // command enqueued, queue is now full
+
+ //
+ // The queue is now full, no more commands for this device.
+ //
+
+ qnumber = DeviceExtension->DevicesPresent[TarLun].qnumber;
+ DeviceExtension->Q_Full_Map[qnumber] = 1;
+
+ DebugPrint((2,"OliEsc2StartIo: Command queue %d is now full.\n",
+ qnumber));
+
+ ScsiPortNotification(NextRequest,
+ DeviceExtension,
+ NULL);
+
+ return(TRUE);
+
+ }
+
+} // end OliEsc2StartIo()
+
+
+BOOLEAN
+OliEsc2Interrupt(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt service routine for the ESC-2 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 output mailbox is full,
+ the SRB 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
+
+
+// START NOTE EFP_MIRRORING_ENABLED.
+//
+// The OliEsc2Interrupt routine always uses the "userid" field of the
+// NORMAL_REPLY struct to retrieve the SRB. This is OK because the "userid"
+// field is at the same offset in both structures (NORMAL_REPLY and
+// MIRROR_REPLY).
+//
+// END NOTE EFP_MIRRORING_ENABLED.
+
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION DeviceExtension;
+ PEISA_CONTROLLER eisaController;
+ PLU_EXTENSION LuExtension;
+ UCHAR messagedata, qnumber, intpending;
+ PSCSI_REQUEST_BLOCK pSrb;
+ UCHAR sensevalid = FALSE;
+
+#if EFP_MIRRORING_ENABLED
+
+ PMIRROR_REPLY mr;
+ PMREPLY_SDATA pMreplySdata;
+ UCHAR TarLun;
+ PTAR_Q pDevInfo;
+ USHORT Index;
+ SENSE_DATA Sdata;
+
+#endif // EFP_MIRRORING_ENABLED
+
+ 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.
+ //
+
+ intpending = ScsiPortReadPortUchar(&eisaController->SystemDoorBell);
+
+ DebugPrint((3, "OliEsc2Interrupt: intpending on entry is: %x.\n",
+ intpending));
+
+ //
+ // Check if this is our interrupt
+ //
+
+ if (!(intpending & INTERRUPTS_ENABLE)) {
+
+ //
+ // No interrupt is pending.
+ // Enable interrupts.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
+ INTERRUPTS_ENABLE);
+ return(FALSE);
+
+ }
+
+ //
+ // Check if any ESC-1 type interrupt
+ //
+
+ if (intpending & ESC_INT_BIT) {
+
+ //
+ // Int this driver, ESC-1 HP interrupts are used only for the
+ // reset_bus command.
+ //
+
+ //
+ // reset the interrupt
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
+ ESC_INT_BIT);
+ //
+ // unlock the semaphore
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore,
+ SEM_UNLOCK);
+
+ }
+
+ //
+ // Check for any EFP_TYPE_MSG interrupt
+ //
+
+ if (intpending & EFP_TYPE_MSG) {
+
+ //
+ // Get the message.
+ //
+
+ messagedata = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+
+ //
+ // Acknowledge the type message interrupt
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ //
+ // Unlock the Result Semaphore, so that the ESC-2 can load
+ // new values in the output mailboxes.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, SEM_UNLOCK);
+
+ //
+ // Check the type of message.
+ //
+
+ if (messagedata & M_ERR_CMDQ_NFUL) {
+
+ //
+ // The queue is no longer full. Notify the ScsiPort driver.
+ //
+
+ messagedata &= 0x7F;
+ qnumber = (UCHAR)messagedata;
+ DeviceExtension->Q_Full_Map[qnumber] = 0;
+
+ ScsiPortNotification(
+ NextLuRequest,
+ DeviceExtension,
+ DeviceExtension->NoncachedExt->QD_Bodies[qnumber].qdb_channel - 1,
+ DeviceExtension->NoncachedExt->QD_Bodies[qnumber].qdb_ID,
+ DeviceExtension->NoncachedExt->QD_Bodies[qnumber].qdb_LUN
+ );
+
+ //
+ // Send a info message to the debug port
+ //
+
+ DebugPrint((2,"OliEsc2Interrupt: Command queue %d not full.\n",
+ qnumber));
+
+ }
+ else if (messagedata == M_REPLY_Q_FULL) {
+
+ //
+ // mark reply Q full
+ //
+
+ DeviceExtension->Reply_Q_Full_Flag = 0x01;
+
+ //
+ // NOTE: This is just a workaround for the ESC-2 firmware.
+ // (at least up to revision 2.25).
+ // If the reply queue gets full, the ESC-2 firmware
+ // sends only the "Reply Queue Full" message.
+ // To make it work with our logic we need to simulate
+ // a "Command Complete" interrupt.
+ //
+
+ intpending |= EFP_CMD_COMPL;
+
+ //
+ // Send a info message to the debug port
+ //
+
+ DebugPrint((2,"OliEsc2Interrupt: Reply queue is full.\n"));
+
+ }
+ else {
+
+ //
+ // Send a warning message to the debug port
+ //
+
+ DebugPrint((1, "OliEsc2Interrupt: Unknown message: %x.\n",
+ messagedata));
+
+ }
+
+ } // end {int_status is EFP_TYPE_MESSAGE}
+
+ //
+ // Check if any EFP command complete interrupt
+ //
+
+ if (intpending & EFP_CMD_COMPL) {
+
+ //
+ // This is the EFP command completion processing
+ //
+ //
+ // Loop through the reply queue start from Reply_Get to look for
+ // valid reply entry and send out command completion
+ //
+
+ while (1) {
+
+ //
+ // Acknowledge interrupt.
+ //
+ // The interrupt is reset each time a reply is dequeued
+ // to remove any subsequent interrupts of the same type.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
+ EFP_ACK_INT);
+
+ //
+ // Dequeue a reply.
+ //
+
+ if (!DequeueEfpReply(DeviceExtension)) {
+
+ //
+ // Note: also if there was an error during the dequeue
+ // phase, we still need to check the RQ_In_Process
+ // for any valid reply.
+ //
+
+ //
+ // Log error.
+ //
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_REPLY_DEQUEUE_ERROR
+ );
+
+ //
+ // Send error message to the debug port
+ //
+
+ DebugPrint((1, "OliEsc2Interrupt: Reply dequeue error.\n"));
+ }
+
+ //
+ // Note: this is the only place where we can exit the loop !
+ //
+
+ if (DeviceExtension->RQ_In_Process == 0x00) { // any reply ?
+ break; // No, exit loop.
+ }
+
+ //
+ // Check for any EFP_TYPE_MSG interrupt.
+ // We need to update the Reply_Q_Full_Flag before dequeuing
+ // another request.
+ //
+
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
+ EFP_TYPE_MSG) {
+
+ //
+ // Get the message.
+ //
+
+ messagedata = ScsiPortReadPortUchar(
+ &eisaController->OutTypeMsg);
+
+ //
+ // Acknowledge the type message interrupt
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
+ EFP_ACK_MSG);
+
+ //
+ // Unlock the Result Semaphore, so that the ESC-2 can load
+ // new values in the output mailboxes.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore,
+ SEM_UNLOCK);
+
+ //
+ // Check the type of message.
+ //
+
+ if (messagedata & M_ERR_CMDQ_NFUL) {
+
+ //
+ // The queue is no longer full. Notify the ScsiPort driver.
+ //
+
+ messagedata &= 0x7F;
+ qnumber = (UCHAR)messagedata;
+ DeviceExtension->Q_Full_Map[qnumber] = 0;
+
+ ScsiPortNotification(
+ NextLuRequest,
+ DeviceExtension,
+ DeviceExtension->NoncachedExt->QD_Bodies[qnumber].
+ qdb_channel - 1,
+ DeviceExtension->NoncachedExt->QD_Bodies[qnumber].
+ qdb_ID,
+ DeviceExtension->NoncachedExt->QD_Bodies[qnumber].
+ qdb_LUN
+ );
+
+ //
+ // Send a info message to the debug port
+ //
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: Command queue %d not full.\n",
+ qnumber));
+
+ }
+ else if (messagedata == M_REPLY_Q_FULL) {
+
+ //
+ // mark reply Q full
+ //
+
+ DeviceExtension->Reply_Q_Full_Flag = 0x01;
+
+ //
+ // Send a info message to the debug port
+ //
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: Reply queue is full.\n"));
+
+ }
+ else {
+
+ //
+ // Send a warning message to the debug port
+ //
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Unknown message: %x.\n",
+ messagedata));
+
+ }
+
+ } // end {int_status is EFP_TYPE_MESSAGE}
+
+ //
+ // Make sure the reply is valid.
+ //
+
+ pSrb = (PVOID) DeviceExtension->Q_Buf.qnrply.nrply_userid;
+ if (pSrb == NULL) {
+
+ //
+ // Can't get valid SRB pointer for this reply entry
+ // Log it as an error.
+ //
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ 0,
+ 0,
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ ESCX_BAD_PHYSICAL_ADDRESS
+ );
+
+ //
+ // Send error message to the debug port
+ //
+
+ DebugPrint((1, "OliEsc2Interrupt: Bad physical address.\n"));
+
+ //
+ // Go check if there is any other reply.
+ //
+
+ continue;
+ }
+
+ //
+ // The ESC-2 and EFP-2 don't report the underrun/overrun error
+ // during the execution of some commands (example: inquiry).
+ // The following code detects these cases and simulates an
+ // underrun/overrun error.
+ //
+
+ if (DeviceExtension->Q_Buf.qnrply.nrply_scsi_len !=
+ pSrb->DataTransferLength &&
+ (DeviceExtension->Q_Buf.qnrply.nrply_status == EFP_CMD_SUCC ||
+ DeviceExtension->Q_Buf.qnrply.nrply_status == EFP_AUTOREC_OK)){
+
+ DeviceExtension->Q_Buf.qnrply.nrply_status = EFP_DATA_RUN;
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: Simulated an overrun/underrun error.\n"));
+ }
+
+ //
+ // Check the status of the operation.
+ //
+
+#if EFP_MIRRORING_ENABLED
+
+ // ---------------------------------------------------------------
+ //
+ // MIRRORING REPLY
+ //
+ // ---------------------------------------------------------------
+
+ //
+ // Initialize pointer to mirroring reply
+ //
+
+ mr = &DeviceExtension->Q_Buf.qmrply;
+
+ if (mr->mrply_flag == MREPLY_VALID) {
+
+ //
+ // Get the reply state.
+ //
+
+ switch (mr->mrply_off_attr){
+
+ case EFP_SOURCE_OFFLINE:
+
+ //
+ // Get the mirror disk status.
+ //
+
+ pMreplySdata = (PMREPLY_SDATA)&mr->mrply_valid2;
+ break;
+
+
+ case EFP_MIRROR_OFFLINE:
+
+ //
+ // Get the source disk status.
+ //
+
+ pMreplySdata = (PMREPLY_SDATA)&mr->mrply_valid1;
+ break;
+
+
+ default:
+
+ //
+ // Mirroring in progress, get the status of
+ // the disk that has the SCSI status related
+ // to the SCSI device and not to controller.
+ // If both disks have a valid SCSI status, get
+ // the lowest. The following table shows the
+ // possible values of "Valid":
+ //
+ // 00 -> Ok or Sense Data related to the controller.
+ // 70 -> Sense data related to the disk.
+ // F0 -> Sense data related to the disk.
+ // 31 -> Ok (condition met).
+ // 32 -> EFP-2 never returns this value because if
+ // the 1st and 2nd ARP fail, it returns
+ // hardware error using the "00" value.
+ // 34 -> Ok (intermediate good).
+ // 35 -> Ok (intermediate good/condition met).
+ // 36 -> EFP-2 never returns this value because if
+ // the 1st and 2nd ARP fail, it returns
+ // hardware error using the "00" value.
+ //
+
+ if (mr->mrply_valid2 == EFP_NO_ERROR) {
+
+ //
+ // Get the source disk status
+ //
+
+ pMreplySdata = (PMREPLY_SDATA)&mr->mrply_valid1;
+ }
+ else if (mr->mrply_valid1 == EFP_NO_ERROR) {
+
+ //
+ // Get the mirror disk status
+ //
+
+ pMreplySdata = (PMREPLY_SDATA)&mr->mrply_valid2;
+ }
+ else if (mr->mrply_valid1 <= mr->mrply_valid2) {
+
+ //
+ // Get the source disk status
+ //
+
+ pMreplySdata = (PMREPLY_SDATA)&mr->mrply_valid1;
+ }
+ else {
+
+ //
+ // Get the mirror disk status
+ //
+
+ pMreplySdata = (PMREPLY_SDATA)&mr->mrply_valid2;
+ }
+ break;
+ }
+
+ //
+ // Convert EFP error in SRB error.
+ //
+
+ switch(mr->mrply_status) {
+
+ case EFP_AUTOREC_OK:
+ case EFP_CMD_SUCC:
+
+ //
+ // Successful command.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_SUCCESS;
+
+ switch(pMreplySdata->Valid) {
+
+ case EFP_NO_ERROR:
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ case EFP_COND_MET:
+
+ pSrb->ScsiStatus = SCSISTAT_CONDITION_MET;
+ break;
+
+ case EFP_INTER_GOOD:
+
+ pSrb->ScsiStatus = SCSISTAT_INTERMEDIATE;
+ break;
+
+ case EFP_INTER_COND:
+
+ pSrb->ScsiStatus =
+ SCSISTAT_INTERMEDIATE_COND_MET;
+ break;
+
+ default:
+
+ //
+ // The autonomous recovery procedure was
+ // successful and after the recovery the
+ // controller received a CHECK_CONDITION
+ // status (with ARS enabled) and a RECOVERED
+ // error as a reply to a REQUEST SENSE.
+ //
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+ }
+ break;
+
+
+ case EFP_DATA_RUN:
+
+ pSrb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
+
+ //
+ // If overrun error, log it.
+ // The underrun error can be very common on
+ // some devices (example: scanner).
+ //
+
+ if (pSrb->DataTransferLength <= mr->mrply_scsi_len) {
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ DeviceExtension->Adapter_ID[pSrb->PathId],
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ EFP_DATA_RUN
+ );
+ }
+
+ pSrb->DataTransferLength = mr->mrply_scsi_len;
+
+ break;
+
+
+ case EFP_AUTOREC_KO:
+ case EFP_WARN_ERR:
+
+ //
+ // Get the device info pointer.
+ //
+
+ TarLun = GET_QINDEX(pSrb->PathId,
+ pSrb->TargetId,
+ pSrb->Lun);
+
+ pDevInfo = &DeviceExtension->DevicesPresent[TarLun];
+
+ //
+ // Check if the source disk is for the first time
+ // off-line.
+ //
+
+ if (mr->mrply_off_attr == EFP_SOURCE_OFFLINE &&
+ (pDevInfo->SourceDiskState == EfpFtMemberHealthy ||
+ pDevInfo->SourceDiskState == EfpFtMemberMissing)) {
+
+ //
+ // Check if we need to log an error.
+ //
+
+ if (!pDevInfo->KnownError) {
+
+ //
+ // Remember it.
+ //
+
+ pDevInfo->KnownError = TRUE;
+
+ //
+ // Log the error
+ //
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ pSrb->TargetId,
+ pSrb->Lun,
+ SP_INTERNAL_ADAPTER_ERROR,
+ (pDevInfo->SourceDiskState ==
+ EfpFtMemberHealthy ?
+ EFP_SOURCE_OFFLINE_ERROR :
+ EFP_MISSING_SOURCE_ERROR) |
+ mr->mrply_d_off
+ );
+
+ //
+ // Send an error message to the debug port
+ //
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Source disk %s,"
+ " Bus=%x, Tid=%x, Lun=%x,\n",
+ pDevInfo->SourceDiskState ==
+ EfpFtMemberHealthy ?
+ "off-line" : "missing",
+ pSrb->PathId, pSrb->TargetId, pSrb->Lun));
+
+ DebugPrint((1,
+ "Valid=%x, "
+ "Sense=%x, "
+ "Addit=%x, "
+ "Qualif=%x, "
+ "Info=%x.\n",
+ mr->mrply_valid1,
+ mr->mrply_sense1,
+ mr->mrply_addit1,
+ mr->mrply_qualif1,
+ mr->mrply_info1
+ ));
+ }
+
+ //
+ // This is the first time that the source disk
+ // is off-line. Update source state.
+ //
+
+ pDevInfo->SourceDiskState = EfpFtMemberDisabled;
+
+ #if EFP_RETURN_BUSY
+
+ //
+ // Return the general SCSI busy error code.
+ // The ScsiPort driver will retry the command.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+ pSrb->ScsiStatus = SCSISTAT_BUSY;
+
+ #else
+
+ //
+ // Return the SRB bus reset error code.
+ // The class driver will retry the command.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_BUS_RESET;
+
+ #endif
+
+ break;
+
+ } // end if (1st time source disk off line)
+
+ //
+ // Check if the mirror disk is for the first time
+ // off-line.
+ //
+
+ if (mr->mrply_off_attr == EFP_MIRROR_OFFLINE &&
+ (pDevInfo->MirrorDiskState == EfpFtMemberHealthy ||
+ pDevInfo->MirrorDiskState == EfpFtMemberMissing)) {
+
+ //
+ // Check if we need to log an error.
+ //
+
+ if (!pDevInfo->KnownError) {
+
+ //
+ // Remember it.
+ //
+
+ pDevInfo->KnownError = TRUE;
+
+ //
+ // Log the error
+ //
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ pSrb->TargetId,
+ pSrb->Lun,
+ SP_INTERNAL_ADAPTER_ERROR,
+ (pDevInfo->MirrorDiskState ==
+ EfpFtMemberHealthy ?
+ EFP_MIRROR_OFFLINE_ERROR :
+ EFP_MISSING_MIRROR_ERROR) |
+ mr->mrply_d_off
+ );
+
+ //
+ // Send an error message to the debug port
+ //
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Mirror disk %s,"
+ " Bus=%x, Tid=%x, Lun=%x,\n",
+ pDevInfo->MirrorDiskState ==
+ EfpFtMemberHealthy ?
+ "off-line" : "missing",
+ D_OFF_TO_PATH(mr->mrply_d_off),
+ D_OFF_TO_TARGET(mr->mrply_d_off),
+ D_OFF_TO_LUN(mr->mrply_d_off)));
+
+ DebugPrint((1,
+ "Valid=%x, "
+ "Sense=%x, "
+ "Addit=%x, "
+ "Qualif=%x, "
+ "Info=%x.\n",
+ mr->mrply_valid2,
+ mr->mrply_sense2,
+ mr->mrply_addit2,
+ mr->mrply_qualif2,
+ mr->mrply_info2
+ ));
+ }
+
+ //
+ // This is the first time that the mirror disk
+ // is off-line. Update mirroring state.
+ //
+
+ pDevInfo->MirrorDiskState = EfpFtMemberDisabled;
+
+ #if EFP_RETURN_BUSY
+
+ //
+ // Return the general SCSI busy error code.
+ // The ScsiPort driver will retry the command.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+ pSrb->ScsiStatus = SCSISTAT_BUSY;
+
+ #else
+
+ //
+ // Return the SRB bus reset error code.
+ // The class driver will retry the command.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_BUS_RESET;
+
+ #endif
+
+ break;
+
+ } // end if (1st time mirror disk off line)
+
+ //
+ // Send an error message to the debug port
+ //
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: EFP command status error,\n"
+ " Status=%x, Valid=%x.\n",
+ mr->mrply_status, pMreplySdata->Valid));
+
+ //
+ // SRB status error.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+
+ //
+ // SCSI status error.
+ //
+
+ switch(pMreplySdata->Valid) {
+
+ case EFP_DEV_BUSY:
+
+ pSrb->ScsiStatus = SCSISTAT_BUSY;
+ break;
+
+ case EFP_RESV_CONF:
+
+ pSrb->ScsiStatus =
+ SCSISTAT_RESERVATION_CONFLICT;
+ break;
+
+ case EFP_SENSE_INFO:
+ case EFP_SENSE_NO_INFO:
+
+ pSrb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ //
+ // Update the SRB sense data area
+ //
+
+ if (pSrb->SenseInfoBufferLength != 0 &&
+ !(pSrb->SrbFlags &
+ SRB_FLAGS_DISABLE_AUTOSENSE )) {
+
+ //
+ // Initialize the sense area to zero
+ //
+
+ for( Index=0;
+ Index < sizeof(SENSE_DATA);
+ Index++) {
+
+ *((PUCHAR)&Sdata + Index) = 0;
+ }
+
+ Sdata.ErrorCode =
+ pMreplySdata->Valid;
+
+ Sdata.SenseKey =
+ pMreplySdata->Sense;
+
+ Sdata.AdditionalSenseCode =
+ pMreplySdata->Addit;
+
+ Sdata.AdditionalSenseCodeQualifier =
+ pMreplySdata->Qualif;
+
+ Sdata.Information[3]=
+ (UCHAR)pMreplySdata->Info;
+
+ Sdata.Information[2]=
+ (UCHAR)(pMreplySdata->Info >> 8);
+
+ Sdata.Information[1]=
+ (UCHAR)(pMreplySdata->Info >> 16);
+
+ Sdata.Information[0]=
+ (UCHAR)(pMreplySdata->Info >> 24);
+
+ //
+ // Copy the sense data in the SRB
+ //
+
+ ScsiPortMoveMemory(
+ pSrb->SenseInfoBuffer,
+ &Sdata,
+ pSrb->SenseInfoBufferLength
+ );
+
+ //
+ // The sense data area is now ok to read
+ //
+
+ pSrb->SrbStatus |=
+ SRB_STATUS_AUTOSENSE_VALID;
+
+ //
+ // Send info to the debug port
+ //
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: Sense data, "
+ "Valid=%x, "
+ "Sense=%x, "
+ "Addit=%x, "
+ "Qualif=%x, "
+ "Info=%x.\n",
+ pMreplySdata->Valid,
+ pMreplySdata->Sense,
+ pMreplySdata->Addit,
+ pMreplySdata->Qualif,
+ pMreplySdata->Info
+ ));
+
+ } // end if (sense data)
+
+ else {
+
+ //
+ // Send a warning message to debug port
+ //
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Warning, "
+ "sense data is lost.\n"));
+ }
+ break;
+
+ case EFP_NO_ERROR:
+
+ //
+ // Device status is related to autonomous
+ // recovery procedure of the controller.
+ // The real status of the device is unknown.
+ //
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ default:
+
+ //
+ // Unknown error.
+ //
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=%x, Valid=%x ?.\n",
+ mr->mrply_status, pMreplySdata->Valid));
+
+ break;
+
+ }
+ break;
+
+
+ default:
+
+ //
+ // Unknown error.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=%x ?.\n",
+ mr->mrply_status));
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ DeviceExtension->Adapter_ID[pSrb->PathId],
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ mr->mrply_status
+ );
+
+ break;
+
+ } // end switch (reply status)
+
+ } // end if (mirroring reply)
+
+ // ---------------------------------------------------------------
+ //
+ // NORMAL/MAINTENANECE REPLY
+ //
+ // ---------------------------------------------------------------
+
+ else {
+
+#endif // EFP_MIRRORING_ENABLED
+
+
+ if (DeviceExtension->Q_Buf.qnrply.nrply_status == EFP_CMD_SUCC) {
+
+ //
+ // Successful command
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_SUCCESS;
+
+ switch(DeviceExtension->Q_Buf.qnrply.nrply_ex_stat) {
+
+ case EFP_NO_ERROR:
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ case EFP_COND_MET:
+
+ pSrb->ScsiStatus = SCSISTAT_CONDITION_MET;
+ break;
+
+ case EFP_INTER_GOOD:
+
+ pSrb->ScsiStatus = SCSISTAT_INTERMEDIATE;
+ break;
+
+ case EFP_INTER_COND:
+
+ pSrb->ScsiStatus = SCSISTAT_INTERMEDIATE_COND_MET;
+ break;
+
+ default:
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=00, ExStat=%x ?.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_ex_stat));
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ } // end switch
+ }
+
+ else if (DeviceExtension->Q_Buf.qnrply.nrply_status ==
+ EFP_AUTOREC_OK) {
+
+ //
+ // Successful command using the autonomous recovery procedure
+ // (ARP).
+ //
+ // Note that only the EFP-2 controller can return this type
+ // of error code.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_SUCCESS;
+
+ switch(DeviceExtension->Q_Buf.qnrply.nrply_ex_stat) {
+
+ case EFP_NO_ERROR:
+ case EFP_CHK_COND:
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ default:
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=10, ExStat=%x ?.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_ex_stat));
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ } // end switch
+ }
+
+ else if (DeviceExtension->Q_Buf.qnrply.nrply_status ==
+ EFP_WARN_ERR) {
+
+ //
+ // The request completed with an error
+ //
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: EFP command status error,\n"
+ " Status=%x, ExStat=%x.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_status,
+ DeviceExtension->Q_Buf.qnrply.nrply_ex_stat));
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+
+ switch(DeviceExtension->Q_Buf.qnrply.nrply_ex_stat) {
+
+ case EFP_NO_ERROR:
+
+ //
+ // We have automatic request sense enabled,
+ // so the 00 in ex_stat together with status = 01
+ // will be interpreted here as a check condition
+ // returned in the sense key.
+ //
+
+ pSrb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ sensevalid = TRUE;
+
+ pSrb->DataTransferLength =
+ DeviceExtension->Q_Buf.qnrply.nrply_scsi_len;
+ break;
+
+ case EFP_CHK_COND:
+
+ // This should NEVER happen (because ARS enabled)
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=01, ExStat=30: w/ARS ?.\n"));
+
+ pSrb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ pSrb->DataTransferLength =
+ DeviceExtension->Q_Buf.qnrply.nrply_scsi_len;
+ break;
+
+ case EFP_DEV_BUSY:
+
+ pSrb->ScsiStatus = SCSISTAT_BUSY;
+ break;
+
+ case EFP_RESV_CONF:
+
+ pSrb->ScsiStatus = SCSISTAT_RESERVATION_CONFLICT;
+ break;
+
+ case EFP_ABORT_CMD:
+
+ pSrb->ScsiStatus = SCSISTAT_COMMAND_TERMINATED;
+ break;
+
+ default:
+
+ //
+ // Unknown error.
+ //
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=01, ExStat=%x ?.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_ex_stat));
+
+ break;
+
+ } // end switch
+
+ //
+ // Check if we need to copy the sense data to the SRB.
+ //
+
+ if (pSrb->SenseInfoBufferLength != 0 &&
+ !(pSrb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) &&
+ sensevalid) {
+
+ //
+ // copy sense data information to the SRB SenseInfoBuffer
+ //
+
+ ScsiPortMoveMemory(pSrb->SenseInfoBuffer,
+ &DeviceExtension->Q_Buf.qnrply.nrply_sense,
+ pSrb->SenseInfoBufferLength);
+
+ pSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
+
+ DebugPrint((2,
+ "SenseKey: %x, AdditCode: %x, AdditQual: %x.\n",
+ ((PSENSE_DATA)pSrb->SenseInfoBuffer)->SenseKey,
+ ((PSENSE_DATA)pSrb->SenseInfoBuffer)->AdditionalSenseCode,
+ ((PSENSE_DATA)
+ pSrb->SenseInfoBuffer)->AdditionalSenseCodeQualifier));
+
+ }
+ else if (sensevalid) {
+ DebugPrint((1,
+ "OliEsc2Interrupt: Warning, sense data is lost.\n"));
+ }
+ }
+
+ else if (DeviceExtension->Q_Buf.qnrply.nrply_status ==
+ EFP_AUTOREC_KO) {
+
+ //
+ // The request completed with an error,
+ // the autonomous recovery procedure (ARP) was not
+ // successful.
+ //
+ // Note that only the EFP-2 controller can return this type
+ // of error code.
+ //
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: EFP command status error,\n"
+ " Status=%x, ExStat=%x.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_status,
+ DeviceExtension->Q_Buf.qnrply.nrply_ex_stat));
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+
+ switch(DeviceExtension->Q_Buf.qnrply.nrply_ex_stat) {
+
+ case EFP_NO_ERROR:
+
+ //
+ // Unknown cause.
+ //
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ case EFP_CHK_COND:
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+ break;
+
+ case EFP_DEV_BUSY:
+
+ pSrb->ScsiStatus = SCSISTAT_BUSY;
+ break;
+
+ case EFP_RESV_CONF:
+
+ pSrb->ScsiStatus = SCSISTAT_RESERVATION_CONFLICT;
+ break;
+
+ default:
+
+ //
+ // Unknown error.
+ //
+
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=18, ExStat=%x ?.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_ex_stat));
+
+ break;
+
+ } // end switch
+ }
+
+ else {
+
+ //
+ // The request completed with an error.
+ //
+
+ DebugPrint((2,
+ "OliEsc2Interrupt: EFP command status error,\n"
+ " Status=%x, ExStat=%x.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_status,
+ DeviceExtension->Q_Buf.qnrply.nrply_ex_stat));
+
+
+ // note that no sense data is returned for these cases.
+
+ switch (DeviceExtension->Q_Buf.qnrply.nrply_status) {
+
+ case EFP_SEL_TIMEOUT:
+
+ pSrb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT;
+ break;
+
+ case EFP_DATA_RUN:
+
+ pSrb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
+
+ //
+ // If overrun error, log it.
+ // The underrun error can be very common on
+ // some devices (example: scanner).
+ //
+
+ if (pSrb->DataTransferLength <=
+ DeviceExtension->Q_Buf.qnrply.nrply_scsi_len) {
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ DeviceExtension->Adapter_ID[pSrb->PathId],
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ EFP_DATA_RUN
+ );
+ }
+
+ pSrb->DataTransferLength =
+ DeviceExtension->Q_Buf.qnrply.nrply_scsi_len;
+
+ break;
+
+ case EFP_BUS_FREE:
+
+ pSrb->SrbStatus = SRB_STATUS_UNEXPECTED_BUS_FREE;
+ break;
+
+ case EFP_PHASE_ERR:
+
+ pSrb->SrbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
+ break;
+
+ //
+ // NOTE: The following error codes are only returned
+ // by the ESC-2 controller.
+
+ case PARITY_ERROR:
+
+ //
+ // This is a severe error.
+ // The controller is now shut down.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_PARITY_ERROR;
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ DeviceExtension->Adapter_ID[pSrb->PathId],
+ 0,
+ SP_BUS_PARITY_ERROR,
+ 0
+ );
+
+ break;
+
+ case BUS_RESET_BY_TARGET:
+
+ //
+ // No error logging.
+ //
+ // Return bus reset error, because the bus has been
+ // reset.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_BUS_RESET;
+ break;
+
+ case PROTOCOL_ERROR:
+
+ //
+ // The ESC-2 resets the bus when it detects this
+ // type of error.
+ // Return bus reset error.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_BUS_RESET;
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ DeviceExtension->Adapter_ID[pSrb->PathId],
+ 0,
+ SP_PROTOCOL_ERROR,
+ 0
+ );
+
+ break;
+
+ case UNEXPECTED_PHASE_CHANGE:
+
+ //
+ // This is a severe error.
+ // The controller is now shut down.
+ //
+
+ case AUTO_REQUEST_SENSE_FAILURE:
+ case PARITY_ERROR_DURING_DATA_PHASE:
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ DeviceExtension->Adapter_ID[pSrb->PathId],
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ DeviceExtension->Q_Buf.qnrply.nrply_status
+ );
+
+ break;
+
+ default:
+
+ //
+ // Unknown error.
+ //
+
+ pSrb->SrbStatus = SRB_STATUS_ERROR;
+ pSrb->ScsiStatus = SCSISTAT_GOOD;
+
+ DebugPrint((1,
+ "OliEsc2Interrupt: Status=%x ?.\n",
+ DeviceExtension->Q_Buf.qnrply.nrply_status));
+
+ ScsiPortLogError(
+ DeviceExtension,
+ NULL,
+ pSrb->PathId,
+ DeviceExtension->Adapter_ID[pSrb->PathId],
+ 0,
+ SP_INTERNAL_ADAPTER_ERROR,
+ DeviceExtension->Q_Buf.qnrply.nrply_status
+ );
+
+ break;
+
+ } // end switch
+ } // end status check
+
+#if EFP_MIRRORING_ENABLED
+
+ } // end if (!mirroring reply)
+
+#endif // EFP_MIRRORING_ENABLED
+
+
+ //
+ // Decrement the pending requests counter for this
+ // (targetId, LUN) pair
+ //
+
+ LuExtension = ScsiPortGetLogicalUnit(DeviceExtension,
+ pSrb->PathId,
+ pSrb->TargetId,
+ pSrb->Lun);
+
+ ASSERT(LuExtension);
+ LuExtension->NumberOfPendingRequests--;
+ ASSERT (LuExtension->NumberOfPendingRequests >= 0);
+
+ //
+ // Call notification routine for the SRB.
+ //
+
+ ScsiPortNotification(RequestComplete, (PVOID)DeviceExtension, pSrb);
+
+ } // end while {w/EFP command completion processing}
+
+ } // end if (command complete interrupt)
+
+ //
+ // Enable interrupts
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBellMask,
+ INTERRUPTS_ENABLE);
+
+ return(TRUE);
+
+} // end OliEsc2Interrupt()
+
+
+BOOLEAN
+OliEsc2ResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+)
+
+/*++
+
+Routine Description:
+
+ Reset ESC-2 SCSI adapter and SCSI bus.
+
+Arguments:
+
+ HwDeviceExtension Device extension for this driver
+ PathId SCSI Bus path ID (always 0 for the ESC-2)
+
+Return Value:
+
+ TRUE if reset and re-initialization of EFP queues appears successful.
+ FALSE otherwise.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION DeviceExtension;
+ PEISA_CONTROLLER eisaController;
+ PLU_EXTENSION LuExtension;
+ UCHAR Lun;
+ UCHAR TargetId;
+ UCHAR Bus;
+
+ DebugPrint((2,"OliEsc2ResetBus: Reset SCSI bus.\n"));
+
+ //
+ // Get the ESC-2 registers' base address
+ //
+
+ DeviceExtension = HwDeviceExtension;
+ eisaController = DeviceExtension->EisaController;
+
+ //
+ // If the reset is already in progress, return TRUE.
+ // This can only happen when this routine is called directly
+ // by the above layer (scsiport.sys) to reset bus #1 and #2.
+ //
+
+ if (DeviceExtension->ResetInProgress) {
+
+ DebugPrint((2,"OliEsc2ResetBus: The reset is already in progess.\n"));
+ return TRUE;
+ }
+
+ //
+ // Reset the controller.
+ //
+
+ OliEsc2ResetAdapter(DeviceExtension);
+
+ //
+ // a) complete all outstanding requests.
+ // b) clear pending request counters.
+ // c) send a "reset detected" notification.
+ //
+
+ for (Bus=0; Bus < DeviceExtension->NumberOfBuses; Bus++) {
+
+ //
+ // Complete all outstanding requests with SRB_STATUS_BUS_RESET
+ //
+
+ ScsiPortCompleteRequest(DeviceExtension,
+ Bus,
+ (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,
+ Bus,
+ TargetId,
+ Lun);
+
+ if (LuExtension != NULL) {
+ LuExtension->NumberOfPendingRequests = 0;
+ }
+ }
+ }
+
+ //
+ // Send a "reset detected" notification.
+ //
+
+ ScsiPortNotification(ResetDetected, DeviceExtension, Bus);
+
+ }
+
+ //
+ // All done
+ //
+
+ return TRUE;
+
+} // end OliEsc2ResetBus
+
+
+
+BOOLEAN
+ReadEsc2ConfigReg(
+ IN PEISA_CONTROLLER EisaController,
+ IN UCHAR ConfigReg,
+ OUT PUCHAR ConfigByteInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Reads one of the ESC-2's 8-bit configuration registers, returning the
+ information stored there. Should be called at system initialization
+ time only, as it uses polling.
+
+Arguments:
+
+ EisaController Pointer to the adapter-info structure for this driver
+ ConfigReg configuration register to be read (eg. 2 = IRQ config)
+ ConfigByteInfo the information obtained from the configuration register
+
+Return Value:
+
+ TRUE Success (and returns the configuration byte in ConfigByteInfo)
+ FALSE Failure
+
+--*/
+
+{
+
+ BOOLEAN Success = FALSE; // Return value
+ UCHAR IntMask; // Current System Doorbell interrupt mask value
+ ULONG i; // Auxiliary variable
+ BOOLEAN GotInt = FALSE;
+
+ //
+ // Get the current System Doorbell Interrupt Mask
+ //
+ // KMK: We never used to do this (and the re-write of the mask at end)
+ //
+
+ IntMask = ScsiPortReadPortUchar(&EisaController->SystemDoorBellMask);
+
+ //
+ // Disable ESC-1 interrupts
+ //
+ // KMK: We used to use STI_CLI(), to let pending interrupts occur
+ //
+
+ ScsiPortWritePortUchar(&EisaController->SystemDoorBellMask,
+ INTERRUPTS_DISABLE);
+
+ //
+ // Gain semaphore 0
+ //
+
+ if (!GainSemaphore0(EisaController)) {
+ // KMK re-enable interrupts
+ Success = FALSE;
+ } else { // successfully gained semaphore
+
+ ScsiPortWritePortUchar(&EisaController->InTypeService, 0);
+ ScsiPortWritePortUchar(&EisaController->InParm1, GET_CONF_INFO);
+ ScsiPortWritePortUchar(&EisaController->InParm2, ConfigReg);
+
+ //ScsiPortWritePortUlong(&EisaController->InAddress, Address);
+
+ //
+ // Send an attention interrupt to the adapter.
+ //
+
+ ScsiPortWritePortUchar(&EisaController->LocalDoorBell, ESC_INT_BIT);
+
+ //
+ // We would re-enable interrupts here.
+ //
+ //ENAB();
+
+ // now loop, waiting for the board to "interrupt" us (we don't yet
+ // have an interrupt line set, so we will poll).
+ // KMK: Note, this is shorter than the wait we used to use!
+
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&EisaController->SystemDoorBell) &
+ ESC_INT_BIT) { // was ESC_INTERRUPT
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ // Re-enable the interrupts here.
+ DebugPrint((1,
+ "No HP interrupt after read config byte attempt.\n"));
+ Success = FALSE;
+ } else { // did get the interrupt
+
+ //
+ // acknowledge the host interrupt, resetting the interrupt register
+ // (was ESC_ACK)
+ //
+
+ ScsiPortWritePortUchar(&EisaController->SystemDoorBell,
+ ESC_INT_BIT);
+
+ //
+ // read the mailbox registers. If the outgoing status was good,
+ // we read the value of the configuration register we queried.
+ //
+
+ if (ScsiPortReadPortUshort((PUSHORT)&EisaController->OutReserved2)
+ != 0) {
+ Success = FALSE;
+ } else {
+
+ Success = TRUE;
+
+ *ConfigByteInfo =
+ ScsiPortReadPortUchar(&EisaController->OutReserved4);
+
+ // reset outgoing semaphore 1
+ ScsiPortWritePortUchar(&EisaController->ResultSemaphore, 0);
+
+ } // end of else {successful status from read conf reg command}
+
+ } // end of else {did get the interrupt}
+
+ } // end of else {successfully gained the semaphore}
+
+ //
+ // Restore the original interrupt mask
+ //
+
+ ScsiPortWritePortUchar(&EisaController->SystemDoorBellMask, IntMask);
+
+ return(Success); // if an error occurred, Success holds FALSE
+
+} // end ReadEsc2ConfigReg
+
+
+
+BOOLEAN
+OliEsc2IrqRegToIrql(
+ IN UCHAR IrqReg,
+ OUT PUCHAR pIrql
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts the IRQ configuration register value in
+ the corrisponding IRQ level.
+
+Arguments:
+
+ IrqReg IRQ configuration register value.
+ pIrql Location where to store the IRQ level.
+
+Return Value:
+
+ TRUE Success
+ FALSE Failure (IRQ config reg was invalid)
+
+--*/
+
+{
+ BOOLEAN Success = TRUE;
+
+ switch(IrqReg) {
+
+ case 0: *pIrql = 11;
+ break;
+
+ case 1: *pIrql = 10;
+ break;
+
+ case 2: *pIrql = 5;
+ break;
+
+ case 3: *pIrql = 15;
+ break;
+
+ default: Success = FALSE;
+ break;
+
+ }
+
+ return Success;
+
+} // end of OliEsc2IrqRegToIrql
+
+
+
+BOOLEAN
+RegisterEfpQueues(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+
+ This routine re-initializes the EFP interface, setting up the
+ queues descriptor, plus the mailbox queue and device queues.
+ It assumes that information on the attached SCSI devices has
+ already been obtained by the Get Configuration call, and is
+ stored in the Noncached Extension.
+
+Arguments:
+
+ DeviceExtension Pointer to the device extension for this driver.
+
+Return Value:
+
+ Returns TRUE if queues initialization completed successfully.
+ Returns FALSE and logs an error if initialization fails.
+
+--*/
+
+{
+ PEISA_CONTROLLER eisaController;
+ PNONCACHED_EXTENSION pNoncachedExt;
+ UCHAR TarLun, EfpMsg;
+ ULONG i, length;
+ BOOLEAN GotInt = FALSE;
+ PGET_CONF Info;
+ PCOMMAND_QUEUE qPtr;
+
+ eisaController = DeviceExtension->EisaController;
+ pNoncachedExt = DeviceExtension->NoncachedExt;
+
+ //
+ // With the information obtained from Get Information and
+ // Get Configuration, build a new queues descriptor, including
+ // device command queues. Allocate queues for all devices, but
+ // fill in Get Configuration device information only into device
+ // command queue descriptor bodies for existing devices.
+ //
+
+ //
+ // Interrupts have already been disabled on entry (see OliEsc2FindAdapter).
+ // Polling is possible through enabling the ESC-1 HP and EFP interface
+ // in the System Doorbell Mask.
+ //
+
+ //
+ // 1. Issue EFP_SET command via I/O instruction 'TYPE SERVICE' to the
+ // BMIC mailbox registers.
+ //
+
+ //
+ // Issue EFP_Set command to register new Queues Descriptor.
+ //
+
+ if (!GainSemaphore0(eisaController)) {
+ return(FALSE);
+ }
+
+ // if got the semaphore, place IRQ parameter in mailbox register 1
+ ScsiPortWritePortUchar(&eisaController->InParm1,
+ DeviceExtension->IRQ_In_Use);
+
+ // output a TYPE SERVICE request: 'efp_set'
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_SET);
+
+ // set bit 1 of the local doorbell register to generate interrupt request
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ // Wait for controller to respond.
+ GotInt = FALSE; // re-initialize GotInt
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell)
+ & EFP_TYPE_MSG) {
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ DebugPrint((1, "RegisterEfpQueues: No interrupt after EFP_SET.\n"));
+ return(FALSE);
+ }
+ else {
+ DebugPrint((4,
+ "RegisterEfpQueues: Set, interrupt after %ld us.\n",
+ i*WAIT_INT_INTERVAL));
+
+ EfpMsg = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+ if (EfpMsg != M_INIT_DIAG_OK) {
+ DebugPrint((1,
+ "RegisterEfpQueues: INIT_DIAG_OK not received after EFP_SET.\n"));
+ if (EfpMsg == M_ERR_INIT) {
+ DebugPrint((1,
+ "RegisterEfpQueues: M_ERR_INIT received after EFP_SET.\n"));
+ }
+ else {
+ DebugPrint((1,
+ "RegisterEfpQueues: Error after EFP_SET: %x hex.\n",
+ EfpMsg));
+ }
+ return(FALSE);
+ }
+ }
+
+ // reset bit 1 of the system doorbell register to clear request
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ // clear semaphore 1 after reading TYPE_MSG (RELEASE_SEM1)
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, 0);
+
+
+ //
+ // 2. Build queues descriptor.
+ //
+
+ //
+ // First, reinitialize queue-related fields in HW_DEVICE_EXTENSION
+ // and NONCACHED_EXTENSION.
+ //
+
+ DeviceExtension->Reply_Q_Full_Flag = 0;
+ DeviceExtension->Reply_Q_Get = 0;
+ DeviceExtension->RQ_In_Process = 0;
+ pNoncachedExt->Command_Qs[0].Cmd_Q_Get = 0;
+ pNoncachedExt->Command_Qs[0].Cmd_Q_Put = 0;
+
+ for (i = 0; i < HA_QUEUES; i++) { // device q's + mailbox q
+ DeviceExtension->Q_Full_Map[i] = 0;
+ }
+
+ for (i = 0; i < REPLY_Q_ENTRIES; i++) {
+ pNoncachedExt->Reply_Q[i].qnrply.nrply_flag = 0;
+ }
+
+ // Set up new queues descriptor header
+
+ // set up Queues Descriptor header
+
+ pNoncachedExt->QD_Head.qdh_maint = 0; // normal environment
+ pNoncachedExt->QD_Head.qdh_n_cmd_q =
+ (DeviceExtension->TotalAttachedDevices + 1);
+ pNoncachedExt->QD_Head.qdh_type_reply = 0; // int after 1+
+ pNoncachedExt->QD_Head.qdh_reserved1 = 0;
+ pNoncachedExt->QD_Head.qdh_reply_q_addr =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL, // No SRB
+ &pNoncachedExt->Reply_Q,
+ &length));
+ pNoncachedExt->QD_Head.qdh_n_ent_reply = REPLY_Q_ENTRIES;
+ pNoncachedExt->QD_Head.qdh_reserved2 = 0;
+
+ // set up Queues Descriptor body for mailbox queue
+
+ pNoncachedExt->QD_Bodies[0].qdb_scsi_level = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_channel = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_ID = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_LUN = 0xFF;
+ pNoncachedExt->QD_Bodies[0].qdb_n_entry_cmd = COMMAND_Q_ENTRIES;
+ pNoncachedExt->QD_Bodies[0].qdb_notfull_int = 1;
+ pNoncachedExt->QD_Bodies[0].qdb_no_ars = 0; // ESC2/EFP2
+ pNoncachedExt->QD_Bodies[0].qdb_timeout = 0;
+ pNoncachedExt->QD_Bodies[0].qdb_cmd_q_addr =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL, // no SRB
+ &pNoncachedExt->Command_Qs[0],
+ &length));
+ pNoncachedExt->QD_Bodies[0].qdb_reserved = 0;
+
+ // store the controller mailbox info in common area
+
+ DeviceExtension->DevicesPresent[0].present = TRUE;
+ DeviceExtension->DevicesPresent[0].qnumber = 0;
+ DeviceExtension->DevicesPresent[0].qPtr = &pNoncachedExt->Command_Qs[0];
+
+
+ // set up Queues Descriptor bodies for device queues
+
+ //
+ // Note that there will be garbage in QD_Bodies entries for non-existant
+ // TarLuns. We must, then, always check if a device/queue exists before
+ // trying to send a command to it.
+ //
+
+ for (i = 1; i <= DeviceExtension->TotalAttachedDevices; i++) {
+
+ Info = &pNoncachedExt->GetConfigInfo[i-1] ;
+
+ DebugPrint((1, "RegisterEfpQueues: channel=%d id=%d lun=%d.\n",
+ Info->gc_channel - 1,
+ Info->gc_id,
+ Info->gc_lun));
+
+ TarLun = GET_QINDEX(((Info->gc_channel)-1), Info->gc_id, Info->gc_lun);
+
+ qPtr = &pNoncachedExt->Command_Qs[i];
+
+ DeviceExtension->DevicesPresent[TarLun].present = TRUE;
+ DeviceExtension->DevicesPresent[TarLun].qnumber = (UCHAR)i;
+ DeviceExtension->DevicesPresent[TarLun].qPtr = qPtr ;
+
+ pNoncachedExt->QD_Bodies[i].qdb_scsi_level = Info->gc_scsi_level;
+
+ // Note: Discussed this with John Hanel. We believe it is unnecessary
+ // to ensure that the device's reported SCSI protocol level does not
+ // exceed the controller's level. If otherwise, change this code.
+
+ pNoncachedExt->QD_Bodies[i].qdb_channel = Info->gc_channel;
+ pNoncachedExt->QD_Bodies[i].qdb_ID = Info->gc_id;
+ pNoncachedExt->QD_Bodies[i].qdb_LUN = Info->gc_lun;
+
+ pNoncachedExt->QD_Bodies[i].qdb_n_entry_cmd = COMMAND_Q_ENTRIES;
+ pNoncachedExt->QD_Bodies[i].qdb_notfull_int = 1;
+ pNoncachedExt->QD_Bodies[i].qdb_no_ars = 0;
+ pNoncachedExt->QD_Bodies[i].qdb_timeout = 0;
+ pNoncachedExt->QD_Bodies[i].qdb_cmd_q_addr =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL, // no SRB
+ qPtr,
+ &length));
+ pNoncachedExt->QD_Bodies[i].qdb_reserved = 0;
+
+ qPtr->Cmd_Q_Get = 0;
+ qPtr->Cmd_Q_Put = 0;
+
+ }
+
+
+ //
+ // 3. Issue EFP_Start.
+ //
+
+ if (!GainSemaphore0(eisaController)) {
+ return(FALSE);
+ }
+
+ // if got the semaphore, output physical address of the queues descriptor
+ // to mailbox registers 1 - 4.
+ ScsiPortWritePortUlong((PULONG)&eisaController->InParm1,
+ DeviceExtension->QueuesDescriptor_PA);
+
+ // output a TYPE SERVICE request to 'efp_start'
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_START);
+
+ // set bit 1 of local doorbell register to generate an interrupt request
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ // Wait for controller to respond.
+ GotInt = FALSE; // re-initialize GotInt
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
+ EFP_TYPE_MSG) { // was EFP_MSG_INT()
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ DebugPrint((1,
+ "RegisterEfpQueues: No interrupt after EFP_START.\n"));
+ return(FALSE);
+ }
+ else {
+ DebugPrint((4,
+ "RegisterEfpQueues: Start, interrupt after %ld us.\n",
+ i*WAIT_INT_INTERVAL));
+
+ EfpMsg = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+ if (EfpMsg != M_INIT_DIAG_OK) {
+ DebugPrint((1,
+ "RegisterEfpQueues: INIT_DIAG_OK not received after EFP_START.\n"));
+ if (EfpMsg == M_ERR_INIT) {
+ DebugPrint((1,
+ "RegisterEfpQueues: M_ERR_INIT received after EFP_START.\n"));
+ }
+ else {
+ DebugPrint((1,
+ "RegisterEfpQueues: Error after EFP_START: %x hex.\n",
+ EfpMsg));
+ }
+ return(FALSE);
+ }
+ }
+
+ // reset bit 1 of the system doorbell register to clear request
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ // clear semaphore 1 after reading TYPE_MSG (RELEASE_SEM1)
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, 0); // release sem
+
+ return(TRUE);
+
+} // end of RegisterEfpQueues()
+
+
+
+VOID
+BuildSgl(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a scatter/gather descriptor list for the EFP
+ command structure (SSG or ESG).
+
+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
+ PEFP_SGL pEFP; // EFP command pointer
+ PQ_ENTRY pCQCmd; // EFP command queue structure
+
+ PVOID dataPointer; // Pointer to the data buffer to send
+ USHORT 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
+ PSG_LIST sgl; // Virtual SGL address
+
+
+ DebugPrint((3,"OliEsc2BuildSgl: Enter routine.\n"));
+
+ //
+ // Initialize some variables
+ //
+
+ dataPointer = Srb->DataBuffer;
+ bytesLeft = Srb->DataTransferLength;
+ pEFP = Srb->SrbExtension;
+ pCQCmd = &pEFP->EfpCmd;
+ sgl = &pEFP->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, "OliEsc2BuildSgl: Data buffer %lx.\n", dataPointer));
+
+ //
+ // Get physical address and length of contiguous
+ // physical buffer.
+ //
+
+ physicalAddress = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ Srb,
+ dataPointer,
+ &length));
+
+ DebugPrint((3, "OliEsc2BuildSgl: Physical address %lx,\n",
+ physicalAddress));
+ DebugPrint((3, "OliEsc2BuildSgl: Data length %lx,\n", length));
+ DebugPrint((3, "OliEsc2BuildSgl: 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);
+
+ // Save the descriptor count in the EFP structure for performance tuning
+
+ pEFP->SGCount = descriptorCount;
+
+ DebugPrint((3, "OliEsc2BuildSgl: SGCount = >>>>>> %d.\n",
+ descriptorCount));
+
+ //
+ // The short/long scatter gather commands are not used because...
+ // a) These commands can be used only for disk devices
+ // (ESC-2 supports all SCSI devices).
+ // b) The miniport need to make some assumtions on the device block
+ // length (the miniport doesn't have enough knowledge to make them).
+ //
+
+ DebugPrint((3,"OliEsc2BuildSgl: Send << ESG >> command.\n"));
+
+ if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
+
+ //
+ // Adapter to system transfer
+ //
+ ((PEXTENDED_SG)pCQCmd)->esg_cmd_type = ESG_WRITE;
+
+ } else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
+
+ //
+ // System to adapter transfer
+ //
+
+ ((PEXTENDED_SG)pCQCmd)->esg_cmd_type = ESG_READ;
+
+ }
+
+ //
+ // Copy the Command Descriptor Block (CDB) into the EFP command
+ //
+
+ ScsiPortMoveMemory(&pCQCmd->qncmd.ncmd_cdb, Srb->Cdb, Srb->CdbLength);
+
+ //
+ // Write SGL length to Short Scatter Gather command.
+ //
+
+ ((PEXTENDED_SG)pCQCmd)->esg_cdb_l = Srb->CdbLength;
+ ((PEXTENDED_SG)pCQCmd)->esg_lb = descriptorCount * sizeof(SG_DESCRIPTOR);
+
+ DebugPrint((3,"OliEsc2BuildSgl: SGL length is %d.\n",
+ descriptorCount * sizeof(SG_DESCRIPTOR) ));
+
+ //
+ // Write SGL address to EFP structure.
+ //
+
+ ((PEXTENDED_SG)pCQCmd)->esg_address = physicalSgl;
+
+ DebugPrint((3,"OliEsc2BuildSgl: SGL address is %lx\n", sgl));
+
+ return;
+
+} // end BuildSgl()
+
+
+
+VOID
+OliEsc2ResetAdapter(
+ 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;
+ PNONCACHED_EXTENSION pNoncachedExt;
+ UCHAR intpending;
+ UCHAR EfpMsg;
+ ULONG Delay;
+ ULONG i;
+ BOOLEAN Error = FALSE;
+ UCHAR Bus;
+
+ DeviceExtension = Context;
+ eisaController = DeviceExtension->EisaController;
+ pNoncachedExt = DeviceExtension->NoncachedExt;
+
+ //
+ // 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.
+ //
+ // Re-register EFP queues:
+ //
+ // Phase 3: Waiting for the EFP_SET command to complete.
+ // Phase 4: Waiting for the EFP_START command to complete.
+ //
+
+ switch(DeviceExtension->ResetInProgress) {
+
+ //
+ // Phase 0: Reset the controller.
+ //
+
+ case 0:
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Disable interrupts.
+ //
+
+ ScsiPortWritePortUchar( &eisaController->SystemIntEnable,
+ INTERRUPTS_DISABLE );
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Reset controller.
+ //
+
+ if (DeviceExtension->Esc2 == TRUE) {
+
+ DebugPrint((2,"OliEsc2ResetAdapter: ESC reset type.\n"));
+
+ DebugPrint((3, "OliEsc2ResetAdapter: "
+ "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(&eisaController->OutReserved2,
+ (UCHAR)(~DIAGNOSTICS_OK_NO_CONFIG_RECEIVED));
+
+ //
+ // Reset ESC-2 and SCSI bus.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell,
+ ADAPTER_RESET);
+
+ //
+ // Request a timer call to complete the reset.
+ //
+
+ DeviceExtension->ResetTimerCalls = ESC_RESET_LOOPS + 1;
+ Delay = ESC_RESET_DELAY;
+ }
+ else {
+
+ DebugPrint((2,"OliEsc2ResetAdapter: EFP reset type.\n"));
+
+ DebugPrint((3, "OliEsc2ResetAdapter: "
+ "Phase 1 (reset adapter) max time = %ld us.\n",
+ EFP_RESET_DELAY + EFP_RESET_INTERVAL * EFP_RESET_LOOPS
+ ));
+
+ //
+ // Try to acquire the semaphore (note that this is not necessary).
+ //
+
+ if (!GainSemaphore0(eisaController)) {
+ DebugPrint((1,
+ "OliEsc2ResetAdapter: Warning, the semaphore is busy, "
+ "issuing the reset anyway.\n"));
+ }
+
+ //
+ // Initialize the input parameters for the reset.
+ //
+
+ for (i=0; i < CFG_REGS_NUMBER; i++) {
+
+ //
+ // The controller will re-initialize the board using
+ // these configuration registers values.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->InTypeService + i,
+ DeviceExtension->CfgRegs[i]);
+ }
+
+ //
+ // Reset EFP-2 and SCSI buses.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell,
+ ADAPTER_CFG_RESET);
+
+ //
+ // Request a timer call to complete the reset.
+ //
+
+ DeviceExtension->ResetTimerCalls = EFP_RESET_LOOPS + 1;
+ Delay = EFP_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 ESC2_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:
+
+ if (DeviceExtension->Esc2 == TRUE) {
+
+ //
+ // Note that after a reset the LOW byte of the ESC-2 Status
+ // register is loaded with the diagnostics result code.
+ //
+
+ if (ScsiPortReadPortUchar(&eisaController->OutReserved2) !=
+ DIAGNOSTICS_OK_NO_CONFIG_RECEIVED) {
+
+ Delay = ESC_RESET_INTERVAL;
+ break;
+ }
+
+ DebugPrint((1,
+ "OliEsc2ResetAdapter: Reset bus succeeded after %ld us.\n",
+ ESC_RESET_DELAY + ESC_RESET_INTERVAL *
+ (ESC_RESET_LOOPS - DeviceExtension->ResetTimerCalls)
+ ));
+ }
+ else {
+
+ //
+ // The following code allows the next revision of the EFP firmware
+ // to use more time during the reset phase.
+ //
+
+ if (ScsiPortReadPortUchar(&eisaController->LocalDoorBell) &
+ ADAPTER_CFG_RESET) {
+
+ Delay = EFP_RESET_INTERVAL;
+ break;
+ }
+
+ DebugPrint((1,
+ "OliEsc2ResetAdapter: Reset bus succeeded after %ld us.\n",
+ EFP_RESET_DELAY + EFP_RESET_INTERVAL *
+ (EFP_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 (ESC-2 controllers).
+ //
+
+ 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.
+ //
+
+ intpending = ScsiPortReadPortUchar(&eisaController->SystemDoorBell);
+
+ //
+ // Check if any ESC-1 type interrupt
+ //
+
+ if (intpending & ESC_INT_BIT) {
+
+ DebugPrint((3, "OliEsc2ResetAdapter: "
+ "The HP interrupt was pending.\n"));
+
+ //
+ // Acknowledge the interrupt.
+ //
+ // No need to unlock semaphore 1, because the controller already
+ // unlocks it during the reset phase.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
+ ESC_INT_BIT);
+ }
+
+ //
+ // Check if any EFP command complete interrupt
+ //
+
+ if (intpending & EFP_CMD_COMPL) {
+
+ DebugPrint((3, "OliEsc2ResetAdapter: "
+ "The EFP command complete interrupt was pending.\n"));
+
+ //
+ // Acknowledge interrupt.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
+ EFP_ACK_INT);
+ }
+
+ //
+ // Check if any EFP_TYPE_MSG interrupt
+ //
+
+ if (intpending & EFP_TYPE_MSG) {
+
+ DebugPrint((3, "OliEsc2ResetAdapter: "
+ "The EFP type message interrupt was pending.\n"));
+
+ //
+ // Acknowledge the interrupt
+ //
+ // No need to unlock semaphore 1, because the controller already
+ // unlocks it during the reset phase.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell,
+ EFP_ACK_MSG);
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Re-initialize the EFP queues, using information stored
+ // from the init-time.
+ //
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Issue EFP_SET command via I/O instruction 'TYPE SERVICE' to the
+ // BMIC mailbox registers.
+ //
+
+ DebugPrint((3, "OliEsc2ResetAdapter: "
+ "Phase 3 (EFP_SET command) max time = %ld us.\n",
+ TIMER_WAIT_INT_INTERVAL * TIMER_WAIT_INT_LOOPS
+ ));
+
+ if (!GainSemaphore0(eisaController)) {
+
+ Error = TRUE;
+ break;
+ }
+
+ //
+ // Place IRQ parameter in mailbox register 1.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->InParm1,
+ DeviceExtension->IRQ_In_Use);
+
+ //
+ // output a TYPE SERVICE request: 'efp_set'.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_SET);
+
+ //
+ // set bit 1 of the local doorbell register to generate interrupt
+ // request.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ //
+ // Request a timer call to continue the re-initialization phase.
+ //
+
+ DeviceExtension->ResetTimerCalls = TIMER_WAIT_INT_LOOPS;
+ Delay = TIMER_WAIT_INT_INTERVAL;
+ DeviceExtension->ResetInProgress++;
+ break;
+
+ //
+ // Phase 3: Waiting for the EFP_SET command to complete.
+ //
+
+ case 3:
+
+ //
+ // Check if a message interrupt is pending.
+ //
+
+ intpending = ScsiPortReadPortUchar(&eisaController->SystemDoorBell);
+
+ if ( !(intpending & EFP_TYPE_MSG)) {
+
+ Delay = TIMER_WAIT_INT_INTERVAL;
+ break;
+ }
+
+ //
+ // Check the command result.
+ //
+
+ EfpMsg = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+
+ if (EfpMsg != M_INIT_DIAG_OK) {
+
+ DebugPrint((1,
+ "OliEsc2ResetAdapter: Error after EFP_SET: %x hex.\n",
+ EfpMsg));
+
+ Error = TRUE;
+ break;
+ }
+
+ //
+ // reset bit 1 of the system doorbell register to clear request
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ //
+ // clear semaphore 1 after reading TYPE_MSG (RELEASE_SEM1)
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, 0);
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Reinitialize the reply queue and the associated variables.
+ //
+
+ for (i = 0; i < REPLY_Q_ENTRIES; i++) {
+ pNoncachedExt->Reply_Q[i].qnrply.nrply_flag = 0;
+ }
+
+ DeviceExtension->Reply_Q_Full_Flag = 0;
+ DeviceExtension->Reply_Q_Get = 0;
+ DeviceExtension->RQ_In_Process = 0;
+
+ //
+ // Reinitialize the command queues and the associated structures.
+ //
+
+ for (i = 0; i <= DeviceExtension->TotalAttachedDevices; i++) {
+ pNoncachedExt->Command_Qs[i].Cmd_Q_Get = 0;
+ pNoncachedExt->Command_Qs[i].Cmd_Q_Put = 0;
+ }
+
+ for (i = 0; i < HA_QUEUES; i++) { // device q's + mailbox q
+ DeviceExtension->Q_Full_Map[i] = 0;
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Issue EFP_Start.
+ //
+
+ DebugPrint((3, "OliEsc2ResetAdapter: "
+ "Phase 4 (EFP_START command) max time = %ld us.\n",
+ TIMER_WAIT_INT_INTERVAL * TIMER_WAIT_INT_LOOPS
+ ));
+
+ if (!GainSemaphore0(eisaController)) {
+
+ Error = TRUE;
+ break;
+ }
+
+ //
+ // Output physical address of the queues descriptor
+ // to mailbox registers 1 - 4.
+ //
+
+ ScsiPortWritePortUlong((PULONG)&eisaController->InParm1,
+ DeviceExtension->QueuesDescriptor_PA);
+
+ //
+ // output a TYPE SERVICE request to 'efp_start'
+ //
+
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_START);
+
+ //
+ // set bit 1 of local doorbell register to generate an interrupt
+ // request.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ //
+ // Request a timer call to continue the re-initialization phase.
+ //
+
+ DeviceExtension->ResetTimerCalls = TIMER_WAIT_INT_LOOPS;
+ Delay = TIMER_WAIT_INT_INTERVAL;
+ DeviceExtension->ResetInProgress++;
+ break;
+
+ //
+ // Phase 4: Waiting for the EFP_START command to complete.
+ //
+
+ case 4:
+
+ //
+ // Check if a message interrupt is pending.
+ //
+
+ intpending = ScsiPortReadPortUchar(&eisaController->SystemDoorBell);
+
+ if ( !(intpending & EFP_TYPE_MSG)) {
+
+ Delay = TIMER_WAIT_INT_INTERVAL;
+ break;
+ }
+
+ //
+ // Check the command result.
+ //
+
+ EfpMsg = ScsiPortReadPortUchar(&eisaController->OutTypeMsg);
+
+ if (EfpMsg != M_INIT_DIAG_OK) {
+
+ DebugPrint((1,
+ "OliEsc2ResetAdapter: Error after EFP_START: %x hex.\n",
+ EfpMsg));
+
+ Error = TRUE;
+ break;
+ }
+
+ //
+ // reset bit 1 of the system doorbell register to clear request
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_MSG);
+
+ //
+ // clear semaphore 1 after reading TYPE_MSG (RELEASE_SEM1)
+ //
+
+ ScsiPortWritePortUchar(&eisaController->ResultSemaphore, 0);
+
+#if EFP_MIRRORING_ENABLED
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // We need to re-initialize all the mirroring structures.
+ //
+
+ OliEsc2MirrorInitialize(DeviceExtension, FALSE);
+
+#endif // EFP_MIRRORING_ENABLED
+
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // Re-enable the controller's interrupts.
+ //
+
+ ScsiPortWritePortUchar(&eisaController->SystemIntEnable,
+ SYSTEM_INTS_ENABLE);
+
+ //////////////////////////////////////////////////////////////////////
+ //
+ // All done !
+ //
+
+ DeviceExtension->ResetInProgress = 0;
+ return;
+
+ default:
+
+ //
+ // Invalid reset phase number. This should never happen!
+ //
+
+ DebugPrint((1,
+ "OliEsc2ResetAdapter: 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,
+ OliEsc2ResetAdapter,
+ Delay);
+
+ //
+ // The "ResetNotification" variable is used to keep track of the
+ // time during the reset. If the reset is not completed before
+ // the next ESC2_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 >= ESC2_RESET_NOTIFICATION) {
+
+ //
+ // Notify that a reset was detected on the SCSI bus.
+ //
+
+ for (Bus=0; Bus < DeviceExtension->NumberOfBuses; Bus++) {
+ ScsiPortNotification(ResetDetected, DeviceExtension, Bus);
+ }
+
+ //
+ // Reset the "reset notification timer".
+ //
+
+ DeviceExtension->ResetNotification = 0;
+ }
+
+ //
+ // Update the "reset notification timer".
+ //
+
+ DeviceExtension->ResetNotification += Delay;
+ }
+ else {
+
+ //
+ // Time-out !
+ //
+
+ DebugPrint((1,
+ "OliEsc2ResetAdapter: 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,
+ 0,
+ 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 OliEsc2ResetAdapter
+
+
+
+BOOLEAN
+GainSemaphore0(
+ IN PEISA_CONTROLLER EisaController
+ )
+
+/*++
+
+Routine Description:
+
+ Acquires semaphore 0 (used by EFP TYPE_SERVICE requests) if the
+ semaphore is available.
+
+Arguments:
+
+ EisaController Base address of the ESC-2 registers' address space
+
+Return Value:
+
+ Returns TRUE if semaphore successfully acquired.
+ Returns FALSE if semaphore busy.
+
+--*/
+
+{
+ ULONG i; // loop counter
+ UCHAR DataByte;
+
+ BOOLEAN GotSem = FALSE;
+
+
+ for (i = 0; i < SEMAPHORE_LOOPS && !GotSem; i++) {
+ ScsiPortWritePortUchar(&EisaController->CommandSemaphore,
+ SEM_LOCK);
+ DataByte = ScsiPortReadPortUchar(&EisaController->CommandSemaphore);
+ DataByte &= 3; // we're interested in only the lower the 2 bits
+ if (DataByte == SEM_GAINED) { // did we get the semaphore?
+ GotSem = TRUE; // if we got it, we'll exit the loop
+ }
+ else {
+ ScsiPortStallExecution(SEMAPHORE_INTERVAL); // delay
+ }
+ }
+ return(GotSem);
+} // end of GainSemaphore0()
+
+
+VOID
+BuildEfpCmd(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ Build an EFP command structure for the ESC-2 EFP interface mode.
+
+Arguments:
+
+ DeviceExtension Pointer to the device extension for this driver.
+ Srb Pointer to the Scsi Request Block to service
+
+Return Value:
+
+ Nothing.
+
+--*/
+
+{
+
+ PEFP_SGL pEFP; // SRB extension pointer
+ PQ_ENTRY pCQCmd; // EFP command queue entry pointer
+ ULONG physicalAddress; // Physical address of the EFP cmd
+ ULONG i; // loop counter
+ ULONG length; // Length of contiguous memory in the
+ // data buffer, starting at the
+ // beginning of the buffer
+
+ DebugPrint((3,"OliEsc2BuildEfpCmd: Enter routine.\n"));
+
+ //
+ // Get the EFP command address
+ //
+
+ pEFP = Srb->SrbExtension;
+ pCQCmd = &pEFP->EfpCmd;
+
+ //
+ // Clear the SRB extension area.
+ //
+
+ for (i=0; i< (sizeof(EFP_SGL)/4); i++) {
+ *(((PULONG)pEFP) + i) = 0; // 4 bytes for each loop
+ }
+
+ //
+ // Save SRB back pointer in EFP_SGL (SRB extension).
+ // The Srb is used at interrupt time.
+ //
+
+ pEFP->SrbAddress = Srb;
+
+ //
+ // The following "cast" is ONLY valid in the 32 bit world.
+ //
+
+ pCQCmd->qncmd.ncmd_userid = (ULONG)Srb;
+
+ pCQCmd->qncmd.ncmd_sort = 1; // ESC-2 provide sorting
+ pCQCmd->qncmd.ncmd_prior = 0; // highest priority (ESC-2 ignores)
+
+#if EFP_MIRRORING_ENABLED
+
+ if (DeviceExtension->Esc2 == TRUE) {
+ pCQCmd->qncmd.ncmd_mod = 0x01; // 01 hex for ESC2 (not 00-normal!)
+ }
+ else {
+ pCQCmd->qncmd.ncmd_mod = 0x00; // command directed to both disks
+ }
+
+#else
+
+ pCQCmd->qncmd.ncmd_mod = 0x01; // 01 hex for ESC2 (not 00-normal!)
+
+#endif // EFP_MIRRORING_ENABLED
+
+ //
+ // Copy the Command Descriptor Block (CDB) into the EFP command
+ //
+
+ ScsiPortMoveMemory(&pCQCmd->qncmd.ncmd_cdb, Srb->Cdb, Srb->CdbLength);
+ DebugPrint((3,
+ "OliEsc2BuildEfpCmd: CDB at %lx, length=%x, SRB at %lx.\n",
+ &pCQCmd->qncmd.ncmd_cdb, Srb->CdbLength, pEFP->SrbAddress));
+
+ //
+ // Build a scatter/gather list in the SRB 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
+ //
+
+ //
+ // Set the CDB length and the data transfer length
+ //
+ pCQCmd->qncmd.ncmd_cdb_l = (UCHAR)Srb->CdbLength;
+ pCQCmd->qncmd.ncmd_length = Srb->DataTransferLength;
+
+ pCQCmd->qncmd.ncmd_address = physicalAddress;
+
+ if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
+
+ //
+ // Adapter to system transfer
+ //
+
+ pCQCmd->qncmd.ncmd_cmd_type = NCMD_WRITE;
+
+ } else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
+
+ //
+ // System to adapter transfer
+ //
+
+ pCQCmd->qncmd.ncmd_cmd_type = NCMD_READ;
+
+ } else if (!Srb->SrbFlags & 0xF0) { // SRB_FLAGS_NO_DATA_TRANSFER
+
+ pCQCmd->qncmd.ncmd_cmd_type = NCMD_NODATA;
+
+ }
+
+ }
+ else { // need scatter/gather list
+ //
+ // The Srb->DataBuffer is not contiguous: we need
+ // scatter/gather descriptors
+ //
+
+ BuildSgl(DeviceExtension, Srb);
+
+ }
+ } else {
+
+ //
+ // No data transfer is requested
+ //
+
+ pCQCmd->qncmd.ncmd_cmd_type = NCMD_NODATA;
+ pCQCmd->qncmd.ncmd_address = 0;
+ pCQCmd->qncmd.ncmd_cdb_l = (UCHAR)Srb->CdbLength;
+
+ }
+
+ return;
+
+} // end BuildEfpCmd()
+
+
+BOOLEAN EnqueueEfpCmd (
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PQ_ENTRY pEfpCmd,
+ IN PCOMMAND_QUEUE qPtr,
+ IN UCHAR TarLun,
+ OUT PUCHAR pSignal
+ )
+/*++
+
+Routine Description:
+
+ Enqueue a command into an EFP command queue. Update Put pointer
+ as needed, observing possible need to wrap, if the addition of
+ this command would exceed the length of the queue. Refer to
+ algorithm in comments below. NOTE: This code assumes that a queue
+ command takes up only queue entry (ESC-2 does NOT use Long scatter
+ gather commands, 64 bytes long).
+
+ Linkage: Called from Get_Information()
+ Get_Configuration()
+
+Arguments:
+
+ DeviceExtension Device extension for this adapter
+ pEfpCmd Pointer to command to be compiled to the queue.
+ TarLun Command queue array index (TARGET/LUN combination)
+ pSignal return status signal
+
+Return Value:
+
+ TRUE Command was enqueued.
+ *pSignal = 0: the queue is now full.
+ *pSignal = 1: the queue is not full.
+
+ FALSE Error enqueuing the command.
+ *pSignal = 0: the queue was full.
+ *pSignal = 1: problem in signalling controller
+ of empty to not empty transition.
+
+Algorithm:
+ If Queue is not full (Put != Get - (1 | 2))
+ Then
+ OldPut = Put
+ copy command (@DS:SI) to Queue[Put]
+ Put = (Put + (1 for normal, 2 for long)) mod Q length
+ read current Get pointer (to prevent race condition)
+ If Queue was empty (OldPut = Get)
+ Then
+ Signal controller of empty -> not empty transition
+ Endif
+ Return
+ Endif
+ Else Queue was initially full
+ Set error flag
+ Return ; caller is responsible for awaiting not full
+ EndElse
+
+--*/
+
+{
+ PQ_ENTRY pqmem;
+ USHORT getp, orig_putp, putp; // get and put pointers
+
+ //
+ // Initialize the local pointers with the real ones.
+ //
+
+ getp = qPtr->Cmd_Q_Get;
+ putp = qPtr->Cmd_Q_Put;
+
+ orig_putp = putp; // Used to empty to not empty message.
+
+ //
+ // Check if the queue is full (Put = Get - 1 or Put = Get - 2, defined
+ // by the fact that we have both normal and dual commands).
+ //
+
+ if (putp >= getp) {
+ getp += COMMAND_Q_ENTRIES; // Make Get > Put for easier comp.
+ }
+
+ getp = (getp - putp); // Entries left
+
+
+ if (getp <= 2) { // Is the queue full ?
+ *pSignal = 0; // Yes, it is. Error !
+ return(FALSE);
+ }
+
+ // Compile our command onto the queue at position of current PUT pointer.
+
+ // First, advance queue pointer to beginning of queue (past Get and Put
+ // pointers), then on up <put pointer count> number of queue elements.
+ // IMPORTANT: Note that sizeof(NORMAL_CMD) works as a standard queue
+ // element size because all commands accepted by the ESC-2 are of the
+ // same length. This would NOT work for an EFP-2!
+
+ pqmem = &qPtr->Cmd_Entries[putp];
+
+ *pqmem = *pEfpCmd;
+
+ //
+ // Update the local Put pointer
+ //
+ // Note: the pointer is incremented only by 1 !
+ // Good only if the command is one entry in size.
+ //
+
+ putp++;
+
+ if (putp == COMMAND_Q_ENTRIES) { // Need to wrap?
+ putp -= COMMAND_Q_ENTRIES; // Yes, do it.
+ }
+
+ //
+ // update Put pointer in the Queue itself (to match local put variable)
+ //
+
+ qPtr->Cmd_Q_Put = (UCHAR)putp;
+
+ //
+ // Re-read Get pointer (to avoid race conditions, we want current info)
+ //
+
+ getp = qPtr->Cmd_Q_Get;
+
+ //
+ // Check if the queue is empty using the original Put ptr and the
+ // current Get ptr.
+ //
+
+ if (getp == orig_putp) {
+
+ //
+ // Yes, the queue is now empty. We need to sent the "empty to not
+ // empty transition" message.
+ //
+
+ *pSignal = 1; // queue is not full.
+
+ //
+ // Send message
+ //
+
+ return(EfpCommand(DeviceExtension, TarLun));
+ }
+ else {
+
+ //
+ // The queue is not empty. Check if it's full.
+ //
+
+ if (putp >= getp) {
+ getp += COMMAND_Q_ENTRIES; // Make Get > Put for easier comp.
+ }
+
+ getp = (getp - putp); // Entries left
+
+
+ if (getp <= 2) { // Is the queue full ?
+ *pSignal = 0; // Yes, it is.
+ }
+ else {
+ *pSignal = 1; // No, it isn't.
+ }
+
+ //
+ // The command has been enqueued successfully.
+ //
+
+ return(TRUE);
+ }
+
+} // end EnqueueEfpCmd
+
+
+BOOLEAN
+DequeueEfpReply (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+
+Routine Description:
+
+ Dequeue one reply entry from the reply queue.
+
+
+ Linkage: Call Near
+ Get_Information(),
+ Get_Configuration()
+Arguments:
+
+ DeviceExtension Device extension for this adapter
+
+Return Value:
+
+ TRUE Ok, the RQ_In_Process (see device extension) variable
+ is valid.
+
+ FALSE Error (in EfpReplyQNotFull), the RQ_In_Process
+ variable (see device extension) is valid.
+
+ RQ_In_Process = 0, No valid reply entry.
+ 1, Dequeued an entry.
+
+Algorithm:
+
+ 1: If GET -> nrply_flag != 0
+ 2: then
+ copy the reply entry into ACB Q_Buf
+ reset the nrply_flag
+ increment the GET pointer
+ 3: If Reply_QFull != 0
+ 4: then
+ reset the Reply_QFull to 0
+ signal the controller the respond queue is no
+ longer full ( Type_service 08H)
+ endif
+ 5: set RQ_In_Process flag
+ 6: return TRUE
+ 7: else
+ reset the RQ_In_Process flag
+ (wait for interrupt from controller to signal a valid
+ entry has been placed in the reply queue)
+ return TRUE
+ endif
+
+
+// START NOTE EFP_MIRRORING_ENABLED.
+//
+// The DequeueEfpReply routine always uses the NORMAL_REPLY struct to
+// dequeue a request. This is possible because the "flag" field is at
+// the same offset in both structures (NORMAL_REPLY and MIRROR_REPLY).
+//
+// The DequeueEfpReply routine validates the reply entry checking if the
+// "flag" field is different from zero. This is OK because a good reply
+// has the "flag" field is set to 1 in NORMAL/MAINTENANCE mode and to 3
+// in MIRRORING mode. A value of zero means reply not good for both
+// environments.
+//
+// END NOTE EFP_MIRRORING_ENABLED.
+
+--*/
+
+{
+ UCHAR reply_get;
+ PQ_ENTRY preply_entry;
+
+ reply_get = DeviceExtension->Reply_Q_Get;
+ preply_entry = &DeviceExtension->NoncachedExt->Reply_Q[reply_get];
+
+ // reply valid flag set indicates reply entry valid and in process
+
+ if (preply_entry->qnrply.nrply_flag) {
+
+ DeviceExtension->RQ_In_Process = 1; // indicate busy
+
+ // copy reply queue entry to local queue entry buffer
+ *(&DeviceExtension->Q_Buf.qnrply) = preply_entry->qnrply;
+
+ // reset reply valid flag
+ preply_entry->qnrply.nrply_flag = 0;
+
+ // update the GET pointer, wrap around if necessary
+
+ if (DeviceExtension->Reply_Q_Get == (REPLY_Q_ENTRIES - 1)) {
+ DeviceExtension->Reply_Q_Get = 0; // yes, wraparound Get ptr
+ } else {
+ DeviceExtension->Reply_Q_Get += 1; // no, just inc Get ptr
+ }
+
+ //
+ // test if the queue is(was) full, and reset reply queue full
+ // signal if needed, signalling the controller that a queue
+ // full to not full transition has occurred.
+ //
+
+ if (DeviceExtension->Reply_Q_Full_Flag == 0x1) { // full?
+ DeviceExtension->Reply_Q_Full_Flag = 0; // reset flag
+
+ // send Reply Q Not Full cmd (q was full, but not now)
+
+ if (EfpReplyQNotFull(DeviceExtension)) {
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+ } else {
+ return (TRUE);
+ }
+
+ } else { // valid flag did NOT indicate reply entry was valid & ready
+
+ // No valid reply entry
+
+ DeviceExtension->RQ_In_Process = 0;
+ return (TRUE);
+ }
+
+} // end DequeueEfpReply
+
+
+BOOLEAN
+EfpReplyQNotFull (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Descriptions:
+
+ Signal controller that reply queue is no longer full
+
+ Issue efp_cmd TYPE_SERVICE request to the BMIC mailbox registers.
+ Signal the controller that the previously full reply queue is no
+ longer full.
+
+ Linkage: Called from DequeueEfpReply()
+
+Arguments:
+
+ DeviceExtension Device extension for this adapter
+
+Return Value:
+
+ returns TRUE if no error.
+ returns FALSE if error.
+
+ --*/
+
+{
+ PEISA_CONTROLLER eisaController;
+
+ eisaController = DeviceExtension->EisaController;
+
+ if (!GainSemaphore0(eisaController)) { // try to get semaphore 0.
+ return(FALSE);
+ }
+
+ // if got the semaphore, output a TYPE SERVICE request to 'efp_rqnf'
+ ScsiPortWritePortUchar(&eisaController->InTypeService, S_EFP_REPNFUL);
+
+ // set bit 1 of the local doorbell register to generate interrupt request
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ return(TRUE);
+
+} // end EfpReplyQNotFull
+
+
+BOOLEAN
+EfpCommand (
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN UCHAR TarLun
+ )
+
+/*++
+
+RoutineDescriptions:
+
+ Signal the controller that at least one command has been
+ entered into a command queue.
+
+ Issue efp_cmd TYPE_SERVICE request to the BMIC mailbox registers.
+ Tell the controller the queue number of the queue to which the
+ command was added.
+
+Arguments:
+
+ DeviceExtension Device extension for this adapter
+ TarLun Command queue array index (TARGET/LUN combination)
+
+Return Value:
+
+ returns TRUE if no error.
+ returns FALSE if error.
+
+ --*/
+
+{
+ PEISA_CONTROLLER eisaController;
+ UCHAR qnumber; // the queue # associated with the TarLun
+
+ //
+ // Get the ESC-2 registers' base address
+ //
+
+ eisaController = DeviceExtension->EisaController;
+
+ if (!GainSemaphore0(eisaController)) { // try to get semaphore 0.
+ return(FALSE);
+ }
+
+ // if got the semaphore, output a TYPE SERVICE request to 'efp_rqnf'
+ qnumber = DeviceExtension->DevicesPresent[TarLun].qnumber;
+ ScsiPortWritePortUchar( &eisaController->InTypeService,
+ (UCHAR)(qnumber | S_EFP_CMD));
+
+ // set bit 1 of local doorbell register to generate interrupt request
+ ScsiPortWritePortUchar(&eisaController->LocalDoorBell, EFP_INT_BIT);
+
+ return(TRUE);
+
+} // end EfpCommand()
+
+
+BOOLEAN
+EfpGetInformation (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Descriptions:
+
+ Issue EFP-interface mailbox command to get information about the
+ controller and its EFP environment.
+
+ Specifically, Get Information returns:
+ 3 bytes -- Firmware release
+ 1 byte -- SCSI level supported by the controller
+ 4 bytes -- Controller ID on first - fourth SCSI bus
+ 1 byte -- Controller environment (ie. MIRRORING or NORMAL)
+ 1 byte -- Constraints on linked commands
+ 1 byte -- Maximum size of a command queue (# of entries)
+
+Arguments:
+
+ DeviceExtension Device extension for this adapter
+
+Return Value:
+
+ returns TRUE if no error.
+ returns FALSE if error.
+
+ --*/
+
+{
+ PEISA_CONTROLLER eisaController;
+ BOOLEAN GotInt = FALSE;
+ UCHAR SignalFlag;
+ ULONG i; // counter
+
+ //
+ // Get the ESC-2 registers' base address
+ //
+
+ eisaController = DeviceExtension->EisaController;
+
+ // Set up the get_information EFP mailbox command
+
+ DeviceExtension->Q_Buf.qmbc.mbc_userid = 1;
+ DeviceExtension->Q_Buf.qmbc.mbc_sort = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_prior = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_reserved = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_cmd_type = MB_GET_INFO;
+ DeviceExtension->Q_Buf.qmbc.mbc_length = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[0] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[1] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[2] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[3] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_addr = 0;
+
+
+ // Enqueue the command
+
+ if (!EnqueueEfpCmd(DeviceExtension,
+ &DeviceExtension->Q_Buf,
+ DeviceExtension->DevicesPresent[0].qPtr,
+ 0, // mailbox queue is queue #0
+ &SignalFlag)) {
+ DebugPrint((1, "Problem enqueueing Get Information EFP command.\n"));
+ return(FALSE);
+ }
+
+ // Wait for controller to respond.
+ //
+ // NOTE: On an ESC-2, the Get_Configuration at this point does not
+ // query devices. The ESC-2 does a Get Configuration early in
+ // the boot process, and simply regurgitates this information when
+ // later (now, at device driver init time) sent a Get Configuration.
+ // Contrast this with the operation of an EFP-2, which may take many
+ // seconds to perform a Get_Configuration, while it queries devices.
+
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
+ EFP_CMD_COMPL) { // was EFP_INTERRUPT()
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ DebugPrint((1, "Controller did not respond to Get_Information.\n"));
+ return(FALSE);
+ }
+
+ // Get reply to the command.
+ if ( !(DequeueEfpReply(DeviceExtension)) ) {
+ DebugPrint((1, "Problem dequeueing reply to Get Information.\n"));
+ return(FALSE);
+ }
+
+ if (!DeviceExtension->RQ_In_Process) { // was a reply properly dequeued?
+ DebugPrint((1, "RQ_In_Process not 01 after Get_Information.\n"));
+ return(FALSE);
+ }
+
+ // Collect reply information
+
+ if (DeviceExtension->Q_Buf.qmbr.mbr_status) {
+ DebugPrint((1, "Get Information command reply status was not 0.\n"));
+ return(FALSE);
+ }
+
+ // Save into the DeviceExtnesion the information obtained by
+ // Get Information.
+
+ DeviceExtension->FW_Rel[0] =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_fw_rel[0]; //minor
+ DeviceExtension->FW_Rel[1] =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_fw_rel[1]; //minor*10
+ DeviceExtension->FW_Rel[2] =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_fw_rel[2]; //major
+ DeviceExtension->SCSI_Level =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_scsi_lev; // ESC2:01
+ DeviceExtension->Adapter_ID[0] =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_id1; // channel #1
+ DeviceExtension->Adapter_ID[1] =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_id2; // channel #2
+ DeviceExtension->Adapter_ID[2] =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_id3; // channel #3
+ DeviceExtension->Adapter_ID[3] =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_id4; // channel #4
+ DeviceExtension->Link_Cmd =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_link;
+ DeviceExtension->Max_CmdQ_ents =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_maxcmds;
+
+#if EFP_MIRRORING_ENABLED
+
+ DeviceExtension->Environment =
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_env;
+
+#endif // EFP_MIRRORING_ENABLED
+
+ // reset bit 1 of the system doorbell register to clear request
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_INT);
+ // was EFP_ACK
+
+ DebugPrint((1,
+ "EfpGetInformation: 1st channel ID = %d, 2nd channel ID = %d.\n",
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_id1,
+ DeviceExtension->Q_Buf.qmbr.mbr_appl.appgi.gi_id2));
+
+ return(TRUE);
+
+} // end of EfpGetInformation()
+
+
+BOOLEAN
+EfpGetConfiguration (
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Descriptions:
+
+ Issue EFP-interface mailbox command to get configuration information
+ about each SCSI device attached to the host adapter.
+
+ Specifically, for each device, Get Configuration returns:
+ 1 byte -- Maximum size of a command queue (# of entries)
+ 1 byte -- the device type (per standard SCSI protocol)
+ 1 byte -- the device type qualifier (per std SCSI protocol)
+ 1 byte -- SCSI level supported by the device
+ 1 byte -- SCSI Channel to which dev is connected (ESC-2: 01)
+ 1 byte -- SCSI ID of the device
+ 1 byte -- SCSI LUN of the device
+ 1 byte -- reserved
+
+Arguments:
+
+ DeviceExtension Device extension for this adapter
+
+Return Value:
+
+ returns TRUE if no error.
+ returns FALSE if error.
+
+ --*/
+
+{
+ PEISA_CONTROLLER eisaController;
+ BOOLEAN GotInt = FALSE;
+ UCHAR SignalFlag;
+ ULONG i; // counter
+ ULONG length; // Length of contiguous memory in the
+ // data buffer, starting at the
+ // beginning of the buffer
+
+
+ //
+ // Get the ESC-2 registers' base address
+ //
+
+ eisaController = DeviceExtension->EisaController;
+
+ // Set up the get_configuration EFP mailbox command
+
+ DeviceExtension->Q_Buf.qmbc.mbc_userid = 2;
+ DeviceExtension->Q_Buf.qmbc.mbc_sort = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_prior = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_reserved = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_cmd_type = MB_GET_CONF;
+ DeviceExtension->Q_Buf.qmbc.mbc_length =
+ sizeof(DeviceExtension->NoncachedExt->GetConfigInfo);
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[0] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[1] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[2] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_user_data[3] = 0;
+ DeviceExtension->Q_Buf.qmbc.mbc_addr =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ NULL, // no SRB
+ &DeviceExtension->NoncachedExt->GetConfigInfo,
+ &length));
+
+ // Enqueue the command
+
+ if (!EnqueueEfpCmd(DeviceExtension,
+ &DeviceExtension->Q_Buf,
+ DeviceExtension->DevicesPresent[0].qPtr,
+ 0, // mailbox queue is queue #0
+ &SignalFlag)) {
+ DebugPrint((1,
+ "Problem enqueueing Get Configuration EFP command.\n"));
+ return(FALSE);
+ }
+
+ // Wait for controller to respond.
+
+ for (i = 0; i < WAIT_INT_LOOPS && !GotInt; i++) {
+ if (ScsiPortReadPortUchar(&eisaController->SystemDoorBell) &
+ EFP_CMD_COMPL) { // was EFP_INTERRUPT()
+ GotInt = TRUE;
+ }
+ else {
+ ScsiPortStallExecution(WAIT_INT_INTERVAL);
+ }
+ }
+
+ if (!GotInt) {
+ DebugPrint((1, "Controller did not respond to Get Configuration.\n"));
+ return(FALSE);
+ }
+
+ // Get reply to the command.
+ if ( !(DequeueEfpReply(DeviceExtension)) ) {
+ DebugPrint((1, "Problem dequeueing reply to Get Configuration.\n"));
+ return(FALSE);
+ }
+
+ if (!DeviceExtension->RQ_In_Process) { // was a reply properly dequeued?
+ DebugPrint((1, "RQ_In_Process not 01 after Get Configuration.\n"));
+ return(FALSE);
+ }
+
+ // Collect reply information (NOTE: since we allocate such a huge buffer
+ // for Get Configuration information, due to the fixed size requirement
+ // imposed by ScsiPortGetUncachedExtension, we never expect to see the
+ // Get Configuration error message that indicates the supplied buffer
+ // was too small (and if we did, there would be nothing we could do
+ // dynamically to adjust the size of the buffer). For this reason, we
+ // do no filtering of error status from this command.
+
+ if (DeviceExtension->Q_Buf.qmbr.mbr_status) {
+ DebugPrint((1, "Get Configuration cmd reply status was not 0.\n"));
+ return(FALSE);
+ }
+
+ // reset bit 1 of the system doorbell register to clear request
+ ScsiPortWritePortUchar(&eisaController->SystemDoorBell, EFP_ACK_INT);
+
+ return(TRUE);
+
+} // end of EfpGetConfiguration()
+
+
+#if EFP_MIRRORING_ENABLED
+
+
+VOID
+OliEsc2MirrorInitialize (
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN BOOLEAN InitTime
+ )
+
+/*++
+
+
+Routine Description:
+
+ Initializes all the FT EFP-2 structures.
+ It assumes that information on the attached SCSI devices has already
+ been obtained by the Get Configuration call, and is stored in the
+ noncached Extension. Note that this routine doesn't log any error.
+
+
+Arguments:
+
+ DeviceExtension Device extension for this driver.
+
+
+Return Value:
+
+ none
+
+
+--*/
+
+{
+ USHORT Index;
+ UCHAR Bus, Tar, Lun, TarLun, Env;
+ PGET_CONF pGetConfigInfo;
+ PTAR_Q pDevInfo;
+
+ //
+ // Analyse the Get Configuration information.
+ //
+
+ for (Index=0; Index < DeviceExtension->TotalAttachedDevices; Index++) {
+
+ //
+ // Initialize pointer to Get Configuration data.
+ //
+
+ pGetConfigInfo = &DeviceExtension->NoncachedExt->GetConfigInfo[Index];
+
+ //
+ // Read the initial mirror state of each TID/LUN from the ctrl info.
+ //
+
+ Env = pGetConfigInfo->gc_env;
+ Bus = pGetConfigInfo->gc_channel - 1; // make it 0-based
+ Tar = pGetConfigInfo->gc_id;
+ Lun = pGetConfigInfo->gc_lun;
+
+ TarLun = GET_QINDEX( Bus, Tar, Lun );
+
+ //
+ // Initialize pointer to mirroring structures.
+ //
+
+ pDevInfo = &DeviceExtension->DevicesPresent[TarLun];
+
+ //
+ // Find out the mirroring type if any.
+ //
+
+ if (InitTime) {
+
+ //
+ // Check if this device (TID/LUN) is mirrored.
+ // Note: the following logic defaults to dual mirroring if the
+ // EFP_DUAL_MIRRORING and EFP_SINGLE_MIRRORING bits are both set.
+ //
+
+ if (Env & EFP_DUAL_MIRRORING) {
+
+ pDevInfo->Type = EfpFtDualBus;
+
+ //
+ // send a info message to the debug port.
+ //
+
+ DebugPrint((1,
+ "OliEsc2MirrorInitialize: Dual bus mirroring,"
+ " Env=%x, Bus=%x, Tid=%x, Lun=%x.\n",
+ Env, Bus, Tar, Lun ));
+
+ }
+ else if (Env & EFP_SINGLE_MIRRORING) {
+
+ pDevInfo->Type = EfpFtSingleBus;
+
+ //
+ // Send a info message to the debug port.
+ //
+
+ DebugPrint((1,
+ "OliEsc2MirrorInitialize: Single bus mirroring,"
+ " Env=%x, Bus=%x, Tid=%x, Lun=%x.\n",
+ Env, Bus, Tar, Lun ));
+
+ }
+ else {
+ pDevInfo->Type = EfpFtNone;
+ }
+
+ //
+ // At the moment we don't know any error.
+ //
+
+ pDevInfo->KnownError = FALSE;
+ }
+
+ //
+ // If this is a mirrored disk, get the states of the disks.
+ //
+
+ if (pDevInfo->Type != EfpFtNone) {
+
+ //
+ // Check if the source disk is present.
+ //
+
+ if (Env & EFP_DISK_SOURCE) {
+
+ //
+ // Source disk is present.
+ //
+
+ pDevInfo->SourceDiskState = EfpFtMemberHealthy;
+ }
+ else {
+
+ //
+ // Source disk is missing.
+ //
+
+ pDevInfo->SourceDiskState = EfpFtMemberMissing;
+ }
+
+ //
+ // Check if the mirror disk is present.
+ //
+
+ if (Env & EFP_DISK_MIRROR) {
+
+ //
+ // Mirror disk is present.
+ //
+
+ pDevInfo->MirrorDiskState = EfpFtMemberHealthy;
+ }
+ else {
+
+ //
+ // Mirror disk is missing.
+ //
+
+ pDevInfo->MirrorDiskState = EfpFtMemberMissing;
+ }
+
+ } // end if (mirroring) ...
+
+ } // end for (each TID/LUN)
+
+ //
+ // all done
+ //
+
+ return;
+
+} // end OliEsc2MirrorInitialize()
+
+#endif // EFP_MIRRORING_ENABLED
diff --git a/private/ntos/miniport/oliscsi/oliesc2.h b/private/ntos/miniport/oliscsi/oliesc2.h
new file mode 100644
index 000000000..0a11a2ee1
--- /dev/null
+++ b/private/ntos/miniport/oliscsi/oliesc2.h
@@ -0,0 +1,1400 @@
+/*++
+
+Copyright (c) Ing. C. Olivetti & C., S.p.A., 1992
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ oliesc2.h
+
+Abstract:
+
+ This module contains the structures specific to the Olivetti ESC-2
+ host bus adapter. Data structures that are part of standard ANSI
+ SCSI will be defined in a header file that will be available to all
+ SCSI device drivers.
+
+Authors:
+
+ Kris Karnos and Young-Chi Tan, 1-November-1992
+
+Revision History:
+
+ 14-Sep-1993: (v-egidis)
+ - Added support for the EFP-2 mirroring mode.
+
+--*/
+
+#include <scsi.h>
+
+
+//
+// To enable the mirroring code set the following define to 1.
+//
+
+#define EFP_MIRRORING_ENABLED 0
+//#define EFP_MIRRORING_ENABLED 1
+
+//
+// The following define controls the type of error code returned when the
+// mirror breaks for the first time. If the define is set to 1, then the
+// SrbStatus/ScsiStatus combination is SRB_STATUS_ERROR/SCSISTAT_BUSY,
+// else the SrbStatus is set to SRB_STATUS_BUS_RESET. In the first case
+// (busy) the request is retried by the scsiport, whereas in the second case
+// (bus reset) the request is retried by the class driver. The busy error
+// code is the best one but because of a bug in the scsiport's busy logic,
+// the current miniport version returns the other one.
+// This define is used only if the EFP_MIRRORING_ENABLED is set to 1.
+//
+
+#define EFP_RETURN_BUSY 0
+//#define EFP_RETURN_BUSY 1
+
+//
+// EISA controller IDs
+//
+ // hi word = card type; lo word = mfg.
+#define ESC1_BOARDID 0x2110893D // ESC-1 (which this ADD won't support)
+#define ESC2_BOARDID 0x2210893D // ESC-2 (2z10893d, where z > 1, = ESC2)
+#define REV_MASK 0xF0FFFFFF // for masking out the revision level (z).
+
+//
+// Maximum number of EISA slots in system
+//
+
+#define MAX_EISA_SLOTS_STD 16 // # of EISA slots possible (per EISA std)
+#define MAX_EISA_SLOTS 8 // max # that Oli machines support
+
+//
+// Maximum number of EISA buses.
+//
+// Note: If you change this define, you need to change also the ScsiInfo
+// variable to ...
+//
+// SCSI_INFO ScsiInfo[MAX_EISA_BUSES][MAX_EISA_SLOTS_STD];
+//
+// ... and of course all the code that uses the variable ...
+//
+// ScsiInfo[ConfigInfo->SystemIoBusNumber][Slot - 1]
+//
+// It is very uncommon for a system to have 2 buses of the same type
+// (especially old buses like ISA and EISA).
+//
+
+#define MAX_EISA_BUSES 1 // # of EISA buses supported.
+
+//
+// Number of devices and EFP queues per host adapter
+//
+// Note that this miniport assumes that the host adapter uses target ID 7.
+// The maximum # of devices that can be attached to a host adapter is therefore
+// 7 targets * 8 LUNs = 56. Because the area we reserve for our queues is fixed
+// (ScsiPortGetUncachedExtension can only be called once per host adapter),
+// we will always create queues for the maximum # of devices that COULD be
+// attached to the host adapter -- rather than the actual number of devices
+// that are attached.
+//
+
+#define HA_BUSES 2 // EFP2 has 2 buses ( ESC-2 just 1 )
+#define HA_TARGETS 7 // SCSI targets 0 - 7 (but 7 is adapter)
+#define HA_LUNS 8 // SCSI LUNs range 0 - 7
+#define HA_DEVICES (HA_TARGETS * HA_LUNS)
+ // # possible attached devices per Bus
+#define HA_QUEUES ( ( HA_DEVICES * HA_BUSES ) + 1 )
+ // queue per device + mailbox queue
+#define MAX_HAIDS 4 // max number of H.A. Ids
+
+#define GET_QINDEX(B,T,L) ( B*HA_DEVICES + T*HA_LUNS + L + 1 )
+
+//
+// Base of the EISA address space
+//
+
+#define EISA_ADDRESS_BASE 0x0C80
+
+//
+// Define constants for request completion in case of bus reset
+//
+
+#define ALL_TARGET_IDS -1
+#define ALL_LUNS -1
+
+//
+// Maximum number of scatter/gather descriptors (the ESC-2 limit is 8192,
+// because the EFP extended scatter gather command provides 16 bits to
+// describe the scatter gather descriptor table length, accessing 64KB,
+// and the size of a scatter gather table element is 8 bytes. 64K/8 = 8K)
+//
+
+//#define MAXIMUM_SGL_DESCRIPTORS 8192
+#define MAXIMUM_SGL_DESCRIPTORS 20
+
+//
+// Maximum data transfer length
+//
+
+#define MAXIMUM_TRANSFER_SIZE 0xffffffff
+
+
+#define NO_BUS_ID 0xFF
+
+//
+// ESC-2 8-bit command codes (for CCB) and configuration commands
+//
+
+#define START_CCB 0x01 // get CCB for this op. Std I/O request.
+#define SEND_CONF_INFO 0x02 // send the Host Adapter config info.
+#define RESET_TARGET 0x04 // reset a specified SCSI target
+#define SET_CONF_INFO 0x40 // set specific configuration bits
+#define GET_CONF_INFO 0x41 // get specific configuration bits
+#define GET_FW_VERSION 0x42 // read firmware revision number
+#define CHECK_DEVICE_PRESENT 0x43 // check presence of a device
+
+//
+// ESC-2 configuration registers
+//
+
+#define CFG_REGS_NUMBER 8 // 8 configuration registers
+#define ATCFG_REGISTER 0x1 // AT-compatibility configuration
+#define IRQL_REGISTER 0x2 // Interrupt level
+
+//
+// ESC-2 ATCFG_REGISTER AT-compatibility configuration byte flags/masks
+//
+
+#define AT_ENABLED 0x01 // AT-compatibility mode enabled
+#define DISK_80_MASK 0x0E // Mask for disk 80's Target ID
+#define DISK_81_MASK 0x70 // Mask for disk 81's Target ID
+
+//
+// ESC-2 IRQL-REGISTER definitions
+//
+
+#define IRQ_MASK 0x03 // Mask for IRQ line bits 0 & 1
+
+#define IRQ_0B 0x00 // ID values of bits 0 and 1 in
+#define IRQ_0A 0x01 // the IRQL-REGISTER
+#define IRQ_05 0x02
+#define IRQ_0F 0x03
+#define IRQB 0x0B // The interrupts values themselves.
+#define IRQA 0x0A
+#define IRQ5 0x05
+#define IRQF 0x0F
+
+//
+// TYPE_SERVICE command IDs and mask (for ESC-2 using EFP interface)
+//
+
+#define S_EFP_SET 0x01 // Request the EFP interface be enabled
+#define S_EFP_START 0x02 // Supply physical addr of q descriptor
+#define S_EFP_SETDLG 0x04 // Req cntrlr start on-board diagnostics
+#define S_EFP_REPNFUL 0x08 // Prev'ly full reply q no longer full
+#define S_EFP_WARN 0x10 // For EFP-2 hw, activate auto-recovery
+
+#define S_EFP_CMD 0x80 // this is OR'd with the queue number
+
+//
+// TYPE MSG messages and masks for messages (EFP interface)
+//
+
+#define M_INIT_DIAG_OK 0x00 // Successful: efp_set|_start|_set_diag
+#define M_REPLY_Q_FULL 0x01 // Reply queue is full
+
+#define M_ERR_INIT 0x40 // Error during efp_set or efp_start
+#define M_ERR_CHECKSUM 0x41 // ROM checksum error has been found
+#define M_ERR_EEPROM 0x42 // EEPROM error encountered
+#define M_ERR_ARBITER 0x45 // Arbiter error exists
+#define M_ERR_SYSBUS 0x49 // System bus cntrlr (ie. BMIC) error
+#define M_ERR_ATCOMP 0x4A // AT-compat cntrlr has fault/failure
+#define M_ERR_UART 0x4C // Failure in the UART
+#define M_ERR_CMD_REJ 0x4E // prev cmd issued by sys was rejected
+#define M_ERR_CMDQ_NFUL 0x80 // *MASK* 1xxxxxxx (xxxxxxx is queue #)
+
+//
+// EFP interface queue lengths
+//
+// Due to restrictions of the NT miniport design, we can no longer
+// adjust the length of EFP command queues according to the number
+// of attached devices. These constants can, and should, be adjusted
+// for the best performance in an "average" configuration of attached
+// devices.
+//
+
+// Testing values for queue lengths: to force queue full case.
+//#define COMMAND_Q_ENTRIES 5
+//#define REPLY_Q_ENTRIES 5
+
+#define COMMAND_Q_ENTRIES 0x20 // 32 entries
+#define REPLY_Q_ENTRIES 0x80 // 128 entries
+
+//
+// Other queue equates
+//
+
+#define Q_ENTRY_SIZE 32 // a q entry is 32 bytes (LSG = 2 entries)
+#define Q_ENTRY_DWORDS 8 // number of dwords in a Q entry (32byte)
+#define Q_ENTRY_SHIFT 5 // for shift multiplies
+
+#define USER_ID_LIMIT 0x0FFFFFFF // wrap around limit for EFP cmd UserID
+ // MSB (bit 28-31) reserved for slot #
+
+//
+// Olivetti disk geometry translation
+//
+
+#define HPC_SCSI 16 // #heads for capacity up to 504MB
+#define THPC_SCSI 64 // #heads for capacity above 504MB
+#define SPT_SCSI 63 // #sectors per track
+#define HPCxSPT HPC_SCSI*SPT_SCSI // heads x sectors (up to 504MB)
+#define THPCxSPT THPC_SCSI*SPT_SCSI // heads x sectors (above 504MB)
+
+
+// YCT I don't think we need the following CCB definition. What you think ?
+//
+// First byte of the Command Control Block:
+//
+// Drive Number / Transfer Direction
+//
+// --------------------------------------
+// | XFER Dir | Target ID | LU Number |
+// --------------------------------------
+// 7 6 5 4 3 2 1 0
+//
+//
+// Subfield constants:
+//
+
+#define CCB_DATA_XFER_ANY_DIR 0 // The adapter decides
+#define CCB_DATA_XFER_IN 0x40 // XFER Dir = 01
+#define CCB_DATA_XFER_OUT 0x80 // XFER Dir = 10
+#define CCB_DATA_XFER_NONE 0xC0 // XFER Dir = 11
+#define CCB_TARGET_ID_SHIFT 3
+
+//
+// Status Register: bit 15-8: adapter status, bits 7-0: target status
+//
+// Adapter status after a power cycle:
+
+#define DIAGNOSTICS_RUNNING 0x53
+#define DIAGNOSTICS_OK_CONFIG_RECEIVED 0x01
+#define DIAGNOSTICS_OK_NO_CONFIG_RECEIVED 0x02
+
+//
+// The ESC-2 controller (and only it) in EFP mode can return some
+// error codes that are not present in the specifications.
+//
+
+//#define NO_ERROR 0x00
+//#define INVALID_COMMAND 0x01
+//#define SELECTION_TIMEOUT_EXPIRED 0x11
+//#define DATA_OVERRUN_UNDERRUN 0x12
+//#define UNEXPECTED_BUS_FREE 0x13
+//#define SCSI_PHASE_SEQUENCE_FAILURE 0x14
+//#define COMMAND_ABORTED 0x15
+//#define COMMAND_TO_BE_ABORTED_NOT_FOUND 0x16
+//#define QUEUE_FULL 0x1F
+//#define INVALID_CONFIGURATION_COMMAND 0x20
+//#define INVALID_CONFIGURATION_REGISTER 0x21
+//#define NO_REQUEST_SENSE_ISSUED 0x3B
+
+#define AUTO_REQUEST_SENSE_FAILURE 0x80
+#define PARITY_ERROR 0x81
+#define UNEXPECTED_PHASE_CHANGE 0x82
+#define BUS_RESET_BY_TARGET 0x83
+#define PARITY_ERROR_DURING_DATA_PHASE 0x84
+#define PROTOCOL_ERROR 0x85
+
+// Codes to identify logged errors related to H/W malfunction.
+// These codes must be shifted left by 16 bits, to distinguish them from
+// the adapter status and extended status after a EFP command.
+//
+// For use with ScsiPortLogError().
+
+#define ESCX_BAD_PHYSICAL_ADDRESS (0x01 << 16)
+#define SEND_COMMAND_TIMED_OUT (0x02 << 16)
+#define ESCX_RESET_FAILED (0x06 << 16)
+#define ESCX_INIT_FAILED (0x07 << 16)
+#define ESCX_REPLY_DEQUEUE_ERROR (0x08 << 16)
+
+#if EFP_MIRRORING_ENABLED
+
+#define EFP_MISSING_SOURCE_ERROR (0x80 << 16)
+#define EFP_MISSING_MIRROR_ERROR (0x81 << 16)
+#define EFP_SOURCE_OFFLINE_ERROR (0x82 << 16)
+#define EFP_MIRROR_OFFLINE_ERROR (0x83 << 16)
+
+#endif // EFP_MIRRORING_ENABLED
+
+//
+// Define various timeouts:
+//
+// RESET_REACTION_TIME number of microseconds the adapter takes to
+// change the status register on the reset command.
+//
+// EFP_RESET_DELAY number of microseconds the driver waits for after
+// a EFP-2 reset command. For the current 6/3/93
+// revision of the EFP firmware, this is the time
+// the board needs to re-initialize itself.
+//
+// EFP_RESET_LOOPS maximum number of attempts made by the driver to
+// get the diagnostics result from the status
+// register after a EFP reset command.
+//
+// EFP_RESET_INTERVAL number of microseconds the driver waits for after
+// each read of the status register (on the reset
+// command).
+//
+// ESC_RESET_DELAY number of microseconds the driver waits for after
+// a ESC-2 reset command. The minimum value for
+// this define is RESET_REACTION_TIME.
+//
+// ESC_RESET_LOOPS maximum number of attempts made by the driver to
+// get the diagnostics result from the status
+// register after a ESC reset command.
+//
+// ESC_RESET_INTERVAL number of microseconds the driver waits for after
+// each read of the status register (on the reset
+// command).
+//
+// POST_RESET_DELAY number of microseconds the adapter needs (!) after
+// a successful reset in order to accept the first
+// command (this should not happen and needs to be
+// investigated).
+//
+// SEMAPHORE_LOOPS maximum number of attempts made by the driver to
+// get the semaphore 0 (each attempt is followed
+// by a SEMAPHORE_INTERVAL delay.
+//
+// SEMAPHRE_INTERVAL number of microseconds the driver waits for before
+// re-trying to get the semaphore #0.
+//
+// WAIT_INT_LOOPS maximum number of attempts made by the driver to
+// get a reply for a get config info etc. during the
+// initialization phase (polling mode).
+//
+// WAIT_INT_INTERVAL number of microseconds the driver waits for before
+// re-checking the interrupt pending status.
+//
+// TIMER_WAIT_INT_LOOPS maximum number of attempts made by the driver to
+// get a reply for a set/start EFP command during the
+// reset phase (polling mode with timer).
+//
+// TIMER_WAIT_INT_INTERVAL number of microseconds the driver waits for before
+// re-checking the interrupt pending status.
+//
+
+#define RESET_REACTION_TIME 80 // 80 usec.
+#define EFP_RESET_DELAY 1000000 // 1 sec.
+#define EFP_RESET_LOOPS 1200 // 2 min.
+#define EFP_RESET_INTERVAL 100000 // 100 msec.
+#define ESC_RESET_DELAY 200000 // 200 msec.
+#define ESC_RESET_LOOPS 140 // 14 sec.
+#define ESC_RESET_INTERVAL 100000 // 100 msec.
+#define POST_RESET_DELAY 50000 // 50 msec.
+#define SEMAPHORE_LOOPS 750 // 75 msec.
+#define SEMAPHORE_INTERVAL 100 // 100 usec.
+#define WAIT_INT_LOOPS 10000 // 10 sec.
+#define WAIT_INT_INTERVAL 1000 // 1 msec.
+#define TIMER_WAIT_INT_LOOPS 1000 // 10 sec.
+#define TIMER_WAIT_INT_INTERVAL 10000 // 10 msec.
+
+//
+// If the reset is not completed before the next ESC2_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.). The value of this define is lower than 4 sec. because:
+// a) it's more implementation indipendent.
+// b) we want really really to make sure that the SRBs are held at the ScsiPort
+// level during the reset phase.
+//
+
+#define ESC2_RESET_NOTIFICATION 1000000 // 1 sec. (in usec).
+
+//
+// System/Local Interrupt register
+//
+// bit 3: Adapter reset w/out reconfiguration (Local Interrupt Register only)
+// bit 4: Adapter reset w/ reconfiguration (Local Interrupt Register only)
+// bit 7: Interrupt pending (read), reset (write) See ESC_INT_BIT
+//
+
+#define ADAPTER_RESET 0x08
+#define ADAPTER_CFG_RESET 0x10
+
+//
+// Global Configuration Register bits
+//
+// Bit 3: 1 = edge-triggered interrupts
+// 0 = level-triggered interrupts
+//
+
+#define EDGE_SENSITIVE 8
+
+//
+// EFP interface register bit definitions
+//
+
+ // Local Doorbell register bits
+#define EFP_INT_BIT 0x02 // driver -> ctrlr that EFP-2 cmd ready
+
+ // System Doorbell register bits
+#define EFP_CMD_COMPL 0x01 // ctrlr -> driver: EFP-2 q cmd completed
+#define EFP_TYPE_MSG 0x02 // ctrlr wants to send special EFP message
+#define EFP_ACK_INT 0x01 // driver -> ctrlr: interrupt serviced.
+#define EFP_ACK_MSG 0x02 // driver -> ctrlr: TYPE_MSG int serviced.
+
+ // ESC-1 High Performance --
+ // System and Local Doorbell registers
+#define ESC_INT_BIT 0x80 // ESC-1 bit for both sys&local doorbells,
+ // set interrupt, acknowledge int, etc.
+ // aka INTERRUPT_PENDING
+
+//
+// System/Local Interrupt Mask Register constants
+//
+
+#define INTERRUPTS_DISABLE 0x00
+#define INTERRUPTS_ENABLE (ESC_INT_BIT | EFP_TYPE_MSG | EFP_CMD_COMPL)
+
+
+ // Values of the incoming mailbox semaphore (SEM0)
+#define SEM_LOCK 0x01 // write 01 to sem port to test if sem free
+#define SEM_GAINED 0x01 // get 01 back if it was free, is now yours
+#define SEM_IN_USE 0x03 // get 11 (3) back, if sem not available
+#define SEM_UNLOCK 0x00 // release the semaphore
+
+//
+// Command Control Block length
+//
+#define CCB_FIXED_LENGTH 18
+
+//
+// SystemIntEnable register bit definition(s)
+//
+
+#define SYSTEM_INTS_ENABLE 0x01 // for SystemIntEnable (bellinte)
+
+//
+// EFP interface Mailbox Command Set
+//
+
+#define MB_GET_INFO 0x0 // get_information
+#define MB_DOWNLOAD 0x1 // download (DIAGNOSTIC cmd)
+#define MB_UPLOAD 0x2 // upload (DIAGNOSTIC cmd)
+#define MB_FORCE_EXE 0x3 // force_execution (DIAGNOSTICS)
+#define MB_GET_CONF 0x4 // get_configuration
+#define MB_RESET_BUS 0x5 // reset_scsi_bus (For EFP-2 board only)
+#define MB_SET_COPY 0x6 // set_copy (MAINTENANCE/MIRRORING)
+#define MB_SET_VERIFY 0x7 // set_verify (MAINTENANCE/MIRRORING)
+#define MB_DOWNLOAD_FW 0x8 // download_firmware
+
+//
+// EFP read/write command type definitions
+//
+
+#define NCMD_READ 0x10 // data xfer from device to sys memory
+#define NCMD_WRITE 0x11 // data xfer from sys memory to device
+#define NCMD_NODATA 0x12 // no data xfer normal command
+
+#define SSG_READ 0x20 // short SG read from device to sys memory
+#define SSG_WRITE 0x21 // short SG write from sys memory to device
+
+#define LSG_READ 0x30 // long SG read from device to sys memory
+#define LSG_WRITE 0x31 // long SG write from sys memory to device
+
+#define ESG_READ 0x40 // extended SG read from dev to sys memory
+#define ESG_WRITE 0x41 // extended SG write from sys memory to dev
+
+//
+// EFP Reply entry global result values (see also ESC-1 host adapter statuses)
+//
+
+#define EFP_CMD_SUCC 0x00 // command successful
+#define EFP_WARN_ERR 0x01 // warning or fatal error
+#define EFP_EISA_ERR 0xFF // EISA bus transfer generic error
+// Note that the subset below are the same as their ESC-1 counterparts
+#define EFP_LINK_COMP 0x0B // linked command complete with flag
+#define EFP_SEL_TIMEOUT 0x11 // selection timeout expired
+#define EFP_DATA_RUN 0x12 // data overrun/underrun
+#define EFP_BUS_FREE 0x13 // unexpected BUS FREE phase detected
+#define EFP_PHASE_ERR 0x14 // SCSI phase sequence failure
+#define EFP_CMD_ABORT 0x15 // command aborted
+#define EFP_ABORT_LOST 0x16 // cmd to be aborted hasn't been found
+#define EFP_INT_Q_FULL 0x1F // internal q is full; wait to send cmds
+#define EFP_AUTOREC_OK 0x10 // autonomous recovery proc was OK
+#define EFP_AUTOREC_KO 0x18 // autonomous recovery proc failed
+
+//
+// EFP Reply entry Extended Status values (compare to SCSI target statuses)
+//
+
+#define EFP_NO_ERROR 0x00 // nothing to report
+#define EFP_CHK_COND 0x30 // check condition
+#define EFP_COND_MET 0x31 // condition met
+#define EFP_DEV_BUSY 0x32 // target busy
+#define EFP_INTER_GOOD 0x34 // intermediate/good
+#define EFP_INTER_COND 0x35 // intermediate/condition
+#define EFP_RESV_CONF 0x36 // reservation conflict
+#define EFP_ABORT_CMD 0x3B // abort command
+
+//
+// EFP Reset result values
+//
+
+#define EFP_RESET_OK 0x0000 // SCSI bus reset succeeded.
+#define EFP_RESET_ERROR 0x0001 // SCSI bus reset error.
+
+//
+// device LuExtension SRB-CHAIN definition
+//
+
+#define SRB_CHAIN 0x8000
+
+//
+// High Performance mode command sent flag
+//
+
+#define RESET_TARGET_SENT 0x80
+#define BUS_RESET_SENT 0x70
+
+//*******************************************************************
+//************************ STRUCTURES *****************************
+//*******************************************************************
+
+//
+// Incoming mailbox format (GetCmdBlock/SendConfInfo requests)
+//
+
+typedef struct _CMDI {
+ UCHAR mbi_taskid; // task identifier
+ UCHAR mbi_cmd; // ESC-1 command code
+ USHORT mbi_cmdlgt; // command length
+ ULONG mbi_address; // data address
+} CMDI, *PCMDI;
+
+//
+// Outgoing mailbox format (GetCmdBlock/SendConfInfo requests)
+//
+
+typedef struct _CMDO {
+ UCHAR mbo_taskid; // task identifier
+ UCHAR mbo_pad;
+ UCHAR mbo_tastat; // target status
+ UCHAR mbo_hastat; // Host Adapter status
+ ULONG mbo_address; // data address
+} CMDO, *PCMDO;
+
+//
+// Incoming mailbox format for Read Internal Configuration request
+//
+
+typedef struct _RICI {
+ UCHAR rici_taskid; // task identifier
+ UCHAR rici_cmd; // ESC-1 command code = 41H
+ UCHAR rici_reg; // internal register to read (0-7)
+} RICI, *PRICI;
+
+//
+// Outgoing mailbox format for Read Internal Configuration request
+//
+
+typedef struct _RICO {
+ UCHAR rico_taskid; // task identifier
+ UCHAR rico_pad;
+ UCHAR rico_tastat; // target status
+ UCHAR rico_hastat; // Host Adapter status
+ UCHAR rico_value; // value of requested register
+} RICO, *PRICO;
+
+//
+// Incoming mailbox format for Read Firmware Revision request
+//
+
+typedef struct _RFWI {
+ UCHAR rfwi_taskid; // task identifier
+ UCHAR rfwi_cmd; // ESC-1 command code = 42H
+} RFWI, *PRFWI;
+
+//
+// Outgoing mailbox format for Read Firmware Revision request
+//
+
+typedef struct _RFWO {
+ UCHAR rfwo_taskid; // task identifier
+ UCHAR rfwo_pad;
+ UCHAR rfwo_tastat; // target status
+ UCHAR rfwo_hastat; // Host Adapter status
+ UCHAR rfwo_minor; // minor revision, binary
+ UCHAR rfwo_major; // major revision, binary
+} RFWO, *PRFWO;
+
+// KMK Note: we've never used Check Device Present.
+//
+// Incoming mailbox format for Check Device Present request
+//
+
+typedef struct _CDPI {
+ UCHAR cdpi_taskid; // task identifier
+ UCHAR cdpi_cmd; // ESC-1 command code = 42H
+ UCHAR cdpi_target; // target ID
+} CDPI;
+
+//
+// Outgoing mailbox format for Check Device Present request
+//
+
+typedef struct _CDPO {
+ UCHAR cdpo_taskid; // task identifier
+ UCHAR cdpo_pad;
+ UCHAR cdpo_tastat; // target status
+ UCHAR cdpo_hastat; // Host Adapter status
+ UCHAR cdpo_target; // target ID
+ UCHAR cdpo_status; // 0 = non present, 1 = present
+} CDPO;
+
+//
+// MBOX structure (to facilitate MBI and MBO copies (OUTs)
+//
+
+typedef struct _MBOX {
+ ULONG mblow;
+ ULONG mbhigh;
+} MBOX, *PMBOX;
+
+//
+// Incoming mailbox command
+//
+
+typedef union _MBI {
+ CMDI cmd; // get CCB or send config info
+ RICI ric; // read internal configuration
+ RFWI rfw; // read firmware revision
+ CDPI cdp; // check device present
+ MBOX mbcopy; // for copying an MBI
+} MBI, *PMBI;
+
+//
+// Outgoing mailbox command
+//
+
+typedef union _MBO {
+ CMDO cmd; // get CCB or send config info
+ RICO ric; // read internal configuration
+ RFWO rfw; // read firmware revision
+ CDPO cdp; // check device present
+ MBOX mbcopy; // for copying an MBO
+} MBO, *PMBO;
+
+//
+// ESC-2 registers model
+// Note that this is designed to begin with EISA_ADDRESS_BASE, 0x0C80.
+// ESC-1 high performance names are in lower case;
+// EFP interface names are in upper case.
+//
+
+typedef struct _EISA_CONTROLLER {
+ UCHAR BoardId[4]; // xC80
+ UCHAR Unused[4]; // we use no register in XC84 - XC87 range.
+ UCHAR GlobalConfiguration; // xC88 - Bit 3 of this register indicates
+ // level- or edge-triggered interrupts
+ UCHAR SystemIntEnable; // xC89 - system int enab/ctrl reg (bellinte)
+ UCHAR CommandSemaphore; // xC8A - Semaphore port 0 for the Incoming
+ // Service Mailbox Regs aka: SEM0, seminc
+ UCHAR ResultSemaphore; // xC8B - Semaphore port 1 for the Outgoing
+ // Msg Mailbox Regs aka: SEM1, semout
+ UCHAR LocalDoorBellMask; // xC8C - Interrupt mask register for the
+ // Local Doorbell register (maskinc)
+ UCHAR LocalDoorBell; // xC8D - Local Doorbell register (bellinc)
+ UCHAR SystemDoorBellMask; // xC8E - Interrupt mask register for the
+ // System Doorbell register (maskout)
+ UCHAR SystemDoorBell; // xC8F - System Doorbell register (bellout)
+ UCHAR InTypeService; // xC90 - 8-bit Incoming Mbox Reg (TYPE_SERV)
+ // (aka mbi_addr)
+ // ESC-1: InTaskId
+ UCHAR InParm1; // xC91 - parameter 1 to TYPE_SERVICE request
+ // ESC-1: Command
+ UCHAR InParm2; // xC92 - parameter 2 to TYPE_SERVICE request
+ // ESC-1: USHORT CommandLength xC92-xC93
+ UCHAR InParm3; // xC93 - parameter 3 to TYPE_SERVICE request
+ UCHAR InParm4; // xC94 - parameter 4 to TYPE_SERVICE request
+ // ESC-1: ULONG InAddress xC94-xC97
+ UCHAR InReserved1; // xC95 - 8-bit mailbox register reserved
+ UCHAR InReserved2; // xC96 - 8-bit mailbox register reserved
+ UCHAR InReserved3; // xC97 - 8-bit mailbox register reserved
+ UCHAR OutTypeMsg; // xC98 - 8-bit Outgoing Mailbox reg (TYPE_MSG)
+ // (aka mbo_addr)
+ // ESC-1: OutTaskId
+ UCHAR OutReserved1; // xC99 - 8-bit mailbox register reserved
+ UCHAR OutReserved2; // xC9A - 8-bit mailbox register reserved
+ // ESC-1: USHORT Status xC9A-xC9B
+ UCHAR OutReserved3; // xC9B - 8-bit mailbox register reserved
+ UCHAR OutReserved4; // xC9C - 8-bit mailbox register reserved
+ // ESC-1: ULONG OutAddress xC9C-xC9F
+ UCHAR OutReserved5; // xC9D - 8-bit mailbox register reserved
+ UCHAR OutReserved6; // xC9E - 8-bit mailbox register reserved
+ UCHAR OutReserved7; // xC9F - 8-bit mailbox register reserved
+ } EISA_CONTROLLER, *PEISA_CONTROLLER;
+
+//
+// EFP QUEUE STRUCTURES section begins ----->
+//
+
+//
+// Queues descriptor header.
+//
+
+typedef struct _QD_HEAD { // 16 bytes
+ USHORT qdh_maint; // 0001h=MAINTENANCE env; 0000h=USER env.
+ USHORT qdh_n_cmd_q; // num of cmd queues allocated by system.
+ USHORT qdh_type_reply; // 1=Ctrlr ints @each reply entry; 0=after 1+
+ USHORT qdh_reserved1;
+ ULONG qdh_reply_q_addr; // phys addr of reply q (must be dword aligned)
+ USHORT qdh_n_ent_reply; // number of entries in the reply queue
+ USHORT qdh_reserved2;
+} QD_HEAD, *PQD_HEAD;
+
+//
+// Queues descriptor body. NOTE: There is one body element for each
+// queue, including the mailbox queue. The mailbox queue descriptor
+// is the first descriptor body and is always referred to as queue 0.
+//
+
+typedef struct _QD_BODY { // 16 bytes
+ UCHAR qdb_scsi_level; // SCSI protocol level. 01h=SCSI-1; 02h=SCSI-2.
+ UCHAR qdb_channel; // SCSI channel. 01h=1st SCSI chan; 02h=2nd.
+ UCHAR qdb_ID; // SCSI ID of the device.
+ UCHAR qdb_LUN; // SCSI LUN of the device.
+ UCHAR qdb_n_entry_cmd; // num of cmd entries in this queue (must be >=4)
+ UCHAR qdb_notfull_int; // ctrl int if q goes full to not full (01=yes)
+ UCHAR qdb_no_ars; // 01h=ARS disabled for this device
+ UCHAR qdb_timeout; // timeout in seconds (0 hex = infinite wait)
+ ULONG qdb_cmd_q_addr; // physical address of cmd queue.
+ ULONG qdb_reserved;
+} QD_BODY, *PQD_BODY;
+
+//
+// Application field definitions for the EFP Get_Information command.
+//
+
+typedef struct _GET_INFO { // 16 bytes
+ UCHAR gi_fw_rel[3]; // byte0=minor; byte1=minor*10; byte 2=major
+ UCHAR gi_scsi_lev; // SCSI level supported by the ctrlr (ESC2: 01)
+ UCHAR gi_env; // bit packed field defining mirroring environment
+ UCHAR gi_link; // defining LINKED command constraints
+ UCHAR gi_maxcmds; // max size of a cmd q; # of 32-byte cmd entries
+ UCHAR gi_res1; // reserved
+ UCHAR gi_id1; // controller ID on first SCSI bus
+ UCHAR gi_id2; // controller ID on second SCSI bus
+ UCHAR gi_id3; // controller ID on third SCSI bus
+ UCHAR gi_id4; // controller ID on fourth SCSI bus
+ ULONG gi_res2; // reserved
+} GET_INFO, *PGET_INFO;
+
+//
+// Structure of info returned from the EFP Get_Configuration cmd.
+//
+
+typedef struct _GET_CONF { // 8 bytes per structure, 1 struc per device
+ UCHAR gc_dev_type; // SCSI device type
+ UCHAR gc_dev_qual; // SCSI device type qualifier
+ UCHAR gc_scsi_level; // SCSI protocol level supported by the device
+ UCHAR gc_env; // EFP interface environment of disk device
+ UCHAR gc_channel; // SCSI channel to which device is connected
+ UCHAR gc_id; // SCSI target ID
+ UCHAR gc_lun; // SCSI Logical Unit Number
+ UCHAR gc_res; // reserved
+} GET_CONF, *PGET_CONF;
+
+
+#if EFP_MIRRORING_ENABLED
+
+//
+// defines for the gc_env of the GET_CONF struct
+//
+
+#define EFP_DUAL_MIRRORING 0x20 // dual bus mirroring
+#define EFP_SINGLE_MIRRORING 0x10 // single bus mirroring
+#define EFP_DISK_MIRROR 0x02 // mirrored disk presence
+#define EFP_DISK_SOURCE 0x01 // source disk presence
+
+#endif // EFP_MIRRORING_ENABLED
+
+//
+// Flexible structure to specific results for a mailbox command's reply
+//
+
+typedef union _MBAPPL { // see MBRPLY
+ GET_INFO appgi; // application field for get_information cmd
+ GET_CONF appgc; // application field for get_configuration cmd
+} MBAPPL, *PMBAPPL;
+
+//
+// Mailbox command entry structure (EFP spec)
+//
+
+typedef struct _MAILBOX_CMD { // 32 bytes
+ ULONG mbc_userid; // command identifier.
+ UCHAR mbc_sort; // cmd can be sorted (1=yes)
+ UCHAR mbc_prior; // cmd priority. range 00h (hi) -> FFh (lo).
+ UCHAR mbc_reserved; // reserved for future use.
+ UCHAR mbc_cmd_type; // valid mailbox command code (see EFP spec)
+ ULONG mbc_length; // length of data transfer in bytes.
+ ULONG mbc_user_data[4]; // generic parameters for the command.
+ ULONG mbc_addr; // phys addr of buffer in system ram
+} MAILBOX_CMD, *PMAILBOX_CMD; // where data is to be transferred to/from.
+
+//
+// Mailbox reply structure (EFP spec)
+//
+
+typedef struct _MAILBOX_REPLY { // 32 bytes
+ ULONG mbr_userid; // command identifier.
+ ULONG mbr_length; // length of data xfer successfully completed.
+ ULONG mbr_reserved; // reserved for future use.
+ MBAPPL mbr_appl; // specific results for each command.
+ USHORT mbr_status; // command global result (see spec).
+ UCHAR mbr_cmd_q; // command queue to which the reply refers.
+ UCHAR mbr_flag; // if controller sets to 1, response is valid;
+} MAILBOX_REPLY, *PMAILBOX_REPLY; // otherwise, response is invalid.
+
+//
+// Normal command structure. (Note: CDB defined in SCSI.H).
+//
+
+typedef struct _NORMAL_CMD { // 32 bytes
+ ULONG ncmd_userid; // command identifier.
+ UCHAR ncmd_sort; // cmd can be sorted (1=yes)
+ UCHAR ncmd_prior; // cmd priority. range 00h (hi) -> FFh (lo).
+ UCHAR ncmd_mod; // mode. 0=norm/maint on ESC-2
+ UCHAR ncmd_cmd_type; // 10h=dev->mem; 11h=mem->dev; 12h=noxfer.
+ UCHAR ncmd_cdb_l; // length of SCSI cmd block.
+ UCHAR ncmd_reserved[3]; // reserved
+ ULONG ncmd_length; // length of the data transfer.
+ CDB ncmd_cdb; // cmd descriptor block (SCSI CDB - size varies).
+ ULONG ncmd_address; // physaddr of system mem buf for data xfer.
+} NORMAL_CMD, *PNORMAL_CMD;
+
+//
+// Short scatter/gather commands definition.
+//
+
+typedef struct _SHORT_SG { // 32 bytes
+ ULONG ssg_userid; // cmd id.
+ UCHAR ssg_sort; // cmd can be sorted (1=YES)
+ UCHAR ssg_prior; // cmd priority. range 00h (hi) -> FFh (lo).
+ UCHAR ssg_mod; // mode. 0=norm/maint on ESC-2
+ UCHAR ssg_cmd_type; // cmd code. 20h=short read SG. 21h="write.
+ ULONG ssg_log_blk; // logical block address of the SCSI device.
+ UCHAR ssg_size_blk; // log. block size of the disk in 256byte units
+ UCHAR ssg_reserved; // reserved for future use
+ USHORT ssg_l1; // length of the buffer associated w/ A1
+ USHORT ssg_l2; // length of the buffer associated w/ A2
+ USHORT ssg_l3; // length of the buffer associated w/ A3
+ ULONG ssg_a1; // physical address associated with L1
+ ULONG ssg_a2; // physical address associated with L2
+ ULONG ssg_a3; // physical address associated with L3
+} SHORT_SG, *PSHORT_SG;
+
+//
+// Long scatter/gather commands definition.
+//
+
+typedef struct _LONG_SG { // 64 bytes
+ ULONG lsg_userid; // cmd id.
+ UCHAR lsg_sort; // cmd can be sorted (1=YES)
+ UCHAR lsg_prior; // cmd priority. range 00h (hi) -> FFh (lo).
+ UCHAR lsg_mod; // mode. 0=norm/maint on ESC-2
+ UCHAR lsg_cmd_type; // cmd code. 30h=long read SG; 31h="write.
+ ULONG lsg_log_blk; // logical block address of the SCSI device.
+ UCHAR lsg_size_blk; // log. block size of the disk in 256byte units
+ UCHAR lsg_reserved; // reserved for future use
+ USHORT lsg_l1; // length of the buffer associated w/ A1
+ USHORT lsg_l2; // length of the buffer associated w/ A2
+ USHORT lsg_l3; // length of the buffer associated w/ A3
+ ULONG lsg_a1; // physical address assocated with L1
+ ULONG lsg_a2; // physical address assocated with L2
+ ULONG lsg_a3; // physical address assocated with L3
+ USHORT lsg_link; // must be FFFF hex; log link to prev cmd entry.
+ USHORT lsg_l4; // length of the buffer associated w/ A4
+ USHORT lsg_l5; // length of the buffer associated w/ A5
+ USHORT lsg_l6; // length of the buffer associated w/ A6
+ USHORT lsg_l7; // length of the buffer associated w/ A7
+ USHORT lsg_l8; // length of the buffer associated w/ A8
+ ULONG lsg_a4; // physical address assocated with L4
+ ULONG lsg_a5; // physical address assocated with L5
+ ULONG lsg_a6; // physical address assocated with L6
+ ULONG lsg_a7; // physical address assocated with L7
+ ULONG lsg_a8; // physical address assocated with L8
+} LONG_SG, *PLONG_SG;
+
+//
+// Extended scatter/gather commands definition.
+//
+
+typedef struct _EXTENDED_SG { // 32 bytes
+ ULONG esg_userid; // cmd id.
+ UCHAR esg_sort; // cmd can be sorted (1=YES)
+ UCHAR esg_prior; // cmd priority. range 00h (hi) -> FFh (lo).
+ UCHAR esg_mod; // mode. 0=norm/maint on ESC-2
+ UCHAR esg_cmd_type; // cmd code. 40h=extended read SG; 41h="write.
+ UCHAR esg_cdb_l; // length of SCSI cmd block.
+ UCHAR esg_reserved1[3];
+ USHORT esg_lb; // length of the scatter gather descriptor table.
+ USHORT esg_reserved2;
+ CDB esg_cdb; // cmd descriptor block (SCSI CDB).
+ ULONG esg_address; // physaddr of scatter gather descriptor table.
+} EXTENDED_SG, *PEXTENDED_SG;
+
+//
+// Reply structure for NORMAL/MAINTENANCE environment. (NOTE: There is
+// no MIRRORING environment supported by the ESC-2). SENSE_DATA in SCSI.H.
+//
+
+typedef struct _NORMAL_REPLY { // 32 bytes
+ ULONG nrply_userid; // cmd id.
+ ULONG nrply_scsi_len; // length of data transfer.
+ SENSE_DATA nrply_sense; // extended info about error detected
+ USHORT nrply_reserved; //
+ UCHAR nrply_status; // cmd global result (0=success;1=warn/err;more)
+ UCHAR nrply_ex_stat; // extended status (see EFP spec)
+ UCHAR nrply_cmd_q; // command queue to which the reply refers.
+ UCHAR nrply_flag; // = 1 means response valid; = 0, resp invalid.
+} NORMAL_REPLY, *PNORMAL_REPLY;
+
+
+#if EFP_MIRRORING_ENABLED
+
+//
+// Reply structure for MIRRORING environment.
+//
+
+typedef struct _MIRROR_REPLY { // 32 bytes
+
+ ULONG mrply_userid; // cmd id.
+ ULONG mrply_scsi_len; // length of data transfer.
+ UCHAR mrply_valid1; // error source.
+ UCHAR mrply_sense1; // sense key.
+ UCHAR mrply_addit1; // additional sense code.
+ UCHAR mrply_qualif1; // additional sense code qualifier.
+ ULONG mrply_info1; // information bytes of request sense xdata.
+ UCHAR mrply_valid2; // error source.
+ UCHAR mrply_sense2; // sense key.
+ UCHAR mrply_addit2; // additional sense code.
+ UCHAR mrply_qualif2; // additional sense code qualifier.
+ ULONG mrply_info2; // information bytes of request sense xdata.
+ USHORT mrply_reserved; //
+ UCHAR mrply_off_attr; // "off line" device attribute.
+ UCHAR mrply_d_off; // "off line" device SCSI ID.
+ UCHAR mrply_status; // cmd global result.
+ UCHAR mrply_ex_stat; // mirroring state (0=OK, 1=KO).
+ UCHAR mrply_cmd_q; // cmd queue to which the reply refers.
+ UCHAR mrply_flag; // 3=response is valid, 0=response is invalid.
+
+} MIRROR_REPLY, *PMIRROR_REPLY;
+
+//
+// "flag" field defines
+//
+
+#define MREPLY_VALID 0x03 // mirroring reply valid
+#define NREPLY_VALID 0x01 // normal/maintenance reply valid
+
+//
+// "off_attr" field defines
+//
+
+#define EFP_SOURCE_OFFLINE 0x01 // source disk off line
+#define EFP_MIRROR_OFFLINE 0x02 // mirror disk off line
+
+//
+// "valid" field defines (specific to the mirroring environment)
+//
+
+#define EFP_SENSE_NO_INFO 0x70 // info doesn't relate to SCSI device
+#define EFP_SENSE_INFO 0xF0 // info relates to SCSI device
+
+//
+// Sense data struct for mirroring replays.
+//
+
+typedef struct _MREPLY_SDATA {
+
+ UCHAR Valid; // error source.
+ UCHAR Sense; // sense key.
+ UCHAR Addit; // additional sense code.
+ UCHAR Qualif; // additional sense code qualifier.
+ ULONG Info; // information bytes of request sense xdata.
+
+} MREPLY_SDATA, *PMREPLY_SDATA;
+
+//
+// EFP_FT_TYPE is an enumerated field that describes the FT types.
+//
+
+typedef enum _EFP_FT_TYPE {
+
+ EfpFtNone,
+ EfpFtSingleBus,
+ EfpFtDualBus
+
+} EFP_FT_TYPE, *PEFP_FT_TYPE;
+
+//
+// EFP_FT_MEMBER_STATE is an enumerated field that describes the state of
+// one member of the SCSI mirror.
+//
+
+typedef enum _EFP_FT_MEMBER_STATE {
+
+ EfpFtMemberHealthy,
+ EfpFtMemberMissing,
+ EfpFtMemberDisabled
+
+} EFP_FT_MEMBER_STATE, *PEFP_FT_MEMBER_STATE;
+
+//
+// Mirroring macros.
+//
+
+#define D_OFF_TO_LUN(x) (((x) >> 5) & 0x7)
+#define D_OFF_TO_TARGET(x) (((x) >> 2) & 0x7)
+#define D_OFF_TO_PATH(x) (((x) + 3) & 0x3)
+
+#endif // EFP_MIRRORING_ENABLED
+
+
+//
+// Flexible structure to hold an EFP queue entry (command or reply)
+//
+
+// START NOTE EFP_MIRRORING_ENABLED.
+//
+// The DequeueEfpReply routine always uses the NORMAL_REPLY struct to
+// dequeue a request. This is possible because the "flag" field is at
+// the same offset in both structures (NORMAL_REPLY and MIRROR_REPLY).
+//
+// The DequeueEfpReply routine validates the reply entry checking if the
+// "flag" field is different from zero. This is OK because a good reply
+// has the "flag" field is set to 1 in NORMAL/MAINTENANCE mode and to 3
+// in MIRRORING mode. A value of zero means reply not good for both
+// environments.
+//
+// The OliEsc2Interrupt routine always uses the "userid" field of the
+// NORMAL_REPLY struct to retrieve the SRB. This is OK because the "userid"
+// field is at the same offset in both structures (NORMAL_REPLY and
+// MIRROR_REPLY).
+//
+// END NOTE EFP_MIRRORING_ENABLED.
+
+typedef union _Q_ENTRY { // see ACB's Qbuf, work space for q cmds/replies
+ MAILBOX_CMD qmbc;
+ MAILBOX_REPLY qmbr;
+ NORMAL_CMD qncmd;
+ SHORT_SG qssg;
+ EXTENDED_SG qesg;
+ NORMAL_REPLY qnrply;
+
+#if EFP_MIRRORING_ENABLED
+
+ MIRROR_REPLY qmrply;
+
+#endif // EFP_MIRRORING_ENABLED
+
+} Q_ENTRY, *PQ_ENTRY;
+
+
+//
+// EFP Command Queue definition (for both mailbox and device command queues)
+//
+// Note: this structure need to be ULONG algned!
+//
+
+typedef struct _EFP_COMMAND_QUEUE {
+ UCHAR Cmd_Q_Get; // get pointer
+ UCHAR Cmd_Q_Res1; // reserved for future use
+ UCHAR Cmd_Q_Put; // put pointer
+ UCHAR Cmd_Q_Res2; // reserved for future use
+ Q_ENTRY Cmd_Entries[COMMAND_Q_ENTRIES];
+} EFP_COMMAND_QUEUE, *PCOMMAND_QUEUE;
+
+//
+// Used by DevicesPresent array to record which devices are attached,
+// and to aid in mapping Target/Lun (TarLun) to queue number.
+//
+
+typedef struct _TAR_Q {
+ BOOLEAN present;
+ UCHAR qnumber;
+ PCOMMAND_QUEUE qPtr;
+
+#if EFP_MIRRORING_ENABLED
+
+ BOOLEAN KnownError; // TRUE=error already logged
+ EFP_FT_TYPE Type; // Mirroring type
+ EFP_FT_MEMBER_STATE SourceDiskState; // Source disk state
+ EFP_FT_MEMBER_STATE MirrorDiskState; // Mirror disk state
+
+#endif // EFP_MIRRORING_ENABLED
+
+} TAR_Q, *PTAR_Q;
+
+
+//
+// <----- EFP QUEUE STRUCTURES section ends
+//
+
+
+//
+// Scatter Gather descriptor
+//
+
+typedef struct _SG_DESCRIPTOR {
+ ULONG Address;
+ ULONG Length;
+} SG_DESCRIPTOR, *PSG_DESCRIPTOR;
+
+//
+// Scatter Gather descriptor list (SGL)
+//
+// YCT - we may reduce the size of MAXIMUM_SGL_DESCRITORS, adjust it later.
+
+typedef struct _SG_LIST { // KMK ??
+ SG_DESCRIPTOR Descriptor[MAXIMUM_SGL_DESCRIPTORS];
+} SG_LIST, *PSG_LIST;
+
+#pragma pack(1)
+
+typedef struct _CCB { // KMK ?? Compare to our old definition
+ // We may not need this. Revisit.
+ //
+ // This first portion is the structure expected by the ESC-1.
+ // Its size is CCB_FIXED_LENGTH and does NOT include the variable
+ // length field Cdb (which can be 6, 10 or 12 bytes long).
+ //
+
+ UCHAR TaskId; // CCB byte 0 (bits 7-6: xfer dir;
+ // bits 5-3: target ID;
+ // bits 2-0: LUN)
+ UCHAR CdbLength; // CCB byte 1 SCSI Command Descriptor
+ // Block length
+ ULONG DataLength; // CCB bytes 2-5 Total data transfer
+ // length
+ ULONG DataAddress; // CCB bytes 6-9 Data segment address
+ ULONG AdditionalRequestBlockLength; // CCB bytes 10-13 Length of the
+ // scatter/gather list
+ ULONG LinkedCommandAddress; // CCB bytes 14-17 Not used
+ UCHAR Cdb[12]; // CCB bytes 18-29 SCSI Command
+ // Descriptor Block
+ //
+ // The following portion is for the miniport driver use only
+ //
+
+ //PVOID SrbAddress; // Address of the related SRB
+ //EFP_SGL Sgl; // Scatter/gather data segment list
+
+} CCB, *PCCB;
+
+#pragma pack()
+
+
+//
+// The first portion is the default EFP 32 byte command structure ( not
+// including the LSG command which is 64 bytes). The SrbAddress and Sgl
+// are needed for command reference.
+//
+typedef struct _EFP_SGL { // SRB extension for EFP command and SGL
+ Q_ENTRY EfpCmd;
+ PVOID SrbAddress; // address of the associated SRB
+ SG_LIST Sgl; // Scatter/Gather data segment list
+ USHORT SGCount; // SG descriptor count
+ PSCSI_REQUEST_BLOCK NextSrb; // linked unprocessed SRB entry
+ USHORT QueueControl; // SRB queue control field
+
+ CCB pCCB; // for RESET_TARGET ( Abort) command
+} EFP_SGL, *PEFP_SGL;
+
+
+//
+// ESC-1 Command Control Block (byte-aligned)
+//
+
+// KMK Here's our old definition, for comparison...
+//** Command control block structure (CCB)
+//typedef struct _CCB {
+// UCHAR CCB_xfer; // targetID/LUN/direction
+// UCHAR CCB_scsilgt; // SCSI command length
+// ULONG CCB_datalen; // data length
+// ULONG CCB_address; // data address
+// ULONG CCB_SG_lgt; // scatter/gather block length
+// ULONG CCB_nextcmd; // linked command address
+// ICDB CCB_cdb; // CDB area
+//} CCB;
+
+//
+// This structure is allocated on a per logical unit basis. It is necessary
+// for the Abort request handling.
+//
+
+typedef struct _LU_EXTENSION { // KMK ??
+ SHORT NumberOfPendingRequests; // Number of SRBs for a logical unit
+} LU_EXTENSION, *PLU_EXTENSION;
+
+
+//
+// The following structure is allocated from noncached memory, which
+// we can only allocate once per adapter, and can not de-allocate.
+// We use this area for the EFP command and reply queues, and associated
+// overhead.
+//
+
+typedef struct _NONCACHED_EXTENSION {
+
+ //
+ // EFP Get Configuration returned information; index: 0 to (maxdevs-1)
+ //
+
+ GET_CONF GetConfigInfo[HA_QUEUES - 1]; // entry #0 = 1st device info
+
+ //
+ // EFP Queues Descriptor
+ //
+
+ QD_HEAD QD_Head; // EFP Queues Descriptor head
+ QD_BODY QD_Bodies[HA_QUEUES]; // EFP Q Desc. body strucs; index:Q#
+
+ //
+ // EFP Reply Queue (one reply queue per host adapter)
+ //
+
+ Q_ENTRY Reply_Q[REPLY_Q_ENTRIES];
+
+ //
+ // EFP Command Queue
+ //
+
+ EFP_COMMAND_QUEUE Command_Qs[1];
+
+} NONCACHED_EXTENSION, *PNONCACHED_EXTENSION;
+
+
+//
+// Device extension
+//
+
+typedef struct _HW_DEVICE_EXTENSION {
+
+ PEISA_CONTROLLER EisaController; // SCSI I/O address
+ PNONCACHED_EXTENSION NoncachedExt; // address of the uncached extension
+ // used for EFP cmd and reply queues.
+ //
+ // Physical address of the queues descriptor
+ //
+
+ ULONG QueuesDescriptor_PA;
+
+ // Device Present array: maps which devices are connected to the
+ // controller, as reported by Get Configuration (this information can
+ // be extracted from GetConfigInfo, but is more easily accessed in this
+ // form). The TAR_Q structure also provides a field that maps the device
+ // to the corresponding device queue.
+ //
+ // Entries should be accessed by TarLun . The first entry is therefore
+ // vacant.
+ //
+
+ TAR_Q DevicesPresent[HA_QUEUES];
+
+ //
+ // General configuration information
+ //
+
+ BOOLEAN Esc2; // Controller type
+ UCHAR NumberOfBuses; // number of SCSI buses
+ BOOLEAN CfgRegsPresent[CFG_REGS_NUMBER];
+ UCHAR CfgRegs[CFG_REGS_NUMBER];
+ UCHAR IRQ_In_Use; // the IRQ used by this host adapter
+
+ //
+ // Reset variables.
+ //
+
+ ULONG ResetInProgress; // >0 if reset is in progress.
+ ULONG ResetTimerCalls; // # of timer calls before time-out.
+ ULONG ResetNotification; // Reset notification trigger.
+
+ //
+ // Enqueue/dequeue information.
+ //
+
+ Q_ENTRY Q_Buf; // scratch space for building an EFP
+ // command queue element
+ UCHAR Q_Full_Map[HA_QUEUES]; // cmd q full (1=full,0=not) index: q #
+ UCHAR Reply_Q_Full_Flag;
+ UCHAR Reply_Q_Get; // Get pointer for reply queue
+ UCHAR RQ_In_Process; // reply queue in process flag
+
+ USHORT TotalAttachedDevices; // number of SCSI devices attached to ctrl
+
+ //
+ // EFP interface's Get Information returned data
+ //
+
+ UCHAR FW_Rel[3]; // byte2: major; b1: minor*10; b0:minor
+ UCHAR SCSI_Level; // SCSI level supported by controller
+ UCHAR Adapter_ID[MAX_HAIDS]; // controller IDs on SCSI buses
+ UCHAR Link_Cmd; // ctrl's constraints on linked cmds
+ UCHAR Max_CmdQ_ents; // the max # 32-byte entries per queue
+
+#if EFP_MIRRORING_ENABLED
+
+ UCHAR Environment; // define the mirroring environment.
+
+#endif // EFP_MIRRORING_ENABLED
+
+} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
+
+
+//
+// The following structures are used to pass information between phase #0
+// and phase #1 of the "FindAdapter" process.
+//
+
+typedef struct _SCSI_INFO {
+
+ UCHAR AdapterPresent; // 0 = adapter not found or in error
+ UCHAR Reserved; // To be defined!
+ USHORT NumberOfDevices; // # of SCSI devices on the adapter
+
+} SCSI_INFO, *PSCSI_INFO;
+
+
+typedef struct _ESC2_CONTEXT {
+
+ UCHAR CheckedSlot; // EISA slot number checked
+ UCHAR Phase; // phase #0 or #1
+
+ SCSI_INFO ScsiInfo[MAX_EISA_SLOTS_STD];
+
+} ESC2_CONTEXT, *PESC2_CONTEXT;
+
+#if EFP_MIRRORING_ENABLED
+
+//
+// NOTE: the following struct doesn't belong to this file!
+//
+
+//
+// Define header for I/O control SRB.
+//
+
+typedef struct _SRB_IO_CONTROL {
+ ULONG HeaderLength;
+ UCHAR Signature[8];
+ ULONG Timeout;
+ ULONG ControlCode;
+ ULONG ReturnCode;
+ ULONG Length;
+} SRB_IO_CONTROL, *PSRB_IO_CONTROL;
+
+#endif // EFP_MIRRORING_ENABLED
diff --git a/private/ntos/miniport/oliscsi/oliscsi.rc b/private/ntos/miniport/oliscsi/oliscsi.rc
new file mode 100644
index 000000000..51a9fecd3
--- /dev/null
+++ b/private/ntos/miniport/oliscsi/oliscsi.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+#define VER_FILEDESCRIPTION_STR "Olivetti SCSI Controllers Driver"
+#define VER_INTERNALNAME_STR "oliscsi.sys"
+#define VER_ORIGINALFILENAME_STR "oliscsi.sys"
+
+#include "common.ver"
+
diff --git a/private/ntos/miniport/oliscsi/sources b/private/ntos/miniport/oliscsi/sources
new file mode 100644
index 000000000..993669102
--- /dev/null
+++ b/private/ntos/miniport/oliscsi/sources
@@ -0,0 +1,35 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=miniport
+
+TARGETNAME=oliscsi
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DRIVER
+
+INCLUDES=..\..\inc
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\scsiport.lib
+
+SOURCES=oliesc1.c oliesc2.c oliscsi.rc