summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/mylex/dce376/dce376nt.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/miniport/mylex/dce376/dce376nt.c2741
1 files changed, 2741 insertions, 0 deletions
diff --git a/private/ntos/miniport/mylex/dce376/dce376nt.c b/private/ntos/miniport/mylex/dce376/dce376nt.c
new file mode 100644
index 000000000..e3df65401
--- /dev/null
+++ b/private/ntos/miniport/mylex/dce376/dce376nt.c
@@ -0,0 +1,2741 @@
+/*
+** Mylex DCE376 miniport driver for Windows NT
+**
+** File: dce376nt.c
+** The driver
+**
+** (c) Copyright 1992 Deutsch-Amerikanische Freundschaft, Inc.
+** Written by Jochen Roth
+**
+** Contacts:
+** Paresh @MYLEX (510)796-6050 x222 (hardware, firmware)
+** Jochen @DAF (415)826-7934 (software)
+**
+**
+** Look for $$$ marking code that might need some attention
+**
+**
+** In ARCMODE, the NoncachedExtension sometimes is physically non-
+** continuous. Throwing out the error check on that solves the
+** problem in a very straight forward way.
+**
+**
+** Tape requests will not work if the data buffer is not
+** physically continuous. (We need MapBuffers=TRUE to update
+** the SenseInfo->Information field)
+**
+**
+** When multi-command firmware becomes available for the DCE, some
+** of the buffers in the NoncachedExtension need to be allocated
+** per request slot!
+**
+**
+** Ask Paresh for list of DCE error status codes to provide an error
+** mapping from DCE error codes to SCSI target status / request sense
+** keys.
+**
+**
+** Bus/adapter Reset for DCE ? nope!
+**
+** IOCTL only if MapBuffers is possible !
+**
+**
+*/
+
+
+#include "miniport.h"
+
+#include "dce376nt.h"
+
+
+
+#define MYPRINT 0
+#define NODEVICESCAN 0
+#define REPORTSPURIOUS 0 // Somewhat overwhelming in ARCMODE
+#define MAXLOGICALADAPTERS 3 // Set to 1: One DCE, disk only
+ // 2: One DCE, disk & scsi
+ // 3: Two DCEs, scsi only on 1st
+
+
+
+//
+// The DCE EISA id and mask
+//
+CONST UCHAR eisa_id[] = DCE_EISA_ID;
+CONST UCHAR eisa_mask[] = DCE_EISA_MASK;
+
+
+
+
+#if MYPRINT
+#define PRINT(f, a, b, c, d) dcehlpPrintf(deviceExtension, f, a, b, c, d)
+#define DELAY(x) ScsiPortStallExecution( (x) * 1000 )
+#else
+#define PRINT(f, a, b, c, d)
+#define DELAY(x)
+#endif
+
+
+
+//
+// Function declarations
+//
+// Functions that start with 'Dce376Nt' are entry points
+// for the OS port driver.
+// Functions that start with 'dcehlp' are helper functions.
+//
+
+ULONG
+DriverEntry(
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ );
+
+ULONG
+Dce376NtEntry(
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ );
+
+ULONG
+Dce376NtConfiguration(
+ IN PVOID DeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ );
+
+BOOLEAN
+Dce376NtInitialize(
+ IN PVOID DeviceExtension
+ );
+
+BOOLEAN
+Dce376NtStartIo(
+ IN PVOID DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+Dce376NtInterrupt(
+ IN PVOID DeviceExtension
+ );
+
+BOOLEAN
+Dce376NtResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+ );
+
+
+BOOLEAN
+dcehlpDiskRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+dcehlpScsiRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+dcehlpSendMBOX(
+ IN PUCHAR EisaAddress,
+ IN PDCE_MBOX mbox
+ );
+
+BOOLEAN
+dcehlpTransferMemory(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN ULONG HostAddress,
+ IN ULONG AdapterAddress,
+ IN USHORT Count,
+ IN UCHAR Direction
+ );
+
+VOID
+dcehlpCheckTarget(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN UCHAR TargetId
+ );
+
+BOOLEAN
+dcehlpContinueScsiRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+dcehlpContinueDiskRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN ULONG index,
+ IN BOOLEAN Start
+ );
+
+BOOLEAN
+dcehlpDiskRequestDone(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN ULONG index,
+ IN UCHAR Status
+ );
+
+BOOLEAN
+dcehlpSplitCopy(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN ULONG PhysicalBufferAddress,
+ IN PUCHAR VirtualUserAddress,
+ IN USHORT Count,
+ IN BOOLEAN ToUser
+ );
+
+
+USHORT dcehlpGetM16(PUCHAR p);
+ULONG dcehlpGetM24(PUCHAR p);
+ULONG dcehlpGetM32(PUCHAR p);
+void dcehlpPutM16(PUCHAR p, USHORT s);
+void dcehlpPutM24(PUCHAR p, ULONG l);
+void dcehlpPutM32(PUCHAR p, ULONG l);
+void dcehlpPutI16(PUCHAR p, USHORT s);
+void dcehlpPutI32(PUCHAR p, ULONG l);
+ULONG dcehlpSwapM32(ULONG l);
+
+
+
+#if MYPRINT
+ULONG dcehlpColumn = 0;
+UCHAR dcehlpHex[] = "0123456789ABCDEF";
+VOID dcehlpPutchar(PUSHORT BaseAddr, UCHAR c);
+VOID dcehlpPrintHex(PUSHORT BaseAddr, ULONG v, ULONG len);
+VOID dcehlpPrintf(PHW_DEVICE_EXTENSION deviceExtension,
+ PUCHAR fmt,
+ ULONG a1,
+ ULONG a2,
+ ULONG a3,
+ ULONG a4);
+#endif
+
+
+
+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 Dce376NtEntry(DriverObject, Argument2);
+
+} // end DriverEntry()
+
+
+
+
+
+ULONG
+Dce376NtEntry(
+ 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 DCE376 host adapters.
+
+Arguments:
+
+ Driver Object
+
+Return Value:
+
+ Status from ScsiPortInitialize()
+
+--*/
+
+{
+ HW_INITIALIZATION_DATA hwInitializationData;
+ ULONG i;
+ SCANCONTEXT context;
+
+
+
+ //
+ // Zero out structure.
+ //
+ for (i=0; i<sizeof(HW_INITIALIZATION_DATA); i++)
+ ((PUCHAR)&hwInitializationData)[i] = 0;
+
+ context.Slot = 0;
+ context.AdapterCount = 0;
+
+ //
+ // Set size of hwInitializationData.
+ //
+ hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
+
+ //
+ // Set entry points.
+ //
+ hwInitializationData.HwInitialize = Dce376NtInitialize;
+ hwInitializationData.HwFindAdapter = Dce376NtConfiguration;
+ hwInitializationData.HwStartIo = Dce376NtStartIo;
+ hwInitializationData.HwInterrupt = Dce376NtInterrupt;
+ hwInitializationData.HwResetBus = Dce376NtResetBus;
+
+ //
+ // Set number of access ranges and bus type.
+ //
+#if MYPRINT
+ hwInitializationData.NumberOfAccessRanges = 2;
+#else
+ hwInitializationData.NumberOfAccessRanges = 1;
+#endif
+ hwInitializationData.AdapterInterfaceType = Eisa;
+
+ //
+ // Indicate no buffer mapping.
+ // Indicate will need physical addresses.
+ //
+ hwInitializationData.MapBuffers = FALSE;
+ hwInitializationData.NeedPhysicalAddresses = TRUE;
+
+ //
+ // Indicate auto request sense is supported.
+ //
+ hwInitializationData.AutoRequestSense = TRUE;
+ hwInitializationData.MultipleRequestPerLu = FALSE;
+
+ //
+ // Specify size of extensions.
+ //
+ hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
+
+ //
+ // Ask for SRB extensions.
+ // $$$ Note: If we set SrbExtensionSize=0 NT crashes!
+ //
+ hwInitializationData.SrbExtensionSize = 4; // this works
+
+
+ return(ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &context));
+
+} // end Dce376NtEntry()
+
+
+
+
+ULONG
+Dce376NtConfiguration(
+ 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;
+ ULONG eisaSlotNumber;
+ PUCHAR eisaAddress;
+ PSCANCONTEXT context = Context;
+ ULONG i;
+ ULONG length;
+ UCHAR abyte;
+ BOOLEAN found=FALSE;
+ BOOLEAN scsiThing=FALSE;
+ ULONG IrqLevel;
+ ULONG RangeStart, RangeLength;
+
+
+ //
+ // Check to see if adapter present in system.
+ //
+ if(context->AdapterCount==1) {
+ //
+ // Found first dce last time, so this is the scsi extension...
+ //
+ eisaAddress = ScsiPortGetDeviceBase(deviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ ScsiPortConvertUlongToPhysicalAddress(0),
+ 0x200,
+ TRUE);
+
+ scsiThing = TRUE;
+ eisaSlotNumber = context->Slot;
+ IrqLevel = DCE_SCSI_IRQ;
+ RangeStart = 0x1f0;
+ RangeLength = 8;
+ }
+ else {
+ //
+ // Scan for DCE EISA id
+ //
+ for(eisaSlotNumber=context->Slot + 1; eisaSlotNumber<MAXIMUM_EISA_SLOTS; eisaSlotNumber++) {
+
+ // Update the slot count to indicate this slot has been checked.
+ context->Slot++;
+
+ //
+ // 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);
+
+ // Look at EISA id
+ for(found=TRUE, i=0; i<EISA_ID_COUNT; i++) {
+ abyte = ScsiPortReadPortUchar(eisaAddress+EISA_ID_START+i);
+ if( ((UCHAR)(abyte & eisa_mask[i])) != eisa_id[i] ) {
+ found = FALSE;
+ break;
+ }
+ }
+
+ if(found) {
+ break;
+ }
+
+ //
+ // If an adapter was not found unmap it.
+ //
+
+ ScsiPortFreeDeviceBase(deviceExtension, eisaAddress);
+ } // end for (eisaSlotNumber ...
+
+
+ if(!found) {
+ // No adapter was found. Indicate that we are done and there are no
+ // more adapters here.
+
+ *Again = FALSE;
+ return SP_RETURN_NOT_FOUND;
+ }
+
+ IrqLevel = context->AdapterCount ? DCE_SECONDARY_IRQ : DCE_PRIMARY_IRQ;
+ RangeStart = 0x1000 * eisaSlotNumber;
+ RangeLength = 0x1000;
+
+ } // end if(not next after first dce)
+
+
+
+#if MYPRINT
+ deviceExtension->printAddr =
+ ScsiPortGetDeviceBase(
+ deviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ ScsiPortConvertUlongToPhysicalAddress((ULONG)0xb0000),
+ 0x1000,
+ (BOOLEAN) FALSE); // InIoSpace
+
+ PRINT("\nHello, world! ", 0, 0, 0, 0);
+ PRINT("Version: " __DATE__ " " __TIME__ "\n", 0, 0, 0, 0);
+ PRINT(" slot=%b count=%b irq=%b io=%w\n",
+ eisaSlotNumber, context->AdapterCount, IrqLevel, RangeStart);
+
+ if(sizeof(DCE_MBOX)!=16) {
+ PRINT("\n MBOX SIZE FAILURE %b !!!!!!!\n", sizeof(DCE_MBOX), 0,0,0);
+ return(SP_RETURN_ERROR);
+ }
+
+#endif
+
+
+ deviceExtension->AdapterIndex = context->AdapterCount;
+ context->AdapterCount++;
+
+ if(context->AdapterCount < MAXLOGICALADAPTERS)
+ *Again = TRUE;
+ else
+ *Again = FALSE;
+
+
+ //
+ // There is still more to look at.
+ //
+
+
+ // Get the system interrupt vector and IRQL.
+ ConfigInfo->BusInterruptLevel = IrqLevel;
+
+ // Indicate maximum transfer length in bytes.
+ ConfigInfo->MaximumTransferLength = 0x20000;
+
+ // Maximum number of physical segments is 32.
+ ConfigInfo->NumberOfPhysicalBreaks = 17;
+
+ //
+ // Fill in the access array information.
+ //
+ (*ConfigInfo->AccessRanges)[0].RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(RangeStart);
+ (*ConfigInfo->AccessRanges)[0].RangeLength = RangeLength;
+ (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
+#if MYPRINT
+ (*ConfigInfo->AccessRanges)[1].RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(0xb0000);
+ (*ConfigInfo->AccessRanges)[1].RangeLength = 0x2000;
+ (*ConfigInfo->AccessRanges)[1].RangeInMemory = TRUE;
+#endif
+
+
+ // Store host adapter SCSI id
+ ConfigInfo->NumberOfBuses = 1;
+ ConfigInfo->InitiatorBusId[0] = 7;
+
+ // Bob Rinne: since we say Busmaster & NeedPhysicalAddresses
+ // this is not even being looked at !
+ ConfigInfo->ScatterGather = TRUE;
+
+ ConfigInfo->Master = TRUE;
+ ConfigInfo->CachesData = TRUE;
+ ConfigInfo->AtdiskPrimaryClaimed = scsiThing;
+ ConfigInfo->Dma32BitAddresses = TRUE; // $$$ Find out whether this costs
+
+
+ //
+ // Allocate a Noncached Extension to use for mail boxes.
+ //
+ deviceExtension->NoncachedExtension = ScsiPortGetUncachedExtension(
+ deviceExtension,
+ ConfigInfo,
+ sizeof(NONCACHED_EXTENSION));
+
+ if (deviceExtension->NoncachedExtension == NULL) {
+ // Sorry !
+ PRINT("Could not get uncached extension\n", 0, 0, 0, 0);
+ return(SP_RETURN_ERROR);
+ }
+
+
+
+ //
+ // Convert virtual to physical buffer addresses.
+ //
+ deviceExtension->NoncachedExtension->PhysicalBufferAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ NULL,
+ deviceExtension->NoncachedExtension->Buffer,
+ &length));
+ if(length < DCE_THUNK) {
+ PRINT("Noncached size too small %w/%w\n", length, DCE_THUNK, 0, 0);
+//$$$ return(SP_RETURN_ERROR);
+ }
+
+
+ if(scsiThing) {
+
+ //
+ // The SCSI routines need more:
+ //
+
+ deviceExtension->NoncachedExtension->PhysicalScsiReqAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ NULL,
+ deviceExtension->NoncachedExtension->ScsiReq,
+ &length));
+ if(length < DCE_SCSIREQLEN) {
+ PRINT("Noncached size dce scsireq too small %w/%w\n", length, DCE_SCSIREQLEN, 0, 0);
+//$$$ return(SP_RETURN_ERROR);
+ }
+
+ deviceExtension->NoncachedExtension->PhysicalReqSenseAddress =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ NULL,
+ deviceExtension->NoncachedExtension->ReqSense,
+ &length));
+ if(length < DCE_MAXRQS) {
+ PRINT("Noncached size rqs buffer too small %w/%w\n", length, DCE_MAXRQS, 0, 0);
+//$$$ return(SP_RETURN_ERROR);
+ }
+
+ } // end if(scsiThing)
+
+
+
+ // Store EISA slot base address
+ deviceExtension->EisaAddress = eisaAddress;
+
+ deviceExtension->HostTargetId = ConfigInfo->InitiatorBusId[0];
+
+ deviceExtension->ShutDown = FALSE;
+
+
+ //
+ // Setup our private control structures
+ //
+ for(i=0; i<8; i++)
+ deviceExtension->DiskDev[i] = 0;
+
+ deviceExtension->PendingSrb = NULL;
+
+ deviceExtension->ActiveCmds = 0;
+ for(i=0; i<DCE_MAX_IOCMDS; i++) {
+ deviceExtension->ActiveSrb[i] = NULL;
+ deviceExtension->ActiveRcb[i].WaitInt = FALSE;
+ }
+
+ deviceExtension->Kicked = FALSE;
+ deviceExtension->ActiveScsiSrb = NULL;
+
+ return SP_RETURN_FOUND;
+
+} // end Dce376NtConfiguration()
+
+
+
+
+BOOLEAN
+Dce376NtInitialize(
+ 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;
+ PNONCACHED_EXTENSION NoncachedExtension;
+ PUCHAR EisaAddress;
+ DCE_MBOX mbox;
+ PDCE_DPT dpt;
+ ULONG i, cnt, length, unit, target, cyls, hds, spt;
+ UCHAR dbell, status, errcode;
+
+
+
+ NoncachedExtension = deviceExtension->NoncachedExtension;
+ EisaAddress = deviceExtension->EisaAddress;
+
+ PRINT("Initializing adapter %b ...\n", deviceExtension->AdapterIndex, 0, 0, 0);
+
+
+ if(deviceExtension->AdapterIndex==1) {
+ // scsiThing
+
+#if NODEVICESCAN
+
+ // Preset for disk on scsi(0), all others non-cached
+ deviceExtension->ScsiDevType[0] = 0;
+ deviceExtension->DiskDev[0] = 1;
+ for(i=1; i<7; i++)
+ deviceExtension->DiskDev[i] = 0;
+
+#else
+
+ // Check all devices
+ for(i=0; i<7; i++) {
+ dcehlpCheckTarget(deviceExtension, (UCHAR)i);
+ if(deviceExtension->ScsiDevType[i]==0)
+ // Hard drive
+ deviceExtension->DiskDev[i]=1;
+ }
+ DELAY(1000);
+
+ // Once again after possible bus reset Unit Attention
+ for(i=0; i<7; i++) {
+ dcehlpCheckTarget(deviceExtension, (UCHAR)i);
+ if(deviceExtension->ScsiDevType[i]==0)
+ // Hard drive
+ deviceExtension->DiskDev[i]=1;
+ }
+ DELAY(1000);
+
+#endif
+
+ return(TRUE);
+ }
+
+
+
+ // Disable DCE interrupts
+ PRINT("disable DCE interrupts\n", 0, 0, 0, 0);
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB_ENABLE, 0);
+ ScsiPortWritePortUchar(EisaAddress+BMIC_SYSINTCTRL, 0);
+
+
+
+ //
+ // If second DCE, set EOI interrupt vector
+ // AdapterIndex 1 (SCSI) is handled above
+ //
+ if(deviceExtension->AdapterIndex) {
+
+ PRINT("Set IRQ10 ", 0, 0, 0, 0);
+ mbox.eimbox.Command = DCE_EOCIRQ;
+ mbox.eimbox.Reserved1 = 0;
+ mbox.eimbox.Status = 0;
+ mbox.eimbox.IRQSelect = 1;
+ mbox.eimbox.Unused1 = 0;
+ mbox.eimbox.Unused2 = 0;
+ mbox.eimbox.Unused3 = 0;
+
+ dcehlpSendMBOX(EisaAddress, &mbox);
+
+ // Poll the complete bit
+ for(cnt=0; cnt<0x3FFFFFFL; cnt++) {
+ dbell = ScsiPortReadPortUchar(EisaAddress+BMIC_EISA_DB);
+ if(dbell & 1)
+ break;
+ ScsiPortStallExecution(100);
+ }
+
+ ScsiPortStallExecution(500);
+
+ status = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+2);
+ errcode = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+3);
+
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB, dbell);
+
+ PRINT("done db=%b s=%b e=%b\n", dbell, status, errcode, 0);
+ }
+
+
+
+#if NODEVICESCAN
+
+ // Preset for Maxtor 120 MB as target 0
+ PRINT("setting diskdev[0]=%d\n", 0x106 * 0xF * 0x3F, 0, 0, 0);
+ deviceExtension->DiskDev[0] = 1;
+ deviceExtension->Capacity[0] = 0x106 * 0xF * 0x3F;
+
+#else
+
+ // Scan for devices
+ PRINT("scanning for devices... ",0,0,0,0);
+ dpt = NoncachedExtension->DevParms;
+ mbox.dpmbox.PhysAddr =
+ ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension, NULL, dpt, &length));
+
+ if(length < sizeof(DCE_DPT)*DPT_NUMENTS) {
+ PRINT("DPT table too small\n", 0, 0, 0, 0);
+ return(FALSE);
+ }
+
+ // Preset end mark in case DCE does not respond
+ dpt[0].DriveID = 0xffff;
+
+ // Setup mailbox
+ mbox.dpmbox.Command = DCE_DEVPARMS;
+ mbox.dpmbox.Reserved1 = 0;
+ mbox.dpmbox.Status = 0;
+ mbox.dpmbox.DriveType = 0;
+ mbox.dpmbox.Reserved2 = 0;
+ mbox.dpmbox.Reserved3 = 0;
+ mbox.dpmbox.Reserved4 = 0;
+
+ dcehlpSendMBOX(EisaAddress, &mbox);
+
+ // Poll the complete bit
+ for(cnt=0; cnt < 0x10000; cnt++) {
+ dbell = ScsiPortReadPortUchar(EisaAddress+BMIC_EISA_DB);
+ if(dbell & 1)
+ break;
+ ScsiPortStallExecution(100);
+ }
+
+ status = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+2);
+ errcode = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+3);
+
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB, dbell);
+
+ PRINT("done db=%b s=%b e=%b\n", dbell, status, errcode, 0);
+
+ for(unit=0; unit<8; unit++) {
+ if((target=dpt[unit].DriveID)==0xffff)
+ break;
+ cyls = (ULONG)dpt[unit].Cylinders;
+ hds = (ULONG)dpt[unit].Heads;
+ spt = (ULONG)dpt[unit].SectorsPerTrack;
+ PRINT("dev %b: %w cyls %b hds %b spt\n",
+ target, cyls, hds, spt);
+ deviceExtension->DiskDev[target] = 1;
+ deviceExtension->Capacity[target] = cyls*hds*spt;
+ }
+
+ DELAY(1000);
+
+#endif
+
+ // Enable DCE interrupts
+ PRINT("enable DCE interrupts\n", 0, 0, 0, 0);
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB_ENABLE, 1);
+ ScsiPortWritePortUchar(EisaAddress+BMIC_SYSINTCTRL, BMIC_SIC_ENABLE);
+
+
+ PRINT("Get going!\n", 0, 0, 0, 0);
+
+
+ return(TRUE);
+} // end Dce376NtInitialize()
+
+
+
+
+
+BOOLEAN
+Dce376NtStartIo(
+ IN PVOID HwDeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the SCSI port driver synchronized
+ with the kernel to start a request
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+ Srb - IO request packet
+
+Return Value:
+
+ TRUE
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PSCSI_REQUEST_BLOCK abortedSrb;
+ ULONG i = 0;
+ BOOLEAN status;
+
+
+
+ PRINT("IO %b T%b F=%w ", Srb->Function, Srb->TargetId, Srb->SrbFlags, 0);
+
+
+
+ switch(Srb->Function) {
+
+ case SRB_FUNCTION_SHUTDOWN:
+ deviceExtension->ShutDown = TRUE;
+
+ case SRB_FUNCTION_FLUSH:
+ PRINT("FLUSH/SHUTDOWN\n",0,0,0,0);
+ DELAY(1000);
+
+ case SRB_FUNCTION_EXECUTE_SCSI:
+
+ // Determine type of request needed
+ if(deviceExtension->DiskDev[Srb->TargetId])
+ status = dcehlpDiskRequest(deviceExtension, Srb);
+ else
+ status = dcehlpScsiRequest(deviceExtension, Srb);
+
+ if(status==FALSE) {
+ PRINT("StartIo: DCE is busy\n",0,0,0,0);
+
+ // Save the request until a pending one completes.
+ if(deviceExtension->PendingSrb != NULL) {
+ //
+ // This should never happen:
+ PRINT("StartIo: Queue already full\n",0,0,0,0);
+ // Already one queued, abort the newer one
+ //
+ Srb->SrbStatus = SRB_STATUS_BUSY;
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ }
+ else {
+ // Put this request on queue
+ deviceExtension->PendingSrb = Srb;
+ }
+ return(TRUE);
+ }
+
+ //
+ // Adapter ready for next request.
+ //
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+ return(TRUE);
+
+
+ case SRB_FUNCTION_ABORT_COMMAND:
+ PRINT("ABORT ",0,0,0,0);
+ abortedSrb = NULL;
+
+ //
+ // Verify that SRB to abort is still outstanding.
+ //
+ if(Srb->NextSrb == deviceExtension->PendingSrb ) {
+ // Was pending
+ abortedSrb = Srb->NextSrb;
+ deviceExtension->PendingSrb = NULL;
+ }
+ else {
+ // TAGTAG add tagging support here
+ if(Srb->NextSrb == deviceExtension->ActiveSrb[0] ) {
+ PRINT("StartIo: SRB to abort already running\n",0,0,0,0);
+ abortedSrb = deviceExtension->ActiveSrb[0];
+ deviceExtension->ActiveSrb[0] = NULL;
+ deviceExtension->ActiveCmds--;
+ //
+ // Reset DCE
+ //
+ //$$$ we need something here to wake up the
+ // DCE if it really hangs.
+ }
+ else {
+ PRINT("StartIo: SRB to abort not found\n",0,0,0,0);
+ // Complete abort SRB.
+ Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
+ }
+ }
+
+ if(abortedSrb==NULL) {
+ // Nope !
+ Srb->SrbStatus = SRB_STATUS_ABORT_FAILED;
+ }
+ else {
+ // Process the aborted request
+ abortedSrb->SrbStatus = SRB_STATUS_ABORTED;
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ abortedSrb);
+
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ }
+
+ // Abort request completed
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+
+ // Adapter ready for next request.
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return(TRUE);
+
+
+ case SRB_FUNCTION_IO_CONTROL:
+ case SRB_FUNCTION_RESET_BUS:
+ default:
+
+ //
+ // Set error, complete request
+ // and signal ready for next request.
+ //
+ PRINT("invalid request\n",0,0,0,0);
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return(TRUE);
+
+ } // end switch
+
+} // end Dce376NtStartIo()
+
+
+
+
+BOOLEAN
+Dce376NtInterrupt(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This is the interrupt service routine for the DCE376 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.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE if we handled the interrupt
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PUCHAR EisaAddress;
+ ULONG index;
+ UCHAR interruptStatus;
+ UCHAR status;
+ UCHAR errcode;
+
+
+
+ EisaAddress = deviceExtension->EisaAddress;
+#if REPORTSPURIOUS
+ PRINT("!",0,0,0,0);
+#endif
+
+ switch(deviceExtension->AdapterIndex) {
+
+ case 1: // First DCE SCSI part
+
+ // Check for pending request
+ if(deviceExtension->ActiveScsiSrb==NULL) {
+ // Nothing to do
+#if REPORTSPURIOUS
+ PRINT("}",0,0,0,0);
+#endif
+ deviceExtension->ScsiInterruptCount++; // If in init part
+ return(TRUE);
+ }
+
+
+ // Check if a command was started
+ if(deviceExtension->Kicked) {
+ // There's something waiting
+ errcode = ScsiPortReadPortUchar(EisaAddress+0x1f6);
+ if(errcode!=0xff) {
+ // No spurious interrupt
+ PRINT(">", 0, 0, 0, 0);
+ deviceExtension->Kicked=0;
+ if(dcehlpContinueScsiRequest(deviceExtension,
+ deviceExtension->ActiveScsiSrb)==FALSE) {
+ // Request no longer active
+ deviceExtension->ActiveScsiSrb = NULL;
+ }
+ }
+ }
+
+ // Check for pending requests. If there is one then start it now.
+ if(deviceExtension->ActiveScsiSrb==NULL)
+ if(deviceExtension->PendingSrb != NULL) {
+ PSCSI_REQUEST_BLOCK anotherSrb;
+
+ PRINT("pending-> \n",0,0,0,0);
+ anotherSrb = deviceExtension->PendingSrb;
+ deviceExtension->PendingSrb = NULL;
+ Dce376NtStartIo(deviceExtension, anotherSrb);
+ }
+
+ break;
+
+ default: // Disk parts
+
+ //
+ // Check interrupt pending.
+ //
+ interruptStatus = ScsiPortReadPortUchar(EisaAddress+BMIC_SYSINTCTRL);
+ if(!(interruptStatus & BMIC_SIC_PENDING)) {
+#if REPORTSPURIOUS
+ PRINT("Spurious interrupt\n", 0, 0, 0, 0);
+#endif
+ return FALSE;
+ }
+
+
+ //
+ // Read interrupt status from BMIC and acknowledge
+ //
+ // $$$ For setupapp, this needs some change:
+ // sometimes the SIC_PENDING is set, but
+ // EISA_DB is not. In that case we need to loop
+ // a couple times.
+ // $$$ We need not, because we get called again...
+ //
+ interruptStatus = ScsiPortReadPortUchar(EisaAddress+BMIC_EISA_DB);
+
+ status = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+2);
+ errcode = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+3);
+
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB, interruptStatus);
+
+ if(!(interruptStatus&1)) {
+ // From DCE, but unknown source
+#if REPORTSPURIOUS
+ PRINT("Dce376NtInterrupt: Unknown source\n", 0, 0, 0, 0);
+#endif
+ return(TRUE);
+ }
+
+
+ // Check...
+ if(deviceExtension->ActiveCmds<=0) {
+ // No one there interrupting us
+ PRINT("ActiveCmds==0!\n",0,0,0,0);
+ return(TRUE);
+ }
+
+
+ //
+ // TAGTAG Add tagging support here: find
+ // index of RCB for interrupting request
+ //
+ index = 0;
+
+
+ //
+ // Check whether this SRB is actually running
+ //
+ if(deviceExtension->ActiveSrb[index] == NULL) {
+ // No one there interrupting us, again
+ PRINT("ActiveSrb[%b]==0!\n",index,0,0,0);
+ return(TRUE);
+ }
+
+ if(deviceExtension->ActiveRcb[index].WaitInt == 0) {
+ // No one there interrupting us, again
+ PRINT("ActiveRcb[%b].WaitInt==0!\n",index,0,0,0);
+ return(TRUE);
+ }
+
+ // Update DCE status fields in RCB
+ deviceExtension->ActiveRcb[index].WaitInt = 0;
+ deviceExtension->ActiveRcb[index].DceStatus = status;
+ deviceExtension->ActiveRcb[index].DceErrcode = errcode;
+
+
+ // Continue or finish the interrupting SRB request
+ dcehlpContinueDiskRequest(deviceExtension, index, FALSE);
+
+
+ if(deviceExtension->ActiveCmds < DCE_MAX_IOCMDS) {
+ // A request slot is free now
+ // Check for pending requests.
+ // If there is one then start it now.
+
+ if(deviceExtension->PendingSrb != NULL) {
+ PSCSI_REQUEST_BLOCK anotherSrb;
+
+ PRINT("pending-> \n",0,0,0,0);
+ anotherSrb = deviceExtension->PendingSrb;
+ deviceExtension->PendingSrb = NULL;
+ Dce376NtStartIo(deviceExtension, anotherSrb);
+ }
+ }
+
+ // Definitively was our interrupt...
+ return TRUE;
+ }
+
+} // end Dce376NtInterrupt()
+
+
+
+
+BOOLEAN
+dcehlpDiskRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ Build disk request from SRB and send it to the DCE
+
+Arguments:
+
+ DeviceExtension
+ SRB
+
+Return Value:
+
+ TRUE if command was started
+ FALSE if host adapter is busy
+
+--*/
+{
+ ULONG index;
+ PRCB rcb;
+ ULONG blocks=0, blockAddr=0;
+ UCHAR Target;
+ UCHAR DceCommand;
+
+
+
+ Target = Srb->TargetId;
+
+ if(Srb->Lun!=0) {
+ // LUN not supported
+ Srb->SrbStatus = SRB_STATUS_INVALID_LUN;
+ ScsiPortNotification(RequestComplete, deviceExtension, Srb);
+ PRINT("diskio dce%b T%b: cmd=%b LUN=%b not supported\n",
+ deviceExtension->AdapterIndex, Target, Srb->Cdb[0], Srb->Lun);
+ return(TRUE);
+ }
+
+ if(deviceExtension->AdapterIndex==1) {
+ // Disk devices on SCSI part not supported
+ Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+ ScsiPortNotification(RequestComplete, deviceExtension, Srb);
+ PRINT("diskio dce%b T%b: cmd=%b not supported\n",
+ deviceExtension->AdapterIndex, Target, Srb->Cdb[0], 0);
+ return(TRUE);
+ }
+
+
+ if(Srb->Function == SRB_FUNCTION_EXECUTE_SCSI) {
+
+ switch(Srb->Cdb[0]) {
+
+ case SCSIOP_READ:
+ DceCommand = DCE_LREAD;
+ blocks = (ULONG)dcehlpGetM16(&Srb->Cdb[7]);
+ blockAddr = dcehlpGetM32(&Srb->Cdb[2]);
+ break;
+
+ case SCSIOP_WRITE:
+ case SCSIOP_WRITE_VERIFY:
+ DceCommand = DCE_LWRITE;
+ blocks = (ULONG)dcehlpGetM16(&Srb->Cdb[7]);
+ blockAddr = dcehlpGetM32(&Srb->Cdb[2]);
+ break;
+
+ case SCSIOP_READ6:
+ DceCommand = DCE_LREAD;
+ blocks = (ULONG)Srb->Cdb[4];
+ blockAddr = dcehlpGetM24(&Srb->Cdb[1]) & 0x1fffff;
+ break;
+
+ case SCSIOP_WRITE6:
+ DceCommand = DCE_LWRITE;
+ blocks = (ULONG)Srb->Cdb[4];
+ blockAddr = dcehlpGetM24(&Srb->Cdb[1]) & 0x1fffff;
+ break;
+
+ case SCSIOP_REQUEST_SENSE:
+ case SCSIOP_INQUIRY:
+ case SCSIOP_READ_CAPACITY:
+
+ PRINT("T%b: cmd=%b len=%b \n",
+ Target, Srb->Cdb[0], Srb->DataTransferLength, 0);
+
+ DceCommand = DCE_HOSTSCSI;
+ blocks = 0;
+ break;
+
+ case SCSIOP_TEST_UNIT_READY:
+ case SCSIOP_REZERO_UNIT:
+ case SCSIOP_SEEK6:
+ case SCSIOP_VERIFY6:
+ case SCSIOP_RESERVE_UNIT:
+ case SCSIOP_RELEASE_UNIT:
+ case SCSIOP_SEEK:
+ case SCSIOP_VERIFY:
+ PRINT("target %b: cmd=%b ignored\n",
+ Target, Srb->Cdb[0], 0, 0);
+
+ // Complete
+ Srb->ScsiStatus = SCSISTAT_GOOD;
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ ScsiPortNotification(RequestComplete, deviceExtension, Srb);
+ return(TRUE);
+
+ case SCSIOP_FORMAT_UNIT:
+ default:
+ // Unknown request
+ PRINT("target %b: cmd=%b unknown\n",
+ Target, Srb->Cdb[0], 0, 0);
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ return(TRUE);
+ }
+ }
+ else {
+ // can only be flush
+ PRINT("T%b: FLUSH \n", Target, 0, 0, 0);
+ DceCommand = DCE_FLUSH;
+ blocks = 0;
+ }
+
+
+ // PRINT("T%b: cmd=%b @%d, %w ", Target, Srb->Cdb[0], blockAddr, blocks);
+
+
+ // Check for request slot availability
+ if(deviceExtension->ActiveCmds >= DCE_MAX_IOCMDS) {
+ // dce is busy
+ PRINT("dce is busy\n",0,0,0,0);
+ return(FALSE);
+ }
+
+ //
+ // Put this SRB on queue
+ // TAGTAG Add tag support here
+ //
+ index = 0;
+
+ deviceExtension->ActiveCmds++;
+ deviceExtension->ActiveSrb[index] = Srb;
+
+ rcb = &deviceExtension->ActiveRcb[index];
+ rcb->DceCommand = DceCommand;
+ if(Srb->SrbFlags & SRB_FLAGS_ADAPTER_CACHE_ENABLE)
+ rcb->RcbFlags = 0;
+ else {
+ if(DceCommand==DCE_LREAD)
+ rcb->RcbFlags = RCB_PREFLUSH;
+ else
+ rcb->RcbFlags = RCB_POSTFLUSH;
+ }
+
+
+ rcb->VirtualTransferAddress = (PUCHAR)(Srb->DataBuffer);
+ rcb->BlockAddress = blockAddr;
+ if(blocks!=0)
+ rcb->BytesToGo = blocks*512;
+ else
+ rcb->BytesToGo = Srb->DataTransferLength;
+
+ // Start command
+ dcehlpContinueDiskRequest(deviceExtension, index, TRUE);
+
+ return(TRUE);
+}
+
+
+
+
+BOOLEAN
+dcehlpScsiRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+ Build SCSI request from SRB and send it to the DCE
+
+Arguments:
+
+ DeviceExtenson
+ SRB
+
+Return Value:
+
+ TRUE if command was started
+ FALSE if host adapter is busy and request need be queued
+
+--*/
+
+{
+ PSCCB sccb;
+ ULONG length;
+
+
+
+ sccb = &deviceExtension->Sccb;
+
+ if(deviceExtension->AdapterIndex!=1) {
+ // Non-disk devices on disk part not supported
+ Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
+ ScsiPortNotification(RequestComplete, deviceExtension, Srb);
+ PRINT("scsiio dce%b T%b: cmd=%b not supported\n",
+ deviceExtension->AdapterIndex,
+ Srb->TargetId, Srb->Cdb[0], 0);
+ return(TRUE);
+ }
+
+ if(Srb->Function != SRB_FUNCTION_EXECUTE_SCSI) {
+ //
+ // Not SCSI, must be flush
+ // Say ack
+ //
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ ScsiPortNotification(RequestComplete, deviceExtension, Srb);
+ return(TRUE);
+ }
+
+ // Check for request slot availability
+ if(deviceExtension->ActiveScsiSrb) {
+ // dce is busy
+ PRINT("scsi is busy\n",0,0,0,0);
+ return(FALSE);
+ }
+
+ // This SRB is being run now
+ deviceExtension->ActiveScsiSrb = Srb;
+
+
+ // Set flag for first request
+ sccb->Started = 0;
+
+
+ // Call the breakdown routine
+ if(dcehlpContinueScsiRequest(deviceExtension, Srb)==FALSE) {
+ // Trouble starting this request
+ deviceExtension->ActiveScsiSrb = NULL;
+ }
+
+ // Don't put request on queue
+ return(TRUE);
+}
+
+
+
+
+VOID
+dcehlpSendMBOX(
+ IN PUCHAR EisaAddress,
+ IN PDCE_MBOX mbox
+ )
+
+/*++
+
+Routine Description:
+
+ Start up conventional DCE command
+
+Arguments:
+
+ Eisa base IO address
+ DCE mailbox
+
+Return Value:
+
+ none
+
+--*/
+
+{
+ PUCHAR ptr;
+ ULONG i;
+
+
+ ptr = (PUCHAR)mbox;
+ for(i=0; i<16; i++)
+ ScsiPortWritePortUchar(EisaAddress+BMIC_MBOX+i, ptr[i]);
+
+ // Kick butt
+ ScsiPortWritePortUchar(EisaAddress+BMIC_LOCAL_DB, 1);
+}
+
+
+
+
+BOOLEAN
+Dce376NtResetBus(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+)
+
+/*++
+
+Routine Description:
+
+ Reset Dce376Nt SCSI adapter and SCSI bus.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ Nothing.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+
+
+ PRINT("Reset Bus\n",0,0,0,0);
+ //
+ // Complete all outstanding requests.
+ //
+ ScsiPortCompleteRequest(deviceExtension,
+ 0,
+ (UCHAR)-1,
+ (UCHAR)-1,
+ SRB_STATUS_BUS_RESET);
+
+ return TRUE;
+
+} // end Dce376NtResetBus()
+
+
+
+//
+// Transfer memory to/from DCE
+// Return FALSE if an error occured
+// TRUE otherwise
+//
+BOOLEAN
+dcehlpTransferMemory(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN ULONG HostAddress,
+ IN ULONG AdapterAddress,
+ IN USHORT Count,
+ IN UCHAR Direction
+ )
+{
+ PUCHAR EisaAddress;
+ DCE_MBOX mbox;
+ ULONG cnt;
+ UCHAR dbell, status, errcode;
+
+
+
+ EisaAddress = deviceExtension->EisaAddress;
+
+
+ // Disable DCE interrupts
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB_ENABLE, 0);
+ ScsiPortWritePortUchar(EisaAddress+BMIC_SYSINTCTRL, 0);
+
+
+ // Setup mailbox
+ mbox.mtmbox.Command = DCE_MEMXFER;
+ mbox.mtmbox.Reserved1 = 0;
+ mbox.mtmbox.Status = 0;
+ mbox.mtmbox.Error = 0;
+ mbox.mtmbox.AdapterAddress = AdapterAddress;
+ mbox.mtmbox.HostAddress = HostAddress;
+ mbox.mtmbox.Direction = Direction;
+ mbox.mtmbox.Unused = 0;
+ mbox.mtmbox.TransferCount = Count;
+
+
+ dcehlpSendMBOX(EisaAddress, &mbox);
+
+ //
+ // Poll the complete bit
+ // Magic here: if called from ContinueScsiRequest,
+ // the dbell sticks to 0xff !!!???
+ //
+ for(cnt=0; cnt<0x1000; cnt++) {
+ ScsiPortStallExecution(100);
+ dbell = ScsiPortReadPortUchar(EisaAddress+BMIC_EISA_DB);
+ if(dbell==0xff && cnt<1000)
+ continue;
+ if(dbell & 1)
+ break;
+ }
+
+ ScsiPortStallExecution(100); // To be sure ! ???
+
+ status = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+2);
+ errcode = ScsiPortReadPortUchar(EisaAddress+BMIC_MBOX+3);
+
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB, dbell);
+
+ ScsiPortStallExecution(100);
+
+ // Enable DCE interrupts
+ ScsiPortWritePortUchar(EisaAddress+BMIC_EISA_DB_ENABLE, 1);
+ ScsiPortWritePortUchar(EisaAddress+BMIC_SYSINTCTRL, BMIC_SIC_ENABLE);
+
+ if( (cnt>0x4000) || (errcode&1) ) {
+ PRINT("MT cnt=%w db=%b s=%b e=%b\n", cnt, dbell, status, errcode);
+ DELAY(1000);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+
+VOID
+dcehlpCheckTarget(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN UCHAR TargetId
+ )
+{
+ PNONCACHED_EXTENSION NoncachedExtension;
+ PUCHAR EisaAddress;
+ PUCHAR scsiReq;
+ ULONG i, cnt, tstat_reg, to_reg, err_reg;
+ PUCHAR pppScsiReq;
+
+
+
+ NoncachedExtension = deviceExtension->NoncachedExtension;
+ EisaAddress = deviceExtension->EisaAddress;
+ scsiReq = NoncachedExtension->ScsiReq;
+
+ PRINT("T%b : ", TargetId, 0, 0, 0);
+
+ // Clear scsi request block
+ for(i=0; i<DCE_SCSIREQLEN; i++)
+ scsiReq[i] = 0;
+
+
+ // Setup scsi request block
+#if 0
+ scsiReq->TargetID = TargetId;
+ scsiReq->cdbSize = 6;
+ scsiReq->cdb[0] = 0x12; // Inquiry command
+ scsiReq->cdb[4] = 36; // Response length
+ scsiReq->Opcode = DCE_SCSI_READ;
+ scsiReq->ppXferAddr = NoncachedExtension->PhysicalBufferAddress;
+ scsiReq->XferCount = 36;
+ scsiReq->ppSenseBuf = NoncachedExtension->PhysicalReqSenseAddress;
+ scsiReq->SenseLen = 14;
+#endif
+ scsiReq[0] = TargetId;
+ scsiReq[1] = 6;
+ scsiReq[2+0] = 0x12; // Inquiry command
+ scsiReq[2+4] = 36; // Response length
+ scsiReq[18] = DCE_SCSI_READ;
+ dcehlpPutI32(scsiReq+14, NoncachedExtension->PhysicalBufferAddress);
+ dcehlpPutI16(scsiReq+19, 36);
+ dcehlpPutI32(scsiReq+23, NoncachedExtension->PhysicalReqSenseAddress);
+ scsiReq[22] = 14;
+
+
+ // Program four bytes of physical address into dce
+ pppScsiReq = (PUCHAR)(&NoncachedExtension->PhysicalScsiReqAddress);
+ for(i=0; i<4; i++)
+ ScsiPortWritePortUchar(EisaAddress+0x1f2+i, pppScsiReq[i]);
+ deviceExtension->ScsiInterruptCount = 0;
+
+ //
+ // Set marker
+ // setupapp calls the interrupt handler continuosly,
+ // so we need this to determine if the
+ // DCE is actually through with the request
+ //
+ ScsiPortWritePortUchar(EisaAddress+0x1f6, 0xff);
+ NoncachedExtension->Buffer[0] = 0xff;
+
+ // Kick the dce
+ ScsiPortWritePortUchar(EisaAddress+0x1f7, 0x98);
+
+#if 0
+ // Output register values before execution finishes
+ tstat_reg = ScsiPortReadPortUchar(EisaAddress+0x1f5);
+ to_reg = ScsiPortReadPortUchar(EisaAddress+0x1f6);
+ err_reg = ScsiPortReadPortUchar(EisaAddress+0x1f2);
+ PRINT("ts=%b to=%b err=%b ", tstat_reg, to_reg, err_reg, 0);
+#endif
+
+ // Wait for command to finish
+ for(cnt=0; cnt<10000; cnt++) {
+ // Check if interrupt occured
+ if(deviceExtension->ScsiInterruptCount)
+ break;
+ // Check if interrupt got lost
+ if(ScsiPortReadPortUchar(EisaAddress+0x1f6) != (UCHAR)0xff)
+ break;
+ ScsiPortStallExecution(100);
+ }
+
+ // Wait another 100 ms to be sure
+ ScsiPortStallExecution(100 * 1000);
+
+ // Read execution status registers and ack the interrupt
+ tstat_reg = ScsiPortReadPortUchar(EisaAddress+0x1f5);
+ to_reg = ScsiPortReadPortUchar(EisaAddress+0x1f6);
+ err_reg = ScsiPortReadPortUchar(EisaAddress+0x1f2);
+ ScsiPortWritePortUchar(EisaAddress+0x1f2, 0x99);
+ PRINT("ts=%b to=%b err=%b\n", tstat_reg, to_reg, err_reg, 0);
+
+ deviceExtension->ScsiDevType[TargetId] = (UCHAR)0xff;
+ if(to_reg!=0x2d) {
+ if(tstat_reg!=2 && err_reg==0) {
+ PINQUIRYDATA inq = (PINQUIRYDATA)(NoncachedExtension->Buffer);
+
+ deviceExtension->ScsiDevType[TargetId] = inq->DeviceType;
+
+ #if MYPRINT
+ PRINT("target %b : type=%b/%b len=%b '",
+ TargetId, inq->DeviceType, inq->DeviceTypeModifier,
+ inq->AdditionalLength);
+ inq->VendorSpecific[0]=0;
+ PRINT(inq->VendorId, 0, 0, 0, 0);
+ PRINT("'\n", 0, 0, 0, 0);
+ #endif
+ }
+ }
+}
+
+
+
+/*
+** Continue scsi request
+** Return TRUE if request is active
+** FALSE if request has completed (or was never started)
+*/
+BOOLEAN
+dcehlpContinueScsiRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+{
+ PSCCB sccb;
+ ULONG bytes;
+ BOOLEAN nobreaks = FALSE;
+ PNONCACHED_EXTENSION NoncachedExtension;
+ PUCHAR EisaAddress;
+ PUCHAR scsiReq;
+ ULONG physDataPtr;
+ ULONG physRqsPtr;
+ ULONG maxBytesThisReq;
+ ULONG maxBlocksPerReq;
+ ULONG i, cnt, length;
+ UCHAR tstat_reg, to_reg, err_reg;
+ PUCHAR pppScsiReq;
+
+
+
+ NoncachedExtension = deviceExtension->NoncachedExtension;
+ EisaAddress = deviceExtension->EisaAddress;
+ scsiReq = NoncachedExtension->ScsiReq;
+ sccb = &deviceExtension->Sccb;
+
+
+ // Check if this is the first call
+ if(sccb->Started==0) {
+ //
+ // New kid on the control block. Get things started.
+ //
+ sccb->Started = 1;
+
+ PRINT("C%b L=%w ", Srb->Cdb[0], Srb->DataTransferLength, 0, 0);
+
+ // Check data transfer length
+ bytes = Srb->DataTransferLength;
+ if(!(Srb->SrbFlags & (SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT)))
+ bytes = 0;
+
+ if(bytes==0)
+ sccb->Opcode = DCE_SCSI_NONE;
+ else if(Srb->SrbFlags & SRB_FLAGS_DATA_IN)
+ sccb->Opcode = DCE_SCSI_READ;
+ else
+ sccb->Opcode = DCE_SCSI_WRITE;
+
+ // Store virtual data transfer address
+ sccb->VirtualTransferAddress = (PUCHAR)Srb->DataBuffer;
+
+ // Store SCSI device type
+ sccb->DevType = deviceExtension->ScsiDevType[Srb->TargetId];
+
+ //
+ // Determine data transfer parameters
+ //
+ switch(Srb->Cdb[0]) {
+ case SCSIOP_READ6:
+ case SCSIOP_WRITE6:
+ // Short CDB, determine device type
+ if(sccb->DevType == 1) {
+ // Sequential device (SCSI tape)
+ sccb->DeviceAddress = 0;
+ sccb->BlocksToGo = dcehlpGetM24(&Srb->Cdb[2]);
+ sccb->BytesPerBlock = bytes / sccb->BlocksToGo;
+ }
+ else {
+ // Non-sequential device (disk, cd-rom, etc)
+ // Note: we take the LUN bits into the device
+ // address; that makes the PutM() easier, too.
+ sccb->DeviceAddress = dcehlpGetM24(&Srb->Cdb[1]);
+ sccb->BlocksToGo = (ULONG)Srb->Cdb[4];
+ if(sccb->BlocksToGo==0)
+ sccb->BlocksToGo = 256;
+ sccb->BytesPerBlock = bytes / sccb->BlocksToGo;
+ }
+ break;
+
+ case SCSIOP_READ:
+ case SCSIOP_WRITE:
+ case SCSIOP_WRITE_VERIFY:
+ // Long CDB
+ sccb->DeviceAddress = dcehlpGetM32(&Srb->Cdb[2]);
+ sccb->BlocksToGo = (ULONG)dcehlpGetM16(&Srb->Cdb[7]);
+ if(sccb->BlocksToGo==0)
+ sccb->BlocksToGo==65536;
+ sccb->BytesPerBlock = bytes / sccb->BlocksToGo;
+ break;
+
+ default:
+ sccb->BytesPerBlock = 0;
+ nobreaks = TRUE;
+ break;
+ }
+
+ if(sccb->BytesPerBlock==0)
+ // Can't break this down
+ nobreaks = TRUE;
+
+ } // end if(sccb->Started==0)
+ else {
+ //
+ // We started before, so this is interrupt time
+ //
+
+ //
+ // Read execution status registers and ack the interrupt
+ //
+ tstat_reg = ScsiPortReadPortUchar(EisaAddress+0x1f5);
+ to_reg = ScsiPortReadPortUchar(EisaAddress+0x1f6);
+ err_reg = ScsiPortReadPortUchar(EisaAddress+0x1f2);
+ ScsiPortWritePortUchar(EisaAddress+0x1f2, 0x99);
+#if MYPRINT
+ if(tstat_reg || to_reg || err_reg) {
+ PRINT("ts=%b to=%b e=%b ", tstat_reg, to_reg, err_reg, 0);
+ }
+#endif
+
+ //
+ // Adjust pointers
+ //
+ sccb->DeviceAddress += sccb->BlocksThisReq;
+ sccb->BlocksToGo -= sccb->BlocksThisReq;
+ sccb->VirtualTransferAddress += sccb->BytesThisReq;
+
+ //
+ // Check for selection timeout
+ //
+ if(to_reg==0x2d) {
+ // Timeout on selection
+ PRINT("TOUT\n", 0, 0, 0, 0);
+ Srb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT;
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ return(FALSE);
+ }
+
+ //
+ // Check for other errors
+ //
+ if(err_reg) {
+ // Some error
+ Srb->ScsiStatus = tstat_reg;
+ if(tstat_reg==8)
+ Srb->SrbStatus = SRB_STATUS_BUSY;
+ else {
+ if(Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) {
+ PRINT("AutoSense DIS ",0,0,0,0);
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ }
+ else {
+ PRINT("AutoSense ",0,0,0,0);
+ Srb->SrbStatus = SRB_STATUS_ERROR | SRB_STATUS_AUTOSENSE_VALID;
+ // $$$ If tape request, change the Information[] field
+ // in SenseInfoBuffer. It represents the number of tape
+ // blocks/bytes not read or written.
+ // We cannot use dcehlpTransferMemory(), because we would
+ // have to syncronize this with disk requests running on
+ // a different logical adapter (As of now, the DCE runs
+ // only one request at a time). What a mess!
+ // Using MapBuffers would come in handy here...
+ }
+ }
+ PRINT("ERR\n", 0, 0, 0, 0);
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ return(FALSE);
+ }
+
+ //
+ // See if we're done
+ //
+ if(sccb->BlocksToGo==0) {
+ // We're done
+ PRINT("OK\n", 0, 0, 0, 0);
+ Srb->ScsiStatus = 0;
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ return(FALSE);
+ }
+
+ // Otherwise start next part of request
+ PRINT("Cont:\n", 0, 0, 0, 0);
+ }
+
+
+ //
+ // If we get here, there's something left to do
+ //
+
+
+ if(sccb->Opcode != DCE_SCSI_NONE) {
+ //
+ // Data to transfer
+ // Get physical data buffer address
+ //
+ physDataPtr = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ Srb,
+ sccb->VirtualTransferAddress,
+ &length));
+ }
+ else
+ physDataPtr = 0;
+
+ // Setup common part of scsi request block
+ scsiReq[0] = Srb->TargetId;
+ scsiReq[1] = Srb->CdbLength;
+ for(i=0; i<Srb->CdbLength; i++)
+ scsiReq[2+i] = Srb->Cdb[i];
+ dcehlpPutI32(scsiReq+14, physDataPtr);
+ scsiReq[18] = sccb->Opcode;
+ scsiReq[21] = 0;
+
+
+ if(nobreaks) {
+ //
+ // Request may not be broken up
+ // We got here on first pass, so 'bytes' is valid
+ //
+ if(length < bytes) {
+ // The data area is not physically continuous
+ // $$$ might use better error code here
+ PRINT("NOBREAKS SCSI S/G\n",0,0,0,0);
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ return(FALSE);
+ }
+ PRINT("ONCE ", 0, 0, 0, 0);
+ sccb->BlocksToGo = sccb->BlocksThisReq = 1;
+ sccb->BytesThisReq = sccb->BytesPerBlock = bytes;
+
+ // Leave CDB as is
+ }
+ else {
+ //
+ // Request can be broken down
+ // Determine number of blocks for this request
+ //
+ maxBytesThisReq = length < DCE_MAX_XFERLEN ? length : DCE_MAX_XFERLEN;
+ maxBlocksPerReq = maxBytesThisReq / sccb->BytesPerBlock;
+ if(maxBlocksPerReq == 0) {
+ // Out of luck!
+ PRINT("SCSI S/G ACROSS BLOCK (%w)\n", maxBytesThisReq, 0, 0, 0);
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+ return(FALSE);
+ }
+
+ if(sccb->BlocksToGo > maxBlocksPerReq)
+ sccb->BlocksThisReq = maxBlocksPerReq;
+ else
+ sccb->BlocksThisReq = sccb->BlocksToGo;
+ sccb->BytesThisReq = sccb->BlocksThisReq * sccb->BytesPerBlock;
+
+ PRINT("mbr=%b btg=%b btr=%b ", maxBlocksPerReq, sccb->BlocksToGo, sccb->BlocksThisReq, 0);
+
+ // We have to modify the CDB
+ switch(scsiReq[2+0]) {
+ case SCSIOP_READ6:
+ case SCSIOP_WRITE6:
+ // Short CDB
+ if(sccb->DevType == 1) {
+ // Sequential device (SCSI tape)
+ dcehlpPutM24(&scsiReq[2+2], sccb->BlocksThisReq);
+ }
+ else {
+ // Non-sequential device (disk, cd-rom, etc)
+ // Note: we had the LUN bits in the device address!
+ dcehlpPutM24(&scsiReq[2+1], sccb->DeviceAddress);
+ scsiReq[2+4] = (UCHAR)(sccb->BlocksThisReq);
+ }
+ break;
+
+ case SCSIOP_READ:
+ case SCSIOP_WRITE:
+ case SCSIOP_WRITE_VERIFY:
+ // Long CDB
+ dcehlpPutM32(&scsiReq[2+2], sccb->DeviceAddress);
+ dcehlpPutM16(&scsiReq[2+7], (USHORT)sccb->BlocksThisReq);
+ break;
+
+ default:
+ PRINT("WEIRD!!! \n", 0, 0, 0, 0);
+ break;
+ }
+ }
+
+ // Update transfer length field
+ dcehlpPutI16(scsiReq+19, (USHORT)sccb->BytesThisReq);
+
+
+ //
+ // Set auto request sense fields
+ //
+ if(Srb->SrbFlags & SRB_FLAGS_DISABLE_AUTOSENSE) {
+ // Stuff the request sense info elsewhere
+ physRqsPtr = NoncachedExtension->PhysicalReqSenseAddress;
+ scsiReq[22] = 14;
+ }
+ else {
+ // Get physical address of SenseInfoBuffer
+ physRqsPtr = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ NULL,
+ Srb->SenseInfoBuffer,
+ &length));
+ // $$$ should verify length >= SenseInfoBufferLength here
+ scsiReq[22] = Srb->SenseInfoBufferLength;
+ }
+ dcehlpPutI32(scsiReq+23, physRqsPtr);
+
+
+ //
+ // Program four bytes of physical address into DCE
+ //
+ PRINT("* ",0,0,0,0);
+ pppScsiReq = (PUCHAR)(&NoncachedExtension->PhysicalScsiReqAddress);
+ for(i=0; i<4; i++)
+ ScsiPortWritePortUchar(EisaAddress+0x1f2+i, pppScsiReq[i]);
+ deviceExtension->ScsiInterruptCount = 0;
+ deviceExtension->Kicked = 1;
+
+
+ // Set marker (see explanation at CheckTarget)
+ ScsiPortWritePortUchar(EisaAddress+0x1f6, 0xff);
+
+
+ // Kick the dce
+ ScsiPortWritePortUchar(EisaAddress+0x1f7, 0x98);
+
+
+ // Wait for interrupt
+ return(TRUE);
+}
+
+
+
+
+/*
+** Continue disk request
+** Return TRUE if a request slot became available
+** FALSE if not
+*/
+BOOLEAN
+dcehlpContinueDiskRequest(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN ULONG index,
+ IN BOOLEAN Start
+ )
+{
+ PRCB rcb;
+ PSCSI_REQUEST_BLOCK srb;
+ PNONCACHED_EXTENSION nce;
+ DCE_MBOX mbox;
+ ULONG physAddr;
+ ULONG length, blocks, bytes;
+ PUCHAR EisaAddress;
+ ULONG i;
+
+
+
+ EisaAddress = deviceExtension->EisaAddress;
+ rcb = &deviceExtension->ActiveRcb[index];
+ srb = deviceExtension->ActiveSrb[index];
+ nce = deviceExtension->NoncachedExtension;
+
+
+
+ if(Start==FALSE) {
+ //
+ // DCE interrupt time call
+ // Determine status of last DCE request
+ //
+
+ if(rcb->DceErrcode & 1) {
+ // The DCE detected an error
+ PRINT("error=%b status=%b\n",rcb->DceErrcode,rcb->DceStatus,0,0);
+
+ // $$$ Add error code mapping here
+ dcehlpDiskRequestDone(deviceExtension, index,
+ SRB_STATUS_TIMEOUT);
+
+ // Slot free
+ return(TRUE);
+ }
+
+ // Status was okay, check post-read copy flag
+ if(rcb->RcbFlags & RCB_NEEDCOPY) {
+ // Last block was a scattered single block read
+ if(!dcehlpSplitCopy(deviceExtension, srb,
+ nce->PhysicalBufferAddress,
+ rcb->VirtualTransferAddress, 512, TRUE)) {
+ // Error breaking up the s/g mess
+ PRINT("SG ERROR !\n",0,0,0,0);
+ dcehlpDiskRequestDone(deviceExtension, index,
+ SRB_STATUS_PARITY_ERROR);
+ return(TRUE);
+ }
+
+ // Reset flag
+ rcb->RcbFlags &= (~RCB_NEEDCOPY);
+ }
+
+ // Advance pointers
+ rcb->BytesToGo -= rcb->BytesThisReq;
+ rcb->VirtualTransferAddress += rcb->BytesThisReq;
+
+ // Check if more to do
+ if(rcb->BytesToGo==0) {
+ //
+ // This SRB's data transfer is done
+ //
+ if(rcb->RcbFlags & RCB_POSTFLUSH) {
+ //
+ // Need to flush buffers before we're through
+ //
+ rcb->RcbFlags &= (~RCB_POSTFLUSH);
+ //PRINT("POSTFLUSH\n",0,0,0,0);
+ rcb->DceCommand = DCE_FLUSH;
+ }
+ else {
+ //
+ // We're actually done here !
+ //
+ PRINT("OK \r",0,0,0,0);
+
+ // Update SCSI status.
+ // $$$ can we manipulate this for non SCSI requests ?
+ srb->ScsiStatus = SCSISTAT_GOOD;
+
+ // Finish
+ dcehlpDiskRequestDone(deviceExtension, index,
+ SRB_STATUS_SUCCESS);
+ return TRUE;
+ }
+ }
+
+ //
+ // No error but SRB not completely done.
+ //
+ PRINT("MORE:\r",0,0,0,0);
+ }
+ else {
+ //
+ // We start an SRB here, initialize
+ // RCB control block variables
+ //
+ rcb->RcbFlags &= (~RCB_NEEDCOPY); // be safe
+
+ // $$$ Double check if flags indicate any data transfer at all !
+ }
+
+
+ if(rcb->BytesToGo) {
+ //
+ // We want to transfer some data, get the physical address
+ //
+ physAddr = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ srb,
+ rcb->VirtualTransferAddress,
+ &length));
+
+ // Get maximum length for this request
+ if(length < rcb->BytesToGo)
+ bytes = length;
+ else
+ bytes = rcb->BytesToGo;
+
+ if(rcb->DceCommand==DCE_LREAD || rcb->DceCommand==DCE_LWRITE) {
+ //
+ // Disk read/write: get number of blocks
+ //
+ if( (blocks = bytes/512) == 0 ) {
+ //
+ // Here we have a scatter gather break within the next block !
+ // Set I/O to one block to/from our buffer
+ //
+ blocks = 1;
+ physAddr = nce->PhysicalBufferAddress;
+
+ if(rcb->DceCommand==DCE_LWRITE) {
+ // Write command, fill buffer first
+ if(!dcehlpSplitCopy(deviceExtension, srb, physAddr,
+ rcb->VirtualTransferAddress, 512, FALSE)) {
+ // Error breaking up the s/g mess
+ PRINT("SG ERROR !\n",0,0,0,0);
+ dcehlpDiskRequestDone(deviceExtension, index,
+ SRB_STATUS_PARITY_ERROR);
+ return(TRUE);
+ }
+ }
+ else {
+ // Read command, need copy later
+ rcb->RcbFlags |= RCB_NEEDCOPY;
+ }
+ }
+
+ //
+ // Important: in case of scatter/gather over block
+ // boundaries, round bytes down to full multiple of 512
+ // This will leave us with less than 512 bytes next time
+ // in case of a s/g across block boundaries
+ //
+ bytes = blocks*512;
+ }
+ else {
+ //
+ // Not a disk read/write
+ //
+ if(bytes != rcb->BytesToGo) {
+ //
+ // Scatter Gather within a non read/write command
+ // This would need a SplitCopy() :-|
+ // Stuff like this makes programmers happy and
+ // should cost h/w developers their job.
+ //
+ PRINT("S/G within non-rw, len=%w/%w\n",
+ length, rcb->BytesToGo, 0, 0);
+ dcehlpDiskRequestDone(deviceExtension, index,
+ SRB_STATUS_PARITY_ERROR);
+ return(TRUE);
+ }
+ }
+ }
+ else {
+ //
+ // We don't have data to transfer
+ //
+ bytes = 0;
+ blocks = 0;
+ }
+
+
+ //
+ // Now look at the specific DCE command
+ //
+ switch(rcb->DceCommand) {
+
+ case DCE_LREAD:
+ case DCE_LWRITE:
+ // Disk read/write
+ if(blocks==0) {
+ PRINT("LIO: blocks==0! ",0,0,0,0);
+ // Cancel this command with some garbage error code
+ dcehlpDiskRequestDone(deviceExtension, index,
+ SRB_STATUS_PARITY_ERROR);
+ return(TRUE);
+ }
+ //
+ // Check if we need to flush first (non-cached read)
+ //
+ if(rcb->RcbFlags & RCB_PREFLUSH) {
+ // Reset flush and copy flags, if set
+ rcb->RcbFlags &= (~(RCB_NEEDCOPY|RCB_PREFLUSH));
+ //PRINT("PREFLUSH\n",0,0,0,0);
+
+ // Flush buffers, invalidate cache
+ mbox.ivmbox.Command = DCE_INVALIDATE;
+ mbox.ivmbox.Reserved1 = 0;
+ mbox.ivmbox.Status = srb->TargetId;
+ mbox.ivmbox.Error = 0;
+ mbox.ivmbox.Unused1 = 0;
+ mbox.ivmbox.Unused2 = 0;
+ mbox.ivmbox.Unused3 = 0;
+ // Don't advance pointers this pass !
+ bytes = 0;
+ blocks = 0;
+ break;
+ }
+ else {
+ // Transfer data
+ mbox.iombox.Command = rcb->DceCommand;
+ mbox.iombox.Reserved1 = 0;
+ mbox.iombox.Status = srb->TargetId;
+ mbox.iombox.Error = 0;
+ mbox.iombox.SectorCount = (USHORT)blocks;
+ mbox.iombox.Reserved2 = 0;
+ mbox.iombox.PhysAddr = physAddr;
+ mbox.iombox.Block = rcb->BlockAddress;
+ // PRINT(" %d-%d,%w ", physAddr, rcb->BlockAddress, blocks, 0);
+ }
+ break;
+
+ default:
+ PRINT("DR: unknown DceCommand=%b\n", rcb->DceCommand, 0, 0, 0);
+
+ // Cancel this command with some garbage error code
+ dcehlpDiskRequestDone(deviceExtension, index,
+ SRB_STATUS_PARITY_ERROR);
+ return(TRUE);
+
+ case DCE_RECAL:
+ // Recalibrate
+ mbox.rdmbox.Command = DCE_RECAL;
+ mbox.rdmbox.Reserved1 = 0;
+ mbox.rdmbox.Status = (UCHAR)srb->TargetId;
+ mbox.rdmbox.Error = 0;
+ mbox.rdmbox.Unused1 = 0;
+ mbox.rdmbox.Unused2 = 0;
+ mbox.rdmbox.Unused3 = 0;
+ rcb->BytesToGo = 0; // Just to be safe
+ bytes = 0;
+ break;
+
+ case DCE_FLUSH:
+ // Flush buffers
+ mbox.flmbox.Command = DCE_FLUSH;
+ mbox.flmbox.Reserved1 = 0;
+ mbox.flmbox.Status = 0;
+ mbox.flmbox.Error = 0;
+ mbox.flmbox.Unused1 = 0;
+ mbox.flmbox.Unused2 = 0;
+ mbox.flmbox.Unused3 = 0;
+
+ // In case we get here for a post-flush,
+ // set variables so we're done next time
+ rcb->BytesToGo = 0;
+ bytes = 0;
+ blocks = 0;
+ break;
+
+ case DCE_HOSTSCSI:
+ // SCSI commands like inquiry, read capacity
+ {
+ PUCHAR VirtualCdbPtr = nce->Buffer;
+ ULONG PhysicalCdbPtr = nce->PhysicalBufferAddress;
+
+ // Make the CDB storage dword aligned
+ while( PhysicalCdbPtr & 3 ) {
+ PhysicalCdbPtr++;
+ VirtualCdbPtr++;
+ }
+
+ // Copy CDB
+ for(i=0; i<(ULONG)srb->CdbLength; i++)
+ VirtualCdbPtr[i] = srb->Cdb[i];
+
+ // Setup mailbox
+ mbox.xsmbox.Command = rcb->DceCommand;
+ mbox.xsmbox.Reserved1 = 0;
+ mbox.xsmbox.Status = (UCHAR)srb->TargetId;
+ mbox.xsmbox.Error = srb->CdbLength;
+ mbox.xsmbox.CdbAddress = PhysicalCdbPtr;
+ mbox.xsmbox.HostAddress = physAddr;
+ mbox.xsmbox.Direction = DCE_DEV2HOST;
+ mbox.xsmbox.Unused = 0;
+ mbox.xsmbox.TransferCount = (USHORT)bytes;
+ }
+ break;
+ }
+
+
+ // Advance pointers
+ rcb->BytesThisReq = bytes;
+ rcb->BlockAddress += blocks;
+
+
+ // Fire up command
+ rcb->WaitInt = 1;
+ dcehlpSendMBOX(EisaAddress, &mbox);
+
+
+ // No SRB slot freed
+ return(FALSE);
+}
+
+
+
+//
+// Disk Request Done
+// Dequeue, set status, notify Miniport layer
+// Always returns TRUE (slot freed)
+//
+BOOLEAN
+dcehlpDiskRequestDone(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN ULONG index,
+ IN UCHAR Status
+ )
+{
+ PSCSI_REQUEST_BLOCK srb;
+
+
+
+ srb = deviceExtension->ActiveSrb[index];
+
+ // Set status
+ srb->SrbStatus = Status;
+
+ // This SRB is through
+ deviceExtension->ActiveSrb[index] = NULL;
+ deviceExtension->ActiveCmds--;
+
+ // Call notification routine for the SRB.
+ ScsiPortNotification(RequestComplete,
+ (PVOID)deviceExtension,
+ srb);
+
+ return(TRUE);
+}
+
+
+
+//
+// Return TRUE if successfull, FALSE otherwise
+//
+// The 'Srb' parameter is only used for the
+// ScsiPortGetPhysicalAddress() call and must
+// be NULL if we're dealing with e.g the
+// SenseInfoBuffer
+//
+BOOLEAN
+dcehlpSplitCopy(
+ IN PHW_DEVICE_EXTENSION deviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb,
+ IN ULONG PhysicalBufferAddress,
+ IN PUCHAR VirtualUserAddress,
+ IN USHORT Count,
+ IN BOOLEAN ToUser
+ )
+{
+ ULONG physUserAddress;
+ ULONG length;
+ USHORT chunk;
+
+
+
+ PRINT("# ",0,0,0,0);
+ while(Count) {
+
+ // Prepare for check
+ length = 0;
+
+ // Get physical user address
+ physUserAddress = ScsiPortConvertPhysicalAddressToUlong(
+ ScsiPortGetPhysicalAddress(deviceExtension,
+ Srb,
+ VirtualUserAddress,
+ &length));
+
+ // Check length
+ if(length==0) {
+ // Something went wrong here
+ PRINT("SplitCopy: length==0!\n", 0, 0, 0, 0);
+ return(FALSE);
+ }
+
+ // Determine maximum transfer length this time
+ if(length > ((ULONG)Count))
+ chunk = Count;
+ else
+ chunk = (USHORT)length;
+
+ // Copy
+ if(ToUser) {
+ // Copy to user:
+ // Buffer -> DCE -> User
+ // PRINT("%p>%w>%p ", PhysicalBufferAddress, chunk, physUserAddress, 0);
+ if(!dcehlpTransferMemory(deviceExtension, PhysicalBufferAddress, DCE_BUFLOC, chunk, DCE_HOST2DCE))
+ // Error
+ return(FALSE);
+ if(!dcehlpTransferMemory(deviceExtension, physUserAddress, DCE_BUFLOC, chunk, DCE_DCE2HOST))
+ // Error
+ return(FALSE);
+ }
+ else {
+ // Copy from user:
+ // User -> DCE -> Buffer
+ // PRINT("%p<%w<%p ", PhysicalBufferAddress, chunk, physUserAddress, 0);
+ if(!dcehlpTransferMemory(deviceExtension, physUserAddress, DCE_BUFLOC, chunk, DCE_HOST2DCE))
+ // Error
+ return(FALSE);
+ if(!dcehlpTransferMemory(deviceExtension, PhysicalBufferAddress, DCE_BUFLOC, chunk, DCE_DCE2HOST))
+ // Error
+ return(FALSE);
+ }
+
+ // Advance pointers
+ VirtualUserAddress += chunk;
+ PhysicalBufferAddress += chunk;
+ Count -= chunk;
+ }
+
+ // PRINT("SC \n",0,0,0,0);
+
+ return(TRUE);
+}
+
+
+
+
+
+// Word order functions
+
+USHORT dcehlpGetM16(PUCHAR p)
+{
+ USHORT s;
+ PUCHAR sp=(PUCHAR)&s;
+
+ sp[0] = p[1];
+ sp[1] = p[0];
+ return(s);
+}
+
+ULONG dcehlpGetM24(PUCHAR p)
+{
+ ULONG l;
+ PUCHAR lp=(PUCHAR)&l;
+
+ lp[0] = p[2];
+ lp[1] = p[1];
+ lp[2] = p[0];
+ lp[3] = 0;
+ return(l);
+}
+
+ULONG dcehlpGetM32(PUCHAR p)
+{
+ ULONG l;
+ PUCHAR lp=(PUCHAR)&l;
+
+ lp[0] = p[3];
+ lp[1] = p[2];
+ lp[2] = p[1];
+ lp[3] = p[0];
+ return(l);
+}
+
+void dcehlpPutM16(PUCHAR p, USHORT s)
+{
+ PUCHAR sp=(PUCHAR)&s;
+
+ p[0] = sp[1];
+ p[1] = sp[0];
+}
+
+void dcehlpPutM24(PUCHAR p, ULONG l)
+{
+ PUCHAR lp=(PUCHAR)&l;
+
+ p[0] = lp[2];
+ p[1] = lp[1];
+ p[2] = lp[0];
+}
+
+void dcehlpPutM32(PUCHAR p, ULONG l)
+{
+ PUCHAR lp=(PUCHAR)&l;
+
+ p[0] = lp[3];
+ p[1] = lp[2];
+ p[2] = lp[1];
+ p[3] = lp[0];
+}
+
+void dcehlpPutI16(PUCHAR p, USHORT s)
+{
+ PUCHAR sp=(PUCHAR)&s;
+
+ p[0] = sp[0];
+ p[1] = sp[1];
+}
+
+void dcehlpPutI32(PUCHAR p, ULONG l)
+{
+ PUCHAR lp=(PUCHAR)&l;
+
+ p[0] = lp[0];
+ p[1] = lp[1];
+ p[2] = lp[2];
+ p[3] = lp[3];
+}
+
+ULONG dcehlpSwapM32(ULONG l)
+{
+ ULONG lres;
+ PUCHAR lp=(PUCHAR)&l;
+ PUCHAR lpres=(PUCHAR)&lres;
+
+ lpres[0] = lp[3];
+ lpres[1] = lp[2];
+ lpres[2] = lp[1];
+ lpres[3] = lp[0];
+
+ return(lres);
+}
+
+
+
+#if MYPRINT
+//
+// The monochrome screen printf() helpers start here
+//
+VOID dcehlpPutchar(PUSHORT BaseAddr, UCHAR c)
+{
+ BOOLEAN newline=FALSE;
+ USHORT s;
+ ULONG i;
+
+
+ if(c=='\r')
+ dcehlpColumn = 0;
+ else if(c=='\n')
+ newline=TRUE;
+ else {
+ if(c==9) c==' ';
+ ScsiPortWriteRegisterUshort(
+ BaseAddr+80*24+dcehlpColumn, (USHORT)(((USHORT)c)|0xF00));
+ if(++dcehlpColumn >= 80)
+ newline=TRUE;
+ }
+
+ if(newline) {
+ for(i=0; i<80*24; i++) {
+ s = ScsiPortReadRegisterUshort(BaseAddr+80+i);
+ ScsiPortWriteRegisterUshort(BaseAddr+i, s);
+ }
+ for(i=0; i<80; i++)
+ ScsiPortWriteRegisterUshort(BaseAddr+80*24+i, 0x720);
+ dcehlpColumn = 0;
+ }
+}
+
+
+
+VOID dcehlpPrintHex(PUSHORT BaseAddr, ULONG v, ULONG len)
+{
+ ULONG shift;
+ ULONG nibble;
+
+ len *= 2;
+ shift = len*4;
+ while(len--) {
+ shift -= 4;
+ nibble = (v>>shift) & 0xF;
+ dcehlpPutchar(BaseAddr, dcehlpHex[nibble]);
+ }
+}
+
+
+
+VOID dcehlpPrintf(PHW_DEVICE_EXTENSION deviceExtension,
+ PUCHAR fmt,
+ ULONG a1,
+ ULONG a2,
+ ULONG a3,
+ ULONG a4)
+{
+
+ if(deviceExtension->printAddr == 0)
+ return;
+
+ while(*fmt) {
+
+ if(*fmt=='%') {
+ fmt++;
+ switch(*fmt) {
+ case 0:
+ fmt--;
+ break;
+ case 'b':
+ dcehlpPrintHex(deviceExtension->printAddr, a1, 1);
+ break;
+ case 'w':
+ dcehlpPrintHex(deviceExtension->printAddr, a1, 2);
+ break;
+ case 'p':
+ dcehlpPrintHex(deviceExtension->printAddr, a1, 3);
+ break;
+ case 'd':
+ dcehlpPrintHex(deviceExtension->printAddr, a1, 4);
+ break;
+ default:
+ dcehlpPutchar(deviceExtension->printAddr, '?');
+ break;
+ }
+ fmt++;
+ a1 = a2;
+ a2 = a3;
+ a3 = a4;
+ }
+ else {
+ dcehlpPutchar(deviceExtension->printAddr, *fmt);
+ fmt++;
+ }
+ }
+}
+#endif // MYPRINT
+