diff options
Diffstat (limited to 'private/ntos/miniport/oliscsi')
-rw-r--r-- | private/ntos/miniport/oliscsi/makefile | 6 | ||||
-rw-r--r-- | private/ntos/miniport/oliscsi/oliesc1.c | 2364 | ||||
-rw-r--r-- | private/ntos/miniport/oliscsi/oliesc1.h | 363 | ||||
-rw-r--r-- | private/ntos/miniport/oliscsi/oliesc2.c | 6108 | ||||
-rw-r--r-- | private/ntos/miniport/oliscsi/oliesc2.h | 1400 | ||||
-rw-r--r-- | private/ntos/miniport/oliscsi/oliscsi.rc | 12 | ||||
-rw-r--r-- | private/ntos/miniport/oliscsi/sources | 35 |
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 |