diff options
Diffstat (limited to '')
-rw-r--r-- | private/ntos/miniport/aha174x/aha174x.c | 2005 |
1 files changed, 2005 insertions, 0 deletions
diff --git a/private/ntos/miniport/aha174x/aha174x.c b/private/ntos/miniport/aha174x/aha174x.c new file mode 100644 index 000000000..b46eb1084 --- /dev/null +++ b/private/ntos/miniport/aha174x/aha174x.c @@ -0,0 +1,2005 @@ +/*++ + +Copyright (c) 1991 Microsoft Corporation + +Module Name: + + aha174x.c + +Abstract: + + This is the port driver for the AHA174X SCSI adapter. + +Authors: + + Mike Glass + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "miniport.h" +#include "aha174x.h" // includes scsi.h + +// +// Device extension +// + +typedef struct _HW_DEVICE_EXTENSION { + + PEISA_CONTROLLER EisaController; + + UCHAR HostTargetId; + + PSCSI_REQUEST_BLOCK PendingSrb; + + UCHAR RequestCount[8][8]; + +} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION; + +// +// Define the maximum number of outstanding I/O requests per logical unit. +// + +#define MAX_QUEUE_DEPTH 2 + + +// +// Function declarations +// +// Functions that start with 'Aha174x' are entry points +// for the OS port driver. +// + +ULONG +DriverEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ); + +ULONG +Aha174xEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ); + +ULONG +Aha174xConfiguration( + IN PVOID DeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ); + +BOOLEAN +Aha174xInitialize( + IN PVOID DeviceExtension + ); + +BOOLEAN +Aha174xStartIo( + IN PVOID DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +Aha174xInterrupt( + IN PVOID DeviceExtension + ); + +BOOLEAN +Aha174xResetBus( + IN PVOID HwDeviceExtension, + IN ULONG PathId + ); + +// +// This function is called from Aha174xStartIo. +// + +VOID +A174xBuildEcb( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +// +// This function is called from A174xBuildEcb. +// + +VOID +A174xBuildSgl( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +VOID +A174xBuildRequestSense( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +A174xSendCommand( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN UCHAR OperationCode, + IN ULONG Address + ); + +// +// This function is called from Aha174xInterrupt. +// + +VOID +A174xMapStatus( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PSTATUS_BLOCK StatusBlock + ); + + +ULONG +DriverEntry ( + IN PVOID DriverObject, + IN PVOID Argument2 + ) + +/*++ + +Routine Description: + + Installable driver initialization entry point for system. + +Arguments: + + Driver Object + +Return Value: + + Status from ScsiPortInitialize() + +--*/ + +{ + return Aha174xEntry(DriverObject, Argument2); + +} // end DriverEntry() + + +ULONG +Aha174xEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ) + +/*++ + +Routine Description: + + This routine is called from DriverEntry if this driver is installable + or directly from the system if the driver is built into the kernel. + It scans the EISA slots looking for an AHA174X that is configured + to the ENHANCED mode. + +Arguments: + + Driver Object + +Return Value: + + Status from ScsiPortInitialize() + +--*/ + +{ + HW_INITIALIZATION_DATA hwInitializationData; + ULONG i; + ULONG AdapterCount = 0; + + DebugPrint((1,"\n\nSCSI Aha174x 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 = Aha174xInitialize; + hwInitializationData.HwFindAdapter = Aha174xConfiguration; + hwInitializationData.HwStartIo = Aha174xStartIo; + hwInitializationData.HwInterrupt = Aha174xInterrupt; + hwInitializationData.HwResetBus = Aha174xResetBus; + + // + // Set number of access ranges and bus type. + // + + hwInitializationData.NumberOfAccessRanges = 2; + hwInitializationData.AdapterInterfaceType = Eisa; + + // + // Indicate no buffer mapping but will need physical addresses. + // + + hwInitializationData.NeedPhysicalAddresses = TRUE; + + // + // Indicate auto request sense is supported. + // + + hwInitializationData.MultipleRequestPerLu = TRUE; + hwInitializationData.AutoRequestSense = TRUE; + + // + // Specify size of extensions. + // + + hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION); + + // + // Ask for SRB extensions for ECBs. + // + + hwInitializationData.SrbExtensionSize = sizeof(ECB); + + return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &AdapterCount); + +} // end Aha174xEntry() + + +ULONG +Aha174xConfiguration( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ) + +/*++ + +Routine Description: + + This function is called by the OS-specific port driver after + the necessary storage has been allocated, to gather information + about the adapter's configuration. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + ConfigInfo - Configuration information structure describing HBA + +Return Value: + + TRUE if adapter present in system + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PEISA_CONTROLLER eisaController; + ULONG eisaSlotNumber; + PVOID eisaAddress; + UCHAR dataByte; + PULONG adapterCount = Context; + + // + // Check to see if adapter present in system. + // + + for (eisaSlotNumber=*adapterCount + 1; eisaSlotNumber<MAXIMUM_EISA_SLOTS; eisaSlotNumber++) { + + // + // Update the adapter count to indicate this slot has been checked. + // + + (*adapterCount)++; + + // + // Get the system address for this card. + // The card uses I/O space. + // + + eisaAddress = ScsiPortGetDeviceBase(deviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(0x1000 * eisaSlotNumber), + 0x1000, + TRUE); + + eisaController = + (PEISA_CONTROLLER)((PUCHAR)eisaAddress + EISA_ADDRESS_BASE); + + if (ScsiPortReadPortUchar(&eisaController->BoardId[0]) == 0x04 && + ScsiPortReadPortUchar(&eisaController->BoardId[1]) == 0x90 && + (ScsiPortReadPortUchar(&eisaController->BoardId[2]) == 0x00 || + ScsiPortReadPortUchar(&eisaController->BoardId[2]) == 0x04)) { + + DebugPrint((1,"AHA174X: Adapter found at EISA slot %d\n", + eisaSlotNumber)); +#ifdef MIPS + // + // Add code to configure the device if necessary. This is only + // needed until we get an EISA configuration program. + // + + if (!(ScsiPortReadPortUchar(&eisaController->EBControl) & 0x1)) { + + // + // The card as not been configured. Jam in a default one. + // Enable the card, enable enhanced mode operation, set the + // irql to 14, set the target id to 7 and enable the DMA. + // + + ScsiPortWritePortUchar(&eisaController->EBControl, 1); + ScsiPortWritePortUchar(&eisaController->PortAddress, 0x80); + ScsiPortWritePortUchar(&eisaController->BiosAddress, 0x00); + ScsiPortWritePortUchar(&eisaController->Interrupt, 0x1d); + ScsiPortWritePortUchar(&eisaController->ScsiId, 0x7); + ScsiPortWritePortUchar(&eisaController->DmaChannel, 0x2); + ScsiPortStallExecution(1000); + + } +#endif + if (ScsiPortReadPortUchar(&eisaController->PortAddress) & + ENHANCED_INTERFACE_ENABLED) { + + // + // An adapter with the enhanced interface enabled was found. + // + + break; + + } else { + + DebugPrint((1,"AHA174X: Adapter is in STANDARD mode\n")); + } + } + + // + // If an adapter was not found unmap it. + // + + ScsiPortFreeDeviceBase(deviceExtension, + eisaAddress); + + } // end for (eisaSlotNumber ... + + if (!(eisaSlotNumber < MAXIMUM_EISA_SLOTS)) { + + // + // No adapter was found. Indicate that we are done and there are no + // more adapters here. Clear the adapter count for the next bus. + // + + *Again = FALSE; + *adapterCount = 0; + return SP_RETURN_NOT_FOUND; + } + + // + // There is still more to look at. + // + + *Again = TRUE; + + // + // Store base address of EISA registers in device extension. + // + + deviceExtension->EisaController = eisaController; + + ConfigInfo->InitiatorBusId[0] = + ScsiPortReadPortUchar(&eisaController->ScsiId) & 0x0F; + + deviceExtension->HostTargetId = ConfigInfo->InitiatorBusId[0]; + + // + // Indicate maximum transfer length in bytes. + // + + ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_SIZE; + + // + // Maximum number of physical segments is 32. + // + + ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SGL_DESCRIPTORS; + + ConfigInfo->ScatterGather = TRUE; + ConfigInfo->Master = TRUE; + ConfigInfo->NumberOfBuses = 1; + + // + // Get the system interrupt vector and IRQL. + // + + dataByte = ScsiPortReadPortUchar(&eisaController->Interrupt); + ConfigInfo->BusInterruptLevel = (dataByte & 7) + 9; + + // + // Determine level or edge interrupt. + // + + ConfigInfo->InterruptMode = dataByte & 0x08 ? Latched : 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; + + + // + // Determine the BIOS address. + // + + dataByte = ScsiPortReadPortUchar(&eisaController->BiosAddress); + + if (dataByte & BIOS_ENABLED) { + + dataByte &= BIOS_ADDRESS; + + // + // Calculate the bios base address. + // + + eisaSlotNumber = 0xC0000 + (dataByte * 0x4000); + + if (eisaSlotNumber < 0xF0000) { + + DebugPrint((1, "Aha174xConfiguration: Bios address at: %lx.\n", eisaSlotNumber)); + (*ConfigInfo->AccessRanges)[1].RangeStart = + ScsiPortConvertUlongToPhysicalAddress(eisaSlotNumber); + (*ConfigInfo->AccessRanges)[1].RangeLength = BIOS_LENGTH; + (*ConfigInfo->AccessRanges)[1].RangeInMemory = TRUE; + + } + } + + return SP_RETURN_FOUND; + +} // end Aha174xConfiguration() + + +BOOLEAN +Aha174xInitialize( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + Inititialize adapter and mailbox. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + TRUE - if initialization successful. + FALSE - if initialization unsuccessful. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + + // + // Reset Aha174x and SCSI bus. + // + + if (!Aha174xResetBus(deviceExtension, 0)) { + + DebugPrint((1, "Aha174xInitialize: Reset bus failed\n")); + return FALSE; + + } else { + + ScsiPortNotification(ResetDetected, deviceExtension, 0); + + return TRUE; + } + +} // end Aha174xInitialize() + + +BOOLEAN +Aha174xStartIo( + 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 ECB or issue an immediate command. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + TRUE + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PEISA_CONTROLLER eisaController = deviceExtension->EisaController; + PECB ecb; + PSCSI_REQUEST_BLOCK abortedSrb; + UCHAR opCode; + ULONG physicalEcb; + ULONG length; + ULONG i = 0; + UCHAR count = MAX_QUEUE_DEPTH; + + ASSERT(Srb->SrbStatus == SRB_STATUS_PENDING); + + // + // Get ECB from SRB. + // + + if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) { + + // + // 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, "A174xStartIo: SRB to abort already completed\n")); + + // + // Complete abort SRB. + // + + Srb->SrbStatus = SRB_STATUS_ABORT_FAILED; + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + // + // Adapter ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + return TRUE; + } + + // + // Get ECB to abort. + // + + ecb = Srb->NextSrb->SrbExtension; + + // + // Set abort SRB for completion. + // + + ecb->AbortSrb = Srb; + + } else { + + ecb = Srb->SrbExtension; + + // + // Save SRB back pointer in ECB. + // + + ecb->SrbAddress = Srb; + ecb->AbortSrb = NULL; + + } + + // + // Get ECB physical address. + // + + physicalEcb = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(deviceExtension, NULL, ecb, &length)); + + // + // Assume physical address is contiguous for size of ECB. + // + + ASSERT(length >= sizeof(ECB)); + + switch (Srb->Function) { + + case SRB_FUNCTION_EXECUTE_SCSI: + + // + // Build ECB for regular request or request sense. + // + + if (Srb->Cdb[0] == SCSIOP_REQUEST_SENSE) { + A174xBuildRequestSense(deviceExtension, Srb); + } else { + A174xBuildEcb(deviceExtension, Srb); + } + + // + // Increment the request count. + // + + count = ++deviceExtension->RequestCount[Srb->TargetId][Srb->Lun]; + + opCode = START_ECB; + + break; + + case SRB_FUNCTION_ABORT_COMMAND: + + DebugPrint((1, "Aha174xStartIo: Abort request received\n")); + + opCode = ABORT_ECB; + + break; + + default: + + // + // Set error, complete request + // and signal ready for next request. + // + + Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + return TRUE; + + } // end switch + + if (!A174xSendCommand(deviceExtension, + (UCHAR)(opCode | Srb->TargetId), + physicalEcb)) { + + DebugPrint((1,"Aha174xStartIo: Send command timed out\n")); + + // + // Save the request utill a pending one completes. + // + + deviceExtension->PendingSrb = Srb; + + return(TRUE); + + } + + // + // Adapter ready for next request. + // + + if (count < MAX_QUEUE_DEPTH) { + + // + // Request another request for this logical unit. + // + + ScsiPortNotification(NextLuRequest, + deviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + + } else { + + // + // Request another request for this adapter. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + + } + + return TRUE; + +} // end Aha174xStartIo() + + +BOOLEAN +Aha174xInterrupt( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + This is the interrupt service routine for the Aha174x SCSI adapter. + It reads the interrupt register to determine if the adapter is indeed + the source of the interrupt and clears the interrupt at the device. + If the adapter is interrupting because a mailbox is full, the ECB is + retrieved to complete the request. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + TRUE if MailboxIn full + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PECB ecb; + PSCSI_REQUEST_BLOCK srb; + PEISA_CONTROLLER eisaController = deviceExtension->EisaController; + PSTATUS_BLOCK statusBlock; + UCHAR targetId; + UCHAR lun; + ULONG physicalEcb; + UCHAR interruptStatus; + + // + // Check interrupt pending. + // + + if (!(ScsiPortReadPortUchar(&eisaController->Status) & + INTERRUPT_PENDING)) { + + DebugPrint((4, "Aha174xInterrupt: Spurious interrupt\n")); + return FALSE; + } + + // + // Read interrupt status. + // + + interruptStatus = ScsiPortReadPortUchar( + &eisaController->InterruptStatus); + + // + // Get targetId + // + + targetId = interruptStatus & 0x0F; + + // + // Get physical address of ECB. + // + + physicalEcb = ScsiPortReadPortUlong(&eisaController->MailBoxIn); + + // + // Acknowledge interrupt. + // + + ScsiPortWritePortUchar(&eisaController->Control, CLEAR_INTERRUPT); + + // + // Check for pending requests. If there is one then start it. + // + + if (deviceExtension->PendingSrb != NULL) { + + srb = deviceExtension->PendingSrb; + deviceExtension->PendingSrb = NULL; + + Aha174xStartIo(deviceExtension, srb); + + } + + switch (interruptStatus>>4) { + + case ECB_COMPLETE_SUCCESS: + case ECB_COMPLETE_SUCCESS_RETRY: + + // + // Get virtual ECB address. + // + + ecb = ScsiPortGetVirtualAddress(deviceExtension, ScsiPortConvertUlongToPhysicalAddress(physicalEcb)); + + // + // Make sure this was a valid physical address. + // + + if (ecb == NULL || ecb->SrbAddress == NULL) { + break; + } + + // + // Get SRB from ECB. + // + + srb = ecb->SrbAddress; + + // + // Clear SRB from ECB. + // + + ecb->SrbAddress = NULL; + + // + // Update SRB statuses. + // + + srb->SrbStatus = SRB_STATUS_SUCCESS; + srb->ScsiStatus = SCSISTAT_GOOD; + + // + // If there is a peneding abort request, then complete it. + // This adapter does not interrupt when an abort completes. + // So one of three cases will occur: + // The abort succeeds and the command is termainated. + // The abort is too late and command termainates. + // The abort fails but the command does not terminate. + // The first two cases are handled by completing the abort when the + // command completes. The last case is handled by the abort timing + // out. + // + + if (ecb->AbortSrb != NULL) { + + ecb->AbortSrb->SrbStatus = SRB_STATUS_SUCCESS; + + // + // Complete the abort request. + // + + ScsiPortNotification( + RequestComplete, + deviceExtension, + ecb->AbortSrb + ); + + ecb->AbortSrb = NULL; + } + + if (deviceExtension->RequestCount[srb->TargetId][srb->Lun]-- + == MAX_QUEUE_DEPTH) { + + // + // The adapter can now take another request for this device. + // + + ScsiPortNotification(NextLuRequest, + deviceExtension, + srb->PathId, + srb->TargetId, + srb->Lun); + + } + + // + // Call notification routine for the SRB. + // + + ScsiPortNotification(RequestComplete, + (PVOID)deviceExtension, + srb); + + return TRUE; + + case ECB_COMPLETE_ERROR: + + // + // Get virtual ECB address. + // + + ecb = ScsiPortGetVirtualAddress(deviceExtension, ScsiPortConvertUlongToPhysicalAddress(physicalEcb)); + + // + // Make sure this was a valid physical address. + // + + if (ecb == NULL || ecb->SrbAddress == NULL) { + break; + } + + // + // Get SRB from ECB. + // + + srb = ecb->SrbAddress; + + // + // Clear SRB from ECB. + // + + ecb->SrbAddress = NULL; + + // + // Get Status Block virtual address. + // + + statusBlock = ScsiPortGetVirtualAddress(deviceExtension, + ScsiPortConvertUlongToPhysicalAddress(ecb->StatusBlockAddress)); + + // + // If there is a peneding abort request, then complete it. + // This adapter does not interrupt when an abort completes. + // So one of three cases will occur: + // The abort succeeds and the command is termainated. + // The abort is too late and command termainates. + // The abort fails but the command does not terminate. + // The first two cases are handled by completing the abort when the + // command completes. The last case is handled by the abort timing + // out. + // + + if (ecb->AbortSrb != NULL) { + + ecb->AbortSrb->SrbStatus = SRB_STATUS_SUCCESS; + + // + // Complete the abort request. + // + + ScsiPortNotification( + RequestComplete, + deviceExtension, + ecb->AbortSrb + ); + + ecb->AbortSrb = NULL; + } + + // + // Update SRB status. + // + + A174xMapStatus(deviceExtension, srb, statusBlock); + + if (deviceExtension->RequestCount[srb->TargetId][srb->Lun]-- + == MAX_QUEUE_DEPTH) { + + // + // The adapter can now take another request for this device. + // + + ScsiPortNotification(NextLuRequest, + deviceExtension, + srb->PathId, + srb->TargetId, + srb->Lun); + + } + + // + // Call notification routine for the SRB. + // + + ScsiPortNotification(RequestComplete, + (PVOID)deviceExtension, + srb); + + return TRUE; + + case IMMEDIATE_COMMAND_SUCCESS: + + DebugPrint((2,"Aha174xInterrupt: Immediate command completed\n")); + return TRUE; + + case ASYNCHRONOUS_EVENT_NOTIFICATION: + + // + // Check if bus was reset. + // + + if ((physicalEcb >> 24) == 0x23) { + + // + // Clear the reqeust counts. + // + + for (targetId = 0; targetId < 8; targetId++) { + for (lun = 0; lun < 8; lun++) { + + deviceExtension->RequestCount[targetId][lun] = 0; + } + } + + // + // Complete all outstanding requests. + // + + ScsiPortCompleteRequest(deviceExtension, + 0, + SP_UNTAGGED, + 0, + SRB_STATUS_BUS_RESET); + + + // + // Notify operating system of SCSI bus reset. + // + + ScsiPortNotification(ResetDetected, + deviceExtension, + NULL); + } + + return TRUE; + + case IMMEDIATE_COMMAND_ERROR: + default: + + DebugPrint((1, "A174xInterrupt: Unrecognized interrupt status %x\n", + interruptStatus)); + + // + // Log the error. + // + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 1 << 16 | interruptStatus + ); + + return TRUE; + + } // end switch + + // + // A bad physcial address was return by the adapter. + // Log it as an error. + // + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 5 << 16 | interruptStatus + ); + + return TRUE; + +} // end Aha174xInterrupt() + + +VOID +A174xBuildEcb( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Build ECB for Aha174x. + +Arguments: + + DeviceExtenson + SRB + +Return Value: + + Nothing. + +--*/ + +{ + PECB ecb = Srb->SrbExtension; + PSTATUS_BLOCK statusBlock = &ecb->StatusBlock; + ULONG length; + + // + // Set ECB command. + // + + ecb->Command = ECB_COMMAND_INITIATOR_COMMAND; + + // + // Disable updating status block on success; + // + + ecb->Flags[0] = ECB_FLAGS_DISABLE_STATUS_BLOCK; + + // + // initialize ECB flags + // + + ecb->Flags[1] = 0; + + // + // Set transfer direction bit. + // + + if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + + // + // Write command. + // + + ecb->Flags[1] |= ECB_FLAGS_WRITE; + + } else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + + // + // Read command. + // + + ecb->Flags[1] |= ECB_FLAGS_READ; + } + + // + // Check if disconnect explicity forbidden. + // + + if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) { + + ecb->Flags[1] |= ECB_FLAGS_NO_DISCONNECT; + } + + // + // Set LUN (bits 16, 17 and 18). + // + + ecb->Flags[1] |= Srb->Lun; + + // + // Set CDB length and copy to ECB. + // + + ecb->CdbLength = Srb->CdbLength; + ScsiPortMoveMemory(ecb->Cdb, Srb->Cdb, Srb->CdbLength); + + // + // Build SGL in ECB if data transfer. + // + + if (Srb->DataTransferLength > 0) { + ecb->Flags[0] |= ECB_FLAGS_SCATTER_GATHER; + A174xBuildSgl(DeviceExtension, Srb); + } else { + ecb->SglLength = 0; + } + + // + // Set status block pointer. + // + + ecb->StatusBlockAddress = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, + NULL, + statusBlock, + &length)); + + ASSERT(length >= sizeof(STATUS_BLOCK)); + + // + // Setup auto sense if necessary. + // + + if (Srb->SenseInfoBufferLength != 0 && + !(Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE)) { + + // + // Set the flag to enable auto sense and fill in the address and length + // of the sense buffer. + // + + ecb->Flags[0] |= ECB_FLAGS_AUTO_REQUEST_SENSE; + ecb->SenseInfoLength = Srb->SenseInfoBufferLength; + ecb->SenseInfoAddress = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, + Srb, + Srb->SenseInfoBuffer, + &length)); + + ASSERT(length >= Srb->SenseInfoBufferLength); + + } else { + + ecb->SenseInfoLength = 0; + } + + // + // Zero out next ECB, request sense info fields + // and statuses in status block. + // + + ecb->NextEcb = 0; + statusBlock->HaStatus = 0; + statusBlock->TargetStatus = 0; + + return; + +} // end A174xBuildEcb() + + +VOID +A174xBuildSgl( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine builds a scatter/gather descriptor list for the ECB. + +Arguments: + + DeviceExtension + Srb + +Return Value: + + None + +--*/ + +{ + PVOID dataPointer = Srb->DataBuffer; + ULONG bytesLeft = Srb->DataTransferLength; + PECB ecb = Srb->SrbExtension; + PSGL sgl = &ecb->Sgl; + ULONG physicalSgl; + ULONG physicalAddress; + ULONG length; + ULONG 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 { + + // + // Get physical address and length of contiguous + // physical buffer. + // + + physicalAddress = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, + Srb, + dataPointer, + &length)); + + // + // 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 ECB. + // + + ecb->SglLength = descriptorCount * sizeof(SG_DESCRIPTOR); + + // + // Write SGL address to ECB. + // + + ecb->PhysicalSgl = physicalSgl; + + return; + +} // end A174xBuildSgl() + + +VOID +A174xBuildRequestSense( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine is called when a request sense is detected. An adapter + command is then built for a request sense. This is the + only way to clear the contingent alligience condition that the adapter + is always in following a check condition. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + TRUE is request succeeds. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PEISA_CONTROLLER eisaController = deviceExtension->EisaController; + PECB ecb = Srb->SrbExtension; + PSTATUS_BLOCK statusBlock = &ecb->StatusBlock; + ULONG length; + + // + // Set ECB command. + // + + ecb->Command = ECB_COMMAND_READ_SENSE_INFO; + + // + // Disable updating status block on success and enable + // automatic request senes. + // + + ecb->Flags[0] = ECB_FLAGS_DISABLE_STATUS_BLOCK | + ECB_FLAGS_SUPPRESS_UNDERRUN; + + // + // Set transfer direction bit. + // + + ecb->Flags[1] = ECB_FLAGS_READ; + + // + // Check if disconnect explicity forbidden. + // + + if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) { + + ecb->Flags[1] |= ECB_FLAGS_NO_DISCONNECT; + } + + // + // Set LUN (bits 16, 17 and 18). + // + + ecb->Flags[1] |= Srb->Lun; + + // + // Set status block pointer. + // + + ecb->StatusBlockAddress = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(deviceExtension, + NULL, + statusBlock, + &length)); + + // + // Set request sense address and length. + // + + ecb->SenseInfoAddress = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(deviceExtension, + Srb, + Srb->DataBuffer, + &length)); + + ASSERT(length >= Srb->DataTransferLength); + + ecb->SenseInfoLength = (UCHAR) Srb->DataTransferLength; + + // + // Zero out next ECB, request sense info fields + // and statuses in status block. + // + + ecb->NextEcb = 0; + statusBlock->HaStatus = 0; + statusBlock->TargetStatus = 0; + + return; + +} // end A174xBuildRequestSense() + + +BOOLEAN +A174xSendCommand( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN UCHAR OperationCode, + IN ULONG Address + ) + +/*++ + +Routine Description: + + Send ECB or immediate command to AHA174X. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + OperationCode - value to be written to attention register + Address - ECB address or immediate command + +Return Value: + + True if command sent. + False if adapter never reached 'ready for next command' state. + +--*/ + +{ + PEISA_CONTROLLER eisaController = DeviceExtension->EisaController; + ULONG i; + + for (i=0; i<10; i++) { + + UCHAR status; + + status = ScsiPortReadPortUchar(&eisaController->Status); + + if ((status & MAILBOX_OUT_EMPTY) && + !(status & ADAPTER_BUSY)) { + + // + // Adapter ready for next command. + // + + break; + + } else { + + // + // Stall 50 microseconds before trying again. + // + + ScsiPortStallExecution(50); + } + } + + if (i == 10) { + + return FALSE; + } + + // + // Write ECB address or immediate command. + // + + ScsiPortWritePortUlong(&eisaController->MailBoxOut, Address); + + // + // Write operation code to attention register. + // + + ScsiPortWritePortUchar(&eisaController->Attention, OperationCode); + + return TRUE; + +} // end A174xSendCommand() + +BOOLEAN +Aha174xResetBus( + IN PVOID HwDeviceExtension, + IN ULONG PathId +) + +/*++ + +Routine Description: + + Reset Aha174x SCSI adapter and SCSI bus. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + Nothing. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PEISA_CONTROLLER eisaController = deviceExtension->EisaController; + ULONG j; + UCHAR targetId; + UCHAR lun; + + + UNREFERENCED_PARAMETER(PathId); + + DebugPrint((2,"ResetBus: Reset Aha174x and SCSI bus\n")); + + // + // Clean up pending requests. + // + + if (deviceExtension->PendingSrb) { + + // + // Notify the port driver that another request can be accepted. + // + + ScsiPortNotification(NextRequest, deviceExtension); + + // + // Clear the pending request. It will be completed by + // ScsiPortCompleteRequest. + // + + deviceExtension->PendingSrb = NULL; + + } + + // + // Clear the reqeust counts. + // + + for (targetId = 0; targetId < 8; targetId++) { + for (lun = 0; lun < 8; lun++) { + + deviceExtension->RequestCount[targetId][lun] = 0; + } + } + + // + // Complete all outstanding requests. + // + + ScsiPortCompleteRequest(deviceExtension, + 0, + SP_UNTAGGED, + SP_UNTAGGED, + SRB_STATUS_BUS_RESET); + + targetId = deviceExtension->HostTargetId; + + // + // Allow the adapter card to settle. + // + + ScsiPortStallExecution(75000); + ScsiPortReadPortUchar(&eisaController->Status); + ScsiPortStallExecution(1); + + if (!A174xSendCommand(deviceExtension, + (UCHAR)(IMMEDIATE_COMMAND | targetId), + ECB_IMMEDIATE_RESET)) { + + // + // Timed out waiting for adapter to become ready. + // + + ScsiPortLogError( + deviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 4 << 16 + ); + + // + // Adapter never reached state to receive command. + // Try a hard reset by wiggling the control line. + // + + ScsiPortWritePortUchar(&eisaController->Control, HARD_RESET); + + // + // Wait at least 10 microseconds. + // + + ScsiPortStallExecution(10); + + // + // Clear the reset line now that it has been held for 10 us. + // + + ScsiPortWritePortUchar(&eisaController->Control, 0); + + // + // Write the attention register to wake up the firmware so that + // it will clear the busy line in the status register. + // The attention value written (0) is ignored by the controller + // but will wakeup the firmware. + // + + ScsiPortStallExecution(20000); // Add a little delay + ScsiPortWritePortUchar(&eisaController->Attention, 0); + + // + // Wait for busy to go low. + // + + j = 0; + while (ScsiPortReadPortUchar(&eisaController->Status) & ADAPTER_BUSY) { + + j++; + if (j > 200000) { + + // + // Busy has not gone low. Assume the card is gone. + // Log the error and fail the request. + // + + + ScsiPortLogError( + deviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 3 << 16 + ); + + return FALSE; + + } + + ScsiPortStallExecution(10); + } + } + + return TRUE; + +} // end Aha174xResetBus() + + +VOID +A174xMapStatus( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PSTATUS_BLOCK StatusBlock + ) + +/*++ + +Routine Description: + + Translate Aha174x error to SRB error. + +Arguments: + + SRB + Status block for request completing with error. + +Return Value: + + Updated SRB + +--*/ + +{ + ULONG logError = 0; + UCHAR srbStatus; + PECB ecb = Srb->SrbExtension; + + + DebugPrint((2, + "A174xMapStatus: Status word is %x\n", + StatusBlock->StatusWord)); + + if (StatusBlock->TargetStatus == SCSISTAT_CHECK_CONDITION) { + + // + // A check condition occured. Set the srb status and process the + // auto sense data. + // + + Srb->SrbStatus = SRB_STATUS_ERROR; + + // + // Set target SCSI status in SRB. + // + + Srb->ScsiStatus = StatusBlock->TargetStatus; + + // + // Update SRB with actual bytes transferred. + // + + Srb->DataTransferLength -= StatusBlock->ResidualByteCount; + + if (StatusBlock->StatusWord & SB_STATUS_SENSE_INFORMATION) { + + // + // Indicate the sense information is valid and update the length. + // + + Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; + Srb->SenseInfoBufferLength = StatusBlock->RequestSenseLength; + } + + return; + } + + switch (StatusBlock->HaStatus) { + + case SB_HASTATUS_SELECTION_TIMEOUT: + srbStatus = SRB_STATUS_SELECTION_TIMEOUT; + break; + + case SB_HASTATUS_DATA_OVERUNDER_RUN: + DebugPrint((1,"A174xMapStatus: Data over/underrun\n")); + + // + // Update SRB with actual bytes transferred. + // + + Srb->DataTransferLength -= StatusBlock->ResidualByteCount; + + srbStatus = SRB_STATUS_DATA_OVERRUN; + break; + + case SB_HASTATUS_UNEXPECTED_BUS_FREE: + DebugPrint((1,"A174xMapStatus: Unexpected bus free\n")); + logError = SP_PROTOCOL_ERROR; + srbStatus = SRB_STATUS_UNEXPECTED_BUS_FREE; + break; + + case SB_HASTATUS_INVALID_BUS_PHASE: + DebugPrint((1,"A174xMapStatus: Invalid bus phase\n")); + logError = SP_PROTOCOL_ERROR; + srbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE; + break; + + case SB_HASTATUS_TARGET_NOT_USED: + DebugPrint((1,"A174xMapStatus: Target not used\n")); + srbStatus = SRB_STATUS_NO_DEVICE; + break; + + case SB_HASTATUS_INVALID_ECB: + DebugPrint((1,"A174xMapStatus: Invalid ECB\n")); + logError = SP_INTERNAL_ADAPTER_ERROR; + srbStatus = SRB_STATUS_INVALID_REQUEST; + break; + + case SB_HASTATUS_ADAPTER_HARDWARE_ERROR: + DebugPrint((1,"A174xMapStatus: Hardware error\n")); + logError = SP_INTERNAL_ADAPTER_ERROR; + srbStatus = SRB_STATUS_ERROR; + break; + + case SB_HASTATUS_ADAPTER_RESET_BUS: + DebugPrint((1,"A174xMapStatus: Adapter reset bus\n")); + srbStatus = SRB_STATUS_BUS_RESET; + break; + + case SB_HASTATUS_DEVICE_RESET_BUS: + DebugPrint((1,"A174xMapStatus: Device reset bus\n")); + srbStatus = SRB_STATUS_BUS_RESET; + break; + + case SB_HASTATUS_CHECKSUM_FAILURE: + DebugPrint((1,"A174xMapStatus: Checksum failure\n")); + logError = SP_INTERNAL_ADAPTER_ERROR; + srbStatus = SRB_STATUS_ERROR; + break; + + case SB_HASTATUS_ADAPTER_ABORTED: + DebugPrint((1,"A174xMapStatus: Adapter aborted\n")); + srbStatus = SRB_STATUS_ABORTED; + break; + + case SB_HASTATUS_HOST_ABORTED: + DebugPrint((1,"A174xMapStatus: Host aborted\n")); + srbStatus = SRB_STATUS_ABORTED; + break; + + case SB_HASTATUS_FW_NOT_DOWNLOADED: + DebugPrint((1,"A174xMapStatus: Firmware not downloaded\n")); + logError = SP_INTERNAL_ADAPTER_ERROR; + srbStatus = SRB_STATUS_ERROR; + break; + + case SB_HASTATUS_INVALID_SGL: + DebugPrint((1,"A174xMapStatus: Invalid SGL\n")); + logError = SP_INTERNAL_ADAPTER_ERROR; + srbStatus = SRB_STATUS_INVALID_REQUEST; + break; + + case SB_HASTATUS_REQUEST_SENSE_FAILED: + DebugPrint((1,"A174xMapStatus: Request sense failed\n")); + srbStatus = SRB_STATUS_ERROR; + break; + + default: + + srbStatus = SRB_STATUS_ERROR; + + // + // Check status block word. + // + + if (StatusBlock->StatusWord & SB_STATUS_NO_ERROR) { + + // + // This should never happen as this routine is only + // called when there is an error. + // + + DebugPrint((1,"A174xMapStatus: No error\n")); + srbStatus = SRB_STATUS_SUCCESS; + break; + + } + + // + // Check for underrun. + // + + if (StatusBlock->StatusWord & SB_STATUS_DATA_UNDERRUN) { + + DebugPrint((1, + "A174xMapStatus: Data underrun indicated in status word\n")); + + // + // Update SRB with actual bytes transferred. + // + + Srb->DataTransferLength -= StatusBlock->ResidualByteCount; + break; + } + + // + // Check for overrun. + // + + if (StatusBlock->StatusWord & SB_STATUS_DATA_OVERRUN) { + + DebugPrint((1, + "A174xMapStatus: Data overrun indicate in status word\n")); + logError = SP_PROTOCOL_ERROR; + break; + } + + // + // Check for initialization required. + // + + if (StatusBlock->StatusWord & SB_STATUS_INIT_REQUIRED) { + DebugPrint((1, + "A174xMapStatus: Initialization required\n")); + break; + } + + // + // Check for contingent allegience condition. If this happens + // something is very wrong (because autorequest sense was indicated). + // + + if (StatusBlock->StatusWord & SB_STATUS_EXT_CONT_ALLEGIANCE) { + + DebugPrint((1, + "A174xMapStatus: Contingent allegiance condition\n")); + + ASSERT(0); + } + + if (StatusBlock->StatusWord & SB_STATUS_MAJOR_ERROR) { + + DebugPrint((1, + "A174xMapStatus: Major error indicated in status word\n")); + break; + } + + logError = SP_INTERNAL_ADAPTER_ERROR; + break; + + } // end switch ... + + if (logError != 0) { + + // + // Log error. + // + + ScsiPortLogError( + DeviceExtension, + Srb, + Srb->PathId, + Srb->TargetId, + Srb->Lun, + logError, + 2 << 16 | StatusBlock->HaStatus + ); + + } + + // + // Set SRB status. + // + + Srb->SrbStatus = srbStatus; + + // + // Set target SCSI status in SRB. + // + + Srb->ScsiStatus = StatusBlock->TargetStatus; + + return; + +} // end A174xMapStatus() |