diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/miniport/oliscsi/oliesc1.c | 2364 |
1 files changed, 2364 insertions, 0 deletions
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() |