2742 lines
59 KiB
C
2742 lines
59 KiB
C
/*
|
|
** 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
|
|
|