diff options
Diffstat (limited to 'private/ntos/miniport/dtc/dtc329x.c')
-rw-r--r-- | private/ntos/miniport/dtc/dtc329x.c | 2588 |
1 files changed, 2588 insertions, 0 deletions
diff --git a/private/ntos/miniport/dtc/dtc329x.c b/private/ntos/miniport/dtc/dtc329x.c new file mode 100644 index 000000000..4be0fdeb7 --- /dev/null +++ b/private/ntos/miniport/dtc/dtc329x.c @@ -0,0 +1,2588 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + dtc329x.c + +Abstract: + + This is the port driver for the DTC 3290 and DTC 3292 + EISA SCSI Adapter. This code is based on Microsoft NT aha154x.c + modified. ALL modifications can be turned off by setting DTC329X + to 0 in dtc3290.h file. Also all the names starting aha154x and a154x + have been changed to dtc329x and d329x. + +Environment: + + kernel mode only + +Notes: + +Revision History: + +--*/ + +#include "miniport.h" +#include "dtc329x.h" // includes scsi.h + +// +// The following table specifies the ports to be checked when searching for +// an adapter. A zero entry terminates the search. +// + +CONST ULONG AdapterAddresses[] = {0X330, 0X334, 0X234, 0X134, 0X130, 0X230, 0}; + + +#ifdef DTC329X +#define DTC_MAILBOX_INITIALIZATION 0x42 +CONST ULONG DTC3290IOAddresses[] = {0X330, 0X334, 0X230, 0X234, 0X130, 0X134, 0}; +CONST ULONG DTC3292IOAddresses[] = {0,0,0X130, 0X134, 0X230, 0X234, 0X330, 0X334, 0}; +#endif + +// +// The following structure is allocated +// from noncached memory as data will be DMA'd to +// and from it. +// + +typedef struct _NONCACHED_EXTENSION { + + // + // Physical base address of mailboxes + // + + ULONG MailboxPA; + + // + // Mailboxes + // + + MBO Mbo[MB_COUNT]; + MBI Mbi[MB_COUNT]; + +} NONCACHED_EXTENSION, *PNONCACHED_EXTENSION; + +// +// Device extension +// + +typedef struct _HW_DEVICE_EXTENSION { + + // + // NonCached extension + // + + PNONCACHED_EXTENSION NoncachedExtension; + + // + // Adapter parameters + // + + PBASE_REGISTER BaseIoAddress; + + // + // Host Target id. + // + + UCHAR HostTargetId; + + BOOLEAN Dtc3290; + +} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION; + +// +// Logical unit extension +// + +typedef struct _HW_LU_EXTENSION { + PSCSI_REQUEST_BLOCK CurrentSrb; +} HW_LU_EXTENSION, *PHW_LU_EXTENSION; + + + +#ifdef DTC329X + +// Following algorithm provides the way to detect +// DTC 3x90 or DTC3x92 presented in the system by +// scanning EISA slot and check DTC controller ID +// pattern + + +#define ID_DTC_BYTE0 0x12 +#define ID_DTC_BYTE1 0x83 +#define ID_DTC_3x90_BYTE2 0x31 +#define ID_DTC_3x92_BYTE2 0x39 +#define D3292_IO_MASK 0x7 +#define D3290_IO_MASK 0xf + + + +#define DTC_GET_CONFIG_CMD 0x41 +#define DTC_ENABLE_INT_CMD 0x1 +#define DTC_CLR_INT_CMD 0x1 +#define DTC_RESET_CONFIG_CMD 0x0 + + + +#define PROD_ID_REG 0xc80 +#define D3292_IO_REG 0xc9f +#define D3290_IO_REG 0xc91 + +#define D3290_CONFIG_IO_REG 0xc90 +#define D3290_DOOR_BELL_REG 0xc8d +#define D3290_CLR_INT_IO_REG 0xc8f + +#define TOTAL_EISA_SLOT 0x10 + +#endif + + +// +// Function declarations +// +// Functions that start with 'D329X' are entry points +// for the OS port driver. +// + +ULONG +DriverEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ); + +ULONG +D329XEntry( + IN PVOID DriverObject, + IN PVOID Argument2 + ); + +ULONG +D329XDetermineInstalled( + IN PHW_DEVICE_EXTENSION HwDeviceExtension, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN OUT PULONG AdapterCount, + OUT PBOOLEAN Again + ); + +ULONG +D329XFindAdapter ( + IN PVOID HwDeviceExtension, + IN PVOID Context, + IN PVOID BusInformation, + IN PCHAR ArgumentString, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + OUT PBOOLEAN Again + ); + +BOOLEAN +D329XHwInitialize( + IN PVOID DeviceExtension + ); + +BOOLEAN +D329XStartIo( + IN PVOID DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +BOOLEAN +D329XInterrupt( + IN PVOID DeviceExtension + ); + +BOOLEAN +D329XResetBus( + IN PVOID HwDeviceExtension, + IN ULONG PathId + ); + +// +// This function is called from D329XStartIo. +// + +VOID +BuildCcb( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +// +// This function is called from BuildCcb. +// + +VOID +BuildSdl( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ); + +// +// This function is called from D329XInitialize. +// + +BOOLEAN +AdapterPresent( + IN PVOID HwDeviceExtension + ); + +// +// This function is called from D329XInterrupt. +// + +UCHAR +MapError( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PCCB Ccb + ); + +BOOLEAN +ReadCommandRegister( + IN PHW_DEVICE_EXTENSION DeviceExtension, + OUT PUCHAR DataByte + ); + +BOOLEAN +WriteCommandRegister( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN UCHAR AdapterCommand + ); + + + + +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 adapterCount; + ULONG i; + + DebugPrint((1,"\n\nSCSI DTC 329X 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 = D329XHwInitialize; + hwInitializationData.HwResetBus = D329XResetBus; + hwInitializationData.HwStartIo = D329XStartIo; + hwInitializationData.HwInterrupt = D329XInterrupt; + hwInitializationData.HwFindAdapter = D329XFindAdapter; + + // + // 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(HW_LU_EXTENSION); + + // + // Specifiy the bus type. + // + + hwInitializationData.AdapterInterfaceType = Eisa; + hwInitializationData.NumberOfAccessRanges = 1; + + // + // Ask for SRB extensions for CCBs. + // + + hwInitializationData.SrbExtensionSize = sizeof(CCB); + + // + // The adapter count is used by the find adapter routine to track how + // which adapter addresses have been tested. + // + + adapterCount = 0; + return ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &adapterCount); +} // end D329XEntry() + + +ULONG +D329XFindAdapter ( + 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 + Context - Register base address + ConfigInfo - Configuration information structure describing HBA + This structure is defined in PORT.H. + +Return Value: + + ULONG + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + ULONG length; + ULONG status; + UCHAR adapterTid; + UCHAR dmaChannel; + UCHAR irq; + UCHAR bit; + + // + // Determine if there are any adapters installed. Determine installed + // will initialize the BaseIoAddress if an adapter is found. + // + + status = D329XDetermineInstalled(deviceExtension, + ConfigInfo, + Context, + Again); + + // + // If there are not adapter's found then return. + // + + if (status != SP_RETURN_FOUND) { + return(status); + } + +#if 0 + // + // Set up device extension pointer to 154x mailboxes + // in noncached extension. + // + + deviceExtension->NoncachedExtension = 0; + + // + // Convert virtual to physical mailbox address. + // + + deviceExtension->NoncachedExtension->MailboxPA = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(deviceExtension, + NULL, + deviceExtension->NoncachedExtension->Mbo, + &length)); + + // + // Assume that physical address is below 16M + // + + ASSERT(deviceExtension->NoncachedExtension->MailboxPA < 0x1000000); + + // + // Reset the DTC329X. + // + + if (!D329XResetBus(deviceExtension, 0)) { + DebugPrint((1,"D329XFindAdapter: Reset SCSI bus failed\n")); + return FALSE; + } + + DebugPrint((1,"D329XFindAdapter: Reset completed\n")); +#endif + + // + // Issue adapter command to get IRQ. + // + // Returns 3 data bytes: + // + // Byte 0 Dma Channel + // + // Byte 1 Interrupt Channel + // + // Byte 2 Adapter SCSI ID + // + + if (!WriteCommandRegister(deviceExtension, + AC_RET_CONFIGURATION_DATA)) { + DebugPrint((1,"D329XFindAdapter: Get configuration data command failed\n")); + return SP_RETURN_ERROR; + } + + // + // Determine DMA channel. + // + + if (!ReadCommandRegister(deviceExtension,&dmaChannel)) { + DebugPrint((1,"D329XFindAdapter: Couldn't read dma channel\n")); + return SP_RETURN_ERROR; + } + +#if 0 // Commented out because EISA should not do this: 4/18/94 + WHICH_BIT(dmaChannel,bit); + + ConfigInfo->DmaChannel = bit; + + DebugPrint((2,"D329XFindAdapter: DMA channel is %x\n", + ConfigInfo->DmaChannel)); +#endif + + // + // Determine hardware interrupt vector. + // + + if (!ReadCommandRegister(deviceExtension,&irq)) { + DebugPrint((1,"D329XFindAdapter: Couldn't read adapter irq\n")); + return SP_RETURN_ERROR; + } + + WHICH_BIT(irq, bit); + + ConfigInfo->BusInterruptLevel = (UCHAR) 9 + bit; + + // + // Determine what SCSI bus id the adapter is on. + // + + if (!ReadCommandRegister(deviceExtension,&adapterTid)) { + DebugPrint((1,"D329XFindAdapter: Couldn't read adapter SCSI id\n")); + return SP_RETURN_ERROR; + } + + // + // Set number of buses. + // + + ConfigInfo->NumberOfBuses = 1; + ConfigInfo->InitiatorBusId[0] = adapterTid; + deviceExtension->HostTargetId = adapterTid; + + ConfigInfo->MaximumTransferLength = MAX_TRANSFER_SIZE; + ConfigInfo->ScatterGather = TRUE; + ConfigInfo->Master = TRUE; + ConfigInfo->NumberOfPhysicalBreaks = MAX_SG_DESCRIPTORS - 1; + + // + // Allocate a Noncached Extension to use for mail boxes. + // + + deviceExtension->NoncachedExtension = ScsiPortGetUncachedExtension( + deviceExtension, + ConfigInfo, + sizeof(NONCACHED_EXTENSION)); + + if (deviceExtension->NoncachedExtension == NULL) { + + // + // Log error. + // + + ScsiPortLogError( + deviceExtension, + NULL, + 0, + 0, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 6 << 16 + ); + + return(SP_RETURN_ERROR); + } + + // + // Convert virtual to physical mailbox address. + // + + deviceExtension->NoncachedExtension->MailboxPA = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(deviceExtension, + NULL, + deviceExtension->NoncachedExtension->Mbo, + &length)); + + // + // Assume that physical address is below 16M + // + + ASSERT(deviceExtension->NoncachedExtension->MailboxPA < 0x1000000); + +#if 0 + // + // Reset the DTC329X. + // + + if (!D329XResetBus(deviceExtension, 0)) { + DebugPrint((1,"D329XFindAdapter: Reset SCSI bus failed\n")); + return SP_RETURN_ERROR; + } + + DebugPrint((1,"D329XFindAdapter: Reset completed\n")); +#endif + + DebugPrint((3,"D329XFindAdapter: Configuration completed\n")); + + return SP_RETURN_FOUND; + +} // end D329XFindAdapter() + + +ULONG +D329XDetermineInstalled( + IN PHW_DEVICE_EXTENSION HwDeviceExtension, + IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, + IN OUT PULONG AdapterCount, + OUT PBOOLEAN Again + ) + +/*++ + +Routine Description: + + Determine if DTC 329X SCSI adapter is installed in system + by reading the status register as each base I/O address + and looking for a DTC EISA pattern. If an adapter is found, + the BaseIoAddres is initialized. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + + ConfigInfo - Supplies the known configuraiton information. + + AdapterCount - Supplies the count of adapter slots which have been tested. + + Again - Returns whehter the OS specific driver should call again. + +Return Value: + + Returns a status indicating whether a driver is present or not. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PBASE_REGISTER baseIoAddress; + PUCHAR ioSpace; + PUCHAR slotSpace; + UCHAR portValue; // 03-27-93 + BOOLEAN dtc3290; // 04-09-93, = true if dtc3290 else false + UCHAR status; + +#ifdef DTC329X + ULONG adapterio; + ULONG i; + ULONG j; + UCHAR loid; + UCHAR miid; + UCHAR hiid; + ULONG tempioindex; +#endif + + // + // Get the system physical address for this card. The card uses I/O space. + // + + ioSpace = ScsiPortGetDeviceBase( + HwDeviceExtension, // HwDeviceExtension + ConfigInfo->AdapterInterfaceType, // AdapterInterfaceType + ConfigInfo->SystemIoBusNumber, // SystemIoBusNumber + ScsiPortConvertUlongToPhysicalAddress(0), + 0x400, // NumberOfBytes + TRUE // InIoSpace + ); + + // + // Scan though the adapter address looking for adapters. + // + + while (AdapterAddresses[*AdapterCount] != 0) { + + + // + // Check to see if adapter present in system. + // + + baseIoAddress = (PBASE_REGISTER)(ioSpace + + AdapterAddresses[*AdapterCount]); + +#ifdef DTC329X + adapterio = AdapterAddresses[*AdapterCount]; +#endif + + + // + // Update the adapter count. + // + + (*AdapterCount)++; + + portValue = ScsiPortReadPortUchar((PUCHAR)baseIoAddress); + + // + // Check for Adaptec adapter. + // The mask (0x29) are bits that may or may not be set. + // The bit 0x10 (IOP_SCSI_HBA_IDLE) should be set. + // + + if (!((portValue & ~0x29) == IOP_SCSI_HBA_IDLE)) { + + // + // Board in funky state. Reset it. + // + + ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_HARD_RESET); + + ScsiPortStallExecution(500 * 1000); + + // + // Wait up to 5000 microseconds for adapter to initialize. + // + + for (i = 0; i < 5000; i++) { + + ScsiPortStallExecution(1); + + status = ScsiPortReadPortUchar((PUCHAR)baseIoAddress); + + if (status & IOP_SCSI_HBA_IDLE) { + break; + } + + } + + DebugPrint((2,"ResetBus: Wait done\n")); + + if (!(status & IOP_SCSI_HBA_IDLE)) { + + // + // The DTC sometimes needs a soft reset to recover. + // + + ScsiPortWritePortUchar(&deviceExtension->BaseIoAddress->StatusRegister, IOP_SOFT_RESET); + ScsiPortStallExecution(500 * 1000); + + // + // Wait up to 5000 microseconds for adapter to initialize. + // + + for (i = 0; i < 5000; i++) { + + ScsiPortStallExecution(5); + + status = ScsiPortReadPortUchar((PUCHAR)baseIoAddress); + + if (status & IOP_SCSI_HBA_IDLE) { + break; + } + + } + + } + } + + portValue = ScsiPortReadPortUchar((PUCHAR)baseIoAddress); + + if ((portValue & ~0x29) == IOP_SCSI_HBA_IDLE) { /* 03-27-93 */ + +#ifdef DTC329X + + // following code will make sure that only dtc3292 or dtc3290 board + // is recoginzed by this driver by checking current dtc board IO + // address (get from EISA config reg). + + + for (i= 0x1000; i< TOTAL_EISA_SLOT * 0x1000; i=i+0x1000) { + + slotSpace = ScsiPortGetDeviceBase( + HwDeviceExtension, + ConfigInfo->AdapterInterfaceType, + ConfigInfo->SystemIoBusNumber, + ScsiPortConvertUlongToPhysicalAddress(i), + 0x1000, + TRUE); + loid = ScsiPortReadPortUchar(slotSpace + PROD_ID_REG); + miid = ScsiPortReadPortUchar(slotSpace + PROD_ID_REG + 1); + hiid = ScsiPortReadPortUchar(slotSpace + PROD_ID_REG + 2); + + if (loid == ID_DTC_BYTE0 && miid == ID_DTC_BYTE1 ) { + + switch (hiid ) { + case ID_DTC_3x90_BYTE2: + ScsiPortReadPortUchar(slotSpace+D3290_CONFIG_IO_REG); // dummy read + ScsiPortWritePortUchar(slotSpace+D3290_CONFIG_IO_REG,DTC_GET_CONFIG_CMD); + ScsiPortWritePortUchar(slotSpace+D3290_DOOR_BELL_REG,DTC_ENABLE_INT_CMD); + for (j = 0; j < 2000000; j++) { + ScsiPortStallExecution(1); + if (ScsiPortReadPortUchar(slotSpace+D3290_CONFIG_IO_REG) & 0x80) + break; + } + + tempioindex = ScsiPortReadPortUchar(slotSpace+D3290_IO_REG); + ScsiPortWritePortUchar(slotSpace+D3290_CLR_INT_IO_REG,DTC_CLR_INT_CMD); + ScsiPortWritePortUchar(slotSpace+D3290_CONFIG_IO_REG,DTC_RESET_CONFIG_CMD); + + ScsiPortStallExecution(4000 * 1000); /* 03-27-93 */ + + if (adapterio == DTC3290IOAddresses[tempioindex & D3290_IO_MASK]) + { + dtc3290 = TRUE; + goto find_dtc; + } + else + break; + + case ID_DTC_3x92_BYTE2: + tempioindex = ScsiPortReadPortUchar(slotSpace+D3292_IO_REG); + if (adapterio == DTC3292IOAddresses[tempioindex & D3292_IO_MASK]) + { + dtc3290 = FALSE; + goto find_dtc; + } + else + break; + + default: + break; + } + } + ScsiPortFreeDeviceBase(HwDeviceExtension, + (PVOID) slotSpace); + } + + if (i == TOTAL_EISA_SLOT * 0x1000 ) + continue; + + find_dtc: +#endif + + + DebugPrint((1,"D329X: Base IO address is %x\n", baseIoAddress)); + + // + // An adapter has been found. Set the base address in the device + // extension, and request another call. + // + + ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_SOFT_RESET); /* 03-27-93 */ + ScsiPortStallExecution(3000*1000); + HwDeviceExtension->BaseIoAddress = baseIoAddress; + *Again = TRUE; + + // + // Fill in the access array information. + // + + (*ConfigInfo->AccessRanges)[0].RangeStart = + ScsiPortConvertUlongToPhysicalAddress( + AdapterAddresses[*AdapterCount - 1]); + (*ConfigInfo->AccessRanges)[0].RangeLength = 4; + (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE; + + deviceExtension->Dtc3290 = dtc3290; + + if (dtc3290) ConfigInfo->CachesData = TRUE; // 04-09-93 + else ConfigInfo->CachesData = FALSE; + + return SP_RETURN_FOUND; + } + } + + // + // The entire table has been searched and no adapters have been found. + // There is no need to call again and the device base can now be freed. + // Clear the adapter count for the next bus. + // + + *Again = FALSE; + *(AdapterCount) = 0; + + ScsiPortFreeDeviceBase( + HwDeviceExtension, + ioSpace + ); + + return SP_RETURN_NOT_FOUND; + +} // end D329XDetermineInstalled() + + +BOOLEAN +D329XHwInitialize( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + This routine is a required entry point. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + TRUE - if initialization successful. + FALSE - if initialization unsuccessful. + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PNONCACHED_EXTENSION noncachedExtension = + deviceExtension->NoncachedExtension; + PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress; + UCHAR status; + ULONG i; + + DebugPrint((2,"D329XHwInitialize: Reset DTC329X and SCSI bus\n")); + + // + // Reset SCSI chip. + // + + ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_SOFT_RESET); + + ScsiPortStallExecution(2000*1000); /* 03-27-93 */ + + // + // Wait up to 5000 microseconds for adapter to initialize. + // + + for (i = 0; i < 5000; i++) { + + ScsiPortStallExecution(1); + + status = ScsiPortReadPortUchar(&deviceExtension->BaseIoAddress->StatusRegister); + + if (status & IOP_SCSI_HBA_IDLE) { + break; + } + } + + // + // Inform the port driver that the bus has been reset. + // + + ScsiPortNotification(ResetDetected, HwDeviceExtension, 0); + + // + // Check if reset failed or succeeded. + // + + if (!(status & IOP_SCSI_HBA_IDLE) || !(status & IOP_MAILBOX_INIT_REQUIRED)) { + + DebugPrint((1, "D329XHwInitialize: Soft reset failed.\n")); + + // + // If the soft reset does not work, try a hard reset. + // + + if (!D329XResetBus(HwDeviceExtension, 0)) { + DebugPrint((1,"D329XHwInitialize: Reset SCSI bus failed\n")); + return FALSE; + } + + DebugPrint((1,"D329XHwInitialize: Reset completed\n")); + + return TRUE; + } + + // + // Zero out mailboxes. + // + + for (i=0; i<MB_COUNT; i++) { + + PMBO mailboxOut; + PMBI mailboxIn; + + mailboxIn = &noncachedExtension->Mbi[i]; + mailboxOut = &noncachedExtension->Mbo[i]; + + mailboxOut->Command = mailboxIn->Status = 0; + } + + DebugPrint((3,"ResetBus: Initialize mailbox\n")); + +#ifndef DTC329X + if (!WriteCommandRegister(deviceExtension,AC_MAILBOX_INITIALIZATION)) { + DebugPrint((1,"D329XResetBus: Couldn't initialize mailboxes\n")); + return FALSE; + } + + // + // Send Adapter number of mailbox locations. + // + + if (!WriteCommandRegister(deviceExtension,MB_COUNT)) { + return FALSE; + } + + // + // Send the most significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte2)) { + return FALSE; + } + + // + // Send the middle byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte1)) { + return FALSE; + } + + // + // Send the least significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte0)) { + return FALSE; + } + +#else + + if (!WriteCommandRegister(deviceExtension,DTC_MAILBOX_INITIALIZATION)) { + DebugPrint((1,"D329XResetBus: Couldn't initialize mailboxes\n")); + return FALSE; + } + + + for (i=0; i < 250000; i++) + ScsiPortStallExecution(1); + + // + // Send Adapter number of mailbox locations. + // + + if (!WriteCommandRegister(deviceExtension,MB_COUNT)) { + return FALSE; + } + + // + // Send the least significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte0)) { + return FALSE; + } + + // + // Send the 2nd byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte1)) { + return FALSE; + } + + // + // Send the 3rd byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte2)) { + return FALSE; + } + + // + // Send the most significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte3)) { + return FALSE; + } + +#endif + + DebugPrint((3,"ResetBus: Initialize mailbox done\n")); + return TRUE; + +} // end D329XHwInitialize() + + +BOOLEAN +D329XStartIo( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine is called from the SCSI port driver synchronized + with the kernel. The mailboxes are scanned for an empty one and + the CCB is written to it. Then the doorbell is rung and the + OS port driver is notified that the adapter can take + another request, if any are available. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + Srb - IO request packet + +Return Value: + + TRUE + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PNONCACHED_EXTENSION noncachedExtension = + deviceExtension->NoncachedExtension; + PMBO mailboxOut; + PCCB ccb; + PHW_LU_EXTENSION luExtension; + ULONG physicalCcb; + ULONG length; + ULONG i = 0; + + DebugPrint((3,"D329XStartIo: Enter routine\n")); + + // + // Check if command is an ABORT request. + // + + if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) { + + // + // Verify that SRB to abort is still outstanding. + // + + luExtension = + ScsiPortGetLogicalUnit(deviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + + if (!luExtension->CurrentSrb) { + + DebugPrint((1, "D329XStartIo: 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 CCB to abort. + // + + ccb = Srb->NextSrb->SrbExtension; + + // + // Set abort SRB for completion. + // + + ccb->AbortSrb = Srb; + + } else { + + ccb = Srb->SrbExtension; + + // + // Save SRB back pointer in CCB. + // + + ccb->SrbAddress = Srb; + } + + // + // Get CCB physical address. + // + + physicalCcb = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(deviceExtension, NULL, ccb, &length)); + + // + // Assume physical address is contiguous for size of CCB. + // + + ASSERT(length >= sizeof(CCB)); + + // + // Find free mailboxOut. + // + + do { + + mailboxOut = &noncachedExtension->Mbo[i % MB_COUNT]; + i++; + + } while (mailboxOut->Command != MBO_FREE); + + DebugPrint((3,"D329XStartIo: MBO address %lx, Loop count = %d\n", mailboxOut, i)); + + // + // Write CCB to mailbox. + // + +#ifndef DTC329X + + FOUR_TO_THREE(&mailboxOut->Address, + (PFOUR_BYTE)&physicalCcb); +#else + + FOUR_TO_FOUR(&mailboxOut->Address,(PFOUR_BYTE)&physicalCcb); + +#endif + + + switch (Srb->Function) { + + case SRB_FUNCTION_ABORT_COMMAND: + + DebugPrint((1, "D329XStartIo: Abort request received\n")); + + // + // NOTE: Race condition if aborts occur + // (what if CCB to be aborted + // completes after setting new SrbAddress?) + // + + mailboxOut->Command = MBO_ABORT; + + break; + + case SRB_FUNCTION_RESET_BUS: + + // + // Reset DTC329X and SCSI bus. + // + + DebugPrint((1, "D329XStartIo: Reset bus request received\n")); + + if (!D329XResetBus( + deviceExtension, + Srb->PathId + )) { + + DebugPrint((1,"D329XStartIo: Reset bus failed\n")); + + Srb->SrbStatus = SRB_STATUS_ERROR; + + } else { + + Srb->SrbStatus = SRB_STATUS_SUCCESS; + } + + + ScsiPortNotification(RequestComplete, + deviceExtension, + Srb); + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + return TRUE; + + case SRB_FUNCTION_EXECUTE_SCSI: + + // + // Get logical unit extension. + // + + luExtension = ScsiPortGetLogicalUnit(deviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + + // + // Move SRB to logical unit extension. + // + + luExtension->CurrentSrb = Srb; + + // + // Build CCB. + // + + BuildCcb(deviceExtension, Srb); + + mailboxOut->Command = MBO_START; + + break; + + case SRB_FUNCTION_SHUTDOWN: + case SRB_FUNCTION_FLUSH: // 04-09-93, implement flush cache + + // + // Get logical unit extension. + // + + luExtension = + ScsiPortGetLogicalUnit(deviceExtension, + Srb->PathId, + Srb->TargetId, + Srb->Lun); + + // + // Move SRB to logical unit extension. + // + + luExtension->CurrentSrb = Srb; + + // + // Build CCB. + // + + BuildCcb(deviceExtension, Srb); + + ccb->Cdb[0] = REZERO_UNIT_CMD; // re-zero will force F/W to + // flush cache + ccb->Cdb[1] = 0; // LU + ccb->Cdb[2] = 0; // reserved + ccb->Cdb[3] = 0; // reserved + ccb->Cdb[4] = 0; // reserved + ccb->Cdb[5] = 0; // control byte + ccb->CdbLength = 6; + + mailboxOut->Command = MBO_START; + + DebugPrint((1, "D329XStartIo: Flush cache sent\n")); + + break; + + case SRB_FUNCTION_RESET_DEVICE: + + DebugPrint((1,"D329XStartIo: Reset device not supported\n")); + + // + // Drop through to default. + // + + 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 + + // + // Tell 154xb a CCB is available now. + // + + if (!WriteCommandRegister(deviceExtension,AC_START_SCSI_COMMAND)) { + + // + // Let request time out and fail. + // + + DebugPrint((1,"D329XStartIo: Can't write command to adapter\n")); + } + + // + // Adapter ready for next request. + // + + ScsiPortNotification(NextRequest, + deviceExtension, + NULL); + + return TRUE; + +} // end D329XStartIo() + + +BOOLEAN +D329XInterrupt( + IN PVOID HwDeviceExtension + ) + +/*++ + +Routine Description: + + This is the interrupt service routine for the adaptec 154x 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 CCB 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; + PNONCACHED_EXTENSION noncachedExtension = + deviceExtension->NoncachedExtension; + PCCB ccb; + PSCSI_REQUEST_BLOCK srb; + PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress; + PMBI mailboxIn; + ULONG physicalCcb; + PHW_LU_EXTENSION luExtension; + ULONG i; + + UCHAR InterruptFlags; + + InterruptFlags = ScsiPortReadPortUchar(&baseIoAddress->InterruptRegister); + + // + // Determine cause of interrupt. + // + + if (InterruptFlags & IOP_COMMAND_COMPLETE) { + + // + // Adapter command completed. + // + + DebugPrint((2,"D329XInterrupt: Adapter Command complete\n")); + DebugPrint((3,"D329XInterrupt: Interrupt flags %x\n", InterruptFlags)); + DebugPrint((3,"D329XInterrupt: Status %x\n", + ScsiPortReadPortUchar(&baseIoAddress->StatusRegister))); + + // + // Clear interrupt on adapter. + // + + ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET); + + return TRUE; + + } else if (InterruptFlags & IOP_MBI_FULL) { + + DebugPrint((3,"D329XInterrupt: MBI Full\n")); + + // + // Clear interrupt on adapter. + // + + ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET); + + } else if (InterruptFlags & IOP_SCSI_RESET_DETECTED) { + + DebugPrint((1,"D329XInterrupt: SCSI Reset detected\n")); + + // + // Clear interrupt on adapter. + // + + ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_INTERRUPT_RESET); + + // + // Notify of reset. + // + + ScsiPortNotification(ResetDetected, + deviceExtension, + NULL); + + return TRUE; + + } else { + + DebugPrint((4,"D329XInterrupt: Spurious interrupt\n")); + + return FALSE; + } + + // + // Determine which MailboxIn location contains the CCB. + // + + for (i=0; i<MB_COUNT; i++) { + + mailboxIn = &noncachedExtension->Mbi[i]; + + // + // Look for a mailbox entry with a legitimate status. + // + + if ((mailboxIn->Status == MBI_SUCCESS) || + (mailboxIn->Status == MBI_ERROR) || + (mailboxIn->Status == MBI_ABORT) || + (mailboxIn->Status == MBI_NOT_FOUND)) { + + // + // MBI found. + // + + DebugPrint((3,"D329XInterrupt: MBI address %lx\n", mailboxIn)); + break; + } + } + + if (i == MB_COUNT) { + + // + // No mail. Indicate interrupt was not serviced. + // + + DebugPrint((1, "D329XInterrupt: No CCB in mailboxes\n")); + + return TRUE; + } + + // + // Convert CCB to big endian. + // + +#ifndef DTC329X + + THREE_TO_FOUR((PFOUR_BYTE)&physicalCcb, + &mailboxIn->Address); +#else + + FOUR_TO_FOUR((PFOUR_BYTE)&physicalCcb, &mailboxIn->Address); + +#endif + + DebugPrint((3, "D329XInterrupt: Physical CCB %lx\n", physicalCcb)); + + // + // Check if physical CCB is zero. + // This is done to cover for hardware errors. + // + + if (!physicalCcb) { + + DebugPrint((1,"D329XInterrupt: Physical CCB address is 0\n")); + return TRUE; + } + + // + // Convert Physical CCB to Virtual. + // + + ccb = ScsiPortGetVirtualAddress(deviceExtension, ScsiPortConvertUlongToPhysicalAddress(physicalCcb)); + + + DebugPrint((3, "D329XInterrupt: Virtual CCB %lx\n", ccb)); + + // + // 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( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 5 << 8 + ); + + return(TRUE); + + } + + // + // Get SRB from CCB. + // + + srb = ccb->SrbAddress; + + // + // Get logical unit extension. + // + + luExtension = + ScsiPortGetLogicalUnit(deviceExtension, + srb->PathId, + srb->TargetId, + srb->Lun); + + // + // Make sure the luExtension was found and it has a current request. + // + + if (luExtension == NULL || (luExtension->CurrentSrb == NULL && + mailboxIn->Status != MBI_NOT_FOUND)) { + + + + // + // 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, + (6 << 8) | mailboxIn->Status + ); + + return(TRUE); + + } + + // + // Check MBI status. + // + + switch (mailboxIn->Status) { + + case MBI_SUCCESS: + + srb->SrbStatus = SRB_STATUS_SUCCESS; + + ASSERT(luExtension->CurrentSrb); + + luExtension->CurrentSrb = NULL; + + break; + + case MBI_NOT_FOUND: + + DebugPrint((1, "D329XInterrupt: CCB abort failed %lx\n", ccb)); + + srb = ccb->AbortSrb; + + srb->ScsiStatus = SRB_STATUS_ABORT_FAILED; + + // + // Check if SRB still outstanding. + // + + if (luExtension->CurrentSrb) { + + // + // Complete this SRB. + // + + luExtension->CurrentSrb->SrbStatus = SRB_STATUS_TIMEOUT; + + ScsiPortNotification(RequestComplete, + deviceExtension, + luExtension->CurrentSrb); + + luExtension->CurrentSrb = NULL; + } + + break; + + case MBI_ABORT: + + DebugPrint((1, "D329XInterrupt: CCB aborted\n")); + + // + // Update target status in aborted SRB. + // + + srb->SrbStatus = SRB_STATUS_ABORTED; + + // + // Call notification routine for the aborted SRB. + // + + ScsiPortNotification(RequestComplete, + deviceExtension, + srb); + + luExtension->CurrentSrb = NULL; + + // + // Get the abort SRB from CCB. + // + + srb = ccb->AbortSrb; + + // + // Set status for completing abort request. + // + + srb->SrbStatus = SRB_STATUS_SUCCESS; + + break; + + case MBI_ERROR: + + DebugPrint((2, "D329XInterrupt: Error occurred\n")); + + srb->SrbStatus = MapError(deviceExtension, srb, ccb); + + DebugPrint((2, "D329XInterrupt: SrbStatus = %x\n",srb->SrbStatus)); + // + // Check if ABORT command. + // + + if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) { + + // + // Check if SRB still outstanding. + // + + if (luExtension->CurrentSrb) { + + // + // Complete this SRB. + // + + luExtension->CurrentSrb->SrbStatus = SRB_STATUS_TIMEOUT; + + ScsiPortNotification(RequestComplete, + deviceExtension, + luExtension->CurrentSrb); + + } + + DebugPrint((1,"D329XInterrupt: Abort command failed\n")); + } + + luExtension->CurrentSrb = NULL; + + break; + + default: + + // + // Log the error. + // + + ScsiPortLogError( + HwDeviceExtension, + NULL, + 0, + deviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + (1 << 8) | mailboxIn->Status + ); + + DebugPrint((1, "D329XInterrupt: Unrecognized mailbox status\n")); + + mailboxIn->Status = MBI_FREE; + + return TRUE; + + } // end switch + + // + // Indicate MBI is available. + // + + mailboxIn->Status = MBI_FREE; + + DebugPrint((2, "D329XInterrupt: SCSI Status %x\n", srb->ScsiStatus)); + + DebugPrint((2, "D329XInterrupt: Adapter Status %x\n", ccb->HostStatus)); + + // + // Update target status in SRB. + // + + srb->ScsiStatus = ccb->TargetStatus; + + DebugPrint((2, "D329XInterrupt: Target Status %x\n", ccb->TargetStatus)); + + // + // Signal request completion. + // + + ScsiPortNotification(RequestComplete, + (PVOID)deviceExtension, + srb); + + return TRUE; + +} // end D329XInterrupt() + + +VOID +BuildCcb( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + Build CCB for 154x. + +Arguments: + + DeviceExtenson + SRB + +Return Value: + + Nothing. + +--*/ + +{ + PCCB ccb = Srb->SrbExtension; + + DebugPrint((3,"BuildCcb: Enter routine\n")); + + // + // Set CCB Operation Code. + // + + ccb->OperationCode = SCATTER_GATHER_COMMAND; + + // + // Set target id and LUN. + // + + ccb->ControlByte = (UCHAR)(Srb->TargetId << 5) | Srb->Lun; + + // + // Set transfer direction bit. + // + + if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) { + ccb->ControlByte |= CCB_DATA_XFER_OUT; + } else if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { + ccb->ControlByte |= CCB_DATA_XFER_IN; + } + + // + // 01h disables auto request sense. + // + + ccb->RequestSenseLength = 1; + + // + // Set CDB length and copy to CCB. + // + + ccb->CdbLength = (UCHAR)Srb->CdbLength; + + ScsiPortMoveMemory(ccb->Cdb, Srb->Cdb, ccb->CdbLength); + + // + // Set reserved bytes to zero. + // + + ccb->Reserved[0] = 0; + ccb->Reserved[1] = 0; + + ccb->LinkIdentifier = 0; + + // + // Zero link pointer. + // + + +#ifdef DTC329X + ccb->LinkPointer.Byte0 = 0; + ccb->LinkPointer.Byte1 = 0; + ccb->LinkPointer.Byte2 = 0; + ccb->LinkPointer.Byte3 = 0; +#else + ccb->LinkPointer.Lsb= 0; + ccb->LinkPointer.Msb = 0; + ccb->LinkPointer.Mid = 0; +#endif + + // + // Build SDL in CCB if data transfer. + // + if (Srb->DataTransferLength > 0) { + + BuildSdl(DeviceExtension, Srb); + + } else { + FOUR_TO_FOUR(&ccb->DataLength, (PFOUR_BYTE)&Srb->DataTransferLength); + FOUR_TO_FOUR(&ccb->DataPointer, (PFOUR_BYTE)&Srb->DataTransferLength); + ccb->OperationCode = SCSI_INITIATOR_COMMAND; + + } + + ccb->TargetStatus = 0; + ccb->HostStatus = 0; + + return; + +} // end BuildCcb() + + +VOID +BuildSdl( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb + ) + +/*++ + +Routine Description: + + This routine builds a scatter/gather descriptor list for the CCB. + +Arguments: + + DeviceExtension + Srb + +Return Value: + + None + +--*/ + +{ + PVOID dataPointer = Srb->DataBuffer; + ULONG bytesLeft = Srb->DataTransferLength; + PCCB ccb = Srb->SrbExtension; + PSDL sdl = &ccb->Sdl; + ULONG physicalSdl; + ULONG physicalAddress; + ULONG length; + ULONG four; + ULONG i = 0; + + DebugPrint((3,"BuildSdl: Enter routine\n")); + + // + // Get physical SDL address. + // + + physicalSdl = ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, NULL, + sdl, &length)); + + // + // Assume physical memory contiguous for sizeof(SDL) bytes. + // + + ASSERT(length >= sizeof(SDL)); + + // + // Create SDL segment descriptors. + // + + do { + + DebugPrint((3, "BuildSdl: Data buffer %lx\n", dataPointer)); + + // + // Get physical address and length of contiguous + // physical buffer. + // + + physicalAddress = + ScsiPortConvertPhysicalAddressToUlong( + ScsiPortGetPhysicalAddress(DeviceExtension, + Srb, + dataPointer, + &length)); + + DebugPrint((3, "BuildSdl: Physical address %lx\n", physicalAddress)); + DebugPrint((3, "BuildSdl: Data length %lx\n", length)); + DebugPrint((3, "BuildSdl: 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; + } + + four = length; + +#ifndef DTC329X + + // + // Convert length to 3-byte big endian format. + // + + three = &sdl->Sgd[i].Length; + FOUR_TO_THREE(three, (PFOUR_BYTE)&four); + +#else + // + // Keep original little endian format + // + FOUR_TO_FOUR(&sdl->Sgd[i].Length,(PFOUR_BYTE)&four); + +#endif + + four = (ULONG)physicalAddress; + +#ifndef DTC329X + + // + // Convert physical address to 3-byte big endian format. + // + + three = &sdl->Sgd[i].Address; + FOUR_TO_THREE(three, (PFOUR_BYTE)&four); + +#else + // + // Keep original little endian format + // + FOUR_TO_FOUR(&sdl->Sgd[i].Address,(PFOUR_BYTE)&four); + +#endif + i++; + + // + // Adjust counts. + // + + dataPointer = (PUCHAR)dataPointer + length; + bytesLeft -= length; + + } while (bytesLeft); + + // + // Write SDL length to CCB. + // + + four = i * sizeof(SGD); + +#ifndef DTC329X + + three = &ccb->DataLength; + FOUR_TO_THREE(three, (PFOUR_BYTE)&four); + +#else + + FOUR_TO_FOUR(&ccb->DataLength, (PFOUR_BYTE)&four); + +#endif + + DebugPrint((3,"BuildSdl: SDL length is %d\n", four)); + + // + // Write SDL address to CCB. + // + +#ifndef DTC329X + + FOUR_TO_THREE(&ccb->DataPointer, + (PFOUR_BYTE)&physicalSdl); +#else + + FOUR_TO_FOUR(&ccb->DataPointer,(PFOUR_BYTE)&physicalSdl); + +#endif + + + DebugPrint((3,"BuildSdl: SDL address is %lx\n", sdl)); + + DebugPrint((3,"BuildSdl: CCB address is %lx\n", ccb)); + + return; + +} // end BuildSdl() + + +BOOLEAN +D329XResetBus( + IN PVOID HwDeviceExtension, + IN ULONG PathId + ) + +/*++ + +Routine Description: + + Reset Adaptec 154X SCSI adapter and SCSI bus. + Initialize adpater mailbox. + +Arguments: + + HwDeviceExtension - HBA miniport driver's adapter data storage + +Return Value: + + Nothing. + + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + PNONCACHED_EXTENSION noncachedExtension = + deviceExtension->NoncachedExtension; + PBASE_REGISTER baseIoAddress = deviceExtension->BaseIoAddress; + UCHAR status; + ULONG i; + + DebugPrint((2,"ResetBus: Reset DTC329X and SCSI bus\n")); + + // + // Complete all outstanding requests with SRB_STATUS_BUS_RESET. + // + + ScsiPortCompleteRequest(deviceExtension, + (UCHAR)PathId, + (UCHAR)-1, + (UCHAR)-1, + SRB_STATUS_BUS_RESET); + + // + // Reset SCSI chip. + // + + ScsiPortWritePortUchar(&baseIoAddress->StatusRegister, IOP_HARD_RESET); + + ScsiPortStallExecution(500 * 1000); + + // + // Wait up to 5000 microseconds for adapter to initialize. + // + + for (i = 0; i < 5000; i++) { + + ScsiPortStallExecution(1); + + status = ScsiPortReadPortUchar(&deviceExtension->BaseIoAddress->StatusRegister); + + if (status & IOP_SCSI_HBA_IDLE) { + break; + } + + } + + DebugPrint((2,"ResetBus: Wait done\n")); + + if (!(status & IOP_SCSI_HBA_IDLE)) { + + // + // The DTC sometimes needs a soft reset to recover. + // + + ScsiPortWritePortUchar(&deviceExtension->BaseIoAddress->StatusRegister, IOP_SOFT_RESET); + ScsiPortStallExecution(500 * 1000); + + // + // Wait up to 5000 microseconds for adapter to initialize. + // + + for (i = 0; i < 5000; i++) { + + ScsiPortStallExecution(5); + + status = ScsiPortReadPortUchar(&deviceExtension->BaseIoAddress->StatusRegister); + + if (status & IOP_SCSI_HBA_IDLE) { + break; + } + + } + + } + + if (!(status & IOP_SCSI_HBA_IDLE)) { + return(FALSE); + } + + DebugPrint((2,"ResetBus: Hardreset OK\n")); + + // + // Zero out mailboxes. + // + + for (i=0; i<MB_COUNT; i++) { + + PMBO mailboxOut; + PMBI mailboxIn; + + mailboxIn = &noncachedExtension->Mbi[i]; + mailboxOut = &noncachedExtension->Mbo[i]; + + mailboxOut->Command = mailboxIn->Status = 0; + } + + DebugPrint((3,"D329XResetBus: Initialize mailbox\n")); + +#ifndef DTC329X + if (!WriteCommandRegister(deviceExtension,AC_MAILBOX_INITIALIZATION)) { + DebugPrint((1,"D329XResetBus: Couldn't initialize mailboxes\n")); + return FALSE; + } + + // + // Send Adapter number of mailbox locations. + // + + if (!WriteCommandRegister(deviceExtension,MB_COUNT)) { + return FALSE; + } + + // + // Send the most significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte2)) { + return FALSE; + } + + // + // Send the middle byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte1)) { + return FALSE; + } + + // + // Send the least significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte0)) { + return FALSE; + } + +#else + + + if (!WriteCommandRegister(deviceExtension,DTC_MAILBOX_INITIALIZATION)) { + DebugPrint((1,"D329XResetBus: Couldn't initialize mailboxes\n")); + return FALSE; + } + + + for (i=0; i < 250000; i++) + ScsiPortStallExecution(1); + + // + // Send Adapter number of mailbox locations. + // + + if (!WriteCommandRegister(deviceExtension,MB_COUNT)) { + return FALSE; + } + + // + // Send the least significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte0)) { + return FALSE; + } + + // + // Send the 2nd byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte1)) { + return FALSE; + } + + // + // Send the 3rd byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte2)) { + return FALSE; + } + + // + // Send the most significant byte of the mailbox physical address. + // + + if (!WriteCommandRegister(deviceExtension, + ((PFOUR_BYTE)&noncachedExtension->MailboxPA)->Byte3)) { + return FALSE; + } + +#endif + + + + DebugPrint((3,"D329XResetBus: Initialize mailbox ok\n")); + + + + return TRUE; + +} // end D329XResetBus() + + +UCHAR +MapError( + IN PVOID HwDeviceExtension, + IN PSCSI_REQUEST_BLOCK Srb, + IN PCCB Ccb + ) + +/*++ + +Routine Description: + + Translate D329X error to SRB error, and log an error if necessary. + +Arguments: + + HwDeviceExtension - The hardware device extension. + + Srb - The failing Srb. + + Ccb - Command Control Block contains error. + +Return Value: + + SRB Error + +--*/ + +{ + PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension; + UCHAR status; + ULONG logError; + + switch (Ccb->HostStatus) { + + +#ifdef DTC329X + + case CCB_SELECTION_TIMEOUT: + return SRB_STATUS_SELECTION_TIMEOUT; + + case CCB_COMPLETE: + if (Ccb->TargetStatus != SCSISTAT_GOOD) { + return SRB_STATUS_ERROR; + } else if (!deviceExtension->Dtc3290) { + + // + // The DTC 3292 does not update the scsi bus status assume the + // scsi bus status is a check condition. + // + + Ccb->TargetStatus = SCSISTAT_CHECK_CONDITION; + return SRB_STATUS_ERROR; + } + + // + // Fall through to the under run case. + // + +#else + case CCB_COMPLETE: + case CCB_SELECTION_TIMEOUT: + return SRB_STATUS_ERROR; +#endif + + case CCB_DATA_OVER_UNDER_RUN: + return SRB_STATUS_DATA_OVERRUN; + + case CCB_UNEXPECTED_BUS_FREE: + status = SRB_STATUS_UNEXPECTED_BUS_FREE; + logError = SP_UNEXPECTED_DISCONNECT; + break; + + case CCB_PHASE_SEQUENCE_FAIL: + case CCB_INVALID_DIRECTION: + status = SRB_STATUS_PHASE_SEQUENCE_FAILURE; + logError = SP_PROTOCOL_ERROR; + break; + + case CCB_BAD_MBO_COMMAND: + case CCB_INVALID_OP_CODE: + case CCB_BAD_LINKED_LUN: + case CCB_DUPLICATE_CCB: + case CCB_INVALID_CCB: + status = SRB_STATUS_INVALID_REQUEST; + logError = SP_INTERNAL_ADAPTER_ERROR; + break; + + default: + status = SRB_STATUS_ERROR; + logError = SP_INTERNAL_ADAPTER_ERROR; + break; + } + + ScsiPortLogError( + HwDeviceExtension, + Srb, + Srb->PathId, + Srb->TargetId, + Srb->Lun, + logError, + (2 << 8) | Ccb->HostStatus + ); + + return(status); + +} // end MapError() + + +BOOLEAN +ReadCommandRegister( + IN PHW_DEVICE_EXTENSION DeviceExtension, + OUT PUCHAR DataByte + ) + +/*++ + +Routine Description: + + Read command register. + +Arguments: + + DeviceExtesion - Pointer to adapder extension + DataByte - Byte read from register + +Return Value: + + TRUE if command register read. + FALSE if timed out waiting for adapter. + +--*/ + +{ + PBASE_REGISTER baseIoAddress = DeviceExtension->BaseIoAddress; + ULONG i; + + + // + // Wait up to 500 microseconds for adapter to be ready. + // + +#ifndef DTC329X + + for (i=0; i<500; i++) { + +#else + + for (i=0; i<5000; i++) { /* 03-27-93 */ + +#endif + + if (ScsiPortReadPortUchar(&baseIoAddress->StatusRegister) & + IOP_DATA_IN_PORT_FULL) { + + // + // Adapter ready. Break out of loop. + // + + break; + + } else { + + // + // Stall 1 microsecond before + // trying again. + // + + ScsiPortStallExecution(1); + } + } + +#ifndef DTC329X + if (i==500) { +#else + if (i==5000) { /* 03-27-93 */ +#endif + + ScsiPortLogError( + DeviceExtension, + NULL, + 0, + DeviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 3 << 8 + ); + + DebugPrint((1, "DTC329X:ReadCommandRegister: Read command timed out\n")); + return FALSE; + } + + *DataByte = ScsiPortReadPortUchar(&baseIoAddress->CommandRegister); + + return TRUE; + +} // end ReadCommandRegister() + + +BOOLEAN +WriteCommandRegister( + IN PHW_DEVICE_EXTENSION DeviceExtension, + IN UCHAR AdapterCommand + ) + +/*++ + +Routine Description: + + Write operation code to command register. + +Arguments: + + DeviceExtesion - Pointer to adapter extension + AdapterCommand - Value to be written to register + +Return Value: + + TRUE if command sent. + FALSE if timed out waiting for adapter. + +--*/ + +{ + PBASE_REGISTER baseIoAddress = DeviceExtension->BaseIoAddress; + ULONG i; + + // + // Wait up to 500 microseconds for adapter to be ready. + // + +#ifndef DTC329X + + for (i=0; i<500; i++) { + +#else + + for (i=0; i<5000; i++) { /* 03-27-93 */ +#endif + + if (ScsiPortReadPortUchar(&baseIoAddress->StatusRegister) & + IOP_COMMAND_DATA_OUT_FULL) { + + // + // Stall 1 microsecond before + // trying again. + // + + ScsiPortStallExecution(1); + + } else { + + // + // Adapter ready. Break out of loop. + // + + break; + } + } + +#ifndef DTC329X + if (i==500) { +#else + if (i==5000) { /* 03-27-93 */ +#endif + + ScsiPortLogError( + DeviceExtension, + NULL, + 0, + DeviceExtension->HostTargetId, + 0, + SP_INTERNAL_ADAPTER_ERROR, + 4 << 8 + ); + + DebugPrint((1, "DTC329X:WriteCommandRegister: Write command timed out\n")); + return FALSE; + } + + DebugPrint((3,"AdapterCommand = %x \n", AdapterCommand)); + + ScsiPortWritePortUchar(&baseIoAddress->CommandRegister, AdapterCommand); + + return TRUE; + +} // end WriteCommandRegister() |