summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/ultra14f/ultra14f.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/miniport/ultra14f/ultra14f.c')
-rw-r--r--private/ntos/miniport/ultra14f/ultra14f.c1561
1 files changed, 1561 insertions, 0 deletions
diff --git a/private/ntos/miniport/ultra14f/ultra14f.c b/private/ntos/miniport/ultra14f/ultra14f.c
new file mode 100644
index 000000000..9f406c515
--- /dev/null
+++ b/private/ntos/miniport/ultra14f/ultra14f.c
@@ -0,0 +1,1561 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ ultra14f.c
+
+Abstract:
+
+ This is the port driver for the ULTRASTOR 14F ISA SCSI adapter.
+
+Author:
+
+ Stephen Fong
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "miniport.h"
+#include "ultra14f.h" // includes scsi.h
+
+CONST ULONG AdapterAddresses[] = {0X330, 0X340, /* 0X310, */ 0X240, 0X230, 0X210, 0X140, 0X130, 0};
+
+//
+// Logical Unit extension
+//
+typedef struct _SPECIFIC_LU_EXTENSION {
+ UCHAR DisconnectErrorCount; // keep track of # of disconnect error
+} SPECIFIC_LU_EXTENSION, *PSPECIFIC_LU_EXTENSION;
+
+//
+// Device extension
+//
+
+typedef struct _HW_DEVICE_EXTENSION {
+ PU14_BASEIO_ADDRESS U14_BaseIO_Address;
+ UCHAR HostTargetId;
+} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
+
+
+//
+// Function declarations
+//
+// Functions that start with 'Ultra14f' are entry points
+// for the OS port driver.
+//
+
+ULONG
+DriverEntry(
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ );
+
+ULONG
+Ultra14fFindAdapter(
+ IN PVOID DeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ );
+
+BOOLEAN
+Ultra14fInitialize(
+ IN PVOID DeviceExtension
+ );
+
+BOOLEAN
+Ultra14fStartIo(
+ IN PVOID DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+Ultra14fInterrupt(
+ IN PVOID DeviceExtension
+ );
+
+BOOLEAN
+Ultra14fResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+ );
+
+//
+// This function is called from Ultra14fStartIo.
+//
+
+VOID
+BuildMscp(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN PSPECIFIC_LU_EXTENSION luExtension
+ );
+
+//
+// This function is called from BuildMscp.
+//
+
+VOID
+BuildSgl(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+//
+// This function is called from Ultra14fInterrupt.
+//
+
+VOID
+MapErrorToSrbStatus(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+
+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()
+
+--*/
+
+{
+ HW_INITIALIZATION_DATA hwInitializationData;
+ ULONG i;
+ ULONG AdapterCount = 0;
+
+ DebugPrint((1,"\n\nSCSI UltraStor 14f 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 = Ultra14fInitialize;
+ hwInitializationData.HwFindAdapter = Ultra14fFindAdapter;
+ hwInitializationData.HwStartIo = Ultra14fStartIo;
+ hwInitializationData.HwInterrupt = Ultra14fInterrupt;
+ hwInitializationData.HwResetBus = Ultra14fResetBus;
+
+ //
+ // Set number of access ranges and bus type.
+ //
+
+ hwInitializationData.NumberOfAccessRanges = 1;
+ hwInitializationData.AdapterInterfaceType = Isa;
+
+ //
+ // Indicate no buffer mapping but will need physical addresses.
+ //
+
+ hwInitializationData.NeedPhysicalAddresses = TRUE;
+
+ //
+ // Indicate auto request sense is supported.
+ //
+
+ hwInitializationData.AutoRequestSense = TRUE;
+
+ //
+ // Specify size of logical unit extension.
+ //
+ hwInitializationData.SpecificLuExtensionSize = sizeof(SPECIFIC_LU_EXTENSION);
+
+ //
+ // Specify size of extensions.
+ //
+
+ hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
+
+ //
+ // Ask for SRB extensions for MSCPs.
+ //
+
+ hwInitializationData.SrbExtensionSize = sizeof(MSCP);
+
+ return ScsiPortInitialize(DriverObject,
+ Argument2,
+ &hwInitializationData,
+ &AdapterCount);
+
+} // end DriverEntry()
+
+
+ULONG
+Ultra14fFindAdapter(
+ 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;
+ PU14_BASEIO_ADDRESS baseIoAddress;
+ PVOID ioSpace;
+ PULONG adapterCount = Context;
+ UCHAR interruptLevel;
+ UCHAR dmaChannel;
+ UCHAR productId1;
+ UCHAR productId2;
+
+ //
+ // Check to see if adapter present in system.
+ //
+
+ while (AdapterAddresses[*adapterCount]) {
+
+ //
+ // Get the system address for this card.
+ // The card uses I/O space.
+ //
+
+ baseIoAddress = ScsiPortGetDeviceBase(
+ deviceExtension, // HwDeviceExtension
+ ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType
+ ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber
+ ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount]),
+ 0x10, // NumberOfBytes
+ TRUE // InIoSpace
+ );
+
+ //
+ // Update the adapter count to indicate this IO addr has been checked.
+ //
+
+ (*adapterCount)++;
+
+ productId1 = ScsiPortReadPortUchar(&baseIoAddress->ProductId1);
+ productId2 = ScsiPortReadPortUchar(&baseIoAddress->ProductId2);
+
+ if ((productId1 == ULTRASTOR_14F_ID1) &&
+ ((productId2 & (0xF0)) == ULTRASTOR_14F_ID2)) {
+
+ DebugPrint((1,
+ "Ultra14f: Adapter found at io address %x\n",
+ baseIoAddress));
+
+ break;
+ }
+
+ //
+ // If an adapter was not found unmap it.
+ //
+
+ ScsiPortFreeDeviceBase(deviceExtension,
+ baseIoAddress);
+
+ } // end while (AdapterAddress ...
+
+ if (!AdapterAddresses[*adapterCount]) {
+
+ //
+ // 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 IO base address of Ultra 14F in device extension.
+ //
+
+
+ deviceExtension->U14_BaseIO_Address = baseIoAddress;
+
+ deviceExtension->HostTargetId = ConfigInfo->InitiatorBusId[0] =
+ ScsiPortReadPortUchar(&baseIoAddress->Config2) & 0x07;
+
+ //
+ // Indicate maximum transfer length in bytes.
+ //
+
+ ConfigInfo->MaximumTransferLength = MAXIMUM_TRANSFER_LENGTH;
+
+ //
+ // The supported maximum number of physical segments is 17,
+ // but 16 is used to circumvent a firmware bug.
+ //
+
+ ConfigInfo->NumberOfPhysicalBreaks = MAXIMUM_SG_DESCRIPTORS;
+
+ ConfigInfo->ScatterGather = TRUE;
+ ConfigInfo->Master = TRUE;
+ ConfigInfo->NumberOfBuses = 1;
+
+ //
+ // Get the system interrupt vector and IRQL.
+ //
+
+ interruptLevel =
+ ScsiPortReadPortUchar(&baseIoAddress->Config1) & 0x30;
+
+ switch (interruptLevel) {
+ case US_INTERRUPT_LEVEL_15:
+ ConfigInfo->BusInterruptLevel = 15;
+ break;
+ case US_INTERRUPT_LEVEL_14:
+ ConfigInfo->BusInterruptLevel = 14;
+ break;
+ case US_INTERRUPT_LEVEL_11:
+ ConfigInfo->BusInterruptLevel = 11;
+ break;
+ case US_INTERRUPT_LEVEL_10:
+ ConfigInfo->BusInterruptLevel = 10;
+ break;
+ default:
+ DebugPrint((1,"Ultra24fConfiguration: No interrupt level\n"));
+ return SP_RETURN_ERROR;
+ }
+
+ DebugPrint((2,"Ultra14f: IRQ Configurated at %x\n",
+ ConfigInfo->BusInterruptLevel));
+
+ //
+ // If product sub-model is 1 (indicate 34L), don't setup DMA
+ //
+
+ if (!(productId2 & 0x01)) {
+
+ //
+ // Get the dma Channel
+ //
+
+ dmaChannel =
+ ScsiPortReadPortUchar(&baseIoAddress->Config1) & 0xC0;
+
+ switch (dmaChannel) {
+ case US_DMA_CHANNEL_5:
+ ConfigInfo->DmaChannel = 5;
+ break;
+ case US_DMA_CHANNEL_6:
+ ConfigInfo->DmaChannel = 6;
+ break;
+ case US_DMA_CHANNEL_7:
+ ConfigInfo->DmaChannel = 7;
+ break;
+ case US_DMA_CHANNEL_5_RESERVED:
+ ConfigInfo->DmaChannel = 5;
+ break;
+ default:
+ DebugPrint((1,"Ultra14fFindAdapter: Invalid DMA channel\n"));
+ return SP_RETURN_ERROR;
+ } //end switch
+
+ DebugPrint((2,"Ultra14f: DMA Configurated at %x\n",
+ ConfigInfo->DmaChannel));
+
+ } //end if
+
+ //
+ // Check if ISA TSR port is enabled.
+ //
+
+ if ((ScsiPortReadPortUchar(&baseIoAddress->Config2) & 0xC0) ==
+ US_ISA_SECONDARY_ADDRESS) {
+
+ DebugPrint((1,
+ "Ultra14fFindAdapter: ATDISK emulation at secondary address\n"));
+
+ ConfigInfo->AtdiskSecondaryClaimed = TRUE;
+
+ } else if ((ScsiPortReadPortUchar(&baseIoAddress->Config2) & 0xC0) ==
+ US_ISA_PRIMARY_ADDRESS) {
+
+ DebugPrint((1,
+ "Ultra24fConfiguration: ATDISK emulation at primary address\n"));
+
+ ConfigInfo->AtdiskPrimaryClaimed = TRUE;
+ }
+
+ //
+ // Fill in the access array information.
+ //
+
+ (*ConfigInfo->AccessRanges)[0].RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*adapterCount - 1]);
+
+ (*ConfigInfo->AccessRanges)[0].RangeLength = 16;
+ (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
+
+ return SP_RETURN_FOUND;
+
+} // end Ultra14fFindAdapter()
+
+
+BOOLEAN
+Ultra14fInitialize(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Inititialize adapter.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE - if initialization successful.
+ FALSE - if initialization unsuccessful.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PU14_BASEIO_ADDRESS u14_baseio_address = deviceExtension->U14_BaseIO_Address;
+ UCHAR status;
+ ULONG i;
+
+ //
+ // issue a Soft reset and a SCSI bus reset to Ultra14F
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->LocalDoorBellInterrupt,
+ US_HA_SOFT_RESET);
+
+ ScsiPortStallExecution(500*1000); //stall about 0.5 seconds
+
+ //
+ // Wait up to 1500,000 microseconds (1.5 sec) for adapter to initialize
+ // Keep polling for Soft Reset bit to be clear.
+ //
+
+ for (i = 0; i < 150000; i++) {
+
+ ScsiPortStallExecution(10);
+
+ status = ScsiPortReadPortUchar(
+ &u14_baseio_address->LocalDoorBellInterrupt);
+
+ if (!(status & US_HA_SOFT_RESET)) {
+
+ break;
+ }
+ }
+
+ //
+ // Check if reset failed or succeeded.
+ //
+
+ if (status & US_HA_SOFT_RESET) {
+ DebugPrint((1, "Ultra14F, HwInitialize: Soft reset failed.\n"));
+ }
+
+ //
+ // Enable ICMINT and SINTEN
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->SystemDoorBellMask,
+ US_ENABLE_ICMINT+US_ENABLE_SYSTEM_DOORBELL);
+
+ //
+ // reset the ICMINT
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->SystemDoorBellInterrupt,
+ US_RESET_ICMINT);
+
+ return(TRUE);
+
+ //
+ // Enable system doorbell interrupt and Incoming mail interrupt.
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->SystemDoorBellMask,
+ (US_ENABLE_ICMINT + US_ENABLE_SYSTEM_DOORBELL));
+
+} // end Ultra14fInitialize()
+
+
+BOOLEAN
+Ultra14fStartIo(
+ 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 MSCP.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+ Srb - IO request packet
+
+Return Value:
+
+ TRUE
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PU14_BASEIO_ADDRESS u14_baseio_address = deviceExtension->U14_BaseIO_Address;
+ PSPECIFIC_LU_EXTENSION luExtension;
+ PMSCP mscp;
+ PSCSI_REQUEST_BLOCK abortedSrb;
+ ULONG physicalMscp;
+ ULONG length;
+ ULONG i = 0;
+
+ //
+ // Check for ABORT command.
+ //
+
+ if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) {
+
+ DebugPrint((1, "Ultra14fStartIo: Received Abort command\n"));
+
+ //
+ // 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, "Ultra14fStartIo: 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);
+
+ } else {
+
+ //
+ // Check if interrupt pending.
+ //
+
+ if (ScsiPortReadPortUchar(&u14_baseio_address->SystemDoorBellInterrupt)
+ & US_ICMINT) {
+
+ DebugPrint((1,"Ultra14fStartIo: Interrupt pending\n"));
+
+ //
+ // Reset the ICMINT system doorbell interrupt.
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->SystemDoorBellInterrupt,
+ US_RESET_ICMINT);
+
+ //
+ // Abort the outstanding SRB.
+ //
+
+ abortedSrb->SrbStatus = SRB_STATUS_ABORTED;
+
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ abortedSrb);
+
+ //
+ // Complete abort SRB.
+ //
+
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+
+ //
+ // Adapter ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+ } else {
+
+ //
+ // Ultra14f adapter does not support the abort command.
+ // Reset the bus. The reset routine will complete all
+ // outstanding requests and indicate ready for next request.
+ //
+
+ if (!Ultra14fResetBus(deviceExtension, Srb->PathId)) {
+
+ DebugPrint((1,
+ "Ultra14fStartIo: Reset scsi bus failed\n"));
+ }
+
+ //
+ // Adapter ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+ }
+ }
+
+ return TRUE;
+
+ } else {
+
+ //
+ // This is a request to a device.
+ //
+
+ mscp = Srb->SrbExtension;
+
+ //
+ // Save SRB back pointer in MSCP.
+ //
+
+ mscp->SrbAddress = Srb;
+ mscp->AbortSrb = NULL;
+ }
+
+ //
+ // Get MSCP physical address.
+ //
+
+ physicalMscp =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension, NULL, mscp, &length));
+
+ //
+ // Assume physical address is contiguous for size of ECB.
+ //
+
+ ASSERT(length >= sizeof(MSCP));
+
+ switch (Srb->Function) {
+
+ case SRB_FUNCTION_EXECUTE_SCSI:
+
+ //
+ // Determine the logical unit that this request is for.
+ //
+
+ luExtension = ScsiPortGetLogicalUnit(deviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+ //
+ // Build MSCP for this request.
+ //
+
+ BuildMscp(deviceExtension, Srb, luExtension);
+
+ 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
+
+ //
+ // wait for a free OGM Slot (OGMINT to be clear)
+ //
+
+ for (i=0; i<50000; i++) {
+
+ if (!(ScsiPortReadPortUchar(
+ &u14_baseio_address->LocalDoorBellInterrupt) &
+ US_OGMINT)) {
+ break;
+
+ } else {
+
+ //
+ // Stall 1 microsecond before trying again.
+ //
+
+ ScsiPortStallExecution(1);
+ }
+ }
+
+ if (i == 50000) {
+
+ //
+ // Let operating system time out SRB.
+ //
+
+ DebugPrint((1,"Ultra14fStartIo: Timed out waiting for mailbox\n"));
+
+ } else {
+
+ //
+ // Write MSCP pointer to mailbox.
+ //
+
+ ScsiPortWritePortUlong(&u14_baseio_address->OutGoingMailPointer,
+ physicalMscp);
+
+ //
+ // Issue a OGMINT to send to the host adapter
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->LocalDoorBellInterrupt,
+ US_OGMINT);
+
+ if (!mscp->DisableDisconnect) {
+
+ //
+ // Adapter ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest, deviceExtension);
+
+ }
+ }
+
+ return TRUE;
+
+} // end Ultra14fStartIo()
+
+
+BOOLEAN
+Ultra14fInterrupt(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt service routine for the Ultra14f SCSI adapter.
+ It reads the system Doorbell 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 MSCP 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;
+ PMSCP mscp;
+ PSCSI_REQUEST_BLOCK srb;
+ PU14_BASEIO_ADDRESS u14_baseio_address =
+ deviceExtension->U14_BaseIO_Address;
+ ULONG physicalMscp;
+
+ //
+ // Check interrupt pending.
+ //
+
+ if (!(ScsiPortReadPortUchar(&u14_baseio_address->SystemDoorBellInterrupt)
+ & US_ICMINT)) {
+
+ //
+ // Handle spurious interrupt.
+ //
+
+ DebugPrint((2,"Ultra14fInterrupt: Spurious interrupt\n"));
+
+ return FALSE;
+ }
+
+ //
+ // Get physical address of MSCP
+ //
+
+ physicalMscp = ScsiPortReadPortUlong(
+ &u14_baseio_address->InComingMailPointer);
+
+ //
+ // Get virtual MSCP address.
+ //
+
+ mscp = ScsiPortGetVirtualAddress(deviceExtension,
+ ScsiPortConvertUlongToPhysicalAddress(physicalMscp));
+
+ //
+ // Make sure the physical address was valid.
+ //
+
+ if (mscp == NULL) {
+
+ ScsiPortWritePortUchar(&u14_baseio_address->SystemDoorBellInterrupt,
+ US_RESET_ICMINT);
+
+ DebugPrint((1,"\n\nUltra14f Interrupt: recieve NULL MSCP\n"));
+
+ return FALSE;
+
+ } else {
+
+ //
+ // Get SRB from MSCP.
+ //
+
+ srb = mscp->SrbAddress;
+
+ //
+ // Check status of completing MSCP in mailbox. and update SRB statuses
+ //
+
+ if (!((mscp->AdapterStatus) | (mscp->TargetStatus))) {
+
+ srb->SrbStatus = SRB_STATUS_SUCCESS;
+ srb->ScsiStatus = SCSISTAT_GOOD;
+
+ } else {
+
+ //
+ // Translate adapter status to SRB status
+ // and log error if necessary.
+ //
+
+ MapErrorToSrbStatus(deviceExtension, srb);
+
+ }
+ }
+
+ if (mscp->DisableDisconnect) {
+
+ //
+ // Adapter ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest, deviceExtension);
+
+ }
+
+ //
+ // Call notification routine for the SRB.
+ //
+
+ ScsiPortNotification(RequestComplete,
+ (PVOID)deviceExtension,
+ srb);
+
+ //
+ // Reset the ICMINT system doorbell interrupt.
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->SystemDoorBellInterrupt,
+ US_RESET_ICMINT);
+
+ return TRUE;
+
+} // end Ultra14fInterrupt()
+
+
+VOID
+BuildMscp(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN PSPECIFIC_LU_EXTENSION luExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Build MSCP for Ultra14f from SRB.
+
+Arguments:
+
+ DeviceExtenson
+ SRB
+
+Return Value:
+
+ Nothing.
+
+--*/
+
+{
+ PMSCP mscp = Srb->SrbExtension;
+ ULONG length;
+
+ //
+ // Set MSCP command.
+ //
+
+ mscp->OperationCode = MSCP_OPERATION_SCSI_COMMAND;
+
+ //
+ // Check SRB for disabled disconnect.
+ //
+ // NOTE: UltraStor 14f SCSI adapter does not
+ // support disabling synchronous transfer
+ // per request.
+ //
+
+ if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) {
+ mscp->DisableDisconnect = TRUE;
+ } else {
+
+ //
+ // Earlier ultra14f firmware has a bug with disconnect.
+ // A counter is used here to check whether or not to disable
+ // disconnect due to disconnect related problem that already happened
+ // for the requested device,
+ //
+
+ if (luExtension->DisconnectErrorCount >= 5) {
+
+ if (luExtension->DisconnectErrorCount == 5) {
+
+ ScsiPortLogError(DeviceExtension,
+ NULL,
+ 0,
+ DeviceExtension->HostTargetId,
+ 0,
+ SP_BAD_FW_WARNING,
+ 1 << 16);
+ }
+
+ mscp->DisableDisconnect = TRUE;
+
+ }
+ else {
+
+ mscp->DisableDisconnect = FALSE;
+
+ }
+ }
+
+
+ //
+ // Set channel, target id and lun.
+ //
+
+ mscp->Channel = Srb->PathId;
+ mscp->TargetId = Srb->TargetId;
+ mscp->Lun = Srb->Lun;
+
+ //
+ // Set CDB length and copy to MSCP.
+ //
+
+ mscp->CdbLength = Srb->CdbLength;
+ ScsiPortMoveMemory(mscp->Cdb, Srb->Cdb, Srb->CdbLength);
+
+ //
+ // Build SGL in MSCP if data transfer.
+ //
+
+ if (Srb->DataTransferLength > 0) {
+
+ //
+ // Build scattergather descriptor list.
+ //
+
+ BuildSgl(DeviceExtension, Srb);
+
+ //
+ // Set transfer direction.
+ //
+
+ if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
+
+ //
+ // Write request.
+ //
+
+ mscp->TransferDirection = MSCP_TRANSFER_OUT;
+
+ } else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) {
+
+ //
+ // Read request.
+ //
+
+ mscp->TransferDirection = MSCP_TRANSFER_IN;
+ }
+
+ } else {
+
+ //
+ // Set up MSCP for no data transfer.
+ //
+
+ mscp->TransferDirection = MSCP_NO_TRANSFER;
+ mscp->DataLength = 0;
+ mscp->ScatterGather = FALSE;
+ mscp->SgDescriptorCount = 0;
+ }
+
+ //
+ // 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.
+ //
+
+ mscp->RequestSenseLength = Srb->SenseInfoBufferLength;
+ mscp->RequestSensePointer = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension,
+ Srb,
+ Srb->SenseInfoBuffer,
+ &length));
+
+ ASSERT(length >= Srb->SenseInfoBufferLength);
+
+ } else {
+
+ //
+ // Ultra14F uses nonzero request sense pointer
+ // as an indication of autorequestsense.
+ //
+
+ mscp->RequestSenseLength = 0;
+ mscp->RequestSensePointer = 0;
+ }
+
+ //
+ // Zero out command link, status and abort fields.
+ //
+
+ mscp->CommandLink = 0;
+ mscp->CommandLinkId = 0;
+ mscp->AdapterStatus = 0;
+ mscp->TargetStatus = 0;
+ mscp->AbortSrb = 0;
+
+ //
+ // Bypass cache.
+ //
+
+ mscp->UseCache = FALSE;
+
+ return;
+
+} // end BuildMscp()
+
+
+VOID
+BuildSgl(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a scatter/gather descriptor list in the MSCP.
+
+Arguments:
+
+ DeviceExtension
+ Srb
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PVOID dataPointer = Srb->DataBuffer;
+ ULONG bytesLeft = Srb->DataTransferLength;
+ PMSCP mscp = Srb->SrbExtension;
+ PSDL sdl = &mscp->Sdl;
+ ULONG physicalSdl;
+ ULONG physicalAddress;
+ ULONG length;
+ ULONG descriptorCount = 0;
+
+ //
+ // Get physical SDL address.
+ //
+
+ physicalSdl = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(DeviceExtension, NULL,
+ sdl, &length));
+
+ //
+ // Assume physical memory contiguous for sizeof(SGL) bytes.
+ //
+
+ ASSERT(length >= sizeof(SDL));
+
+ //
+ // Create SDL 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;
+ }
+
+ sdl->Descriptor[descriptorCount].Address = physicalAddress;
+ sdl->Descriptor[descriptorCount].Length = length;
+
+ //
+ // Adjust counts.
+ //
+
+ dataPointer = (PUCHAR)dataPointer + length;
+ bytesLeft -= length;
+ descriptorCount++;
+
+ } while (bytesLeft);
+
+ //
+ // Check for only one descriptor. As an optimization, in these
+ // cases, use nonscattergather requests.
+ //
+
+ if (descriptorCount == 1) {
+
+ //
+ // Set descriptor count to 0.
+ //
+
+ mscp->SgDescriptorCount = 0;
+
+ //
+ // Set data pointer to data buffer.
+ //
+
+ mscp->DataPointer = physicalAddress;
+
+ //
+ // Set data transfer length.
+ //
+
+ mscp->DataLength = Srb->DataTransferLength;
+
+ //
+ // Clear scattergather bit.
+ //
+
+ mscp->ScatterGather = FALSE;
+
+ } else {
+
+ //
+ // Write SDL count to MSCP.
+ //
+
+ mscp->SgDescriptorCount = (UCHAR)descriptorCount;
+
+ //
+ // Write SGL address to ECB.
+ //
+
+ mscp->DataPointer = physicalSdl;
+
+ mscp->DataLength = Srb->DataTransferLength;
+
+ //
+ // Indicate scattergather operation.
+ //
+
+ mscp->ScatterGather = TRUE;
+ }
+
+ return;
+
+} // end BuildSgl()
+
+
+BOOLEAN
+Ultra14fResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+)
+
+/*++
+
+Routine Description:
+
+ Reset Ultra14f SCSI adapter and SCSI bus.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ Nothing.
+
+--*/
+
+{
+
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PU14_BASEIO_ADDRESS u14_baseio_address =
+ deviceExtension->U14_BaseIO_Address;
+ ULONG j;
+
+ //
+ // The Ultra14f only supports a single SCSI channel.
+ //
+
+ UNREFERENCED_PARAMETER(PathId);
+
+
+ //
+ // Reset SCSI bus.
+ //
+
+ ScsiPortWritePortUchar(&u14_baseio_address->LocalDoorBellInterrupt,
+ (US_HA_SOFT_RESET | US_ENABLE_SCSI_BUS_RESET));
+
+ //
+ // Wait for local processor to clear both reset bits.
+ //
+
+ for (j=0; j<200000; j++) {
+
+ if (!(ScsiPortReadPortUchar(&u14_baseio_address->LocalDoorBellInterrupt) &
+ (US_HA_SOFT_RESET | US_ENABLE_SCSI_BUS_RESET))) {
+
+ break;
+ }
+
+ ScsiPortStallExecution(10);
+
+ } // end for (j=0 ...
+
+ if (j == 200000) {
+
+ DebugPrint((1,"Ultra14fResetBus: Reset timed out\n"));
+
+ //
+ // 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(200*1000); // has to wait about 2 seconds
+ // for F/W to be actually ready
+ // don't do this for now until
+ // Ha_Inquiry is implemented later
+
+ //
+ // Complete all outstanding requests.
+ //
+
+ ScsiPortCompleteRequest(deviceExtension,
+ (UCHAR)0,
+ (UCHAR)-1,
+ (UCHAR)-1,
+ SRB_STATUS_BUS_RESET);
+
+ //
+ // Adapter ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return TRUE;
+
+} // end Ultra14fResetBus()
+
+
+VOID
+MapErrorToSrbStatus(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+/*++
+
+Routine Description:
+
+ Translate Ultra14f error to SRB error.
+
+Arguments:
+
+ Device Extension for logging error
+ SRB
+
+Return Value:
+
+ Updated SRB
+
+--*/
+
+{
+ ULONG logError = 0;
+ UCHAR srbStatus;
+ PMSCP mscp = Srb->SrbExtension;
+ PSPECIFIC_LU_EXTENSION luExtension;
+
+ switch (mscp->AdapterStatus) {
+
+ case MSCP_NO_ERROR:
+ srbStatus = SRB_STATUS_ERROR | SRB_STATUS_AUTOSENSE_VALID;
+ break;
+
+ case MSCP_SELECTION_TIMEOUT:
+ srbStatus = SRB_STATUS_SELECTION_TIMEOUT;
+ DebugPrint((1,"MapErrorToSrbStatus: Target SCSI device not found\n"));
+ break;
+
+ case MSCP_BUS_UNDER_OVERRUN:
+ DebugPrint((1,"MapErrorToSrbStatus: Data over/underrun\n"));
+ srbStatus = SRB_STATUS_DATA_OVERRUN;
+ break;
+
+ case MSCP_UNEXPECTED_BUS_FREE:
+
+ //
+ // Ultra14F older firmware has a bug that causes unexpected bus free
+ // error and invalid SCSI command errors during disconnect. A counter
+ // is used here to keep track of the number of such error for each
+ // device. When the number of maximum disconnected error count is
+ // reached, the driver will disable disconnect for the device with the
+ // disconnected problem.
+ //
+
+ DebugPrint((1,"MapErrorToSrbStatus: Unexpected bus free\n"));
+
+ //
+ // Determine the logical unit that this request is for.
+ //
+
+ luExtension = ScsiPortGetLogicalUnit(DeviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+ logError = SP_PROTOCOL_ERROR;
+ luExtension->DisconnectErrorCount += 1;
+ srbStatus = SRB_STATUS_UNEXPECTED_BUS_FREE;
+ break;
+
+ case MSCP_ILLEGAL_SCSI_COMMAND:
+
+ //
+ // Ultra14F older firmware has a bug that causes unexpected bus free
+ // error and invalid SCSI command errors during disconnect. A counter
+ // is used here to keep track of the number of such error for each
+ // device. When the number of maximum disconnected error count is
+ // reached, the driver will disable disconnect for the device with the
+ // disconnected problem.
+ //
+
+ DebugPrint((1,"MapErrorToSrbStatus: Illegal SCSI Command\n"));
+
+ //
+ // Determine the logical unit that this request is for.
+ //
+
+ luExtension = ScsiPortGetLogicalUnit(DeviceExtension,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun);
+ luExtension->DisconnectErrorCount += 1;
+ srbStatus = SRB_STATUS_INVALID_REQUEST;
+ break;
+
+ case MSCP_INVALID_PHASE_CHANGE:
+ DebugPrint((1,"MapErrorToSrbStatus: Invalid bus phase\n"));
+ logError = SP_PROTOCOL_ERROR;
+ srbStatus = SRB_STATUS_PHASE_SEQUENCE_FAILURE;
+ break;
+
+ case MSCP_INVALID_COMMAND:
+ case MSCP_INVALID_PARAMETER:
+ case MSCP_INVALID_DATA_LIST:
+ case MSCP_INVALID_SG_LIST:
+// case MSCP_ILLEGAL_SCSI_COMMAND:
+ DebugPrint((1,"MapErrorToSrbStatus: Invalid command %x\n", mscp->AdapterStatus));
+ srbStatus = SRB_STATUS_INVALID_REQUEST;
+ break;
+
+ case MSCP_BUS_RESET_ERROR:
+ DebugPrint((1,"MapErrorToSrbStatus: Bus reset\n"));
+ srbStatus = SRB_STATUS_BUS_RESET;
+ break;
+
+ case MSCP_ABORT_NOT_FOUND:
+ case MSCP_SCSI_BUS_ABORT_ERROR:
+ DebugPrint((1,"MapErrorToSrbStatus: Abort not found\n"));
+ srbStatus = SRB_STATUS_ABORT_FAILED;
+ break;
+
+ default:
+ logError = SP_PROTOCOL_ERROR;
+ srbStatus = SRB_STATUS_ERROR;
+ break;
+
+ } // end switch ...
+
+ //
+ // Log error if indicated.
+ //
+
+ if (logError) {
+
+ ScsiPortLogError(
+ DeviceExtension,
+ Srb,
+ Srb->PathId,
+ Srb->TargetId,
+ Srb->Lun,
+ logError,
+ 2 << 16 | mscp->AdapterStatus
+ );
+ }
+
+ //
+ // Set SRB status.
+ //
+
+ Srb->SrbStatus = srbStatus;
+
+ //
+ // Set target SCSI status in SRB.
+ //
+
+ Srb->ScsiStatus = mscp->TargetStatus;
+
+ return;
+
+} // end MapErrorToSrbStatus()